I have followed OpenCV's tutorial here for circle detection on my Raspberry Pi. This is the code that I am using which is the same as the tutorial except a different image.
import cv2
import numpy as np
img = cv2.imread('watch.jpg',0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
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,:]:
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow('image',cimg)
cv2.waitKey(0)
cv2.destroyAllWindows()
Then when I ran the script this is what I was presented with this
and this is the original image
What is causing this to happen?
Thank You in Advance!
Edit:
The large amount of circles generated by Hough Circle Transform is caused by the low value of the threshold for center detection, which is param2 in cv2.HoughCircles in your case.
So try to increase the value of param2 to avoid false detections.
Also you can adjust minRadius and maxRadius values for better results.
EDIT:
I have just tried example from here and changed only param2 to 10, minRadius to 30 and maxRadius to 50. The result is good enough:
The example from the link above is written with C++, but you can compare parameters and the sequence of functions invocations to refine your own algorithm.
Related
I am trying to detect silver balls reflecting the environment with OpenCV:
With black balls, I successfully did it by detecting circles:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(5,5),0);
gray = cv2.medianBlur(gray,5)
gray = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,11,3.5)
kernel = np.ones((3,3),np.uint8)
gray = cv2.erode(gray,kernel,iterations = 1)
gray = cv2.dilate(gray,kernel,iterations = 1)
circles = cv2.HoughCircles(gray, cv.CV_HOUGH_GRADIENT, 1, 260, \
param1=30, param2=65, minRadius=0, maxRadius=0)
But when using the program with silver balls, we don't get any result.
When looking at the edges calculated by the program, the edges of the ball are quite sharp. But the Code is not recognizing any ball.
How do I improve the detection rate of the silver ball? I think of two ways doing that:
- Improving edge calculation
- Make the circle detection accept an image with unclear edges
Is that possible? What is the best way of doing so?
Help is very appreciated.
You have to tune your parameters. The HoughCircles function does a good job in detecting circles (even with gaps). Note that HoughCircles performs an internal binarization using canny edge detection. Thus, you don't have to do thresholding.
Given your image above, the code
import cv2
from matplotlib import pyplot as plt
import numpy as np
PATH = 'path/to/the/image.jpg'
img = cv2.imread(PATH, cv2.IMREAD_GRAYSCALE)
plt.imshow(img, cmap='gray')
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20, param1=130, param2=30, minRadius=0, maxRadius=0)
if circles is not None:
for x, y, r in circles[0]:
c = plt.Circle((x, y), r, fill=False, lw=3, ec='C1')
plt.gca().add_patch(c)
plt.gcf().set_size_inches((12, 8))
plt.show()
yields the result
What do the different parameters mean?
The function signature is defined as
cv.HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]])
image and circles are self explanatory and will be skipped.
method
Specifies the variant of the hough algorithm that is used internally. As stated in the documentation, only HOUGH_GRADIENT is support atm. This method utilizes the 21HT (p.2, THE 2-1 HOUGH TRANSFORM) algorithm. The major advantage of this variant lies in the reduction of memory usage. The standard way of detecting circles using hough transform requires a search in a 3D hough space (x, y and radius). However, with 21HT your hough space is reduced to only 2 dimensions, which lowers the memory consumption by a fair amount.
dp
The dp parameter sets the inverse accumulator resolution. A good explanation can be found here. Note that this explanation uses the standard hough transform as an example. But the effect is the same for 21HT. The accumulator for 21HT is just a bit different as for the standard HT.
minDist
Simply specifies the minimum distance between the centers of circles. In the code example above it's set to 20 which means that the centers of two detected circles have to be at least 20 pixels away from each other. I'm not sure how opencv filters the circles out, but scanning the source code it looks like circles with lower matches are thrown out.
param1
Specifies the thresholds that are passed to the Canny Edge algorithm. Basically it's called like cv2.Canny(image, param1 / 2, param1).
param2
This paragraph should probably be validated by someone who is more familiar with the opencv source code.
param2 specifies the accumulator threshold. This value decides how complete a circle has to be in order to count as a valid circle. I'm not sure in which unit the parameter is given, tho. But (scanning the source code again) it looks like it is an absolute vote threshold (meaning that it is directly affected by different radii).
The image below shows different circles (or what can be identified as a circle). The further you go to the right, the lower the threshold has to be in order to detect that circle.
minRadius and maxRadius
Simply limits the circle search in the range [minRadius, maxRadius] for the radius. This is useful (and can increase performance) if you can approximate (or know) the size of the circles you are searching for.
How to classify objects on the basis of shape and size using machine learning?
Say I've an circle and some small dotted squares in an image. The difference between the two is their shape and size. So given an image, how to distinguish between these objects and return the result.
In the actual problem those objects are hot spots in a solar PV folder, which are defected parts of it. I need to classify them.The I/P image is as:
[This is less or more a square type of hot spot:]
https://i.stack.imgur.com/4JL7E.png
This answer doesn't ellaborate machine learning or any approach using classifiers
The circles can be detected by the Hough Circle
Transform
from OpenCV cv2.HoughCircles():
Documentation for Hough Circles in OpenCV
Note: By using the radius you can tune the shape size of the circles you want to detect. And to be honest I didn't really get what dotted squares are, maybe you could show an exemplary image in your question.
If there are only two different kinds of objects in the image you probably don't even need a classifier, because the two classes are already being separated by the subsequent image processing (that though depends highly on your input images).
import cv2
import numpy as np
img = cv2.imread('opencv_logo.png',0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
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,0,255),3)
cv2.imshow('detected circles',cimg)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result of the code is as follows
My goal is to detect the fractured bone using open cv. I tried the following code. and got correct canny detection edges.and also found houghlines.But now my job is to detected the fractured spot in the image. Iam not understanding how to proceed further. In some blogs i found the we can determine angle of houghlines to detect that line is not staright. But not knowing how to find angle of houghline in my code. Can someone help?
import cv2
import numpy as np
import math
img = cv2.imread('bone2.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,100,400,apertureSize =3)
cv2.imshow('canny',edges)
kernel=np.ones((5,5),np.uint8)
boneEdges=cv2.morphologyEx(edges,cv2.MORPH_GRADIENT,kernel)
minLineLength =1000
maxLineGap = 10
lines = cv2.HoughLinesP(boneEdges,1,np.pi/180,100,minLineLength,maxLineGap)
for x1,y1,x2,y2 in lines[0]:
cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
slope=(y2-y1),(x2-x1)
# print('slope',arctan(slope))
cv2.imwrite('houghlines5.jpg',img)
cv2.waitKey(0)
This function returns a list of rho theta sets where theta is the angle you are looking for. Here are the docs.
I've read a lot about the Circular Hough transform on Stack Overflow, but I seem to be missing something. I wrote a program that is supposed to detect the circles of a "Bull's Eye" target. However, even after playing with the parameters, the algorithm is quite bad - it ignores most of the circles and one time it finds a circle but seems to "wander off". I've even tried applying an "Unsharp Mask" to no avail. I have added my code, the image I started with and the output. I hope someone can point me at the right direction.
import cv2
import cv2.cv as cv
import numpy as np
import math
# Load Image
img = cv2.imread('circles1.png',0)
# Apply Unsharp Mask
tmp = cv2.medianBlur(img,5)
img = cv2.addWeighted(img,1.5,tmp,-0.5,0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
# Hough Transform
circles = cv2.HoughCircles(img,cv.CV_HOUGH_GRADIENT,1,5,
param1=100,param2=100,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
# Go over circles, eliminating the ones that are not cocentric enough
height, width = img.shape
center = (width/2,height/2)
for i in circles[0,:]:
# draw the outer circle
if math.sqrt((center[0]-i[0])**2 + (center[1]-i[1])**2) < 15:
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),1)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow('detected circles',cimg)
cv2.waitKey(0)
cv2.destroyAllWindows()
Quick explanation: I load the image, apply Unsharp Mask, use the Hough Transfrom to detect circles, then draw the circles that are close to the center (I found that the other circles are false circles).
I tried playing with the parameters, and this is the best I got. I feel like this is a simple enough problem which has me buffled. I appriciate any help.
My input image:
My output image:
As I mentioned in my comment, you'll need to run successive iterations of cv2.HoughCircles for different range of radii to ensure that you get all of the circles. With the way the Circular Hough Transform works, specifying a minimum and maximum radius that has quite a large range will be inaccurate and will also be slow. They don't tell you this in the documentation, but for the Circular Hough Transform to work successfully, the following two things need to be valid:
maxRadius < 3*minRadius
maxRadius - minRadius < 100
With the above, blindly making the minimum radius very small and the maximum radius very large won't give you great results. Therefore, what you could do is start at... say...radius=1, then iterate up to radius=300 in steps of 20. Between each chunk of 20, run cv2.HoughCircles and update your image with these contours.
Doing this requires very little modification to your code. BTW, I removed the unsharp masking it because I was getting poor results with it. I also changed a couple of parameters in cv2.HoughCircles slightly to get this to work as best as possible given your situation:
import cv2
import cv2.cv as cv
import numpy as np
import math
# Load Image
img = cv2.imread('circles1.png',0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
# Specify different radii
radii = np.arange(0,310,10)
# For each pair of radii...
for idx in range(len(radii)-1):
# Get the minimum and maximum radius
# Note you need to add 1 to each minimum
# as the maximum of the previous pair covers this new minimum
minRadius = radii[idx]+1
maxRadius = radii[idx+1]
# Hough Transform - Change here
circles = cv2.HoughCircles(img,cv.CV_HOUGH_GRADIENT,1,5,
param1=25,param2=75,minRadius=minRadius,maxRadius=maxRadius)
# Skip if no circles are detected - Change here
if circles is None:
continue
circles = np.uint16(np.around(circles))
# Go over circles, eliminating the ones that are not cocentric enough
height, width = img.shape
center = (width/2,height/2)
for i in circles[0,:]:
# draw the outer circle
if math.sqrt((center[0]-i[0])**2 + (center[1]-i[1])**2) < 15:
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),1)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow('detected circles',cimg)
cv2.waitKey(0)
cv2.destroyAllWindows()
I get this figure:
Unfortunately it isn't perfect as it doesn't detect all of the circles. You'll have to play around with the cv2.HoughCircles function until you get good results.
However, I wouldn't recommend using cv2.HoughCircles here. May I suggest using cv2.findContours instead? This finds all of the contours in the image. In this case, these will be the black circles. However, you need to reverse the image because cv2.findContours assumes non-zero pixels are object pixels, so we can subtract 255 from the image assuming a np.uint8 type:
# Make copy of original image
cimg2 = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# Find contours
contours,_ = cv2.findContours(255 - img, cv2.RETR_LIST, cv.CV_CHAIN_APPROX_NONE)
# Draw all detected contours on image in green with a thickness of 1 pixel
cv2.drawContours(cimg2, contours, -1, color=(0,255,0), thickness=1)
# Show the image
cv2.imshow('detected circles', cimg2)
cv2.waitKey(0)
cv2.destroyAllWindows()
This is what I get:
I'm trying to figure out Hough Circles before I incorporate it into my main code for a tracking program I'm trying to write, but I can't seem to get anything but None out from the circles. I'm using the Bengali flag as my image, since it's simple and will be easy to detect. Here's my code:
import numpy as np
import cv2
img = cv2.imread('Capture.PNG')
grayput = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(grayput, cv2.cv.CV_HOUGH_GRADIENT, 1, 20, param1 =50, param2 =10, minRadius=10, maxRadius=40)
print (circles)
# need circles
if circles is not None:
# convert the coord. 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
cv2.circle(img, (x, y), r, (0, 0, 0), 4)
cv2.imwrite("image.PNG",img)
Through trial and error, I was able to manipulate the param1 and param2 to where the cv2.HoughCircle output returns a numpy.ndarray. It seems it returned None if HoughCircle param1 and or param2 threshold is not met.
First off, and most critically, Hough works best (or maybe only) on images that have been converted to edges only. You could try a line this this, after converting to B&W but before calling HoughCircle:
edges = cv2.Canny(grayput, threshold1=10, threshold2=30).
Then pass 'edges' to HoughCircles rather than 'grayput'
I would urge you to also output this image to see it. Make sure you see nice clean circles. You may need to tune either your blur parameter, or these two 'edge' threshold parameters, threshold1 and threshold2. Note that it is recommended to keep the ratio of the two of those to 1:2 to 1:3, and the first always lower than the second.
Second, you can tune your HoughCircle call as well; the most critical parameter, imho, is parameter2, which is the accumulator threshold. Hough works by 'accumulating' votes for possible circles, and if your edge image is very lean (very skinny lines), then you should require fewer votes (lower accumulator threshold, parameter2) to trigger the detection of a circle. Make this too low and you'll see lots of spurious circles, so it takes a little experimentation, but these algos are great. You'll get it.
The following code will give you non-None circles:
import numpy as np
import cv2
img = cv2.imread("../images/opencv_logo.png", 0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
cv2.imshow("grayscale", cimg)
cv2.waitKey(0)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
print (circles)
Indeed, the output is:
[[[ 45.5 133.5 16.50757408]
[ 97.5 45.5 16.80773544]
[ 147.5 133.5 16.32482719]]]
Note: the snippet uses the following as its input image: