128 lines
4.8 KiB
JavaScript
128 lines
4.8 KiB
JavaScript
/*
|
|
* Face recognition:
|
|
* 1. For each photo, extract all faces. Store face rectangles.
|
|
* face_id unique
|
|
* photo_id foreign key
|
|
* top left bottom right
|
|
* identity_id
|
|
* distance (0 == truth; manually assigned identity)
|
|
* 2. For each face_id, create:
|
|
* /${picturesPath}face-data/${face_id % 100}/
|
|
* ${face_id}-normalized
|
|
* ${face_id}-original
|
|
* ${face_id}-data
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
process.env.TZ = "Etc/GMT";
|
|
|
|
console.log("Loading face-recognizer");
|
|
|
|
const config = require("config"),
|
|
Promise = require("bluebird"),
|
|
{ mkdir, unlink } = require("./lib/util"),
|
|
fr = require("face-recognition");
|
|
|
|
require("./console-line.js"); /* Monkey-patch console.log with line numbers */
|
|
|
|
const picturesPath = config.get("picturesPath").replace(/\/$/, "") + "/",
|
|
faceData = picturesPath + "face-data/";
|
|
|
|
let photoDB = null, faceDataExists = false;
|
|
|
|
console.log("Loading pictures out of: " + picturesPath);
|
|
|
|
require("./db/photos").then(function(db) {
|
|
photoDB = db;
|
|
}).then(() => {
|
|
console.log("DB connected.");
|
|
}).then(() => {
|
|
console.log("Beginning face detection scanning.");
|
|
return photoDB.sequelize.query("SELECT photos.id,photos.filename,photos.width,photos.height,albums.path " +
|
|
"FROM photos " +
|
|
"LEFT JOIN albums ON (albums.id=photos.albumId) " +
|
|
"WHERE faces=-1 ORDER BY albums.path,photos.filename", {
|
|
type: photoDB.sequelize.QueryTypes.SELECT
|
|
}
|
|
).then((results) => {
|
|
console.log(`${results.length} photos have not had faces scanned.`);
|
|
return Promise.map(results, (photo) => {
|
|
const filePath = photo.path + photo.filename;
|
|
console.log(`Processing ${filePath}...`);
|
|
|
|
return photoDB.sequelize.transaction(function(transaction) {
|
|
/* Remove any existing face data for this photo */
|
|
return photoDB.sequelize.query("SELECT id FROM faces WHERE photoId=:id", {
|
|
transaction: transaction,
|
|
replacements: photo,
|
|
}).then((faces) => {
|
|
/* For each face-id, remove any face-data files, and then remove all the entries
|
|
* from the DB */
|
|
return Promise.map(faces, (id) => {
|
|
return Promise.mapSeries(["-normalized.png", "-data.json" ], (fileSuffix) => {
|
|
const filePath = faceData + "/" + (id % 100) + "/" + id + fileSuffix;
|
|
return exists(filePath).then((result) => {
|
|
console.log(`...removing ${filePath}`);
|
|
return unlink(filePath);
|
|
});
|
|
});
|
|
}).then(() => {
|
|
return photoDB.sequelize.query("DELETE FROM faces WHERE photoId=:id", {
|
|
transaction: transaction,
|
|
replacements: photo,
|
|
});
|
|
}).then(() => {
|
|
const image = fr.loadImage(filePath),
|
|
detector = fr.FaceDetector();
|
|
|
|
console.log("...detecting faces.");
|
|
const faceRectangles = detector.locateFaces(image)
|
|
if (faceRectangles.length == 0) {
|
|
console.log("...no faces found in image.");
|
|
return;
|
|
}
|
|
|
|
/* Create a face entry in photos for each face found. */
|
|
const faceImages = detector.detectFaces(image, 200)
|
|
|
|
console.log(`...saving ${faceImages.length} faces.`);
|
|
|
|
return Promise.map(faceRectangles, (face, index) => {
|
|
return photoDB.sequelize.query("INSERT INTO faces (photoId,top,left,bottom,right,faceConfidence) " +
|
|
"VALUES (:id,:top,:left,:bottom,:right,:faceConfidence)", {
|
|
replacements: {
|
|
id: photo.id,
|
|
top: face.top / photo.height,
|
|
left: face.left / photo.width,
|
|
bottom: face.top / photo.height,
|
|
right: face.right / photo.width,
|
|
faceConfidence: face.confidence
|
|
},
|
|
transaction: transaction
|
|
}).spread((results, metadata) => {
|
|
return metadata.lastID;
|
|
}).then((id) => {
|
|
console.log(`...DB id ${id}. Writing data and images...`);
|
|
const filePathPrefix = faceData + "/" + (id % 100) + "/" + id;
|
|
/* https://medium.com/@ageitgey/machine-learning-is-fun-part-4-modern-face-recognition-with-deep-learning-c3cffc121d78 */
|
|
const data = [];
|
|
for (let i = 0; i < 128; i++) {
|
|
data.push(Math.random() - 0.5);
|
|
}
|
|
fs.writeFileSync(filePathPrefix + "-data.json", JSON.stringify(data));
|
|
fr.saveImage(filePathPrefix + "-normalized.png", faceImages[index]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}).then(() => {
|
|
console.log("Face detection scanning completed.");
|
|
}).catch((error) => {
|
|
console.error(error);
|
|
process.exit(-1);
|
|
});
|