How to de-skew a text image and retrieve the new bounding box of that image Python OpenCV?


Here’s a receipt image that I’ve got and I’ve plotted it using matplotlib and If you see the image the text in it is not straight. How can I de-skew and fix it?

from skimage import io
import cv2

# x1, y1, x2, y2, x3, y3, x4, y4
bbox_coords = [[20, 68], [336, 68], [336, 100], [20, 100]]

image = io.imread('')
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

fig, ax = plt.subplots(figsize=(20, 20))
ax.imshow(gray, cmap='Greys_r')

# for plotting bounding box uncomment the two lines below
#rect = Polygon(bbox_coords, fill=False, linewidth=1, edgecolor='r')

(847, 486)

receipt image

I think if we want to de-skew first we have to find the edges, so I tried to find the edges using canny algorithm and then get contours like below.

from skimage import filters, feature, measure

def edge_detector(image):
    image = filters.gaussian(image, 2, mode='reflect')
    edges = feature.canny(image)
    contours = measure.find_contours(edges, 0.8)
    return edges, contours

fig, ax = plt.subplots(figsize=(20, 20))

ax.imshow(gray, cmap='Greys_r'); 
gray_image, contours = edge_detector(gray)

for n, contour in enumerate(contours):
    ax.plot(contour[:, 1], contour[:, 0], linewidth=2)

The edges that I’ve got from above code is the edges of each text but that is not what I needed. I need to get edges of receipt right?

Also I need a way to get the new bounding box coordinates after de-skewing the image (i.e straightening the image)?

If anyone has worked on similar problem please help me out? Thanks.


Here’s a modified implementation of the Projection Profile Method to correct skewed images as described in Projection profile based skew estimation algorithm for JBIG
compressed images
. After obtaining a binary image, the idea is to rotate the image at various angles and generate a histogram of pixels in each iteration. To determine the skew angle, we compare the maximum difference between peaks and using this skew angle, rotate the image to correct the skew. The amount of peaks to determine can be adjusted by the delta value, the lower the delta, the more peaks will be checked with the tradeoff that the process will take longer.

Before -> After

Skew angle: -2


import cv2
import numpy as np
from scipy.ndimage import interpolation as inter

def correct_skew(image, delta=1, limit=5):
    def determine_score(arr, angle):
        data = inter.rotate(arr, angle, reshape=False, order=0)
        histogram = np.sum(data, axis=1, dtype=float)
        score = np.sum((histogram[1:] - histogram[:-1]) ** 2, dtype=float)
        return histogram, score

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] 

    scores = []
    angles = np.arange(-limit, limit + delta, delta)
    for angle in angles:
        histogram, score = determine_score(thresh, angle)

    best_angle = angles[scores.index(max(scores))]

    (h, w) = image.shape[:2]
    center = (w // 2, h // 2)
    M = cv2.getRotationMatrix2D(center, best_angle, 1.0)
    corrected = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, \

    return best_angle, corrected

if __name__ == '__main__':
    image = cv2.imread('1.jpg')
    angle, corrected = correct_skew(image)
    print('Skew angle:', angle)
    cv2.imshow('corrected', corrected)

Note: You may have to adjust the delta or limit values depending on the image. The delta value controls iteration step, it will iterate up until the limit which controls the maximum angle. This method is straightforward by iteratively checking each angle + delta and currently only works to correct skew in the range of +/- 5 degrees. If you need to correct at a larger angle, adjust the limit value. For another approach to handle skew, take a look at rotate skewed image to upright position for an alternative method.

Answered By – nathancy

Answer Checked By – Pedro (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.