I try to recognize two areas in the following image. The area inside the inner and the area between the outer and inner - the border - circle with python openCV.
I tried different approaches like:
Detecting circles images using opencv hough circles
Find and draw contours using opencv python
That does not fit very well.
Is this even possible with classical image processing or do I need some neuronal networking?
Edit: Detecting circles images using opencv hough circles
# import the necessary packages
import numpy as np
import argparse
import cv2
from PIL import Image
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread(args["image"])
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 500)
# 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
img = Image.fromarray(image)
if img.height > 1500:
imS = cv2.resize(np.hstack([image, output]), (round((img.width * 2) / 3), round(img.height / 3)))
else:
imS = np.hstack([image, output])
# Resize image
cv2.imshow("gray", gray)
cv2.imshow("output", imS)
cv2.waitKey(0)
else:
print("No circle detected")
Testimage:
General mistake: While using HoughCircles() , the parameters should be chosen appropriately. I see that you are only using first 4 parameters in your code. Ypu can check here to get a good idea about those parameters.
Experienced idea: While using HoughCircles , I noticed that if 2 centers of 2 circles are same or almost close to each other, HoughCircles cant detect them. Even if you assign min_dist parameter to a small value. In your case, the center of circles also same.
My suggestion: I will attach the appropriate parameters with the code for both circles. I couldnt find 2 circles with one parameter list because of the problem I explained above. My suggestion is that apply these two parameters double time for the same image and just get the circles and get the result.
For outer circle result and parameters included code:
Result:
# import the necessary packages
import numpy as np
import argparse
import cv2
from PIL import Image
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread('image.jpg')
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray,15)
rows = gray.shape[0]
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT,1, rows / 8,
param1=100, param2=30,
minRadius=200, maxRadius=260)
# 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
img = Image.fromarray(image)
if img.height > 1500:
imS = cv2.resize(np.hstack([image, output]), (round((img.width * 2) / 3), round(img.height / 3)))
else:
imS = np.hstack([image, output])
# Resize image
cv2.imshow("gray", gray)
cv2.imshow("output", imS)
cv2.waitKey(0)
else:
print("No circle detected")
For inner circle the parameters:
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT,1, rows / 8,
param1=100, param2=30,
minRadius=100, maxRadius=200)
Result:
Related
is it possible to get the coordinates of the incomplete circle? i am using opencv and python. so i can find the most of the circles.
But i have no clue how can i detect the incomplete cirlce in the picture.
I am looking for a simple way to solve it.
import sys
import cv2 as cv
import numpy as np
## [load]
default_file = 'captcha2.png'
# Loads an image
src = cv.imread(cv.samples.findFile(default_file), cv.IMREAD_COLOR)
## [convert_to_gray]
# Convert it to gray
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
## [convert_to_gray]
## [reduce_noise]
# Reduce the noise to avoid false circle detection
gray = cv.medianBlur(gray, 3)
## [reduce_noise]
## [houghcircles]
#rows = gray.shape[0]
circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, 5,
param1=1, param2=35,
minRadius=1, maxRadius=30)
## [houghcircles]
## [draw]
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
center = (i[0], i[1])
# circle center
cv.circle(src, center, 1, (0, 100, 100), 3)
# circle outline
radius = i[2]
cv.circle(src, center, radius, (255, 0, 255), 3)
## [draw]
## [display]
cv.imshow("detected circles", src)
cv.waitKey(0)
## [display]
Hi - there is an other Picture. I want the x and y cords of the incomplete circle, light blue on the lower left.
Here the original Pic:
You need to remove the colorful background of your image and display only circles.
One approach is:
Get the binary mask of the input image
Apply Hough Circle to detect the circles
Binary mask:
Using the binary mask, we will detect the circles:
Code:
# Load the libraries
import cv2
import numpy as np
# Load the image
img = cv2.imread("r5lcN.png")
# Copy the input image
out = img.copy()
# Convert to the HSV color space
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Get binary mask
msk = cv2.inRange(hsv, np.array([0, 0, 130]), np.array([179, 255, 255]))
# Detect circles in the image
crc = cv2.HoughCircles(msk, cv2.HOUGH_GRADIENT, 1, 10, param1=50, param2=25, minRadius=0, maxRadius=0)
# Ensure circles were found
if crc is not None:
# Convert the coordinates and radius of the circles to integers
crc = np.round(crc[0, :]).astype("int")
# For each (x, y) coordinates and radius of the circles
for (x, y, r) in crc:
# Draw the circle
cv2.circle(out, (x, y), r, (0, 255, 0), 4)
# Print coordinates
print("x:{}, y:{}".format(x, y))
# Display
cv2.imshow("out", np.hstack([img, out]))
cv2.waitKey(0)
Output:
x:178, y:60
x:128, y:22
x:248, y:20
x:378, y:52
x:280, y:60
x:294, y:46
x:250, y:44
x:150, y:62
Explanation
We have three chance for finding the thresholding:
Simple Threshold result:
Adaptive Threshold
Binary mask
As we can see the third option gave us a suitable result. Of course, you could get the desired result with other options, but it might take a long time for finding the suitable parameters. Then we applied Hough circles, played with parameter values, and got the desired result.
Update
For the second uploaded image, you can detect the semi-circle by reducing the first and second parameters of the Hough circle.
crc = cv2.HoughCircles(msk, cv2.HOUGH_GRADIENT, 1, 10, param1=10, param2=15, minRadius=0, maxRadius=0)
Replacing the above line in the main code will result in:
Console result
x:238, y:38
x:56, y:30
x:44, y:62
x:208, y:26
I'm trying to extract the detected circles in one image using the circular hough transform. My idea is get every circle or separate each one to then get his color histogram features and after send this features to one classifier as SVM, ANN, KNN etc..
This is my input image:
I'm getting the circles of this way:
import numpy as np
import cv2
import matplotlib.pyplot as plt
cv2.__version__
#read image
file = "lemon.png"
image = cv2.imread(file)
#BGR to RGB
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
#convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray,
cv2.HOUGH_GRADIENT,
15,
41,
param1=31,
param2=31,
minRadius=0,
maxRadius=33)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(image,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(image,(i[0],i[1]),2,(0,0,255),3)
print("Number of circles: "+ str(len(circles[0,:])))
plt.imshow(image, cmap='gray', vmin=0, vmax=255)
plt.show()
Output:
The next step is try to extract those circles but I don't have idea how to do it.
Well guys I would like to see your suggestions, any I idea I will apreciate it.
Thanks so much.
You can create a binary mask for every circle you detect. Use this mask to extract only the ROIs from the input image. Additionally, you can crop these ROIs and store them in a list to pass them to your classifier.
Here's the code:
import numpy as np
import cv2
# image path
path = "C://opencvImages//"
file = path + "LLfN7.png"
image = cv2.imread(file)
# Convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray,
cv2.HOUGH_GRADIENT,
15,
41,
param1=31,
param2=31,
minRadius=0,
maxRadius=33)
# Here are your circles:
circles = np.uint16(np.around(circles))
# Get input size:
dimensions = image.shape
# height, width
height = image.shape[0]
width = image.shape[1]
# Prepare a list to store each ROI:
lemonROIs = []
The idea is that you process one circle at a step. Get the current circle, create a mask, mask the original input, crop the ROI and store it inside the list:
for i in circles[0, :]:
# Prepare a black canvas:
canvas = np.zeros((height, width))
# Draw the outer circle:
color = (255, 255, 255)
thickness = -1
centerX = i[0]
centerY = i[1]
radius = i[2]
cv2.circle(canvas, (centerX, centerY), radius, color, thickness)
# Create a copy of the input and mask input:
imageCopy = image.copy()
imageCopy[canvas == 0] = (0, 0, 0)
# Crop the roi:
x = centerX - radius
y = centerY - radius
h = 2 * radius
w = 2 * radius
croppedImg = imageCopy[y:y + h, x:x + w]
# Store the ROI:
lemonROIs.append(croppedImg)
For each circle you get a cropped ROI:
You can pass that info to your classifier.
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 am trying to detect the count of water pipes in this picture. For this, I am trying to use OpenCV and Python-based detection. The results, I am getting is a little confusing to me because the spread of circles is way too large and inaccurate.
The code
import numpy as np
import argparse
import cv2
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread(args["image"])
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#detect circles in the image
#circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, param1=40,minRadius=10,maxRadius=35)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 8.5,70,minRadius=0,maxRadius=70)
#print(len(circles[0][0]))
# 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")
# count = count+1
# print(count)
# 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([output]))
cv2.imwrite('output.jpg',np.hstack([output]),[cv2.IMWRITE_JPEG_QUALITY, 70])
cv2.waitKey(0)
After I run this, I do see a lot of circles detected, however, the results are complete haywire.
My question is, how do I improve this detection. Which parameters are specifically needed to optimize in the HoughCircles method to achieve greater accuracy? Or, should I take the approach of annotating hundreds of similar images via bounding boxes and then train them over a full-blown CNN like Yolo to perform detection?
Taking the approach mentioned in answer number 2 from here Measuring the diameter pictures of holes in metal parts, photographed with telecentric, monochrome camera with opencv . I got this output. This looks close to performing a count but misses on lot of actual pipes during the brightness transformation of the image.
The most important parameters for your HoughCircles call are:
param1: because you are using cv2.HOUGH_GRADIENT, param1 is the higher threshold for the edge detection algorithm and param1 / 2 is the lower threshold.
param2: it represents the accumulator threshold, so the lower the value, the more circles will be returned.
minRadius and maxRadius: the blue circles in the example have a diameter of roughly 20 pixels, so using 70 pixels for maxRadius is the reason why so many circles are being returned by the algorithm.
minDist: the minimum distance between the centers of two circles.
The parameterization defined below:
circles = cv2.HoughCircles(gray,
cv2.HOUGH_GRADIENT,
minDist=6,
dp=1.1,
param1=150,
param2=15,
minRadius=6,
maxRadius=10)
returns:
You could do an adaptive threshold as preprocessing. This basically looks for areas that are relatively brighter than the neighboring pixels, your global threshold loses some of the pipes, this keeps them a little better.
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('a2MTm.jpg')
blur_hor = cv2.filter2D(img[:, :, 0], cv2.CV_32F, kernel=np.ones((11,1,1), np.float32)/11.0, borderType=cv2.BORDER_CONSTANT)
blur_vert = cv2.filter2D(img[:, :, 0], cv2.CV_32F, kernel=np.ones((1,11,1), np.float32)/11.0, borderType=cv2.BORDER_CONSTANT)
mask = ((img[:,:,0]>blur_hor*1.2) | (img[:,:,0]>blur_vert*1.2)).astype(np.uint8)*255
plt.imshow(mask)
You can then carry on with the same post processing steps.
Here are some example processing steps:
circles = cv2.HoughCircles(mask,
cv2.HOUGH_GRADIENT,
minDist=8,
dp=1,
param1=150,
param2=12,
minRadius=4,
maxRadius=10)
output = img.copy()
for (x, y, r) in circles[0, :, :]:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
You can adjust the parameters to get what you would like, read about the parameters here.
Instead of using cv2.HoughCircles another approach would be to use contour filtering. We can threshold the image then filter using aspect ratio, contour area, and radius of the blob. Here's the result:
Count: 344
Code
import cv2
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,27,3)
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
count = 0
for c in cnts:
area = cv2.contourArea(c)
x,y,w,h = cv2.boundingRect(c)
ratio = w/h
((x, y), r) = cv2.minEnclosingCircle(c)
if ratio > .85 and ratio < 1.20 and area > 50 and area < 120 and r < 7:
cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), -1)
count += 1
print('Count: {}'.format(count))
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()
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 )