from wtforms import SubmitField, StringField, HiddenField, validators, Form from flask_wtf import FlaskForm from flask import request, render_template, redirect, make_response from main import db, app, ma from sqlalchemy import Sequence from sqlalchemy.exc import SQLAlchemyError from path import Path, PathType from files import Entry, Dir, File, PathDirLink from person import Refimg, Person, PersonRefimgLink from flask_login import login_required, current_user from PIL import Image import io import base64 from job import Job, JobExtra, Joblog, NewJob from face import Face, FaceFileLink, FaceRefimgLink, FaceNoMatchOverride, FaceForceMatchOverride # pylint: disable=no-member ################################################################################ # /ai_stats -> placholder for some sort of stats ################################################################################ @app.route("/ai_stats", methods=["GET"]) @login_required def ai_stats(): stats = db.session.execute( "select p.tag, count(f.id) from person p, face f, face_file_link ffl, face_refimg_link frl, person_refimg_link prl where p.id = prl.person_id and prl.refimg_id = frl.refimg_id and frl.face_id = ffl.face_id and ffl.face_id = f.id group by p.tag order by 2 desc" ) cnt_res = db.session.execute( "select count(1) from ( select p.tag from person p, face f, face_file_link ffl, face_refimg_link frl, person_refimg_link prl where p.id = prl.person_id and prl.refimg_id = frl.refimg_id and frl.face_id = ffl.face_id and ffl.face_id = f.id group by p.tag ) as foo" ) num_stats=cnt_res.first()[0] fstats={} fstats['files_with_a_face'] = db.session.execute( "select count(distinct file_eid) as count from face_file_link" ).first()[0] fstats['files_with_a_match'] = db.session.execute( "select count(distinct ffl.file_eid) as count from face_file_link ffl, face_refimg_link frl where frl.face_id = ffl.face_id" ).first()[0] fstats['files_with_missing_matches'] = db.session.execute( "select count(distinct ffl.file_eid) from face f left join face_refimg_link frl on f.id = frl.face_id join face_file_link ffl on f.id = ffl.face_id where frl.refimg_id is null" ).first()[0] # files_with_no_matches? fstats['all_faces'] = db.session.execute( "select count(distinct face_id) as count from face_file_link" ).first()[0] fstats['all_matched_faces'] = db.session.execute( "select count(distinct face_id) as count from face_refimg_link" ).first()[0] fstats['all_unmatched_faces'] = db.session.execute( "select count(f.id) from face f left join face_refimg_link frl on f.id = frl.face_id where frl.refimg_id is null" ).first()[0] return render_template("ai_stats.html", page_title='AI Statistics', stats=stats, num_stats=num_stats, fstats=fstats ) ################################################################################ # /run_ai_on -> creates a job, with extras containing entry ids (eid-0, eid-1, # etc.) and person=all|dad, etc. Room to consider threshold, algo, etc. ################################################################################ @app.route("/run_ai_on", methods=["POST"]) @login_required def run_ai_on(): jex=[] for el in request.form: jex.append( JobExtra( name=f"{el}", value=request.form[el] ) ) job=NewJob( "run_ai_on", num_files=0, wait_for=None, jex=jex, desc="Look for face(s) in selected file(s)" ) return redirect("/jobs") @app.route("/run_ai_on_import", methods=["GET"]) @login_required def run_ai_on_import(): jex=[] ptype=PathType.query.filter(PathType.name=='Import').first() jex.append( JobExtra( name=f"person", value="all" ) ) jex.append( JobExtra( name=f"path_type", value=ptype.id ) ) job=NewJob( "run_ai_on_path", num_files=0, wait_for=None, jex=jex, desc="Look for face(s) in import path(s)") return redirect("/jobs") @app.route("/run_ai_on_storage", methods=["GET"]) @login_required def run_ai_on_storage(): jex=[] ptype=PathType.query.filter(PathType.name=='Storage').first() jex.append( JobExtra( name=f"person", value="all" ) ) jex.append( JobExtra( name=f"path_type", value=ptype.id ) ) job=NewJob( "run_ai_on_path", num_files=0, wait_for=None, jex=jex, desc="Look for face(s) in storage path(s)") return redirect("/jobs") @app.route("/unmatched_faces", methods=["GET"]) @login_required def unmatched_faces(): # get overrides and exclude them as they have been processed already fnmo_ids = [id[0] for id in FaceNoMatchOverride.query.with_entities(FaceNoMatchOverride.face_id).all()] fmo_ids = [id[0] for id in FaceForceMatchOverride.query.with_entities(FaceForceMatchOverride.face_id).all()] faces=Face.query.join(FaceFileLink).join(FaceRefimgLink, isouter=True).filter(FaceRefimgLink.refimg_id==None).filter(Face.id.not_in(fnmo_ids+fmo_ids)).order_by(Face.h.desc()).limit(10).all() imgs={} for face in faces: f = Entry.query.join(File).join(FaceFileLink).filter(FaceFileLink.face_id==face.id).first() face.file_eid=f.id face.url=f.FullPathOnFS() face.filename=f.name x=face.face_left*0.95 y=face.face_top*0.95 x2=face.face_right*1.05 y2=face.face_bottom*1.05 im = Image.open(f.FullPathOnFS()) region = im.crop((x, y, x2, y2)) img_bytearray = io.BytesIO() region.save(img_bytearray, format='JPEG') img_bytearray = img_bytearray.getvalue() face.img = base64.b64encode(img_bytearray) face.img = str(face.img)[2:-1] return render_template("faces.html", faces=faces) # this is called in Ajax, when we manually override a face that is currently unmatched load # the original full image, find the current face's coords, grab pixels 10% larger and return # it so we can show it in the dbox, and be able to pass it around for refimg creation (if needed) @app.route("/get_face_from_image/", methods=["POST"]) @login_required def get_face_from_image(face_id): face=Face.query.get(face_id) f = Entry.query.join(File).join(FaceFileLink).filter(FaceFileLink.face_id==face_id).first() x=face.face_left*0.95 y=face.face_top*0.95 x2=face.face_right*1.05 y2=face.face_bottom*1.05 im = Image.open(f.FullPathOnFS()) region = im.crop((x, y, x2, y2)) img_bytearray = io.BytesIO() region.save(img_bytearray, format='JPEG') img_bytearray = img_bytearray.getvalue() face_img = base64.b64encode(img_bytearray) face_img = str(face_img)[2:-1] return make_response( face_img )