creek

The AI Image Editor of 2030
commit 4a461358a30ce16a62feada9ea65418bf1b1513e
parent e2ec9d7abe9bce68ba1ef3d00a42604c75c12930
Author: Nilotpal Gupta <nilotpalgupta0701@gmail.com>
Date:   Thu, 27 Nov 2025 19:48:41 +0530

unified era in similar structure

Diffstat:

Mlib/services/era_service.dart | 120++++++++++++++++++++++++++++++++++---------------------------------------------
Mlib/services/image_analyzer_service.dart | 18++++++++++++++++++
Mlib/ui/pages/image_details_page.dart | 2+-
Mpubspec.lock | 38++++++++++++++++++--------------------
Mpubspec.yaml | 1+
5 files changed, 90 insertions(+), 89 deletions(-)

diff --git a/lib/services/era_service.dart b/lib/services/era_service.dart @@ -1,39 +1,32 @@ +// lib/services/lighting_service.dart + import 'dart:convert'; -import 'dart:math'; import 'dart:io'; +import 'package:adobe/utils/image_utils.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_onnxruntime/flutter_onnxruntime.dart'; -import 'package:path_provider/path_provider.dart'; +import 'package:onnxruntime/onnxruntime.dart'; import '../utils/clip_image_processor.dart'; class EraEmbeddingsService { - static OrtSession? _session; - static List<List<double>>? _centroids; - static List<String>? _classes; + OrtSession? _session; + List<List<double>>? _centroids; + List<String>? _classes; - static Future<void> initialize() async { + Future<void> initialize({required String modelPath, required String jsonPath}) async { if (_session != null) return; try { debugPrint("Initializing Embeddings (Dino/CLIP)..."); + + OrtEnv.instance.init(); - final rawAsset = await rootBundle.load('assets/clip_image_encoder.onnx'); - final dir = await getTemporaryDirectory(); - final modelFile = File('${dir.path}/clip_model.onnx'); - - if (!await modelFile.exists()) { - await modelFile.writeAsBytes(rawAsset.buffer.asUint8List()); - } - - // Initialize runtime - final ort = OnnxRuntime(); - _session = await ort.createSession(modelFile.path); + // 1. Load Model from passed PATH (No rootBundle!) + final sessionOptions = OrtSessionOptions(); + _session = OrtSession.fromFile(File(modelPath), sessionOptions); + sessionOptions.release(); - final jsonString = await rootBundle.loadString( - 'assets/era_centroids.json', - ); - final jsonData = json.decode(jsonString); + final jsonStr = await File(jsonPath).readAsString(); + final jsonData = json.decode(jsonStr); _classes = List<String>.from(jsonData['classes']); _centroids = @@ -45,36 +38,41 @@ class EraEmbeddingsService { } } - static Future<Map<String, dynamic>?> analyze(String imagePath) async { - if (_session == null) await initialize(); + 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; - OrtValue? inputOrt; - OrtValue? outputOrt; + 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 = await OrtValue.fromList(float32Input, [1, 3, 224, 224]); + inputOrt = OrtValueTensor.createTensorWithDataList( + float32Input, [1, 3, 224, 224]); + + runOptions = OrtRunOptions(); // Run Inference - // 'image' is the input name for CLIP. Check your model if it differs (e.g. 'input') - final outputs = await _session!.run({"image": inputOrt}); - //debugPrint("[EMOTION] Output keys: ${outputs.keys}"); + // 'image' is the input name for CLIP. + outputs = _session!.run(runOptions, {"image": inputOrt}); // <--- UPDATED + //debugPrint("[LIGHTING] Output keys: ${outputs.keys}"); - // Get Output - outputOrt = - outputs['output']; // or outputs['image_features'] depending on model - // If outputOrt is null, try iterating outputs.values.first - final resultOrt = outputOrt ?? outputs.values.first; + if (outputs.isEmpty) throw Exception("No output from model"); - if (resultOrt == null) throw Exception("No output from model"); + // Get Output + final dynamic outputRaw = outputs[0]?.value; // Flatten Output - final List<dynamic> rawList = await resultOrt.asList(); final List<double> imgFeat = []; void flatten(dynamic data) { @@ -87,52 +85,38 @@ class EraEmbeddingsService { } } - flatten(rawList); - - // debugPrint("[EMOTION] Embedding length: ${imgFeat.length}"); - // debugPrint("[EMOTION] Centroid length: ${_centroids?[0].length}"); + flatten(outputRaw); // Normalize & Compare - final normFeat = _l2Normalize(imgFeat); - List<Map<String, dynamic>> scores = []; + final normFeat = l2Normalize(imgFeat); + Map<String, dynamic> scores = {}; for (int i = 0; i < _classes!.length; i++) { - double score = _dotProduct(normFeat, _centroids![i]); - scores.add({"name": _classes![i], "score": score * 100.0}); + double score = dotProduct(normFeat, _centroids![i]); + scores[_classes![i]] = score * 100.0; } - scores.sort( - (a, b) => (b['score'] as double).compareTo(a['score'] as double), + scores = Map.fromEntries( + scores.entries.toList() + ..sort((a, b) => b.value.compareTo(a.value)), ); return { "success": true, - "label": scores.first['name'], - "top5": scores.take(5).toList(), + "predictions": scores, + "best": {scores.keys.first: scores.values.first}, }; } catch (e) { debugPrint("Embeddings Analysis Error: $e"); return null; } finally { - inputOrt?.dispose(); - outputOrt?.dispose(); + inputOrt?.release(); + runOptions?.release(); + outputs?.forEach((element) => element?.release()); } } - static List<double> _l2Normalize(List<double> vec) { - double sum = vec.fold(0, (p, c) => p + c * c); - double norm = sqrt(sum); - if (norm == 0) return vec; - return vec.map((v) => v / norm).toList(); - } - - static double _dotProduct(List<double> a, List<double> b) { - double sum = 0; - // Ensure lengths match to avoid range errors - int len = min(a.length, b.length); - for (int i = 0; i < len; i++) { - sum += a[i] * b[i]; - } - return sum; + void dispose() { + _session?.release(); } } diff --git a/lib/services/image_analyzer_service.dart b/lib/services/image_analyzer_service.dart @@ -49,6 +49,7 @@ class ImageAnalyzerService { copy('style_embeddings.json'), copy('emotion_centroids.json'), copy('lighting_centroids.json'), + copy('era_centroids.json'), ]); return { @@ -59,6 +60,7 @@ class ImageAnalyzerService { 'embedding_json': paths[4], 'emotion_json': paths[5], 'lighting_json': paths[6], + 'era_json': paths[7], }; } @@ -192,6 +194,20 @@ class ImageAnalyzerService { return res; } ), + + _runProfiledJob( + name: 'Era', imagePath: imagePath, rootToken: token, runInIsolate: true, + assetPaths: assetPaths, + task: (path, assets) async { + final service = EraEmbeddingsService(); + final res = await service.analyze(path, + modelPath: assets['clip_model'], + jsonPath: assets['era_json'] + ); + service.dispose(); + return res; + } + ), ]); totalSw.stop(); @@ -207,6 +223,7 @@ class ImageAnalyzerService { 'embedding': results[3], 'emotions': results[4], 'lighting': results[5], + 'era': results[6], } }; @@ -217,6 +234,7 @@ class ImageAnalyzerService { 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"); return finalResult; } catch (e) { diff --git a/lib/ui/pages/image_details_page.dart b/lib/ui/pages/image_details_page.dart @@ -38,7 +38,7 @@ class _ImageDetailPageState extends State<ImageDetailPage> { try { // 1. Fetch detailed image info (including comments if repo supports it, // or fetch separately if not fully integrated yet) - final data = await _imageRepo.getImageDetails(widget.imageId); + final data = null; // await _imageRepo.getImageDetails(widget.imageId); if (data != null) { // Parse Image diff --git a/pubspec.lock b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.12.0" boolean_selector: dependency: transitive description: @@ -85,12 +85,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.3" - version: "1.3.3" + version: "1.3.2" ffi: dependency: transitive description: @@ -289,23 +287,23 @@ packages: sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "11.0.2" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -334,10 +332,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.16.0" mime: dependency: transitive description: @@ -619,10 +617,10 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.4" typed_data: dependency: transitive description: @@ -643,18 +641,18 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.3.1" web: dependency: transitive description: @@ -680,5 +678,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.7.2 <4.0.0" flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: provider: ^6.1.5+1 image: ^4.5.4 onnxruntime: ^1.4.1 + intl: ^0.20.2 dev_dependencies: flutter_test: