nc-photos/app/lib/entity/person/content_provider/recognize.dart
2023-07-22 22:27:54 +08:00

111 lines
3 KiB
Dart

import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/entity/person.dart';
import 'package:nc_photos/entity/recognize_face.dart';
import 'package:nc_photos/entity/recognize_face_item.dart';
import 'package:nc_photos/object_extension.dart';
import 'package:to_string/to_string.dart';
part 'recognize.g.dart';
@toString
class PersonRecognizeProvider
with EquatableMixin
implements PersonContentProvider {
PersonRecognizeProvider({
required this.account,
required this.face,
List<RecognizeFaceItem>? items,
}) : items = items
?.sorted((a, b) => b.fileId.compareTo(a.fileId))
.reversed
.toList();
@override
String toString() => _$toString();
@override
String get fourCc => "RCNZ";
@override
String get id => face.label;
@override
int? get count => items?.length;
@override
String? getCoverUrl(
int width,
int height, {
bool? isKeepAspectRatio,
}) =>
items?.firstOrNull?.run((i) => api_util.getFilePreviewUrl(
account,
i.toFile(),
width: width,
height: height,
isKeepAspectRatio: isKeepAspectRatio ?? false,
));
@override
Matrix4? getCoverTransform(int viewportSize, int imgW, int imgH) {
final detection = items?.firstOrNull?.faceDetections
?.firstWhereOrNull((e) => e["title"] == face.label);
if (detection == null) {
return null;
}
final faceXNorm = (detection["x"] as Object?).as<double>();
final faceYNorm = (detection["y"] as Object?).as<double>();
final faceHNorm = (detection["height"] as Object?).as<double>();
final faceWNorm = (detection["width"] as Object?).as<double>();
if (faceXNorm == null ||
faceYNorm == null ||
faceHNorm == null ||
faceWNorm == null) {
return null;
}
// move image to the face
double mx = imgW * -faceXNorm;
double my = imgH * -faceYNorm;
// add offset in case image is not a square
if (imgW > imgH) {
mx += (imgW - imgH) / 2;
} else if (imgH > imgW) {
my += (imgH - imgW) / 2;
}
// scale image to focus on the face
final faceW = imgW * faceWNorm;
final faceH = imgH * faceHNorm;
double ms;
if (faceW > faceH) {
ms = viewportSize / faceW;
} else {
ms = viewportSize / faceH;
}
// slightly scale down to include pixels around the face
ms *= .75;
// center the scaled image
final resultFaceW = faceW * ms;
final resultFaceH = faceH * ms;
final cx = (viewportSize - resultFaceW) / 2;
final cy = (viewportSize - resultFaceH) / 2;
return Matrix4.identity()
..translate(cx, cy)
..scale(ms)
..translate(mx, my);
}
@override
List<Object?> get props => [account, face, items];
final Account account;
final RecognizeFace face;
final List<RecognizeFaceItem>? items;
}