Clusterface (#4626)

## Description

Fixed bug where we showed "Face not clustered yet, please come back
later" toast message even for faces which had a score too low to ever be
clustered automatically.
This commit is contained in:
Laurens Priem 2025-01-08 07:02:45 +01:00 committed by GitHub
commit 50c65125a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 27 additions and 20 deletions

View File

@ -604,8 +604,6 @@ class MLDataDB {
}
Future<List<FaceDbInfoForClustering>> getFaceInfoForClustering({
double minScore = kMinimumQualityFaceScore,
int minClarity = kLaplacianHardThreshold,
int maxFaces = 20000,
int offset = 0,
int batchSize = 10000,
@ -622,7 +620,7 @@ class MLDataDB {
// Query a batch of rows
final List<Map<String, dynamic>> maps = await db.getAll(
'SELECT $faceIDColumn, $embeddingColumn, $faceScore, $faceBlur, $isSideways FROM $facesTable'
' WHERE $faceScore > $minScore AND $faceBlur > $minClarity'
' WHERE $faceScore > $kMinimumQualityFaceScore AND $faceBlur > $kLaplacianHardThreshold'
' ORDER BY $faceIDColumn'
' DESC LIMIT $batchSize OFFSET $offset',
);
@ -698,12 +696,10 @@ class MLDataDB {
return result;
}
Future<int> getTotalFaceCount({
double minFaceScore = kMinimumQualityFaceScore,
}) async {
Future<int> getTotalFaceCount() async {
final db = await instance.asyncDB;
final List<Map<String, dynamic>> maps = await db.getAll(
'SELECT COUNT(*) as count FROM $facesTable WHERE $faceScore > $minFaceScore AND $faceBlur > $kLaplacianHardThreshold',
'SELECT COUNT(*) as count FROM $facesTable WHERE $faceScore > $kMinimumQualityFaceScore AND $faceBlur > $kLaplacianHardThreshold',
);
return maps.first['count'] as int;
}

View File

@ -17,7 +17,6 @@ import "package:photos/services/filedata/filedata_service.dart";
import "package:photos/services/filedata/model/file_data.dart";
import 'package:photos/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart';
import "package:photos/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart";
import 'package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart';
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
import "package:photos/services/machine_learning/ml_indexing_isolate.dart";
import 'package:photos/services/machine_learning/ml_result.dart';
@ -238,10 +237,7 @@ class MLService {
}
}
Future<void> clusterAllImages({
double minFaceScore = kMinimumQualityFaceScore,
bool clusterInBuckets = true,
}) async {
Future<void> clusterAllImages({bool clusterInBuckets = true}) async {
if (_cannotRunMLFunction()) return;
_logger.info("`clusterAllImages()` called");
@ -269,13 +265,12 @@ class MLService {
// Get a sense of the total number of faces in the database
final int totalFaces =
await MLDataDB.instance.getTotalFaceCount(minFaceScore: minFaceScore);
await MLDataDB.instance.getTotalFaceCount();
final fileIDToCreationTime =
await FilesDB.instance.getFileIDToCreationTime();
final startEmbeddingFetch = DateTime.now();
// read all embeddings
final result = await MLDataDB.instance.getFaceInfoForClustering(
minScore: minFaceScore,
maxFaces: totalFaces,
);
final Set<int> missingFileIDs = {};

View File

@ -5,12 +5,13 @@ import "package:flutter/cupertino.dart";
import "package:flutter/foundation.dart" show kDebugMode;
import "package:flutter/material.dart";
import "package:photos/db/ml/db.dart";
import "package:photos/extensions/stop_watch.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/base/id.dart";
import 'package:photos/models/file/file.dart';
import "package:photos/models/ml/face/face.dart";
import "package:photos/models/ml/face/person.dart";
import "package:photos/services/machine_learning/face_ml/face_detection/detection.dart";
import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart";
import "package:photos/services/machine_learning/face_ml/feedback/cluster_feedback.dart";
import "package:photos/services/search_service.dart";
import "package:photos/theme/ente_theme.dart";
@ -68,16 +69,13 @@ class _FaceWidgetState extends State<FaceWidget> {
if (widget.editMode) return;
log(
"FaceWidget is tapped, with person ${widget.person} and clusterID ${widget.clusterID}",
"FaceWidget is tapped, with person ${widget.person?.data.name} and clusterID ${widget.clusterID}",
name: "FaceWidget",
);
if (widget.person == null && widget.clusterID == null) {
// Get faceID and double check that it doesn't belong to an existing clusterID. If it does, push that cluster page
final w = (kDebugMode ? EnteWatch('FaceWidget') : null)
?..start();
// Double check that it doesn't belong to an existing clusterID.
final existingClusterID = await MLDataDB.instance
.getClusterIDForFaceID(widget.face.faceID);
w?.log('getting existing clusterID for faceID');
if (existingClusterID != null) {
final fileIdsToClusterIds =
await MLDataDB.instance.getFileIdToClusterIds();
@ -99,6 +97,24 @@ class _FaceWidgetState extends State<FaceWidget> {
),
),
);
return;
}
if (widget.face.score <= kMinimumQualityFaceScore) {
// The face score is too low for automatic clustering,
// assigning a manual new clusterID so that the user can cluster it manually
final String clusterID = newClusterID();
await MLDataDB.instance.updateFaceIdToClusterId(
{widget.face.faceID: clusterID},
);
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ClusterPage(
[widget.file],
clusterID: clusterID,
),
),
);
return;
}
showShortToast(