Extracting text OpenCV Contours - python-3.x

I tried doing ocr of each individual contour using tesseract but not getting proper text out of it. Contour Identification is done properly by using Extracting text OpenCV.
Please suggest.

You are not getting proper text from OCR because of bad image pre-processing.
Try various image processing techniques to narrow down on a workable approach for your image.
As you have asked under python, If you have a colour image,
Convert it into black and white image, to remove the colour noise.
img = cv2.imread('name_of_the_coloured_input_image',0)
Blur the image using blurring techniques of opencv (averaging, gaussian blurring, median blurring and bilateral filtering), this decreases various noises in the image.
Please refer to this link and try out various techniques
Then use thresholding (simple, adaptive or otsu thresholding), which removes all the pixels which are less than a certain threshold.
Please refer to this link and try out various techniques
Now, get contours and try using tesseract on the contours to get better results.
Note : Please remember that for tesseract to work, you should have the text in black against a white background.

Please check for the below function, tell me if anything is missing.
#gray out the image
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', gray)
cv2.waitKey(0)
#image blurring
blur = cv2.blur(gray,(1,1))
cv2.imshow('Blur', blur)
cv2.waitKey(0)
#threshold & invert
ret, thresh = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY_INV)
thresh_copy = thresh.copy()
cv2.imshow("Threshold", thresh_copy)
cv2.waitKey(0)
#Erosion
kernel1 = np.ones((1,1), np.uint8)
img_erosion = cv2.erode(thresh, kernel1, iterations=1)
cv2.imshow("Erosion", img_erosion.copy())
cv2.waitKey(0)
#applying dilation
kernel = np.ones((6,10), np.uint8)
img_dilation = cv2.dilate(img_erosion.copy(), kernel, iterations=1)
cv2.imshow("Dilation", img_dilation)
cv2.waitKey(0)
#find contours
im2, ctrs, hier = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return ctrs

Related

OpenCV : How to clear Image Background of ID Card without losing quality

I want to clear an image background of ID without losing quality, Keep only the text with white background
Using the following code is not efficient, produce high noise and distortion
img = cv2.imread(imge)
# Convert into grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of black color in HSV
lower_val = np.array([0,0,0])
upper_val = np.array([179,255,135])
# Threshold the HSV image to get only black colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# invert mask to get black symbols on white background
mask_inv1 = cv2.bitwise_not(mask)
mask_inv = cv2.blur(mask_inv1,(5,5))
How I can achieve clean background with these images
Samples
Output
Using Rembg to get perfect output like below
Rembg is an offline tool to remove images background. you can simply install Rembg package using pip in python
pip install rembg
afterthat,
rembg i path/to/input.png path/to/output.png

How can I extract cheque amount from cheque images?

I am trying to extract the cheque amount (underlined text in Input Image) from cheque images. I am trying to do this in the following 2 steps:
Locate the rectangle box of the amount in the image.
Perform OCR using OCR libraries like Tesseract OCR and get the text.
I tried to locate the rectangle box but it is locating so many things from the image.
How can we approach this problem? If anyone has a different approach to extract the amount then please guide me.
My Code
import numpy as np
import cv2
img = cv2.imread("Ex2.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
contours,_ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for contour in contours:
(x,y,w,h) = cv2.boundingRect(contour)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
cv2.imshow('detected.jpg',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Input Image
Currently, I am getting this.

Connecting the missing pixels

I am trying to fill the missing pixels (as shown in image) in the circle part to make complete and clean circles. I have tried image enhancement techniques, they didn't help much. Please suggest me how to do in Matlab or provide some code to do that. Thanks in advance.
Since your tags suggest you're open to Python solutions as well, I present the following approach using OpenCV, specifically the cv2.HoughCircles method, following this tutorial.
Here's the code:
import cv2
import numpy as np
# Read input image
img = cv2.imread('images/xbHB0.jpg', cv2.IMREAD_GRAYSCALE)
# Blur input image to prevent too much false-positive detected circles
img = cv2.GaussianBlur(img, (3, 3), 0)
# Initialize outputs
clean = np.zeros(img.shape, np.uint8)
compare = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# Detect circles using Hough transform; convert center points and radii to int
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=25, minRadius=10, maxRadius=0)
circles = np.uint16(np.around(circles))
# Draw detected circle to outputs
for i in circles[0, :]:
cv2.circle(clean, (i[0], i[1]), i[2], 255, 1)
cv2.circle(compare, (i[0], i[1]), i[2], (0, 255, 0), 1)
cv2.circle(compare, (i[0], i[1]), 2, (0, 0, 255), 1)
cv2.imshow('Input', img)
cv2.imshow('Comparison', compare)
cv2.imshow('Clean output', clean)
cv2.waitKey(0)
cv2.destroyAllWindows()
The "clean" circles would look like this:
And, for comparison, an overlay on the original image:
As you can see, you won't get perfect results using this method on this specific image. Parameter tuning might improve the result.
Hope that helps!
In case your problem is specific to circles, you can use the Hough Circles algorighm to find the circles in the image and then simply draw them. I don't know how it's done in matlab. In python you can use opencv HoughCircles
If you are looking for a more general solution, morphological operators such as dilate, erode, open, close may be of interest.

How to find the document edges in various coloured backgrounds using opencv python? [Document Scanning in various backgrounds]

I am currently have a document that needs to be smart scanned.
For that, I need to find proper contours of the document in any background so that I can do a warped perspective projection and detection with that image.
The main issue faced while doing this is that the document edge detects any kind of background.
I have tried to use the function HoughLineP and tried to find contours on the grayscale blurred image passed through canny edge detection until now.
MORPH = 9
CANNY = 84
HOUGH = 25
IM_HEIGHT, IM_WIDTH, _ = rescaled_image.shape
# convert the image to grayscale and blur it slightly
gray = cv2.cvtColor(rescaled_image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7,7), 0)
#dilate helps to remove potential holes between edge segments
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
dilated = cv2.dilate(gray, kernel)
# find edges and mark them in the output map using the Canny algorithm
edged = cv2.Canny(dilated, 0, CANNY)
test_corners = self.get_corners(edged)
approx_contours = []
(_, cnts, hierarchy) = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
# loop over the contours
for c in cnts:
# approximate the contour
approx = cv2.approxPolyDP(c, 80, True)
if self.is_valid_contour(approx, IM_WIDTH, IM_HEIGHT):
approx_contours.append(approx)
break
How to find a proper bounding box around the document via OpenCV code.
Any help will be much appreciated.
(The document is taken from the camera in any angle and any coloured background.)
Following code might help you to detect/segment the page in the image...
import cv2
import matplotlib.pyplot as plt
import numpy as np
image = cv2.imread('test_p.jpg')
image = cv2.imread('test_p.jpg')
print(image.shape)
ori = image.copy()
image = cv2.resize(image, (image.shape[1]//10,image.shape[0]//10))
Resized the image to make the operations more faster so that we can work on realtime..
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (11,11), 0)
edged = cv2.Canny(gray, 75, 200)
print("STEP 1: Edge Detection")
plt.imshow(edged)
plt.show()
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts[1], key = cv2.contourArea, reverse = True)[:5]
Here we will consider only first 5 contours from the sorted list based on area
Here the size of the gaussian blur is bit sensitive, so chose it accordingly based on the image size.
After the above operations image may look like..
for c in cnts:
### Approximating the contour
#Calculates a contour perimeter or a curve length
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.01 * peri, True)
# if our approximated contour has four points, then we
# can assume that we have found our screen
screenCnt = approx
if len(approx) == 4:
screenCnt = approx
break
# show the contour (outline)
print("STEP 2: Finding Boundary")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
image_e = cv2.resize(image,(image.shape[1],image.shape[0]))
cv2.imwrite('image_edge.jpg',image_e)
plt.imshow(image_e)
plt.show()
Final Image may look like...
Rest of the things may be handled after getting the final image...
Code Reference :- Git Repository
I guess this answer would be helpful...
There is a similar problem which is called orthographic projection.
Orthographic approaches
Rather than doing, Gaussian blur+morphological operation to get the edge of the document, try to do orthographic projection first and then find contours via your method.
For fining proper bounding box, try some preset values or a reference letter after which an orthographic projection will allow you to compute the height and hence the dimensions of the bounding box.

How to Segment handwritten and printed digit without losing information in opencv?

I've written an algorithm that would detect printed and handwritten digit and segment it but while removing outer rectangle handwritten digit is lost using clear_border from ski-image package. Any suggestion to prevent information.
Sample:
How to get all 5 characters separately?
Segmenting characters from the image -
Approach -
Threshold the image (Convert it to BW)
Perform Dilation
Check the contours are large enough
Find rectangular Contours
Take ROI and save the characters
Python Code -
# import the necessary packages
import numpy as np
import cv2
import imutils
# load the image, convert it to grayscale, and blur it to remove noise
image = cv2.imread("sample1.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# threshold the image
ret,thresh1 = cv2.threshold(gray ,127,255,cv2.THRESH_BINARY_INV)
# dilate the white portions
dilate = cv2.dilate(thresh1, None, iterations=2)
# find contours in the image
cnts = cv2.findContours(dilate.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
orig = image.copy()
i = 0
for cnt in cnts:
# Check the area of contour, if it is very small ignore it
if(cv2.contourArea(cnt) < 100):
continue
# Filtered countours are detected
x,y,w,h = cv2.boundingRect(cnt)
# Taking ROI of the cotour
roi = image[y:y+h, x:x+w]
# Mark them on the image if you want
cv2.rectangle(orig,(x,y),(x+w,y+h),(0,255,0),2)
# Save your contours or characters
cv2.imwrite("roi" + str(i) + ".png", roi)
i = i + 1
cv2.imshow("Image", orig)
cv2.waitKey(0)
First of all I thresholded the image to convert it to black n white. I get characters in white portion of image and background as black. Then I Dilated the image to make the characters (white portions) thick, this will make it easy to find the appropriate contours. Then find findContours method is used to find the contours. Then we need to check that the contour is large enough, if the contour is not large enough then it is ignored ( because that contour is noise ). Then boundingRect method is used to find the rectangle for the contour. And finally, the detected contours are saved and drawn.
Input Image -
Threshold -
Dilated -
Contours -
Saved characters -
Problem of eroded/cropped handwritten digits:
you may solve this problem in the recognition step, or even in image improvement step (before recognition).
if only a very small part of digit is cropped (such your image example), it's enough to pad your image around by 1 or 2 pixels to make the segmentation process easy. Or some morpho filter (dilate) can improve your digit even after padding. (these solution are available in Opencv)
if a enough good part of digit is cropped, you need to add a degraded/cropped pattern of digits to the training Dataset used for digit recognition algorithm, (i.e. digit 3 with all possible cropping cases.. etc)
Problem of characters separation :
opencv offers blob detection algorithm that works well on your issue (choose the correct value for concave & convexity params)
opencv offers as well contour detector (canny() function), that helps to detect the contours of your character then you can find the fitted bounding (offered by Opencv as well : cv2.approxPolyDP(contour,..,..)) box around each character

Resources