Can now create blank identities
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
parent
df764ad342
commit
b86c12fc92
@ -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 {
|
||||
|
@ -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>}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user