How to rotate images taken on Apple devices and load them into Python

Peter Fox
Systems AI
Published in
5 min readMay 28, 2020

--

A small team of IBMers in the AI team at South Bank are taking images on a variety of devices. The mission: to train an action recognition model using deep learning to recognise British Sign Language. But all was not to go to plan on our first task of the project.

Data cleaning is a notorious problem in data science. Once we had a decent amount of images in our Sign Language data set (which in itself is difficult to put a number on, given our relative inexperience with practical deep learning) we could begin to consider how we would build our deep learning model. One of the first problems we encountered was when we uploaded our data set onto Visual Insights and noticed (by good old fashioned inspection) that some of the images were disorientated — in particular they were either rotated by 90, 180 or 270 degrees.

We clearly couldn’t use the images as they were to train our model, since the hand signs were rotated too. It took some time, thought and a jolly good bit of Python to get to the bottom of what was causing this unwanted rotation. What we realised was that some of the devices we used to take the photos were saving the images in a different format that depended on both a) the device used, and b) the orientation of the device when the individual photo was taken. In particular it was the associated json file for each image that was inconsistent. For example, an image taken on an Apple mobile device in landscape might be orientated differently in Visual Insights when compared to an image taken on a HP desktop device in portrait. In order to circumvent this problem we wrote a bespoke Jupyter notebook to perform the necessary re-orientation. All the Python code you need is below (don’t forget to change the names of the files and directories to reflect your own).

from PIL import Image
from PIL import ExifTags
import os, sys
input_dir = “\\Images_raw”
output_dir = “resized_images”
full_path_to_input_dir = “/Users/PeterFox/Documents/ASL/ASL_Images_raw/”
os.chdir(‘Images_raw’)
dir = os.getcwd()
alphabet = map(chr, range(65, 90))def create_output_folder(letter):
if not os.path.exists(os.path.join(dir,output_dir,letter)):
os.mkdir(os.path.join(dir,output_dir,letter))
def reorientateImage(infile, output_dir, letter):
outfile = os.path.splitext(infile)[0] + “_reorientated”
extension = os.path.splitext(infile)[1]
if (extension == “.jpg”) or (extension == “.JPG”) or (extension == “.jpeg”) or (extension == “.JPEG”): if infile != outfile: try :
im = Image.open(infile)
exif = im._getexif()
exif = dict(exif.items())

if 271 in exif:
if exif[271] == ‘Apple’:
if exif[274] == 3:
im=im.rotate(180, expand=True)
elif exif[274] == 8:
im = im.rotate(90, expand=True)
elif exif[274] == 6:
im = im.rotate(270, expand=True)
else:
pass;
absolute_name_with_path = full_path_to_input_dir + output_dir + “/” + letter + ‘correct_orientation’ + outfile + “.JPEG”
im.save(absolute_name_with_path,”JPEG”)
except IOError:
print (“cannot reduce image for “, infile)
def change_folder_and_perform_image_reorientation_if_required():
for letter in alphabet:
#if not os.path.exists(os.path.join(dir,output_dir,letter)):
create_output_folder(letter)
os.chdir(dir + “\\” + letter)
for file in os.listdir(“.”):
reorientateImage(file, output_dir, letter + “/”)
create_output_folder(“”)change_folder_and_perform_image_reorientation_if_required()

As you can see, first of all we need to load a couple of libraries we will need:

from PIL import Image
from PIL import ExifTags
import os, sys

Then to be organised we set up our file structure which we will need later in the code:

input_dir = “\\Images_raw”
output_dir = “resized_images”
full_path_to_input_dir = “/Users/PeterFox/Documents/ASL/ASL_Images_raw/”

You should amend these to reflect your own. A description for each is as follows:

  • input_dir : This is the name of the folder that contains our original images. Please note that this folder should already contain within itself 25 separate folders, one each for each letter of the alphabet (within which are located the image files themselves) and excluding the letter Z since we do not wish to identify it (this is because it is a motion for which the beginning and endpoints are not exclusively Z-like). It ought to commence with two backslashes.
  • ouput_dir : This is the name of the output folder to be produced. output_dir will be located within input_dir once the code has been successfully run. Please note we don’t need to include any backslashes here.
  • full_path_to_input_dir : Self-explanatory. Note a forward slash is also needed on the end (and at the start!) and the path includes the input directory too.
  • dir : This should be the full path to input_dir. Hence please note that we must be located WITHIN input_dir before we execute any of the subsequent code.

Oh and in case you are wondering why we have to declare the path to the input directory twice, there is a discrepency in format between the two.

Then we need to check we are within input_dir:

os.chdir(‘Images_raw’)
dir = os.getcwd()

Great! Now let’s do some juicy coding :) It will be useful to have a list of all the (capitalised) alphabet characters, for when we wish to re-create our folder structure. For this we can simply do the following (excluding Z):

alphabet = map(chr, range(65, 90))

Now let’s make a function called create_output_folder whose purpose in life is, like all well-written functions, self-evident.

def create_output_folder(letter):
if not os.path.exists(os.path.join(dir,output_dir,letter)):
os.mkdir(os.path.join(dir,output_dir,letter))

The following function takes an input image, performs a re-orientation if it is required and then saves the image (rotated or not) into its letter sub-folder. Please note the image MUST currently have extension .jpg, .JPG, .jpeg or .JPEG. Although I should think the code could be amended to take other image types such as png if so required.

def reorientateImage(infile, output_dir, letter):
outfile = os.path.splitext(infile)[0] + “_reorientated”
extension = os.path.splitext(infile)[1]
if (extension == “.jpg”) or (extension == “.JPG”) or (extension == “.jpeg”) or (extension == “.JPEG”):if infile != outfile:try :
im = Image.open(infile)
exif = im._getexif()
exif = dict(exif.items())

if 271 in exif:
if exif[271] == ‘Apple’:
if exif[274] == 3:
im=im.rotate(180, expand=True)
elif exif[274] == 8:
im = im.rotate(90, expand=True)
elif exif[274] == 6:
im = im.rotate(270, expand=True)
else:
pass;
absolute_name_with_path = full_path_to_input_dir + output_dir + “/” + letter + ‘correct_orientation’ + outfile + “.JPEG”
im.save(absolute_name_with_path,”JPEG”)
except IOError:
print (“cannot reduce image for “, infile)

And let’s use both these functions to create our new directory structure and do our image re-orientation.

def change_folder_and_perform_image_reorientation_if_required():
for letter in alphabet:
#if not os.path.exists(os.path.join(dir,output_dir,letter)):
create_output_folder(letter)
os.chdir(dir + “\\” + letter)
for file in os.listdir(“.”):
reorientateImage(file, output_dir, letter + “/”)

Now all that remains for us to do is call our function, right?! Almost — but first we should use our create_output_folder function to produce our output directory:

create_output_folder(“”)

Now we are ready to call our uber-function…

change_folder_and_perform_image_reorientation_if_required()

And that’s it! This code could be easily amended to do some image re-sizing if required.There you go :)

Let us know how you get on by leaving a comment below, and feel free to share if you found this post helpful.

--

--