Opencv HoughCircles missing obvious circle - python

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:

Related

How to get the average RGB value inside circle with python?

I need help with this code. I want to get the RGB value of the circle only.
How do I return the RGB value of the circle?
prevCircle = None
dist = lambda x1,y1,x2,y2 : (x1-x2)**2+(y1-y2)**2
while True:
(grabbed, frame) = videoCapture.read()
grayFrame = cv.cvtColor(frame,cv.COLOR_BGR2GRAY)
blurFrame = cv.GaussianBlur(grayFrame,(11,11),0)
height, width, _ = frame.shape
circles = cv.HoughCircles(blurFrame ,cv.HOUGH_GRADIENT,1.2,50,param1=100,param2=30,minRadius=75,maxRadius=400)
if circles is not None:
circles = np.uint16(np.around(circles))
chosen=None
for i in circles[0 ,:]:
if chosen is None: chosen =i
if prevCircle is not None:
if dist(chosen[0],chosen[1],prevCircle[0],prevCircle[1]) <= dist(i[0],i[1],prevCircle[0],prevCircle[1]):chosen = i
cv.circle(frame,(chosen[0],chosen[1]),1,(0,100,100),3)
cv.circle(frame,(chosen[0],chosen[1]),chosen[2],(0,0,0),3)
prevCirlce = chosen
cv.imshow("Camera", frame)
Here is one way to do that in Python/OpenCV.
After getting the circle from HoughCircles, draw the circle as white filled on a black background as a mask. Then use cv2.mean() with the mask to get the channel colors inside the input image within the found circle.
Input:
import cv2
import numpy as np
# Read image
img = cv2.imread('yellow_circle.jpg')
hh, ww = img.shape[:2]
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# get Hough circles
min_dist = int(ww/10)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=150, param2=20, minRadius=0, maxRadius=0)
print("circles:", circles)
# draw circles
img_circle = img.copy()
mask = np.zeros_like(gray)
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)
r = int(r)
cv2.circle(img_circle, (x, y), r, (0, 0, 255), 2)
cv2.circle(mask, (x, y), r, 255, -1)
# get average color with mask
ave_color = cv2.mean(img, mask=mask)[:3]
print("average circle color:", ave_color)
# save results
cv2.imwrite('yellow_circle_circle.jpg', img_circle)
cv2.imwrite('yellow_circle_mask.jpg', mask)
# show images
cv2.imshow('circle', img_circle)
cv2.imshow('mask', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
Circle Found:
Mask Image:
Data:
circles: [[[596.5 516.5 367.1]]]
average circle color: (1.1791196817751013, 254.96112948094645, 254.88615376615763)

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);

cut out a specific part of an image with opencv in python

I have an image of an IC die and I want to cut out the marking in the center.The marking is always at this specific position above the circle at the bottom left.
The idea is to first find the circle position which I already accomplished with the hough circle transformation. Now I want to cut out the part where the marking is. It should ideally be a not a square or rectangle but something more like in the image:
This is a part of my code:
cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(morph_image, cv2.HOUGH_GRADIENT, 1.3, 20, param1=50, param2=25, minRadius=15,
maxRadius=19)
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# Zeichne äußeren Kreis
cv2.circle(cimg, (i[0], i[1]), i[2], (0, 255, 0), 2)
# Zeichne Kreiszentrum
cv2.circle(cimg, (i[0], i[1]), 2, (0, 0, 255), 3)
# Tupel mit x- und y-Koordinaten des Kreiszentrums
circle_center = (i[0], i[1])
print('Die Koordinaten des Kreiszentrums lauten: ', circle_center)
"""cv2.imshow('Kreis', cimg)
cv2.waitKey(0)
cv2.destroyAllWindows()"""
else:
circle_center = None
print('Kein Kreis gefunden')
"""cv2.imshow('Kein Kreis', cimg)
cv2.waitKey(0)
cv2.destroyAllWindows()"""
so my cicle center has the center position of my circle (e.g. (124, 370)). How can I cut out this part of the image automatically? Can I somehow crop it out? Ideally I would want to crop the marking out into another image to inspect it separately but the normal cropping approach with marking_img = img[y:y+h, x:x+w] wouldn't work I guess.
EDIT:
Here is the original image:
The output should be like the first image and if it is possible something like this:
So in the end I would want to have 2 images: One image with just the die without the marking and one image with just the marking
Here is one way in Python/OpenCV.
Read the image
Read the mask (separately created one time from your other image)
Convert the mask to gray and threshold it to binary, invert it and make it 3 channels
Get the center of the circle from your own code. (I have just measured it manually)
Set the expected x,y offsets of the bottom of the region of text from the center of the circle
Compute the expected top left corner of the mask from the center of the circle, the offsets and the height of the mask image
Put the mask into black image the size of the input at that location
Apply the new mask to the image to make the rest of the image black
Crop out the region of interest from the top left corner and the size of the original mask
OPTIONALLY, crop the original image
Save the results
Input image:
Prepared mask image:
import cv2
import numpy as np
# read image
img = cv2.imread('die.jpg')
ht, wd, cc = img.shape
# read mask as grayscale
mask = cv2.imread('die_mask.png', cv2.IMREAD_GRAYSCALE)
# threshold mask and invert
mask = cv2.threshold(mask,0,255,cv2.THRESH_BINARY)[1]
mask = 255 - mask
hh, ww = mask.shape
# make mask 3 channel
mask = cv2.merge([mask,mask,mask])
# set circle center
cx = 62
cy = 336
# offsets from circle center to bottom of region
dx = -20
dy = -27
# compute top left corner of mask using size of mask and center and offsets
left = cx + dx
top = cy + dy - hh
# put mask into black background image
mask2 = np.zeros_like(img)
mask2[top:top+hh, left:left+ww] = mask
# apply mask to image
img_masked = cv2.bitwise_and(img, mask2)
# crop region
img_masked_cropped = img_masked[top:top+hh, left:left+ww]
# ALTERNATE just crop input
img_cropped = img[top:top+hh, left:left+ww]
cv2.imshow('image', img)
cv2.imshow('mask', mask)
cv2.imshow('mask2', mask2)
cv2.imshow('masked image', img_masked)
cv2.imshow('masked cropped image', img_masked_cropped)
cv2.imshow('cropped image', img_cropped)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save results
cv2.imwrite('die_mask_inserted.jpg', mask2)
cv2.imwrite('die_masked_image.jpg', img_masked)
cv2.imwrite('die_masked_cropped.jpg', img_masked_cropped)
cv2.imwrite('die_cropped.jpg', img_cropped)
Mask inserted in black image:
Masked image:
Crop of masked image:
(Optional) Crop of input image:

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.

Find all circles in image

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.

Categories