diff --git a/pa_job_manager.py b/pa_job_manager.py index 63971fa..d71e1c5 100644 --- a/pa_job_manager.py +++ b/pa_job_manager.py @@ -11,28 +11,37 @@ # ### +### SQLALCHEMY IMPORTS ### + from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import Column, Integer, String, Sequence, Float, ForeignKey, DateTime +from sqlalchemy import Column, Integer, String, Sequence, Float, ForeignKey, DateTime, LargeBinary from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import relationship from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import scoped_session + +### SQLALCHEMY IMPORTS ### + +### LOCAL FILE IMPORTS ### + from shared import DB_URL, PA_JOB_MANAGER_HOST, PA_JOB_MANAGER_PORT from datetime import datetime, timedelta import pytz import time import os import glob -from PIL import Image +from PIL import Image, ImageOps from pymediainfo import MediaInfo import hashlib -import exifread +#import exifread import base64 import numpy import cv2 import socket import threading +import io +import face_recognition DEBUG=1 @@ -113,6 +122,43 @@ class Settings(Base): def __repr__(self): return "".format(self.id, self.import_path ) + +class Person_Refimg_Link(Base): + __tablename__ = "person_refimg_link" + person_id = Column(Integer, ForeignKey('person.id'), unique=True, nullable=False, primary_key=True) + refimg_id = Column(Integer, ForeignKey('refimg.id'), unique=True, nullable=False, primary_key=True) + + def __repr__(self): + return "".format(self.person_id, self.refimg_id) + +class Person(Base): + __tablename__ = "person" + id = Column(Integer, Sequence('person_id_seq'), primary_key=True ) + tag = Column(String(48), unique=False, nullable=False) + surname = Column(String(48), unique=False, nullable=False) + firstname = Column(String(48), unique=False, nullable=False) + refimg = relationship('Refimg', secondary=Person_Refimg_Link.__table__) + + def __repr__(self): + return "".format(self.tag,self.firstname, self.surname, self.refimg) + +class Refimg(Base): + __tablename__ = "refimg" + id = Column(Integer, Sequence('refimg_id_seq'), primary_key=True ) + fname = Column(String(256), unique=True, nullable=False) + encodings = Column(LargeBinary) + + def __repr__(self): + return "".format(self.id, self.fname, self.encodings ) + +class File_Person_Link(Base): + __tablename__ = "file_person_link" + file_id = Column(Integer, ForeignKey('file.eid'), unique=True, nullable=False, primary_key=True) + person_id = Column(Integer, ForeignKey('person.id'), unique=True, nullable=False, primary_key=True) + + def __repr__(self): + return "".format(self.file_id, self.person_id) + ################################################################################ @@ -199,6 +245,13 @@ def ProcessImportDirs(parent_job=None): session.commit() if parent_job: AddLogForJob(parent_job, "adding job id={} {} (wait for: {})".format( job2.id, job2.id, job2.name, job2.wait_for ) ) + jex3=JobExtra( name="path", value=path ) + job3=Job(start_time='now()', last_update='now()', name="processai", state="New", wait_for=job.id, pa_job_state="New" ) + job3.extra.append(jex3) + session.add(job3) + session.commit() + if parent_job: + AddLogForJob(parent_job, "adding job id={} {} (wait for: {})".format( job3.id, job3.id, job3.name, job3.wait_for ) ) HandleJobs() return @@ -225,6 +278,8 @@ def RunJob(job): JobNewImportDir(job) elif job.name =="getfiledetails": JobGetFileDetails(job) + elif job.name == "processai": + JobProcessAI(job) else: print("ERROR: Requested to process unknown job type: {}".format(job.name)) # okay, we finished a job, so check for any jobs that are dependant on this and run them... @@ -414,6 +469,16 @@ def JobNewImportDir(job): session.commit() return +def JobProcessAI(job): + path=[jex.value for jex in job.extra if jex.name == "path"][0] + path='static/photos' + print('meeeee',path) + for e in FilesInDir( path ): + ProcessFilesInDir(job, e, ProcessAI, lambda a: True) + FinishJob(job, "Finished Processesing AI") + r = session.query(Refimg).get(1) + print(r) + def JobImportDir(job): JobProgressState( job, "In Progress" ) settings = session.query(Settings).first() @@ -527,6 +592,65 @@ def HashAndThumbDirHasNew(dir): dir.last_hash_date = time.time() return 1 +def ProcessAI(job, e): + print('AING',e) + + if e.type.name != 'Image': + return + + people = session.query(Person).all() + for person in people: + generateKnownEncodings(person) + + file = e.in_dir[0].path_prefix + '/' + e.name + im_orig = Image.open(file) + im = ImageOps.exif_transpose(im_orig) + + unknown_encodings = generateUnknownEncodings(im) + + for unknown_encoding in unknown_encodings: + for person in people: + lookForPersonInImage(person, unknown_encoding, e) + return + +def lookForPersonInImage(person, unknown_encoding, e): + for refimg in person.refimg: + deserialized_bytes = numpy.frombuffer(refimg.encodings, dtype=numpy.float64) + #deserialized_x = numpy.reshape(deserialized_bytes, newshape=(2,2)) + + results = compareAI(deserialized_bytes, unknown_encoding) + if results[0]: + print(f'Found a match between: {person.tag} and {e.name}') + fpl = File_Person_Link(person_id=person.id, file_id=e.file_details[0].eid) + session.add(fpl) + return + +def generateUnknownEncodings(im): + unknown_image = numpy.array(im) + face_locations = face_recognition.face_locations(unknown_image) + unknown_encodings = face_recognition.face_encodings(unknown_image, known_face_locations=face_locations) + # should save these to the db + # file.locations = face_locations + + + + return unknown_encodings + + +def generateKnownEncodings(person): + for refimg in person.refimg: + img = face_recognition.load_image_file('reference_images/'+refimg.fname) + location = face_recognition.face_locations(img) + encodings = face_recognition.face_encodings(img, known_face_locations=location) + refimg.encodings = encodings[0].tobytes() + session.add(refimg) + session.commit() + +def compareAI(known_encoding, unknown_encoding): + results = face_recognition.compare_faces([known_encoding], unknown_encoding, tolerance=0.55) + return results + + def ProcessFilesInDir(job, e, file_func, go_into_dir_func): if DEBUG==1: print("DEBUG: files in dir - process: {} {}".format(e.name, e.in_dir[0].path_prefix)) @@ -602,18 +726,17 @@ def isImage(file): def GenImageThumbnail(job, file): thumbnail=None AddLogForJob( job, "Generate Thumbnail from Image file: {}".format( file ), file ) - f = open(file, 'rb') try: - tags = exifread.process_file(f) - if '20210121_223307.jpg' in file: - print("Tag: img orientation={}".format( tags['Image Orientation']) ) - print("Tag: GPS GPSLatitude={}".format( tags['GPS GPSLatitude']) ) - thumbnail = base64.b64encode(tags['JPEGThumbnail']) - thumbnail = str(thumbnail)[2:-1] + im_orig = Image.open(file) + im = ImageOps.exif_transpose(im_orig) + im.thumbnail((256,256)) + img_bytearray = io.BytesIO() + im.save(img_bytearray, format='JPEG') + img_bytearray = img_bytearray.getvalue() + thumbnail = base64.b64encode(img_bytearray) except: print('WARNING: NO EXIF TAGS?!?!?!?') AddLogForJob(job, "WARNING: No EXIF TAF found for: {}".format(file)) - f.close() return thumbnail