Merge remote-tracking branch 'origin/mobile_face' into mobile_face
This commit is contained in:
commit
afa8a372d2
6 changed files with 1268 additions and 33 deletions
|
@ -78,6 +78,37 @@ class FaceMLDataDB {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> updatePersonIDForFaceIDIFNotSet(
|
||||
Map<String, int> faceIDToPersonID,
|
||||
) async {
|
||||
final db = await instance.database;
|
||||
const batchSize = 500;
|
||||
final numBatches = (faceIDToPersonID.length / batchSize).ceil();
|
||||
|
||||
for (int i = 0; i < numBatches; i++) {
|
||||
_logger.info('updatePersonIDForFaceIDIFNotSet Batch $i of $numBatches');
|
||||
final start = i * batchSize;
|
||||
final end = min((i + 1) * batchSize, faceIDToPersonID.length);
|
||||
final batch = faceIDToPersonID.entries.toList().sublist(start, end);
|
||||
|
||||
final batchUpdate = db.batch();
|
||||
|
||||
for (final entry in batch) {
|
||||
final faceID = entry.key;
|
||||
final personID = entry.value;
|
||||
|
||||
batchUpdate.update(
|
||||
facesTable,
|
||||
{faceClusterId: personID},
|
||||
where: '$faceIDColumn = ? AND $faceClusterId IS NULL',
|
||||
whereArgs: [faceID],
|
||||
);
|
||||
}
|
||||
|
||||
await batchUpdate.commit(noResult: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<int, int>> getIndexedFileIds() async {
|
||||
final db = await instance.database;
|
||||
final List<Map<String, dynamic>> maps = await db.rawQuery(
|
||||
|
@ -293,28 +324,6 @@ class FaceMLDataDB {
|
|||
return result;
|
||||
}
|
||||
|
||||
Future<void> updatePersonIDForFaceIDIFNotSet(
|
||||
Map<String, int> faceIDToPersonID,
|
||||
) async {
|
||||
final db = await instance.database;
|
||||
|
||||
// Start a batch
|
||||
final batch = db.batch();
|
||||
|
||||
for (final map in faceIDToPersonID.entries) {
|
||||
final faceID = map.key;
|
||||
final personID = map.value;
|
||||
batch.update(
|
||||
facesTable,
|
||||
{faceClusterId: personID},
|
||||
where: '$faceIDColumn = ? AND $faceClusterId IS NULL',
|
||||
whereArgs: [faceID],
|
||||
);
|
||||
}
|
||||
// Commit the batch
|
||||
await batch.commit(noResult: true);
|
||||
}
|
||||
|
||||
Future<void> forceUpdateClusterIds(
|
||||
Map<String, int> faceIDToPersonID,
|
||||
) async {
|
||||
|
@ -347,7 +356,9 @@ class FaceMLDataDB {
|
|||
int offset = 0,
|
||||
int batchSize = 10000,
|
||||
}) async {
|
||||
_logger.info('reading as float');
|
||||
_logger.info(
|
||||
'reading as float offset: $offset, maxFaces: $maxFaces, batchSize: $batchSize',
|
||||
);
|
||||
final db = await instance.database;
|
||||
|
||||
final Map<String, (int?, Uint8List)> result = {};
|
||||
|
|
1209
mobile/lib/services/face_ml/face_ml_service.dart
Normal file
1209
mobile/lib/services/face_ml/face_ml_service.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,7 @@ import "package:photos/db/files_db.dart";
|
|||
import "package:photos/db/ml_data_db.dart";
|
||||
import "package:photos/events/diff_sync_complete_event.dart";
|
||||
import "package:photos/extensions/list.dart";
|
||||
import "package:photos/extensions/stop_watch.dart";
|
||||
import "package:photos/face/db.dart";
|
||||
import "package:photos/face/model/box.dart";
|
||||
import "package:photos/face/model/detection.dart" as face_detection;
|
||||
|
@ -120,7 +121,12 @@ class FaceMlService {
|
|||
if (LocalSettings.instance.isFaceIndexingEnabled == false) {
|
||||
return;
|
||||
}
|
||||
unawaited(indexAllImages());
|
||||
// [neeraj] intentional delay in starting indexing on diff sync, this gives time for the user
|
||||
// to disable face-indexing in case it's causing crash. In the future, we
|
||||
// should have a better way to handle this.
|
||||
Future.delayed(const Duration(seconds: 10), () {
|
||||
unawaited(indexAllImages());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -380,8 +386,8 @@ class FaceMlService {
|
|||
await FilesDB.instance.getFileIDToCreationTime();
|
||||
|
||||
const int bucketSize = 10000;
|
||||
const int batchSize = 10000;
|
||||
const int offsetIncrement = 7500;
|
||||
const int batchSize = 5000;
|
||||
int offset = 0;
|
||||
|
||||
while (true) {
|
||||
|
@ -413,8 +419,11 @@ class FaceMlService {
|
|||
|
||||
await FaceMLDataDB.instance
|
||||
.updatePersonIDForFaceIDIFNotSet(faceIdToCluster);
|
||||
|
||||
offset += offsetIncrement;
|
||||
if (offset == 0) {
|
||||
offset += offsetIncrement;
|
||||
} else {
|
||||
offset += bucketSize;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Read all the embeddings from the database, in a map from faceID to embedding
|
||||
|
@ -518,8 +527,11 @@ class FaceMlService {
|
|||
fileIds.add(f.uploadedFileID!);
|
||||
}
|
||||
try {
|
||||
final EnteWatch? w = kDebugMode ? EnteWatch("face_em_fetch") : null;
|
||||
w?.start();
|
||||
final res =
|
||||
await RemoteFileMLService.instance.getFilessEmbedding(fileIds);
|
||||
w?.logAndReset('fetched ${res.mlData.length} embeddings');
|
||||
final List<Face> faces = [];
|
||||
final remoteFileIdToVersion = <int, int>{};
|
||||
for (FileMl fileMl in res.mlData.values) {
|
||||
|
@ -537,6 +549,7 @@ class FaceMlService {
|
|||
remoteFileIdToVersion[fileMl.fileID] = fileMl.faceEmbedding.version;
|
||||
}
|
||||
await FaceMLDataDB.instance.bulkInsertFaces(faces);
|
||||
w?.logAndReset('stored embeddings');
|
||||
for (final entry in remoteFileIdToVersion.entries) {
|
||||
alreadyIndexedFiles[entry.key] = entry.value;
|
||||
}
|
||||
|
@ -1242,7 +1255,6 @@ class FaceMlService {
|
|||
if (!enteFile.isUploaded || enteFile.isOwner == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip if the file is a video
|
||||
if (enteFile.fileType == FileType.video) {
|
||||
return true;
|
||||
|
|
|
@ -79,7 +79,7 @@ class ClusterFeedbackService {
|
|||
final personClusters = await faceMlDb.getPersonClusterIDs(p.remoteID);
|
||||
dev.log(
|
||||
'existing clusters for ${p.attr.name} are $personClusters',
|
||||
name: "ClusterFeedbackService",
|
||||
name: "getSuggestionsUsingMedian",
|
||||
);
|
||||
|
||||
// Get and update the cluster summary to get the avg (centroid) and count
|
||||
|
@ -353,6 +353,9 @@ class ClusterFeedbackService {
|
|||
Set<int> ignoredClusters,
|
||||
) async {
|
||||
final faceMlDb = FaceMLDataDB.instance;
|
||||
_logger.info(
|
||||
'start getUpdateClusterAvg for ${allClusterIdsToCountMap.length} clusters',
|
||||
);
|
||||
|
||||
final Map<int, (Uint8List, int)> clusterToSummary =
|
||||
await faceMlDb.clusterSummaryAll();
|
||||
|
@ -389,6 +392,7 @@ class ClusterFeedbackService {
|
|||
if (updatesForClusterSummary.isNotEmpty) {
|
||||
await faceMlDb.clusterSummaryUpdate(updatesForClusterSummary);
|
||||
}
|
||||
_logger.info('end getUpdateClusterAvg for ${clusterAvg.length} clusters');
|
||||
|
||||
return clusterAvg;
|
||||
}
|
||||
|
|
|
@ -158,7 +158,8 @@ class _FaceDebugSectionWidgetState extends State<FaceDebugSectionWidget> {
|
|||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
await FaceMlService.instance.clusterAllImages(minFaceScore: 0.75);
|
||||
await FaceMlService.instance
|
||||
.clusterAllImages(minFaceScore: 0.75, clusterInBuckets: true);
|
||||
Bus.instance.fire(PeopleChangedEvent());
|
||||
showShortToast(context, "Done");
|
||||
},
|
||||
|
@ -205,7 +206,7 @@ class _FaceDebugSectionWidgetState extends State<FaceDebugSectionWidget> {
|
|||
if (kDebugMode)
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Pull Embeddings From Local",
|
||||
title: "Compute suggestions",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
|
|
|
@ -37,8 +37,6 @@ class _PersonClustersState extends State<PersonReviewClusterSuggestion> {
|
|||
super.initState();
|
||||
// Initialize the future in initState
|
||||
_fetchClusterSuggestions();
|
||||
// futureClusterSuggestions = ClusterFeedbackService.instance
|
||||
// .getClusterFilesForPersonID(widget.person);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Add table
Reference in a new issue