diff --git a/TODO b/TODO index b472a7d..32a17fb 100644 --- a/TODO +++ b/TODO @@ -1,26 +1,37 @@ ## GENERAL - * face locations: - START FORM SCRATCH so all images have face_locn data - right now GenThumb is in shared, and does width, height as well --> in person.py BUT need this for pa_job_manager - -- show face locns in viewer.html [DONE] - * how hard would bootstrap 5 be, and use form-switch instead of 'badge' + * Face matching: + - upgrade to face distance per face per file + - so we dont get 2 x same face in one file, and if it could match say Cam and Mich for 1 face, take the higher match, not the first one to be over the threshold + - allow for threshold/settings to be tweaked from the GUI? + ---> at least settings for default value (back to 0.6 / 0.5?) + ---> with override table to do per file / per face? + - face locations: + START FORM SCRATCH so all images have face_locn data + - algo: + for each face (even known) in image + foreach refimg + get face_distance + sort by face_distance + for each face + connect lowest score with that face (for this file) + this means this face is no longer 'free' for a match + if (sorted) face distance > 0.55 stop as no further 'matches' + - use cnn model (check ftst.py) for ref images, and potentially as a setting to check images without a face? + - or always? + -- would CUDA be useful here? (which is faster say an old 730 or the AMD cpu?) - * allow for threshold/settings to be tweaked from the GUI - - it would be good to then say, just run the scanner against this image or maybe this DIR, to see how it IDs ppl - ---> settings for default value - ---> override table to do per file combos? - - * refimg - - remove AI menu from top-level -> make a sub-of Person, and just have Match or AI + * viewer: can we make it preload next/prev images, and only reload the image div when we jump? to make arrow-based nav much faster * remove dirs after the duplicate cleanup removes all its content - * fix up logging in general + * could look to remove the hand fixing of json.loads of array data --> seems you can make your own datatype in the ORM, and it can do the conversion every time you use it - https://stackoverflow.com/questions/28143557/sqlalchemy-convert-column-value-back-and-forth-between-internal-and-database-fo + + * fix up logging in general * comment your code * more OO goodness :) diff --git a/face.py b/face.py index 78de3f0..d7432a6 100644 --- a/face.py +++ b/face.py @@ -2,7 +2,6 @@ from main import db, app, ma from sqlalchemy import Sequence from sqlalchemy.exc import SQLAlchemyError - class Face(db.Model): __tablename__ = "face" id = db.Column(db.Integer, db.Sequence('face_id_seq'), primary_key=True ) @@ -17,6 +16,7 @@ class FaceFileLink(db.Model): __tablename__ = "face_file_link" face_id = db.Column(db.Integer, db.ForeignKey("face.id"), primary_key=True ) file_eid = db.Column(db.Integer, db.ForeignKey("file.eid"), primary_key=True ) + model_used = db.Column(db.Integer, db.ForeignKey("ai_model.id"), primary_key=True ) def __repr__(self): return f"" +class AIModel(Base): + __tablename__ = "ai_model" + id = Column(Integer, primary_key=True ) + name = Column(String) + + def __repr__(self): + return f"" + class Face(Base): __tablename__ = "face" id = Column(Integer, Sequence('face_id_seq'), primary_key=True ) @@ -240,6 +248,7 @@ class FaceFileLink(Base): __tablename__ = "face_file_link" face_id = Column(Integer, ForeignKey("face.id"), primary_key=True ) file_eid = Column(Integer, ForeignKey("file.eid"), primary_key=True ) + model_used = Column(Integer, ForeignKey("ai_model.id") ) def __repr__(self): return f"" + ################################################################################ # Class describing Settings in the database, and via sqlalchemy, connected to the DB as well ################################################################################ @@ -53,7 +54,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()]) - default_model = SelectField( 'default_model', choices=[(c.id, c.name) for c in AI_Model.query.order_by('id')] ) + default_model = SelectField( 'default_model', 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()]) submit = SubmitField('Save' ) diff --git a/tables.sql b/tables.sql index dec7a7b..e270955 100644 --- a/tables.sql +++ b/tables.sql @@ -60,7 +60,7 @@ create table FACE_FILE_LINK( FACE_ID integer, FILE_EID integer, MODEL_USED integ constraint FK_FFL_FILE_EID foreign key (FILE_EID) references FILE(EID), constraint FK_FFL_MODEL_USED foreign key (MODEL_USED) references AI_MODEL(ID) ); -create table FACE_REFIMG_LINK( FACE_ID integer, REFIMG_ID integer, MODEL_USED integer, +create table FACE_REFIMG_LINK( FACE_ID integer, REFIMG_ID integer, MODEL_USED integer, FACE_DISTANCE integer, constraint PK_FRL_FACE_ID_REFIMG_ID primary key(FACE_ID, REFIMG_ID), constraint FK_FRL_FACE_ID foreign key (FACE_ID) references FACE(ID) on delete cascade, constraint FK_FRL_REFIMG_ID foreign key (REFIMG_ID) references REFIMG(ID),