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!