浏览代码

[mob][photos] Automatically reject overlapping suggestions

laurenspriem 1 年之前
父节点
当前提交
43cbfbfa33
共有 1 个文件被更改,包括 29 次插入6 次删除
  1. 29 6
      mobile/lib/services/machine_learning/face_ml/feedback/cluster_feedback.dart

+ 29 - 6
mobile/lib/services/machine_learning/face_ml/feedback/cluster_feedback.dart

@@ -98,10 +98,10 @@ class ClusterFeedbackService {
         }
         }
       }
       }
 
 
-      final List<ClusterSuggestion> clusterIdAndFiles = [];
+      final List<ClusterSuggestion> finalSuggestions = [];
       for (final clusterSuggestion in foundSuggestions) {
       for (final clusterSuggestion in foundSuggestions) {
         if (clusterIDToFiles.containsKey(clusterSuggestion.$1)) {
         if (clusterIDToFiles.containsKey(clusterSuggestion.$1)) {
-          clusterIdAndFiles.add(
+          finalSuggestions.add(
             ClusterSuggestion(
             ClusterSuggestion(
               clusterSuggestion.$1,
               clusterSuggestion.$1,
               clusterSuggestion.$2,
               clusterSuggestion.$2,
@@ -116,13 +116,13 @@ class ClusterFeedbackService {
 
 
       final sortingStartTime = DateTime.now();
       final sortingStartTime = DateTime.now();
       if (extremeFilesFirst) {
       if (extremeFilesFirst) {
-        await _sortSuggestionsOnDistanceToPerson(person, clusterIdAndFiles);
+        await _sortSuggestionsOnDistanceToPerson(person, finalSuggestions);
       }
       }
       _logger.info(
       _logger.info(
         'getSuggestionForPerson post-processing suggestions took ${DateTime.now().difference(findSuggestionsTime).inMilliseconds} ms, of which sorting took ${DateTime.now().difference(sortingStartTime).inMilliseconds} ms and getting files took ${getFilesTime.difference(findSuggestionsTime).inMilliseconds} ms',
         'getSuggestionForPerson post-processing suggestions took ${DateTime.now().difference(findSuggestionsTime).inMilliseconds} ms, of which sorting took ${DateTime.now().difference(sortingStartTime).inMilliseconds} ms and getting files took ${getFilesTime.difference(findSuggestionsTime).inMilliseconds} ms',
       );
       );
 
 
-      return clusterIdAndFiles;
+      return finalSuggestions;
     } catch (e, s) {
     } catch (e, s) {
       _logger.severe("Error in getClusterFilesForPersonID", e, s);
       _logger.severe("Error in getClusterFilesForPersonID", e, s);
       rethrow;
       rethrow;
@@ -463,9 +463,15 @@ class ClusterFeedbackService {
     final allClusterIdsToCountMap = await faceMlDb.clusterIdToFaceCount();
     final allClusterIdsToCountMap = await faceMlDb.clusterIdToFaceCount();
     final ignoredClusters = await faceMlDb.getPersonIgnoredClusters(p.remoteID);
     final ignoredClusters = await faceMlDb.getPersonIgnoredClusters(p.remoteID);
     final personClusters = await faceMlDb.getPersonClusterIDs(p.remoteID);
     final personClusters = await faceMlDb.getPersonClusterIDs(p.remoteID);
+    final personFaceIDs =
+        await FaceMLDataDB.instance.getFaceIDsForPerson(p.remoteID);
+    final personFileIDs = personFaceIDs.map(getFileIdFromFaceId).toSet();
     w?.log(
     w?.log(
       '${p.data.name} has ${personClusters.length} existing clusters, getting all database data done',
       '${p.data.name} has ${personClusters.length} existing clusters, getting all database data done',
     );
     );
+    final allClusterIdToFaceIDs =
+        await FaceMLDataDB.instance.getAllClusterIdToFaceIDs();
+    w?.log('getAllClusterIdToFaceIDs done');
 
 
     // First only do a simple check on the big clusters, if the person does not have small clusters yet
     // First only do a simple check on the big clusters, if the person does not have small clusters yet
     final smallestPersonClusterSize = personClusters
     final smallestPersonClusterSize = personClusters
@@ -473,6 +479,7 @@ class ClusterFeedbackService {
         .reduce((value, element) => min(value, element));
         .reduce((value, element) => min(value, element));
     final checkSizes = [kMinimumClusterSizeSearchResult, 20, 10, 5, 1];
     final checkSizes = [kMinimumClusterSizeSearchResult, 20, 10, 5, 1];
     late Map<int, Vector> clusterAvgBigClusters;
     late Map<int, Vector> clusterAvgBigClusters;
+    final List<(int, double)> suggestionsMean = [];
     for (final minimumSize in checkSizes.toSet()) {
     for (final minimumSize in checkSizes.toSet()) {
       // if (smallestPersonClusterSize >= minimumSize) {
       // if (smallestPersonClusterSize >= minimumSize) {
       clusterAvgBigClusters = await _getUpdateClusterAvg(
       clusterAvgBigClusters = await _getUpdateClusterAvg(
@@ -493,8 +500,24 @@ class ClusterFeedbackService {
       w?.log(
       w?.log(
         'Calculate suggestions using mean for ${clusterAvgBigClusters.length} clusters of min size $minimumSize',
         'Calculate suggestions using mean for ${clusterAvgBigClusters.length} clusters of min size $minimumSize',
       );
       );
-      if (suggestionsMeanBigClusters.isNotEmpty) {
-        return suggestionsMeanBigClusters
+      for (final suggestion in suggestionsMeanBigClusters) {
+        // Skip suggestions that have a high overlap with the person's files
+        final suggestionSet = allClusterIdToFaceIDs[suggestion.$1]!
+            .map((faceID) => getFileIdFromFaceId(faceID))
+            .toSet();
+        final overlap = personFileIDs.intersection(suggestionSet);
+        if (overlap.isNotEmpty &&
+            ((overlap.length / suggestionSet.length) > 0.5)) {
+          await FaceMLDataDB.instance.captureNotPersonFeedback(
+            personID: p.remoteID,
+            clusterID: suggestion.$1,
+          );
+          continue;
+        }
+        suggestionsMean.add(suggestion);
+      }
+      if (suggestionsMean.isNotEmpty) {
+        return suggestionsMean
             .map((e) => (e.$1, e.$2, true))
             .map((e) => (e.$1, e.$2, true))
             .toList(growable: false);
             .toList(growable: false);
         // }
         // }