[mob][photos] Only decode image once for face thumbnails in file info
This commit is contained in:
parent
8b1545239c
commit
1cd31d2cab
2 changed files with 73 additions and 8 deletions
|
@ -26,6 +26,7 @@ const useGeneratedFaceCrops = true;
|
|||
class FaceWidget extends StatefulWidget {
|
||||
final EnteFile file;
|
||||
final Face face;
|
||||
final Future<Map<String, Uint8List>?>? faceCrops;
|
||||
final PersonEntity? person;
|
||||
final int? clusterID;
|
||||
final bool highlight;
|
||||
|
@ -34,6 +35,7 @@ class FaceWidget extends StatefulWidget {
|
|||
const FaceWidget(
|
||||
this.file,
|
||||
this.face, {
|
||||
this.faceCrops,
|
||||
this.person,
|
||||
this.clusterID,
|
||||
this.highlight = false,
|
||||
|
@ -50,12 +52,13 @@ class _FaceWidgetState extends State<FaceWidget> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool givenFaces = widget.faceCrops != null;
|
||||
if (useGeneratedFaceCrops) {
|
||||
return FutureBuilder<Uint8List?>(
|
||||
future: getFaceCrop(),
|
||||
return FutureBuilder<Map<String, Uint8List>?>(
|
||||
future: givenFaces ? widget.faceCrops : getFaceCrop(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final ImageProvider imageProvider = MemoryImage(snapshot.data!);
|
||||
final ImageProvider imageProvider = MemoryImage(snapshot.data![widget.face.faceID]!);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
|
@ -460,17 +463,17 @@ class _FaceWidgetState extends State<FaceWidget> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Uint8List?> getFaceCrop() async {
|
||||
Future<Map<String, Uint8List>?> getFaceCrop() async {
|
||||
try {
|
||||
final Uint8List? cachedFace = faceCropCache.get(widget.face.faceID);
|
||||
if (cachedFace != null) {
|
||||
return cachedFace;
|
||||
return {widget.face.faceID: cachedFace};
|
||||
}
|
||||
final faceCropCacheFile = cachedFaceCropPath(widget.face.faceID);
|
||||
if ((await faceCropCacheFile.exists())) {
|
||||
final data = await faceCropCacheFile.readAsBytes();
|
||||
faceCropCache.put(widget.face.faceID, data);
|
||||
return data;
|
||||
return {widget.face.faceID: data};
|
||||
}
|
||||
|
||||
final result = await pool.withResource(
|
||||
|
@ -486,7 +489,7 @@ class _FaceWidgetState extends State<FaceWidget> {
|
|||
faceCropCache.put(widget.face.faceID, computedCrop);
|
||||
faceCropCacheFile.writeAsBytes(computedCrop).ignore();
|
||||
}
|
||||
return computedCrop;
|
||||
return {widget.face.faceID: computedCrop!};
|
||||
} catch (e, s) {
|
||||
log(
|
||||
"Error getting face for faceID: ${widget.face.faceID}",
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import "package:flutter/foundation.dart" show kDebugMode;
|
||||
import "dart:developer" as dev show log;
|
||||
|
||||
import "package:flutter/foundation.dart" show Uint8List, kDebugMode;
|
||||
import "package:flutter/material.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/face/db.dart";
|
||||
import "package:photos/face/model/box.dart";
|
||||
import "package:photos/face/model/face.dart";
|
||||
import "package:photos/face/model/person.dart";
|
||||
import "package:photos/models/file/file.dart";
|
||||
|
@ -10,6 +13,8 @@ import "package:photos/services/machine_learning/face_ml/person/person_service.d
|
|||
import "package:photos/ui/components/buttons/chip_button_widget.dart";
|
||||
import "package:photos/ui/components/info_item_widget.dart";
|
||||
import "package:photos/ui/viewer/file_details/face_widget.dart";
|
||||
import "package:photos/utils/face/face_box_crop.dart";
|
||||
import "package:photos/utils/thumbnail_util.dart";
|
||||
|
||||
class FacesItemWidget extends StatefulWidget {
|
||||
final EnteFile file;
|
||||
|
@ -119,6 +124,7 @@ class _FacesItemWidgetState extends State<FacesItemWidget> {
|
|||
final lastViewedClusterID = ClusterFeedbackService.lastViewedClusterID;
|
||||
|
||||
final faceWidgets = <FaceWidget>[];
|
||||
final faceCrops = getRelevantFaceCrops(faces);
|
||||
for (final Face face in faces) {
|
||||
final int? clusterID = faceIdsToClusterIds[face.faceID];
|
||||
final PersonEntity? person = clusterIDToPerson[clusterID] != null
|
||||
|
@ -130,6 +136,7 @@ class _FacesItemWidgetState extends State<FacesItemWidget> {
|
|||
FaceWidget(
|
||||
file,
|
||||
face,
|
||||
faceCrops: faceCrops,
|
||||
clusterID: clusterID,
|
||||
person: person,
|
||||
highlight: highlight,
|
||||
|
@ -144,4 +151,59 @@ class _FacesItemWidgetState extends State<FacesItemWidget> {
|
|||
return <FaceWidget>[];
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, Uint8List>?> getRelevantFaceCrops(
|
||||
Iterable<Face> faces,
|
||||
) async {
|
||||
try {
|
||||
final faceIdToCrop = <String, Uint8List>{};
|
||||
final facesWithoutCrops = <String, FaceBox>{};
|
||||
for (final face in faces) {
|
||||
final Uint8List? cachedFace = faceCropCache.get(face.faceID);
|
||||
if (cachedFace != null) {
|
||||
faceIdToCrop[face.faceID] = cachedFace;
|
||||
} else {
|
||||
final faceCropCacheFile = cachedFaceCropPath(face.faceID);
|
||||
if ((await faceCropCacheFile.exists())) {
|
||||
final data = await faceCropCacheFile.readAsBytes();
|
||||
faceCropCache.put(face.faceID, data);
|
||||
faceIdToCrop[face.faceID] = data;
|
||||
} else {
|
||||
facesWithoutCrops[face.faceID] = face.detection.box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (facesWithoutCrops.isEmpty) {
|
||||
return faceIdToCrop;
|
||||
}
|
||||
|
||||
final result = await pool.withResource(
|
||||
() async => await getFaceCrops(
|
||||
widget.file,
|
||||
facesWithoutCrops,
|
||||
),
|
||||
);
|
||||
if (result == null) {
|
||||
return (faceIdToCrop.isEmpty) ? null : faceIdToCrop;
|
||||
}
|
||||
for (final entry in result.entries) {
|
||||
final Uint8List? computedCrop = result[entry.key];
|
||||
if (computedCrop != null) {
|
||||
faceCropCache.put(entry.key, computedCrop);
|
||||
final faceCropCacheFile = cachedFaceCropPath(entry.key);
|
||||
faceCropCacheFile.writeAsBytes(computedCrop).ignore();
|
||||
faceIdToCrop[entry.key] = computedCrop;
|
||||
}
|
||||
}
|
||||
return (faceIdToCrop.isEmpty) ? null : faceIdToCrop;
|
||||
} catch (e, s) {
|
||||
dev.log(
|
||||
"Error getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()}",
|
||||
error: e,
|
||||
stackTrace: s,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue