ID can now be set per face

Signed-off-by: James P. Ketrenos <james.p.ketrenos@intel.com>
This commit is contained in:
James P. Ketrenos 2023-01-22 18:12:54 -08:00
parent 40b3a0d819
commit 83e006b43c
3 changed files with 110 additions and 20 deletions

View File

@ -403,6 +403,7 @@ const Button = ({ onClick, children }: any) => {
const App = () => {
const [identities, setIdentities] = useState<IdentityData[]>([]);
const { identityId, faceId } = useParams();
const [selectedIdentities, setSelectedIdentities] = useState<number[]>([]);
const [identity, setIdentity] = useState<IdentityData | undefined>(undefined);
const [image, setImage] = useState<number>(0);
const { loading, data } = useApi(
@ -503,6 +504,27 @@ const App = () => {
}
};
const changeSelectedIdentity = async () => {
if (selectedIdentities.length === 0) {
window.alert('You need to select an identity first (CTRL+CLICK)');
return;
}
try {
const res = await window.fetch(
`${base}/api/v1/identities/faces/add/${selectedIdentities[0]}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ faces: selected })
});
const data = await res.json();
removeFacesFromIdentities(data.faces);
} catch (error) {
console.error(error);
}
};
const markSelectedNotFace = async () => {
try {
const res = await window.fetch(
@ -534,7 +556,36 @@ const App = () => {
};
const identitiesOnFaceClick = (e: any, face: FaceData) => {
const identitiesEl = document.querySelector('.Identities');
if (!identitiesEl) {
return;
}
const identityId = face.identityId;
const el = e.currentTarget;
/* Control -- select / deselect single item */
if (e.ctrlKey) {
[...identitiesEl.querySelectorAll('.Selected')].forEach(item => {
item.classList.remove('Selected')
});
el.classList.toggle('Selected');
const selected = [...identitiesEl.querySelectorAll('.Selected')]
.map((face: any) => face.getAttribute('data-identity-id'));
setSelectedIdentities(selected);
return;
}
/* Shift -- select groups */
if (e.shiftKey) {
return;
}
/* Default to load image */
e.stopPropagation();
e.preventDefault();
loadIdentity(identityId);
}
@ -559,6 +610,7 @@ const App = () => {
{ selected.length !== 0 && <>
<Button onClick={markSelectedIncorrectIdentity}>Remove</Button>
<Button onClick={markSelectedNotFace}>Not a face</Button>
<Button onClick={changeSelectedIdentity}>Change Identity</Button>
</>}
</div>
</Panel>

View File

@ -100,6 +100,7 @@ def load_faces(db_path ):
INNER JOIN photos ON (photos.duplicate == 0 OR photos.duplicate IS NULL)
JOIN facedescriptors ON (faces.descriptorId=facedescriptors.id)
WHERE faces.identityId IS null
AND faces.classifiedBy != 'not-a-face'
AND faces.photoId=photos.id
''')
for row in res.fetchall():

View File

@ -134,7 +134,24 @@ router.put('/:id', async (req, res) => {
return res.status(200).send(identity);
});
router.put("/faces/remove/:id", (req, res) => {
const addFaceToIdentityDescriptors = (identity, face) => {
};
const removeFaceToIdentityDescriptors = (identity, face) => {
};
const writeIdentityDescriptors = async (identity) => {
await photoDB.sequelize.query(
'UPDATE identities ' +
'SET descriptors=:descriptors' +
'WHERE id=:identityId', {
replacements: identity
}
);
};
router.put("/faces/remove/:id", async (req, res) => {
console.log(`PUT ${req.url}`)
if (!req.user.maintainer) {
console.warn(`${req.user.name} attempted to modify photos.`);
@ -150,23 +167,28 @@ router.put("/faces/remove/:id", (req, res) => {
return res.status(400).send({ message: "No faces supplied." });
}
return photoDB.sequelize.query(
try {
await photoDB.sequelize.query(
"UPDATE faces SET identityId=null " +
"WHERE id IN (:faceIds)", {
replacements: {
identityId: id,
faceIds: req.body.faces
}
}).then(() => {
});
const identity = {
id: id,
faces: req.body.faces
};
identity.faces = identity.faces.map(id => +id);
updateIdentityDescriptors(identity);
return res.status(200).json(identity);
}).catch((error) => {
} catch (error) {
console.error(error);
return res.status(500).send({message: "Error processing request." });
});
};
});
router.put("/faces/add/:id", async (req, res) => {
@ -197,7 +219,11 @@ router.put("/faces/add/:id", async (req, res) => {
id: id,
faces: req.body.faces
};
return res.status(200).json([identity]);
identity.faces = identity.faces.map(id => +id);
updateIdentityDescriptors(identity);
return res.status(200).json(identity);
} catch (error) {
console.error(error);
return res.status(500).send("Error processing request.");
@ -249,10 +275,16 @@ router.post("/", (req, res) => {
});
function bufferToFloat32Array(buffer) {
return new Float64Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Float64Array.BYTES_PER_ELEMENT);
return new Float64Array(buffer.buffer,
buffer.byteOffset,
buffer.byteLength / Float64Array.BYTES_PER_ELEMENT);
}
function euclideanDistance(a, b) {
if (!a.buffer || !b.buffer) {
return -1;
}
let A = bufferToFloat32Array(a);
let B = bufferToFloat32Array(b);
let sum = 0;
@ -270,7 +302,7 @@ const getUnknownIdentity = async (faceCount) => {
firstName: '',
middleName: '',
displayName: 'Unknown',
descriptors: [],
descriptors: new Float32Array(0),
relatedFaces: []
};
const limit = faceCount
@ -293,7 +325,7 @@ const getUnknownIdentity = async (faceCount) => {
unknownIdentity.relatedFaces.forEach(face => {
face.identityId = -1;
face.distance = face.faceConfidence;
face.descriptors = [];
face.descriptors = new Float32Array(0);
delete face.faceConfidence;
});
return unknownIdentity;
@ -361,10 +393,15 @@ router.get("/:id?", async (req, res) => {
descriptors = descriptors.map(entry => entry.descriptors);
identity.relatedFaces = relatedFaces.map((faceId, index) => {
const distance = euclideanDistance(
let distance = 0;
if (descriptors[index] && identity.descriptors) {
distance = euclideanDistance(
descriptors[index],
identity.descriptors
);
} else {
distance = -1;
}
return {
identityId: identity.id,