Overlaying Text and Graphics on a Photo and Tweeting it – pt 5 Twitter App series
Today we’re going to take a photo, overlay some text and graphics on it and then tweet it. In this series, we’ve been building a Raspberry Pi Twitter app and we’re adding more to it. This is a great way to develop software. Add things one step at a time and don’t move on to the next part until it works well, and you understand it.
Why Would You Want To Do This?
Let me backtrack and explain why I wanted to do this in the first place. I have a weather station running on a Raspberry Pi. It logs the following data to Xively…
- Inside temperature
- Outside temperature
- Inside light level
- Outside light level
- Barometric pressure
When away from the house, I like to monitor what’s happening. I have security cameras for this, but last summer I used a Pi camera as well.
So, I thought it would be cool if I could grab the weather station data from Xively and overlay that on a photo of the garden, along with time and date, and then tweet the photo. It sounds hard, but it didn’t take long to find out how to do it.
And then I thought “What about overlaying a logo as well?” That didn’t take too long to work out either.
What I’m Not Covering
I’m not going to cover the specifics of how I get the data from Xively because…
- I’m using the obsolete COSM pachube interface (it still works, but it’s not the way forward).
- Whatever text you want to overlay, may come from a totally different place, so it wouldn’t be helpful for you
But I am going to cover the mechanics of doing the text and graphics overlay. So how do we do the overlays?
I had no idea how to overlay text on a photo, but I headed over to the Raspberry Pi forums and did a search. It didn’t take long to find a helpful thread.
It can all be done with an excellent package called ImageMagick. So let’s install it…
sudo apt-get update (update package list)
sudo apt-get install imagemagick (install program)
From the command line, this is how it works…
convert your_photo.jpg -pointsize 36 -fill white -annotate +40+728 'your overlay text' your_output_photo.jpg
There are a couple of traps though because, from within a program, it’s better to specify the full path to files and programs. So it becomes…
/usr/bin/convert /path/to/your_photo.jpg -pointsize 36 -fill white -annotate +40+728 'your overlay text' /path/to/your_output_photo.jpg
So What Does This Do?
It takes the photo your_photo.jpg and overlays the text your overlay text 40 pixels in from the left-hand edge and 728 pixels from the top edge. The text will be 36 points high and white. The resulting file is saved as your_output_photo.jpg, but you can use the same filename and path as the input file to overwrite the original.
This is incredibly powerful and – once you’ve got your head round it – not very hard. So the program does this step for each piece of data it wants to overlay. There is a way to change the font, but I had trouble with it, so stayed with the default font.
But I want logos too. :)
We use the same program as before, but different command arguments and parameters…
/usr/bin/convert /path/to/your_photo.jpg /home/pi/overlay.png -geometry +1+1 -composite path/to/your_modified_photo.jpg
This uses ‘convert’ to take your_photo.jpg and overlay the file overlay.png 1 pixel from the left-hand edge and 1 pixel down from the top edge. It puts the output in the file your_modified_photo.jpg
So, in my program, I ran this command twice. Once for each logo. It’s a good idea to make sure your logos are a suitable size. If they’re not, you can scale them, but if you’re doing the same thing over and over again, it’s more efficient to do that processing once and in advance.
So What About the Code Then?
Here is a slightly modified version of the code I used at the Cambridge Raspberry Jam last weekend to tweet photos of people who visited the HDMIPi stand.
#!/usr/bin/env python2.7 # tweetpic6BLOG.py by Alex Eames https://raspi.tv/?p=6004 # take a photo with the Pi camera, overlay some text # then overlay a small logo and tweet the final image import tweepy from time import sleep from subprocess import call from datetime import datetime import sys if len(sys.argv) >= 2: tweet_text = sys.argv else: tweet_text = "Photo from insert your location here" if len(tweet_text) > 110: print "tweet not sent. Too long. 110 chars Max. Try again." sys.exit() # Twitter Bits. Consumer keys and access tokens, used for OAuth consumer_key = 'copy your consumer key here' consumer_secret = 'copy your consumer secret here' access_token = 'copy your access token here' access_token_secret = 'copy your access token secret here' # OAuth process, using the keys and tokens auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) # Creation of the actual interface, using authentication api = tweepy.API(auth) def photo_tweet(): i = datetime.now() now = i.strftime('%Y%m%d-%H%M%S') tweettime = i.strftime('%Y/%m/%d %H:%M:%S') photo_name = now + '.jpg' cmd = 'raspistill -t 500 -w 1024 -h 768 -o /home/pi/' + photo_name print 'cmd ' +cmd print "about to take photo" call ([cmd], shell=True) print "photo taken" photo_path = '/home/pi/' + photo_name status = tweet_text + ' #optionalhashtag ' + tweettime # Add text overlay of data on the photo we just took print "about to set overlay_text variable" overlay_text = "/usr/bin/convert "+ photo_path + " -pointsize 36 -fill white -annotate +40+728 '" + tweettime + "' " overlay_text += " -pointsize 36 -fill white -annotate +40+630 'Your Text annotation here ' " + photo_path print "overlaying text" call ([overlay_text], shell=True) print "adding your logo" # you'll need a file called overlay.png in /home/pi overlay_text = '/usr/bin/convert '+ photo_path + ' /home/pi/overlay.png -geometry +1+1 -composite ' + photo_path call ([overlay_text], shell=True) print "added logo 1" print "tweeting photo" api.update_with_media(photo_path, status=status) print "photo tweeted, deleting .jpg" cmd = 'rm ' + photo_path call ([cmd], shell=True) print 'start of program' photo_tweet()
There is one major caveat. The spaces at the ends of lines 49 (“‘ “), 50 (‘ “) and 56 (composite ‘) are essential or the imagemagick convert command will fail.
A minor caveat is that I use the same path/filename for the input and output of all overlays, which means that the original photo is overwritten at each step.
Here’s an explanation of what the code does…
1 shebang line enables this script to be executed directly (if permissions correct)
2-4 and comments
5-9 import required libraries
11-15 use command line input as tweet text, or default set in 15
17-19 ensure tweet text is not too long
21-32 set up twitter interface
34-63 main function defined
35-38 deal with time stamping and photo file name
39-43 take the photo
44 set full photo file path
45 assemble the full tweet text
48-53 build and execute the ‘convert’ text overlay command
55-58 build and execute the ‘convert’ graphic overlay command
60-61 tweet the overlaid photo
62-64 delete the photo (comment out 64 if you want to keep it)
68 call the main function photo_tweet()
What Does the Output Look Like?
And here’s an embedded tweet from last weekend’s Cambridge Jam, where I used a very similar script to tweet photos from the event. This is myself and Ben Nuttall (although the weather data was still from my home-based weather station)…
Photo from #CamJam 8th Feb 2014 #CamJam 2014/02/08 16:11:08 pic.twitter.com/Pkm5mvoCzL
— RasP.iO (@RasPiO1) February 8, 2014
At the Cambridge Jam, I realised it would be nice to be able to view the photo afterwards on the HDMIPi screen that was attached to the Pi.
So I’ve implemented that for next week’s Oxford Jam (watch for the hashtag #OxJam on 18th Feb in the evening).
I think our work here with twitter and Python tweepy may be nearly done. I may discover more we can add, but for the time being I may well take a break. We’ll see how it goes. I hope you’ve enjoyed the series. Let us know how you get on and if you make a twitter app of your own.
I’ve created an index page of all the twitter app on the Raspberry Pi series here https://raspi.tv/raspitweets
And you can download the full set of Python scripts from my Github repo here
or, from Pi command line, use…
git clone https://github.com/raspitv/raspitweets
Or you could use the Image.Draw function within Python Image Library (PIL) to add text, then its nicely contained within the Python ecosystem.
I’ll have to have a look at that. I’m perfectly happy using imagemagick, but always looking for new ways (to me) of doing things.
Couple of minor points:
Depending on the background of your image, plain-white-text may not always be easily readable. http://www.imagemagick.org/Usage/annotating/#anno_on suggests a number of techniques for dealing with that.
Rather than getting confused about where spaces and quote marks are supposed to start and stop, you may find string formatting more useful? i.e. you can replace:
overlay_text = "/usr/bin/convert "+ photo_path + " -pointsize 36 -fill white -annotate +40+728 '" + tweettime + "' "
overlay_text += " -pointsize 36 -fill white -annotate +40+630 'Your Text annotation here ' " + photo_path
overlay_text = "/usr/bin/convert %s -pointsize 36 -fill white -annotate +40+728 '%s' -pointsize 36 -fill white -annotate +40+630 '%s' %s" % (photo_path, tweettime, "Your Text annotation here", photo_path)
which (depending on your preference) you may or may not find easier to read ;-)
(I haven’t checked, but maybe the duplicate -pointsize and -fill flags are redundant?)
The text on the images on http://www.interrose.co.uk/flowers/14_happy_roses.shtml was added with ImageMagick in exactly the same way too ;-) (except I was calling /usr/bin/convert from Perl, instead of from Python, and I wasn’t doing it on a Raspberry Pi)
Your minor points are always interesting. I use string formatting in print statements, but it never occurred to me to use it here. That’s a good idea. Bit less ‘clunky’ :)
Imagemagick is awesome. Was talking to Dave Akerman on Tuesday at the Oxford Jam. He said that’s what he uses on his balloon flights to process the photos.
Yeah, ImageMagick has lots and lots of options – it’s almost like a CLI version of GIMP ;-)
Great job and thank you for the post!
I am attempting to do something similar but could sure use some help.
I have image files in a folder with each file using a naming convention which utilizes the date and time the image was taken:
example “20140227-134726.jpg” -Taken Feb 27 2014 at 01:47:26 PM
I also have several .csv files which contain data I wish to overlay onto all the images I have taken.
For example, “Humidity.csv” contains:
Anybody have any clue how I could import the data from the CSV, match THAT to the closest timestamp of the image, then overlay the corresponding value onto an image?
I know it sounds easy, but for a non-programmer, it isn’t. ANY help would be appreciated.
As always in software development, the solution is to break it down into smaller sub-steps… :-)
Reading data from a CSV file: http://docs.python.org/2/library/csv.html
Reading filenames in a directory: http://docs.python.org/2/library/os.html#os.listdir
Converting a date in text format to a datetime object: http://docs.python.org/2/library/datetime.html#datetime.datetime.strptime
Subtracting one datetime object from another, to get a timedelta object: http://docs.python.org/2/library/datetime.html#datetime-objects
Comparing timedelta objects: http://docs.python.org/2/library/datetime.html#timedelta-objects
So to get the ‘closest’ timestamp, you simply need to find the two timestamps where the abs(datetime1 – datetime2) gives the smallest value.
And then you can just follow Alex’s instructions above to overlay the data onto your image :)
[…] setup allowed me to merge example code from Raspi.tv and example code from picamera to take a picture once every 5 minutes and uploaded it to […]
Python 3 support would be nice :)
I’m probably going to switch to Python 3 at some point within the next few months, but in order to do that, I will first have to learn the differences. I’m not sure if tweepy operates in Python 3, but I’ve heard that twython does.
I am using your script and it is running just fine, except at the end it takes a second photo.
Here is a quick way of adding the text using PIL. Note I think PIL itself is no longer maintained, so use pillow instead:
pip install pillow
from PIL import Image, ImageDraw, ImageFont
image = Image.open(photo_path)
draw = ImageDraw.Draw(image)
font = ImageFont.truetype(“DejaVuSansMono.ttf”, 20, encoding=”unic”)
draw.text( (40,728), tweettime, fill=”white”, font=font)