Find all circles in image - python

I am new to python & image processing. I am working on a hobby project in which I want to find ALL circles in image and then figure out which one have cross ('X') marked inside it. I have put some code together to find circles so far (below). It works on one image but fails to recognize all circles on another one. Please guide me how i can improve performance of find_circles algorithm.
Test Image:
Result Image:
import cv2
import cv
import numpy as np
import operator
from PIL import Image
def find_circles(img):
im_gray = cv2.imread(img, cv2.CV_LOAD_IMAGE_GRAYSCALE)
(thresh, im_bw) = cv2.threshold(im_gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
img_bw = cv2.threshold(im_gray, thresh, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('img_bw.png',img_bw)
rows, cols =img_bw.shape
circles = cv2.HoughCircles(img_bw,cv.CV_HOUGH_GRADIENT,1,rows/32, param1=100,param2=40,minRadius=0,maxRadius=100)
circles = np.uint16(np.around(circles))
return circles
def draw_circles(img, circles):
img = cv2.imread(img,0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
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.putText(cimg,str(i[0])+str(',')+str(i[1]), (i[0],i[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.4, 255)
return cimg
def main():
img = "query_circle9.png"
circles = find_circles(img)
img_circle = draw_circles(img,circles)
cv2.imwrite('cricle.png',img_circle)
if __name__=='__main__':
main()

#!/usr/bin/env python
import cv2
def draw_circles(img, circles):
# img = cv2.imread(img,0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
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.putText(cimg,str(i[0])+str(',')+str(i[1]), (i[0],i[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.4, 255)
return cimg
def detect_circles(image_path):
gray = cv2.imread(image_path, cv2.CV_LOAD_IMAGE_GRAYSCALE)
gray_blur = cv2.medianBlur(gray, 13) # Remove noise before laplacian
gray_lap = cv2.Laplacian(gray_blur, cv2.CV_8UC1, ksize=5)
dilate_lap = cv2.dilate(gray_lap, (3, 3)) # Fill in gaps from blurring. This helps to detect circles with broken edges.
# Furture remove noise introduced by laplacian. This removes false pos in space between the two groups of circles.
lap_blur = cv2.bilateralFilter(dilate_lap, 5, 9, 9)
# Fix the resolution to 16. This helps it find more circles. Also, set distance between circles to 55 by measuring dist in image.
# Minimum radius and max radius are also set by examining the image.
circles = cv2.HoughCircles(lap_blur, cv2.cv.CV_HOUGH_GRADIENT, 16, 55, param2=450, minRadius=20, maxRadius=40)
cimg = draw_circles(gray, circles)
print("{} circles detected.".format(circles[0].shape[0]))
# There are some false positives left in the regions containing the numbers.
# They can be filtered out based on their y-coordinates if your images are aligned to a canonical axis.
# I'll leave that to you.
return cimg
The result:
cimg = detect_circles("circles.png")
There are some left-over false detection. If your images are aligned, then you can filter these false positives based on their y-coordinates. I'll leave that to you.

Related

Opencv HoughCircles missing obvious circle

I have tried some variations in the parameters, read a detailed description of their meaning, but I can't seem to detect a simple circle in an image. This is a simplified function I have tried:
def get_a_circles(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, # input grayscale image
cv2.HOUGH_GRADIENT,
2,
minDist=5,
param1=10, param2=200,
minRadius=0,
maxRadius=0)
return circles
which, when run on this image:
img = cv2.imread("step2.jpg")
get_a_circle(img.copy())
returns none.
It does however detect the circles in this image:
image
circles found & highlighted
I tried to add some blur to the image that fails, with either
gray = cv2.medianBlur(gray, 5) or gray = cv2.GaussianBlur(gray,(5,5),0) but it does not improve the results.
Any suggestions on what to try with that image? (It would seem it's an obvious circle)
You need to lower param2 in HoughCircles in Python/OpenCV.
Input:
import cv2
import numpy as np
# Read image
img = cv2.imread('dot.jpg')
hh, ww = img.shape[:2]
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# median filter
gray = cv2.medianBlur(gray, 5)
# get Hough circles
min_dist = int(ww/20)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=150, param2=10, 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, 0, 255), 1)
# save results
cv2.imwrite('dot_circle.jpg', result)
# show images
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

HoughCircles circle detection not working?

I have been trying to write a program that can detect circles on my screen.
This is my screen before code processing
As you can see on the image, there are three circles that the code should detect. I am using HoughCircles function from OpenCV library to achieve this task. My code is below.
ss = gui.screenshot()
img = cv2.cvtColor(np.array(ss), cv2.COLOR_RGB2BGR)
output = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)
if circles is not None:
print("circles found", len(circles))
circles = np.round(circles[0, :]).astype("int")
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)
cv2.imshow("output", np.hstack([gray, output]))
cv2.waitKey(0)
cv2.imshow("output", gray)
cv2.waitKey(0)
I am first taking screenshot of my screen. Then, I convert it to use it for opencv.
However, this code does not detect any circles for the screenshot shown in the first picture. I know this because when ran, my program does not print "circles found". Moreover, to show that I have been taking screenshots and transforming them to grayscale properly, I have this image taken from the last two lines of my code.
picture in a gray scale
To show that my code works with other circle images, here is a picture of a regular circle:
before detection
after detection
Any help would be very appreciated!
Here's an alternative solution to detect the circles without using the Hough Transform. As your input image has a very distinct blue hue to the blobs of interest, you can try to create a segmentation mask based on their HSV values. Then, detect contours and approximate each contour using a circle. The last step can be implemented using the cv2.minEnclosingCircle, which, as its name suggest, can compute the Minimum Enclosing Circle of a contour.
Let's see the code:
# image path
path = "D://opencvImages//"
fileName = "XUzFw.png"
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Create a deep copy of the input for results:
inputImageCopy = inputImage.copy()
# Convert the image to the HSV color space:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)
# Set the HSV values:
lowRange = np.array([78, 0, 158])
uppRange = np.array([125, 255, 255])
# Create the HSV mask
mask = cv2.inRange(hsvImage, lowRange, uppRange)
This generates the following segmentation mask:
As you can see, the only blobs that remain are the circles. Now, let's compute the contours and find the minimum enclosing circle:
# Find the circle blobs on the binary mask:
contours, hierarchy = cv2.findContours(mask, 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)
# 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])
# Let's see the results:
cv2.namedWindow("Circles", cv2.WINDOW_NORMAL)
cv2.imshow("Circles", inputImageCopy)
cv2.waitKey(0)
This is the result of the detection:
Additionally, you can check out the data stored in the detectedCircles list:
# Check out the detected circles:
for i in range(len(detectedCircles)):
# Get circle data:
center, r = detectedCircles[i]
# Print it:
print("i: "+str(i)+" x: "+str(center[0])+" y: "+str(center[1])+" r: "+str(r))
Which yields:
i: 0 x: 395 y: 391 r: 35
i: 1 x: 221 y: 391 r: 36
i: 2 x: 567 y: 304 r: 35
These are the parameters of houghCircles that works for me. You should also consider running a gaussian blur over the image before trying to find the circles.
I'm not a huge fan of houghCircles. I find it to be really finicky and I don't like how much of what it does is hidden inside the function. It makes tuning it mostly trial-and-error. These parameters work for this particular image, but I wouldn't count on this continuing to work under different lighting conditions or for different colors.
import cv2
import numpy as np
# load image
img = cv2.imread("spheres.png");
# grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
gray = cv2.GaussianBlur(gray,(5,5),0);
# circles
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp = 1, minDist = 100, param1=65, param2=20, minRadius=20, maxRadius=50)
# draw circles
if circles is not None:
# round to ints
circles = np.uint16(np.around(circles));
for circle in circles[0, :]:
# unpack and draw
x, y, radius = circle;
center = (x,y);
cv2.circle(img, center, radius, (255, 0, 255), 3);
# show
cv2.imshow("Image", img);
cv2.imshow("Gray", gray);
cv2.waitKey(0);

Why is OpenCV creating weird rectangles?

I am making a software to track the changes in the areas of different bubbles over the span of a video. I am trying to use the OpenCV multitracker to track these circles, which were created by a Hough's Circle Transformation. My understanding is, I need to define a bounding box to pass to this multitracker tool. So I made the box with basic geometry, but it gave a weird result when I was displaying the box, through the code seen below:
import cv2
import numpy as np
import matplotlib.pyplot as plt
vidObj = cv2.VideoCapture('Data/video.mp4')
success = True
tracked = False
multiTracker = cv2.MultiTracker_create()
def Find_Circles(img):
# Convert to gray-scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Blur the image to reduce noise
img_blur = cv2.bilateralFilter(gray, 7, 50, 50)
# Apply hough transform on the image
circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, 70, param1=110, param2=10, minRadius=20, maxRadius=100)
return circles
def Draw_Circles(img,circles,tracked):
if circles is not None:
circles = np.uint16(np.around(circles))
if tracked is False:
for i in circles[0, :]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0),2)
cv2.rectangle(img, (i[0]-i[2], i[1]-i[2]), (i[0]+i[2], i[1]+i[2]), (0, 255, 0),2)
else:
(success, circles) = multiTracker.update(img)
print(circles)
while success:
success, img = vidObj.read()
if img is not None:
Draw_Circles(img, Find_Circles(img),tracked)
tracked = True
imgplot = plt.imshow(img,cmap='gray')
plt.show()
else:
success = False
Here is the result. Any ideas why? Can provide video in mp4 format if needed.
This line causes error:
circles = np.uint16(np.around(circles))
Values in "circles" can be negative, so you can not use uint16 (unsigned integer) type. Use int16:
circles = np.int16(np.around(circles))

I try to find a detect array of circles using findCirclesgrid in open cv, but i doesn't work

I try to detect array of circles in below picture.
Luckily, I found a python code in the internet. However, It doesn't work and I have no idea what the problems are. Please let me know the fine solutions and introduce the simple method to detect all the circles array in a picture.
This is the code I found in the internet
import numpy as np
import cv2 as cv
img = cv.imread('circles0.png')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, corners = cv.findCirclesGrid(gray, (4,9), None, flags = cv.CALIB_CB_ASYMMETRIC_GRID)
if ret == True:
corners2 = corners
# corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001))
cv.drawChessboardCorners(img, (4,9), corners2, ret)
cv.imwrite('output.jpg', img)
cv.imshow('img', img)
cv.waitKey(0)
The code you are using is looking for a rectilinear grid of circles, 4 rows by 9 columns.
The image your using doesnt match that criteria.
To find circles anywhere in an image Hough Circle Transform is probably your best bet for your image.
Try the following & be aware that the parameters in cv2.HoughCircles() is very image sensitive.
import cv2
import numpy as np
img = cv2.imread('circles0.png',0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 70,
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)
Ouput,

HoughCircles not working properly

I would like to know the radius of the red circle on the dart board (where you can score 50).
I converted to HSV and than defined the red color range.
But when I tried to detect circles on my mask image I didn't get the results what I expected. I tried to play with the attributes but I couldn't find a working combination.
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('testImages/testImage3_6.jpg')
if img is None:
print "There is no image with this name! (maybe typo)"
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([0,50,50])
upper_red = np.array([10,255,255])
mask = cv2.inRange(hsv, lower_red, upper_red)
result = cv2.bitwise_and(img.copy(),img.copy(), mask= mask)
edges = cv2.Canny(mask,1000,1500)
maskSmoothed = cv2.medianBlur(mask,5)
circles = cv2.HoughCircles(maskSmoothed,cv2.cv.CV_HOUGH_GRADIENT,1,50,
param1=20,param2=20,minRadius=0,maxRadius=0)
output = img
cmask = cv2.cvtColor(maskSmoothed, cv2.COLOR_GRAY2BGR)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
cv2.circle(cmask,(i[0],i[1]),i[2],(0,255,0),1) # draw the outer circle
cv2.circle(cmask,(i[0],i[1]),2,(0,0,255),3) # draw the center of the circle
cv2.imshow('win', cmask)
cv2.waitKey(0)
Original image
Image about the detected mask and edges
Detected circles on the mask
How could I detect it precisely? I need the radius.

Categories