diff options
| author | Ken D'Ambrosio <ken@jots.org> | 2026-06-08 17:09:51 +0000 |
|---|---|---|
| committer | Ken D'Ambrosio <ken@jots.org> | 2026-06-08 17:09:51 +0000 |
| commit | da28a20f091372375822f9dde4486ecade859e7e (patch) | |
| tree | 80d02f26c1b9d52f1a09e36f5d8946b1e3fedf6a /scripts/faces.py | |
| parent | 4ba9f6451f5ab1e5ae95c0871d6fa594f49372cc (diff) | |
Add opt-in facial recognition: detection and embedding storage
- scripts/faces.py: Python helper using face_recognition (dlib/HOG) to
detect faces and return 128-D encodings as JSON; called by update.rb
- scripts/update.rb: enrich_faces() stores face boxes and encodings in
album.json per image (null = not yet processed, [] = processed/none found);
skips files already processed; gated on faces.enabled in config.yml
- Reads CONFIG_PATH (same env var as app.rb) to check faces.enabled flag
- Feature is off by default; enabled in this install via config.yml
- README.md, DESIGN.md: document installation, opt-in config, data model,
and planned clustering/people-management pipeline
People management UI and clustering script are the next milestone.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'scripts/faces.py')
| -rw-r--r-- | scripts/faces.py | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/scripts/faces.py b/scripts/faces.py new file mode 100644 index 0000000..d072376 --- /dev/null +++ b/scripts/faces.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +""" +Detect faces in an image and return their bounding boxes and 128-D encodings. + +Usage: python3 faces.py <image_path> + +Stdout: JSON array — one object per face: + [{"box": [top, right, bottom, left], "encoding": [128 floats]}, ...] + +Returns "[]" when no faces are found or the image cannot be opened. +Errors are written to stderr; stdout is always valid JSON. +""" +import sys +import json + + +def main(): + if len(sys.argv) < 2: + print("[]") + return + + path = sys.argv[1] + try: + import face_recognition + except ImportError as e: + print(f"face_recognition not available: {e}", file=sys.stderr) + print("[]") + return + + try: + img = face_recognition.load_image_file(path) + except Exception as e: + print(f"Could not load {path}: {e}", file=sys.stderr) + print("[]") + return + + try: + locations = face_recognition.face_locations(img, model="hog") + encodings = face_recognition.face_encodings(img, locations) + result = [ + {"box": list(loc), "encoding": enc.tolist()} + for loc, enc in zip(locations, encodings) + ] + print(json.dumps(result)) + except Exception as e: + print(f"Detection error for {path}: {e}", file=sys.stderr) + print("[]") + + +if __name__ == "__main__": + main() |
