diff --git a/BUGs b/BUGs index ab7241f..4d138d0 100644 --- a/BUGs +++ b/BUGs @@ -1,5 +1,2 @@ -### Next: 92 +### Next: 94 BUG-85: once we rebuild data from scratch, need to reset just clean out pa_user_state's -BUG-91: face_recognition not working on many of Mandy's newer phone images - -- its when a photo is rotated -- can use: - exifautotran file.jpg, and then all should work (no loss), could then skip fix in thumbs too? diff --git a/TODO b/TODO index faa2533..ed1ee4d 100644 --- a/TODO +++ b/TODO @@ -7,6 +7,7 @@ [DONE] - ignore/not a face/too young [DONE] - redraw 'ignore's as a greyed out box? [DONE] - menu should only allow override IF we have put override on... + all NMO's need to handle delete data and rebuild / allow recreation of content form FS (not just test, it causes a bug now / db constraint violation) --> need to test the 'override' when we re-ai-match (AFTER re-build from FS) * run_ai_on throws log line, for matching even if there are no faces, would be less noisy to not do that (or should say no faces?) diff --git a/pa_job_manager.py b/pa_job_manager.py index 3838091..5f9a1f3 100644 --- a/pa_job_manager.py +++ b/pa_job_manager.py @@ -238,6 +238,7 @@ class Settings(Base): import_path = Column(String) storage_path = Column(String) recycle_bin_path = Column(String) + auto_rotate = Column(Boolean) default_refimg_model = Column(Integer,ForeignKey('ai_model.id'), unique=True, nullable=False) default_scan_model = Column(Integer,ForeignKey('ai_model.id'), unique=True, nullable=False) default_threshold = Column(Integer) @@ -248,7 +249,7 @@ class Settings(Base): bin_cleanup_file_age = Column(Integer) def __repr__(self): - return f"" + return f"" ################################################################################ # Class describing Person to Refimg link in DB via sqlalchemy @@ -422,6 +423,37 @@ class PA_JobManager_FE_Message(Base): def __repr__(self): return "" + + ############################################################################## # MessageToFE(): sends a specific alert/messasge for a given job via the DB to # the front end @@ -789,6 +821,7 @@ def JobScanStorageDir(job): ############################################################################## def JobForceScan(job): JobProgressState( job, "In Progress" ) + session.query(PA_UserState).delete() session.query(FaceFileLink).delete() session.query(FaceRefimgLink).delete() session.query(Face).delete() @@ -1526,7 +1559,8 @@ def JobTransformImage(job): out = im.rotate(int(amt), expand=True) out.save( e.FullPathOnFS() ) print( f"JobTransformImage DONE transform: job={job.id}, id={id}, amt={amt}" ) - e.file_details.thumbnail, _ , _ = GenThumb( e.FullPathOnFS() ) + settings = session.query(Settings).first() + e.file_details.thumbnail, _ , _ = GenThumb( e.FullPathOnFS(), settings.auto_rotate ) e.file_details.hash = md5( job, e ) print( f"JobTransformImage DONE thumb: job={job.id}, id={id}, amt={amt}" ) session.add(e) @@ -1642,7 +1676,8 @@ def isImage(file): #################################################################################################################################### def GenImageThumbnail(job, file): ProcessFileForJob( job, "Generate Thumbnail from Image file: {}".format( file ), file ) - thumb, _, _ = GenThumb(file) + settings = session.query(Settings).first() + thumb, _, _ = GenThumb(file, settings.auto_rotate) return thumb #################################################################################################################################### diff --git a/person.py b/person.py index d364412..36a6c17 100644 --- a/person.py +++ b/person.py @@ -97,7 +97,8 @@ class PersonForm(FlaskForm): def AddRefimgToPerson( filename, person ): refimg = Refimg( fname=os.path.basename( filename ) ) try: - refimg.thumbnail, refimg.orig_w, refimg.orig_h = GenThumb( filename ) + #False == dont autorotate, its not needed on this image + refimg.thumbnail, refimg.orig_w, refimg.orig_h = GenThumb( filename, False ) settings = Settings.query.first() model=AIModel.query.get(settings.default_refimg_model) refimg.face, face_locn = GenFace( filename, model=model.name ) diff --git a/settings.py b/settings.py index 5233c0c..8cda88d 100644 --- a/settings.py +++ b/settings.py @@ -1,11 +1,11 @@ from wtforms import SubmitField, StringField, IntegerField, FloatField, HiddenField, validators, Form, SelectField, BooleanField from flask_wtf import FlaskForm from flask import request, render_template, redirect, url_for -from main import db, app, ma from sqlalchemy import Sequence from sqlalchemy.exc import SQLAlchemyError from status import st, Status from flask_login import login_required, current_user +from main import db, app, ma # pylint: disable=no-member diff --git a/shared.py b/shared.py index c45cef9..1e20c69 100644 --- a/shared.py +++ b/shared.py @@ -3,6 +3,7 @@ import os import face_recognition import io import base64 +import subprocess from PIL import Image, ImageOps class PA: @@ -114,13 +115,21 @@ def SymlinkName(ptype, path, file): # generates the thumbnail for an image - uses THUMBSIZE, and deals with non RGB images, and rotated images (based on exif) # returns data for thumbnail and original width and height, which gets stored in DB. Used when re-scaling viewed thumbs (refimgs on person page) -def GenThumb(fname): +def GenThumb(fname,auto_rotate): try: - im_orig = Image.open(fname) - if im_orig.format == 'JPEG': - im = ImageOps.exif_transpose(im_orig) + if auto_rotate: + # run cmdline util to re-orient jpeg (only changes if needed, and does it losslessly) + print( f"exifautotran {fname}") + p = subprocess.run(["/usr/bin/exifautotran",fname] ) + im=Image.open(fname) + # if we don't autorotate/touch the original, we still want the thumbnail oriented the right way else: - im = im_orig + im_orig = Image.open(fname) + if im_orig.format == 'JPEG': + im = ImageOps.exif_transpose(im_orig) + else: + im = im_orig + # if mode isn't RGB thumbnail fails, so force it if needed if im.mode != "RGB": im = im.convert('RGB') orig_w, orig_h = im.size