Image processing 将嘈杂的硬币重塑成圆形
我正在使用JavaCV(OpenCV包装器)进行硬币检测,但在连接硬币时,我遇到了一个小问题。如果我试图腐蚀它们来分离这些硬币,它们就会失去它们的圆形形状,如果我试图计算每个硬币内部的像素,可能会出现问题,因此一些硬币可能会被误认为是一个更大的硬币。我想做的是首先重塑它们,使它们像一个圆(等于硬币的半径),然后计算其中的像素数 这是我的阈值图像: 这是被侵蚀的图像:Image processing 将嘈杂的硬币重塑成圆形,image-processing,opencv,javacv,Image Processing,Opencv,Javacv,我正在使用JavaCV(OpenCV包装器)进行硬币检测,但在连接硬币时,我遇到了一个小问题。如果我试图腐蚀它们来分离这些硬币,它们就会失去它们的圆形形状,如果我试图计算每个硬币内部的像素,可能会出现问题,因此一些硬币可能会被误认为是一个更大的硬币。我想做的是首先重塑它们,使它们像一个圆(等于硬币的半径),然后计算其中的像素数 这是我的阈值图像: 这是被侵蚀的图像: 有什么建议吗?或者有没有更好的方法来打破硬币之间的桥梁?OpenCV有一个名为HoughCircles()的函数,可以应用于您的案
有什么建议吗?或者有没有更好的方法来打破硬币之间的桥梁?OpenCV有一个名为HoughCircles()的函数,可以应用于您的案例,而无需分离不同的圆。你能从JavaCV中调用它吗?如果是这样,它将做你想做的事情(检测和计算圆),绕过你的分离问题
主要的一点是在不分离圆的情况下准确地检测圆。其他算法(如模板匹配)可以用来代替广义Hough变换,但您必须考虑硬币的不同大小。您不需要腐蚀,只需为
cvHoughCircles()提供一组好的参数即可:
用于生成此图像的代码来自我的另一篇文章:,带有以下参数:
CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, gray->height/12, 80, 26);
这看起来和我最近不得不分离琼脂平板上生长的细菌菌落的问题相似。
我对阈值图像执行了距离变换(在您的情况下,您需要将其反转)。
然后找到距离图的峰值(通过计算扩展的距离图和距离图之间的差值并找到零值)。
然后,我假设每个峰值是圆(硬币)的中心,距离图中峰值的值是圆的半径
以下是经过此管道后的图像结果:
我是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;
}
int main(int argc,char**argv){
cv::垫对象、距离、峰值、结果;
矢量轮廓;
objects=cv::imread(“CUfWj.jpg”);
对象。复制到(结果);
cv::CVT颜色(对象,对象,cv_bgr2灰色);
//这是模糊这篇文章的CF评论图像的一行
cv::blur(对象,对象,cv::Size(3,3));
cv::threshold(对象,对象,125255,cv::THRESH_BINARY_INV);
/*将距离变换应用于“对象”。
*结果保存在“距离”中*/
cv::distanceTransform(对象、距离、cv_distance_L2、cv_distance_MASK_5);
/*为了找到局部最大值,“距离”
*从膨胀的结果中减去
*“距离”。所有峰值保持保存值*/
扩张(距离,峰值,cv::Mat(),cv::Point(-1,-1),3);
cv::Explate(对象,对象,cv::Mat(),cv::Point(-1,-1),3);
/*现在所有的峰值都应该精确为0*/
峰值=峰值距离;
/*和非高峰*/
cv::阈值(峰值,峰值,0255,cv::阈值二元);
峰值。转换为(峰值,CV_8U);
/*只有非零值的“峰值”的零值
*在“对象”中是真正的峰值*/
cv::按位异或(峰值、对象、峰值);
/*距离小于
*通过放大合并2个像素*/
扩张(峰值,峰值,cv::Mat(),cv::Point(-1,-1),1);
/*为了映射峰值,使用findContours()。
*结果存储在“等高线”中*/
cv::findContours(山峰、轮廓、cv_-RETR_-CCOMP、cv_-CHAIN_-APPROX_-SIMPLE);
/*以下步骤仅在以下情况下适用,至少:,
*存在一个轮廓*/
cv::imwrite(“CUfWj2.jpg”,峰值);
如果(等高线.size()>0){
/*定义向量以存储峰值、中心和
*以及利益客体的理论界*/
std::向量MOM(contours.size());
std::向量中心(contours.size());
std::矢量圆(courts.size());
浮动半径x,y;
/*计算每个峰值的力矩,然后计算峰值的中心
*它们大致是每个感兴趣对象的中心*/
对于(无符号整数i=0;i0&&y>0){
rad=(float)(距离在((int)y,(int)x)+1);
圆[i][0]=x;
圆[i][3]=y;
圆[i][2]=rad;
cv::circle(结果,中心[i],rad+1,cv::Scalar(255,0,0),2,4,0);
}
}
cv::imwrite(“CUfWj2.jpg”,结果);
}
返回1;
}
基于侵蚀的对象识别的常用方法是标记侵蚀图像中的连续区域,然后重新生长它们,直到它们与原始图像中的区域匹配。不过,在您的情况下,Hough圆是一个更好的主意。在检测到连接的硬币后,我建议应用形态学操作对区域a进行分类s“绝对投币”和“绝对不投币”,应用距离变换,然后运行分水岭以确定边界。此场景实际上是OpenCV中分水岭算法的演示示例− 也许它是为了回答这个问题而创建的。首先我应用了高斯模糊,然后是cvHoughCircles,但在一些图像中,它找不到正确的半径和圆心。在一些图像中,它还发现了错误的圆——特别是在硬币分组且有很多圆的情况下。使用me可能更好dian滤波器而不是高斯滤波器,因为拟合将限制模糊效果(因此它将提高算法的准确性)。然而,主要问题是很难通过形态学来分离这些复杂的形状(你必须进行大量的总结)。如果Hough圆不起作用,您可以尝试使用不同比例的圆进行模板匹配,直接检测圆。好的,这很酷,但在Hough圆方法中,在某些情况下,5美分和10美分可以用相同的半径绘制,这不是我想要的,我想要