Advanced: Raspberry Pi photo frame rotates and uses your pictures from your Google Drive

Intro
In my previous post I implemented a digital photo frame using the PiDisplay, which, it should be noted, is a 7″, widescreen, diagonal LCD display to which the Ras Pi is screwed on so together they weigh maybe two pounds.

Here I’m going to go a little nuts and attempt to rotate the whole thing 90 degrees, as needed, to display each picture in the slideshow at its maximum size, given that some pictures are taken in portrait mode and get severely reduced in size leaving unused display real estate in order to be displayed right-side-up.

I’m not sure it’s gonna work, but I will try with relatively inexpensive components.

Pictures illustrate the problem.

Back of pidisplay with Raspberry Pi 3 attached

To be continued…

This example python program actually worked for me with the Sunfounder 55g servo mentioned in the references.

import RPi.GPIO as GPIO
import time
 
servoPIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN, GPIO.OUT)
 
p = GPIO.PWM(servoPIN, 50) # GPIO 17 for PWM with 50Hz
p.start(7.5) # Initialization
# 2.5 does a counterclockwise rotate 90 degrees frmo neutral
p.ChangeDutyCycle(2.5)
time.sleep(2)
# 12.5 does a clockwise rotate 90 degrees from neutral
p.ChangeDutyCycle(12.5)
time.sleep(2)
# 7.5 is neutral
p.ChangeDutyCycle(7.5)
time.sleep(3)
p.stop()
GPIO.cleanup()

I put the pins as follows:
– brown – 6 (ground)
– red – 1 (5 volts)
– orange – 11 (pwm signal)

Based on that, here is a tiny program to rotate 90 degrees clockwise, starting from neutral, which I called pwmclockwise.py:

import RPi.GPIO as GPIO
import time
 
servoPIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN, GPIO.OUT)
 
p = GPIO.PWM(servoPIN, 50) # GPIO 17 for PWM with 50Hz
# start in neutral - 7.5 duty cycle
p.start(7.5)
# 12.5 does a clockwise rotate 90 degrees from neutral
p.ChangeDutyCycle(12.5)
time.sleep(0.1)
p.stop()
GPIO.cleanup()

and here is the corresponding program to rotate from that position 90 degrees counterclockwise back to neutral position, which I call pwmcounterclockwise.py

import RPi.GPIO as GPIO
import time
 
servoPIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN, GPIO.OUT)
 
p = GPIO.PWM(servoPIN, 50) # GPIO 17 for PWM with 50Hz
# start in already clockwise-rotated position
p.start(12.5)
# 7.5 does a counterclockwise rotate 90 degrees back to neutral
p.ChangeDutyCycle(7.5)
time.sleep(0.1)
p.stop()
GPIO.cleanup()


Power surge

When the servo first starts to move, I noticed that the pidisplay screen goes dim for a very brief fractional second. Once it’s in motion the display returns to its standard illumination. This can be attributed to electric motor’s standard power surge upon start-up. An electrical engineer friend of mine suggested that Adafruit’s Servo hat would fix that. I don’t think the problem is so bad so I don’t think I’ll go that route, but it’s good to knmow there are options. See references for a link.


Formula for turning duty cycle variable into servo angle

Recall for algebra that knowledge of two points determines the slope and y intercept of the line that intercepts them. In our case the two points are (2.5, -90) and (12.5,90). After doing the math I arrive at this formula:

angle = 18 x duty_cycle – 135

or for a known angle

duty_cycle = (angle – 135) / 18

I will need this as my attached propeller thingy is off by a few degrees so I want to wteak my duty cycles to compensate for that offset.

Examination of orientation tag, width and height
Python program to examine a single image
I called it getinfo.py.

import os,sys
from PIL import Image
from PIL.ExifTags import TAGS
 
for (k,v) in Image.open(sys.argv[1])._getexif().iteritems():
        print '%s = %s' % (TAGS.get(k), v)
im = Image.open(sys.argv[1])
width = im.size[0]
height = im.size[1]
print 'width: %s' % width
print 'height: %s' % height

It tends to spit out some garbage as well, so I tend to run it like this:
$ python ../getinfo.py “20151127_160928.jpg”|strings

ImageWidth = 3264
ImageLength = 2448
ApertureValue = (276, 100)
DateTimeOriginal = 2015:11:27 16:09:28
DateTimeDigitized = 2015:11:27 16:09:28
MaxApertureValue = (276, 100)
ExifVersion = 0220
ComponentsConfiguration =
LightSource = 0
Flash = 1
FocalLength = (370, 100)
ExifImageWidth = 3264
Make = SAMSUNG
Model = SCH-I535
Orientation = 8
YCbCrPositioning = 1
SensingMethod = 2
ExposureBiasValue = (0, 10)
XResolution = (72, 1)
YResolution = (72, 1)
ExposureTime = (1, 30)
ExifInteroperabilityOffset = 4920
ImageUniqueID = ZDFI02
ExposureProgram = 3
ColorSpace = 1
GPSInfo = {0: '\x02\x02\x00\x00'}
ISOSpeedRatings = 400
ResolutionUnit = 2
WhiteBalance = 0
MeteringMode = 3
FNumber = (26, 10)
Software = I535VRUDNE1
DateTime = 2015:11:27 16:09:28
ShutterSpeedValue = (1256, 256)
SceneType = 1
FlashPixVersion = 0100
UserComment = ASCII
ZDFE02
Cur.ZDFI02CML
018001001509FD0901090109010D010D010D090D
FLFL
SVN#
1       A
JKJK
JKJKqf
JKJK
AFAF$
AFAF
AFAF
SceneCaptureType = 0
ExifImageHeight = 2448
ExposureMode = 0
ExifOffset = 242
BrightnessValue = (186, 256)
MakerNote =
0100
width: 3264
height: 2448

Then compare by eye to qiv’s display of the picture:

$ qiv ‐m “20151127_160928.jpg”

Here’s a tiny script I wrote to analyze all the randomly selected pictures in a directory.

ls -1|while read line; do 
echo filename is "$line" >> ~/analyze.log
python ../getinfo.py "$line"|strings >> ~/analyze.log; done

I then peruse the analyze.log file by hand.

You then can categorize the most commonly seen cases and what our actions should be. Make a 2-tuple (orientation,flag), where orientation is the ExIf-derived orientation, and flag is y if width > height, else N. so, in short, (O,f).

Cases   comment                    s/w prep action      Servo action        Example               Make/Model
(1,y)   normal landscape           None                 None                20190701_111351.jpg   Samsung/SM-G965U
(1,n)   upright but shrunk to fit  rotate c.clockwise   rotate clockwise    c and m 3.jpg         LG/LG-VS700
(6,y)   shown sideways             None                 rotate clockwise    DSC04728.JPG          SONY/DSC-W80
(8,y)   shown sideways             None                 rotate c.clockwise  20151127_160928.jpg   Samsung/SCH-I535
(3,y)   upside down                qiv with -l flag     None                20151127_144553.jpg   Samsung/SCH-I535
( ,n)   missing ExIf tag; s/w-gen  rotate c.clockwise   rotate clockwise    xmas20100041.jpg      HP/HP pstc4200
Strange cases
(6,n)   yet it shows upright        ?                   ?                   My Files0084.jpg      KODAK/V530 ZOOM
( ,y)   no Exif tags. landscape    rotate c.clockwise   rotate clockwise    scan-337.jpg          NA
( ,n)   no Exif tags. squished     None                 None                scan-127.jpg          NA

The old Kodak digital camera basically recorded the wrong orientation compared to the modern interpretation. ExIf version is 0200. all our other pics have newer Exif versions, so maybe that is the key. More study is needed. But not all pics from this camera are bad – perhaps it’s just the ones where width < height/ Python image rotate script

import PIL, os
import sys
from PIL import Image
 
picture= Image.open(sys.argv[1])
 
picture.rotate(90,expand=True).save("rot_" + sys.argv[1])

That expand=True qualifier is important. Without it it preserves the horizontal and vertical dimensions and just rotates the picture center with the rest being cropped.

I believe that this script does not alter the Exif orientation.

References and related
Previous post: Raspberry Pi photo frame using your pictures on your Google Drive

Servo motor I am trying. Stall torque is 12 kg/cm and only costs $12. https://smile.amazon.com/gp/product/B01KZKOIZC/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1
Data sheet for that servo: http://wiki.sunfounder.cc/images/7/78/SunFounder_55g_Servo_datasheet.pdf

Adafruit 16-Channel PWM / Servo HAT for Raspberry Pi – costs about $17.

This explains how PWM controls servo motors.

A good description of the rpi.GPIO python library which comes pre-installed on your Raspberry Pi: https://sourceforge.net/p/raspberry-gpio-python/wiki/PWM/

Jumper wires to plug into the Raspberry Pi’s GPIO pins.

Excellent discussion about JPEG orientation – it’s a lot more complex than you’d think!

Lousy PWM control by Raspberry Pi tutorial – until I find a better one.

This entry was posted in Linux, Raspberry Pi. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *