[mob] Use more conservative cluster threshold for sideways faces
This commit is contained in:
parent
2b88daa15f
commit
ecc1bc9980
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import "dart:convert" show json;
|
||||||
import "dart:io" show Directory;
|
import "dart:io" show Directory;
|
||||||
import "dart:math";
|
import "dart:math";
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ import 'package:path_provider/path_provider.dart';
|
||||||
import "package:photos/extensions/stop_watch.dart";
|
import "package:photos/extensions/stop_watch.dart";
|
||||||
import 'package:photos/face/db_fields.dart';
|
import 'package:photos/face/db_fields.dart';
|
||||||
import "package:photos/face/db_model_mappers.dart";
|
import "package:photos/face/db_model_mappers.dart";
|
||||||
|
import "package:photos/face/model/detection.dart";
|
||||||
import "package:photos/face/model/face.dart";
|
import "package:photos/face/model/face.dart";
|
||||||
import "package:photos/models/file/file.dart";
|
import "package:photos/models/file/file.dart";
|
||||||
import "package:photos/services/machine_learning/face_ml/face_clustering/face_info_for_clustering.dart";
|
import "package:photos/services/machine_learning/face_ml/face_clustering/face_info_for_clustering.dart";
|
||||||
|
@ -455,6 +457,31 @@ class FaceMLDataDB {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove this method and replace by correct logic during indexing
|
||||||
|
Future<Map<String, bool>> getFaceIdsToIsSidewaysFaceTEMP() async {
|
||||||
|
final db = await instance.sqliteAsyncDB;
|
||||||
|
final List<Map<String, dynamic>> maps = await db.getAll(
|
||||||
|
'SELECT $faceIDColumn, $faceDetectionColumn FROM $facesTable',
|
||||||
|
);
|
||||||
|
|
||||||
|
final time = DateTime.now();
|
||||||
|
final Map<String, bool> result = {};
|
||||||
|
for (final map in maps) {
|
||||||
|
final faceID = map[faceIDColumn] as String;
|
||||||
|
final detection =
|
||||||
|
Detection.fromJson(json.decode(map[faceDetectionColumn] as String));
|
||||||
|
result[faceID] = detection.faceIsSideways();
|
||||||
|
}
|
||||||
|
_logger.info(
|
||||||
|
'decoding detections and calculating face sideways bools took ${DateTime.now().difference(time).inMilliseconds} ms',
|
||||||
|
);
|
||||||
|
final double sidewaysRatio =
|
||||||
|
result.values.where((e) => e).length / result.length;
|
||||||
|
_logger.info('sideways face ratio: $sidewaysRatio');
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Set<FaceInfoForClustering>> getFaceInfoForClustering({
|
Future<Set<FaceInfoForClustering>> getFaceInfoForClustering({
|
||||||
double minScore = kMinHighQualityFaceScore,
|
double minScore = kMinHighQualityFaceScore,
|
||||||
int minClarity = kLaplacianHardThreshold,
|
int minClarity = kLaplacianHardThreshold,
|
||||||
|
@ -468,6 +495,9 @@ class FaceMLDataDB {
|
||||||
);
|
);
|
||||||
final db = await instance.sqliteAsyncDB;
|
final db = await instance.sqliteAsyncDB;
|
||||||
|
|
||||||
|
final Map<String, bool> faceIdsToIsSideways =
|
||||||
|
await getFaceIdsToIsSidewaysFaceTEMP();
|
||||||
|
|
||||||
final Set<FaceInfoForClustering> result = {};
|
final Set<FaceInfoForClustering> result = {};
|
||||||
while (true) {
|
while (true) {
|
||||||
// Query a batch of rows
|
// Query a batch of rows
|
||||||
|
@ -494,6 +524,7 @@ class FaceMLDataDB {
|
||||||
embeddingBytes: map[faceEmbeddingBlob] as Uint8List,
|
embeddingBytes: map[faceEmbeddingBlob] as Uint8List,
|
||||||
faceScore: map[faceScore] as double,
|
faceScore: map[faceScore] as double,
|
||||||
blurValue: map[faceBlur] as double,
|
blurValue: map[faceBlur] as double,
|
||||||
|
isSideways: faceIdsToIsSideways[faceID] ?? false,
|
||||||
);
|
);
|
||||||
result.add(faceInfo);
|
result.add(faceInfo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ class Detection {
|
||||||
|
|
||||||
bool get isEmpty => box.width == 0 && box.height == 0 && landmarks.isEmpty;
|
bool get isEmpty => box.width == 0 && box.height == 0 && landmarks.isEmpty;
|
||||||
|
|
||||||
// emoty box
|
// empty box
|
||||||
Detection.empty()
|
Detection.empty()
|
||||||
: box = FaceBox(
|
: box = FaceBox(
|
||||||
xMin: 0,
|
xMin: 0,
|
||||||
|
@ -94,6 +94,9 @@ class Detection {
|
||||||
}
|
}
|
||||||
|
|
||||||
FaceDirection getFaceDirection() {
|
FaceDirection getFaceDirection() {
|
||||||
|
if (isEmpty) {
|
||||||
|
return FaceDirection.straight;
|
||||||
|
}
|
||||||
final leftEye = [landmarks[0].x, landmarks[0].y];
|
final leftEye = [landmarks[0].x, landmarks[0].y];
|
||||||
final rightEye = [landmarks[1].x, landmarks[1].y];
|
final rightEye = [landmarks[1].x, landmarks[1].y];
|
||||||
final nose = [landmarks[2].x, landmarks[2].y];
|
final nose = [landmarks[2].x, landmarks[2].y];
|
||||||
|
@ -131,6 +134,9 @@ class Detection {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool faceIsSideways() {
|
bool faceIsSideways() {
|
||||||
|
if (isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final leftEye = [landmarks[0].x, landmarks[0].y];
|
final leftEye = [landmarks[0].x, landmarks[0].y];
|
||||||
final rightEye = [landmarks[1].x, landmarks[1].y];
|
final rightEye = [landmarks[1].x, landmarks[1].y];
|
||||||
final nose = [landmarks[2].x, landmarks[2].y];
|
final nose = [landmarks[2].x, landmarks[2].y];
|
||||||
|
|
|
@ -452,7 +452,8 @@ class FaceClusteringService {
|
||||||
faceScore: face.faceScore,
|
faceScore: face.faceScore,
|
||||||
blurValue: face.blurValue,
|
blurValue: face.blurValue,
|
||||||
badFace: face.faceScore < kMinHighQualityFaceScore ||
|
badFace: face.faceScore < kMinHighQualityFaceScore ||
|
||||||
face.blurValue < kLaplacianSoftThreshold,
|
face.blurValue < kLaplacianSoftThreshold ||
|
||||||
|
face.isSideways,
|
||||||
vEmbedding: Vector.fromList(
|
vEmbedding: Vector.fromList(
|
||||||
EVector.fromBuffer(face.embeddingBytes).values,
|
EVector.fromBuffer(face.embeddingBytes).values,
|
||||||
dtype: DType.float32,
|
dtype: DType.float32,
|
||||||
|
@ -606,7 +607,7 @@ class FaceClusteringService {
|
||||||
);
|
);
|
||||||
if (useDynamicThreshold) {
|
if (useDynamicThreshold) {
|
||||||
log(
|
log(
|
||||||
"[ClusterIsolate] ${DateTime.now()} Dynamic thresholding: $dynamicThresholdCount faces had a low face score or high blur value",
|
"[ClusterIsolate] ${DateTime.now()} Dynamic thresholding: $dynamicThresholdCount faces had a low face score or low blur clarity",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import "dart:typed_data" show Uint8List;
|
import "dart:typed_data" show Uint8List;
|
||||||
|
|
||||||
class FaceInfoForClustering {
|
class FaceInfoForClustering {
|
||||||
|
@ -7,6 +6,7 @@ class FaceInfoForClustering {
|
||||||
final Uint8List embeddingBytes;
|
final Uint8List embeddingBytes;
|
||||||
final double faceScore;
|
final double faceScore;
|
||||||
final double blurValue;
|
final double blurValue;
|
||||||
|
final bool isSideways;
|
||||||
|
|
||||||
FaceInfoForClustering({
|
FaceInfoForClustering({
|
||||||
required this.faceID,
|
required this.faceID,
|
||||||
|
@ -14,5 +14,6 @@ class FaceInfoForClustering {
|
||||||
required this.embeddingBytes,
|
required this.embeddingBytes,
|
||||||
required this.faceScore,
|
required this.faceScore,
|
||||||
required this.blurValue,
|
required this.blurValue,
|
||||||
|
this.isSideways = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue