Hi there. In this blog post, I'll be addressing a need-based problem. My problem is adding labels or any kind of text to images in a directory in a bulk way. For example a copyright note or an address information. I think, this can be solved by creating a stencil in GIMP. But what if the text to be added is not a constant? My actual problem was adding sequence numbers to images. Consequtive numbers starting from one in the corner of each photo. In this case, a stencil wouldn't be a solution. And even if it can be done by editing each and every image with GIMP, it won't be very practical for several hundred images. Most practical solution is writing a simple script for this. By the way, I could have done this sequencing with their filenames, but I didn't want to touch then, because I also want to preserve the timestamp of the file. And let's assume, I want to use these photos on a web page, where their filenames won't be visible at first glance. Finally, with such a script, you can generate time stamps, similar like the cameras from 90s.
When I talk about scripting, bash comes to (my) mind very first, however it's unfortunately not the best tool for this job. AFAIK, there is no image library usable with bash. Of course, nothing is impossible. It can be done, but as you don't use a hammer to knock down a wall, when you can have a sledgehammer, choosing the appropriate tools for the job is the first step of all solutions. Python lovers may get angry for that, but python, the second best scripting language after bash, has a library for exactly this purpose, which is called Python Imaging Library (PIL). I used a fork of PIL called Pillow in my script. This library is easily imported with pip install pillow command.
I again uploaded the code to my github account the keep the article short. It's a small script, with just some tricks in it. First of all, like any other python script, there are import statements at the very beginning. The Image module of the library contains image related functions. ImageDraw contains simple 2D image effects, which is needed to rotate the image in my script and finally ImageFont for fonts and other text effects.
Exif Data and Orientation
The first challenge of this project is that the images cannot be viewed on the computer as easily as we see it on the phone. Let's take the image below, that I took with my cell phone, as an example:
>>> img = Image.open("image.jpg")
>>> img.show()
it appears horizontally.
and when I open it with GIMP, a strange dialog box says that the image contains "Exif orienation data" and asks if I want to rotate it. But why?
While holding a cell phone horizontally* and taking a photo, it doesn't actually rotate the photo. When I open such photos, taken with a cell phone or a digital camera, in python, those which are not taken vertically, are shown on the screen with the same orientation as they're taken. The camera saves the orientation info (thanks to their gravity sensors) inside the picture, and rotates them while viewing. If the orientation was not saved, we would have to turn the phone to the exact orientation, at which it was originally taken, each time.
*: The default position of some cameras are horizontal, but some are vertical.
From the above statement, it's clear that an image doesn't only consist of pixel data. There is a field called Exif, where the metadata of an image is stored and today, all image formats as well as cameras support Exif. There is a table on Wikipedia about the data stored in Exif. Typical fields are the manufacturer and the model of the camera, image orientation, the time and date the photo was taken, image resolution etc. For example, saving the timestamp of the photo allows you to find out when the photo was actually taken and to sort it by date, even if the file is renamed afterwards. On the other hand, some phones put the coordinates there from phones GPS, and reveal where the photo was taken and some platforms can then automatically tag the location of the photo, when it is shared. These are occasions, that will send shivers down the spine of those who is sensitive to the privacy of personal data. According to legend, Ukrainians asked Russian soldiers online for their photos, and used the coordinate data of this photos to launch attacks.
In Linux, a tool called exiftool can be used to review this information (exiftool -list <filename>), be manipulated or wiped completely (exiftool -all= <filename>). For the example image above, there is a difference of around 77 KB between the original image and the image with all Exif data wiped.
After a long (and unnecessary) explanation about Exif, let's go back to the image orientation. In Pillow, there is an Image.getexif() function [1], to read and parse Exif data. When I print the output of this function to screen (22., 23. and 24. lines, commented out), I can see the orientation info of .jpg files in the directory. As mentioned in [1], the values 2, 7, 4 and 5 were not in my images. Likewise 8, therefore I didn't implement 8 in my code either, but it's easy. For the value 1, I used "else" in the if structure (line 31), so for 8 and all other values, the image is not rotated.
For rotation, Pillow has Image.rotate() function [2]. Using that, I rotated the image 90 degrees for Orientation=3, and 270 degrees Orientation=6. I have to open a parenthesis here for the line 27: If there is no image orientation field in Exif, or if there is no Exif information in the image at all, the code will throw an error. For this reason, the best practice says, better check the return value of getexif() function first and then rotate, if there is no error. Since all my images have Exif, I did not have any problem and kicked the can down the road. It doesn't mean that you also won't have any issues.
So far, I've corrected the image orientation. I used [3] to solve the main problem. There, it is demonstrated how to add text in a simple way, I only adjusted the parameters for my system. In line 35, an ImageDraw object is created to add text. The next line creates a font object with ImageFont.truetype() function. As the font used in [3] isn't on my machine (and as I don't check the return value of the function for errors), I chose a font among my fonts under /usr/share/fonts/ . The second parameter is font size. I found its value by trial and error. My photos were relatively large (8 MP), so 128 could generate a barely visible caption. In the next line, at the given coordinate of the image (25, 25 - top left), I added the sequence number "sirano" with the font I created at the previous line with red color. At this step, the date and time of the photo could be added to the image automatically, either from its filename or from its Exif data. An example of an enumerated photo is below:
Not: Alternatively, it's possible to do the same thing in OpenCV with cv2.putText() function, but I'm keeping this for another post.
[1]: https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
[2]: https://note.nkmk.me/en/python-pillow-rotate/
[3]: https://www.geeksforgeeks.org/python/adding-text-on-image-using-python-pil/
No comments:
Post a Comment