How to Rotate Images in Python Using a Horizon Detection Algorithm

February 22, 2017
by
· 4 min read

This article was originally published at Algorithimia’s website. The company was acquired by DataRobot in 2021. This article may not be entirely up-to-date or refer to products and offerings no longer in existence. Find out more about DataRobot MLOps here

When we look at an image, it’s fairly easy to detect the horizon line.

For computers, this task is somewhat more difficult: they need to understand the basic structure of the image, locate edges which might indicate a horizon, and pare out the edges which do not matter. Fortunately, Algorithmia boils this all down to a single API call: just send your image to deep horizon, an algorithm for horizon detection, and it tells you where the horizon line is.

Let’s see how we can use this tool, in combination with Pillow (a fork of the Python Image Library), to automatically recompose any image so it’s level.

Step 1: Call the Deep Horizon Microservice

It only takes a couple lines of code to call any of our algorithms. Let’s wrap it up in a function:

import Algorithmia
def find_horizon(infile):
"""Find the horizon line on an image"""
algo = client.algo('ukyvision/deephorizon/0.1.0')
image = base64.b64encode(open(infile, "rb").read())
return algo.pipe({'image':'data:image/jpg;base64,'+image}).result

This simply calls the version 0.1.0 of the ukyvision/deephorizon algorithm, passes it an image (after reading the file and base64 encoding the contents), and gets back the results, which will have the values “left” and “right”. Each of these is a pair of [x,y]  image space coordinates, which are the endpoints of the horizon line.

If we had omitted the version number and simply called client.algo(“ukyvision/deephorizon”), it would run the most recent version of the algorithm. If the author ever changes the API, this could break, so it is better to always include a specific version number in your Algorithmia calls.

Also note that we’re prefixing the image content with data:image/jpg;base64, to indicate that it is a base64-encoded jpg. This prefix would be slightly different for other filetypes / encodings. Deep Horizon also accepts image URLs or data URIs, which are especially useful for large files.

Step 2: Determine Image Rotation

Now that we know where the horizon line’s endpoints are, we need to calculate how many degrees of rotation will be needed to make the image level.

import math
def calculate_rotation(coords):
"""Transform coordinates {left: [x1,y1], right: [x2,y2]} to rotation, in degrees """
(x1, y1) = coords['left']
(x2, y2) = coords['right']
slope = (y2-y1)/(x2-x1)
return math.degrees(math.atan(slope))

From our high school math classes, we may remember that the slope of a line, in degrees, is the inverse-tangent of the rise divided by the run.

In other words, if we have coordinates [x1, y1] and [x2, y2] of a line, then the slope is tan-1 of (y2-y1) / (x2-x1).

Step 3: Rotate the Image

Pillow provides a one line command to rotate an image. We’ll add the library using pip:

pip install Pillow

from PIL import Image
def rotate_image(infile, outfile, degrees, crop):
"""Rotate an image by a number of degrees, crop if desired, and save to outfile"""
Image.open(infile).rotate(degrees, expand=not crop, resample=Image.BILINEAR).save(outfile)

Then, we can create a utility function to rotate an image and save it as a new file. Pillow will also automatically crop the image, unless we disable this with the parameter ‘expand’. Lastly, we’ll get better image quality if we add bilinear resampling.

Step 5: Putting It All Together

Once we have these three functions, straightening an image is as simple as finding the horizon, calculating the slope, and then rotating by the negative of that slope:

# get your API key at algorithmia.com/user#credentials
client = Algorithmia.client('your_api_key')
infile = "/some/filename.jpg"
outfile = "/some/outputfile.jpg"
line = find_horizon(infile)
rotation = calculate_rotation(line)
rotate_image(infile, outfile, -rotation, True)

…and that’s all we need to do. We now have a script, we can read any JPG image, detect the horizon line, and straighten it out for you while automatically cropping and resampling the output image.

You could easily modify it to handle other filetypes, turn off cropping or resampling, or pull files from the web.

Tools used:

Here’s the whole script, ready for you to cut-and-paste, or grab it (and other fun examples) from Algorithmia’s sample-apps repository on Github

import Algorithmia
import base64
import math
from PIL import Image
def find_horizon(infile):
"""Find the horizon line on an image"""
algo = client.algo('ukyvision/deephorizon/0.1.0')
image = base64.b64encode(open(infile, "rb").read())
return algo.pipe({'image':'data:image/jpg;base64,'+image}).result
def calculate_rotation(coords):
"""Transform coordinates {left: [x1,y1], right: [x2,y2]} to rotation, in degrees """
(x1, y1) = coords['left']
(x2, y2) = coords['right']
slope = (y2-y1)/(x2-x1)
return math.degrees(math.atan(slope))
def rotate_image(infile, outfile, degrees, crop):
"""Rotate an image by a number of degrees, crop if desired, and save to outfile"""
Image.open(infile).rotate(degrees, expand=not crop, resample=Image.BILINEAR).save(outfile)
# get your API key at algorithmia.com/user#credentials
client = Algorithmia.client('your_api_key')
infile = "/some/filename.jpg"
outfile = "/some/outputfile.jpg"
line = find_horizon(infile)
rotation = calculate_rotation(line)
rotate_image(infile, outfile, -rotation, True)

Demo
See DataRobot MLOps in Action
Request a demo
About the author
DataRobot

Value-Driven AI

DataRobot is the leader in Value-Driven AI – a unique and collaborative approach to AI that combines our open AI platform, deep AI expertise and broad use-case implementation to improve how customers run, grow and optimize their business. The DataRobot AI Platform is the only complete AI lifecycle platform that interoperates with your existing investments in data, applications and business processes, and can be deployed on-prem or in any cloud environment. DataRobot and our partners have a decade of world-class AI expertise collaborating with AI teams (data scientists, business and IT), removing common blockers and developing best practices to successfully navigate projects that result in faster time to value, increased revenue and reduced costs. DataRobot customers include 40% of the Fortune 50, 8 of top 10 US banks, 7 of the top 10 pharmaceutical companies, 7 of the top 10 telcos, 5 of top 10 global manufacturers.

Meet DataRobot
  • Listen to the blog
     
  • Share this post
    Subscribe to DataRobot Blog
    Newsletter Subscription
    Subscribe to our Blog