4

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('https://i.ibb.co/3WCsVBc/test.jpg')
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')
#ax.add_patch(rect)
plt.show()

print(gray.shape)
(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.

nathancy
  • 42,661
  • 14
  • 115
  • 137
user_12
  • 1,778
  • 7
  • 31
  • 72
  • https://stackoverflow.com/a/35014061/5008845 – Miki Jan 09 '20 at 09:54
  • @Miki Thank you :) it didn't de-skewed my image and also it didn't contain any info on how to get the new bounding box coordinates after de-skewing the image. i tried the python code based on your approach given in second answer. – user_12 Jan 09 '20 at 12:17
  • Does this answer your question? [Split text lines in scanned document](https://stackoverflow.com/questions/34981144/split-text-lines-in-scanned-document) – stovfl Jan 09 '20 at 12:40
  • @stovfl That question only asks about de-skewing the text image but In my question I also need a way to obtain de-skewed bounding box coordinates as well. Please check the bottom of my question. – user_12 Jan 09 '20 at 13:31
  • @user_12 ***You have changed your questions title***. Does not, the shown `green` boxes, have the coordinates? – stovfl Jan 09 '20 at 14:04
  • @stovfl I have changed the question title because it shows my question is a duplicate but in the body of my question I have asked how to obtain de-skewed bounding box coordinates and I didn't change anything in the body – user_12 Jan 09 '20 at 14:06
  • For my original image I have multiple bounding boxes for each text line but when I de-skew it by rotation the old given bounding boxes doesn't work. So I am also looking for a way to modify those old bounding boxes to support the new de-skewed image. – user_12 Jan 09 '20 at 14:08
  • @user_12 ***"when I de-skew ... the old given bounding boxes doesn't work"***: What's the problem to renew the **boxes** as you tell you are able to get it before? – stovfl Jan 09 '20 at 14:11
  • The old bounding boxes were given as annotations for my dataset but after de-skewing (i.e the image rotates) and if we plot it the bounding boxes it won't fit right? so I want to know what I can do so that it works with the new de-skewed images. – user_12 Jan 09 '20 at 14:16
  • @user_12 Are you looking for something like this: [robustly-crop-rotated-bounding-box-on-photos](https://stackoverflow.com/questions/45288026/robustly-crop-rotated-bounding-box-on-photos) and [extracting-selected-text-by-bounding-box-from-an-image](https://stackoverflow.com/questions/56442156/extracting-selected-text-by-bounding-box-from-an-image) – stovfl Jan 09 '20 at 14:23

1 Answers1

9

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

Code

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)
        scores.append(score)

    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, \
            borderMode=cv2.BORDER_REPLICATE)

    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)
    cv2.waitKey()

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.

nathancy
  • 42,661
  • 14
  • 115
  • 137
  • thank you so much :) ...there's one more thing that I've asked in my question for the original image I have also bounding box coordinates for the title "YONG TAT HARDWARE TRADING" it's in the form of `[x1, y1, x2, y2, x3, y3, x4, y4] (i.e four vertices of a rectangle)` and when we correct_skew and rotate the image how can I also modify my bounding box coordinates to support my new cropped image? This was my main problem. – user_12 Jan 10 '20 at 18:19
  • can you explain what you mean by finding a new bounding box on rotated image? do you mean we have to annotate the image again? – user_12 Jan 10 '20 at 21:38
  • I'm just curious is it possible to achieve what I'm looking for? It's just if it is then should I just ask another question so other might answer it. – user_12 Jan 10 '20 at 21:49
  • 1
    Yes, the way I would do it would annotate the image again. I think its possible to achieve what you want using some rotation matrix but you should open another question for that task – nathancy Jan 10 '20 at 21:53