Can now create blank identities

Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
James Ketr 2023-01-25 13:43:43 -08:00
parent df764ad342
commit b86c12fc92
3 changed files with 157 additions and 97 deletions

View File

@ -121,7 +121,15 @@ div {
.IdentityForm {
display: grid;
gap: 0.25rem;
grid-template-columns: 1fr 1fr;
align-items: center;
justify-items: flex-end;
}
.IdentityForm input {
min-height: 1rem;
padding: 0.25rem;
}
.Face.Active,
@ -145,11 +153,18 @@ div {
color: white;
}
.Info .Face .Image {
width: 10rem;
height: 10rem;
}
.Face .Image {
position: relative;
box-sizing: border-box;
display: flex;
justify-content: center;
min-width: 8rem;
min-height: 8rem;
}
.Cluster {
@ -175,8 +190,6 @@ div {
object-fit: cover; /* contain */
width: 100%;
height: 100%;
min-width: 8rem;
min-height: 8rem;
}
.Cluster .Faces {

View File

@ -209,7 +209,7 @@ const Face = ({ face, onFaceClick, title, isSelected }: any) => {
type ClusterProps = {
identity: IdentityData,
setIdentity(identity: IdentityData | undefined): void,
setIdentity(identity: IdentityData): void,
identities: IdentityData[],
setIdentities(identiteis: IdentityData[]): void,
setImage(image: number): void,
@ -275,7 +275,7 @@ const Cluster = ({
if (index !== -1) {
identities.splice(index, 1);
}
setIdentity(undefined);
setIdentity(EmptyIdentity);
setIdentities([...identities]);
} catch (error) {
console.error(error);
@ -338,27 +338,37 @@ const Cluster = ({
return (
<div className='Cluster'>
<div className="Info">
<form className="IdentityForm">
<div>Last name:</div>
<input type="text"
value={identity.lastName}
onChange={lastNameChanged}/>
<div>First name:</div>
<input type="text"
value={identity.firstName}
onChange={firstNameChanged} />
<div>Middle name:</div><input type="text"
value={identity.middleName}
onChange={middleNameChanged} />
<div>Display name:</div>
<input type="text"
value={identity.displayName}
onChange={displayNameChanged} />
</form>
<div style={{ display: "flex", flexDirection: "row", gap: "0.25rem" }}>
<form className="IdentityForm">
<div>Last name:</div>
<input type="text"
value={identity.lastName}
onChange={lastNameChanged}/>
<div>First name:</div>
<input type="text"
value={identity.firstName}
onChange={firstNameChanged} />
<div>Middle name:</div><input type="text"
value={identity.middleName}
onChange={middleNameChanged} />
<div>Display name:</div>
<input type="text"
value={identity.displayName}
onChange={displayNameChanged} />
</form>
<Face
face={identity.relatedFaces.length
? identity.relatedFaces[0]
: UnknownFace}
onFaceClick={() => {}}
title={`${identity.displayName} (${identity.facesCount})`} />
</div>
<div className="Actions">
<Button onClick={createIdentity}>Create</Button>
<Button onClick={updateIdentity}>Update</Button>
<Button onClick={deleteIdentity}>Delete</Button>
{ identity.identityId !== -1 && <>
<Button onClick={updateIdentity}>Update</Button>
<Button onClick={deleteIdentity}>Delete</Button>
</> }
</div>
</div>
<div>Faces: {identity.relatedFaces.length}</div>
@ -405,6 +415,32 @@ type IdentityData = {
faceId: number
};
const EmptyIdentity: IdentityData = {
lastName: '',
middleName: '',
firstName: '',
descriptors: [],
identityId: -1,
displayName: '',
relatedFaces: [],
facesCount: 0,
faceId: -1
};
const UnknownFace = {
faceId: -1,
photoId: -1,
identityId: -1,
distance: 0,
descriptors: [],
top: 0,
left: 0,
bottom: 0,
right: 0,
identity: EmptyIdentity
};
interface IdentitiesProps {
identities: IdentityData[],
onFaceClick(e: any, face: FaceData): void
@ -448,7 +484,7 @@ const App = () => {
const [identities, setIdentities] = useState<IdentityData[]>([]);
const { identityId, faceId } = useParams();
const [selectedIdentities, setSelectedIdentities] = useState<number[]>([]);
const [identity, setIdentity] = useState<IdentityData | undefined>(undefined);
const [identity, setIdentity] = useState<IdentityData>(EmptyIdentity);
const [image, setImage] = useState<number>(0);
const [guess, setGuess] = useState<FaceData|undefined>(undefined);
const { loading, data } = useApi( /* TODO: Switch away from using useApi */
@ -575,7 +611,7 @@ const App = () => {
if (index !== -1) {
identities.splice(index, 1);
}
setIdentity(undefined);
setIdentity(EmptyIdentity);
setIdentities([...identities]);
} catch (error) {
console.error(error);
@ -721,7 +757,6 @@ const App = () => {
autoSaveId="persistence" direction="horizontal">
<Panel defaultSize={50} className="ClusterEditor">
{loading && <div style={{ margin: '1rem' }}>Loading...</div>}
{ !loading && identity !== undefined &&
<Cluster {...{
identity,
setIdentity,
@ -730,7 +765,7 @@ const App = () => {
setImage,
selected,
setSelected,
}} /> }
}} />
{!loading && identity === undefined && <div className="Cluster">
Select identity to edit
</div>}

View File

@ -44,6 +44,7 @@ const upsertIdentity = async(id, {
replacements: identity
});
identity.identityId = lastId;
console.log('Created identity: ', identity)
} else {
await photoDB.sequelize.query(
`UPDATE identities ` +
@ -55,6 +56,7 @@ const upsertIdentity = async(id, {
'WHERE id=:identityId', {
replacements: identity
});
console.log('Updated identity: ', identity)
}
return identity;
@ -502,82 +504,92 @@ const updateIdentityFaces = async (identity) => {
.parseFloat(euclideanDistanceArray(identity.descriptors, average))
.toFixed(4);
/* If the average position has not changed, then face distances should
* not change either! */
await Promise.map(faces, async (face) => {
/* All the buffer are already arrays, so use the short-cut version */
const distance = Number
.parseFloat(euclideanDistanceArray(face.descriptors, average))
.toFixed(4);
const t = await photoDB.sequelize.transaction();
try {
/* If the average position has not changed, then face distances should
* not change either! */
await Promise.map(faces, async (face) => {
/* All the buffer are already arrays, so use the short-cut version */
const distance = Number
.parseFloat(euclideanDistanceArray(face.descriptors, average))
.toFixed(4);
if (Math.abs(distance - face.distance) > MIN_DISTANCE_COMMIT) {
if (Math.abs(distance - face.distance) > MIN_DISTANCE_COMMIT) {
console.log(
`Updating face ${face.id} to ${round(distance, 2)} ` +
`(${distance - face.distance}) ` +
`from identity ${identity.identityId} (${identity.displayName})`);
face.distance = distance;
await photoDB.sequelize.query(
'UPDATE faces SET distance=:distance WHERE id=:id', {
replacements: face,
transaction: t
}
);
}
}, {
concurrency: 5
});
let sql = '';
/* If there is a new closestId, then set the faceId field */
if (closestId !== -1 && closestId !== identity.faceId) {
console.log(
`Updating face ${face.id} to ${round(distance, 2)} ` +
`(${distance - face.distance}) ` +
`from identity ${identity.identityId} (${identity.displayName})`);
face.distance = distance;
`Updating identity ${identity.identityId} closest face to ${closestId}`);
sql = `${sql} faceId=:faceId`;
identity.faceId = closestId;
}
/* If the centroid changed, update the identity descriptors to
* the new average */
if (Math.abs(moved) > MIN_DISTANCE_COMMIT) {
console.log(
`Updating identity ${identity.identityId} centroid ` +
`(moved ${Number.parseFloat(moved).toFixed(4)}).`);
if (sql !== '') {
sql = `${sql}, `;
}
sql = `${sql} descriptors=:descriptors`;
// this: identity.descriptors = average;
// gives: Invalid value Float64Array(2622)
//
// this: identity.descriptors = new Blob(average);
// gives: Invalid value Blob { size: 54008, type: '' }
//
// this: identity.descriptors = Buffer.from(average);
// gives: all zeroes
//
// this: identity.descriptors = Buffer.from(average.buffer);
// gives: IT WORKS!!!
identity.descriptors = Buffer.from(average.buffer);
}
/* If the number of faces changed, update the facesCount */
if (identity.facesCount !== faces.length) {
if (sql !== '') {
sql = `${sql}, `;
}
console.log(
`Updating identity ${identity.identityId} face count to ${faces.length}`);
identity.facesCount = faces.length;
sql = `${sql} facesCount=${faces.length}`;
}
/* If any of the above required changes, actually commit to the DB */
if (sql !== '') {
await photoDB.sequelize.query(
'UPDATE faces SET distance=:distance WHERE id=:id', {
replacements: face
`UPDATE identities SET ${sql} ` +
`WHERE id=:identityId`, {
replacements: identity,
transaction: t
}
);
}
}, {
concurrency: 5
});
let sql = '';
/* If there is a new closestId, then set the faceId field */
if (closestId !== -1 && closestId !== identity.faceId) {
console.log(
`Updating identity ${identity.identityId} closest face to ${closestId}`);
sql = `${sql} faceId=:faceId`;
identity.faceId = closestId;
}
/* If the centroid changed, update the identity descriptors to
* the new average */
if (Math.abs(moved) > MIN_DISTANCE_COMMIT) {
console.log(
`Updating identity ${identity.identityId} centroid ` +
`(moved ${Number.parseFloat(moved).toFixed(4)}).`);
if (sql !== '') {
sql = `${sql}, `;
}
sql = `${sql} descriptors=:descriptors`;
// this: identity.descriptors = average;
// gives: Invalid value Float64Array(2622)
//
// this: identity.descriptors = new Blob(average);
// gives: Invalid value Blob { size: 54008, type: '' }
//
// this: identity.descriptors = Buffer.from(average);
// gives: all zeroes
//
// this: identity.descriptors = Buffer.from(average.buffer);
// gives: IT WORKS!!!
identity.descriptors = Buffer.from(average.buffer);
}
/* If the number of faces changed, update the facesCount */
if (identity.facesCount !== faces.length) {
if (sql !== '') {
sql = `${sql}, `;
}
console.log(
`Updating identity ${identity.identityId} face count to ${faces.length}`);
identity.facesCount = faces.length;
sql = `${sql} facesCount=${faces.length}`;
}
/* If any of the above required changes, actually commit to the DB */
if (sql !== '') {
await photoDB.sequelize.query(
`UPDATE identities SET ${sql} ` +
`WHERE id=:identityId`, {
replacements: identity
}
);
t.commit();
} catch (error) {
console.error(error);
t.rollback();
return;
}
};