I am following this tutorial: https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/
I was playing around with the parameters ( even those you don't see in the code ex: param2) of HoughCircles and it seems very innacurate, in my project, the disks you see on the picture will be placed on random spots and i need to be able to detect them and their color.
Currently i am only able to detect few circles, and sometimes some random circles are drawn where there is no circles so i am a bit confused.
Is this the best way to do circle detection with openCV or is there a more accurate way of doing it ?
Also why is my code not detecting every circles ?
Initial board : https://imgur.com/BrPB5Ox
Circle drawn : https://imgur.com/dT7k29E
My code :
import cv2
import numpy as np
img = cv2.imread('Photos/board.jpg')
output = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 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), 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([img, output]))
cv2.waitKey(0)
Thanks a lot.
First of all you can not expect HoughCircles to detect all circles in different type of situations. It is not an AI. It has different parameters according to get desired results. You can check here to learn more about those parameters.
HoughCircles is a contour based function so you should be sure the contours are being detected properly. In your example I am sure bad contour results will come up because of the lighting problem. Metal materials cause light explosion in image processing and this affects finding contours badly.
What you should do:
Solve the lighting problem
Be sure about the HoughCircle parameters to get desired output
Instead of using HoughCircle you can detect each contour and their mass center ( moments help you to find their mass center). Then you can measure each length of contour points to that mass center if all equal then its a circle.
Hough transform works best on monochromatic/binary image, so you may want to preprocess it with some sort of threshold function. Parameter values for the function are very important for proper recognition.
Is this the best way to do circle detection with openCV or is there a more accurate way of doing it ? Also why is my code not detecting every circles ?
there's also findContours function
https://docs.opencv.org/master/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0
which, to my liking, is more robust and general; you may want to give it a try
Related
I need to draw thousands of dots on a given area of an image (frame of a video).
Using a loop is the easiest way to do this.
while i < num:
x = random.randint(min_x, max_x)
y = random.randint(min_y, max_y)
//this if is to check the the random points are within the original shape
if cv2.pointPolygonTest(contour, (int(x), int(y)), False)==1:
cv2.circle(img, (int(x), int(y)), 2, color, -1)
i = i+1;
this process takes a long time to complete.
how can I achieve this more efficiently?
Maybe it's possible to speed it up using several tricks:
Try to get rid of the loop and vectorize your operations.
You can vectorize the (x, y) point generation by passing size to random.randint. If after filtering they don't amount to num, you can generate another set.
Instead of pointPolygonTest, maybe you can try matplotlib.path.Path.contains_points that operates on a vector of points rather than a single point.
For the circle, once you filter all your circle centers, create an all zeros image to draw your circles, then mark centers in this image (again, vectorize). If you want color circles you'll have to set the pixel value to appropriate gray level for each channel. Then dilate using a circular structuring element having the required radius. For small radii, these circles should be fine. Or, you can try Gaussian blur as well, because, if you convolve a Gaussian with an impulse, it'll give you a Gaussian, and a symmetric Gaussian will resemble a circle in the image. You can also use filter2D to do this if your circles have the same size. If the dilation result isn't good, you can create your own kernel resembling the circle you want, then convolve it with centers image.
Copy all non-zero pixels from this circles image to your img using the circles image as a mask.
A simple example for creating circles:
import numpy as np
import cv2 as cv
# create random centers for circles
img = np.random.randint(low=0, high=1000, size=(256, 256))
img = np.uint8(img < 1) * 255
# use Gaussian bluer to create cricles
img1 = cv.GaussianBlur(img, (9, 9), 3)*20
# use morphological dilation bluer to create cricles
se = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))
img2 = cv.dilate(img, se)
Centers:
Circles using dilation:
Circles using Gaussian blur:
as the title states, I'm trying to crop the largest circle out of an image. I'm using OpenCV in python. To be exact, it's a shooting target, which always has the same format, but the picture of it can be taken with any mobile device and in different lighting conditions (I will include some examples lower).
I'm completely new to image recognition, so I have been trying out many different ways of doing this, but couldn't figure out a universal solution, that would work on all of my target images.
Why I'm trying to do this:
My assignment is to calculate score of one or multiple shots on the given target image. I have tried color segmentation to find the shots, but since the shots can be on different backgrounds, this wouldn't work properly. So now I'm trying to see the difference between the empty shooting target image and the already shot on target image. Also, I need to be able to tell, which target it was shot on (there are two target types). So I'm trying to crop out only the target from image to get rid of the background interference and then continue with the shot identifications.
What I have tried so far:
1) Finding the largest circle with HoughCircles. My next step would be to somehow remove the outer part of that found circle. I have played with the configuration of HoughCircles method for quite some time, but always one of the example images wasn't highlighting the most outer circle correctly or wasn't highlighting any of the circles :/.
My final configuration looked something like this:
img = cv2.GaussianBlur(img, (3, 3), 0)
cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 2, 10000, param1=50, param2=100, minRadius=200, maxRadius=0)
It seemed like using HoughCircles wouldn't be the right way to do this, so I moved on to another possible solution I found on the internet.
2) Finding all the countours by filtering the 'black' color range in which the circles seem to be on the pictures and than finding the largest one. The problem with this solution seemed to be that sometimes the pictures had a shadow that destroyed the outer circle and therefore it seemed impossible to crop by it.
My code looked like this:
# black color boundaries [B, G, R]
lower = [0, 0, 0]
upper = [150, 150, 150]
# create NumPy arrays from the boundaries
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")
# find the colors within the specified boundaries and apply the mask
mask = cv2.inRange(img, lower, upper)
output = cv2.bitwise_and(img, img, mask=mask)
ret, thresh = cv2.threshold(mask, 40, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
if len(contours) != 0:
# draw in blue the contours that were founded
cv2.drawContours(output, contours, -1, 255, 3)
# find the biggest countour (c) by the area
c = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(c)
After that, I would try to draw a circle by the largest found contour (c) and crop by it. But I have already seen that the drawn circles weren't complete (probably due to some shadow on the picture) and therefore this wouldn't work anyway.
After those failures, I have tried so many solutions from other questions on here, but none would work for my problem.
Example images:
Target example 1
Target example 2
Target to calc score 1
Target to calc score 2
To be completely honest with you, I'm really lost on how to go about this. I would appreciate any help, advice, anything.
There are two different types of target in your samples. You may want to process them separately or ask the user for the input, what kind of target it is. Basically, you want to know how large the black part of the target, does it cover 7-10 or 4-10.
Binarize your image. Build a histogram along X and Y -- you'll find the position of the black part of your target as (x_left, x_right, y_top, y_bottom). Once you know that, you can calculate the center ((top+bottom)/2, (left+right)/2). After that you can easily calculate the score for every pixel of the image, since you know the center, the black spot size and the number of different score areas within.
I am trying to find the corners of the 4 pillars which are of yellow in colour and also detecting extreme corners of the board which is of white in colour.
Basically i want to calculate the area of whole space after subtracting the area of each pillar.
For that first am trying to identifying the corner of pillars to find the area of each pillar.
Here is the code which I tried, I am almost half way through it.
import numpy as np
import cv2
img = cv2.imread('Corner_0.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
corners = cv2.goodFeaturesToTrack(gray, 100, 0.01, 10)
corners = np.int0(corners)
for corner in corners:
x,y = corner.ravel()
cv2.circle(img,(x,y),3,255,-1)
cv2.imwrite('Detected_Corner_0.jpg',img)
I would like to detect corner and calculating the area of the pillar.
When I use Grabcut I am able to apply for one pillar, does this make sense?
Corner detectors often cannot be relied on. The show extra corners and miss the ones you expect. What's more, you have to identify an regroup them.
You obtain interesting results by computing a saturation image (S in LSH). Then by binarization and blob analysis, you can easily find the areas.
Using OpenCV to identify the iris region + pupil region (outer grey area + inner black circle) as seen in this image
Tried the following approaches, but unable to extract the iris region 100%.
Approach 1
Iris area detection using detection of color code of the pixels in the image
import cv2
from PIL import Image
#import cv2.cv as cv
img = cv2.imread('i1.jpg')
im = Image.open('i1.jpg')
pix = im.load()
#cv2.imshow('detected Edge',img)
height, width = img.shape[:2]
print height,width
height=height-1
width=width-1
count=0
print pix[width,height]
print pix[0,0]
for eh in range(height):
for ew in range(width):
r,g,b=pix[ew,eh]
if r<=30 and g<=30 and b<=30:
print eh,ew
cv2.circle(img,(ew,eh),1,(0,255,0),1)
print height,width
cv2.imshow('detected Edge',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Click here to view output of the above code.
Approach 2
Iris area detection using Hough Circles method
import cv2
#import cv2.cv as cv
img1 = cv2.imread('i.jpg')
img = cv2.imread('i.jpg',0)
ret, thresh = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY)
edges = cv2.Canny(thresh, 100, 200)
#cv2.imshow('detected ',edges)
cimg=cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 10000, param1 = 50, param2 = 30, minRadius = 0, maxRadius = 0)
#circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
# param1=50,param2=30,minRadius=0,maxRadius=0)
print circles
for i in circles[0,:]:
i[2]=i[2]+4
cv2.circle(img1,(i[0],i[1]),i[2],(0,255,0),1)
#Code to close Window
cv2.imshow('detected Edge',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
for i in range (1,5):
cv2.waitKey(1)
Click here to view the output of the code.
Kindly guide us how we can automatically extract the circular black area in human eye pictures.
I used the following reference.
http://www.cvip.uofl.edu/wwwcvip/education/ECE523/Iris%20Biometrics.pdf
To identify iris region in human eye images, you can use the following steps:-
1) Identification of pupil region:- As pupil region intensity would be very close to zero, you can use binary threshold to find pupil region.You can use connected components labelling to get regions of same intensity and then select region having eccentricity near to zero to be identified as pupil circle.The centroid of this connected region would be circle's centre and you can get the radius by the dimensions of connected components box.
2) Identification of Iris region:- Now that you have got your pupil region,you can use hough circle method to get iris region.Use canny edge detection to get edge map.Take the centre of the iris circle in a box around pupil centre and radius of the iris more than pupil radius and less than a fixed amount.Make multiple circles with varying centre and varying radius as specified above and count the number of edge map points lying on such circles.The circle with maximum number of edge points lying on it would be iris circle.
Note:- In my experience, I had found that getting iris circle was very costly as you had to make multiple circles with varying centre and radius.One solution was to keep the circle centre fixed as pupil centre and only varying radius as iris circle would be very near to pupil centre. However, it was giving wrong result as the eyelash edge maps at top and bottom were giving wrong edge map points. To solve this, I did a jugaad. I kept the iris centre fixed as pupil centre and found iris radius only for left hand part of image from pupil centre. Similarly, I found iris radius for right hand side of the image from pupil centre. I used the average of both radius and centre as pupil centre to get iris boundary. It worked for me.
Using approach 2, could you start at the center of the pupil and then travel outwards staying in the same row (travel left or right of the pupil center) until you hit the sclera of the eye. Use this as the radius for the circle containing the iris.
radius_iris = abs(first_column_of_sclera - column_of_pupil_center)
#this is the yellow line in the attatched image
To Find the Sclera: take a small pixel region like a 3x3 block (or similar, this is the green box in the image) and check for two criteria
The variance of the r,g,b channels is small. White (or gray shades) have R=G=B so that means white would have low variance
You also need to check that the rgb value is above some threshold. Someone with grey or black eyes will meet criteria 1, but unless the pixels are very light (near white) we haven't reached the sclera
Create an iris mask by creating a circle centered at the pupil with radius_iris if you want, you can also use the pupil mask to extract ONLY THE iris
To avoid wrong results and improve performance you should always use proper boundaries for HoughCircles. Iris and pupil radii will be in a certain range.
I would look for a black blob of reasonable size in the image to locate the pupil. Once you know where the pupil is you know where to look for the iris. extract a region of interest that will contain the iris (use pupil size to estimate iris size) but not much more. Then do two hough transforms to get iris and pupil position and radius.
Afterwards you can further improve accuracy by fitting a circle/ellipse using the knowledge from your hough transform, if necessary.
I've been laboring on a pet project for a bit on how to find a simple basketball in an image. I've tried a bunch of permutations of using hough.circles and transform , etc for the last few weeks but I cant seem to come anywhere close to isolating the basketball with the code examples and my own tinkering.
Here is an example photo:
And here is the result after a simple version of circle finding code I've been tinkering with:
Anyone have any idea where I have gone wrong and how I can get it right?
Here is the the code I am fiddling with:
import cv2
import cv2.cv as cv # here
import numpy as np
def draw_circles(storage, output):
circles = np.asarray(storage)
for circle in circles:
Radius, x, y = int(circle[0][3]), int(circle[0][0]), int(circle[0][4])
cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)
orig = cv.LoadImage('basket.jpg')
processed = cv.LoadImage('basket.jpg',cv.CV_LOAD_IMAGE_GRAYSCALE)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
#use canny, as HoughCircles seems to prefer ring like circles to filled ones.
cv.Canny(processed, processed, 5, 70, 3)
#smooth to reduce noise a bit more
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, 550)
draw_circles(storage, orig)
cv.imwrite('found_basketball.jpg',orig)
I agree with the other posters, that using the colour of the basketball is a good approach. Here is some simple code that does that:
import cv2
import numpy as np
im = cv2.imread('../media/basketball.jpg')
# convert to HSV space
im_hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
# take only the orange, highly saturated, and bright parts
im_hsv = cv2.inRange(im_hsv, (7,180,180), (11,255,255))
# To show the detected orange parts:
im_orange = im.copy()
im_orange[im_hsv==0] = 0
# cv2.imshow('im_orange',im_orange)
# Perform opening to remove smaller elements
element = np.ones((5,5)).astype(np.uint8)
im_hsv = cv2.erode(im_hsv, element)
im_hsv = cv2.dilate(im_hsv, element)
points = np.dstack(np.where(im_hsv>0)).astype(np.float32)
# fit a bounding circle to the orange points
center, radius = cv2.minEnclosingCircle(points)
# draw this circle
cv2.circle(im, (int(center[1]), int(center[0])), int(radius), (255,0,0), thickness=3)
out = np.vstack([im_orange,im])
cv2.imwrite('out.png',out)
result:
I assume that:
Always one and only one basketball is present
The basketball is the principal orange item in the scene
With these assumptions, if we find anything the correct colour, we can assume its the ball and fit a circle to it. This way we don't do any circle detection at all.
As you can see in the upper image, there are some smaller orangey elements (from the shorts) which would mess up our ball radius estimate. The code uses an opening operation (erosion followed by dilation), to remove these. This works nicely for your example image. But for other images a different method might be better: using circle detection too, or contour shape, size, or if we are dealing with a video, we could track the ball position.
I ran this code (only modified for video) on a random short basketball video, and it worked surprisingly ok (not great.. but ok).
A few thoughts:
Filter by color first to simplify the image. If you're looking specifically for an orange basketball, you could eliminate a lot of other colors. I'd recommend using HSI color space instead of RGB, but in any case you should be able to exclude colors that are some distance in color 3-space from your trained basketball color.
Try substituting Sobel or some other kernel-based edge detector that doesn't rely on manual parameters. Display the edge image to see if it looks "right" to you.
Allow for weaker edges. In the grayscale image, the contrast between the basketball and the player's dark jersey is not as great as the difference between the white undershirt and the black jersey.
Hough may yield unexpected results if the object is only nominally circular in cross section, but is actually elongated or has noisy edges in the actual image. I usually write my own Hough algorithm and haven't touched the OpenCV implementation, so I'm not sure what parameter to change, but see if you can allow for fuzzier edges.
Maybe eliminate the smooth operation. In any case, try smooth before finding edges rather than the other way around.
Try to write your own rough Hough algorithm. Although a quickie implementation may not be as flexible as the OpenCV implementation, by getting your hands dirty you may stumble onto the source of the problem.