renamed AI_Model to AIModel for consistency, added it as a functioning drop-down select on settings page, added face_distance to db and code, put face_distance model_used into all classes ready for use
This commit is contained in:
33
TODO
33
TODO
@@ -1,26 +1,37 @@
|
|||||||
## GENERAL
|
## GENERAL
|
||||||
|
|
||||||
* face locations:
|
* 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
|
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
|
- algo:
|
||||||
-- show face locns in viewer.html [DONE]
|
for each face (even known) in image
|
||||||
* how hard would bootstrap 5 be, and use form-switch instead of 'badge'
|
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:
|
* 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
|
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
|
* 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
|
* 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
|
- 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
|
* comment your code
|
||||||
* more OO goodness :)
|
* more OO goodness :)
|
||||||
|
|
||||||
|
|||||||
4
face.py
4
face.py
@@ -2,7 +2,6 @@ from main import db, app, ma
|
|||||||
from sqlalchemy import Sequence
|
from sqlalchemy import Sequence
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
|
||||||
|
|
||||||
class Face(db.Model):
|
class Face(db.Model):
|
||||||
__tablename__ = "face"
|
__tablename__ = "face"
|
||||||
id = db.Column(db.Integer, db.Sequence('face_id_seq'), primary_key=True )
|
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"
|
__tablename__ = "face_file_link"
|
||||||
face_id = db.Column(db.Integer, db.ForeignKey("face.id"), primary_key=True )
|
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 )
|
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):
|
def __repr__(self):
|
||||||
return f"<face_id: {self.face_id}, file_eid={self.file_eid}"
|
return f"<face_id: {self.face_id}, file_eid={self.file_eid}"
|
||||||
@@ -25,6 +25,8 @@ class FaceRefimgLink(db.Model):
|
|||||||
__tablename__ = "face_refimg_link"
|
__tablename__ = "face_refimg_link"
|
||||||
face_id = db.Column(db.Integer, db.ForeignKey("face.id"), primary_key=True )
|
face_id = db.Column(db.Integer, db.ForeignKey("face.id"), primary_key=True )
|
||||||
refimg_id = db.Column(db.Integer, db.ForeignKey("refimg.id"), primary_key=True )
|
refimg_id = db.Column(db.Integer, db.ForeignKey("refimg.id"), primary_key=True )
|
||||||
|
model_used = db.Column(db.Integer, db.ForeignKey("ai_model.id"), primary_key=True )
|
||||||
|
face_distance = db.Column(db.Integer)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}"
|
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}"
|
||||||
|
|||||||
@@ -227,6 +227,14 @@ class Refimg(Base):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<id: {self.id}, fname: {self.fname}, created_on: {self.created_on}>"
|
return f"<id: {self.id}, fname: {self.fname}, created_on: {self.created_on}>"
|
||||||
|
|
||||||
|
class AIModel(Base):
|
||||||
|
__tablename__ = "ai_model"
|
||||||
|
id = Column(Integer, primary_key=True )
|
||||||
|
name = Column(String)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<id: {self.id}, name: {self.name}>"
|
||||||
|
|
||||||
class Face(Base):
|
class Face(Base):
|
||||||
__tablename__ = "face"
|
__tablename__ = "face"
|
||||||
id = Column(Integer, Sequence('face_id_seq'), primary_key=True )
|
id = Column(Integer, Sequence('face_id_seq'), primary_key=True )
|
||||||
@@ -240,6 +248,7 @@ class FaceFileLink(Base):
|
|||||||
__tablename__ = "face_file_link"
|
__tablename__ = "face_file_link"
|
||||||
face_id = Column(Integer, ForeignKey("face.id"), primary_key=True )
|
face_id = Column(Integer, ForeignKey("face.id"), primary_key=True )
|
||||||
file_eid = Column(Integer, ForeignKey("file.eid"), primary_key=True )
|
file_eid = Column(Integer, ForeignKey("file.eid"), primary_key=True )
|
||||||
|
model_used = Column(Integer, ForeignKey("ai_model.id") )
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<face_id: {self.face_id}, file_eid={self.file_eid}"
|
return f"<face_id: {self.face_id}, file_eid={self.file_eid}"
|
||||||
@@ -248,6 +257,8 @@ class FaceRefimgLink(Base):
|
|||||||
__tablename__ = "face_refimg_link"
|
__tablename__ = "face_refimg_link"
|
||||||
face_id = Column(Integer, ForeignKey("face.id"), primary_key=True )
|
face_id = Column(Integer, ForeignKey("face.id"), primary_key=True )
|
||||||
refimg_id = Column(Integer, ForeignKey("refimg.id"), primary_key=True )
|
refimg_id = Column(Integer, ForeignKey("refimg.id"), primary_key=True )
|
||||||
|
model_used = Column(Integer, ForeignKey("ai_model.id") )
|
||||||
|
face_distance = Column(Integer)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}"
|
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from flask_login import login_required, current_user
|
|||||||
################################################################################
|
################################################################################
|
||||||
# Class describing AI_MODEL in the database, and via sqlalchemy, connected to the DB as well
|
# Class describing AI_MODEL in the database, and via sqlalchemy, connected to the DB as well
|
||||||
################################################################################
|
################################################################################
|
||||||
class AI_Model(db.Model):
|
class AIModel(db.Model):
|
||||||
__tablename__ = "ai_model"
|
__tablename__ = "ai_model"
|
||||||
id = db.Column(db.Integer, primary_key=True )
|
id = db.Column(db.Integer, primary_key=True )
|
||||||
name = db.Column(db.String)
|
name = db.Column(db.String)
|
||||||
@@ -20,6 +20,7 @@ class AI_Model(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<id: {self.id}, name: {self.name}>"
|
return f"<id: {self.id}, name: {self.name}>"
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Class describing Settings in the database, and via sqlalchemy, connected to the DB as well
|
# 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()])
|
import_path = StringField('Path(s) to import from:', [validators.DataRequired()])
|
||||||
storage_path = StringField('Path to store sorted images to:', [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()])
|
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()])
|
default_threshold = StringField('Face Distance threshold (below is a match):', [validators.DataRequired()])
|
||||||
submit = SubmitField('Save' )
|
submit = SubmitField('Save' )
|
||||||
|
|
||||||
|
|||||||
@@ -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_FILE_EID foreign key (FILE_EID) references FILE(EID),
|
||||||
constraint FK_FFL_MODEL_USED foreign key (MODEL_USED) references AI_MODEL(ID) );
|
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 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_FACE_ID foreign key (FACE_ID) references FACE(ID) on delete cascade,
|
||||||
constraint FK_FRL_REFIMG_ID foreign key (REFIMG_ID) references REFIMG(ID),
|
constraint FK_FRL_REFIMG_ID foreign key (REFIMG_ID) references REFIMG(ID),
|
||||||
|
|||||||
Reference in New Issue
Block a user