From e1a6348967b96e40ed4c90de2922daa693326cbc Mon Sep 17 00:00:00 2001 From: Damien De Paoli Date: Wed, 2 Feb 2022 21:12:14 +1100 Subject: [PATCH] face_size_limit added so we ignore faces that are too small --- pa_job_manager.py | 11 +++++++---- settings.py | 6 +++++- tables.sql | 7 ++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pa_job_manager.py b/pa_job_manager.py index 2cc1acb..9b5d77c 100644 --- a/pa_job_manager.py +++ b/pa_job_manager.py @@ -242,13 +242,14 @@ class Settings(Base): default_refimg_model = Column(Integer,ForeignKey('ai_model.id'), unique=True, nullable=False) default_scan_model = Column(Integer,ForeignKey('ai_model.id'), unique=True, nullable=False) default_threshold = Column(Integer) + face_size_limit = Column(Integer) scheduled_import_scan = Column(Integer) scheduled_storage_scan = Column(Integer) scheduled_bin_cleanup = Column(Integer) bin_cleanup_file_age = Column(Integer) def __repr__(self): - return f"" + return f"" ################################################################################ # Class describing Person to Refimg link in DB via sqlalchemy @@ -1913,16 +1914,18 @@ def InitialValidationChecks(): #################################################################################################################################### # AddFaceToFile(): adds the specified face, location & model_used to the specified file #################################################################################################################################### -def AddFaceToFile( locn_data, face_data, file_eid, model_id ): +def AddFaceToFile( locn_data, face_data, file_eid, model_id, settings ): w = locn_data[1] - locn_data[3] h = locn_data[2] - locn_data[0] + if w < settings.face_size_limit or h < settings.face_size_limit: + return face = Face( face=face_data.tobytes(), locn=json.dumps(locn_data), w=w, h=h ) session.add(face) session.commit() ffl = FaceFileLink( face_id=face.id, file_eid=file_eid, model_used=model_id ) session.add(ffl) session.commit() - return face + return #################################################################################################################################### # DelFacesForFile(): quick func to delete any faces associated with the specified file @@ -2022,7 +2025,7 @@ def ScanFileForPerson( job, e, force=False ): face_locations = face_recognition.face_locations(im, model=model.name ) unknown_encodings = face_recognition.face_encodings(im, known_face_locations=face_locations) for locn, face in zip( face_locations, unknown_encodings ): - AddFaceToFile( locn, face, e.id, model.id ) + AddFaceToFile( locn, face, e.id, model.id, settings ) file_h.faces_created_on = time.time() faces = session.query(Face).join(FaceFileLink).filter(FaceFileLink.file_eid==e.id).all() diff --git a/settings.py b/settings.py index 104e520..13c61d8 100644 --- a/settings.py +++ b/settings.py @@ -33,6 +33,7 @@ class Settings(db.Model): 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) + face_size_limit = db.Column(db.Integer) scheduled_import_scan = db.Column(db.Integer) scheduled_storage_scan = db.Column(db.Integer) scheduled_bin_cleanup = db.Column(db.Integer) @@ -40,7 +41,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 @@ -65,6 +66,7 @@ class SettingsForm(FlaskForm): 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()]) + face_size_limit = StringField('Minimum size of a face:', [validators.DataRequired()]) scheduled_import_scan = IntegerField('Days between forced scan of import path', [validators.DataRequired()]) scheduled_storage_scan = IntegerField('Days between forced scan of storage path', [validators.DataRequired()]) scheduled_bin_cleanup = IntegerField('Days between checking to clean Recycle Bin:', [validators.DataRequired()]) @@ -88,6 +90,7 @@ def settings(): 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" + HELP['face_size_limit']="The minimum size of a AI detected face, anything below this is so small that all matches fail, so we use this limit to hide them" HELP['scheduled_import_scan']="The # of days between forced scans of the import path for new images" HELP['scheduled_storage_scan']="The # of days between forced scans of the storage path for any file system changes outside of Photo Assistant" HELP['scheduled_bin_cleanup']="The # of days between running a job to delete old files from the Recycle Bin" @@ -106,6 +109,7 @@ def settings(): 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'] + s.face_size_limit = request.form['face_size_limit'] s.scheduled_import_scan = request.form['scheduled_import_scan'] s.scheduled_storage_scan = request.form['scheduled_storage_scan'] s.scheduled_bin_cleanup = request.form['scheduled_bin_cleanup'] diff --git a/tables.sql b/tables.sql index d98673c..a1098d7 100644 --- a/tables.sql +++ b/tables.sql @@ -8,6 +8,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, DEFAULT_REFIMG_MODEL integer, DEFAULT_SCAN_MODEL integer, DEFAULT_THRESHOLD float, + FACE_SIZE_LIMIT integer, SCHEDULED_IMPORT_SCAN integer, SCHEDULED_STORAGE_SCAN integer, SCHEDULED_BIN_CLEANUP integer, BIN_CLEANUP_FILE_AGE integer, JOB_ARCHIVE_AGE integer, @@ -146,8 +147,8 @@ 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, 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', 1, 1, 7, 30, 3 ); +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 ); -- DEV(cam): ---insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, default_refimg_model, default_scan_model, default_threshold, 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', 1, 1, 7, 30, 3 ); +--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: ---insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, default_refimg_model, default_scan_model, default_threshold, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) values ( (select nextval('SETTINGS_ID_SEQ')), '/export/docker/storage/', 'Camera_uploads/', 'photos/', '.pa_bin/', 1, 1, '0.55', 1, 1, 7, 30, 4 ); +--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')), '/export/docker/storage/', 'Camera_uploads/', 'photos/', '.pa_bin/', 1, 1, '0.55', 43, 1, 1, 7, 30, 4 );