diff --git a/docker-compose.yml b/docker-compose.yml
index 9a284bc..c29e695 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -18,5 +18,6 @@ services:
- ${PWD}/config/local.json:/website/config/local.json
- /opt/ketrface/models:/root/.deepface
# - ${PWD}:/website
+ - ${PWD}/ketrface:/website/ketrface
- ${PWD}/frontend:/website/frontend
- ${PWD}/server:/website/server
diff --git a/frontend/clusters.html b/frontend/clusters.html
deleted file mode 100644
index bb72a25..0000000
--- a/frontend/clusters.html
+++ /dev/null
@@ -1,199 +0,0 @@
-
-
-
-
-
diff --git a/frontend/identities.html b/frontend/identities.html
index 1d0e3e2..9c3bfe7 100755
--- a/frontend/identities.html
+++ b/frontend/identities.html
@@ -165,7 +165,7 @@ function getIdentities(faceId) {
const identitiesBlock = document.getElementById("identities");
identitiesBlock.innerHTML = "";
- const search = faceId ? "?withScore=" + faceId : "";
+ const search = ''; //faceId ? "?withScore=" + faceId : "";
window.fetch("api/v1/identities" + search).then(res => res.json()).then((identities) => {
identities.sort((a, b) => {
if (a.lastName == b.lastName) {
diff --git a/ketrface/cluster.py b/ketrface/cluster.py
index 57926c0..95d8c00 100644
--- a/ketrface/cluster.py
+++ b/ketrface/cluster.py
@@ -197,18 +197,19 @@ straglers = build_straglers(faces)
reduced = reduced + DBSCAN(straglers)
# Build a final cluster with all remaining uncategorized faces
-remaining_cluster = {
- 'id': len(reduced) + 1,
- 'distance': 0,
- 'descriptors': [],
- 'cluster': Undefined,
- 'faces': []
-}
-straglers = build_straglers(faces)
-for face in straglers:
- face['cluster'] = remaining_cluster
- remaining_cluster['faces'].append(face)
-reduced.append(remaining_cluster)
+if False:
+ remaining_cluster = {
+ 'id': len(reduced) + 1,
+ 'distance': 0,
+ 'descriptors': [],
+ 'cluster': Undefined,
+ 'faces': []
+ }
+ straglers = build_straglers(faces)
+ for face in straglers:
+ face['cluster'] = remaining_cluster
+ remaining_cluster['faces'].append(face)
+ reduced.append(remaining_cluster)
# Give all merged identity lists a unique ID
for id, identity in enumerate(reduced):
@@ -241,3 +242,46 @@ print('Writing to "auto-clusters.html"')
redirect_on(os.path.join(html_path, 'auto-clusters.html'))
gen_html(reduced)
redirect_off()
+
+def create_identity(conn, identity):
+ """
+ Create a new identity in the identities table
+ :param conn:
+ :param identity:
+ :return: identity id
+ """
+ sql = '''
+ INSERT INTO identities(descriptors,displayName)
+ VALUES(?,?)
+ '''
+ cur = conn.cursor()
+ cur.execute(sql, (
+ np.array(identity['descriptors']),
+ f'cluster-{identity["id"]}'
+ ))
+ conn.commit()
+ return cur.lastrowid
+
+def update_face_identity(conn, faceId, identityId = None):
+ """
+ Update the identity associated with this face
+ :param conn:
+ :param faceId:
+ :param identityId:
+ :return: None
+ """
+ sql = '''
+ UPDATE faces SET identityId=? WHERE id=?
+ '''
+ cur = conn.cursor()
+ cur.execute(sql, (identityId, faceId))
+ conn.commit()
+ return None
+
+print(f'Connecting to database: {db_path}')
+conn = create_connection(db_path)
+with conn:
+ for identity in reduced:
+ id = create_identity(conn, identity)
+ for face in identity['faces']:
+ update_face_identity(conn, face['id'], id)
diff --git a/ketrface/ketrface/.gitignore b/ketrface/ketrface/.gitignore
new file mode 100644
index 0000000..bee8a64
--- /dev/null
+++ b/ketrface/ketrface/.gitignore
@@ -0,0 +1 @@
+__pycache__
diff --git a/ketrface/ketrface/__pycache__/__init__.cpython-310.pyc b/ketrface/ketrface/__pycache__/__init__.cpython-310.pyc
deleted file mode 100644
index 1faa59e..0000000
Binary files a/ketrface/ketrface/__pycache__/__init__.cpython-310.pyc and /dev/null differ
diff --git a/ketrface/ketrface/__pycache__/db.cpython-310.pyc b/ketrface/ketrface/__pycache__/db.cpython-310.pyc
deleted file mode 100644
index 057e651..0000000
Binary files a/ketrface/ketrface/__pycache__/db.cpython-310.pyc and /dev/null differ
diff --git a/ketrface/ketrface/__pycache__/dbscan.cpython-310.pyc b/ketrface/ketrface/__pycache__/dbscan.cpython-310.pyc
deleted file mode 100644
index 8d11d0b..0000000
Binary files a/ketrface/ketrface/__pycache__/dbscan.cpython-310.pyc and /dev/null differ
diff --git a/ketrface/ketrface/__pycache__/util.cpython-310.pyc b/ketrface/ketrface/__pycache__/util.cpython-310.pyc
deleted file mode 100644
index a196fda..0000000
Binary files a/ketrface/ketrface/__pycache__/util.cpython-310.pyc and /dev/null differ
diff --git a/server/routes/identities.js b/server/routes/identities.js
index ebde173..b264847 100755
--- a/server/routes/identities.js
+++ b/server/routes/identities.js
@@ -26,7 +26,8 @@ router.put("/faces/add/:id", (req, res) => {
return res.status(400).send("No faces supplied.");
}
- return photoDB.sequelize.query("UPDATE faces SET identityId=:identityId,identityDistance=0 " +
+ return photoDB.sequelize.query(
+ "UPDATE faces SET identityId=:identityId " +
"WHERE id IN (:faceIds)", {
replacements: {
identityId: id,
@@ -71,7 +72,8 @@ router.post("/", (req, res) => {
replacements: identity,
}).then(([ results, metadata ]) => {
identity.id = metadata.lastID;
- return photoDB.sequelize.query("UPDATE faces SET identityId=:identityId,identityDistance=0 " +
+ return photoDB.sequelize.query(
+ "UPDATE faces SET identityId=:identityId " +
"WHERE id IN (:faceIds)", {
replacements: {
identityId: identity.id,
@@ -102,7 +104,7 @@ function euclideanDistance(a, b) {
return Math.sqrt(sum);
}
-router.get("/:id?", (req, res) => {
+router.get("/:id?", async (req, res) => {
let id;
if (req.params.id) {
@@ -114,7 +116,7 @@ router.get("/:id?", (req, res) => {
const filter = id ? "WHERE identities.id=:id " : "";
- return photoDB.sequelize.query("SELECT " +
+ const identities = await photoDB.sequelize.query("SELECT " +
"identities.*," +
"GROUP_CONCAT(faces.id) AS relatedFaceIds," +
"GROUP_CONCAT(faces.photoId) AS relatedFacePhotoIds," +
@@ -127,79 +129,88 @@ router.get("/:id?", (req, res) => {
replacements: { id },
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
- }).then((identities) => {
- identities.forEach((identity) => {
- const relatedFaces = identity.relatedFaceIds.split(","),
- relatedFacePhotos = identity.relatedFacePhotoIds.split(","),
- relatedIdentityDistances = identity.relatedIdentityDistances.split(",");
- if (relatedFaces.length != relatedFacePhotos.length) {
- console.warn("Face ID to Photo ID mapping doesn't match!");
+ });
+
+ identities.forEach((identity) => {
+ [ 'firstName', 'middleName', 'lastName' ].forEach(key => {
+ if (!identity[key]) {
+ identity[key] = '';
}
- delete identity.relatedFaceIds;
- delete identity.relatedFacePhotoIds;
- identity.relatedFaces = relatedFaces.map((faceId, index) => {
- const distance = euclideanDistance(
- relatedIdentityDistances[index],
- identity.descriptors
- );
- return {
- faceId: faceId,
- photoId: relatedFacePhotos[index],
- distance
- };
- });
});
- if (!req.query.withScore) {
- console.log("No score request.");
- return identities;
- }
- console.log("Looking up score against: " + req.query.withScore);
+ const relatedFaces = identity.relatedFaceIds.split(","),
+ relatedFacePhotos = identity.relatedFacePhotoIds.split(","),
+ relatedIdentityDescriptors =
+ identity.relatedIdentityDescriptors.split(",");
- return Promise.map(identities, (identity) => {
- return photoDB.sequelize.query("SELECT * FROM facedescriptors WHERE faceId IN (:id,:faceIds)", {
- replacements: {
- id: parseInt(req.query.withScore),
- faceIds: identity.relatedFaces.map(face => face.faceId)
- },
- type: photoDB.Sequelize.QueryTypes.SELECT,
- raw: true
- }).then((descriptors) => {
- let target;
- for (let i = 0; i < descriptors.length; i++) {
- if (descriptors[i].faceId == req.query.withScore) {
- target = descriptors[i].descriptors;
- break;
- }
- }
- if (!target) {
- console.warn("Could not find descriptor for requested face: " + req.query.withScore);
+ identity.relatedFaces = relatedFaces.map((faceId, index) => {
+ const distance = euclideanDistance(
+ relatedIdentityDescriptors[index],
+ identity.descriptors
+ );
+ return {
+ faceId,
+ photoId: relatedFacePhotos[index],
+ distance
+ };
+ });
+
+ delete identity.relatedFaceIds;
+ delete identity.relatedFacePhotoIds;
+ delete identity.relatedIdentityDescriptors;
+ });
+
+ //if (!req.query.withScore) {
+ console.log("No score request.");
+ return res.status(200).json(identities);
+ //}
+
+ // THe rest of this routine needs to be reworked -- I don't
+ // recall what it was doing; maybe getting a list of all identities
+ // sorted with distance to this faceId?
+ console.log("Looking up score against: " + req.query.withScore);
+
+ await Promise.map(identities, async (identity) => {
+ const descriptors = photoDB.sequelize.query(
+ "SELECT id FROM facedescriptors " +
+ "WHERE descriptorId " +
+ "IN (:id,:descriptorIds)", {
+ replacements: {
+ id: parseInt(req.query.withScore),
+ descriptorIds: identity.relatedFaces.map(
+ face => parseInt(face.faceId))
+ },
+ type: photoDB.Sequelize.QueryTypes.SELECT,
+ raw: true
+ });
+ let target;
+ for (let i = 0; i < descriptors.length; i++) {
+ if (descriptors[i].descriptorId == req.query.withScore) {
+ target = descriptors[i].descriptors;
+ break;
+ }
+ }
+ if (!target) {
+ console.warn("Could not find descriptor for requested face: " + req.query.withScore);
+ return;
+ }
+
+ /* For each face's descriptor returned for this identity, compute the distance between the
+ * requested photo and that face descriptor */
+ descriptors.forEach((descriptor) => {
+ for (let i = 0; i < identity.relatedFaces.length; i++) {
+ if (identity.relatedFaces[i].faceId == descriptor.faceId) {
+ identity.relatedFaces[i].distance = euclideanDistance(target, descriptor.descriptors);
+ identity.relatedFaces[i].descriptors = descriptor.descriptors;
return;
}
-
- /* For each face's descriptor returned for this identity, compute the distance between the
- * requested photo and that face descriptor */
- descriptors.forEach((descriptor) => {
- for (let i = 0; i < identity.relatedFaces.length; i++) {
- if (identity.relatedFaces[i].faceId == descriptor.faceId) {
- identity.relatedFaces[i].distance = euclideanDistance(target, descriptor.descriptors);
- identity.relatedFaces[i].descriptors = descriptor.descriptors;
- return;
- }
- }
- });
- });
- }, {
- concurrency: 5
- }).then(() => {
- return identities;
+ }
});
- }).then((identities) => {
- return res.status(200).json(identities);
- }).catch((error) => {
- console.error(error);
- return res.status(500).send("Error processing request.");
+ }, {
+ concurrency: 5
});
+
+ return res.status(200).json(identities);
});
module.exports = router;