[mob][photos] Precompute face thumbnails for suggestions

This commit is contained in:
laurenspriem 2024-04-24 18:59:08 +05:30
parent 6f6f976dec
commit 3fbfa8c0e6
2 changed files with 85 additions and 4 deletions

View file

@ -1,3 +1,4 @@
import "dart:async" show unawaited;
import "dart:math";
import "package:flutter/foundation.dart" show kDebugMode;
@ -61,8 +62,9 @@ class _PersonClustersState extends State<PersonReviewClusterSuggestion> {
),
);
}
final numberOfDifferentSuggestions = snapshot.data!.length;
final currentSuggestion = snapshot.data![currentSuggestionIndex];
final allSuggestions = snapshot.data!;
final numberOfDifferentSuggestions = allSuggestions.length;
final currentSuggestion = allSuggestions[currentSuggestionIndex];
final int clusterID = currentSuggestion.clusterIDToMerge;
final double distance = currentSuggestion.distancePersonToCluster;
final bool usingMean = currentSuggestion.usedOnlyMeanForSuggestion;
@ -90,6 +92,7 @@ class _PersonClustersState extends State<PersonReviewClusterSuggestion> {
usingMean,
files,
numberOfDifferentSuggestions,
allSuggestions,
),
),
);
@ -155,8 +158,9 @@ class _PersonClustersState extends State<PersonReviewClusterSuggestion> {
bool usingMean,
List<EnteFile> files,
int numberOfSuggestions,
List<ClusterSuggestion> allSuggestions,
) {
return Column(
final widgetToReturn = Column(
key: ValueKey("cluster_id-$clusterID"),
children: <Widget>[
if (kDebugMode)
@ -233,6 +237,28 @@ class _PersonClustersState extends State<PersonReviewClusterSuggestion> {
),
],
);
// Precompute face thumbnails for next suggestions, in case there are
const precompute = 6;
const maxComputations = 10;
int compCount = 0;
if (allSuggestions.length > currentSuggestionIndex + 1) {
for (final suggestion in allSuggestions.sublist(
currentSuggestionIndex + 1,
min(allSuggestions.length, currentSuggestionIndex + precompute),
)) {
final files = suggestion.filesInCluster;
final clusterID = suggestion.clusterIDToMerge;
for (final file in files.sublist(0, min(files.length, 8))) {
unawaited(PersonFaceWidget.precomputeFaceCrops(file, clusterID));
compCount++;
if (compCount >= maxComputations) {
break;
}
}
}
}
return widgetToReturn;
}
List<Widget> _buildThumbnailWidgets(

View file

@ -33,9 +33,64 @@ class PersonFaceWidget extends StatelessWidget {
),
super(key: key);
static Future<void> precomputeFaceCrops(file, clusterID) async {
try {
final Face? face = await FaceMLDataDB.instance.getCoverFaceForPerson(
recentFileID: file.uploadedFileID!,
clusterID: clusterID,
);
if (face == null) {
debugPrint(
"No cover face for cluster $clusterID and recentFile ${file.uploadedFileID}",
);
return;
}
final Uint8List? cachedFace = faceCropCache.get(face.faceID);
if (cachedFace != null) {
return;
}
final faceCropCacheFile = cachedFaceCropPath(face.faceID);
if ((await faceCropCacheFile.exists())) {
final data = await faceCropCacheFile.readAsBytes();
faceCropCache.put(face.faceID, data);
return;
}
EnteFile? fileForFaceCrop = file;
if (face.fileID != file.uploadedFileID!) {
fileForFaceCrop =
await FilesDB.instance.getAnyUploadedFile(face.fileID);
}
if (fileForFaceCrop == null) {
return;
}
final result = await pool.withResource(
() async => await getFaceCrops(
fileForFaceCrop!,
{
face.faceID: face.detection.box,
},
),
);
final Uint8List? computedCrop = result?[face.faceID];
if (computedCrop != null) {
faceCropCache.put(face.faceID, computedCrop);
faceCropCacheFile.writeAsBytes(computedCrop).ignore();
}
return;
} catch (e, s) {
log(
"Error getting cover face for cluster $clusterID",
error: e,
stackTrace: s,
);
return;
}
}
@override
Widget build(BuildContext context) {
if (useGeneratedFaceCrops) {
if (!useGeneratedFaceCrops) {
return FutureBuilder<Uint8List?>(
future: getFaceCrop(),
builder: (context, snapshot) {