OpenCV - Closing a contour against image edge - python

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?

Related

Differentiate between Circles and Radio buttons Opencv python

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)

How to find the center of black objects in an image with python opencv?

I have a problem finding the contours of black objects on white background.
here I added an example of an image.
Now I need to find center of black regions and I use following code.
im = cv2.imread(img)
plt.imshow(im)
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(im, 60, 255, cv2.THRESH_BINARY_INV)
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
However it gives an error like
TypeError: Expected Ptr for argument 'image' " for the image argument of cv2.findeContours().
How can I solve this? or if you have any other idea how to find center of black regions I would like to hear. Thanks.
You're on the right track. When you perform find contours on your image, OpenCV expects that the foreground objects to detect are in white with the background in black. To do this, you can Otsu's threshold with the cv2.THRESH_BINARY_INV parameter to get the objects in white. From here we can find contours and find the center of each region by calculating for the centroid with cv2.moments. Here's the results:
Binary image
Center of object drawn in blue with the bounding box in green. You can get the coordinates of each contour by checking the cX and cY variables.
You can crop each individual ROI using Numpy slicing then save with cv2.imwrite
Code
import cv2
import numpy as np
# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('1.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Find contours
ROI_number = 0
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
# Obtain bounding rectangle to get measurements
x,y,w,h = cv2.boundingRect(c)
# Find centroid
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# Crop and save ROI
ROI = original[y:y+h, x:x+w]
cv2.imwrite('ROI_{}.png'.format(ROI_number), ROI)
ROI_number += 1
# Draw the contour and center of the shape on the image
cv2.rectangle(image,(x,y),(x+w,y+h),(36,255,12), 4)
cv2.circle(image, (cX, cY), 10, (320, 159, 22), -1)
cv2.imwrite('image.png', image)
cv2.imwrite('thresh.png', thresh)
cv2.waitKey()

Finding the center line and center point of rectangular region

I ran the following code to create a rectangle contour:
#import the necessary packages
import argparse
import imutils
import cv2
import numpy as np
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to the input image")
args = vars(ap.parse_args())
# load the image, convert it to grayscale, blur it slightly, and threshold it
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
# threshold the image, then perform a series of erosions + dilations to remove any small regions of noise
thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# Find the index of the largest contour
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
# show the output image
cv2.imshow("Image", image)
cv2.waitKey(0)
I would like to find the center line and center point of the rectangular contour. Please advise.
As you already have (x, y, w, h) of the desired contour using x,y,w,h = cv2.boundingRect(cnt) in above code, so center of the vertical mid line can be given by (x+w//2, y+h//2) and vertical line can be drawn using below code:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
# center line
cv2.line(image, (x+w//2, y), (x+w//2, y+h), (0, 0, 255), 2)
# below circle to denote mid point of center line
center = (x+w//2, y+h//2)
radius = 2
cv2.circle(image, center, radius, (255, 255, 0), 2)
output:
Since you already have the bounding box, you can use cv2.moments() to find the center coordinates. This gives us the centroid (i.e., the center (x, y)-coordinates of the object)
M = cv2.moments(cnt)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
The center point is simply (cX, cY) and you can draw this with cv2.circle()
cv2.circle(image, (cX, cY), 5, (36, 255, 12), -1)
Similarly, we can draw the center line using cv2.line() or Numpy slicing
cv2.line(image, (x + int(w/2), y), (x + int(w/2), y+h), (0, 0, 255), 3)
image[int(cY - h/2):int(cY+h/2), cX] = (0, 0, 255)
import imutils
import cv2
import numpy as np
# load the image, convert it to grayscale, blur it slightly, and threshold it
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
# threshold the image, then perform a series of erosions + dilations to remove any small regions of noise
thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# Find the index of the largest contour
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
M = cv2.moments(cnt)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
cv2.circle(image, (cX, cY), 5, (36, 255, 12), -1)
# To draw line you can use cv2.line or numpy slicing
cv2.line(image, (x + int(w/2), y), (x + int(w/2), y+h), (0, 0, 255), 3)
# image[int(cY - h/2):int(cY+h/2), cX] = (36, 255, 12)
# show the output image
cv2.imshow("Image", image)
cv2.imwrite("Image.png", image)
cv2.waitKey(0)

Python Open CV overlay image on contour of a big image

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:

detecting contours in image with white background

I am having trouble detecting contours in this image
Class Diagram Image
I want to detect all border contours in the diagram but so far my program is only detecting the image border as shown in this image
Ignore the 0 in the center that is just to show that it is contour 0.
I am not sure where the problem lies in my code as it can detect contours in images with a black background.
filename = sys.argv[1]
t = int(sys.argv[2])
img = cv2.imread(filename)
resized = imutils.resize(img, width=300)
ratio = img.shape[0] / float(resized.shape[0])
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
(t, binary) = cv2.threshold(blur, t, 255, cv2.THRESH_BINARY)
_ ,cnts, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for (i,c) in enumerate(cnts):
M = cv2.moments(c)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
else:
cX, cY = 0, 0
(x,y,w,h) = cv2.boundingRect(c)
area = cv2.contourArea(c)
cv2.rectangle(img, (x, y), (x + w, y + h),(0, 255, 255), 2)
print("Object %d has dimensions x=%d, y=%d, w=%d, h=%d area=%d" % (i,x,y,w,h,int(w*h)))
cv2.putText(img, str(i), (cX, cY),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
I am note sure where the problem lies in my code as it can detect contours in images with a black background.
This is almost definitly your problem. From the OpenCV tutorial
In OpenCV, finding contours is like finding white object from black background. So remember, object to be found should be white and background should be black.
This SO Q/A shows you how to invert your image.
You definitely need to be detecting white on black, as discussed here
If you don't want to invert your image, you can use cv2.THRESH_BINARY_INV instead.
(t, binary) = cv2.threshold(blur, t, 255, cv2.THRESH_BINARY_INV)

Categories