Compare commits

..

2 Commits

Author SHA1 Message Date
58a2baddde Working again
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
2023-01-23 08:49:43 -08:00
3e9438bb27 Switched back to port 80 (http) instead of https in the container
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
2023-01-23 07:54:15 -08:00
7 changed files with 205 additions and 80 deletions

View File

@ -30,6 +30,9 @@ if [[ -z "${DEVELOPMENT}" ]]; then
{ while true; do npm start ; sleep 3 ; done ; }
else
echo "Running in DEVELOPMENT mode."
if [[ ! -d /website/frontend ]]; then
fail "/website/frontend not found. Is the volume mounted? Did you run via './launch.sh'?"
fi
if [[ ! -d /website/frontend/bower_components ]]; then
echo "...installing bower_components for frontend"
cd /website/frontend

View File

@ -226,5 +226,7 @@ with conn:
for identity in reduced:
print(f'Writing identity {identity["id"]} to DB')
id = create_identity(conn, identity)
first = True
for face in identity['faces']:
update_face_identity(conn, face['id'], id)
update_face_identity(conn, face['id'], id, first)
first = False

View File

@ -142,7 +142,7 @@ def create_identity(conn, identity):
conn.commit()
return cur.lastrowid
def update_face_identity(conn, faceId, identityId = None):
def update_face_identity(conn, faceId, identityId = None, first = False):
"""
Update the identity associated with this face
:param conn:
@ -152,8 +152,13 @@ def update_face_identity(conn, faceId, identityId = None):
"""
sql = '''
UPDATE faces SET identityId=? WHERE id=?
'''
'''
cur = conn.cursor()
cur.execute(sql, (identityId, faceId))
if first:
sql = '''
UPDATE identities SET faceId=? WHERE id=?
'''
cur.execute(sql, (faceId, identityId))
conn.commit()
return None

View File

@ -1,7 +1,7 @@
#!/bin/bash
pid=$(ps aux |
grep -E '[0-9] (/usr/bin/)?node .*client.*react-scripts/scripts/start.js' |
grep -E '[0-9] (/usr/bin/)?node .*client.*craco.*scripts/start.js' |
while read user pid rest; do
echo $pid;
done)

View File

@ -111,6 +111,14 @@ function init() {
type: Sequelize.STRING,
allowNull: false
},
faceId: {
type: Sequelize.INTEGER,
sallowNull: true,
references: {
model: Photo,
key: 'id',
}
},
descriptors: Sequelize.BLOB /* average of all faces mapped to this */
}, {
timestamps: false
@ -180,6 +188,10 @@ function init() {
key: 'id',
}
},
distance: {
type: Sequelize.FLOAT,
defaultValue: -1
},
classifiedBy: {
type: Sequelize.DataTypes.ENUM(
'machine',

View File

@ -1,9 +1,10 @@
# DEVELOPMENT -- use npm development server on port 3000 (entrypoint.sh)
location /identities/api/v1/ {
rewrite ^/identities/api/v1/(.*)$ /api/v1/$1 break;
proxy_pass https://localhost/;
proxy_pass http://localhost:8123;
proxy_redirect off;
proxy_set_header Host $host;
proxy_ssl_verify off;
}
location /identities {
@ -17,5 +18,6 @@ location /identities {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_ssl_verify off;
proxy_pass https://localhost:3000;
}

View File

@ -11,7 +11,7 @@ require("../db/photos").then(function(db) {
const router = express.Router();
const addOrUpdateIdentity = async(id, {
const upsertIdentity = async(id, {
displayName,
firstName,
lastName,
@ -106,7 +106,7 @@ router.post('/', async (req, res) => {
return res.status(401).send({ message: "Unauthorized to modify photos." });
}
const identity = await addOrUpdateIdentity(-1, req.body, res);
const identity = await upsertIdentity(-1, req.body, res);
if (!identity) {
return;
}
@ -126,7 +126,7 @@ router.put('/:id', async (req, res) => {
return res.status(400).send({ message: `Invalid identity id ${id}` });
}
const identity = await addOrUpdateIdentity(id, req.body, res);
const identity = await upsertIdentity(id, req.body, res);
if (!identity) {
return;
}
@ -135,6 +135,35 @@ router.put('/:id', async (req, res) => {
});
router.delete('/:id', async (req, res) => {
console.log(`DELETE ${req.url}`)
if (!req.user.maintainer) {
console.warn(`${req.user.name} attempted to modify photos.`);
return res.status(401).send({ message: "Unauthorized to modify photos." });
}
const { id } = req.params;
if (!id || isNaN(+id)) {
return res.status(400).send({ message: `Invalid identity id ${id}` });
}
await photoDB.sequelize.query(
'UPDATE faces SET distance=0,identityId=NULL ' +
'WHERE identityId=:id', {
replacements: { id }
}
);
await photoDB.sequelize.query(
'DELETE FROM identities ' +
'WHERE identityId=:id', {
replacements: { id }
});
return res.status(200).send({});
});
const addFaceToIdentityDescriptors = (identity, face) => {
};
@ -331,6 +360,105 @@ const getUnknownIdentity = async (faceCount) => {
return unknownIdentity;
}
/* Compute the identity's centroid descriptor from all faces
* and determine closest face to that centroid. If either of
* those values have changed, update the identity.
*
* Also updates the 'distance' on each face to the identity
* centroid.
*/
const updateIdentityFaces = async (identity) => {
if (!identity.identityId) {
identity.identityId = identity.id;
}
const faces = await photoDB.sequelize.query(
"SELECT " +
"faces.*,faceDescriptors.* " +
"FROM faces,faceDescriptors " +
"WHERE " +
"faces.identityId=:identityId " +
"AND faceDescriptors.id=faces.descriptorId", {
replacements: identity,
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
});
let average = undefined,
closestId = -1,
closestDistance = -1,
count = 0;
faces.forEach((face) => {
if (!identity.descriptors) {
return;
}
if (!face.descriptors) {
return;
}
face.distance = euclideanDistance(
face.descriptors,
identity.descriptors
);
face.descriptors = bufferToFloat32Array(face.descriptors).map(x => x * x);
if (closestId === -1) {
closestId = face.id;
closestDistance = face.distance;
average = descriptors;
count = 1;
return;
}
descriptors.forEach((x, i) => {
average[i] += x;
});
count++;
if (face.distance < closestDistance) {
closestDistance = face.distance;
closestId = face.id;
}
});
let same = true;
if (average) {
average = average.map(x => x / count);
same = bufferToFloat32Array(identity.descriptors)
.find((x, i) => average[i] === x) === undefined;
await Promise(faces, async (face) => {
const distance = euclideanDistanceArray(face.descriptors, average);
if (distance !== face.distance) {
await photoDB.sequelize.query(
'UPDATE faces SET distance=:distance WHERE id=:faceId', {
replacements: face
}
);
}
});
}
let sql = '';
if (closestId !== -1 && closestId !== identity.faceId) {
sql = `${sql} faceId=:faceId`;
}
if (!same) {
if (sql !== '') {
sql = `${sql}, `;
}
sql = `${sql} descriptors=:descriptors`;
identity.descriptors = average;
}
if (sql !== '') {
identity.faceId = closestId;
await photoDB.sequelize.query(
`UPDATE identities SET ${sql} ` +
`WHERE id=:identityId`, {
replacements: identity
}
);
}
};
router.get("/:id?", async (req, res) => {
console.log(`GET ${req.url}`);
@ -352,100 +480,73 @@ router.get("/:id?", async (req, res) => {
const filter = id ? "WHERE identities.id=:id " : "";
const identities = await photoDB.sequelize.query("SELECT " +
"identities.*," +
"GROUP_CONCAT(faces.id) AS relatedFaceIds," +
"GROUP_CONCAT(faces.descriptorId) AS relatedFaceDescriptorIds," +
"GROUP_CONCAT(faces.photoId) AS relatedFacePhotoIds " +
const identities = await photoDB.sequelize.query(
"SELECT " +
"identities.id AS identityId," +
"identities.firstName," +
"identities.lastName," +
"identities.middleName," +
"identities.displayName," +
"identities.faceId " +
"FROM identities " +
"LEFT JOIN faces ON identities.id=faces.identityId " +
filter +
"GROUP BY identities.id", {
filter, {
replacements: { id },
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
});
await Promise.map(identities, async (identity) => {
[ 'firstName', 'middleName', 'lastName' ].forEach(key => {
if (!identity[key]) {
identity[key] = '';
for (let field in identity) {
if (field.match(/.*Name/) && identity[field] === null) {
identity[field] = '';
}
});
identity.identityId = identity.id;
if (!identity.relatedFaceIds) {
identity.relatedFaces = [];
} else {
const relatedFaces = identity.relatedFaceIds.split(","),
relatedFacePhotos = identity.relatedFacePhotoIds.split(",");
let descriptors = await photoDB.sequelize.query(
`SELECT descriptors FROM facedescriptors WHERE id in (:ids)`, {
replacements: {
ids: identity.relatedFaceDescriptorIds.split(',')
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
}
);
descriptors = descriptors.map(entry => entry.descriptors);
identity.relatedFaces = relatedFaces.map((faceId, index) => {
let distance = 0;
if (descriptors[index] && identity.descriptors) {
distance = euclideanDistance(
descriptors[index],
identity.descriptors
);
} else {
distance = -1;
}
return {
identityId: identity.id,
faceId,
photoId: relatedFacePhotos[index],
distance
};
});
}
let where, limit = '';
/* If id was not set, only return a single face */
if (id === undefined) {
if (identity.faceId !== -1 && identity.faceId !== null) {
where = 'faceId=:faceId';
} else {
where = 'identityId=:identityId';
limit = 'LIMIT 1';
}
} else {
where = 'identityId=:identityId'
}
identity.relatedFaces = await photoDB.sequelize.query(
'SELECT id as faceId,identityId,photoId,distance ' +
'FROM faces ' +
`WHERE ${where} ` +
'ORDER BY distance ASC ' +
limit, {
replacements: identity,
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
}
);
if (identity.relatedFaces.length !== 0
&& (!identity.faceId || identity.faceId === -1)) {
await updateIdentityFaces(identity);
}
/* If there were no faces, then add a 'Unknown' face */
if (identity.relatedFaces.length === 0) {
identity.relatedFaces.push({
faceId: -1,
photoId: -1,
identityId: identity.id,
identityId: identity.identityId,
distance: 0,
faceConfidence: 0
});
}
identity
.relatedFaces
.sort((A, B) => {
return A.distance - B.distance;
});
/* If no filter was specified, only return the best face for
* the identity */
if (!filter) {
identity.relatedFaces = [ identity.relatedFaces[0] ];
}
delete identity.id;
delete identity.descriptors;
delete identity.relatedFaceIds;
delete identity.relatedFacePhotoIds;
delete identity.relatedFaceDescriptorIds;
delete identity.relatedIdentityDescriptors;
});
/* If no ID was provided (so no 'filter') then this call is returning
/* If no ID was provided then this call is returning
* a list of all identities -- we create a fake identity for all
* unlabeled faces */
if (!filter) {
if (!id) {
const unknownIdentity = await getUnknownIdentity(1)
identities.push(unknownIdentity);
}