Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove borders from image but keep text written on borders (preprocessing before OCR)

digit extraction

Having an image such as one above, I am able to crop it into four square boxes, remove the borders using OpenCV morphological operations (basic dilation, erosion) and get a result such as:

two

Which works great in most cases, but if someone writes over the line, this may get predicted as 7 instead of 2.

I am having trouble finding a solution that would recover the parts of the character written over the line while removing the borders. Images I have are already converted to grayscale so I can't distinguish written digits based on the color. What would be the best way to approach this problem?

like image 563
Laurynas Avatar asked Dec 23 '22 21:12

Laurynas


1 Answers

Here's a pipeline

  • Convert image to grayscale
  • Otsu's threshold to obtain a binary image
  • Remove vertical lines
  • Remove horizontal lines
  • Construct repair kernel and repair image
  • Invert image

After converting to grayscale, we Otsu's threshold

enter image description here

From here we remove vertical lines

enter image description here

Then remove horizontal lines

enter image description here

This leaves us with a gap in the characters, to fix this, we create a repair kernel to dilate the image

enter image description here

Next we bitwise-and with the thresholded image to maintain our character detail

enter image description here

The gap is still there but a little better. We perform morph close to close the gap

enter image description here

It's now closed but we lost character detail. We perform a final bitwise-and with the thresholded image to recover our detail

enter image description here

To get the desired result, we invert the image

enter image description here

import cv2

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

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(removed, [c], -1, (255,255,255), 15)

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(removed, [c], -1, (255,255,255), 5)

# Repair kernel
repair_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
removed = 255 - removed
dilate = cv2.dilate(removed, repair_kernel, iterations=5)
dilate = cv2.cvtColor(dilate, cv2.COLOR_BGR2GRAY)
pre_result = cv2.bitwise_and(dilate, thresh)

result = cv2.morphologyEx(pre_result, cv2.MORPH_CLOSE, repair_kernel, iterations=5)
final = cv2.bitwise_and(result, thresh)

invert_final = 255 - final

cv2.imshow('thresh', thresh)
cv2.imshow('removed', removed)
cv2.imshow('dilate', dilate)
cv2.imshow('pre_result', pre_result)
cv2.imshow('result', result)
cv2.imshow('final', final)
cv2.imshow('invert_final', invert_final)
cv2.waitKey()
like image 150
nathancy Avatar answered Jan 01 '23 17:01

nathancy