I have a task to detect Circles and Radio buttons in an Image. For this I tried Hough circles by having different parameters.
Issues: If the circles in the Image are of same radius of Radio buttons both are detected, but in our case it should only detect only one.
Is there a way to differentiate between circles and Radio buttons (when they are not checked).
Right now I am limiting them by Radius with 2 different functions one for circle and one for radio button.
The above code is for circles
circle_contours=[]
# Converting the image Gray scale
gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
# Blur the image to reduce noise
img_blur = cv2.medianBlur(gray, 5)
# Apply hough transform on the image
circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1,20, param1=50, param2=20,
minRadius=11, maxRadius=21)
# Draw detected circles
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# Draw outer circle
cv2.circle(image1, (i[0], i[1]), i[2], (34, 255, 34), 2)
circle_contours.append(circles)
I have used a similar approach for radio buttons but with different parameters as below.
radio_buttons= cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1,20, param1=50, param2=16,
minRadius=9, maxRadius=10)
Original Image
Image 1:
Image 2:
For the Image1 it detects circles correctly and when it is passed to the radio buttons function it also draws circles(Image2) for the inner part of it with a reduced radius which are also detected as radio buttons
In Image3Image3 it has to detect Circle and Radio buttons, where my code is only able to detect circles.
I have also tried using draw contours but it had issues when the Image also has checkboxes.
Is there any other approach or a better way for detection?
Find and draw all the contours in the answer-sheet.
Apply HoughCircles
Step #1: We could start with finding all contours in the given answer-sheet.
contourIdx=-1 means to draw all the contours.
import cv2
import numpy as np
image = cv2.imread('zip_grade_form.png')
# Converting the image Gray scale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(src=gray, thresh=127, maxval=255, type=0)
contours, hierarchy = cv2.findContours(image=thresh,
mode=cv2.RETR_TREE,
method=cv2.CHAIN_APPROX_SIMPLE)
gray = cv2.drawContours(image=gray, contours=contours, contourIdx=-1,
color=(255, 255, 255), thickness=2)
Result:
From above we can see that all features except circles are removed. We use the findContours method to remove unwanted artifacts.
Step#2: Apply HoughCircles. The same code you wrote on the question. Result:
Code:
import cv2
import numpy as np
image = cv2.imread('zip_grade_form.png')
# Converting the image Gray scale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(src=gray, thresh=127, maxval=255, type=0)
contours, hierarchy = cv2.findContours(image=thresh,
mode=cv2.RETR_TREE,
method=cv2.CHAIN_APPROX_SIMPLE)
gray = cv2.drawContours(image=gray, contours=contours, contourIdx=-1,
color=(255, 255, 255), thickness=2)
cv2.imwrite("gray.png", gray)
img_blur = cv2.medianBlur(gray, 5)
circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=16,
minRadius=9, maxRadius=10)
circle_contours = []
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# Draw outer circle
cv2.circle(image, (i[0], i[1]), i[2], (108, 105, 255), 2)
circle_contours.append(circles)
cv2.imwrite("circles.png", image)
Update
For detecting check-boxes and radio-buttons you need to calculate the contour-perimeter (p) and the contour-approximation (a). source
We can separate each object using p and a value since each object has a unique p and a values.
For instance, in image-3,
check-box: p= 73 and a = 4
radio-button: p = 64 and a = 8.
You can find the values observing the code.
Apply the 1st step again.
Result:
Now find the contours in the above image:
if len(approx) == 8 and int(p) == 64:
cv2.drawContours(image, [c], -1, (180, 105, 255), 3)
elif len(approx) == 4 and int(p) == 73:
cv2.drawContours(image, [c], -1, (180, 105, 255), 3)
Result:
Code:
import cv2
from imutils import grab_contours as grb_cns
from imutils import resize as rsz
image = cv2.imread('K1Z94.png')
# Converting the image Gray scale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(src=gray, thresh=127, maxval=255, type=0)
contours, hierarchy = cv2.findContours(image=thresh.copy(),
mode=cv2.RETR_TREE,
method=cv2.CHAIN_APPROX_SIMPLE)
gray = cv2.drawContours(image=gray, contours=contours, contourIdx=-1, color=(255, 255, 255), thickness=2)
resized = rsz(gray, width=300)
ratio = gray.shape[0] / float(gray.shape[0])
canny = cv2.Canny(gray, 50, 200)
thresh = cv2.threshold(src=canny, thresh=60, maxval=255,
type=cv2.THRESH_OTSU + cv2.THRESH_BINARY)[1]
cns = cv2.findContours(image=thresh.copy(), mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
cns = grb_cns(cns)
for c in cns:
p = cv2.arcLength(c, True) # perimeter
approx = cv2.approxPolyDP(c, 0.04 * p, True)
M = cv2.moments(c)
# check if the all values of M are 0.
all_zr = all(value == 0 for value in M.values())
if not all_zr:
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
c = c.astype("float")
c *= ratio
c = c.astype("int")
# Circles: (radio-buttons)
if len(approx) == 8 and int(p) == 64:
cv2.drawContours(image, [c], -1, (180, 105, 255), 3)
elif len(approx) == 4 and int(p) == 73:
cv2.drawContours(image, [c], -1, (180, 105, 255), 3)
cv2.imwrite("result.png", image)
Related
I have the following image: mask
I'm trying separate each white piece into its own image. My approach is to find the contours, iterate over them and fill each one with white color then save the new image.
So far, I've found the contours after using Canny Edge Detection: contours
But I can't seem to fill them all on the inside, since the edges are not fully connected:contours filled
Is there a way to fill in the contours without using dilation/erosion? I intend to preserve the image as it is, not altering it more than needed.
I've used the following code.
import cv2
import numpy as np
def get_blank_image(image):
return np.zeros((image.shape[0], image.shape[1], 3), np.uint8)
# Let's load a simple image with 3 black squares
image = cv2.imread('img.png')
cv2.waitKey(0)
blank_image = get_blank_image(image)
# cv2.imshow('blank', blank_image)
# Grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Find Canny edges
edged = cv2.Canny(gray, 30, 50)
cv2.waitKey(0)
# Finding Contours
# Use a copy of the image e.g. edged.copy()
# since findContours alters the image
contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
good_contours = []
for con in contours:
area = cv2.contourArea(con)
if area > 10:
good_contours.append(con)
cv2.drawContours(blank_image, [con], 0, (255, 255, 255), thickness=cv2.FILLED)
# cv2.fillPoly(blank_image, pts=[con], color=(255, 255, 255))
# cv2.imshow('blank', blank_image)
# cv2.waitKey(0)
# good_contours.remove(con)
# cv2.imshow('Canny Edges After Contouring', edged)
cv2.waitKey(0)
cv2.imshow('blank', blank_image)
print("Number of Contours found = " + str(len(good_contours)))
# Draw all contours
# -1 signifies drawing all contours
cv2.drawContours(image, good_contours, -1, (0, 255, 0), 3)
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
I've got images like this one:
I need to detect the center of this circular element:
(More precisely - I'm looking for the midpoint of the circular element)
Currently my code detect the mold (the plastic circle that holds the circular element) and select the rectangle ROI to focus the image into the relevant area:
import cv2
import imutils
import numpy as np
if __name__ == "__main__":
image = cv2.imread('Dart - Overview Image - with Film.bmp')
img = imutils.resize(image, width=700)
image = img
output = image.copy()
roi = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# cv2.imshow("Gray", gray)
# cv2.waitKey(0)
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(output, (x, y), r, (0, 255, 0), 2)
cv2.rectangle(output, (x - 2, y - 2), (x + 2, y + 2), (0, 128, 255), -1)
roi = roi[y - r: y + r, x - r: x + r]
cv2.imshow("img", roi)
cv2.waitKey(0)
This will show this image:
Now, I'm trying to find the midpoint of the circular element inside this ROI image by detecting the circular element but It's challenged for me, I've try this methods:
gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray_roi, (3, 3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)[1]
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
(x, y), radius = cv2.minEnclosingCircle(c)
cv2.circle(roi, (int(x), int(y)), int(radius), (35, 255, 12), 3)
cv2.circle(roi, (int(x), int(y)), 1, (35, 255, 12), 2)
print(x, y)
break
# Find Canny edges
edged = cv2.Canny(roi, 30, 121, apertureSize=3, L2gradient=True)
cv2.waitKey(0)
# Finding Contours
# Use a copy of the image e.g. edged.copy()
# since findContours alters the image
contours, hierarchy = cv2.findContours(edged,
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.imshow('Canny Edges After Contouring', edged)
cv2.waitKey(0)
And got this output:
I've also tried to change the threshold parameters in the cv2.Canny function but it's didn't give me better results.
Thanks a lot!
I'm using the following code to detect the brightly illuminated lamp. The illumination might vary. I'm using the following code to detect the same.
img = cv2.imread("input_img.jpg")
rgb = img.copy()
img_grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
while True:
th3 = cv2.adaptiveThreshold(img_grey, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \
cv2.THRESH_BINARY, 11, 2)
cv2.imshow("th3",th3)
edged = cv2.Canny(th3, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)
cv2.imshow("edge", edged)
cnts = cv2.findContours(edged.copy(), cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
areaArray = []
for i, c in enumerate(cnts):
area = cv2.contourArea(c)
areaArray.append(area)
sorteddata = sorted(zip(areaArray, cnts), key=lambda x: x[0], reverse=True)
thirdlargestcontour = sorteddata[2][1]
x, y, w, h = cv2.boundingRect(thirdlargestcontour)
cv2.drawContours(rgb, thirdlargestcontour, -1, (255, 0, 0), 2)
cv2.rectangle(rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("rgb", rgb)
if cv2.waitKey(1) == 27:
break
The above code works but,
It only gives the rectangle that encompasses the lamp. How do I get the four corner points of the lamp precisely?
How can I improve detection? at the moment I'm picking the third-largest contour which does not guarantee that it will always be the lamp as the environment poses challenge?
ApproxPolydp works when the contour is complete but if the contour is incomplete, ApproxPolydp is not returning the proper coordinate. for instance in the following image the approxpolydp returns a wrong coordinates.
Here is one way to do that in Python/OpenCV.
Read the input image and convert to grayscale
Use adaptive thresholding to get a thick outline of the lamp region
Find the contours
Filter the contours on area to remove extraneous regions and keep only the larger of the two (inner and outer contours of thresholded region)
Get the perimeter
Fit the perimeter to a polygon, which should be a quadrilateral with the right choice of arguments.
Draw the contour (red) and polygon (blue) over a copy of the input image as the result
Input:
import cv2
import numpy as np
# load image
img = cv2.imread("lamp.jpg")
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold image
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 10)
thresh = 255 - thresh
# find contours
cntrs = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
# Contour filtering -- remove small objects and those that are too large
# Keep the larger of the two contours (inner and outer contours from thresh)
area_thresh = 0
for c in cntrs:
area = cv2.contourArea(c)
if area > 200 and area > area_thresh:
big_contour = c
area_thresh = area
# draw big_contour on image in red and polygon in blue and print corners
results = img.copy()
cv2.drawContours(results,[big_contour],0,(0,0,255),1)
peri = cv2.arcLength(big_contour, True)
corners = cv2.approxPolyDP(big_contour, 0.04 * peri, True)
cv2.drawContours(results,[corners],0,(255,0,0),1)
print(len(corners))
print(corners)
# write result to disk
cv2.imwrite("lamp_thresh.jpg", thresh)
cv2.imwrite("lamp_corners.jpg", results)
cv2.imshow("THRESH", thresh)
cv2.imshow("RESULTS", results)
cv2.waitKey(0)
cv2.destroyAllWindows()
Thresholded Image:
Result Image:
Corner Coordinates:
[[[233 145]]
[[219 346]]
[[542 348]]
[[508 153]]]
I have a big image of shape where i'm looking to overlap an image on a shape based on the contour
I have this image
I have this contur where it is detecting the shape and color of the image
I want to place an image on this shape
I want to custom resize the image
Out Put Desired:-
Code:-
if color == "blue" and shape == "pentagon":
A_img = cv2.imread("frame.png")
print(A_img)
x_offset=y_offset=50
B_img[y_offset:y_offset+A_img.shape[0], x_offset:x_offset+A_img.shape[1]] = A_img
That code is not working the monkey is getting printed on the top left
I have a working solution. Hope it is what you were looking for.
Code:
import cv2
import numpy as np
image = cv2.imread('C:/Users/524316/Desktop/shapes.png', 1)
monkey = cv2.imread('C:/Users/524316/Desktop/monkey.png', 1)
image2 = image.copy()
image3 = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
#cv2.imshow('thresh', thresh)
_, cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
#---- making sure to avoid small unwanted contours ---
if cv2.contourArea(c) > 150:
#--- selecting contours having 5 sides ---
if len(cv2.approxPolyDP(c, 0.04 * cv2.arcLength(c, True), True)) == 5:
cv2.drawContours(image2, [c], -1, (0, 255, 0), 2)
#--- finding bounding box dimensions of the contour ---
x, y, w, h = cv2.boundingRect(c)
print(x, y, w, h)
#--- overlaying the monkey in place of pentagons using the bounding box dimensions---
image3[y:y+h, x:x+w] = cv2.resize(monkey, (np.abs(x - (x+w)), np.abs(y - (y+h))))
cv2.imshow('image2', image2)
cv2.imshow('image3', image3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
How can I highly accurately find the area of smooth surfaces. I am trying to find areas of bubbles in a cylinder.
This code works relatively well, but does not provide the area for the open surfaces.
It also finds a fake bubble (which you can see if running the code)
Image:
Is there a way to make the edge of the image the end point of the contour?
import cv2
import numpy as np
import imutils
image = cv2.imread('Image.png')
# Convert Image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.bitwise_not(gray)
(thresh, bw) = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# Show greyscale image
cv2.namedWindow("main", cv2.WINDOW_NORMAL)
cv2.imshow('main', bw)
cv2.waitKey(0)
cv2.destroyAllWindows()
_, cnts, _ = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
print("no of shapes {}".format(len(contours)))
for c in cnts:
if cv2.contourArea(c) > 0:
# compute the center of the contour
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# draw the contour and center of the shape on the image
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.circle(image, (cX, cY), 7, (255,0,0), -1)
cv2.putText(image, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,0), 2)
# show the image
cv2.namedWindow("main", cv2.WINDOW_NORMAL)
cv2.imshow("main", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
area = cv2.contourArea(c)
print(area)
I have full flexibility in the output of the image (colour of the lines, background colour, line thickness) as the output is from simulations.
What settings would work best for OpenCV?