30

Detect angle and rotate image in python

I want to detect an angle (i.e, A) on the left hand side of figure (a) and rotate it in a correct one (i.e. figure b). This image is an answer sheet paper.

How can I do this in Python?

Martin Evans
  • 43,220
  • 16
  • 78
  • 90
Welcome789
  • 475
  • 2
  • 6
  • 11
  • 1
    Your question is broad, and the topic it belongs to is not part of standard python. I recommend you read about opencv. Also for the reputation you have should know that your question is off-topic in SO. – eyllanesc Oct 13 '17 at 14:19

1 Answers1

61

You could use OpenCV with HoughLines to detect lines in the image. The angle of each of the lines can be found from this:

import numpy as np
import cv2
import math
from scipy import ndimage

img_before = cv2.imread('rotate_me.png')

cv2.imshow("Before", img_before)    
key = cv2.waitKey(0)

img_gray = cv2.cvtColor(img_before, cv2.COLOR_BGR2GRAY)
img_edges = cv2.Canny(img_gray, 100, 100, apertureSize=3)
lines = cv2.HoughLinesP(img_edges, 1, math.pi / 180.0, 100, minLineLength=100, maxLineGap=5)

angles = []

for [[x1, y1, x2, y2]] in lines:
    cv2.line(img_before, (x1, y1), (x2, y2), (255, 0, 0), 3)
    angle = math.degrees(math.atan2(y2 - y1, x2 - x1))
    angles.append(angle)

cv2.imshow("Detected lines", img_before)    
key = cv2.waitKey(0)

median_angle = np.median(angles)
img_rotated = ndimage.rotate(img_before, median_angle)

print(f"Angle is {median_angle:.04f}")
cv2.imwrite('rotated.jpg', img_rotated)  

This would give you an output as:

rotated image

It shows the lines that were detected to rotate. The angle calculated is:

Angle is 3.9793

statistics.median() could also be used instead of the numpy version if you are using Python 3.4 or later.

Martin Evans
  • 43,220
  • 16
  • 78
  • 90
  • 2
    Thank you so much Martin Evans, it helps me a lot. I want to find the angle, and that is a good answer. – Welcome789 Oct 13 '17 at 15:13
  • is there a way we can change the background black colour with the same white colour after rotation ? thank you – DataDoctor Jan 06 '20 at 18:57
  • 5
    You could try and investigate the `cval` parameter for the [`rotate`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.rotate.html#scipy-ndimage-rotate) function. – Martin Evans Jan 06 '20 at 19:36
  • 1
    Shouldn't the for loop be `for [[x1, y1, x2, y2]] in lines:`? And btw, if you write `print(...)` it's python3 compatible ;) – myrdd Aug 29 '20 at 08:25
  • @MartinEvans Sorry to bother you Martin, I'm trying to solve the exact same problem using your code. But when it comes to OpenCV and image processing I'm completely off. If it possible for you can you please explain what might cause the angle to be calculated as zero? Thank you. – Ravexina Dec 10 '20 at 22:22
  • I suggest you first try with the image shown above to test it gives a similar result with your setup. Your image might have a much higher resolution and so would probably need different settings for `Canny()` and `HoughLinesP()`. A possible workaround would be to first resize your image. – Martin Evans Dec 11 '20 at 08:53
  • Thanks @MartinEvans. I've already tried poking around the values and resizing the image. I've posted my question [here](https://stackoverflow.com/questions/65256508/automatically-rotate-transform-picture-using-opencv). If it's possible for you, I would be thankful if you could check it out and give your opinion on it. Thanks. – Ravexina Dec 11 '20 at 18:17
  • For the image in your example the code is actually working but most of the lines are actually straight. My example uses a `median()` of all values which is why you are getting `0`. Do a `print(angles)` and it will show you all the angles found. You could then use different logic to pick the angle you need. – Martin Evans Dec 14 '20 at 21:28