diff --git a/web/packages/new/photos/services/ml/cluster-new.ts b/web/packages/new/photos/services/ml/cluster-new.ts index e49db72385..df97ca4bd9 100644 --- a/web/packages/new/photos/services/ml/cluster-new.ts +++ b/web/packages/new/photos/services/ml/cluster-new.ts @@ -525,34 +525,56 @@ export const clusterFacesHdb = async (faceIndexes: FaceIndex[]) => { // TODO-Cluster this is likely not needed since hdbscan already has a min? const validClusters = clusters.filter(({ faceIDs }) => faceIDs.length > 1); - let cgroups = await clusterGroups(); + // let cgroups = await clusterGroups(); + + // // TODO-Cluster - Currently we're not syncing with remote or saving anything + // // locally, so cgroups will be empty. Create a temporary (unsaved, unsynced) + // // cgroup, one per cluster. + // cgroups = cgroups.concat( + // validClusters.map((c) => ({ + // id: c.id, + // name: undefined, + // clusterIDs: [c.id], + // isHidden: false, + // avatarFaceID: undefined, + // displayFaceID: undefined, + // })), + // ); + + // // For each cluster group, use the highest scoring face in any of its + // // clusters as its display face. + // for (const cgroup of cgroups) { + // cgroup.displayFaceID = cgroup.clusterIDs + // .map((clusterID) => clusterIndexForClusterID.get(clusterID)) + // .filter((i) => i !== undefined) /* 0 is a valid index */ + // .flatMap((i) => clusters[i]?.faceIDs ?? []) + // .map((faceID) => faceForFaceID.get(faceID)) + // .filter((face) => !!face) + // .reduce((max, face) => + // max.score > face.score ? max : face, + // ).faceID; + // } // TODO-Cluster - Currently we're not syncing with remote or saving anything // locally, so cgroups will be empty. Create a temporary (unsaved, unsynced) // cgroup, one per cluster. - cgroups = cgroups.concat( - validClusters.map((c) => ({ - id: c.id, + + const cgroups: CGroup[] = []; + for (const cluster of sortedClusters) { + const faces = cluster.faceIDs.map((id) => + ensure(faceForFaceID.get(id)), + ); + const topFace = faces.reduce((max, face) => + max.score > face.score ? max : face, + ); + cgroups.push({ + id: cluster.id, name: undefined, - clusterIDs: [c.id], + clusterIDs: [cluster.id], isHidden: false, avatarFaceID: undefined, - displayFaceID: undefined, - })), - ); - - // For each cluster group, use the highest scoring face in any of its - // clusters as its display face. - for (const cgroup of cgroups) { - cgroup.displayFaceID = cgroup.clusterIDs - .map((clusterID) => clusterIndexForClusterID.get(clusterID)) - .filter((i) => i !== undefined) /* 0 is a valid index */ - .flatMap((i) => clusters[i]?.faceIDs ?? []) - .map((faceID) => faceForFaceID.get(faceID)) - .filter((face) => !!face) - .reduce((max, face) => - max.score > face.score ? max : face, - ).faceID; + displayFaceID: topFace.faceID, + }); } // log.info("ml/cluster", { @@ -566,7 +588,7 @@ export const clusterFacesHdb = async (faceIndexes: FaceIndex[]) => { `Clustered ${faces.length} faces into ${validClusters.length} clusters, with ${faces.length - clusterIDForFaceID.size} faces remaining unclustered (${Date.now() - t} ms)`, ); - const clusteredCount = clusterIDForFaceID.size + const clusteredCount = clusterIDForFaceID.size; const unclusteredCount = faces.length - clusteredCount; return { diff --git a/web/packages/new/photos/services/ml/index.ts b/web/packages/new/photos/services/ml/index.ts index 3f588c09ad..4248c295f0 100644 --- a/web/packages/new/photos/services/ml/index.ts +++ b/web/packages/new/photos/services/ml/index.ts @@ -396,7 +396,7 @@ export const wipClusterDebugPageContents = async (): Promise< clusterIDForFaceID, } = await worker().then((w) => w.clusterFacesHdb()); - const searchPersons = await convertToSearchPersons(clusters, cgroups); + // const searchPersons = await convertToSearchPersons(clusters, cgroups); const localFiles = await getAllLocalFiles(); const localFileByID = new Map(localFiles.map((f) => [f.id, f])); @@ -422,6 +422,32 @@ export const wipClusterDebugPageContents = async (): Promise< })), })); + const clusterByID = new Map(clusters.map((c) => [c.id, c])); + + const searchPersons = cgroups + .map((cgroup) => { + const faceID = ensure(cgroup.displayFaceID); + const fileID = ensure(fileIDFromFaceID(faceID)); + const file = ensure(localFileByID.get(fileID)); + + const faceIDs = cgroup.clusterIDs + .map((id) => ensure(clusterByID.get(id))) + .flatMap((cluster) => cluster.faceIDs); + const fileIDs = faceIDs + .map((faceID) => fileIDFromFaceID(faceID)) + .filter((fileID) => fileID !== undefined); + + return { + id: cgroup.id, + name: cgroup.name, + faceIDs, + files: [...new Set(fileIDs)], + displayFaceID: faceID, + displayFaceFile: file, + }; + }) + .sort((a, b) => b.faceIDs.length - a.faceIDs.length); + _wip_isClustering = false; _wip_searchPersons = searchPersons; triggerStatusUpdate(); @@ -437,7 +463,8 @@ export const wipClusterDebugPageContents = async (): Promise< export const wipCluster = () => void wipClusterDebugPageContents(); -const convertToSearchPersons = async ( +// TODO-Cluster remove me +export const convertToSearchPersons = async ( clusters: FaceCluster[], cgroups: CGroup[], ) => {