From f990863bb2f287fa3f11b8001b836684f015d900 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 4 Sep 2024 10:44:32 +0530 Subject: [PATCH] Retain that info --- .../new/photos/services/ml/cluster.ts | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/web/packages/new/photos/services/ml/cluster.ts b/web/packages/new/photos/services/ml/cluster.ts index 99ab3d7ee4..24de764f03 100644 --- a/web/packages/new/photos/services/ml/cluster.ts +++ b/web/packages/new/photos/services/ml/cluster.ts @@ -133,6 +133,7 @@ export type OnClusteringProgress = (progress: ClusteringProgress) => void; /** A {@link Face} annotated with data needed during clustering. */ export type ClusterFace = Omit & { embedding: Float32Array; + isBadFace: boolean; }; export interface ClusterPreview { @@ -149,25 +150,17 @@ export interface ClusterPreviewFace { /** * Cluster faces into groups. * - * [Note: Face clustering algorithm] - * * A cgroup (cluster group) consists of clusters, each of which itself is a set * of faces. * * cgroup << cluster << face * - * The clusters are generated locally by clients using the following algorithm: + * This function generates clusters locally using a batched form of linear + * clustering, with a bit of lookback (and a dollop of heuristics) to get the + * clusters to merge across batches. * - * 1. clusters = [] initially, or fetched from remote. - * - * 2. For each face, find its nearest neighbour in the embedding space. - * - * 3. If no such neighbour is found within our threshold, create a new cluster. - * - * 4. Otherwise assign this face to the same cluster as its nearest neighbour. - * - * This user can then tweak the output of the algorithm by performing the - * following actions to the list of clusters that they can see: + * This user can later tweak these clusters by performing the following actions + * to the list of clusters that they can see: * * - They can provide a name for a cluster ("name a person"). This upgrades a * cluster into a "cgroup", which is an entity that gets synced via remote @@ -365,20 +358,39 @@ export const clusterFaces = ( }; /** - * A generator function that returns a stream of {faceID, embedding} values, + * A generator function that returns a stream of {@link ClusterFace}s by * flattening all the the faces present in the given {@link faceIndices}. * - * It also converts the embeddings to Float32Arrays to speed up the dot product - * calculations that will happen during clustering. + * During this, it also converts the embeddings to Float32Arrays to speed up the + * dot product calculations that will happen during clustering and attaches + * other information that the clustering algorithm needs. */ function* enumerateFaces(faceIndices: FaceIndex[]) { for (const fi of faceIndices) { for (const f of fi.faces) { - yield { ...f, embedding: new Float32Array(f.embedding) }; + yield { + ...f, + embedding: new Float32Array(f.embedding), + isBadFace: isBadFace(f), + }; } } } +/** + * Return true if the given face is above the minimum inclusion thresholds, but + * is otherwise heuristically determined to be possibly spurious face detection. + * + * We apply a higher threshold when clustering such faces. + */ +const isBadFace = (face: Face) => + face.blur < 50 || + (face.blur < 200 && face.blur < 0.85) || + isSidewaysFace(face); + +const isSidewaysFace = (face: Face) => + faceDirection(face.detection) != "straight"; + /** Generate a new cluster ID. */ const newClusterID = () => newNonSecureID("cluster_"); @@ -475,17 +487,3 @@ const clusterBatchLinear = ( return state; }; - -/** - * Return true if the given face is above the minimum inclusion thresholds, but - * is otherwise heuristically determined to be possibly spurious face detection. - * - * We apply a higher threshold when clustering such faces. - */ -const isBadFace = (face: Face) => - face.blur < 50 || - (face.blur < 200 && face.blur < 0.85) || - isSidewaysFace(face); - -const isSidewaysFace = (face: Face) => - faceDirection(face.detection) != "straight";