Include preview faces
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useIsSmallWidth } from "@/base/hooks";
|
||||
import { pt } from "@/base/i18n";
|
||||
import { faceCrop, type AnnotatedFaceID } from "@/new/photos/services/ml";
|
||||
import type { Person } from "@/new/photos/services/ml/people";
|
||||
import type { Person, PreviewableFace } from "@/new/photos/services/ml/people";
|
||||
import type { EnteFile } from "@/new/photos/types/file";
|
||||
import { Skeleton, Typography, styled } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
@@ -198,7 +198,7 @@ export interface SuggestionFaceListProps {
|
||||
* Faces, each annotated with the corresponding {@link EnteFile}, to show in
|
||||
* the list.
|
||||
*/
|
||||
faces: { enteFile: EnteFile; faceID: string }[];
|
||||
faces: [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,14 +228,10 @@ const SuggestionFaceList_ = styled("div")`
|
||||
gap: 5px;
|
||||
`;
|
||||
|
||||
interface FaceCropImageViewProps {
|
||||
/** The ID of the face to display. */
|
||||
faceID: string;
|
||||
/** The {@link EnteFile} which contains this face. */
|
||||
enteFile: EnteFile;
|
||||
type FaceCropImageViewProps = PreviewableFace & {
|
||||
/** Width and height for the placeholder. */
|
||||
placeholderDimension: number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An image view showing the face crop for the given face.
|
||||
|
||||
@@ -381,7 +381,6 @@ const SuggestionsDialog: React.FC<SuggestionsDialogProps> = ({
|
||||
const go = async () => {
|
||||
try {
|
||||
const suggestions = await suggestionsForPerson(person);
|
||||
console.log("fetching");
|
||||
dispatch({ type: "fetched", personID, suggestions });
|
||||
} catch (e) {
|
||||
log.error("Failed to generate suggestions", e);
|
||||
@@ -426,8 +425,6 @@ const SuggestionsDialog: React.FC<SuggestionsDialogProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
console.log({ f: "render", open, person, state });
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
@@ -518,7 +515,7 @@ const SuggestionsList: React.FC<SuggestionsListProps> = ({
|
||||
}}
|
||||
key={suggestion.id}
|
||||
>
|
||||
<Typography>{`${suggestion.faces.length} faces ntaoheu naoehtu aosnehu asoenuh aoenuht`}</Typography>
|
||||
<Typography>{`${suggestion.previewFaces.length} faces ntaoheu naoehtu aosnehu asoenuh aoenuht`}</Typography>
|
||||
<ToggleButtonGroup
|
||||
value={markedSuggestionIDs.get(suggestion.id)}
|
||||
exclusive
|
||||
|
||||
@@ -45,17 +45,6 @@ export type ClusterFace = Omit<Face, "embedding"> & {
|
||||
isBadFace: boolean;
|
||||
};
|
||||
|
||||
export interface ClusterPreview {
|
||||
clusterSize: number;
|
||||
faces: ClusterPreviewFace[];
|
||||
}
|
||||
|
||||
export interface ClusterPreviewFace {
|
||||
face: ClusterFace;
|
||||
cosineSimilarity: number;
|
||||
wasMerged: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates clusters from the given faces using a batched form of linear
|
||||
* clustering, with a bit of lookback (and a dollop of heuristics) to get the
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { assertionFailed } from "@/base/assert";
|
||||
import log from "@/base/log";
|
||||
import type { EnteFile } from "../../types/file";
|
||||
import { getLocalFiles } from "../files";
|
||||
@@ -144,6 +145,19 @@ export type NamedPerson = Person & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A face ID annotated with the {@link EnteFile} that contains it.
|
||||
*
|
||||
* Both these pieces of information are needed for a UI element to show the
|
||||
* face.
|
||||
*/
|
||||
export interface PreviewableFace {
|
||||
/** The ID of the face to display. */
|
||||
faceID: string;
|
||||
/** The {@link EnteFile} which contains this face. */
|
||||
enteFile: EnteFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct in-memory people using the data present locally, ignoring faces
|
||||
* belonging to deleted and hidden files.
|
||||
@@ -308,7 +322,23 @@ export const filterNamedPeople = (people: Person[]): NamedPerson[] => {
|
||||
return namedPeople;
|
||||
};
|
||||
|
||||
export type PersonSuggestion = FaceCluster;
|
||||
export interface PersonSuggestion {
|
||||
/**
|
||||
* The ID of the suggestion. This is the same as the cluster's ID,
|
||||
* duplicated here for the UI's convenience.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The underlying {@link FaceCluster} that is being offered as the
|
||||
* suggestion.
|
||||
*/
|
||||
cluster: FaceCluster;
|
||||
/**
|
||||
* A list of up to 3 "preview" faces for this cluster, each annotated with
|
||||
* the corresponding {@link EnteFile} that contains them.
|
||||
*/
|
||||
previewFaces: PreviewableFace[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns suggestions for the given person.
|
||||
@@ -367,13 +397,41 @@ export const suggestionsForPerson = async (person: CGroupPerson) => {
|
||||
if (suggest) suggestedClusters.push(cluster);
|
||||
}
|
||||
|
||||
const result = suggestedClusters.sort(
|
||||
(a, b) => b.faces.length - a.faces.length,
|
||||
);
|
||||
suggestedClusters.sort((a, b) => b.faces.length - a.faces.length);
|
||||
|
||||
// Annotate the clusters with the information that the UI needs to show its
|
||||
// preview faces.
|
||||
|
||||
const files = await getLocalFiles("normal");
|
||||
const fileByID = new Map(files.map((f) => [f.id, f]));
|
||||
|
||||
const suggestions = suggestedClusters.map((cluster) => {
|
||||
const previewFaces = cluster.faces
|
||||
.slice(0, 3)
|
||||
.map((faceID) => {
|
||||
const fileID = fileIDFromFaceID(faceID);
|
||||
if (!fileID) {
|
||||
assertionFailed();
|
||||
return undefined;
|
||||
}
|
||||
const file = fileByID.get(fileID);
|
||||
if (!file) {
|
||||
// TODO-Cluster: This might be a hidden/trash file, so this
|
||||
// assert is not appropriate, we instead need a "until 3".
|
||||
// assertionFailed();
|
||||
return undefined;
|
||||
}
|
||||
return { enteFile: file, faceID };
|
||||
})
|
||||
.filter((f) => !!f);
|
||||
|
||||
const id = cluster.id;
|
||||
return { id, cluster, previewFaces };
|
||||
});
|
||||
|
||||
log.info(
|
||||
`Generated ${result.length} suggestions for ${person.id} (${Date.now() - startTime} ms)`,
|
||||
`Generated ${suggestions.length} suggestions for ${person.id} (${Date.now() - startTime} ms)`,
|
||||
);
|
||||
|
||||
return result;
|
||||
return suggestions;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user