diff --git a/web/apps/photos/src/pages/cluster-debug.tsx b/web/apps/photos/src/pages/cluster-debug.tsx index e5f8148fbc..1f6a1036f6 100644 --- a/web/apps/photos/src/pages/cluster-debug.tsx +++ b/web/apps/photos/src/pages/cluster-debug.tsx @@ -70,7 +70,7 @@ export default function ClusterDebug() { minBlur: 10, minScore: 0.8, minClusterSize: 2, - joinThreshold: 0.6, + joinThreshold: 0.76, earlyExitThreshold: 0.9, batchSize: 10000, offsetIncrement: 7500, @@ -344,7 +344,7 @@ const ClusterList: React.FC> = ({ index === 0 ? 140 : index === 1 - ? 130 + ? 110 : Array.isArray(items[index - 2]) ? listItemHeight : 36; @@ -466,13 +466,10 @@ const ClusterResHeader: React.FC = ({ clusterRes }) => { For each cluster showing only up to 50 faces, sorted by cosine - similarity to highest scoring face in the cluster. - - - Below each face is its{" "} - blur - score - cosineSimilarity - direction. + similarity to its highest scoring face. + Below each face is its blur, score, cosineSimilarity, direction. Bad faces are outlined. @@ -544,7 +541,7 @@ const FaceItem: React.FC = ({ faceWithFile }) => { return ( diff --git a/web/packages/new/photos/services/ml/cluster.ts b/web/packages/new/photos/services/ml/cluster.ts index 18a9b52151..e4928aa991 100644 --- a/web/packages/new/photos/services/ml/cluster.ts +++ b/web/packages/new/photos/services/ml/cluster.ts @@ -195,6 +195,7 @@ export const clusterFaces = ( earlyExitThreshold, batchSize, offsetIncrement, + badFaceHeuristics, } = opts; const t = Date.now(); @@ -256,6 +257,7 @@ export const clusterFaces = ( oldState, joinThreshold, earlyExitThreshold, + badFaceHeuristics, ({ completed }: ClusteringProgress) => onProgress({ completed: offset + completed, @@ -432,6 +434,7 @@ const clusterBatchLinear = ( oldState: ClusteringState, joinThreshold: number, earlyExitThreshold: number, + badFaceHeuristics: boolean, onProgress: (progress: ClusteringProgress) => void, ) => { const state: ClusteringState = { @@ -454,7 +457,7 @@ const clusterBatchLinear = ( // Find the nearest neighbour among the previous faces in this batch. let nnIndex: number | undefined; - let nnCosineSimilarity = joinThreshold; + let nnCosineSimilarity = 0; for (let j = i - 1; j >= 0; j--) { // ! This is an O(n^2) loop, be careful when adding more code here. @@ -464,13 +467,15 @@ const clusterBatchLinear = ( // The vectors are already normalized, so we can directly use their // dot product as their cosine similarity. const csim = dotProduct(fi.embedding, fj.embedding); - if (csim > nnCosineSimilarity) { + const threshold = + badFaceHeuristics && fj.isBadFace ? 0.84 : joinThreshold; + if (csim > nnCosineSimilarity && csim >= threshold) { nnIndex = j; nnCosineSimilarity = csim; // If we've found something "near enough", stop looking for a // better match (A heuristic to speed up clustering). - if (earlyExitThreshold > 0 && csim > earlyExitThreshold) break; + if (earlyExitThreshold > 0 && csim >= earlyExitThreshold) break; } }