HOWTO: Automate One-A-Day Photography

This will be a somewhat unusual HOWTO compared to those typically on this site.  In an effort to lose weight, I decided I wanted to take a photo of myself, once per day so I could track and further motivate the progress of regular exercise.  I then wanted to overlay the date each photo was taken as well as my weight on that day.  Finally, I wanted animate these daily photos into a GIF and upload it to my website so I can remind myself anywhere of my objective of losing weight.  The trick is I wanted to automate this entire process.  This meant I needed to find a solution for the following:

  1. Remotely control my camera to be able to take a picture and download it from an automated script
  2. Be able to programmatically crop the image from the camera to remove everything except the white background and myself
  3. Have a mechanism to be able to overlay text onto an existing image so I can add the current date any my weight data
  4. Obtain my latest weight from my Garmin Index scale via Garmin Connect and append it to the image
  5. Combine all of the previous images into an animated gif so I can watch my progress
  6. Automatically upload the animated gif to my website so I can view it anywhere

To complete this project, I decided to try and leverage my Canon S100 which I purchased as Canon’s top-end prosumer camera in 2012.  As I researched my options however, I discovered that Canon disabled any kind of remote control capabilities for this camera.  Fortunately I discovered that some very smart people created an alternative firmware for many popular Canon cameras including mine.  This firmware is called the CHDK or “Canon Hackers Developer Kit”.  This provides all sorts of additional features that are otherwise only available in Canon’s DSLR professional series cameras.  The one that I’m most interested in however is that the firmware provided the option to remote control my Canon S100.

Once I had the new firmware installed on my camera, I then obtained a software called CHDKPTP which is what I will use to actually control the camera.  In order for it to work with the custom firmware however, I had to first install a custom USB driver that would allow for direct access between the camera and the computer.

With the ability to now programmatically control my camera, I then leveraged a free tool called “convert.exe” from ImageMagik that allowed me to crop the incoming images, add custom overlay text and convert the end product into GIF files which can then be animated.  Finally I found a tool called gifsicle that can take a series of gif files and animate them together.  I then used winscp.com to upload the final GIF file to my website using SFTP.  Next I wrote a script that would grab my last weight data submitted from my Garmin Scale on Garmin Connect. Finally, I leveraged Autohotkey to map a shortcut to launching a custom PowerShell script I wrote to bring all of these pieces together.

Putting it all together, the work flow I’ve come up with is as follows:

  1. Each morning I first step on my Garmin Index scale which sends all of my weight and health data to Garmin Connect
  2. I power on my S100 camera and on my computer I press “Windows Key+Z”.  This launches my master PowerShell control script.  I then stand in front of the camera at which point the photo is taken, cropped, the date and my weight is added to the bottom right of the image, it’s converted into an animated gif adding to all of the images from previous days and it’s uploaded to my website, all automagically

I’ve included links below to all of the software used as well as my master PowerShell control script for reference.  This can be useful for other forms of camera automation.  For example, you can use the techniques described below to take pictures from multiple cameras at the same time or create more elaborate time lapse photos or any number of other things.  The limit is really your imagination.

Determine the firmware version installed on the camera now so you know what version of the CHDK custom firmware to download
CameraVersion13-sfx.exehttp://savedonthe.net/download/247/CameraVersion13-sfx.html

Download the custom CHDK firmware – I used the full version
s100-101b-1.4.1-4929-full.ziphttp://mighty-hoernsche.de/

Download and install the libusb driver for Windows to allow PTPcam to access the camera directly over USB
zadig-2.3.exehttp://zadig.akeo.ie/

The software that includes ptpcam.exe that is used to actually control the camera with LUA commands
ptpcam_V2.0.ziphttp://forum.chdk-treff.de/viewtopic.php?f=7&t=2122 (In German)

ImageMagick includes the command line tool convert.exe allowing for cropping and annotation of images as well as converting from JPG to GIF
ImageMagick-7.0.7-16-Q16-x64-dll.exehttps://www.imagemagick.org/script/download.php

Gifscile is a command line tool that can generate an animated gif from an included input of individual gifs
gifsicle-1.89-win64.ziphttps://eternallybored.org/misc/gifsicle/

The PowerShell script I cobbled together to make all this work is below.  It’s far from perfect but it does what I needed it to do so.

# Connect to a Canon S100 camera, take a photo, show the photo to the user to confirm they want it, annotate it with date and weight of Robbie, generate an animated gif out of it and upload it to my website
# Home; Weight; S100; Camera; Automated; Automation; CHDK; chdkptp; Animated; Gif; 

$CHDKCLIPath = "c:\CHDKPTP" # The location you've extracted the CHDKPTP application to
$FromCameraPath = "c:\CHDKPTP\FromCamera" # This is where images will be saved and stored that are directly from the Camera
$ProcessingPath = "c:\CHDKPTP\Processing" # The converted GIFs will be stored here that will be used to generate the animated GIF
$OutputPath = "c:\CHDKPTP\Output" # The final animated GIF will bes stored here
$CMDOut = $null

# Check to see if the camera is visible on the USB bus (aka plugged in and turned on)
$CMDOut = & $CHDKCLIPath\chdkptp.exe -c -e"list" 2>&1
if($CMDOut -match 'no matching devices found') { write-warning "Cannot connect to camera.  Make sure it's powered on."; break}
start-sleep -Seconds 2

# We want to give myself a few seconds to get into position once the script has started before the picture is taken
try
{
    write-host "Taking picture in..." -ForegroundColor Green
    for($i = 5; $i -gt 0; $i--)
    {
        write-host $i -ForegroundColor Green
        Start-Sleep -Seconds 1
    }

# chdkptp sends LUA commands.  Rec puts it into shooting mode, shoot actually tkaes the picture and IMDL downloads an image.  We grab the last image taken and save it using the same name as stored on the camera
    write-host "Take picture and save it to the PC for processing" -ForegroundColor Green
    & $CHDKCLIPath\chdkptp.exe -c -e"rec" -e"shoot" -e"imdl -d=$FromCameraPath\`${name} -last=1"
    Start-sleep -Seconds 5
    
# I can probably do this better but the idea is we need to know both the full filename as well as the base filename without the extension.  I saved them into an object.  This approach is a bit clunky admitedly
    $LastFileObject = Get-ChildItem $FromCameraPath | sort LastWriteTime | select -last 1
    
    if(!(Test-path $FromCameraPath\$($LastFileObject.Name))) { Write-Warning "$FromCameraPath\$($LastFileObject.Name) not fonud.  Looks like it didn't download from the camera." ; break  } 
    
    # These dimensions were calculated by hand as to crop out everything in the frame (Which is fixed since the camera is fixed) other than the white wall for the background
    # The LAST two numbers represent the location you'll stop cropping from while the FIRST two numbers represent the window size from that position that you wish to capture
    convert -crop 511x790+432+248 $FromCameraPath\$($LastFileObject.Name) $FromCameraPath\$($LastFileObject.Name)

    # It turns out that in Windows 10, it appears there is no way to call the Windows Photo (Modern) App and pass it a photo to open.  I eventually gave up and just used Internet Explorer instead
    # The intent is to be able to review the image and approve it before it is added to the animated gif
    & 'C:\Program Files\internet explorer\iexplore.exe' $FromCameraPath\$($LastFileObject.Name)
    Start-Sleep -Seconds 5
}
catch { $_; break }

# Display a GUI prompt confirming if you want to save the picture shown on screen to the animated gif or not
$a = new-object -ComObject wscript.shell
$intAnswer = $a.popup("Save the picture and add it to the GIF animation?", 0,"Image Capture",4)

# 6 just happens to be what the "OK" button returns
if($intAnswer -eq 6) 
{ 
    # Calls a seperate script that does all the heavy lifting of connecting to Garmin Connect to get the last weight
    write-host "Connecting to Garmin Connect to grab last weight in value" -ForegroundColor Green
    $CurrentWeight = C:\SVNScripts\garminconnectweightreport\getlastweight.ps1

    $CurrentDate = (Get-Date -Format "MM/dd/yyyy HH:mm")

    write-host "Adding Current date to bottom right of image..." -ForegroundColor Green
    convert $FromCameraPath\$($LastFileObject.Name) -gravity SouthEast -fill Yellow -pointsize 36 -annotate +15+60 "$CurrentDate" $ProcessingPath\$($LastFileObject.Name)
    
    write-host "Adding Current weight to bottom right of image..." -ForegroundColor Green
    convert $ProcessingPath\$($LastFileObject.Name) -gravity SouthEast -fill Yellow -pointsize 36 -annotate +15+10 "$CurrentWeight lbs" $ProcessingPath\$($LastFileObject.Name)

    # We use the fuzz and dither options to try and shink the GIF files as much as possible as we'll ultimately intend to have 365 of them so we have to keep the individual file sizes down
    write-host "Converting image from JPG to GIF..." -ForegroundColor Green
    $GIFOutputFile = "$ProcessingPath\$($LastFileObject.BaseName)" + ".Gif"
    & convert $ProcessingPath\$($LastFileObject.Name) -coalesce -fuzz 2% +dither -layers optimize $GIFOutputFile

    Remove-Item $ProcessingPath\$($LastFileObject.Name) -Verbose

    # This creates the actual animated gif.  There is a 75ms delay between each image and it is set to loop forever
    write-host "Creating animation from existing images..." -ForegroundColor Green
    & C:\CHDKPTP\gifsicle.exe -d 75 --colors 256 --loop=0 -i C:\CHDKPTP\Processing\*.gif -o C:\CHDKPTP\Output\animtemp.gif
    
    # In an effort to save more space, the image is recompressed
    write-host "Reencoding final GIF to try and reduce file size..." -ForegroundColor Green
    & convert C:\CHDKPTP\Output\animtemp.gif -fuzz 10% +dither -layers optimize C:\CHDKPTP\Output\transform.gif

    Remove-item C:\CHDKPTP\Output\animtemp.gif

    # The file is uploaded to my website so I can view it from anywhere
    & c:\bin\winscp\winscp.com -script:C:\chdkptp\ftpdetails.txt
}

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.