now have functional add/remove manual override to existing person
This commit is contained in:
52
face.py
52
face.py
@@ -1,6 +1,7 @@
|
|||||||
from main import db, app, ma
|
from main import db, app, ma
|
||||||
from sqlalchemy import Sequence
|
from sqlalchemy import Sequence
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
from shared import PA
|
||||||
|
|
||||||
|
|
||||||
# DEL ME SOON
|
# DEL ME SOON
|
||||||
@@ -64,24 +65,33 @@ class FaceRefimgLink(db.Model):
|
|||||||
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}, face_distance: {self.face_distance}"
|
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}, face_distance: {self.face_distance}"
|
||||||
|
|
||||||
|
|
||||||
### DDP: todo next, make these into sqlachemy classes, THEN person.py add the override in ORM, THEN draw override in blue/not green in DrawImg
|
class FaceOverrideType(db.Model):
|
||||||
### THEN find_person needs to call appropriate override func (OR pass in type?) and get it to be smarter - that sounds okay actually
|
__tablename__ = "face_override_type"
|
||||||
# create table FACE_OVERRIDE_TYPE ( ID integer, NAME varchar unique, constraint PK_FACE_OVERRIDE_TYPE_ID primary key(ID) );
|
id = db.Column(db.Integer, db.Sequence('face_override_type_id_seq'), primary_key=True )
|
||||||
#create sequence FACE_OVERRIDE_TYPE_ID_SEQ;
|
name = db.Column( db.String )
|
||||||
#create sequence FACE_OVERRIDE_ID_SEQ;
|
|
||||||
#create table FACE_OVERRIDE_TYPE ( ID integer, NAME varchar unique, constraint PK_FACE_OVERRIDE_TYPE_ID primary key(ID) );
|
def __repr__(self):
|
||||||
#insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Not a face' );
|
return f"<id: {self.id}, name={self.name}>"
|
||||||
#insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Too young' );
|
|
||||||
#insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Ignore face' );
|
class FaceNoMatchOverride(db.Model):
|
||||||
#insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Manual match' );
|
__tablename__ = "face_no_match_override"
|
||||||
#
|
id = db.Column(db.Integer, db.Sequence('face_override_id_seq'), primary_key=True )
|
||||||
#-- keep non-redundant FACE because, when we rebuild data we may have a null FACE_ID, but still want to connect to this override
|
face_id = db.Column(db.Integer, db.ForeignKey("face.id"), primary_key=True )
|
||||||
#-- from a previous AI pass... (would happen if we delete a file and then reimport/scan it), OR, more likely we change (say) a threshold, etc.
|
type_id = db.Column(db.Integer, db.ForeignKey("face_override_type.id"))
|
||||||
#-- any reordering of faces, generates new face_ids... (but if the face data was the same, then this override should stand)
|
type = db.relationship("FaceOverrideType")
|
||||||
#create table FACE_NO_MATCH_OVERRIDE ( ID integer, FACE_ID integer, TYPE integer, FACE bytea,
|
face = db.Column( db.LargeBinary )
|
||||||
# constraint FK_FNMO_FACE_ID foreign key (FACE_ID) references FACE(ID),
|
|
||||||
# constraint FK_FNMO_TYPE foreign key (TYPE) references FACE_OVERRIDE_TYPE(ID),
|
def __repr__(self):
|
||||||
# constraint PK_FNMO_ID primary key(ID) );
|
return f"<id: {self.id}, face_id={self.face_id}, type: {self.type}>"
|
||||||
#
|
|
||||||
#-- manual match goes to person not refimg, so on search, etc. we deal with this anomaly (via sql not ORM)
|
|
||||||
#create table FACE_MANUAL_OVERRIDE ( ID integer, FACE_ID integer, PERSON_ID integer, TYPE integer, constraint PK_FACE_MANUAL_OVERRIDE_ID primary key(ID) );
|
class FaceManualOverride(db.Model):
|
||||||
|
__tablename__ = "face_manual_override"
|
||||||
|
id = db.Column(db.Integer, db.Sequence('face_override_id_seq'), primary_key=True )
|
||||||
|
face_id = db.Column(db.Integer, db.ForeignKey("face.id"), primary_key=True )
|
||||||
|
face = db.Column( db.LargeBinary )
|
||||||
|
person_id = db.Column(db.Integer, db.ForeignKey("person.id"), primary_key=True )
|
||||||
|
person = db.relationship("Person")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<id: {self.id}, face_id={self.face_id}, person_id={self.person_id}>"
|
||||||
|
|||||||
10
files.py
10
files.py
@@ -32,7 +32,7 @@ from person import Refimg, Person, PersonRefimgLink
|
|||||||
from settings import Settings, SettingsIPath, SettingsSPath, SettingsRBPath
|
from settings import Settings, SettingsIPath, SettingsSPath, SettingsRBPath
|
||||||
from shared import SymlinkName
|
from shared import SymlinkName
|
||||||
from dups import Duplicates
|
from dups import Duplicates
|
||||||
from face import Face, FaceFileLink, FaceRefimgLink
|
from face import Face, FaceFileLink, FaceRefimgLink, FaceNoMatchOverride, FaceManualOverride
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
|
|
||||||
@@ -731,7 +731,13 @@ def view(id):
|
|||||||
# DB contains disconnected faces with no locn data - BUG: 87
|
# DB contains disconnected faces with no locn data - BUG: 87
|
||||||
face.tmp_locn = [ 0,0,0,0 ]
|
face.tmp_locn = [ 0,0,0,0 ]
|
||||||
st.SetMessage( f"For some reason this face does not have a locn: face_id={face.id} tell ddp", "warning" )
|
st.SetMessage( f"For some reason this face does not have a locn: face_id={face.id} tell ddp", "warning" )
|
||||||
|
# now get any relevant override and store it in objs...
|
||||||
|
fnmo = FaceNoMatchOverride.query.filter(FaceNoMatchOverride.face_id==face.id).first()
|
||||||
|
if fnmo:
|
||||||
|
face.no_match_override=fnmo
|
||||||
|
mo = FaceManualOverride.query.filter(FaceManualOverride.face_id==face.id).first()
|
||||||
|
if mo:
|
||||||
|
face.manual_override=mo
|
||||||
|
|
||||||
|
|
||||||
eids=eids.rstrip(",")
|
eids=eids.rstrip(",")
|
||||||
|
|||||||
@@ -26,6 +26,32 @@ function NewHeight()
|
|||||||
return im.height*gap / (im.width/window.innerWidth)
|
return im.height*gap / (im.width/window.innerWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DrawLabelOnFace(str)
|
||||||
|
{
|
||||||
|
// finish face box, need to clear out new settings for // transparent backed-name tag
|
||||||
|
context.stroke();
|
||||||
|
context.beginPath()
|
||||||
|
context.lineWidth = 0.1
|
||||||
|
context.font = "30px Arial"
|
||||||
|
context.globalAlpha = 0.6
|
||||||
|
|
||||||
|
bbox = context.measureText(str);
|
||||||
|
f_h=bbox.fontBoundingBoxAscent
|
||||||
|
if( bbox.fontBoundingBoxDescent )
|
||||||
|
f_h += bbox.fontBoundingBoxDescent
|
||||||
|
f_h -= 8
|
||||||
|
context.rect( x+w/2-bbox.width/2, y-f_h, bbox.width, f_h )
|
||||||
|
context.fillStyle="white"
|
||||||
|
context.fill()
|
||||||
|
context.stroke();
|
||||||
|
context.beginPath()
|
||||||
|
context.globalAlpha = 1.0
|
||||||
|
context.font = "30px Arial"
|
||||||
|
context.textAlign = "center"
|
||||||
|
context.fillStyle = context.strokeStyle
|
||||||
|
context.fillText(str, x+w/2, y-2)
|
||||||
|
}
|
||||||
|
|
||||||
// This draws the image, it can be called on resize events, img.src finishing
|
// This draws the image, it can be called on resize events, img.src finishing
|
||||||
// loading or explicitly on page load. Will also deal with all state/toggles
|
// loading or explicitly on page load. Will also deal with all state/toggles
|
||||||
// for items like name, grayscale, etc.
|
// for items like name, grayscale, etc.
|
||||||
@@ -87,46 +113,23 @@ function DrawImg()
|
|||||||
context.beginPath()
|
context.beginPath()
|
||||||
context.rect( x, y, w, h )
|
context.rect( x, y, w, h )
|
||||||
context.lineWidth = 2
|
context.lineWidth = 2
|
||||||
|
|
||||||
|
// this face has an override so diff colour
|
||||||
|
if( objs[current].faces[i].override )
|
||||||
|
context.strokeStyle = 'blue'
|
||||||
|
else
|
||||||
context.strokeStyle = 'green'
|
context.strokeStyle = 'green'
|
||||||
|
|
||||||
|
if( objs[current].faces[i].no_match_override)
|
||||||
|
DrawLabelOnFace( objs[current].faces[i].no_match_override.type )
|
||||||
|
|
||||||
if( objs[current].faces[i].who )
|
if( objs[current].faces[i].who )
|
||||||
{
|
{
|
||||||
// finish face box, need to clear out new settings for
|
|
||||||
// transparent backed-name tag
|
|
||||||
context.stroke();
|
|
||||||
context.beginPath()
|
|
||||||
context.lineWidth = 0.1
|
|
||||||
context.font = "30px Arial"
|
|
||||||
context.globalAlpha = 0.6
|
|
||||||
str=objs[current].faces[i].who
|
str=objs[current].faces[i].who
|
||||||
if( $('#distance').prop('checked') )
|
if( $('#distance').prop('checked') )
|
||||||
str += "("+objs[current].faces[i].distance+")"
|
str += "("+objs[current].faces[i].distance+")"
|
||||||
|
DrawLabelOnFace( str )
|
||||||
bbox = context.measureText(str);
|
|
||||||
f_h=bbox.fontBoundingBoxAscent
|
|
||||||
if( bbox.fontBoundingBoxDescent )
|
|
||||||
f_h += bbox.fontBoundingBoxDescent
|
|
||||||
f_h -= 8
|
|
||||||
context.rect( x+w/2-bbox.width/2, y-f_h, bbox.width, f_h )
|
|
||||||
context.fillStyle="white"
|
|
||||||
context.fill()
|
|
||||||
context.stroke();
|
|
||||||
context.beginPath()
|
|
||||||
context.globalAlpha = 1.0
|
|
||||||
context.font = "30px Arial"
|
|
||||||
context.textAlign = "center"
|
|
||||||
context.fillStyle = "green"
|
|
||||||
context.fillText(str, x+w/2, y-2)
|
|
||||||
}
|
}
|
||||||
/* can use to show lower left coords of a face for debugging
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.font = "14px Arial"
|
|
||||||
context.textAlign = "center"
|
|
||||||
context.fillStyle = "black"
|
|
||||||
context.fillText( 'x=' + objs[current].faces[i].x + ', y=' + objs[current].faces[i].y, x+w/2, y-2)
|
|
||||||
context.fillText( 'x=' + objs[current].faces[i].x + ', y=' + objs[current].faces[i].y, x+w/2, y-2)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
context.stroke();
|
context.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,7 +228,11 @@ $(document).ready( function()
|
|||||||
|
|
||||||
if( x >= fx && x <= fx+fw && y >= fy && y <= fy+fh )
|
if( x >= fx && x <= fx+fw && y >= fy && y <= fy+fh )
|
||||||
{
|
{
|
||||||
if( objs[current].faces[i].who )
|
if( objs[current].faces[i].override )
|
||||||
|
{
|
||||||
|
item_list['remove_override']={ 'name': 'Remove override for this face', 'which_face': i, 'id': objs[current].faces[i].id }
|
||||||
|
}
|
||||||
|
else if( objs[current].faces[i].who )
|
||||||
{
|
{
|
||||||
item_list['match']={ 'name': objs[current].faces[i].who, 'which_face': i, 'id': objs[current].faces[i].id }
|
item_list['match']={ 'name': objs[current].faces[i].who, 'which_face': i, 'id': objs[current].faces[i].id }
|
||||||
item_list['wrong_person']={ 'name': 'wrong person', 'which_face': i, 'id': objs[current].faces[i].id }
|
item_list['wrong_person']={ 'name': 'wrong person', 'which_face': i, 'id': objs[current].faces[i].id }
|
||||||
@@ -238,7 +245,6 @@ $(document).ready( function()
|
|||||||
item_list['no_match_no_face']={ 'name': 'Mark as not a face', 'which_face': i, 'id': objs[current].faces[i].id }
|
item_list['no_match_no_face']={ 'name': 'Mark as not a face', 'which_face': i, 'id': objs[current].faces[i].id }
|
||||||
item_list['no_match_too_young']={ 'name': 'Mark as face too young', 'which_face': i, 'id': objs[current].faces[i].id }
|
item_list['no_match_too_young']={ 'name': 'Mark as face too young', 'which_face': i, 'id': objs[current].faces[i].id }
|
||||||
item_list['no_match_ignore']={ 'name': 'Ignore this face', 'which_face': i, 'id': objs[current].faces[i].id }
|
item_list['no_match_ignore']={ 'name': 'Ignore this face', 'which_face': i, 'id': objs[current].faces[i].id }
|
||||||
item_list['remove_override']={ 'name': 'Remove override for this face', 'which_face': i, 'id': objs[current].faces[i].id }
|
|
||||||
}
|
}
|
||||||
delete item_list['not_a_face']
|
delete item_list['not_a_face']
|
||||||
$('#canvas').prop('menu_item', item_list )
|
$('#canvas').prop('menu_item', item_list )
|
||||||
@@ -258,14 +264,19 @@ $(document).ready( function()
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// quick wrapper function to make calling this ajax code simpler in SearchForPerson
|
// quick wrapper function to make calling this ajax code simpler in SearchForPerson
|
||||||
function OverrideForceMatch( person, face )
|
function OverrideForceMatch( person_id, face_id, face_pos )
|
||||||
{
|
{
|
||||||
ofm='&person_id='+person+'&face_id='+face
|
ofm='&person_id='+person_id+'&face_id='+face_id
|
||||||
// on success, close the dbox, force face drawing on and redraw the face with the new override
|
// on success, close the dbox, force face drawing on and redraw the face with the new override
|
||||||
$.ajax({ type: 'POST', data: ofm, url: '/override_force_match', success: function(data) {
|
$.ajax({ type: 'POST', data: ofm, url: '/override_force_match', success: function(data) {
|
||||||
$('#dbox').modal('hide');
|
if( objs[current].faces[face_pos].who )
|
||||||
$('#faces').prop('checked',true);
|
objs[current].faces[face_pos].old_who=objs[current].faces[face_pos].who
|
||||||
DrawImg();
|
objs[current].faces[face_pos].who=data.person_tag
|
||||||
|
objs[current].faces[face_pos].override=1
|
||||||
|
|
||||||
|
$('#dbox').modal('hide')
|
||||||
|
$('#faces').prop('checked',true)
|
||||||
|
DrawImg()
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
@@ -273,7 +284,7 @@ function OverrideForceMatch( person, face )
|
|||||||
// function to facilitate adding a face match override to this "found" person
|
// function to facilitate adding a face match override to this "found" person
|
||||||
// uses Ajax to the f/e to get any person matching #stext's content (via any name/tag)
|
// uses Ajax to the f/e to get any person matching #stext's content (via any name/tag)
|
||||||
// and displays results in #search_person_results
|
// and displays results in #search_person_results
|
||||||
function SearchForPerson(face_id)
|
function SearchForPerson(face_id, face_pos)
|
||||||
{
|
{
|
||||||
// make URI safe
|
// make URI safe
|
||||||
who = encodeURIComponent( $('#stext').val() )
|
who = encodeURIComponent( $('#stext').val() )
|
||||||
@@ -283,7 +294,8 @@ function SearchForPerson(face_id)
|
|||||||
content='Click one of the link(s) below to manually connect this face as once-off connection to the person:<br><br>'
|
content='Click one of the link(s) below to manually connect this face as once-off connection to the person:<br><br>'
|
||||||
for( var key in data ) {
|
for( var key in data ) {
|
||||||
var person = data[key];
|
var person = data[key];
|
||||||
content+= '<a onClick="OverrideForceMatch('+person.id+','+face_id+')">'+person.firstname+' '+person.surname+'('+person.tag+')</a><br>'
|
content+= '<a class="link-primary" onClick="OverrideForceMatch('+person.id+','
|
||||||
|
+face_id+','+face_pos+')">'+person.firstname+' '+person.surname+' ('+person.tag+')</a><br>'
|
||||||
}
|
}
|
||||||
$('#search_person_results').html( content )
|
$('#search_person_results').html( content )
|
||||||
}
|
}
|
||||||
@@ -291,14 +303,34 @@ function SearchForPerson(face_id)
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RemoveOverride()
|
||||||
|
{
|
||||||
|
d='&face_id='+objs[current].faces[face_pos].id+'&person_tag='+objs[current].faces[face_pos].who+
|
||||||
|
'&file_eid='+current
|
||||||
|
$.ajax({ type: 'POST', data: d, url: '/remove_override',
|
||||||
|
success: function(data) {
|
||||||
|
if( objs[current].faces[face_pos].old_who )
|
||||||
|
objs[current].faces[face_pos].who=objs[current].faces[face_pos].old_who
|
||||||
|
else
|
||||||
|
delete objs[current].faces[face_pos].who
|
||||||
|
delete objs[current].faces[face_pos].override
|
||||||
|
$('#dbox').modal('hide')
|
||||||
|
DrawImg()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// function that is called when we click on a face in the viewer and we want to
|
// function that is called when we click on a face in the viewer and we want to
|
||||||
// potentially override the non-match / match... it shows the face, and then
|
// potentially override the non-match / match... it shows the face, and then
|
||||||
// based on which menu item got us here, shows appropriate text to do next action
|
// based on which menu item got us here, shows appropriate text to do next action
|
||||||
function FaceDBox(key, item)
|
function FaceDBox(key, item)
|
||||||
{
|
{
|
||||||
|
face_pos=item[key]['which_face']
|
||||||
div ='<p>'
|
div ='<p>'
|
||||||
div+='Face position #' + item[key]['which_face']
|
div+='Face position #' + face_pos
|
||||||
div+='<div id="face_img"></div>'
|
div+='<div id="face_img"></div>'
|
||||||
$.ajax({ type: 'POST', data: null, url: '/get_face_from_image/'+item[key]['id'],
|
$.ajax({ type: 'POST', data: null, url: '/get_face_from_image/'+item[key]['id'],
|
||||||
success: function(img_data) {
|
success: function(img_data) {
|
||||||
@@ -306,6 +338,15 @@ function FaceDBox(key, item)
|
|||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
div+='</p>'
|
div+='</p>'
|
||||||
|
if ( key == 'remove_override' )
|
||||||
|
{
|
||||||
|
div+='<div class="row col-12">remove this override (force match to: ' + objs[current].faces[face_pos].who + ')'
|
||||||
|
div+='<div class="row">'
|
||||||
|
div+='<button class="btn btn-outline-info col-6" type="button" onClick="$(\'#dbox\').modal(\'hide\'); return false">Cancel</button>'
|
||||||
|
div+='<button class="btn btn-outline-danger col-6" type="button" '+
|
||||||
|
'onClick="RemoveOverride(' +face_pos+ ')">Remove</button>'
|
||||||
|
div+='</div>'
|
||||||
|
}
|
||||||
if ( key == 'no_match_new_person' )
|
if ( key == 'no_match_new_person' )
|
||||||
{
|
{
|
||||||
div+='<br>create new person'
|
div+='<br>create new person'
|
||||||
@@ -317,7 +358,7 @@ function FaceDBox(key, item)
|
|||||||
`
|
`
|
||||||
<div class="input-group mb-3"><input type="text" class="form-control" id="stext" placeholder="tag/name">
|
<div class="input-group mb-3"><input type="text" class="form-control" id="stext" placeholder="tag/name">
|
||||||
<button class="btn btn-outline-success" type="button" onClick="SearchForPerson(`
|
<button class="btn btn-outline-success" type="button" onClick="SearchForPerson(`
|
||||||
div+= item[key]['id']
|
div+= item[key]['id'] + ',' + face_pos
|
||||||
div+=`)">Search</button>
|
div+=`)">Search</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="search_person_results">
|
<div id="search_person_results">
|
||||||
|
|||||||
37
person.py
37
person.py
@@ -9,7 +9,7 @@ from status import st, Status
|
|||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from shared import GenFace, GenThumb
|
from shared import GenFace, GenThumb
|
||||||
from face import Face, FaceRefimgLink
|
from face import Face, FaceRefimgLink, FaceOverrideType, FaceNoMatchOverride, FaceManualOverride
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
@@ -243,7 +243,6 @@ def find_persons(who):
|
|||||||
resp[p.id]['firstname']=p.firstname
|
resp[p.id]['firstname']=p.firstname
|
||||||
resp[p.id]['surname']=p.surname
|
resp[p.id]['surname']=p.surname
|
||||||
|
|
||||||
print( resp )
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -253,15 +252,39 @@ def find_persons(who):
|
|||||||
@login_required
|
@login_required
|
||||||
def override_force_match():
|
def override_force_match():
|
||||||
person_id = request.form['person_id']
|
person_id = request.form['person_id']
|
||||||
face_id = request.form['face_id']
|
|
||||||
p = Person.query.get(person_id);
|
p = Person.query.get(person_id);
|
||||||
if not p:
|
if not p:
|
||||||
raise Exception("could not find person to add override too!")
|
raise Exception("could not find person to add override too!")
|
||||||
|
|
||||||
|
face_id = request.form['face_id']
|
||||||
f = Face.query.get(face_id);
|
f = Face.query.get(face_id);
|
||||||
if not f:
|
if not f:
|
||||||
raise Exception("could not find face to add override for!")
|
raise Exception("could not find face to add override for!")
|
||||||
|
|
||||||
print( f"Being asked to force an override match for face_id {face_id}, for person: {p.tag} -- doing nothing for now" )
|
mo = FaceManualOverride( face_id=f.id, face=f.face, person_id=p.id )
|
||||||
st.SetMessage( f"Being asked to force an override match for face_id {face_id}, for person: {p.tag} -- doing nothing for now" )
|
db.session.add( mo )
|
||||||
# might need to do something smarter here (reload to old view is good though & happens now, not sure why (last url)?)
|
db.session.commit()
|
||||||
return "ok"
|
|
||||||
|
print( f"Placing an override match with face_id {face_id}, for person: {p.tag}" )
|
||||||
|
# this will reply to the Ajax / POST, and cause the page to re-draw with new face override to person_tag
|
||||||
|
resp={}
|
||||||
|
resp['person_tag']=p.tag
|
||||||
|
return resp
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /remove_override -> POST
|
||||||
|
################################################################################
|
||||||
|
@app.route("/remove_override", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def remove_override():
|
||||||
|
face_id = request.form['face_id']
|
||||||
|
person_tag = request.form['person_tag']
|
||||||
|
file_eid = request.form['file_eid']
|
||||||
|
print( f"Remove override with face_id={face_id} to person_tag={person_tag}" )
|
||||||
|
|
||||||
|
FaceManualOverride.query.filter( FaceManualOverride.face_id==face_id ).delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# this will reply to the Ajax / POST, and cause the page to re-draw with new face override
|
||||||
|
resp={}
|
||||||
|
return resp
|
||||||
|
|||||||
@@ -103,13 +103,13 @@ insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_S
|
|||||||
-- keep non-redundant FACE because, when we rebuild data we may have a null FACE_ID, but still want to connect to this override
|
-- keep non-redundant FACE because, when we rebuild data we may have a null FACE_ID, but still want to connect to this override
|
||||||
-- from a previous AI pass... (would happen if we delete a file and then reimport/scan it), OR, more likely we change (say) a threshold, etc.
|
-- from a previous AI pass... (would happen if we delete a file and then reimport/scan it), OR, more likely we change (say) a threshold, etc.
|
||||||
-- any reordering of faces, generates new face_ids... (but if the face data was the same, then this override should stand)
|
-- any reordering of faces, generates new face_ids... (but if the face data was the same, then this override should stand)
|
||||||
create table FACE_NO_MATCH_OVERRIDE ( ID integer, FACE_ID integer, TYPE integer, FACE bytea,
|
create table FACE_NO_MATCH_OVERRIDE ( ID integer, FACE_ID integer, TYPE_ID integer, FACE bytea,
|
||||||
constraint FK_FNMO_FACE_ID foreign key (FACE_ID) references FACE(ID),
|
constraint FK_FNMO_FACE_ID foreign key (FACE_ID) references FACE(ID),
|
||||||
constraint FK_FNMO_TYPE foreign key (TYPE) references FACE_OVERRIDE_TYPE(ID),
|
constraint FK_FNMO_TYPE foreign key (TYPE_ID) references FACE_OVERRIDE_TYPE(ID),
|
||||||
constraint PK_FNMO_ID primary key(ID) );
|
constraint PK_FNMO_ID primary key(ID) );
|
||||||
|
|
||||||
-- manual match goes to person not refimg, so on search, etc. we deal with this anomaly (via sql not ORM)
|
-- manual match goes to person not refimg, so on search, etc. we deal with this anomaly (via sql not ORM)
|
||||||
create table FACE_MANUAL_OVERRIDE ( ID integer, FACE_ID integer, PERSON_ID integer, TYPE integer, constraint PK_FACE_MANUAL_OVERRIDE_ID primary key(ID) );
|
create table FACE_MANUAL_OVERRIDE ( ID integer, FACE_ID integer, PERSON_ID integer, FACE bytea, constraint PK_FACE_MANUAL_OVERRIDE_ID primary key(ID) );
|
||||||
|
|
||||||
create table PERSON_REFIMG_LINK ( PERSON_ID integer, REFIMG_ID integer,
|
create table PERSON_REFIMG_LINK ( PERSON_ID integer, REFIMG_ID integer,
|
||||||
constraint PK_PRL primary key(PERSON_ID, REFIMG_ID),
|
constraint PK_PRL primary key(PERSON_ID, REFIMG_ID),
|
||||||
|
|||||||
@@ -48,6 +48,17 @@
|
|||||||
data['who']='{{face.refimg.person.tag}}'
|
data['who']='{{face.refimg.person.tag}}'
|
||||||
data['distance']="{{face.refimg_lnk.face_distance|round(2)}}"
|
data['distance']="{{face.refimg_lnk.face_distance|round(2)}}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if face.no_match_override %}
|
||||||
|
data['override']=1
|
||||||
|
data['no_match_override'] = {
|
||||||
|
'face_id' : '{{face.no_match_override.face_id}}',
|
||||||
|
'type' : '{{face.no_match_override.type.name}}',
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if face.manual_override %}
|
||||||
|
data['override']=1
|
||||||
|
data['who']='{{face.manual_override.person.tag}}'
|
||||||
|
{% endif %}
|
||||||
e.faces.push( data )
|
e.faces.push( data )
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
objs[{{id}}]=e
|
objs[{{id}}]=e
|
||||||
|
|||||||
Reference in New Issue
Block a user