Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting text from inside a circular border

I'm trying to develop a script using Python and OpenCV to detect some highlighted regions on a scanned instrumentation diagram and output text using Tesseract's OCR function. My workflow is first to detect the general vicinity of the region of interest, and then apply processing steps to remove everything aside from the blocks of text (lines, borders, noise). The processed image is then feed into Tesseract's OCR engine.

This workflow is works on about half of the images, but fails on the rest due to the text touching the borders. I'll show some examples of what I mean below:

Step 1: Find regions of interest by creating a mask using InRange with the color range of the highlighter.

Step 2: Contour regions of interest, crop and save to file.

--- Referenced code begins here ---

Step 3: Threshold image and apply Canny Edge Detection

Step 4: Contour the edges and filter them into circular shape using cv2.approxPolyDP and looking at ones with vertices greater than 8. Taking the first or second largest contour usually corresponds to the inner edge.

Step 5: Using masks and bitwise operations, everything inside contour is transferred to a white background image. Dilation and erosion is applied to de-noise the image and create the final image that gets fed into the OCR engine.

import cv2
import numpy as np 
import pytesseract 
pytesseract.pytesseract.tesseract_cmd = 'C:/Program Files (x86)/Tesseract-OCR/tesseract'

d_path = "Test images\\"

img_name = "cropped_12.jpg"

img = cv2.imread(d_path + img_name)  # Reads the image

## Resize image before calculating contour 
height, width = img.shape[:2]
img = cv2.resize(img,(2*width,2*height),interpolation = cv2.INTER_CUBIC)  

img_orig = img.copy()           # Makes copy of original image  

img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)    # Convert to grayscale

#  Apply threshold to get binary image and write to file
_, img = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)


# Edge detection 
edges = cv2.Canny(img,100,200)

# Find contours of mask threshold 
_, contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Find contours associated w/ polygons with 8 sides or more 
cnt_list = []
area_list = [cv2.contourArea(c) for c in contours]
for j in contours:
    poly_pts = cv2.approxPolyDP(j,0.01*cv2.arcLength(j,True),True)
    area = cv2.contourArea(j)
    if (len(poly_pts) > 8) & (area == max(area_list)):
        cnt_list.append(j)

cv2.drawContours(img_orig, cnt_list, -1, (255,0,0), 2)

# Show contours 
cv2.namedWindow('Show',cv2.WINDOW_NORMAL)
cv2.imshow("Show",img_orig)
cv2.waitKey()
cv2.destroyAllWindows()

# Zero pixels outside circle 
mask = np.zeros(img.shape).astype(img.dtype)
cv2.fillPoly(mask, cnt_list, (255,255,255))
mask_inv = cv2.bitwise_not(mask)

a = cv2.bitwise_and(img,img,mask = mask)
wh_back = np.ones(img.shape).astype(img.dtype)*255
b = cv2.bitwise_and(wh_back,wh_back,mask = mask_inv)

res = cv2.add(a,b)

# Get rid of noise 
kernel = np.ones((2, 2), np.uint8)
res = cv2.dilate(res, kernel, iterations=1)
res = cv2.erode(res, kernel, iterations=1)

# Show final image 
cv2.namedWindow('result',cv2.WINDOW_NORMAL)
cv2.imshow("result",res)
cv2.waitKey()
cv2.destroyAllWindows()

When code works, these are the images that get outputted: Working

However, in the instances where the text touches the circular border, the code assumes part of the text is part of the larger contour and ignores the last letter. For example: Not working

Are there any processing steps that can help me bypass this problem? Or perhaps a different approach? I've tried using Hough Circle Transforms to try to detect the borders, but they're quite finicky and doesn't work as well as contouring.

I'm quite new to OpenCV and Python so any help would be appreciated.

like image 643
dirac_delta Avatar asked Nov 07 '22 18:11

dirac_delta


1 Answers

If the Hough circle transform didn't work for you I think you're best option will be to approximate the boarder shape. The best method I know for that is: Douglas-Peucker algorithm which will make your contour simpler by reducing the perimeter on pics.

You can check this reference file from OpenCV to see the type of post processing you can apply to your boarder. They also mention Douglas-Peucker: OpenCV boarder processing

like image 114
hackela Avatar answered Nov 15 '22 05:11

hackela