Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: PictureFrame 2020 New MQTT option #64

Open
Duzeper opened this issue May 24, 2021 · 7 comments
Open

Request: PictureFrame 2020 New MQTT option #64

Duzeper opened this issue May 24, 2021 · 7 comments

Comments

@Duzeper
Copy link

Duzeper commented May 24, 2021

Is there any way to display the contents of an Exif field (specifically 0x9286 UserComment), using MQTT, in the same way as (Date, Location, Filename), which are all working great.
I am using ExifTool to add the image's containing folder name to this field and it would be nice to have an MQTT option to display the contents of that field.

Martin Dashper

@paddywwoof
Copy link
Member

Martin It obviously would be possible in the same way that the other exif info is displayed. However it would need several changes to the code: 1) a field in the db meta table 2) an entry in the EXIF_TO_FIELD dictionary 3) __get_exif_info() would need changing to use EXIF_TO_FIELD (which I will do anyway to tidy it up) GetImageMeta.get_exif() should work unchanged but.. 4) the largest amount of changes would be in viewer_display here and here, model here, configuration.yaml, and additional functions to turn on and off in interface_mqtt, interface_http and index.html.

So quite a lot of changes - which begs the question why we hard coded so much in? Which is a good question, in retrospect!

For your specific application the folder name containing the image is already available. Can you use that?

@Duzeper
Copy link
Author

Duzeper commented May 24, 2021

Thanks Paddy.
I am clearly using an earlier version of PictureFrame2020config.py as my 'show_text' line does not include 'folder'. Obviously I can update the application (which is probably a good idea anyway). However, if, as I assume, that means the folder on the pi containing the images, then that would not achieve what I am looking for.

Basically I am looking to display the location of the image. For images that have GPS data then that is working fine, but many of my images do not have GPS data so, at the moment, I have to display the date of the image and then go back to my PC (where the originals are stored) and look up the location for that date.

My photos are stored in folders on the PC named by approximate location and date, for example 'Tenerife 2008', 'Malta 2011' and so on. For images without GPS data then that name would be adequate for my purposes. The images on the pi for the picture frame are in folders based on common keywords and contain images from multiple PC folders. My thought was to embed the original folder name in the image metadata and the exif 'UserComment' field seemed a reasonable option (although, I assume that any Ascii type Exif tag would work as well). As I said, I have found a successful method to do this using ExifTool.

I wondered, therefore, in the same way that PictureFrame2020geo.py retrieves the Exif GPS data, whether the 'UserComment' data could be retrieved similarly.

I take it, however, that the issues that you highlighted would still apply, although all the links you mentioned all point to 'picframe' and not 'pi3d_demos'

Martin

@paddywwoof
Copy link
Member

Martin, sorry, I've been concentrating entirely on the picframe version since end of last year so hadn't realised this was all about PictureFrame2020 - I will think again about your question and get back to you!

Paddy

@helgeerbe
Copy link

Hi @Duzeper have a look at https://github.com/helgeerbe/picframe. You could store your location infos in the exif title or caption field. I'm not sure if you already use this fields, but if not this would be a good workaround.

The new PictureFrame allows to show title, caption, file name, folder, location and date.

@Duzeper
Copy link
Author

Duzeper commented May 27, 2021

Thanks, I will bear that in mind. However, I am not a developer. My entire, and very limited, knowledge of Linux and Python has come from Pictureframe2020 and I have now largely got it doing what I want (apart from this question obviously) and so I am wary about starting from scratch with a new application. I just thought that, given that PictureFrame2020geo can retrieve Exif GPS data from an image, surely it shouldn't be too difficult to do the same for another Exif field.
You mention Exif Title and Caption, but as far as I can see, these are XMP fields and do not appear in the list of Exif fields on my images even if I explicitly write data to them, whereas UserComment does.

@paddywwoof
Copy link
Member

Hi, I can understand not wanting to unscramble the work you've already done and find all the problems with a new version of the app. However there are quite a few improvements to the way the picture frame chaches info so it doesn't have to reload everything from scratch each time, among many others.

I don't know which version of PictureFrame2020 you are using but here is a diff of the latest on gitub/pi3d/pi3d_demos compared with a very quick scan through of the things I think you will need to add. I've not attempted to run it yet so there could well be typos and bits of wrong code!

PictureFrame2020.py

diff --git a/PictureFrame2020.py b/PictureFrame2020.py
index 9293b52..92a599b 100644
--- a/PictureFrame2020.py
+++ b/PictureFrame2020.py
@@ -27,7 +27,7 @@ from PIL import Image, ExifTags, ImageFilter # these are needed for getting exif
 import PictureFrame2020config as config
 
 class Pic:
-  def __init__(self, fname, orientation=1, mtime=None, dt=None, fdt=None, location="", aspect=1.5):
+  def __init__(self, fname, orientation=1, mtime=None, dt=None, fdt=None, location="", aspect=1.5, user_comment=""):
     self.fname = fname
     self.orientation = orientation
     self.mtime = mtime
@@ -35,6 +35,7 @@ class Pic:
     self.fdt = fdt
     self.location = location
     self.aspect = aspect
+    self.user_comment = user_comment
     self.shown_with = None # set to pic_num of image this was paired with
 
 try:
@@ -144,12 +145,13 @@ def tex_load(pic_num, iFiles, size=None):
       if pic_num < len(iFiles) - 1: # i.e can't do this on the last image in list
         for f_rec in iFiles[pic_num + 1:]:
           if f_rec.dt is None or f_rec.fdt is None: # dt and fdt set to None before exif read
-            (f_orientation, f_dt, f_fdt, f_location, f_aspect) = get_exif_info(f_rec.fname)
+            (f_orientation, f_dt, f_fdt, f_location, f_aspect, f_user_comment) = get_exif_info(f_rec.fname)
             f_rec.orientation = f_orientation
             f_rec.dt = f_dt
             f_rec.fdt = f_fdt
             f_rec.location = f_location
             f_rec.aspect = f_aspect
+            f_rec.user_comment = f_user_comment
           if f_rec.aspect < 1.0 and f_rec.shown_with is None:
             im2 = Image.open(f_rec.fname)
             f_rec.shown_with = pic_num
@@ -243,8 +245,9 @@ def get_files(dt_from=None, dt_to=None):
               fdt = None
               location = ""
               aspect = 1.5 # assume landscape aspect until we determine otherwise
+              user_comment = ""
               if not config.DELAY_EXIF and EXIF_DATID is not None and EXIF_ORIENTATION is not None:
-                (orientation, dt, fdt, location, aspect) = get_exif_info(file_path_name)
+                (orientation, dt, fdt, location, aspect, user_comment) = get_exif_info(file_path_name)
                 if (dt_from is not None and dt < dt_from) or (dt_to is not None and dt > dt_to):
                   include_flag = False
               if include_flag:
@@ -255,7 +258,8 @@ def get_files(dt_from=None, dt_to=None):
                                     dt,
                                     fdt,
                                     location,
-                                    aspect))
+                                    aspect,
+                                    user_comment))
   if shuffle:
     file_list.sort(key=lambda x: x.mtime) # will be later files last
     temp_list_first = file_list[-config.RECENT_N:]
@@ -273,6 +277,7 @@ def get_exif_info(file_path_name, im=None):
   orientation = 1
   location = ""
   aspect = 1.5 # assume landscape aspect until we determine otherwise
+  user_comment = ""
   try:
     if im is None:
       im = Image.open(file_path_name) # lazy operation so shouldn't load (better test though)
@@ -285,13 +290,15 @@ def get_exif_info(file_path_name, im=None):
         orientation = int(exif_data[EXIF_ORIENTATION])
         if orientation == 6 or orientation == 8:
             aspect = 1.0 / aspect # image rotated 270 or 90 degrees
+    if EXIF_USERCOMMENT in exif_data:
+        user_comment = exif_data[EXIF_USERCOMMENT]
     if config.LOAD_GEOLOC and geo.EXIF_GPSINFO in exif_data:
       location = geo.get_location(exif_data[geo.EXIF_GPSINFO])
   except Exception as e: # NB should really check error here but it's almost certainly due to lack of exif data
     if config.VERBOSE:
       print('trying to read exif', e)
   fdt = time.strftime(config.SHOW_TEXT_FM, time.localtime(dt))
-  return (orientation, dt, fdt, location, aspect)
+  return (orientation, dt, fdt, location, aspect, user_comment)
 
 def convert_heif(fname):
     try:
@@ -307,11 +314,14 @@ def convert_heif(fname):
 
 EXIF_DATID = None # this needs to be set before get_files() above can extract exif date info
 EXIF_ORIENTATION = None
+EXIF_USERCOMMENT = None
 for k in ExifTags.TAGS:
   if ExifTags.TAGS[k] == 'DateTimeOriginal':
     EXIF_DATID = k
   if ExifTags.TAGS[k] == 'Orientation':
     EXIF_ORIENTATION = k
+  if ExifTags.TAGS[k] == 'UserComment':
+    EXIF_USERCOMMENT = k
 
 if config.LOAD_GEOLOC:
   import PictureFrame2020geo as geo
@@ -344,6 +354,7 @@ if config.USE_MQTT:
       client.subscribe("{}text_on".format(id), qos=0) # toggle file name on and off. payload text show time in seconds
       client.subscribe("{}date_on".format(id), qos=0) # toggle date (exif if avail else file) on and off. payload show time
       client.subscribe("{}location_on".format(id), qos=0) # toggle location (if enabled) on and off. payload show time
+      client.subscribe("{}usercomment_on".format(id), qos=0) # toggle location (if enabled) on and off. payload show time
       client.subscribe("{}text_off".format(id), qos=0) # turn all name, date, location off
       client.subscribe("{}text_refresh".format(id), qos=0) # restarts current slide showing text set above
       client.subscribe("{}brightness".format(id), qos=0) # set shader brightness
@@ -433,6 +444,10 @@ if config.USE_MQTT:
           config.SHOW_TEXT_TM = float_msg if float_msg > 2.0 else 0.33 * config.TIME_DELAY
           config.SHOW_TEXT ^= 4
           text_start_tm = -0.1
+      elif message.topic == "{}usercomment_on".format(id):
+          config.SHOW_TEXT_TM = float_msg if float_msg > 2.0 else 0.33 * config.TIME_DELAY
+          config.SHOW_TEXT ^= 16
+          text_start_tm = -0.1
       elif message.topic == "{}text_off".format(id):
           config.SHOW_TEXT = 0
           text_start_tm = -0.1
@@ -588,6 +603,8 @@ while DISPLAY.loop_running():
             info_strings.append(loc_string)
         if (config.SHOW_TEXT & 8) == 8: # folder
           info_strings.append(sanitize_string(os.path.basename(os.path.dirname(iFiles[pic_num].fname))))
+        if (config.SHOW_TEXT & 16) == 16: # usercomment
+          info_strings.append(sanitize_string(iFiles[pic_num].user_comment))
         if paused:
           info_strings.append("PAUSED")

PictureFrame2020config.py

diff --git a/PictureFrame2020config.py b/PictureFrame2020config.py
index 8e75b19..0bf52b2 100644
--- a/PictureFrame2020config.py
+++ b/PictureFrame2020config.py
@@ -25,6 +25,8 @@ def parse_show_text(txt):
         show_text |= 4
     if "folder" in txt:
         show_text |= 8
+    if "usercomment" in txt:
+        show_text |= 16
     return show_text
 
 # NB the reason that absolute paths are used here is because relative ones can lead
@@ -55,7 +57,7 @@ parse.add_argument("-r", "--reshuffle_num", default=1, type=int, help="times thr
 parse.add_argument("-s", "--show_text_tm",  default=6.0, type=float, help="time to show text over the image")
 parse.add_argument(      "--show_text_fm",  default="%b %d, %Y", help="format to show date over the image")
 parse.add_argument(      "--show_text_sz",  default=40, type=int, help="text character size")
-parse.add_argument(      "--show_text",     default="date folder location", help="show text, include combination of words: name, date, location")
+parse.add_argument(      "--show_text",     default="name date folder location usercomment", help="show text, include combination of words: name, date, location")
 parse.add_argument(      "--text_width",    default=90, type=int, help="number of character before breaking into new line")
 parse.add_argument("-t", "--fit",           default=False, type=str_to_bool, help="shrink to fit screen i.e. don't crop")
 parse.add_argument("-u", "--kenburns",      default=False, type=str_to_bool, help="will set FIT->False and BLUR_EDGES->False")

@Duzeper
Copy link
Author

Duzeper commented Jun 1, 2021

Thanks. I'll look into that. However, at the moment, with, at last, some good weather, my garden is calling me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants