EDIT: A better approach might be to install the tesseocr package instead since it works with the most updated Tesseract version.
Conda: conda install -c conda-forge tesserocr
from tesserocr import PyTessBaseAPI, OEM, PSM
def get_angles2(img):
with PyTessBaseAPI( psm=PSM.OSD_ONLY, lang="osd", oem=OEM.TESSERACT_LSTM_COMBINED ) as api:
api.SetImage(img)
os = api.DetectOrientationScript()
if os['orient_deg'] == 0:
return 0
elif os['orient_deg'] > 90:
return 360-os['orient_deg']
else:
return -os['orient_deg']
ORIGINAL
My answer is based on computing the angle between the lines generated by a Hough Transform because nothing else worked for my dataset. This is a fast approach that turned out to work well in practice.
This prerequisite to this function is grayscaling, binarizing, and color inversion.
import cv2
img = cv2.imread('test0.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
img = cv2.bitwise_not(img)
After this, you can run the function below and get all the angles for all the though lines detected. Please do tune the threshold parameter (currently at 300) as specified in the OpenCV documentation: Accumulator threshold parameter. Only those lines are returned that get enough votes ( >threshold ). For more information on calculating the angle on the (x,y) coordinates, refer to this Stack Overflow.
import cv2
import numpy as np
def get_angles(img):
edges = cv2.Canny(img, 50, 150, apertureSize = 3)
lines = cv2.HoughLines(edges, 1, np.pi/180, threshold=300)
angles = []
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
radians = np.arctan2(y2-y1, x2-x1)
degrees = np.degrees(radians)
angles.append(degrees)
return angles
After running this function, you will get a long list of angles from the Hough Transformation. From an image that SHOULD NOT be rotated:
[-90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -0.974421553508672, -0.974421553508672, -0.974421553508672, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.9749091578796124, 0.9749091578796124, 0.9749091578796124, 0.9749091578796124, 1.0030752389838637, 1.0030752389838637, 3.9855957480807316, 3.9875880958503185]
An image that SHOULD be rotated:
[-90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -90.0, -88.99692476101613, -88.99692476101613, -88.99692476101613, -88.99692476101613, -88.99692476101613, -88.99692476101613, -88.99692476101613, -88.99692476101613, -88.99692476101613, -88.99642282400909, -88.99642282400909, -88.02210297626898, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99346106671473, -87.99245711203707, -87.99245711203707, -87.99245711203707, -87.99245711203707, -86.99022425882445, -86.99022425882445, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.98871912968818, -86.01440425191927, -86.01440425191927, -86.01440425191927, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -86.01241190414969, -85.00791883390836, -85.00791883390836, -85.00791883390836, -85.00791883390836, -85.00542418989113, -85.00542418989113, -0.974421553508672, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.02866221847606629, 0.9749091578796124, 85.9838177634312, 86.98871912968818, 86.98871912968818, 86.98871912968818, 86.99022425882445, 87.99346106671473, 87.99346106671473, 87.99346106671473, 87.99346106671473, 87.99346106671473, 87.99346106671473, 87.99346106671473, 87.99346106671473, 88.99692476101613, 88.99692476101613, 88.99692476101613, 88.99692476101613, 88.99692476101613, 88.99692476101613, 88.99692476101613, 88.99692476101613]
This is where I will leave it up to you with a few options on which angle to choose for rotation. Option 3 should work great for the arrays I presented above, but please do tune it to your case:
- Use the median angle to rotate the image
- Average of the first, middle, and last angle
- Find the average of the first 10 and last 10 values. If the difference is too large, the image does not need rotation. However, if they are close, one could find the average of the 20 values (from the first 10 and last 10) and use that as the value for rotation.
Here is the list of guides that I tested and which did not work (for me). I believe most of these packages do not work well if financial data (like equations or tables) are included. However, if you only have text in an image, these guides could work for you:
- This first one gave me -90 degrees for both an image that needed to be rotated and an image that did not need to be rotated. https://becominghuman.ai/how-to-automatically-deskew-straighten-a-text-image-using-opencv-a0c30aed83df
- This gave a lot of errors in Python 3. After fixing the code, it turned out not to work at all. https://mzucker.github.io/2016/08/15/page-dewarping.html
- You can add Mousam Singh's example above. This did not work because Tesseract throws an error. Furthermore, I am not sure it is too wise to run Tesseract twice.
- This package did not work for me. It was too simple of an approach. https://github.com/sbrunner/deskew
- A noteworthy mention that I did not get to try out was Leptonica, which is used by Tesseract, OpenCV and other major packages. It requires managing a dependency that I did not want to deal with, however, it could work for you if you already have some experience with C. https://tpgit.github.io/Leptonica/skew_8c.html