Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
helgeerbe committed Sep 22, 2021
2 parents 64e1fa9 + 096be16 commit e68baaa
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 192 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,6 @@ dmypy.json

# Pyre type checker
.pyre/

#mac os
.DS_Store
2 changes: 2 additions & 0 deletions picframe/config/configuration_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ viewer:
model:
pic_dir: "~/Pictures" # default="~/Pictures", root folder for images
deleted_pictures: "~/DeletedPictures" # move deleted pictures here
follow_links: False # default=False, By default, picframe will not walk down into symbolic links that resolve to directories. Set follow_links to True to visit directories pointed to by symlinks, on systems that support them.
no_files_img: "~/picframe_data/data/no_pictures.jpg" # default="PictureFrame2020img.jpg", image to show if none selected
subdirectory: "" # default="", subdir of pic_dir - can be changed by MQTT"
recent_n: 7 # default=7 (days), when shuffling file change date more recent than this number of days play before the rest
Expand All @@ -62,6 +63,7 @@ model:
"EXIF FocalLength",
"EXIF DateTimeOriginal",
"Image Model",
"Image Make",
"IPTC Caption/Abstract",
"IPTC Object Name",
"IPTC Keywords"]
Expand Down
48 changes: 45 additions & 3 deletions picframe/get_image_meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import exifread
import logging
import os
from PIL import Image

class GetImageMeta:
Expand Down Expand Up @@ -102,7 +103,23 @@ def get_orientation(self):

def get_exif(self, key):
try:
val = self.__get_if_exist(key)
iso_keys = ['EXIF ISOSpeedRatings', 'EXIF PhotographicSensitivity', 'EXIF ISO'] # ISO prior 2.2, ISOSpeedRatings 2.2, PhotographicSensitivity 2.3
if key in iso_keys:
for iso in iso_keys:
val = self.__get_if_exist(iso)
if val:
break
else:
val = self.__get_if_exist(key)

if val is None:
grp, tag = key.split(" ", 1)
if grp == "EXIF":
newkey = "Image" + " " + tag
val = self.__get_if_exist(newkey)
elif grp == "Image":
newkey = "EXIF" + " " + tag
val = self.__get_if_exist(newkey)
if val is not None:
if key == 'EXIF FNumber':
val = round(val.values[0].num / val.values[0].den, 1)
Expand All @@ -117,7 +134,32 @@ def get_exif(self, key):

def get_size(self):
try: # corrupt image file might crash app
return Image.open(self.__filename).size
return GetImageMeta.get_image_object(self.__filename).size
except Exception as e:
self.__logger.warning("get_size failed on %s -> %s", self.__filename, e)
return (0, 0)
return (0, 0)

@staticmethod
def get_image_object(fname):
ext = os.path.splitext(fname)[1].lower()
if ext in ('.heif','.heic'):
try:
import pyheif

heif_file = pyheif.read(fname)
image = Image.frombytes(heif_file.mode, heif_file.size, heif_file.data,
"raw", heif_file.mode, heif_file.stride)
if image.mode not in ("RGB", "RGBA"):
image = image.convert("RGB")
return image
except:
logger = logging.getLogger("get_image_meta.GetImageMeta")
logger.warning("Failed attempt to convert %s \n** Have you installed pyheif? **", fname)
else:
try:
image = Image.open(fname)
if image.mode not in ("RGB", "RGBA"): # mat system needs RGB or more
image = image.convert("RGB")
except: # for whatever reason
image = None
return image
29 changes: 18 additions & 11 deletions picframe/image_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ImageCache:

EXTENSIONS = ['.png','.jpg','.jpeg','.heif','.heic']
EXIF_TO_FIELD = {'EXIF FNumber': 'f_number',
'EXIF Make': 'make',
'Image Make': 'make',
'Image Model': 'model',
'EXIF ExposureTime': 'exposure_time',
'EXIF ISOSpeedRatings': 'iso',
Expand All @@ -22,7 +22,7 @@ class ImageCache:
'IPTC Object Name': 'title'}


def __init__(self, picture_dir, db_file, geo_reverse, portrait_pairs=False):
def __init__(self, picture_dir, follow_links, db_file, geo_reverse, portrait_pairs=False):
# TODO these class methods will crash if Model attempts to instantiate this using a
# different version from the latest one - should this argument be taken out?
self.__modified_folders = []
Expand All @@ -32,6 +32,7 @@ def __init__(self, picture_dir, db_file, geo_reverse, portrait_pairs=False):
self.__logger = logging.getLogger("image_cache.ImageCache")
self.__logger.debug('Creating an instance of ImageCache')
self.__picture_dir = picture_dir
self.__follow_links = follow_links
self.__db_file = db_file
self.__geo_reverse = geo_reverse
self.__portrait_pairs = portrait_pairs #TODO have a function to turn this on and off?
Expand Down Expand Up @@ -95,9 +96,9 @@ def update_cache(self):
self.__logger.debug('Inserting: %s', file)
self.__insert_file(file)

# If we've process all files in the current collection, update the cached folder mod times
# If we've process all files in the current collection, update the cached folder info
if not self.__modified_files:
self.__update_folder_modtimes(self.__modified_folders)
self.__update_folder_info(self.__modified_folders)
self.__modified_folders.clear()

# If looping is still not paused, remove any files or folders from the db that are no longer on disk
Expand Down Expand Up @@ -346,10 +347,15 @@ def __update_schema(self, required_db_schema_version):
self.__db.execute('INSERT INTO db_info VALUES(?)', (required_db_schema_version,))


# --- Returns a set of folders matching any of
# - Found on disk, but not currently in the 'folder' table
# - Found on disk, but newer than the associated record in the 'folder' table
# - Found on disk, but flagged as 'missing' in the 'folder' table
# --- Note that all folders returned currently exist on disk
def __get_modified_folders(self):
out_of_date_folders = []
sql_select = "SELECT * FROM folder WHERE name = ?"
for dir in [d[0] for d in os.walk(self.__picture_dir)]:
for dir in [d[0] for d in os.walk(self.__picture_dir, followlinks=self.__follow_links)]:
mod_tm = int(os.stat(dir).st_mtime)
found = self.__db.execute(sql_select, (dir,)).fetchone()
if not found or found['last_modified'] < mod_tm or found['missing'] == 1:
Expand Down Expand Up @@ -403,9 +409,9 @@ def __insert_file(self, file):
self.__db.execute(meta_insert, vals)


def __update_folder_modtimes(self, folder_collection):
def __update_folder_info(self, folder_collection):
update_data = []
sql = "UPDATE folder SET last_modified = ? WHERE name = ?"
sql = "UPDATE folder SET last_modified = ?, missing = 0 WHERE name = ?"
for folder, modtime in folder_collection:
update_data.append((modtime, folder))
self.__db.executemany(sql, update_data)
Expand Down Expand Up @@ -454,19 +460,20 @@ def __get_exif_info(self, file_path_name):
e['orientation'] = exifs.get_orientation()

width, height = exifs.get_size()
if e['orientation'] in (5, 6, 7, 8):
ext = os.path.splitext(file_path_name)[1].lower()
if ext not in ('.heif','.heic') and e['orientation'] in (5, 6, 7, 8):
width, height = height, width # swap values
e['width'] = width
e['height'] = height


e['f_number'] = exifs.get_exif('EXIF FNumber')
e['make'] = exifs.get_exif('EXIF Make')
e['make'] = exifs.get_exif('Image Make')
e['model'] = exifs.get_exif('Image Model')
e['exposure_time'] = exifs.get_exif('EXIF ExposureTime')
e['iso'] = exifs.get_exif('EXIF ISOSpeedRatings')
e['focal_length'] = exifs.get_exif('EXIF FocalLength')
e['rating'] = exifs.get_exif('EXIF Rating')
e['rating'] = exifs.get_exif('Image Rating')
e['lens'] = exifs.get_exif('EXIF LensModel')
e['exif_datetime'] = None
val = exifs.get_exif('EXIF DateTimeOriginal')
Expand Down Expand Up @@ -500,7 +507,7 @@ def __get_exif_info(self, file_path_name):

# If being executed (instead of imported), kick it off...
if __name__ == "__main__":
cache = ImageCache(picture_dir='/home/pi/Pictures', db_file='/home/pi/db.db3', geo_reverse=None)
cache = ImageCache(picture_dir='/home/pi/Pictures', follow_links=False, db_file='/home/pi/db.db3', geo_reverse=None)
#cache.update_cache()
# items = cache.query_cache("make like '%google%'", "exif_datetime asc")
#info = cache.get_file_info(12)
4 changes: 3 additions & 1 deletion picframe/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

'pic_dir': '~/Pictures',
'no_files_img': '~/picframe_data/data/no_pictures.jpg',
'follow_links': False,
'subdirectory': '',
'recent_n': 3,
'reshuffle_num': 1,
Expand Down Expand Up @@ -170,6 +171,7 @@ def __init__(self, configfile = DEFAULT_CONFIGFILE):
self.__load_geoloc = model_config['load_geoloc']
self.__geo_reverse = geo_reverse.GeoReverse(model_config['geo_key'], key_list=self.get_model_config()['key_list'])
self.__image_cache = image_cache.ImageCache(self.__pic_dir,
model_config['follow_links'],
os.path.expanduser(model_config['db_file']),
self.__geo_reverse,
model_config['portrait_pairs'])
Expand Down Expand Up @@ -264,7 +266,7 @@ def get_directory_list(self):
actual_dir = root
if self.subdirectory != '':
actual_dir = self.subdirectory
subdir_list = next(os.walk(self.__pic_dir))[1]
subdir_list = next(os.walk(self.__pic_dir, self.get_model_config()['follow_links']))[1]
subdir_list[:] = [d for d in subdir_list if not d[0] == '.']
subdir_list.insert(0,root)
return actual_dir, subdir_list
Expand Down
44 changes: 14 additions & 30 deletions picframe/viewer_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import os
import numpy as np
from PIL import Image, ImageFilter, ImageFile
from picframe import mat_image
from picframe import mat_image, get_image_meta
from datetime import datetime

# supported display modes for display switch
Expand Down Expand Up @@ -173,29 +173,6 @@ def clock_is_on(self):
def clock_is_on(self, val):
self.__show_clock = val

def __check_heif_then_open(self, fname):
ext = os.path.splitext(fname)[1].lower()
if ext in ('.heif','.heic'):
try:
import pyheif

heif_file = pyheif.read(fname)
image = Image.frombytes(heif_file.mode, heif_file.size, heif_file.data,
"raw", heif_file.mode, heif_file.stride)
if image.mode not in ("RGB", "RGBA"):
image = image.convert("RGB")
return image
except:
self.__logger.warning("Failed attempt to convert %s \n** Have you installed pyheif? **", fname)
else:
try:
image = Image.open(fname)
if image.mode not in ("RGB", "RGBA"): # mat system needs RGB or more
image = image.convert("RGB")
except: # for whatever reason
image = None
return image

# Concatenate the specified images horizontally. Clip the taller
# image to the height of the shorter image.
def __create_image_pair(self, im1, im2):
Expand All @@ -210,7 +187,11 @@ def __create_image_pair(self, im1, im2):
dst.paste(im2, (im1.width + sep, 0))
return dst

def __orientate_image(self, im, orientation):
def __orientate_image(self, im, pic):
ext = os.path.splitext(pic.fname)[1].lower()
if ext in ('.heif','.heic'): # heif and heic images are converted to PIL.Image obects and are alway in correct orienation
return im
orientation = pic.orientation
if orientation == 2:
im = im.transpose(Image.FLIP_LEFT_RIGHT)
elif orientation == 3:
Expand Down Expand Up @@ -272,15 +253,18 @@ def __tex_load(self, pics, size=None):

# Load the image(s) and correct their orientation as necessary
if pics[0]:
im = self.__check_heif_then_open(pics[0].fname)
if pics[0].orientation != 1:
im = self.__orientate_image(im, pics[0].orientation)
im = get_image_meta.GetImageMeta.get_image_object(pics[0].fname)
if im is None:
return None
if pics[0].orientation != 1:
im = self.__orientate_image(im, pics[0])

if pics[1]:
im2 = self.__check_heif_then_open(pics[1].fname)
im2 = get_image_meta.GetImageMeta.get_image_object(pics[1].fname)
if im2 is None:
return None
if pics[1].orientation != 1:
im2 = self.__orientate_image(im2, pics[1].orientation)
im2 = self.__orientate_image(im2, pics[1])

screen_aspect, image_aspect, diff_aspect = self.__get_aspect_diff(size, im.size)

Expand Down
File renamed without changes
File renamed without changes
Binary file added test/images/test3.HEIC
Binary file not shown.
Loading

0 comments on commit e68baaa

Please sign in to comment.