James Ketrenos e5a55de73c Improved face dropping from photopanel
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
2023-01-28 20:19:52 -08:00

196 lines
6.4 KiB
JavaScript
Executable File

"use strict";
const express = require("express"),
config = require("config"),
crypto = require("crypto"),
Promise = require("bluebird");
let photoDB;
require("../db/photos").then(function(db) {
photoDB = db;
});
const router = express.Router();
const picturesPath = config.get("picturesPath").replace(/\/$/, "") + "/";
router.put("/:id?", async (req, res/*, next*/) => {
console.log(`PUT ${req.url}`);
if (!req.user.maintainer) {
return res.status(401).json({ message: "Unauthorized to modify photos."});
}
let { id } = req.params, faces = [];
if (id && isNaN(+id)) {
return res.status(400).json({message: `Invalid id ${id}`});
} else {
if (id) {
faces = [ +id ];
} else {
faces = req.body.faces;
}
}
if (faces.length === 0) {
return res.status(400).json({message: `No faces supplied.`});
}
const { action } = req.body;
console.log(`${action}: ${faces}`);
if ([ 'not-a-face', 'forget' ].indexOf(action) !== -1) {
await photoDB.sequelize.query(
`UPDATE faces SET classifiedBy=:action,identityId=NULL ` +
`WHERE id IN (:faces)`, {
replacements: {
action,
faces
}
}
);
return res.status(200).json(faces);
}
return res.status(400).json({ message: "Invalid request" });
});
router.delete("/:id", function(req, res/*, next*/) {
console.log(`DELETE /${req.params.id}`);
if (!req.user.maintainer) {
return res.status(401).send("Unauthorized to delete photos.");
}
return photoDB.sequelize.query("UPDATE faces SET faceConfidence=0 WHERE id=:id", {
replacements: {
id: req.params.id
}
}).then(() => {
return res.status(200).send({ message: `${req.params.id} deleted` });
}).catch((error) => {
return res.status(500).send({ error: error });
})
});
function getFacesForPhoto(id) {
/* Get the set of faces in this photo */
return photoDB.sequelize.query(
"SELECT * FROM faces WHERE photoId=:id AND faceConfidence>0.9", {
replacements: {
id: id,
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
}).then((faces) => {
/* For each face in the photo, get the related faces */
return photoDB.sequelize.query(
"SELECT relatedFaces.photoId AS photoId,fd.descriptor1Id,fd.descriptor2Id,fd.distanceCosine,relatedFaces.faceConfidence " +
"FROM (SELECT id,photoId,faceConfidence FROM faces WHERE faces.faceConfidence>=0.9 AND faces.id IN (:ids)) AS faces " +
"INNER JOIN faces AS relatedFaces ON relatedFaces.faceConfidence>=0.9 AND relatedFaces.id IN (fd.descriptor1Id,fd.descriptor2Id) " +
"INNER JOIN facedistances AS fd ON fd.distanceCosine<=0.5 " +
" AND (fd.descriptor1Id=faces.id OR fd.descriptor2Id=faces.id) " +
"WHERE (faces.id=fd.descriptor1Id OR faces.id=fd.descriptor2Id) " +
"ORDER BY fd.distanceCosine ASC", {
replacements: {
ids: faces.map(face => face.id),
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
}).then((relatedFaces) => {
faces.forEach((face) => {
face.relatedFaces = relatedFaces.filter((related) => {
return (related.photoId != id && (related.descriptor1Id == face.id || related.descriptor2Id == face.id));
}).map((related) => {
return {
distanceCosine: related.distanceCosine,
faceConfidence: related.faceConfidence,
photoId: related.photoId,
faceId: related.descriptor1Id != face.id ? related.descriptor1Id : related.descriptor2Id
}
});
});
return faces;
});
});
}
router.get("/:id?", (req, res) => {
let id;
if (req.params.id) {
id = parseInt(req.params.id);
if (id != req.params.id) {
return res.status(400).send({ message: "Usage /[id]"});
}
}
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,
raw: true
}).then((results) => {
if (!results[0].count) {
return [];
}
const random = Math.floor(Math.random() * results[0].count);
return photoDB.sequelize.query(
"SELECT * FROM faces WHERE faceConfidence>=0.9 AND identityId IS NULL ORDER BY id LIMIT :index,1", {
replacements: {
index: random
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
});
});
}
return promise.then((faces) => {
if (!faces.length) {
return [];
}
console.log("Looking up " + faces.map(face => face.id).join(","));
return photoDB.sequelize.query(
"SELECT relatedFaces.photoId AS photoId,fd.descriptor1Id,fd.descriptor2Id,fd.distanceCosine,relatedFaces.faceConfidence " +
"FROM (SELECT id,photoId,faceConfidence FROM faces WHERE faces.id IN (:ids)) AS faces " +
"INNER JOIN faces AS relatedFaces ON relatedFaces.identityId IS NULL AND relatedFaces.faceConfidence>=0.9 AND relatedFaces.id IN (fd.descriptor1Id,fd.descriptor2Id) " +
"INNER JOIN facedistances AS fd ON fd.distanceCosine<=0.5 " +
" AND (fd.descriptor1Id=faces.id OR fd.descriptor2Id=faces.id) " +
"WHERE (faces.id=fd.descriptor1Id OR faces.id=fd.descriptor2Id) " +
"ORDER BY fd.distanceCosine ASC",{
replacements: {
ids: faces.map(face => face.id)
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
}).then((relatedFaces) => {
faces.forEach((face) => {
face.relatedFaces = relatedFaces.filter((related) => {
return (related.photoId != faces[0].photoId && (related.descriptor1Id == face.id || related.descriptor2Id == face.id));
}).map((related) => {
return {
distanceCosine: related.distanceCosine,
faceConfidence: related.faceConfidence,
photoId: related.photoId,
faceId: related.descriptor1Id != face.id ? related.descriptor1Id : related.descriptor2Id
}
});
});
return faces;
});
}).then((results) => {
return res.status(200).json(results);
}).catch((error) => {
console.error(error);
return res.status(500).send("Error processing request.");
});
});
module.exports = router;