creek
The AI Image Editor of 2030git clone https://github.com/maydayv7/creek
commit b26e95e9d0641d26dd9080239dc05e833a9f2318 parent 0aab94b287b57e9d4498af1b0e19e810e1cb6abf Author: Nilotpal Gupta <nilotpalgupta0701@gmail.com> Date: Thu, 27 Nov 2025 23:09:23 +0530 json schema matchup = model centralization + clenup
Diffstat:
| M | lib/services/analyze/color.dart | | | 2 | ++ |
| M | lib/services/analyze/embedding.dart | | | 24 | ++++++++++++++---------- |
| D | lib/services/analyze/emotional_embeddings.dart | | | 124 | ------------------------------------------------------------------------------- |
| D | lib/services/analyze/era.dart | | | 120 | ------------------------------------------------------------------------------- |
| M | lib/services/analyze/image_analyzer.dart | | | 118 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
| M | lib/services/analyze/layout.dart | | | 2 | ++ |
| D | lib/services/analyze/lighting.dart | | | 120 | ------------------------------------------------------------------------------- |
| M | lib/services/analyze/texture.dart | | | 27 | +++++++++++---------------- |
8 files changed, 97 insertions(+), 440 deletions(-)
diff --git a/lib/services/analyze/color.dart b/lib/services/analyze/color.dart @@ -1,3 +1,5 @@ +// lib/services/color_analyzer_service.dart + import 'package:flutter/foundation.dart'; import 'python_service.dart'; diff --git a/lib/services/analyze/embedding.dart b/lib/services/analyze/embedding.dart @@ -1,9 +1,11 @@ +// lib/services/embedding_analyzer_service.dart + import 'dart:convert'; import 'dart:io'; +import 'package:adobe/utils/image_utils.dart'; import 'package:flutter/foundation.dart'; import 'package:onnxruntime/onnxruntime.dart'; -import 'package:adobe/utils/image_utils.dart'; -import 'package:adobe/utils/clip_image_processor.dart'; +import '../../utils/clip_image_processor.dart'; class EmbeddingAnalyzerService { OrtSession? _session; @@ -39,9 +41,9 @@ class EmbeddingAnalyzerService { String? jsonPath }) async { // Safety check - if (modelPath == null || jsonPath == null) return null; + if (modelPath == null || jsonPath == null) return {'success': false, 'scores': {}, 'error': 'Paths missing'}; await initialize(modelPath: modelPath, jsonPath: jsonPath); - if (_session == null || _centroids == null) return null; + if (_session == null || _centroids == null) return {'success': false, 'scores': {}, 'error': 'Init failed'}; OrtValueTensor? inputOrt; OrtRunOptions? runOptions; @@ -49,7 +51,7 @@ class EmbeddingAnalyzerService { try { final float32Input = await ClipImageProcessor.preprocess(imagePath); - if (float32Input == null) return null; + if (float32Input == null) return {'success': false, 'scores': {}, 'error': 'Image decode failed'}; // Create Tensor // Note: ensure ClipImageProcessor returns a flat List<double> @@ -94,19 +96,21 @@ class EmbeddingAnalyzerService { } scores = Map.fromEntries( - scores.entries.toList() - ..sort((a, b) => b.value.compareTo(a.value)) + (scores.entries.toList() + ..sort((a, b) => b.value + .compareTo(a.value)) + )//.take(3) ); return { "success": true, - "predictions": scores, - "best": {scores.keys.first: scores.values.first}, + "scores": scores, + "error": null, }; } catch (e) { debugPrint("Embeddings Analysis Error: $e"); - return null; + return {'success': false, 'scores': {}, 'error': e.toString()}; } finally { inputOrt?.release(); runOptions?.release(); diff --git a/lib/services/analyze/emotional_embeddings.dart b/lib/services/analyze/emotional_embeddings.dart @@ -1,124 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:flutter/foundation.dart'; -import 'package:onnxruntime/onnxruntime.dart'; -import 'package:adobe/utils/image_utils.dart'; -import 'package:adobe/utils/clip_image_processor.dart'; - -class EmotionalEmbeddingsService { - OrtSession? _session; - List<List<double>>? _centroids; - List<String>? _classes; - - Future<void> initialize({required String modelPath, required String jsonPath}) async { - if (_session != null) return; - - try { - debugPrint("Initializing Embeddings (Dino/CLIP)..."); - - OrtEnv.instance.init(); - - // 1. Load Model from passed PATH (No rootBundle!) - final sessionOptions = OrtSessionOptions(); - _session = OrtSession.fromFile(File(modelPath), sessionOptions); - sessionOptions.release(); - - final jsonString = await File(jsonPath).readAsString(); - final jsonData = json.decode(jsonString); - - _classes = List<String>.from(jsonData['classes']); - _centroids = - (jsonData['centroids'] as List) - .map((e) => List<double>.from(e)) - .toList(); - } catch (e) { - debugPrint("Embeddings Init Error: $e"); - } - } - - Future<Map<String, dynamic>?> analyze(String imagePath, { - String? modelPath, - String? jsonPath - }) async { - // Safety check - if (modelPath == null || jsonPath == null) return null; - await initialize(modelPath: modelPath, jsonPath: jsonPath); - if (_session == null || _centroids == null) return null; - - OrtValueTensor? inputOrt; - OrtRunOptions? runOptions; - List<OrtValue?>? outputs; - - try { - final float32Input = await ClipImageProcessor.preprocess(imagePath); - //debugPrint("[EMOTION] Preprocess length: ${float32Input?.length}"); - if (float32Input == null) return null; - - // Create Tensor - // Note: ensure ClipImageProcessor returns a flat List<double> - inputOrt = OrtValueTensor.createTensorWithDataList( - float32Input, [1, 3, 224, 224]); - - runOptions = OrtRunOptions(); - - // Run Inference - // 'image' is the input name for CLIP. - outputs = _session!.run(runOptions, {"image": inputOrt}); - //debugPrint("[EMOTION] Output keys: ${outputs.keys}"); - - if (outputs.isEmpty) throw Exception("No output from model"); - - // Get Output - final dynamic outputRaw = outputs[0]?.value; - - // Flatten Output - final List<double> imgFeat = []; - - void flatten(dynamic data) { - if (data is num) { - imgFeat.add(data.toDouble()); - } else if (data is List) { - for (var item in data) { - flatten(item); - } - } - } - - flatten(outputRaw); - - // debugPrint("[EMOTION] Embedding length: ${imgFeat.length}"); - // debugPrint("[EMOTION] Centroid length: ${_centroids?[0].length}"); - - // Normalize & Compare - final normFeat = l2Normalize(imgFeat); - Map<String, dynamic> scores = {}; - - for (int i = 0; i < _classes!.length; i++) { - double score = dotProduct(normFeat, _centroids![i]); - scores[_classes![i]] = score * 100.0; - } - - scores = Map.fromEntries( - scores.entries.toList() - ..sort((a, b) => b.value.compareTo(a.value)) - ); - - return { - "success": true, - "predictions": scores, - "best": {scores.keys.first: scores.values.first}, - }; - } catch (e) { - debugPrint("Embeddings Analysis Error: $e"); - return null; - } finally { - inputOrt?.release(); - runOptions?.release(); - outputs?.forEach((element) => element?.release()); - } - } - - void dispose() { - _session?.release(); - } -} diff --git a/lib/services/analyze/era.dart b/lib/services/analyze/era.dart @@ -1,120 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:flutter/foundation.dart'; -import 'package:onnxruntime/onnxruntime.dart'; -import 'package:adobe/utils/image_utils.dart'; -import 'package:adobe/utils/clip_image_processor.dart'; - -class EraEmbeddingsService { - OrtSession? _session; - List<List<double>>? _centroids; - List<String>? _classes; - - Future<void> initialize({required String modelPath, required String jsonPath}) async { - if (_session != null) return; - - try { - debugPrint("Initializing Embeddings (Dino/CLIP)..."); - - OrtEnv.instance.init(); - - // 1. Load Model from passed PATH (No rootBundle!) - final sessionOptions = OrtSessionOptions(); - _session = OrtSession.fromFile(File(modelPath), sessionOptions); - sessionOptions.release(); - - final jsonStr = await File(jsonPath).readAsString(); - final jsonData = json.decode(jsonStr); - - _classes = List<String>.from(jsonData['classes']); - _centroids = - (jsonData['centroids'] as List) - .map((e) => List<double>.from(e)) - .toList(); - } catch (e) { - debugPrint("Embeddings Init Error: $e"); - } - } - - Future<Map<String, dynamic>?> analyze(String imagePath, { - String? modelPath, - String? jsonPath, - }) async { - // Safety check - if (modelPath == null || jsonPath == null) return null; - await initialize(modelPath: modelPath, jsonPath: jsonPath); - if (_session == null || _centroids == null) return null; - - OrtValueTensor? inputOrt; - OrtRunOptions? runOptions; - List<OrtValue?>? outputs; - - try { - final float32Input = await ClipImageProcessor.preprocess(imagePath); - if (float32Input == null) return null; - - // Create Tensor - // Note: ensure ClipImageProcessor returns a flat List<double> - inputOrt = OrtValueTensor.createTensorWithDataList( - float32Input, [1, 3, 224, 224]); - - runOptions = OrtRunOptions(); - - // Run Inference - // 'image' is the input name for CLIP. - outputs = _session!.run(runOptions, {"image": inputOrt}); // <--- UPDATED - //debugPrint("[LIGHTING] Output keys: ${outputs.keys}"); - - if (outputs.isEmpty) throw Exception("No output from model"); - - // Get Output - final dynamic outputRaw = outputs[0]?.value; - - // Flatten Output - final List<double> imgFeat = []; - - void flatten(dynamic data) { - if (data is num) { - imgFeat.add(data.toDouble()); - } else if (data is List) { - for (var item in data) { - flatten(item); - } - } - } - - flatten(outputRaw); - - // Normalize & Compare - final normFeat = l2Normalize(imgFeat); - Map<String, dynamic> scores = {}; - - for (int i = 0; i < _classes!.length; i++) { - double score = dotProduct(normFeat, _centroids![i]); - scores[_classes![i]] = score * 100.0; - } - - scores = Map.fromEntries( - scores.entries.toList() - ..sort((a, b) => b.value.compareTo(a.value)), - ); - - return { - "success": true, - "predictions": scores, - "best": {scores.keys.first: scores.values.first}, - }; - } catch (e) { - debugPrint("Embeddings Analysis Error: $e"); - return null; - } finally { - inputOrt?.release(); - runOptions?.release(); - outputs?.forEach((element) => element?.release()); - } - } - - void dispose() { - _session?.release(); - } -} diff --git a/lib/services/analyze/image_analyzer.dart b/lib/services/analyze/image_analyzer.dart @@ -1,3 +1,5 @@ +// lib/services/image_analyzer_service.dart + import 'dart:async'; import 'dart:io'; import 'dart:isolate'; @@ -6,15 +8,13 @@ import 'dart:developer' as dev; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart' as p; // Import analysis services -import 'layout.dart'; import 'color.dart'; import 'texture.dart'; import 'embedding.dart'; -import 'emotional_embeddings.dart'; -import 'lighting.dart'; -import 'era.dart'; +import 'layout.dart'; class ImageAnalyzerService { @@ -26,7 +26,6 @@ class ImageAnalyzerService { Future<String> copy(String assetName, {String? filename}) async { final name = filename ?? assetName; final file = File('${dir.path}/$name'); - // Only copy if it doesn't exist or is empty if (!await file.exists() || await file.length() == 0) { final data = await rootBundle.load('assets/$assetName'); await file.writeAsBytes(data.buffer.asUint8List(), flush: true); @@ -35,14 +34,13 @@ class ImageAnalyzerService { } // Copy everything needed by the services - // Make sure your assets/ folder actually contains these files! final paths = await Future.wait([ - // Texture + // DINO copy('dinov2.onnx'), copy('dinov2.onnx.data'), copy('texture_centroids.json'), - // CLIP (Embeddings/Emotion/Lighting) + // CLIP copy('clip_image_encoder.onnx'), copy('style_embeddings.json'), copy('emotion_centroids.json'), @@ -67,7 +65,6 @@ class ImageAnalyzerService { required String imagePath, required RootIsolateToken rootToken, required bool runInIsolate, - // Pass config map to the task required Map<String, String> assetPaths, required Future<dynamic> Function(String, Map<String, String>) task, }) async { @@ -92,16 +89,20 @@ class ImageAnalyzerService { stopwatch.stop(); timelineTask.finish(); + result['execution_time']= stopwatch.elapsedMilliseconds; + return result; - return { - 'data': result, - 'stats': {'execution_time_ms': stopwatch.elapsedMilliseconds} - }; } catch (e) { stopwatch.stop(); timelineTask.finish(); debugPrint("[$name] Failed: $e"); - return {'error': e.toString()}; + + return { + 'success': false, + 'scores': {}, + 'error': e.toString(), + 'execution_time': stopwatch.elapsedMilliseconds, + }; } } @@ -169,7 +170,7 @@ class ImageAnalyzerService { name: 'Emotional', imagePath: imagePath, rootToken: token, runInIsolate: true, assetPaths: assetPaths, task: (path, assets) async { - final service = EmotionalEmbeddingsService(); + final service = EmbeddingAnalyzerService(); final res = await service.analyze(path, modelPath: assets['clip_model'], jsonPath: assets['emotion_json'] @@ -183,7 +184,7 @@ class ImageAnalyzerService { name: 'Lighting', imagePath: imagePath, rootToken: token, runInIsolate: true, assetPaths: assetPaths, task: (path, assets) async { - final service = LightingEmbeddingsService(); + final service = EmbeddingAnalyzerService(); final res = await service.analyze(path, modelPath: assets['clip_model'], jsonPath: assets['lighting_json'] @@ -197,7 +198,7 @@ class ImageAnalyzerService { name: 'Era', imagePath: imagePath, rootToken: token, runInIsolate: true, assetPaths: assetPaths, task: (path, assets) async { - final service = EraEmbeddingsService(); + final service = EmbeddingAnalyzerService(); final res = await service.analyze(path, modelPath: assets['clip_model'], jsonPath: assets['era_json'] @@ -210,61 +211,78 @@ class ImageAnalyzerService { totalSw.stop(); - final finalResult = { + // This is only for testing + final logResult = { 'success': true, - 'total_time_ms': totalSw.elapsedMilliseconds, - 'timestamp': DateTime.now().toIso8601String(), + 'total_time': totalSw.elapsedMilliseconds, + 'filename': p.basename(imagePath), 'results': { - 'layout': results[0], - 'color': results[1], - 'texture': results[2], - 'embedding': results[3], - 'emotions': results[4], - 'lighting': results[5], - 'era': results[6], + 'Layout': results[0]['execution_time'], + 'Color': results[1]['execution_time'], + 'Texture': results[2]['execution_time'], + 'Style': results[3]['execution_time'], + 'Emotions': results[4]['execution_time'], + 'Lighting': results[5]['execution_time'], + 'Era': results[6]['execution_time'], } }; + _logSummary(logResult); - _logSummary(finalResult); - print("\n\n\n layout: ${results[0]} \n\n\n"); - print("color: ${results[1]} \n\n\n"); - print("texture: ${results[2]} \n\n\n"); - print("embedding: ${results[3]} \n\n\n"); - print("emotions: ${results[4]} \n\n\n"); - print("lighting: ${results[5]} \n\n\n"); - print("era: ${results[6]} \n\n\n"); + final finalResult = { + 'success': true, + 'data': { + 'filename': p.basename(imagePath), + 'results': { + 'Style': {"scores": results[3]['scores']}, + 'Texture': {"scores": results[2]['scores']}, + 'Lighting': {"scores": results[5]['scores']}, + 'Colour Pallete': {"scores": results[1]['scores']}, + 'Emotions': {"scores": results[4]['scores']}, + 'Era': {"scores": results[6]['scores']}, + 'Layout': {"scores": results[0]['scores']} + } + }, + 'error': null, + }; + debugPrint(finalResult.toString()); return finalResult; } catch (e) { debugPrint("Master Analysis Failed: $e"); - return {'success': false, 'error': e.toString()}; + return { + 'success': false, + 'data': { + 'filename': '', + 'results': { + 'Style': {"scores": {}}, + 'Texture': {"scores": {}}, + 'Lighting': {"scores": {}}, + 'Colour Pallete': {"scores": {}}, + 'Emotions': {"scores": {}}, + 'Era': {"scores": {}}, + } + }, + 'error': e.toString() + }; } } static void _logSummary(Map<String, dynamic> result) { - final int total = result['total_time_ms']; + final int total = result['total_time']; final Map<String, dynamic> subTasks = result['results']; - StringBuffer output = StringBuffer(); + output.writeln('\n══════════ ⚡ HYBRID PARALLEL REPORT ⚡ ══════════'); output.writeln(' 🕒 Total Wall Time: ${total}ms'); output.writeln('──────────────────────────────────────────────────────'); subTasks.forEach((key, value) { - if (value is Map && value.containsKey('stats')) { - final int time = value['stats']['execution_time_ms']; - final int barLength = (time / 50).ceil().clamp(0, 20); - final String bar = '█' * barLength; - - output.writeln(' ${key.padRight(12)} : ${time.toString().padLeft(4)}ms $bar'); - } else { - String err = value['error'] ?? "Unknown"; - if (err.length > 30) err = "${err.substring(0, 30)}..."; - output.writeln(' ${key.padRight(12)} : FAILED ❌ ($err)'); - } + final int barLength = (value / 50).ceil().clamp(0, 20); + final String bar = '█' * barLength; + output.writeln(' ${key.padRight(12)} : ${value.toString().padLeft(4)}ms $bar'); }); - output.writeln('══════════════════════════════════════════════════════\n'); + output.writeln('══════════════════════════════════════════════════════\n'); dev.log(output.toString(), name: 'ImageAnalyzer'); } } diff --git a/lib/services/analyze/layout.dart b/lib/services/analyze/layout.dart @@ -1,3 +1,5 @@ +// lib/services/layout_analyzer_service.dart + import 'package:flutter/foundation.dart'; import 'python_service.dart'; diff --git a/lib/services/analyze/lighting.dart b/lib/services/analyze/lighting.dart @@ -1,120 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:flutter/foundation.dart'; -import 'package:onnxruntime/onnxruntime.dart'; -import 'package:adobe/utils/image_utils.dart'; -import 'package:adobe/utils/clip_image_processor.dart'; - -class LightingEmbeddingsService { - OrtSession? _session; - List<List<double>>? _centroids; - List<String>? _classes; - - Future<void> initialize({required String modelPath, required String jsonPath}) async { - if (_session != null) return; - - try { - debugPrint("Initializing Embeddings (Dino/CLIP)..."); - - OrtEnv.instance.init(); - - // 1. Load Model from passed PATH (No rootBundle!) - final sessionOptions = OrtSessionOptions(); - _session = OrtSession.fromFile(File(modelPath), sessionOptions); - sessionOptions.release(); - - final jsonStr = await File(jsonPath).readAsString(); - final jsonData = json.decode(jsonStr); - - _classes = List<String>.from(jsonData['classes']); - _centroids = - (jsonData['centroids'] as List) - .map((e) => List<double>.from(e)) - .toList(); - } catch (e) { - debugPrint("Embeddings Init Error: $e"); - } - } - - Future<Map<String, dynamic>?> analyze(String imagePath, { - String? modelPath, - String? jsonPath, - }) async { - // Safety check - if (modelPath == null || jsonPath == null) return null; - await initialize(modelPath: modelPath, jsonPath: jsonPath); - if (_session == null || _centroids == null) return null; - - OrtValueTensor? inputOrt; - OrtRunOptions? runOptions; - List<OrtValue?>? outputs; - - try { - final float32Input = await ClipImageProcessor.preprocess(imagePath); - if (float32Input == null) return null; - - // Create Tensor - // Note: ensure ClipImageProcessor returns a flat List<double> - inputOrt = OrtValueTensor.createTensorWithDataList( - float32Input, [1, 3, 224, 224]); - - runOptions = OrtRunOptions(); - - // Run Inference - // 'image' is the input name for CLIP. - outputs = _session!.run(runOptions, {"image": inputOrt}); // <--- UPDATED - //debugPrint("[LIGHTING] Output keys: ${outputs.keys}"); - - if (outputs.isEmpty) throw Exception("No output from model"); - - // Get Output - final dynamic outputRaw = outputs[0]?.value; - - // Flatten Output - final List<double> imgFeat = []; - - void flatten(dynamic data) { - if (data is num) { - imgFeat.add(data.toDouble()); - } else if (data is List) { - for (var item in data) { - flatten(item); - } - } - } - - flatten(outputRaw); - - // Normalize & Compare - final normFeat = l2Normalize(imgFeat); - Map<String, dynamic> scores = {}; - - for (int i = 0; i < _classes!.length; i++) { - double score = dotProduct(normFeat, _centroids![i]); - scores[_classes![i]] = score * 100.0; - } - - scores = Map.fromEntries( - scores.entries.toList() - ..sort((a, b) => b.value.compareTo(a.value)), - ); - - return { - "success": true, - "predictions": scores, - "best": {scores.keys.first: scores.values.first}, - }; - } catch (e) { - debugPrint("Embeddings Analysis Error: $e"); - return null; - } finally { - inputOrt?.release(); - runOptions?.release(); - outputs?.forEach((element) => element?.release()); - } - } - - void dispose() { - _session?.release(); - } -} diff --git a/lib/services/analyze/texture.dart b/lib/services/analyze/texture.dart @@ -1,9 +1,11 @@ +// lib/services/texture_analyzer_service.dart + import 'dart:io'; import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:image/image.dart' as img; import 'package:onnxruntime/onnxruntime.dart'; -import 'package:adobe/utils/image_utils.dart'; +import '../../utils/image_utils.dart'; class TextureAnalyzerService { OrtSession? _session; @@ -46,9 +48,9 @@ class TextureAnalyzerService { String? jsonPath }) async { // Safety check - if (modelPath == null || jsonPath == null) return {'success': false, 'error': 'Paths missing'}; + if (modelPath == null || jsonPath == null) return {'success': false, 'scores': {}, 'error': 'Paths missing'}; await initialize(modelPath: modelPath, jsonPath: jsonPath); - if (_session == null || _centroids == null) return {'success': false, 'error': 'Init failed'}; + if (_session == null || _centroids == null) return {'success': false, 'scores': {}, 'error': 'Init failed'}; OrtValueTensor? inputOrt; OrtRunOptions? runOptions; @@ -58,7 +60,7 @@ class TextureAnalyzerService { // 1. Preprocess final bytes = await File(path).readAsBytes(); final image = img.decodeImage(bytes); - if (image == null) return {'success': false, 'error': 'Image decode failed'}; + if (image == null) return {'success': false, 'scores': {}, 'error': 'Image decode failed'}; final resized = img.copyResize(image, width: INPUT_SIZE, height: INPUT_SIZE); @@ -125,27 +127,20 @@ class TextureAnalyzerService { var sorted = rawScores.entries.toList()..sort((a, b) => b.value.compareTo(a.value)); Map<String, dynamic> finalResults = {}; - if (sorted.isNotEmpty) { - double topScore = sorted.first.value; - for (var entry in sorted) { - if (finalResults.length >= MAX_COUNT) break; - if (entry.value < ABSOLUTE_FLOOR) continue; - if (entry.value < (topScore * RELATIVE_THRESH)) break; - - finalResults[entry.key] = entry.value; - } + for(var entry in sorted) { //.take(3) + finalResults[entry.key] = entry.value; } // Return Map consistent with other services return { 'success': true, - 'predictions': finalResults, - 'best': {finalResults.keys.first: finalResults.values.first}, + 'scores': finalResults, + 'error': null, }; } catch (e) { debugPrint("Texture Analysis Failed: $e"); - return {'success': false, 'error': e.toString()}; + return {'success': false, 'scores': {}, 'error': e.toString()}; } finally { // FFI Requires manual release of C++ resources inputOrt?.release();