This commit is contained in:
Manav Rathi
2024-08-29 19:20:56 +05:30
parent 2179b193d2
commit 3d95212023
3 changed files with 42 additions and 31 deletions

View File

@@ -4,10 +4,9 @@ import {
faceCrop,
wipClusterDebugPageContents,
type ClusterDebugPageContents,
type FaceFileNeighbour,
type FaceFileNeighbours,
type ClusterPreviewFaceWF,
type ClusterPreviewWF,
} from "@/new/photos/services/ml";
import type { Face } from "@/new/photos/services/ml/face";
import {
FlexWrapper,
FluidContainer,
@@ -15,7 +14,7 @@ import {
} from "@ente/shared/components/Container";
import EnteSpinner from "@ente/shared/components/EnteSpinner";
import BackButton from "@mui/icons-material/ArrowBackOutlined";
import { Box, IconButton, styled, Typography } from "@mui/material";
import { Box, IconButton, Stack, styled, Typography } from "@mui/material";
import { useRouter } from "next/router";
import { AppContext } from "pages/_app";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
@@ -53,8 +52,9 @@ export default function ClusterDebug() {
{`${clusterRes.clusters.length} clusters`}
</Typography>
<Typography variant="small" color="text.muted">
Showing only top 20 and bottom 10 clusters (and only up to 50 faces in
each, sorted by cosine distance to highest scoring face in the cluster).
Showing only top 20 and bottom 10 clusters (and only up to 50
faces in each, sorted by cosine distance to highest scoring face
in the cluster).
</Typography>
<hr />
<Container>
@@ -112,7 +112,7 @@ const ClusterPhotoList: React.FC<ClusterPhotoListProps> = ({
width,
clusterRes,
}) => {
const { faceFNs, clusterIDForFaceID } = clusterRes;
const { clusterPreviewWFs, clusterIDForFaceID } = clusterRes;
const [itemList, setItemList] = useState<ItemListItem[]>([]);
const listRef = useRef(null);
@@ -125,8 +125,8 @@ const ClusterPhotoList: React.FC<ClusterPhotoListProps> = ({
const listItemHeight = 120 * shrinkRatio + 24 + 4;
useEffect(() => {
setItemList(itemListFromFaceFNs(faceFNs, columns));
}, [columns, faceFNs]);
setItemList(itemListFromClusterPreviewWFs(clusterPreviewWFs, columns));
}, [columns, clusterPreviewWFs]);
useEffect(() => {
listRef.current?.resetAfterIndex(0);
@@ -138,7 +138,7 @@ const ClusterPhotoList: React.FC<ClusterPhotoListProps> = ({
const generateKey = (i: number) =>
Array.isArray(itemList[i])
? `${itemList[i][0].enteFile.id}/${itemList[i][0].face.faceID}-${itemList[i].slice(-1)[0].enteFile.id}/${itemList[i].slice(-1)[0].face.faceID}-${i}`
: `${itemList[i].faceID}-${i}`;
: `${itemList[i]}-${i}`;
return (
<VariableSizeList
@@ -163,13 +163,13 @@ const ClusterPhotoList: React.FC<ClusterPhotoListProps> = ({
>
{!Array.isArray(item) ? (
<LabelContainer span={columns}>
{`score ${item.score.toFixed(2)} blur ${item.blur.toFixed(0)}`}
{`cluster size ${item.toFixed(2)}`}
</LabelContainer>
) : (
item.map((faceFN, i) => (
item.map((faceWF, i) => (
<FaceItem
key={i.toString()}
{...{ faceFN, clusterIDForFaceID }}
{...{ faceWF, clusterIDForFaceID }}
/>
))
)}
@@ -181,19 +181,20 @@ const ClusterPhotoList: React.FC<ClusterPhotoListProps> = ({
);
};
type ItemListItem = Face | FaceFileNeighbour[];
// type ItemListItem = Face | FaceFileNeighbour[];
type ItemListItem = number | ClusterPreviewFaceWF[];
const itemListFromFaceFNs = (
faceFNs: FaceFileNeighbours[],
const itemListFromClusterPreviewWFs = (
clusterPreviewWFs: ClusterPreviewWF[],
columns: number,
) => {
const result: ItemListItem[] = [];
for (let index = 0; index < faceFNs.length; index++) {
const { face, neighbours } = faceFNs[index];
result.push(face);
for (let index = 0; index < clusterPreviewWFs.length; index++) {
const { clusterSize, faces } = clusterPreviewWFs[index];
result.push(clusterSize);
let lastIndex = 0;
while (lastIndex < neighbours.length) {
result.push(neighbours.slice(lastIndex, lastIndex + columns));
while (lastIndex < faces.length) {
result.push(faces.slice(lastIndex, lastIndex + columns));
lastIndex += columns;
}
}
@@ -210,12 +211,12 @@ const getShrinkRatio = (width: number, columns: number) =>
(columns * 120);
interface FaceItemProps {
faceFN: FaceFileNeighbour;
faceWF: ClusterPreviewFaceWF;
clusterIDForFaceID: Map<string, string>;
}
const FaceItem: React.FC<FaceItemProps> = ({ faceFN, clusterIDForFaceID }) => {
const { face, enteFile, cosineSimilarity } = faceFN;
const FaceItem: React.FC<FaceItemProps> = ({ faceWF, clusterIDForFaceID }) => {
const { face, enteFile, cosineSimilarity } = faceWF;
const { faceID } = face;
const [objectURL, setObjectURL] = useState<string | undefined>();
@@ -252,9 +253,15 @@ const FaceItem: React.FC<FaceItemProps> = ({ faceFN, clusterIDForFaceID }) => {
src={objectURL}
/>
)}
<Typography variant="small" color="text.muted" textAlign="right">
{cosineSimilarity.toFixed(2)}
</Typography>
<Stack direction="row" justifyContent="space-between">
<Typography variant="small" color="text.muted">
{`${face.blur.toFixed(0)} blr`}
</Typography>
<Typography variant="small" color="text.muted">
{`cos ${cosineSimilarity.toFixed(2)}`}
</Typography>
</Stack>
</FaceChip>
);
};

View File

@@ -352,7 +352,10 @@ export const clusterFacesHdb = async (faceIndexes: FaceIndex[]) => {
const t = Date.now();
// A flattened array of faces.
const faces0 = [...enumerateFaces(faceIndexes)];
// TODO-Cluster ad-hoc filtering and slicing
const faces0 = [...enumerateFaces(faceIndexes)]
.filter((f) => f.blur > 50)
.slice(0, 1000);
// TODO-Cluster testing code, can be removed once done
const faces = Array(1)
.fill(0)
@@ -433,7 +436,7 @@ export const clusterFacesHdb = async (faceIndexes: FaceIndex[]) => {
// Use a higher cosine similarity threshold if either of the two
// faces are blurry.
const threshold =
existingFace.blur < 100 || newFace.blur < 100 ? 0.84 : 0.7;
existingFace.blur < 100 || newFace.blur < 100 ? 0.9 : 0.7;
if (csim > threshold && csim > nnCosineSimilarity) {
nnCluster = existingCluster;
nnCosineSimilarity = csim;

View File

@@ -366,14 +366,15 @@ export interface ClusterPreviewWF {
faces: ClusterPreviewFaceWF[];
}
interface ClusterPreviewFaceWF {
export interface ClusterPreviewFaceWF {
face: Face;
enteFile: EnteFile;
cosineSimilarity: number;
}
export interface ClusterDebugPageContents {
faceFNs: FaceFileNeighbours[];
// faceFNs: FaceFileNeighbours[];
clusterPreviewWFs: ClusterPreviewWF[];
clusters: FaceCluster[];
clusterIDForFaceID: Map<string, string>;
}