I need to count the number of metal balls inside a small metal cup. I tried template matching but it showed only one result having most probability. But i need the count of total metal balls visible. Since background too is metallic i was unable to do color thresholding. I tried a method of finding the first occurrence using template matching and then fill that area with RGB(0,0,0) and again did the template matching on that image, but several false detections are occurring. My primary requirement is to find the images that have three balls filled inside the cup and any other quantities other than three should not be detected.
Please see the images of different quantities filled inside the cup
Use Hough circles - see the OpenCV documentation for how to do this. Then just count the circles that are with some empirically determined radius range.
Here are some results and code that will enable you to do what you want:
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
using namespace std;
using namespace cv;
bool circle_compare (Vec3f i,Vec3f j) { return (i[2]>j[2]); }
int main(int argc, char** argv)
{
/// Read the image
Mat one = imread("one.jpg", 1 );
Mat two = imread("two.jpg", 1 );
Mat three = imread("three.jpg", 1 );
Mat four = imread("four.jpg", 1 );
if(!one.data || !two.data || !three.data || !four.data)
{
return -1;
}
// put all the images into one
Mat src(one.rows * 2, one.cols * 2, one.type());
Rect roi1(0, 0, one.cols, one.rows);
one.copyTo(src(roi1));
Rect roi2(one.cols, 0, one.cols, one.rows);
two.copyTo(src(roi2));
Rect roi3(0, one.rows, one.cols, one.rows);
three.copyTo(src(roi3));
Rect roi4(one.cols, one.rows, one.cols, one.rows);
four.copyTo(src(roi4));
// extract the blue channel because the circles show up better there
vector<cv::Mat> channels;
cv::split(src, channels);
cv::Mat blue;
GaussianBlur( channels[0], blue, Size(7, 7), 4, 4 );
vector<Vec3f> circles;
vector<Vec3f> candidate_circles;
/// Find the circles
HoughCircles( blue, candidate_circles, CV_HOUGH_GRADIENT, 1, 1, 30, 55);//, 0, 200 );
// sort candidate cirles by size, largest first
// so the accepted circles are the largest that meet other criteria
std::sort (candidate_circles.begin(), candidate_circles.end(), circle_compare);
/// Draw the circles detected
for( size_t i = 0; i < candidate_circles.size(); ++i )
{
Point center(cvRound(candidate_circles[i][0]), cvRound(candidate_circles[i][4]));
int radius = cvRound(candidate_circles[i][5]);
// skip over big circles
if(radius > 35)
continue;
// test whether centre of candidate_circle is inside of accepted circle
bool inside = false;
for( size_t j = 0; j < circles.size(); ++j )
{
Point c(cvRound(circles[j][0]), cvRound(circles[j][6]));
int r = cvRound(circles[j][7]);
int d = sqrt((center.x - c.x) * (center.x - c.x) + (center.y - c.y) * (center.y - c.y));
if(d <= r)
{
inside = true; // candidate is inside an existing circle
}
}
if(inside)
continue;
// accept the current candidate circle then draw it
circles.push_back(candidate_circles[i]);
circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );
circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
// now fill the circles in the quadrant that has three balls
vector<Vec3f> tl, tr, bl, br;
for( size_t i = 0; i < circles.size(); ++i )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][8]));
int radius = cvRound(circles[i][9]);
if(center.x < one.cols)
{
if(center.y < one.rows)
{
tl.push_back(circles[i]);
}
else
{
bl.push_back(circles[i]);
}
}
else
{
if(center.y < one.rows)
{
tr.push_back(circles[i]);
}
else
{
br.push_back(circles[i]);
}
}
vector<vector<Vec3f>> all;
all.push_back(tl);
all.push_back(tr);
all.push_back(bl);
all.push_back(bl);
for( size_t k = 0; k < all.size(); ++k )
{
if(all[k].size() == 3)
{
for( size_t i = 0; i < all[k].size(); ++i )
{
Point center(cvRound(all[k][i][0]), cvRound(all[k][i][10]));
int radius = cvRound(all[k][i][11]);
circle( src, center, radius, Scalar(0,255, 255), -1, 4, 0 );
}
}
}
}
// resize for easier display
resize(src, src, one.size());
/// Save results and display them
imwrite("balls.png", src);
//namedWindow( "Balls", CV_WINDOW_AUTOSIZE );
imshow( "Balls", src );
waitKey(0);
return 0;
}