I'm working on opencv problem to figure out which circles are filled. However, sometimes edge of circles are cause of false positive. It makes my wonder if I can remove these circles by turning all pixels white that have high R value in RGB. My approach is to create a mask of pixels that are pinkish and then subtract mask from original image to remove circles. As of now I am getting black mask. I'm doing something wrong. Please guide.
rgb = cv2.imread(img, cv2.CV_LOAD_IMAGE_COLOR)
rgb_filtered = cv2.inRange(rgb, (200, 0, 90), (255, 110, 255))
cv2.imwrite('mask.png',rgb_filtered)
Here is my solution. Unfortunately it's in C++ too and this is how it works:
threshold the image to find out which parts are background (white paper)
find the circles by extracting contours.
now each contour is assumed to be a circle, so compute the minimum circle enclosing that contour. No parameter tuning necessary if the input is ok (that means every circle is a single contour, so circle may not be connected by drawing for example)
check for each circle, whether there are more foreground (drawing) or background (white paper) pixel inside (by some ratio threshold).
int main()
{
cv::Mat colorImage = cv::imread("countFilledCircles.png");
cv::Mat image = cv::imread("countFilledCircles.png", CV_LOAD_IMAGE_GRAYSCALE);
// threshold the image!
cv::Mat thresholded;
cv::threshold(image,thresholded,0,255,CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
// save threshold image for demonstration:
cv::imwrite("countFilledCircles_threshold.png", thresholded);
// find outer-contours in the image these should be the circles!
cv::Mat conts = thresholded.clone();
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(conts,contours,hierarchy, CV_RETR_EXTERNAL, CV_C HAIN_APPROX_SIMPLE, cv::Point(0,0));
// colors in which marked/unmarked circle outlines will be drawn:
cv::Scalar colorMarked(0,255,0);
cv::Scalar colorUnMarked(0,0,255);
// each outer contour is assumed to be a circle
// TODO: you could first find the mean radius of all assumed circles and try to find outlier (dirt etc in the image)
for(unsigned int i=0; i<contours.size(); ++i)
{
cv::Point2f center;
float radius;
// find minimum circle enclosing the contour
cv::minEnclosingCircle(contours[i],center,radius);
bool marked = false;
cv::Rect circleROI(center.x-radius, center.y-radius, center.x+radius, center.y+radius);
//circleROI = circleROI & cv::Rect(0,0,image.cols, image.rows);
// count pixel inside the circle
float sumCirclePixel = 0;
float sumCirclePixelMarked = 0;
for(int j=circleROI.y; j<circleROI.y+circleROI.height; ++j)
for(int i=circleROI.x; i<circleROI.x+circleROI.width; ++i)
{
cv::Point2f current(i,j);
// test if pixel really inside the circle:
if(cv::norm(current-center) < radius)
{
// count total number of pixel in the circle
sumCirclePixel = sumCirclePixel+1.0f;
// and count all pixel in the circle which hold the segmentation threshold
if(thresholded.at<unsigned char>(j,i))
sumCirclePixelMarked = sumCirclePixelMarked + 1.0f;
}
}
const float ratioThreshold = 0.5f;
if(sumCirclePixel)
if(sumCirclePixelMarked/sumCirclePixel > ratioThreshold) marked = true;
// draw the circle for demonstration
if(marked)
cv::circle(colorImage,center,radius,colorMarked,1);
else
cv::circle(colorImage,center,radius,colorUnMarked,1);
}
cv::imshow("thres", thresholded);
cv::imshow("colorImage", colorImage);
cv::imwrite("countFilledCircles_output.png", colorImage);
cv::waitKey(-1);
}
giving me these results:
after otsu thresholding:
final image:
I've tried to come up with a solution in Python. Basically the process is the following:
Gaussian blur to reduce noise.
Otsu's threshold.
Find contours that have no parents, those contours should be the circles.
Check the ratio of white-to-black pixels inside each contour.
You may need to tune up the white ratio threshold to fit your application. I've used 0.7 as it seems a reasonable value.
import cv2
import numpy
# Read image and apply gaussian blur
img = cv2.imread("circles.png", cv2.CV_LOAD_IMAGE_GRAYSCALE)
img = cv2.GaussianBlur(img, (5, 5), 0)
# Apply OTSU thresholding and reverse it so the circles are in the foreground (white)
_, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
otsu = cv2.bitwise_not(otsu).astype("uint8")
# Find contours that have no parent
contours, hierarchy = cv2.findContours(numpy.copy(otsu), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
parent_contours = [contours[idx] for idx, val in enumerate(hierarchy[0]) if val[3] == -1]
# Loop through all contours to check the ratio of white to black pixels inside each one
filled_circles_contours = list()
for contour in parent_contours:
contour_mask = numpy.zeros(img.shape).astype("uint8")
cv2.drawContours(contour_mask, [contour], -1, 1, thickness=-1)
white_len_mask = len(cv2.findNonZero(contour_mask))
white_len_thresholded = len(cv2.findNonZero(contour_mask * otsu))
white_ratio = float(white_len_thresholded) / white_len_mask
if white_ratio > 0.7:
filled_circles_contours.append(contour)
# Show image with detected circles
cv2.drawContours(img, filled_circles_contours, -1, (0, 0, 0), thickness=2)
cv2.namedWindow("Result")
cv2.imshow("Result", img)
cv2.waitKey(0)
This is the result I obtained from applying the code above to your image:
Here's how I did it:
Convert to grayscale, apply gaussian blur to remove noises
Apply otsu thresholding, it's quite good to separate fore and background, you should read about it
Apply Hough circle transform to find candidate circles, sadly this requires heavy tuning. Maybe watershed segmentation is a better alternative
Extract the ROI from the candidate circles, and find the ratio of black and white pixels.
Here's my sample result:
When we draw our result on original image:
Here's the sample code (sorry in C++):
void findFilledCircles( Mat& img ){
Mat gray;
cvtColor( img, gray, CV_BGR2GRAY );
/* Apply some blurring to remove some noises */
GaussianBlur( gray, gray, Size(5, 5), 1, 1);
/* Otsu thresholding maximizes inter class variance, pretty good in separating background from foreground */
threshold( gray, gray, 0.0, 255.0, CV_THRESH_OTSU );
erode( gray, gray, Mat(), Point(-1, -1), 1 );
/* Sadly, this is tuning heavy, adjust the params for Hough Circles */
double dp = 1.0;
double min_dist = 15.0;
double param1 = 40.0;
double param2 = 10.0;
int min_radius = 15;
int max_radius = 22;
/* Use hough circles to find the circles, maybe we could use watershed for segmentation instead(?) */
vector<Vec3f> found_circles;
HoughCircles( gray, found_circles, CV_HOUGH_GRADIENT, dp, min_dist, param1, param2, min_radius, max_radius );
/* This is just to draw coloured circles on the 'originally' gray image */
vector<Mat> out = { gray, gray, gray };
Mat output;
merge( out, output );
float diameter = max_radius * 2;
float area = diameter * diameter;
Mat roi( max_radius, max_radius, CV_8UC3, Scalar(255, 255, 255) );
for( Vec3f circ: found_circles ) {
/* Basically we extract the region of the circles, and count the ratio of black pixels (0) and white pixels (255) */
Mat( gray, Rect( circ[0] - max_radius, circ[1] - max_radius, diameter, diameter ) ).copyTo( roi );
float filled_percentage = 1.0 - 1.0 * countNonZero( roi ) / area;
/* If more than half is filled, then maybe it's filled */
if( filled_percentage > 0.5 )
circle( output, Point2f( circ[0], circ[1] ), max_radius, Scalar( 0, 0, 255), 3 );
else
circle( output, Point2f( circ[0], circ[1] ), max_radius, Scalar( 255, 255, 0), 3 );
}
namedWindow("");
moveWindow("", 0, 0);
imshow("", output );
waitKey();
}
Related
I want to get the angles from the binary image in python, but I don't know how to convert this cpp to python.
If there is a other way to detect angles from this 'inspected image' for example with blob detection please let me know.
example image:
inspected image
example cpp code:
cv::Mat input = cv::imread("../inputData/rectangles.png");
cv::Mat gray;
cv::cvtColor(input,gray,CV_BGR2GRAY);
// since your image has compression artifacts, we have to threshold the image
int threshold = 200;
cv::Mat mask = gray > threshold;
cv::imshow("mask", mask);
// extract contours
std::vector<std::vector<cv::Point> > contours;
cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
for(int i=0; i<contours.size(); ++i)
{
// fit bounding rectangle around contour
cv::RotatedRect rotatedRect = cv::minAreaRect(contours[i]);
// read points and angle
cv::Point2f rect_points[4];
rotatedRect.points( rect_points );
float angle = rotatedRect.angle; // angle
// read center of rotated rect
cv::Point2f center = rotatedRect.center; // center
// draw rotated rect
for(unsigned int j=0; j<4; ++j)
cv::line(input, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,255,0));
// draw center and print text
std::stringstream ss; ss << angle; // convert float to string
cv::circle(input, center, 5, cv::Scalar(0,255,0)); // draw center
cv::putText(input, ss.str(), center + cv::Point2f(-25,25),
cv::FONT_HERSHEY_COMPLEX_SMALL,1, cv::Scalar(255,0,255)); // print angle
}
my code:
contours_yellow, hierarchy = cv2.findContours(mask_yellow, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
yellow_contours = cv2.drawContours(mask_yellow, contours_yellow, -1, (0,255,0), 3)
angle = 0
for contour in yellow_contours :
{
#fit bounding rectangle around contour
RotatedRect = minAreaRect(contours_yellow[value])
#read point and angle
point2f rect_points[4]
rotatedRect.points(rect_points)
angle = rotatedRect.angle; # angle
}
print (angle)
The function cv2.minAreaRect() returns a tuple of 3 elements: the first two are the coordinates of the rotated rectangle and the angle of rotation. We need to print this last element for each contour.
In Python, to obtain the last element in a list, array or tuple you can do myList[-1].
img = cv2.imread('white_boxes.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold the image
th = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]
# find contours
contours, _ = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
rect = cv2.minAreaRect(c)
print('Angle:', rect[-1])
I am a newbie in android, and open CV both. However, I am trying to take an image from the camera, convert it into the desired format, and pass it to the tflite model.
Code for capturing image, and applying image processing to it.
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
float mh = mRGBA.height();
float cw = (float) Resources.getSystem().getDisplayMetrics().widthPixels;
float scale = mh / cw * 0.7f;
mRGBA = inputFrame.rgba();
frame = classifier.processMat(mRGBA);
Mat temp = new Mat();
Mat temp3= new Mat();
if (!isDebug) {
if (counter == CLASSIFY_INTERVAL) {
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGBA2GRAY);
Core.rotate(frame, frame, Core.ROTATE_90_CLOCKWISE);
Imgproc.GaussianBlur(frame, frame, new Size(5, 5), 0);
Imgproc.adaptiveThreshold(frame, frame, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV , 3, 2);
Bitmap bmsp = null;
runInterpreter();
counter = 0;
} else {
counter++;
}
}
Imgproc.rectangle(mRGBA,
new Point(mRGBA.cols() / 2f - (mRGBA.cols() * scale / 2),
mRGBA.rows() / 2f - (mRGBA.cols() * scale / 2)),
new Point(mRGBA.cols() / 2f + (mRGBA.cols() * scale / 2),
mRGBA.rows() / 2f + (mRGBA.cols() * scale / 2)),
new Scalar(0, 255, 0), 1);
if (isEdge) {
mRGBA = classifier.debugMat(mRGBA);
}
System.gc();
return mRGBA;
}
My output looks like this image, but I want the hand to be filled with white color before passing it to model. Can somebody suggest?
The main issue is that the result of adaptiveThreshold has gaps in the external edge, so you can't use it as input to findContours.
I think that using GaussianBlur makes things worst, because it blurs the edge between the hand and the background.
You may use the following stages:
Convert frame to Grayscale.
Apply adaptiveThreshold with large kernel size (I used size 51).
Using a large kernel size, keeps a thick edge line without gaps (except from a small gap at the fingernail).
Find contours.
Find the contour with the maximum area.
Draw the contour (fill with solid value of 255) on a zeros image.
There is a problem: the inner part of the hand is not filled due to the weird shape of the contour.
For complete the filling:
Find the center of the contour, and fill it using floodFill.
Here is a Python code sample:
import numpy as np
import cv2
frame = cv2.imread("hand.jpg") # Read image from file (for testing).
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Use BGR to Gray conversion (not RGBA, because image is read from file)
# Apply adaptiveThreshold with large filter size.
thres_gray = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 51, 2)
# Find contours (external contours)
cnts, hier = cv2.findContours(thres_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# Find contour with the maximum area
c = max(cnts, key=cv2.contourArea)
res = np.zeros_like(gray) # Create new zeros images for storing the result.
# Fill the contour with white color - draw the filled contour on res image.
cv2.drawContours(res, [c], -1, 255, -1)
# Compute the center of the contour
# https://www.pyimagesearch.com/2016/02/01/opencv-center-of-contour/
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# Use floodFill for filling the center of the contour
cv2.floodFill(res, None, (cX, cY), 255)
# Show images for testing
cv2.imshow('thres_gray', thres_gray)
cv2.imshow('res', res)
cv2.waitKey()
cv2.destroyAllWindows()
Results:
thres_gray:
res before floodFill:
res after floodFill:
JAVA implementation:
package myproject;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.CvType;
import org.opencv.core.Scalar;
import org.opencv.core.Point;
import org.opencv.core.MatOfPoint;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgproc.Moments;
import org.opencv.imgcodecs.Imgcodecs;
import java.util.List;
import java.util.ArrayList;
class Sample {
static { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }
public static void main(String[] args) {
Mat frame = Imgcodecs.imread("hand.jpg");
Mat gray = new Mat();
Mat thres_gray = new Mat();
Imgproc.cvtColor(frame, gray, Imgproc.COLOR_BGR2GRAY);
//Apply adaptiveThreshold with large filter size.
Imgproc.adaptiveThreshold(gray, thres_gray, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV, 51, 2);
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
//Find contours
Imgproc.findContours(thres_gray, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE);
//Find contour with the maximum area
//https://stackoverflow.com/questions/38759925/how-to-find-largest-contour-in-java-opencv
double maxVal = 0;
int maxValIdx = 0;
for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++)
{
double contourArea = Imgproc.contourArea(contours.get(contourIdx));
if (maxVal < contourArea)
{
maxVal = contourArea;
maxValIdx = contourIdx;
}
}
Mat res = Mat.zeros(gray.size(), CvType.CV_8UC1); //Create new zeros images for storing the result.
Imgproc.drawContours(res, contours, maxValIdx, new Scalar(255), -1);
//Compute the center of the contour
//https://www.pyimagesearch.com/2016/02/01/opencv-center-of-contour/
Moments M = Imgproc.moments(contours.get(maxValIdx));
int cX = (int)(M.get_m10() / M.get_m00());
int cY = (int)(M.get_m01() / M.get_m00());
//Use floodFill for filling the center of the contour.
Mat mask = Mat.zeros(res.rows() + 2, res.cols() + 2, CvType.CV_8UC1);
Imgproc.floodFill(res, mask, new Point(cX, cY), new Scalar(255));
Imgcodecs.imwrite("res.png", res);
}
}
I am new to OpenCV and Python and I have been encountering a problem in removing noises in my input image. I only wanted to extract the nucleus of a WBC so I used addition to highlight the nucleus and used thresholding to remove the RBCs in the image. I successfully removed the RBCs but the platelets are not removed and some lines appeared in the borders. I also tried using dilation, erosion, opening and closing to denoise the image but the nucleus gets destroyed.
Here is my code:
img = cv2.imread('1.bmp')
img_2 = cv2.imread('1.bmp')
input_img = cv2.addWeighted(img, 0.55, img_2, 0.6, 0)
retval, threshold = cv2.threshold(input_img, 158, 255, cv2.THRESH_BINARY)
threshold = cv2.cvtColor(threshold, cv2.COLOR_BGR2GRAY)
retval2, threshold2 = cv2.threshold(threshold, 0, 255,
cv2.THRESH_BINARY+cv2.THRESH_OTSU)
blur2 = cv2.medianBlur(threshold2,5)
Here is the original image:
After Thresholding:
If the nucleus of a WBC as you have highlighted is always the largest contour before thresholding, I would suggest using findContours to store it alone and remove the smaller blobs like this:
vector<vector<Point>>contours; //Vector for storing contour
vector<Vec4i> hierarchy;
//Find the contours in the image
findContours(input_img, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
for (int i = 0; i< contours.size(); i++) // iterate through each contour.
{
double a = contourArea(contours[i], false); // Find the area of contour
if (a>largest_area){
largest_area = a;
//Store the index of largest contour
largest_contour_index = i;
// Find the bounding rectangle for biggest contour
bounding_rect = boundingRect(contours[i]);
}
}
Scalar color(255, 255, 255);
// Draw the largest contour using the previously stored index.
Mat dst;
drawContours(dst, contours, largest_contour_index, color, CV_FILLED, 8, hierarchy);
My code is C++ but you can find python examples: How to detect and draw contours using OpenCV in Python?
I'm trying to count how many red color bar from this video
tes1.mp4
and this is my function to count the object by count the center of the object
def count(frame):
frame = imutils.resize(frame,640,480)
hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
mask=cv2.inRange(hsv, lower, upper)
contours,_ = cv2.findContours(mask.copy(), cv2.RETR_CCOMP,cv2.CHAIN_APPROX_TC89_L1)
center=[]
for i in range(len(contours)):
c = max(contours, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
if M["m00"]==0:
M["m00"]=0.6
cx=int (M["m10"]/M["m00"])
cy=int(M["m01"] / M["m00"])
print "center",len(center)
center.append((cx,cy))
cv2.circle(frame, center[-1],3,(0,0,0),-1)
but the problem is len(center) appear more than 300. can anyone help to count how many the red bar. any solution will be helpful.
Here's how I made it: first, subtract blue and green channels from the red channel to find the "red" pixels. The result looks like this:
Threshold works really well on this, obviously:
From here we can find contours:
Count them, and that's the number of stripes. But there's also connectedComponents function, it can count those without finding contours.
import cv2
import numpy as np
#load a frame as int16 array to avoid overflow etc. when subtracting
img = cv2.imread("test_images/redbars.png").astype(np.int16)
#separating into blue, green and red channels
img_b = img[:, :, 0]
img_g = img[:, :, 1]
img_r = img[:, :, 2]
#for a red stripe, the red component should be much bigger than the rest
res_br = img_r - img_b
res_gr = img_r - img_g
res = np.maximum(res_br, res_gr)
res[res < 0] = 0
res = res.astype(np.uint8) #convert back to uint8 for findContours etc.
ret, thresh = cv2.threshold(res, 50, 1, cv2.THRESH_BINARY)
#find contours and count them
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#gives the correct output of 5
print(len(contours))
#alternatively, count connected components with this:
ncomp, nimg = cv2.connectedComponents(thresh)
print(ncomp - 1) #it counts the background as one of the components, so subtract 1
Here is a pipeline you can use to count the number of red strips on a frame:
int main(int argc, char** argv)
{
Mat input = imread("red_lines.png");
Mat hsv;
cvtColor(input, hsv, CV_BGR2HSV);
Mat mask;
inRange(hsv, Scalar(0, 0,0), Scalar(15, 255,255), mask);
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(7, 7));
erode(mask, mask, kernel);
dilate(mask, mask, kernel);
vector<vector<Point>> contours;
findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
cout << "objects found: " << contours.size() << endl;
for (int i = 0; i < contours.size(); i++)
{
drawContours(input, contours, i, Scalar(0, 255, 0), 2);
}
imshow("mask", mask);
imshow("contours", input);
waitKey(0);
return 0;
}
The important part in my code, is the morphological operations (erosion then dilation) I apply on the mask (resulting from inRange). This allows to remove the "mask's noise" and thus to catch only the big red strips.
To further improve the robustness, I recommend you to check the area of each contour. If it is above a certain threshold, just discard it.
P.S. This is C++ code, but I'm sure you'll figure out how to translate it into Python code... :)
I have an image to process.I need detect all the circles in the image.Here is it.
And here is my code.
import cv2
import cv2.cv as cv
img = cv2.imread(imgpath)
cv2.imshow("imgorg",img)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("gray",gray)
ret,thresh = cv2.threshold(gray, 199, 255, cv.CV_THRESH_BINARY_INV)
cv2.imshow("thresh",thresh)
cv2.waitKey(0)
cv2.destrotAllWindows()
Then,I got a image like this.
And I tried to use erode and dilate to divided them into single.But it doesnt work.My question is how to divide these contacted circles into single,so i can detect them.
According to #Micka's idea,I tried to process the image in following way,and here is my code.
import cv2
import cv2.cv as cv
import numpy as np
def findcircles(img,contours):
minArea = 300;
minCircleRatio = 0.5;
for contour in contours:
area = cv2.contourArea(contour)
if area < minArea:
continue
(x,y),radius = cv2.minEnclosingCircle(contour)
center = (int(x),int(y))
radius = int(radius)
circleArea = radius*radius*cv.CV_PI;
if area/circleArea < minCircleRatio:
continue;
cv2.circle(img, center, radius, (0, 255, 0), 2)
cv2.imshow("imggg",img)
img = cv2.imread("a.png")
cv2.imshow("org",img)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,threshold = cv2.threshold(gray, 199, 255,cv. CV_THRESH_BINARY_INV)
cv2.imshow("threshold",threshold)
blur = cv2.medianBlur(gray,5)
cv2.imshow("blur",blur)
laplacian=cv2.Laplacian(blur,-1,ksize = 5,delta = -50)
cv2.imshow("laplacian",laplacian)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(7,7))
dilation = cv2.dilate(laplacian,kernel,iterations = 1)
cv2.imshow("dilation", dilation)
result= cv2.subtract(threshold,dilation)
cv2.imshow("result",result)
contours, hierarchy = cv2.findContours(result,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)
findcircles(gray,contours)
But I dont get the same effect as #Micka's.I dont know which step is wrong.
Adapting the idea of #jochen I came to this:
extract the full circle mask as you've done (I called it fullForeground )
from your colored image, compute grayscale, blur (median blur size 7) it and and extract edges, for example with cv::Laplacian
This laplacian thresholded > 50 gives:
cv::Laplacian(blurred, lap, 0, 5); // no delta
lapMask = lap > 50; // thresholding to values > 50
This one dilated once gives:
cv::dilate(lapMask, dilatedThresholdedLaplacian, cv::Mat()); // dilate the edge mask once
Now subtraction fullForeground - dilatedThresholdedLaplacian (same as and_not operator for this type of masks) gives:
from this you can compute contours. For each contour you can compute the area and compare it to the area of an enclosing circle, giving this code and result:
std::vector<std::vector<cv::Point> > contours;
cv::findContours(separated.clone(), contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
double minArea = 500;
double minCircleRatio = 0.5;
for(unsigned int i=0; i<contours.size(); ++i)
{
double cArea = cv::contourArea(contours[i]);
if(cArea < minArea) continue;
//filteredContours.push_back(contours[i]);
//cv::drawContours(input, contours, i, cv::Scalar(0,255,0), 1);
cv::Point2f center;
float radius;
cv::minEnclosingCircle(contours[i], center, radius);
double circleArea = radius*radius*CV_PI;
if(cArea/circleArea < minCircleRatio) continue;
cv::circle(input, center, radius, cv::Scalar(0,0,255),2);
}
here is another image showing the coverage:
hope this helps
I think the first mistake ist the value of thesh.
In your example the command cv2.threshold converts all white areas to black and everything else to white. I would suggest using a smaller value for thesh so that all black pixel get converted to white and all white or "colored" pixels (inside the circles) get converted to black or vise versa. The value of thesh should be a little bigger than the brightest of the black pixels.
See opencv docu for threshold for more information.
Afterwards I would let opencv find all contours in the thresholded image and filter them for "valid" circles, e.g. by size and shape.
If that is not sufficiant you could segment the inner circle from the rest of the image: First compute threasholdImageA with all white areas colored black. Then compute threasholdImageB with all the black areas being black. Afterwards combine both, threasholdImageA and threasholdImageB, (e.g. with numpy.logical_and) to have a binary image with only the inner circle being white and the rest black. Of course the values for the threshold have to be chosen wisely to get the specific result.
That way also circles where the inner part directly touches the background will be segmented.