diff --git a/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart b/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart index 45d2240344..06941cbb30 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_recognition_service.dart @@ -89,7 +89,7 @@ class FaceRecognitionService { } Future _syncFaceEmbeddings({int retryFetchCount = 10}) async { - final filesToIndex = await getFilesForMlIndexing(); + final List filesToIndex = await getFilesForMlIndexing(); final List> chunks = filesToIndex.chunks(_embeddingFetchLimit); // Chunks of 200 int fetchedCount = 0; diff --git a/mobile/lib/services/machine_learning/file_ml/file_ml.dart b/mobile/lib/services/machine_learning/file_ml/file_ml.dart index 6a149af664..b97a59e4eb 100644 --- a/mobile/lib/services/machine_learning/file_ml/file_ml.dart +++ b/mobile/lib/services/machine_learning/file_ml/file_ml.dart @@ -1,45 +1,58 @@ import "package:photos/face/model/face.dart"; +const _faceKey = 'face'; +const _clipKey = 'clip'; + class RemoteFileML { final int fileID; final Map remoteRawData; - final RemoteFaceEmbedding? faceEmbedding; - final RemoteClipEmbedding? clipEmbedding; RemoteFileML( this.fileID, - this.remoteRawData, { - required this.faceEmbedding, - this.clipEmbedding, - }); + this.remoteRawData, + ); - // toJson - Map toJson() { - throw UnimplementedError(); - } - - // fromRemote factory RemoteFileML.fromRemote(int fileID, Map json) { return RemoteFileML( fileID, json, - faceEmbedding: json['face'] != null - ? RemoteFaceEmbedding.fromJson( - json['face'] as Map, - ) - : null, - clipEmbedding: json['clip'] == null - ? null - : RemoteClipEmbedding.fromJson( - json['clip'] as Map, - ), ); } + + static RemoteFileML empty(int i) { + final Map json = {}; + return RemoteFileML(i, json); + } + + void putFaceIfNotNull(RemoteFaceEmbedding? faceEmbedding) { + if (faceEmbedding != null) { + remoteRawData[_faceKey] = faceEmbedding.toJson(); + } + } + + void putClipIfNotNull(RemoteClipEmbedding? clipEmbedding) { + if (clipEmbedding != null) { + remoteRawData[_clipKey] = clipEmbedding.toJson(); + } + } + + RemoteFaceEmbedding? get faceEmbedding => remoteRawData[_faceKey] != null + ? RemoteFaceEmbedding.fromJson( + remoteRawData[_faceKey] as Map, + ) + : null; + + RemoteClipEmbedding? get clipEmbedding => remoteRawData[_clipKey] != null + ? RemoteClipEmbedding.fromJson( + remoteRawData[_clipKey] as Map, + ) + : null; } class RemoteFaceEmbedding { final List faces; final int version; + // packageName/version final String client; final int height; diff --git a/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart b/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart index f8d6fe629c..7fb62155e2 100644 --- a/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart +++ b/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart @@ -3,7 +3,7 @@ import "dart:convert"; import "dart:io"; import "package:computer/computer.dart"; -import "package:flutter/foundation.dart" show Uint8List, debugPrint; +import "package:flutter/foundation.dart" show Uint8List; import "package:logging/logging.dart"; import "package:photos/core/network/network.dart"; import "package:photos/db/files_db.dart"; @@ -28,14 +28,19 @@ class RemoteFileMLService { void init(SharedPreferences prefs) {} - Future putFileEmbedding(EnteFile file, RemoteFileML fileML) async { - throw Exception("need to update implementation"); + Future putFileEmbedding( + EnteFile file, + RemoteFileML fileML, { + RemoteClipEmbedding? clipEmbedding, + RemoteFaceEmbedding? faceEmbedding, + }) async { + fileML.putClipIfNotNull(clipEmbedding); + fileML.putFaceIfNotNull(faceEmbedding); final encryptionKey = getFileKey(file); - final embeddingJSON = jsonEncode(fileML.toJson()); - final encryptedEmbedding = await CryptoUtil.encryptChaCha( - utf8.encode(embeddingJSON), - encryptionKey, - ); + final embeddingJSON = jsonEncode(fileML.remoteRawData); + final compressedData = gzipUInt8List(utf8.encode(embeddingJSON)); + final encryptedEmbedding = + await CryptoUtil.encryptChaCha(compressedData, encryptionKey); final encryptedData = CryptoUtil.bin2base64(encryptedEmbedding.encryptedData!); final header = CryptoUtil.bin2base64(encryptedEmbedding.header!); @@ -44,12 +49,11 @@ class RemoteFileMLService { "/embeddings", data: { "fileID": file.uploadedFileID!, - "model": 'file-ml-clip-face', + "model": 'ggml-clip', "encryptedEmbedding": encryptedData, "decryptionHeader": header, }, ); - // final updationTime = response.data["updatedAt"]; } catch (e, s) { _logger.severe("Failed to put embedding", e, s); rethrow; @@ -127,6 +131,13 @@ Uint8List ungzipUint8List(Uint8List compressedData) { return Uint8List.fromList(decompressedList); } +// gzipUInt8List +Uint8List gzipUInt8List(Uint8List data) { + final codec = GZipCodec(); + final compressedData = codec.encode(data); + return Uint8List.fromList(compressedData); +} + Future> _decryptFileMLComputer( Map args, ) async { @@ -140,7 +151,6 @@ Future> _decryptFileMLComputer( decryptArgs["header"] = CryptoUtil.base642bin(input.embedding.decryptionHeader); final embeddingData = chachaDecryptData(decryptArgs); - // unzip the gzip data final unzippedData = ungzipUint8List(embeddingData); final decodedJson = jsonDecode(utf8.decode(unzippedData)); final RemoteFileML decodedEmbedding = RemoteFileML.fromRemote( diff --git a/mobile/lib/services/machine_learning/ml_service.dart b/mobile/lib/services/machine_learning/ml_service.dart index 800ad07502..96cf021515 100644 --- a/mobile/lib/services/machine_learning/ml_service.dart +++ b/mobile/lib/services/machine_learning/ml_service.dart @@ -469,24 +469,26 @@ class MLService { if (!result.errorOccured) { await RemoteFileMLService.instance.putFileEmbedding( instruction.enteFile, - RemoteFileML( - instruction.enteFile.uploadedFileID!, - {}, - faceEmbedding: RemoteFaceEmbedding( - faces, - result.mlVersion, - client: client, - height: result.decodedImageSize.height, - width: result.decodedImageSize.width, - ), - clipEmbedding: result.clipRan - ? RemoteClipEmbedding( - result.clip!.embedding, - version: result.mlVersion, - client: client, - ) - : null, - ), + instruction.existingRemoteFileML ?? + RemoteFileML.empty( + instruction.enteFile.uploadedFileID!, + ), + faceEmbedding: result.facesRan + ? RemoteFaceEmbedding( + faces, + result.mlVersion, + client: client, + height: result.decodedImageSize.height, + width: result.decodedImageSize.width, + ) + : null, + clipEmbedding: result.clipRan + ? RemoteClipEmbedding( + result.clip!.embedding, + version: result.mlVersion, + client: client, + ) + : null, ); } else { _logger.warning( diff --git a/mobile/lib/utils/ml_util.dart b/mobile/lib/utils/ml_util.dart index 46feaca3c6..f70a1b7cc2 100644 --- a/mobile/lib/utils/ml_util.dart +++ b/mobile/lib/utils/ml_util.dart @@ -32,12 +32,14 @@ class FileMLInstruction { final bool shouldRunFaces; final bool shouldRunClip; + RemoteFileML? existingRemoteFileML; FileMLInstruction({ required this.enteFile, required this.shouldRunFaces, required this.shouldRunClip, }); + } Future getIndexStatus() async {