Thursday, 20 June 2013

Moment-Based De-skewing

I needed to discover the orientation of an object in a binary image then de-skew the image. Hough transform would not work for me cause I needed speed so I opted for the use of image moments. Using second order central moments, it is possible to calculate the angle that the major axis of the object forms with the horizontal axis. The algorithm used can determine this angle to within ±90 degrees. So let get started.

Code

import cv2
from math import atan, pi, ceil

img  =  cv2.imread('mask3.jpg', 0)
h,w = img.shape[:2]
cv2.imshow('original', img)
m = cv2.moments(img)

# You can read about Image Moments 
# Chapter 18 Shape analysis - Page 633 
# Digital Image Processing 4th Edition By William K. Pratt
# OR http://en.wikipedia.org/wiki/Image_moment

x = m['m10']/m['m00']
y = m['m01']/m['m00']
mu02 = m['mu02']
mu20 = m['mu20']
mu11 = m['mu11']

lambda1 = 0.5*( mu20 + mu02 ) + 0.5*( mu20**2 + mu02**2 - 2*mu20*mu02 + 4*mu11**2 )**0.5
lambda2 = 0.5*( mu20 + mu02 ) - 0.5*( mu20**2 + mu02**2 - 2*mu20*mu02 + 4*mu11**2 )**0.5 
lambda_m = max(lambda1, lambda2)

# Convert from radians to degrees
angle = atan((lambda_m - mu20)/mu11)*180/pi

print angle

# Create a rotation matrix and use it to de-skew the image.
center = tuple(map(int, (x, y)))
rotmat = cv2.getRotationMatrix2D(center, angle , 1)
rotatedImg = cv2.warpAffine(img, rotmat, (w, h), flags = cv2.INTER_CUBIC)       
   
cv2.imshow('final', rotatedImg)
cv2.waitKey()

Results

I would not be explaining the maths behind Image moments, if you care to know more check the reference in the code. Nothing explains code better than some tests! Here are a few test images and the de-skewed resulting images.

Original Image De-skewed Image Orientation (degree)
-89.99
-0.0
-39.03
46.95
3.57

Conclusion

As you can see the algorithm works pretty well, an anticlockwise skew returns a negative angle and a clockwise skew returns a positive angle. Be aware the results you get will vary based on the quality of the segmented mask. Low quality (see last test image) segmentation would change the centre of the mask and this would after the calculated angle. So depending on your case you might need some pre-processing before de-skewing. Please feel free to post questions in the comments. Happy Coding!