Connecting the missing pixels - python-3.x

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.

Related

How to apply threshold counters to only specified/masking region in the image using opencv

I am new in OpenCV and stuck at a point actually, I study lots of QA but all are unable or I can say not fully satisfied my requirement.
Suppose I have an image and I want to find counters for objectifying specified area only.
It is easy to counter a complete image but I want to counters only masking area.
For masking purpose I have x,y coordinates with me.
So my question is how to counter only masking area in the image using OpenCV
This is the one solution matching with my question->Stackoverflow suggestion -:
But it does not solving my problem completely.
The given solution in this solution is quite complicated and not solving my problem. as I want my counters ROI to be show in the same image using mask coordinates(xmin, ymin, xmax, ymax).
Means i first drawing reactangle mask into the image and i want to draw counters boundary only into that rectangle mask in the same image.
This is my Demo.py file:-
import cv2
img = cv2.imread("F:/mycar.jpg",1)
crop_img = cv2.rectangle(img, (631,181), (698,386), (0,255,0), 3)
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
retval, thresh = cv2.threshold(gray_img, 127, 255, 0)
img_contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, img_contours, -1, (255, 0, 0))
cv2.imshow("dflj",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
output
From this image, you can see that the complete image is countering.
But I want to just counter only that green-rectangle part of the image.
Means that blue counter boundary lines is visible inside whole image but I want to show only it inside of green-rectangle box.
I have rectangle coordinates with me (eg. minx-631, miny-181, maxx-698, maxy-386).
Please help.
You can apply functions to ROIs using Numpy indexing as explained in this tutoral:
top,left = 338,282
h,w = 216,106
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
crop_img = cv2.rectangle(img, (left,top), (left+w,top+h), (0,255,0), 3)
retval, thresh = cv2.threshold(gray_img, 127, 255, 0)
img_contours, _ = cv2.findContours(thresh[top:top+h,left:left+w], cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img[top:top+h,left:left+w], img_contours, -1, (255, 0, 0))
Please note that with Numpy indexing y comes first then x, as opposed to point coordinates with (x,y).
With this image we get:

Remove lines from this image

Trying to remove those lines after stitching multiple images but these lines are just not going away
Tried morphological Transformations of opencv nothing worked. Any help would be great.
You can use Gaussian blurring for this. However, as you want to remove vertical lines use a m x 1 kernel as it would affect only the vertical lines and will not blur horizontally.
img = cv2.imread('vertical_noise.jpg')
img = cv2.GaussianBlur(img, (11, 1), 0)
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
You can increase the kernel size to completely remove the lines but the image will also get blurrier.
EDIT
Sorry for the extremely late edit. If you don't want to blur then I don't know how to remove the lines in the car. However, the lines outside the car can be removed. For this create a threshold using cv2.threshold with a high thresh value and then find contours on it. Then using cv2.drawContours the blank spaces in the threshold image can be filled which can be treated as a mask. Using cv2.bitwise_and obtain two images using that as a mask with a blank image and using its inverse as a mask along with the original image. Then combine then cv2.bitwise_or.
import cv2
import numpy as np
img = cv2.imread('vertical_noise.jpg')
height, width, ch = img.shape
blur = cv2.GaussianBlur(img, (11, 1), 0)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
_, cnts, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(thresh, cnts, -1, 0, 3)
res = cv2.bitwise_and(img, img, mask=cv2.bitwise_not(thresh))
blank = np.ones((height, width, ch), np.uint8)*255
inv_res = cv2.bitwise_and(blank, blank, mask=thresh)
res = cv2.bitwise_or(res, inv_res)
cv2.imshow('image', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
Now if you want you can blur it a little for the lines inside the car.

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.

Extracting text OpenCV Contours

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

Text Detection: Getting Bounding boxes

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.

Resources