From 3710b573ea12b6bd65a4a099631bb8917b1a9370 Mon Sep 17 00:00:00 2001 From: Damien De Paoli Date: Wed, 13 Jul 2022 00:26:24 +1000 Subject: [PATCH] added auto-rotate to settings, not used in job mgr yet --- BUGs | 2 ++ Dockerfile | 2 +- TODO | 3 ++- settings.py | 11 +++++++++-- shared.py | 1 + tables.sql | 3 ++- templates/settings.html | 10 ++++++---- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/BUGs b/BUGs index 1e134f3..ab7241f 100644 --- a/BUGs +++ b/BUGs @@ -1,3 +1,5 @@ ### Next: 92 BUG-85: once we rebuild data from scratch, need to reset just clean out pa_user_state's BUG-91: face_recognition not working on many of Mandy's newer phone images + -- its when a photo is rotated -- can use: + exifautotran file.jpg, and then all should work (no loss), could then skip fix in thumbs too? diff --git a/Dockerfile b/Dockerfile index b1cf7e3..b2ca7e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN truncate -s0 /tmp/preseed.cfg && \ apt-get install -y tzdata ## cleanup of files from setup RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -RUN apt-get update && apt-get -y install python3-pip python3-psycopg2 libpq-dev gunicorn mediainfo cmake libgl1-mesa-glx libglib2.0-0 python3-ldap +RUN apt-get update && apt-get -y install python3-pip python3-psycopg2 libpq-dev gunicorn mediainfo cmake libgl1-mesa-glx libglib2.0-0 python3-ldap libjpeg-turbo-progs COPY requirements.txt requirements.txt RUN pip3 install -r requirements.txt RUN pip3 install --upgrade pillow --user diff --git a/TODO b/TODO index e0270ea..faa2533 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,8 @@ * on viewer: - allow face to be used to: - create person - [PARTIAL - person.py to go] - add to existing person + [DONE] - add to existing person + --> still need to consider whether we trigger an AI search immediately [DONE] - ignore/not a face/too young [DONE] - redraw 'ignore's as a greyed out box? [DONE] - menu should only allow override IF we have put override on... diff --git a/settings.py b/settings.py index 13c61d8..5233c0c 100644 --- a/settings.py +++ b/settings.py @@ -1,4 +1,4 @@ -from wtforms import SubmitField, StringField, IntegerField, FloatField, HiddenField, validators, Form, SelectField +from wtforms import SubmitField, StringField, IntegerField, FloatField, HiddenField, validators, Form, SelectField, BooleanField from flask_wtf import FlaskForm from flask import request, render_template, redirect, url_for from main import db, app, ma @@ -30,6 +30,7 @@ class Settings(db.Model): import_path = db.Column(db.String) storage_path = db.Column(db.String) recycle_bin_path = db.Column(db.String) + auto_rotate = db.Column(db.Boolean) default_refimg_model = db.Column(db.Integer,db.ForeignKey('ai_model.id'), unique=True, nullable=False) default_scan_model = db.Column(db.Integer,db.ForeignKey('ai_model.id'), unique=True, nullable=False) default_threshold = db.Column(db.Integer) @@ -41,7 +42,7 @@ class Settings(db.Model): job_archive_age = db.Column(db.Integer) def __repr__(self): - return f"" + return f"" ################################################################################ # Helper class that inherits a .dump() method to turn class Settings into json / useful in jinja2 @@ -63,6 +64,7 @@ class SettingsForm(FlaskForm): import_path = StringField('Path(s) to import from:', [validators.DataRequired()]) storage_path = StringField('Path to store sorted images to:', [validators.DataRequired()]) recycle_bin_path = StringField('Path to temporarily store deleted images in:', [validators.DataRequired()]) + auto_rotate = BooleanField('Automatically rotate jpegs based on exif', [validators.AnyOf([True, False])]) default_refimg_model = SelectField( 'Default model to use for reference images', choices=[(c.id, c.name) for c in AIModel.query.order_by('id')] ) default_scan_model = SelectField( 'Default model to use for all scanned images', choices=[(c.id, c.name) for c in AIModel.query.order_by('id')] ) default_threshold = StringField('Face Distance threshold (below is a match):', [validators.DataRequired()]) @@ -87,6 +89,7 @@ def settings(): HELP['import_path']="Path(s) to import files from. If starting with /, then used literally, otherwise base path is prepended" HELP['storage_path']="Path(s) to store sorted files to. If starting with /, then used literally, otherwise base path is prepended" HELP['recycle_bin_path']="Path where deleted files are moved to. If starting with /, then used literally, otherwise base path is prepended" + HELP['auto_rotate']="Automatically rotate jpegs based on exif to orient them so that AI matching will work. NOTE: this actually changes/rewrites the file - as it is a simple rotate, it is down without losing quality/content" HELP['default_refimg_model']="Default face recognition model used for reference images - cnn is slower/more accurate, hog is faster/less accurate - we scan (small) refimg once, so cnn is okay" HELP['default_scan_model']="Default face recognition model used for scanned images - cnn is slower/more accurate, hog is faster/less accurate - we scan (large) scanned images lots, so cnn NEEDS gpu/mem" HELP['default_threshold']="The distance below which a face is considered a match. The default is usually 0.6, we are trying for about 0.55 with kids. YMMV" @@ -106,6 +109,10 @@ def settings(): s.import_path = request.form['import_path'] s.storage_path = request.form['storage_path'] s.recycle_bin_path = request.form['recycle_bin_path'] + if 'auto_rotate' in request.form: + s.auto_rotate = True + else: + s.auto_rotate = False s.default_refimg_model = request.form['default_refimg_model'] s.default_scan_model = request.form['default_scan_model'] s.default_threshold = request.form['default_threshold'] diff --git a/shared.py b/shared.py index aa67238..c45cef9 100644 --- a/shared.py +++ b/shared.py @@ -140,6 +140,7 @@ def GenThumb(fname): def GenFace(fname, model): img = face_recognition.load_image_file(fname) location = face_recognition.face_locations(img, model=model) + print( f"locn={location}" ) encodings = face_recognition.face_encodings(img, known_face_locations=location) if len(encodings) and len(location): return encodings[0].tobytes(), location[0] diff --git a/tables.sql b/tables.sql index 8c98913..cac8c0b 100644 --- a/tables.sql +++ b/tables.sql @@ -7,6 +7,7 @@ insert into AI_MODEL values ( 2, 'cnn', 'more accurate / much slower' ); create table SETTINGS( ID integer, BASE_PATH varchar, IMPORT_PATH varchar, STORAGE_PATH varchar, RECYCLE_BIN_PATH varchar, + AUTO_ROTATE Boolean, DEFAULT_REFIMG_MODEL integer, DEFAULT_SCAN_MODEL integer, DEFAULT_THRESHOLD float, FACE_SIZE_LIMIT integer, SCHEDULED_IMPORT_SCAN integer, SCHEDULED_STORAGE_SCAN integer, @@ -166,7 +167,7 @@ insert into PERSON values ( (select nextval('PERSON_ID_SEQ')), 'mum', 'Mandy', ' insert into PERSON values ( (select nextval('PERSON_ID_SEQ')), 'cam', 'Cameron', 'De Paoli' ); insert into PERSON values ( (select nextval('PERSON_ID_SEQ')), 'mich', 'Michelle', 'De Paoli' ); -- DEV(ddp): -insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) values ( (select nextval('SETTINGS_ID_SEQ')), '/home/ddp/src/photoassistant/', 'images_to_process/', 'photos/', '.pa_bin/', 1, 1, '0.55', 43, 1, 1, 7, 30, 3 ); +insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, auto_rotate, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) values ( (select nextval('SETTINGS_ID_SEQ')), '/home/ddp/src/photoassistant/', 'images_to_process/', 'photos/', '.pa_bin/', true, 1, 1, '0.55', 43, 1, 1, 7, 30, 3 ); -- DEV(cam): --insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) values ( (select nextval('SETTINGS_ID_SEQ')), 'c:/Users/cam/Desktop/code/python/photoassistant/', 'c:\images_to_process', 'photos/', '.pa_bin/', 1, 1, '0.55', 43, 1, 1, 7, 30, 3 ); -- PROD: diff --git a/templates/settings.html b/templates/settings.html index 6c0fc5f..6b84eb6 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -8,17 +8,19 @@ {{field}} {% elif field.type != 'SubmitField' %}
- {{ field.label( class="input-group-text col-3 justify-content-end", title=HELP[field.name] ) }} + {{ field.label( class="input-group-text col-4 justify-content-end", title=HELP[field.name] ) }} {% if field.type == 'SelectField' %} - {{ field( class="form-select col-9" ) }} + {{ field( class="form-select col-8" ) }} + {% elif field.type == 'BooleanField' %} + {{ field( class="form-check form-check-input mt-2" ) }} {% else %} - {{ field( class="form-control col-9" ) }} + {{ field( class="form-control col-8" ) }} {% endif %}
{% endif %} {% endfor %}
- {{form.submit(class="btn btn-primary offset-2 col-2 mt-4" )}} + {{form.submit(class="btn btn-primary offset-4 col-2 mt-4" )}}