Text Detection: Getting Bounding boxes - python-3.x

I have a black and white image, of a text document. I want to be able to get a list of the bounding boxes for each character. I have attempted an algorithm myself, but it takes excessively long, and is only somewhat successful. Are there any python libraries I can use to find the bounding box? I've been looking into opencv but the documentation is hard to follow. And in this tutorial I can't even decipher whether the bounding boxes were found because I can't easily find what the functions actually do.

You can use boundingRect(). Make sure your image background is black and text in image is white.Using this code you can draw rectangles around text in your image. To get a list of every rectangle please add respective code segment as per your requirement.
import cv2
img = cv2.imread('input.png', 0)
cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU,img)
image, contours, hier = cv2.findContours(img, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
for c in contours:
# get the bounding rect
x, y, w, h = cv2.boundingRect(c)
# draw a white rectangle to visualize the bounding rect
cv2.rectangle(img, (x, y), (x + w, y + h), 255, 1)
cv2.drawContours(img, contours, -1, (255, 255, 0), 1)
cv2.imwrite("output.png",img)

I would suggest that you look into the boundingRect() function in the openCV library:
https://docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/bounding_rects_circles/bounding_rects_circles.html
The documentation is based on cpp but it can be implemented in python as well.

Related

Make Area OUTSIDE of Bounding Box/ROI Transparent Using CV2

I'm wanting to know if there's a better way of doing this, but given an image, and bounding box coordinates, how can I transform the pixels OUTSIDE of the bounding box transparent?
Here's a simple pseudo reproducible example, just have to provide an image for it from your own machine. Note this code is not working as you'll see, looking for the best option to do this:
import cv2
# Read in the image and clone it
image = cv2.imread("YOUR_IMAGE.jpg", cv2.IMREAD_UNCHANGED)
clone = image.copy()
# Define a bounding box
bound_box = {"xmin": "54", "ymin": "40", "xmax": "434", "ymax": "306"}
# Create 4 rectangles (left, top, right, and bottom) around bounding box coords minus 1 pixel.
# This would be the sample line of code to use 4 times
cv2.rectangle(clone, (x, y), (x+w, y+h), (0, 200, 0), -1)
# Transparency factor.
alpha = 0.0
# Add the transparency to the 4 rectangles?
clone = cv2.addWeighted(clone, alpha, image, 1 - alpha, 0)

Bounding boxes for individual contours except one color

I have an image as below
I want to add bounding boxes for each of the regions as shown in the pic below using OpenCV & Python
I now how to find contours is the region is one colour. However, here I want to find contours for all non-Black regions. I am just not able to figure it out. Can anyone help?
Regrading some regions being regions being non-continuous (2 vertical lines on the left), you can ignore that. I will dilate & make them continuous.
If I understand what you want, here is one way in Python/OpenCV.
Read the input
Convert to gray
Threshold to black and white
Find external contours and their bounding boxes
Draw the bounding box rectangles on a copy of the input
Save the results
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('white_green.png')
# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]\
# get contour bounding boxes and draw on copy of input
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
for c in contours:
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(result, (x, y), (x+w-1, y+h-1), (0, 0, 255), 1)
# view result
cv2.imshow("threshold", thresh)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save result
cv2.imwrite("white_green_thresh.jpg", thresh)
cv2.imwrite("white_green_bboxes.jpg", result)
Thresholded image:
Bounding Boxes:

How to properly filter this image using OpenCV in Python

I have a computer vision project where I need to recognise digits on some totems which contain direction signages, an example image is here :
So I tried many methods such as taking laplacian,
img = cv2.imread(imgpath)
img = cv2.resize(img,(600,600))
imaj = cv2.GaussianBlur(img,(11,11),0)
imaj = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
laplas = cv2.Laplacian(imaj,cv2.CV_16SC1,ksize=5,scale=5,delta=1)
After applying this, I get a pretty good distinguish between background and the digits but since the output becomes 16SC1, I can not take the contours in the image. I tried thresholding this, but still can not get anything clear out of it.
That's what i get after thresholding from range(5000,8000) and converting it to uint8,
In the end, I try to take contours from it with this code here :
def drawcntMap(filteredimg):
"""
Draws bounding boxes on the contour map for each image
"""
contour = cv2.findContours(filteredimg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]
digitCnts = []
# draw bounding boxes for contour areas, then filter them approximately on
# digit size and append filtered ones to a list
for c in contour:
(x, y, w, h) = cv2.boundingRect(c)
if w >= 7 and h >= 10 and w <=50 and h <=70:
digitCnts.append(c)
#create another contour map with filtered contour areas
cnt2 = cv2.drawContours(img.copy(), digitCnts, -1, (0,0,255),2)
#draw bounding boxes again on the filtered contour map.
for c in digitCnts:
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(cnt2, (x, y), (x+w, y+h), (0,255,0), 2)
return cnt2,digitCnts
result would be :
How can I improve my solutions for this task and get all the digits? Besides laplacian filter, I tried darkening the image by lowering contrast and extracting white color regions (It did kinda good but still couldn't get all the digits), I tried gaussian blur and canny edge but at some places where the totem and the background are in the same pixel value contours merged.

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.

Resources