I have an Image in which i want to extract a region of interest which is a circle
and then crop it.
I have done some preprocessing and i am sharing the result and code.
please let me know how can i achieve this.
import imutils
import cv2
import numpy as np
#edge detection
image = cv2.imread('newimage.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image = cv2.GaussianBlur(image, (5, 5), 0)
canny = cv2.Canny(image, 30, 150)
#circle detection
img = cv2.medianBlur(image,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(canny,cv2.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
#outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
#center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
plt.title('Circle Detected')
plt.xticks([])
plt.yticks([])
plt.imshow(cimg,cmap = 'gray')
plt.show()
In your example you should get circle with the biggest radius which is in i[2]
max_circle = max(circles[0,:], key=lambda x:x[2])
print(max_circle)
i = max_circle
#outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
#center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
Check this out, the main tips:
play with blurring params;
play with HoughCircles params: minSize, maxSize and minDist
(minimal distance between circles)
img = cv2.imread('newimage.jpg', cv2.IMREAD_COLOR)
# Convert to grayscale and blur
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_blurred = cv2.bilateralFilter(gray, 11, 30, 30)
# tune circles size
detected_circles = cv2.HoughCircles(gray_blurred,
cv2.HOUGH_GRADIENT, 1,
param1=50,
param2=30,
minDist=100,
minRadius=50,
maxRadius=70)
if detected_circles is not None:
# Convert the circle parameters a, b and r to integers.
detected_circles = np.uint16(np.around(detected_circles))
for pt in detected_circles[0, :]:
a, b, r = pt[0], pt[1], pt[2]
# Draw the circumference of the circle.
cv2.circle(img, (a, b), r, (0, 255, 0), 2)
# Draw a small circle (of radius 1) to show the center.
cv2.circle(img, (a, b), 1, (0, 0, 255), 3)
cv2.imshow("Detected circles", img)
cv2.waitKey(0)
small circle values:
minRadius=50
maxRadius=60
big circle values:
minRadius=100
maxRadius=200
Related
This question already has an answer here:
How to blackout area outside circle with OpenCV Python?
(1 answer)
Closed 5 months ago.
I want the color code of circle (rgb) for that I have detected circle but doesn't know further process to dynamically mask/crop every background except inside circle
import numpy as np
import cv2
img = cv2.imread('4.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.medianBlur(gray, 25) #cv2.bilateralFilter(gray,10,50,50)
minDist = 100
param1 = 30 #500
param2 = 50 #200 #smaller value-> more false circles
minRadius = 5
maxRadius = 100 #10
# docstring of HoughCircles: HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) -> circles
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)
# Show result for testing:
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows() ````
The code in the question finds the circles using circles=cv2.HoughCircles() method of OpenCV which returns in case of the below shown image:
[[[226.5 211.5 111.6]
[597.5 364.5 108.2]
[498.5 145.5 95.9]]]
# ^ ^ ^
# x y r of found circles x,y == center and r == radius
Don't be fooled by the fact that there are float values returned for specifying integer pixel coordinates of the circle center in the image. They can be used as they are to preserve their precision or turned into integer values if necessary for indexing the corresponding pixel in the image to obtain its BGR color value (OpenCV uses by default BGR color format not RGB).
With the above results it's easy to create a mask by creating a new black image of same size as the original image mask = np.zeros_like(img) with white filled (specify thickness of the drawn circle line as -1 to get a filled circle) circles. With known coordinates of circle centers you can extract their BGR colors from the image array by indexing it with img[y,x]. If you want RGB just switch G and R in the color tuple.
import numpy as np
import cv2 as cv
img = cv.imread('circles.png')
cv.imshow('original image: circles.png', img)
cv.waitKey(0)
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow('gray image', img_gray)
cv.waitKey(0)
img_gray_blurred = cv.medianBlur(img_gray, 25) # cv.bilateralFilter(gray,10,50,50)
cv.imshow('gray/blurred image', img_gray_blurred)
cv.waitKey(0)
minDist = 10
param1 = 30 #500
param2 = 70 #200 # smaller value-> more false circles
minRadius = 5
maxRadius = 150 #10
# docstring of HoughCircles: HoughCircles(image, method, dp, minDist
# [, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) -> circles
circles = cv.HoughCircles( img_gray_blurred,
cv.HOUGH_GRADIENT,
1,
minDist,
param1=param1, param2=param2,
minRadius=minRadius, maxRadius=maxRadius)
"""
print(circles) # gives float values for x, y, r of the cirles
[[[226.5 211.5 111.6]
[597.5 364.5 108.2]
[498.5 145.5 95.9]]]
"""
mask = np.zeros_like(img) # create empty black image with img size
white_color = (255, 255, 255)
line_color = (255, 0, 255)
filled_circle = -1
line_thickness = 3
if circles is not None: # found a circle or more than one circle
circles = np.uint16(np.around(circles)) # <-- turn float to integer
for i, (x, y, r) in enumerate(circles[0,:]):
cv.circle(img, (x, y), r, line_color, line_thickness)
print(f' Circle {i+1} center BGR is: {img[y,x]=}')
# draw filled circle in white on black background as mask:
mask = cv.circle(mask, (x, y), r, white_color, filled_circle)
# Apply mask to image
result = cv.bitwise_and(img, mask)
# Show result for testing:
cv.imshow('image with marked circles', img)
cv.waitKey(0)
cv.imshow('mask', mask)
cv.waitKey(0)
cv.imshow('masked image', result)
cv.waitKey(0)
cv.destroyAllWindows()
gives as output:
Circle 1 center BGR is: img[y,x]=array([ 18, 239, 57], dtype=uint8)
Circle 2 center BGR is: img[y,x]=array([ 64, 236, 13], dtype=uint8)
Circle 3 center BGR is: img[y,x]=array([ 6, 240, 105], dtype=uint8)
I'm using OpenCV houghcircles to identify all the circles (both hollow and filled). Follow is my code:
import numpy as np
import cv2
img = cv2.imread('images/32x32.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
bilateral = cv2.bilateralFilter(gray,10,50,50)
minDist = 30
param1 = 30
param2 = 50
minRadius = 5
maxRadius = 100
circles = cv2.HoughCircles(bilateral, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2)
# Show result for testing:
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Test input image 1:
Test output image1:
As you can see I'm able identity most of the circles except for few. What am I missing here? I've tried varying the parameters but this is the best i could get.
Also, if I use even more compact circles the script does not identify any circles whatsoever.
An alternative idea is to use find contour method and chek whether the contour is a circle using appox as below.
import cv2
img = cv2.imread('32x32.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
inputImageCopy = img.copy()
# Find the circle blobs on the binary mask:
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Use a list to store the center and radius of the target circles:
detectedCircles = []
# Look for the outer contours:
for i, c in enumerate(contours):
# Approximate the contour to a circle:
(x, y), radius = cv2.minEnclosingCircle(c)
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx)>5: # check if the contour is circle
# Compute the center and radius:
center = (int(x), int(y))
radius = int(radius)
# Draw the circles:
cv2.circle(inputImageCopy, center, radius, (0, 0, 255), 2)
# Store the center and radius:
detectedCircles.append([center, radius])
cv2.imshow("Circles", inputImageCopy)
cv2.waitKey(0)
cv2.destroyAllWindows()
I solved your problem. Using same code as your. No needed to modified. I changed value from 50 to 30. That all.
#!/usr/bin/python39
#OpenCV 4.5.5 Raspberry Pi 3/B/4B-w/4/8GB RAM, Bullseye,v11.
#Date: 19th April, 2022
import numpy as np
import cv2
img = cv2.imread('fill_circles.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
bilateral = cv2.bilateralFilter(gray,10,50,50)
minDist = 30
param1 = 30
param2 = 30
minRadius = 5
maxRadius = 100
circles = cv2.HoughCircles(bilateral, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2)
cv2.imwrite('lego.png', img)
# Show result for testing:
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output:
If you can always get(or create from real image) such "clean" image, the bounding-box of the grid(2-dimensional array of circles) region can be easily obtained.
Therefore, you can know the rectangular area of interest (and the angle of rotation of the grid, if rotation is possible).
If you examine the pixels along the axial direction of the rectangle, you can easily find out how many circles are lined up and the diameter of the circle.
Because lines that all pixel are black are gaps between adjacent row(or column).
(Sum up the pixel values along the direction of the axis. Whether it is 0 or not tells you whether the line passes over the grid-cell or not.)
If necessary, you may check that the shape of the contour in each gird-cell is really circular.
When I try to detect circle from Coins Image:
In the first program, I use Matlab and everything works fine.
Now I try do detect The same circles from the coin's image but used OpenCV and I get a bad result.
I think that I do something wrong but I don't know what?
% This Example for Hough Transform Circles Matlab
% First step read image and make pre-processing
img = imread('coins2.png'); % Coins Image is Gray
figure, imshow(img), title('Orignal Coins Image');
edges = edge(img, 'Canny', [0.01, 0.5]);
figure, imshow(edges), title('Edge Image');
% Second Step Find all circles with radius [Rmin,Rmax]
Rmin = 10;
Rmax = 40;
[center, radii, metric] = imfindcircles(edges, [Rmin, Rmax]);
% Retain the N strongest circles according to the metric values
N = 24; % Hyperparam I can choose or I can display all circles that I get from the voting
centerStrong = center(1:N,:);
radiusStrong = radii(1:N);
metricStrong = metric(1:N);
% Draw the N strongest circle perimeters over the original image.
figure, imshow(img), hold on, viscircles(centerStrong, radiusStrong,'EdgeColor','b'),
title('Circle Segment'), hold off;
# Python exam:
import cv2
img = cv2.imread('coins2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert the image to gray scale
edges = cv2.Canny(image=gray, threshold1=200, threshold2=100, apertureSize=3)
cv2.imshow('Orignal Image', img), cv2.imshow('Edge Image', edges)
circles = cv2.HoughCircles(image=edges, method=cv2.HOUGH_GRADIENT, dp=20, minDist=1,
param1=30, param2=50, minRadius=10, maxRadius=50)
for circle in circles[0, :]:
a, b = int(circle[0]), int(circle[1])
radius = int(circle[2])
cv2.circle(img=img, center=(a, b), radius=radius, color=(255, 0, 0), thickness=2)
cv2.imshow('Circle Segment', img), cv2.waitKey(0), cv2.destroyAllWindows()
Thanks to everyone for the answers it was very helpful.
Attached the code that brought me almost the same MATLAB performance.
import cv2
img = cv2.imread('coins2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert the image to gray scale
thres=230
edges = cv2.Canny(image=gray, threshold1=thres, threshold2=thres/2,
apertureSize=3, L2gradient=True)
cv2.imshow('Orignal Image', img), cv2.imshow('Edge Image', edges)
circles = cv2.HoughCircles(image=gray, method=cv2.HOUGH_GRADIENT, dp=0.06,
minDist=14, param1=thres, param2=30, minRadius=10,
maxRadius=40)
for circle in circles[0, :]:
a, b = int(circle[0]), int(circle[1])
radius = int(circle[2])
cv2.circle(img=img, center=(a, b), radius=radius, color=(255, 0, 0),
thickness=2)
cv2.imshow('Circle Segment', img), cv2.waitKey(0), cv2.destroyAllWindows()
So I have this image (480, 640, 3):
And I want to do detect different circles in it (mainly the red one, but you don't care).
Here is my code :
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('img0.png')
print(img.shape)
sat = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)[:, :, 1]
print(img.shape)
circles = circles = cv2.HoughCircles(sat, cv2.HOUGH_GRADIENT, 1, minDist=30, maxRadius=500)
print(circles)
if circles is not None: # code stolen from here : https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/
# 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(img, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(img, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
plt.imshow(img)
plt.show()
Note that I use the saturation for the gray image, as I want the red part of my hand spinner, it is easier for me
Which output this :
Which is not that bad for me(ignore the wrong color scheme, It's bgr of opencv displayed as rgb), except that there is circles with too big radius. The output of print(circle) is :
[[[420.5 182.5 141.3]
[420.5 238.5 84.5]
[335.5 283.5 35. ]
[253.5 323.5 42.7]
[417.5 337.5 43.6]]]
(it is [x,y, radius])
Basically, it means that circles of interest has radius below 50, and I want to get rid of the two first one. I wanted to use the maxRadius parameter (notice that in my code, it is currently 500). So my guess was that if I set maxRadius at 50, it would remove the unwanted radius, but instead, it deleted all the circles... I have found that with maxRadius at 400, I got an output that "works":
And with maxRadius below 200, there is no more circles found.
What I am missing here ?
I am working on windows, python 3.7.7, last version of opencv
The following seems to work in Python/OpenCV.
Read the input
Convert to HSV and extract the saturation channel
Median filter
Do Hough Circles processing
Draw the circles
Save the results
Input
import cv2
import numpy as np
# Read image
img = cv2.imread('circles.png')
hh, ww = img.shape[:2]
# Convert to HSV and extract the saturation channel
sat = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,1]
# median filter
median = cv2.medianBlur(sat, 7)
# get Hough circles
min_dist = int(ww/20)
circles = cv2.HoughCircles(median, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=150, param2=50, minRadius=0, maxRadius=0)
print(circles)
# draw circles
result = img.copy()
for circle in circles[0]:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
(x,y,r) = circle
x = int(x)
y = int(y)
cv2.circle(result, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(result, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
# save results
cv2.imwrite('circles_saturation.jpg', sat)
cv2.imwrite('circles_median.jpg', sat)
cv2.imwrite('circles_result.jpg', result)
# show images
cv2.imshow('sat', sat)
cv2.imshow('median', median)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Saturation image:
Median filtered image:
Results:
Circles data:
[[[258.5 323.5 52.7]
[340.5 193.5 51.3]
[422.5 326.5 34.1]
[333.5 276.5 33.6]]]
I'm trying to detect this black circle here. Shouldn't be too difficult but for some reason I just get 0 circles or approximately 500 circles everywhere, depending on the arguments. But there is no middle ground. Feels like I have tried to play with the arguments for hours, but absolutely no success. Is there a problem using HoughCircles and black or white picture? The task seems simple to a human eye, but is this difficult to the computer for some reason?
Here's my code:
import numpy as np
import cv2
image = cv2.imread('temp.png')
output = image.copy()
blurred = cv2.blur(image,(10,10))
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.5, 20, 100, 600, 10, 100)
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
print len(circles)
for (x, y, r) in circles:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
show the output image
cv2.imshow("output", np.hstack([output]))
cv2.waitKey(0)
There are few minor mistakes in your approach.
Here is the code I used from the documentation:
img = cv2.imread('temp.png',0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
cimg1 = cimg.copy()
circles = cv2.HoughCircles img,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,255,255),3)
cv2.imshow('detected circles.jpg',cimg)
joint = np.hstack([cimg1, cimg]) #---Posting the original image along with the image having the detected circle
cv2.imshow('detected circle and output', joint )