我正在使用JavaCV(OpenCV包装器)进行硬币检测,但是当硬币连接时我遇到了一些问题。如果我试图侵蚀它们以分离这些硬币,它们会松开它们的圆形形状,如果我试图计算每个硬币内部的像素,就会出现问题,因此一些硬币可能被错误地计算为更大的硬币。我想要做的是首先重塑它们并使它们像一个圆圈(等于该硬币的半径),然后计算它们内部的像素。
这是我的阈值图像:
这是被侵蚀的图片:
有什么建议吗?或者有没有更好的方法来打破硬币之间的桥梁?
答案 0 :(得分:11)
它看起来类似于我最近分离在琼脂平板上生长的细菌菌落的问题。 我在阈值图像上执行了距离变换(在您的情况下,您需要将其反转)。 然后找到距离图的峰值(通过计算扩张距离图和距离图之间的差异并找到零值)。 然后,我假设每个峰值为圆心(硬币),距离图中峰值的值为圆的半径。
以下是此管道之后的图片结果:
我是OpenCV和c ++的新手,所以我的代码可能非常混乱,但我这样做了:
int main( int argc, char** argv ){
cv::Mat objects, distance,peaks,results;
std::vector<std::vector<cv::Point> > contours;
objects=cv::imread("CUfWj.jpg");
objects.copyTo(results);
cv::cvtColor(objects, objects, CV_BGR2GRAY);
//THIS IS THE LINE TO BLUR THE IMAGE CF COMMENTS OF THIS POST
cv::blur( objects,objects,cv::Size(3,3));
cv::threshold(objects,objects,125,255,cv::THRESH_BINARY_INV);
/*Applies a distance transform to "objects".
* The result is saved in "distance" */
cv::distanceTransform(objects,distance,CV_DIST_L2,CV_DIST_MASK_5);
/* In order to find the local maxima, "distance"
* is subtracted from the result of the dilatation of
* "distance". All the peaks keep the save value */
cv::dilate(distance,peaks,cv::Mat(),cv::Point(-1,-1),3);
cv::dilate(objects,objects,cv::Mat(),cv::Point(-1,-1),3);
/* Now all the peaks should be exactely 0*/
peaks=peaks-distance;
/* And the non-peaks 255*/
cv::threshold(peaks,peaks,0,255,cv::THRESH_BINARY);
peaks.convertTo(peaks,CV_8U);
/* Only the zero values of "peaks" that are non-zero
* in "objects" are the real peaks*/
cv::bitwise_xor(peaks,objects,peaks);
/* The peaks that are distant from less than
* 2 pixels are merged by dilatation */
cv::dilate(peaks,peaks,cv::Mat(),cv::Point(-1,-1),1);
/* In order to map the peaks, findContours() is used.
* The results are stored in "contours" */
cv::findContours(peaks, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
/* The next steps are applied only if, at least,
* one contour exists */
cv::imwrite("CUfWj2.jpg",peaks);
if(contours.size()>0){
/* Defines vectors to store the moments of the peaks, the center
* and the theoritical circles of the object of interest*/
std::vector <cv::Moments> moms(contours.size());
std::vector <cv::Point> centers(contours.size());
std::vector<cv::Vec3f> circles(contours.size());
float rad,x,y;
/* Caculates the moments of each peak and then the center of the peak
* which are approximatively the center of each objects of interest*/
for(unsigned int i=0;i<contours.size();i++) {
moms[i]= cv::moments(contours[i]);
centers[i]= cv::Point(moms[i].m10/moms[i].m00,moms[i].m01/moms[i].m00);
x= (float) (centers[i].x);
y= (float) (centers[i].y);
if(x>0 && y>0){
rad= (float) (distance.at<float>((int)y,(int)x)+1);
circles[i][0]= x;
circles[i][3]= y;
circles[i][2]= rad;
cv::circle(results,centers[i],rad+1,cv::Scalar( 255, 0,0 ), 2, 4, 0 );
}
}
cv::imwrite("CUfWj2.jpg",results);
}
return 1;
}
答案 1 :(得分:5)
你不需要侵蚀,只需要cvHoughCircles()
的一组好的参数:
用于生成此图片的代码来自我的其他帖子:Detecting Circles,其中包含以下参数:
CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, gray->height/12, 80, 26);
答案 2 :(得分:2)
OpenCV有一个名为HoughCircles()的函数,可以应用于你的情况,而不会分离不同的圆圈。你能用JavaCV打电话吗?如果是这样,它会做你想要的(检测和计算圆圈),绕过你的分离问题。
重点是准确检测圆圈而不先将它们分开。其他算法(例如模板匹配可以用来代替广义Hough变换,但你必须考虑到不同大小的硬币。
答案 3 :(得分:1)
基于侵蚀的物体识别的常用方法是在侵蚀图像中标记连续区域,然后重新生长它们,直到它们与原始图像中的区域匹配。不过,在你的情况下,霍夫圈是一个更好的主意。
答案 4 :(得分:0)
在检测到连接的硬币后,我建议应用形态学操作将区域分类为&#34;绝对硬币&#34;并且&#34;绝对不是硬币&#34;,应用距离变换,然后运行分水岭以确定边界。这个场景实际上是OpenCV中分水岭算法的演示示例 - 也许是为了回答这个问题而创建的。