Working through identity editor
Signed-off-by: James Ketrenos <james_gitlab@ketrenos.com>
This commit is contained in:
parent
efcaa7f5b8
commit
2037a3f636
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
function createFace(faceId, photoId, selectable) {
|
function createFace(faceId, photoId, selectable) {
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
div.classList.add("face");
|
div.classList.add("face");
|
||||||
@ -21,7 +20,14 @@ function createFace(faceId, photoId, selectable) {
|
|||||||
div.setAttribute("face-id", faceId);
|
div.setAttribute("face-id", faceId);
|
||||||
div.style.backgroundImage = "url(face-data/" + (faceId % 100) + "/" + faceId + "-original.png)";
|
div.style.backgroundImage = "url(face-data/" + (faceId % 100) + "/" + faceId + "-original.png)";
|
||||||
div.addEventListener("click", (event) => {
|
div.addEventListener("click", (event) => {
|
||||||
if (!selectable || event.ctrlKey) {
|
if (event.shiftKey) { /* identities */
|
||||||
|
let faceId = parseInt(event.currentTarget.getAttribute("face-id"));
|
||||||
|
if (faceId) {
|
||||||
|
window.location.href = "identities.html?id=" + faceId;
|
||||||
|
} else {
|
||||||
|
alert("No face id mapped to face.");
|
||||||
|
}
|
||||||
|
} else if (!selectable || event.ctrlKey) { /* face-explorer */
|
||||||
let photoId = parseInt(event.currentTarget.getAttribute("photo-id"));
|
let photoId = parseInt(event.currentTarget.getAttribute("photo-id"));
|
||||||
if (photoId) {
|
if (photoId) {
|
||||||
window.open("face-explorer.html?" + photoId, "photo-" + photoId);
|
window.open("face-explorer.html?" + photoId, "photo-" + photoId);
|
||||||
@ -39,20 +45,22 @@ function createFace(faceId, photoId, selectable) {
|
|||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shuffle(array) {
|
|
||||||
var i = array.length, tmp, random;
|
|
||||||
while (i > 0) {
|
|
||||||
random = Math.floor(Math.random() * i);
|
|
||||||
i--;
|
|
||||||
tmp = array[i];
|
|
||||||
array[i] = array[random];
|
|
||||||
array[random] = tmp;
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (event) => {
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
loadFace();
|
let params = window.location.search ? window.location.search.replace(/^\?/, "").split("&") : [],
|
||||||
|
id;
|
||||||
|
|
||||||
|
for (let i in params) {
|
||||||
|
let parts = params[i].split("=");
|
||||||
|
switch (parts[0]) {
|
||||||
|
case "id":
|
||||||
|
id = parseInt(parts[1]);
|
||||||
|
if (id != parts[1]) {
|
||||||
|
id = undefined;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadFace(id);
|
||||||
const newIdentity = document.getElementById("new-identity");
|
const newIdentity = document.getElementById("new-identity");
|
||||||
newIdentity.appendChild(createNewIdenityEditor());
|
newIdentity.appendChild(createNewIdenityEditor());
|
||||||
});
|
});
|
||||||
@ -68,7 +76,11 @@ function loadFace(id) {
|
|||||||
|
|
||||||
const face = faces[0];
|
const face = faces[0];
|
||||||
|
|
||||||
|
if (face.identityId) {
|
||||||
|
getIdentity(face.identityId);
|
||||||
|
} else {
|
||||||
getIdentities(face.id);
|
getIdentities(face.id);
|
||||||
|
}
|
||||||
|
|
||||||
const editor = document.createElement("div");
|
const editor = document.createElement("div");
|
||||||
editor.classList.add("editor");
|
editor.classList.add("editor");
|
||||||
@ -86,7 +98,7 @@ function loadFace(id) {
|
|||||||
editor.appendChild(row);
|
editor.appendChild(row);
|
||||||
row = document.createElement("div");
|
row = document.createElement("div");
|
||||||
row.classList.add("editor-row");
|
row.classList.add("editor-row");
|
||||||
let message = "Related faces: ";
|
let message = "Unassigned related faces: ";
|
||||||
if (face.relatedFaces.length > 0) {
|
if (face.relatedFaces.length > 0) {
|
||||||
message += face.relatedFaces.length + " related (tap to deselect)";
|
message += face.relatedFaces.length + " related (tap to deselect)";
|
||||||
} else {
|
} else {
|
||||||
@ -133,11 +145,26 @@ function showMore(row, faces) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIdentities(id) {
|
|
||||||
|
function getIdentity(identityId) {
|
||||||
const identitiesBlock = document.getElementById("identities");
|
const identitiesBlock = document.getElementById("identities");
|
||||||
identitiesBlock.innerHTML = "";
|
identitiesBlock.innerHTML = "";
|
||||||
|
|
||||||
const search = id ? "?withScore=" + id : "";
|
window.fetch("api/v1/identities/" + identityId).then(res => res.json()).then((identities) => {
|
||||||
|
identities.forEach((identity) => {
|
||||||
|
const block = createIdentityBlock(identity, true),
|
||||||
|
button = createUseThisIdentityButton(identity);
|
||||||
|
block.insertBefore(button, block.firstChild);
|
||||||
|
identitiesBlock.appendChild(block);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIdentities(faceId) {
|
||||||
|
const identitiesBlock = document.getElementById("identities");
|
||||||
|
identitiesBlock.innerHTML = "";
|
||||||
|
|
||||||
|
const search = faceId ? "?withScore=" + faceId : "";
|
||||||
window.fetch("api/v1/identities" + search).then(res => res.json()).then((identities) => {
|
window.fetch("api/v1/identities" + search).then(res => res.json()).then((identities) => {
|
||||||
identities.sort((a, b) => {
|
identities.sort((a, b) => {
|
||||||
if (a.lastName == b.lastName) {
|
if (a.lastName == b.lastName) {
|
||||||
@ -147,12 +174,17 @@ function getIdentities(id) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
identities.forEach((identity) => {
|
identities.forEach((identity) => {
|
||||||
const block = document.createElement("div");
|
const block = createIdentityBlock(identity),
|
||||||
block.classList.add("block");
|
button = createUseThisIdentityButton(identity);
|
||||||
|
block.insertBefore(button, block.firstChild);
|
||||||
|
identitiesBlock.appendChild(block);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUseThisIdentityButton(identity) {
|
||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.textContent = "Use this identity";
|
button.textContent = "Use this identity";
|
||||||
block.appendChild(button);
|
|
||||||
button.addEventListener("click", (event) => {
|
button.addEventListener("click", (event) => {
|
||||||
const object = {
|
const object = {
|
||||||
faces: []
|
faces: []
|
||||||
@ -175,10 +207,18 @@ function getIdentities(id) {
|
|||||||
loadFace(parseInt(face.getAttribute("face-id")));
|
loadFace(parseInt(face.getAttribute("face-id")));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createIdentityBlock(identity, nolimit) {
|
||||||
|
const block = document.createElement("div");
|
||||||
|
|
||||||
|
block.classList.add("block");
|
||||||
let div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
div.textContent = identity.name;
|
div.textContent = identity.name;
|
||||||
|
|
||||||
block.appendChild(div);
|
block.appendChild(div);
|
||||||
|
|
||||||
div = document.createElement("div");
|
div = document.createElement("div");
|
||||||
div.textContent = "Related faces " + identity.relatedFaces.length;
|
div.textContent = "Related faces " + identity.relatedFaces.length;
|
||||||
block.appendChild(div);
|
block.appendChild(div);
|
||||||
@ -186,13 +226,21 @@ function getIdentities(id) {
|
|||||||
identity.relatedFaces.sort((a, b) => {
|
identity.relatedFaces.sort((a, b) => {
|
||||||
return a.distance - b.distance;
|
return a.distance - b.distance;
|
||||||
});
|
});
|
||||||
|
|
||||||
div = document.createElement("div");
|
div = document.createElement("div");
|
||||||
div.classList.add("face-block");
|
div.classList.add("face-block");
|
||||||
|
if (nolimit) {
|
||||||
|
div.classList.add("face-block-nolimit");
|
||||||
|
}
|
||||||
|
|
||||||
let minDistance = {
|
let minDistance = {
|
||||||
distance: 1
|
distance: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let i = 0; i < identity.relatedFaces.length && i < 4; i++) {
|
for (let i = 0; i < identity.relatedFaces.length; i++) {
|
||||||
|
if (!nolimit && i >= 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
const target = identity.relatedFaces[i];
|
const target = identity.relatedFaces[i];
|
||||||
const facePhoto = createFace(target.faceId, target.photoId),
|
const facePhoto = createFace(target.faceId, target.photoId),
|
||||||
distance = document.createElement("div");
|
distance = document.createElement("div");
|
||||||
@ -224,11 +272,11 @@ function getIdentities(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
block.appendChild(div);
|
block.appendChild(div);
|
||||||
identitiesBlock.appendChild(block);
|
|
||||||
});
|
return block;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function createNewIdenityEditor() {
|
function createNewIdenityEditor() {
|
||||||
const block = document.createElement("div");
|
const block = document.createElement("div");
|
||||||
block.classList.add("block");
|
block.classList.add("block");
|
||||||
@ -343,6 +391,13 @@ body {
|
|||||||
max-height: 128px;
|
max-height: 128px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.face-block-nolimit {
|
||||||
|
min-width: 128px;
|
||||||
|
max-width: initial;
|
||||||
|
min-height: 128px;
|
||||||
|
max-height: initial;
|
||||||
|
}
|
||||||
|
|
||||||
.face-block .face {
|
.face-block .face {
|
||||||
max-width: 64px;
|
max-width: 64px;
|
||||||
max-height: 64px;
|
max-height: 64px;
|
||||||
|
20
server/routes/faces.js
Normal file → Executable file
20
server/routes/faces.js
Normal file → Executable file
@ -81,7 +81,19 @@ router.get("/:id?", (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return photoDB.sequelize.query("SELECT COUNT(id) AS count FROM faces WHERE faceConfidence>=0.9 AND identityId IS NULL", {
|
let promise;
|
||||||
|
if (id) {
|
||||||
|
promise = photoDB.sequelize.query(
|
||||||
|
"SELECT * FROM faces WHERE id=:id", {
|
||||||
|
replacements: {
|
||||||
|
id: id
|
||||||
|
},
|
||||||
|
type: photoDB.Sequelize.QueryTypes.SELECT,
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
promise = photoDB.sequelize.query(
|
||||||
|
"SELECT COUNT(id) AS count FROM faces WHERE faceConfidence>=0.9 AND identityId IS NULL", {
|
||||||
type: photoDB.Sequelize.QueryTypes.SELECT,
|
type: photoDB.Sequelize.QueryTypes.SELECT,
|
||||||
raw: true
|
raw: true
|
||||||
}).then((results) => {
|
}).then((results) => {
|
||||||
@ -94,7 +106,11 @@ router.get("/:id?", (req, res) => {
|
|||||||
type: photoDB.Sequelize.QueryTypes.SELECT,
|
type: photoDB.Sequelize.QueryTypes.SELECT,
|
||||||
raw: true
|
raw: true
|
||||||
});
|
});
|
||||||
}).then((faces) => {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((faces) => {
|
||||||
|
console.log("Looking up " + faces.map(face => face.id).join(","));
|
||||||
return photoDB.sequelize.query(
|
return photoDB.sequelize.query(
|
||||||
"SELECT relatedFaces.photoId AS photoId,fd.face1Id,fd.face2Id,fd.distance,relatedFaces.faceConfidence " +
|
"SELECT relatedFaces.photoId AS photoId,fd.face1Id,fd.face2Id,fd.distance,relatedFaces.faceConfidence " +
|
||||||
"FROM (SELECT id,photoId,faceConfidence FROM faces WHERE faces.id IN (:ids)) AS faces " +
|
"FROM (SELECT id,photoId,faceConfidence FROM faces WHERE faces.id IN (:ids)) AS faces " +
|
||||||
|
@ -110,12 +110,13 @@ router.get("/:id?", (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter = id ? "WHERE id=:id " : "";
|
const filter = id ? "WHERE identities.id=:id " : "";
|
||||||
|
|
||||||
return photoDB.sequelize.query("SELECT " +
|
return photoDB.sequelize.query("SELECT " +
|
||||||
"identities.*," +
|
"identities.*," +
|
||||||
"GROUP_CONCAT(faces.id) AS relatedFaceIds," +
|
"GROUP_CONCAT(faces.id) AS relatedFaceIds," +
|
||||||
"GROUP_CONCAT(faces.photoId) AS relatedFacePhotoIds " +
|
"GROUP_CONCAT(faces.photoId) AS relatedFacePhotoIds," +
|
||||||
|
"GROUP_CONCAT(faces.identityDistance) AS relatedIdentityDistances " +
|
||||||
"FROM identities " +
|
"FROM identities " +
|
||||||
"INNER JOIN faces ON identities.id=faces.identityId " +
|
"INNER JOIN faces ON identities.id=faces.identityId " +
|
||||||
filter +
|
filter +
|
||||||
@ -128,7 +129,8 @@ router.get("/:id?", (req, res) => {
|
|||||||
}).then((identities) => {
|
}).then((identities) => {
|
||||||
identities.forEach((identity) => {
|
identities.forEach((identity) => {
|
||||||
const relatedFaces = identity.relatedFaceIds.split(","),
|
const relatedFaces = identity.relatedFaceIds.split(","),
|
||||||
relatedFacePhotos = identity.relatedFacePhotoIds.split(",");
|
relatedFacePhotos = identity.relatedFacePhotoIds.split(","),
|
||||||
|
relatedIdentityDistances = identity.relatedIdentityDistances.split(",");
|
||||||
if (relatedFaces.length != relatedFacePhotos.length) {
|
if (relatedFaces.length != relatedFacePhotos.length) {
|
||||||
console.warn("Face ID to Photo ID mapping doesn't match!");
|
console.warn("Face ID to Photo ID mapping doesn't match!");
|
||||||
}
|
}
|
||||||
@ -137,7 +139,8 @@ router.get("/:id?", (req, res) => {
|
|||||||
identity.relatedFaces = relatedFaces.map((faceId, index) => {
|
identity.relatedFaces = relatedFaces.map((faceId, index) => {
|
||||||
return {
|
return {
|
||||||
faceId: faceId,
|
faceId: faceId,
|
||||||
photoId: relatedFacePhotos[index]
|
photoId: relatedFacePhotos[index],
|
||||||
|
distance: parseFloat(relatedIdentityDistances[index] !== undefined ? relatedIdentityDistances[index] : -1)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user