creek

The AI Image Editor of 2030
commit 1e16c94c95d703ee0e32af21f22c4a1d119ee289
parent b764b8571395a7ffaa09baa856f272f4b20a104a
Author: Nilotpal Gupta <nilotpalgupta0701@gmail.com>
Date:   Mon,  1 Dec 2025 01:30:25 +0530

flask services update

Diffstat:
Mlib/services/flask_service.dart | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 195 insertions(+), 58 deletions(-)

diff --git a/lib/services/flask_service.dart b/lib/services/flask_service.dart @@ -4,68 +4,206 @@ import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; -class GenerateImageService { - // Replace with your actual server URL. - // Android Emulator: 'http://10.0.2.2:5000/generate' - // iOS Simulator: 'http://127.0.0.1:5000/generate' - // Physical Device: 'http://YOUR_PC_IP:5000/generate' - static const String _serverUrl = 'http://10.0.2.2:5000/generate'; - - /// Sends a prompt to the Flask server, decodes the Base64 image, - /// saves it as a PNG, and returns the file path. +class FlaskService { + // =========================================================================== + // CONFIGURATION + // =========================================================================== + + static const String _serverUrl = 'https://locustlike-trieciously-rudolph.ngrok-free.dev'; + static const Map<String, String> _headers = {'Content-Type': 'application/json'}; + + // =========================================================================== + // 1. PIPELINES (Complex workflows) + // =========================================================================== + + /// [Sketch-to-Image Pipeline] + /// 1. Analyzes the sketch to get a text description. + /// 2. Combines User Prompt + Sketch Description + Style Prompt. + /// 3. Generates a new image based on this global prompt. + Future<String?> sketchToImage({ + required String sketchPath, + required String userPrompt, + required String stylePrompt, + }) async { + debugPrint("🔗 [Pipeline] Starting Sketch-to-Image..."); + + // Step 1: Get the description of the sketch + // We use a specific prompt to ensure we get structural details + final String? sketchDescription = await describeImage( + imagePath: sketchPath, + prompt: '<MORE_DETAILED_CAPTION>' + ); + + if (sketchDescription == null) { + debugPrint("❌ [Pipeline] Failed: Could not analyze sketch."); + return null; + } + + // Step 2: Construct the Global Prompt + // Strategy: Style + User Intent + Content context + final String globalPrompt = + "$stylePrompt. $userPrompt. The image features: $sketchDescription"; + + debugPrint("🔗 [Pipeline] Generated Global Prompt: \n$globalPrompt"); + + // Step 3: Generate the final image + return generateAndSaveImage(globalPrompt); + } + + // =========================================================================== + // 2. GENERATION SERVICES (Returns File Path) + // =========================================================================== + + /// [Text-to-Image] Future<String?> generateAndSaveImage(String prompt) async { - try { - debugPrint("🎨 Sending prompt to server: '$prompt'..."); + return _performImageOperation( + endpoint: '/generate', + logPrefix: '🎨 Text-to-Image', + body: {'prompt': prompt}, + filenamePrefix: prompt, + ); + } - // 1. Send POST request to Flask API - final response = await http.post( - Uri.parse(_serverUrl), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({'prompt': prompt}), - ); + /// [Inpainting] + Future<String?> inpaintImage({ + required String imagePath, + required String maskPath, + required String prompt, + }) async { + final String? base64Image = await _encodeFile(imagePath); + final String? base64Mask = await _encodeFile(maskPath); + + if (base64Image == null || base64Mask == null) return null; + + return _performImageOperation( + endpoint: '/inpainting', + logPrefix: '🖌️ Inpainting', + body: { + 'prompt': prompt, + 'negative_prompt': 'blurry, bad quality, low res, ugly', + 'image': base64Image, + 'mask_image': base64Mask, + }, + filenamePrefix: 'inpaint_$prompt', + ); + } + + /// [Background Removal] + Future<String?> generateAsset({required String imagePath}) async { + final String? base64Image = await _encodeFile(imagePath); + if (base64Image == null) return null; - if (response.statusCode == 200) { - final data = jsonDecode(response.body); - - if (data['image'] != null) { - final String base64String = data['image']; - - // 2. Decode Base64 string to bytes - final Uint8List imageBytes = base64Decode(base64String); - - // 3. Get directory to save the file - final directory = await getApplicationDocumentsDirectory(); - final imagesDir = Directory('${directory.path}/generated_images'); - if (!await imagesDir.exists()) { - await imagesDir.create(recursive: true); - } - - // 4. Generate unique filename - final timestamp = DateTime.now().millisecondsSinceEpoch; - // Sanitize prompt for filename (remove special chars) - final safePrompt = prompt.replaceAll(RegExp(r'[^\w\s]'), '').trim().replaceAll(' ', '_'); - // Limit filename length - final shortPrompt = safePrompt.length > 20 ? safePrompt.substring(0, 20) : safePrompt; - - final String filePath = '${imagesDir.path}/${shortPrompt}_$timestamp.png'; - final File file = File(filePath); - - // 5. Save bytes to file - await file.writeAsBytes(imageBytes); - - debugPrint("✅ Image saved successfully at: $filePath"); - return filePath; - } else { - debugPrint("❌ Server response missing 'image' field."); - return null; - } - } else { - debugPrint("❌ Server Error: ${response.statusCode} - ${response.body}"); - return null; + return _performImageOperation( + endpoint: '/remove-background', + logPrefix: '✂️ Asset Gen', + body: {'image': base64Image}, + filenamePrefix: 'asset', + ); + } + + // =========================================================================== + // 3. ANALYSIS SERVICES (Returns String) + // =========================================================================== + + /// [Image Captioning] + Future<String?> describeImage({ + required String imagePath, + String prompt = '<MORE_DETAILED_CAPTION>', + }) async { + debugPrint("👁️ [Describe] Preparing request..."); + + final String? base64Image = await _encodeFile(imagePath); + if (base64Image == null) return null; + + final response = await _postRequest( + endpoint: '/describe', + body: { + 'image': base64Image, + 'prompt': prompt, + }, + ); + + if (response != null && response.statusCode == 200) { + final data = jsonDecode(response.body); + if (data['caption'] != null) { + debugPrint("✅ [Describe] Success: ${data['caption']}"); + return data['caption']; } + } + + debugPrint("❌ [Describe] Failed."); + return null; + } + + // =========================================================================== + // PRIVATE HELPERS + // =========================================================================== + + Future<String?> _performImageOperation({ + required String endpoint, + required String logPrefix, + required Map<String, dynamic> body, + required String filenamePrefix, + }) async { + debugPrint("$logPrefix Sending request to $endpoint..."); + + final response = await _postRequest(endpoint: endpoint, body: body); + + if (response != null && response.statusCode == 200) { + return _saveImageFromResponse(response, filenamePrefix); + } + + debugPrint("❌ $logPrefix Failed: ${response?.statusCode ?? 'No Connection'}"); + return null; + } + + Future<http.Response?> _postRequest({ + required String endpoint, + required Map<String, dynamic> body, + }) async { + try { + return await http.post( + Uri.parse("$_serverUrl$endpoint"), + headers: _headers, + body: jsonEncode(body), + ); + } catch (e) { + debugPrint("❌ Network Error ($endpoint): $e"); + return null; + } + } + + Future<String?> _encodeFile(String path) async { + final file = File(path); + if (!file.existsSync()) { + debugPrint("❌ File not found: $path"); + return null; + } + return base64Encode(await file.readAsBytes()); + } + + Future<String?> _saveImageFromResponse(http.Response response, String prefix) async { + try { + final data = jsonDecode(response.body); + if (data['image'] == null) return null; + + final Uint8List imageBytes = base64Decode(data['image']); + + final directory = await getApplicationDocumentsDirectory(); + final imagesDir = Directory('${directory.path}/generated_images'); + if (!await imagesDir.exists()) await imagesDir.create(recursive: true); + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final safePrefix = prefix.replaceAll(RegExp(r'[^\w\s]'), '').trim().replaceAll(' ', '_'); + final shortPrefix = safePrefix.length > 20 ? safePrefix.substring(0, 20) : safePrefix; + final String filePath = '${imagesDir.path}/${shortPrefix}_$timestamp.png'; + + await File(filePath).writeAsBytes(imageBytes); + debugPrint("✅ Image saved: $filePath"); + return filePath; } catch (e) { - debugPrint("❌ Error generating image: $e"); + debugPrint("❌ Error saving image: $e"); return null; } } -} -\ No newline at end of file +}