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

  1. import cv2
  2. from math import atan, pi, ceil
  3.  
  4. img = cv2.imread('mask3.jpg', 0)
  5. h,w = img.shape[:2]
  6. cv2.imshow('original', img)
  7. m = cv2.moments(img)
  8.  
  9. # You can read about Image Moments
  10. # Chapter 18 Shape analysis - Page 633
  11. # Digital Image Processing 4th Edition By William K. Pratt
  12. # OR http://en.wikipedia.org/wiki/Image_moment
  13.  
  14. x = m['m10']/m['m00']
  15. y = m['m01']/m['m00']
  16. mu02 = m['mu02']
  17. mu20 = m['mu20']
  18. mu11 = m['mu11']
  19.  
  20. lambda1 = 0.5*( mu20 + mu02 ) + 0.5*( mu20**2 + mu02**2 - 2*mu20*mu02 + 4*mu11**2 )**0.5
  21. lambda2 = 0.5*( mu20 + mu02 ) - 0.5*( mu20**2 + mu02**2 - 2*mu20*mu02 + 4*mu11**2 )**0.5
  22. lambda_m = max(lambda1, lambda2)
  23.  
  24. # Convert from radians to degrees
  25. angle = atan((lambda_m - mu20)/mu11)*180/pi
  26.  
  27. print angle
  28.  
  29. # Create a rotation matrix and use it to de-skew the image.
  30. center = tuple(map(int, (x, y)))
  31. rotmat = cv2.getRotationMatrix2D(center, angle , 1)
  32. rotatedImg = cv2.warpAffine(img, rotmat, (w, h), flags = cv2.INTER_CUBIC)
  33. cv2.imshow('final', rotatedImg)
  34. 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!