我正在开发用于跟踪培养皿(或其他圆形容器)中的小动物的应用程序。 在进行任何跟踪之前,前几帧用于定义区域。 每个碟子将匹配一个圆形的独立静态区域(即在跟踪期间不会更新)。 用户可以请求程序尝试从原始图像中查找菜肴并将其用作区域。
以下是示例:
为了执行此任务,我正在使用 Hough Circle Transform 。 但实际上,不同的用户将拥有非常不同的设置和图像,我不想让用户手动定义参数。 我不能只猜测所有参数。
但是,我还有其他想要使用的信息:
我知道要检测的圆圈的确切数量。
因此,我可以将要定义的参数数量缩小为一个:阈值。 使用这些信息并考虑到我有N个圆圈可以找到,我当前的解决方案是 测试许多阈值并保持标准差最小的圆(因为所有圆都应具有相似的大小):
//at this point, minRad and maxRad were calculated from the size of the image and the number of circles to find.
//assuming circles should altogether fill more than 1/3 of the images but cannot be altogether larger than the image.
//N is the integer number of circles to find.
//img is the picture of the scene (filtered).
//the vectors containing the detected circles and the --so far-- best circles found.
std::vector<cv::Vec3f> circles, bestCircles;
//the score of the --so far-- best set of circles
double bestSsem = 0;
for(int t=5; t<400 ; t=t+2){
//Apply Hough Circles with the threshold t
cv::HoughCircles(img, circles, CV_HOUGH_GRADIENT, 3, minRad*2, t,3, minRad, maxRad );
if(circles.size() >= N){
//call a routine to give a score to this set of circles according to the similarity of their radii
double ssem = scoreSetOfCircles(circles,N);
//if no circles are recorded yet, or if the score of this set of circles is higher than the former best
if( bestCircles.size() < N || ssem > bestSsem){
//this set become the temporary best set of circles
bestCircles=circles;
bestSsem=ssem;
}
}
}
使用:
//the methods to assess how good is a set of circle (the more similar the circles are, the higher is ssem)
double scoreSetOfCircles(std::vector<cv::Vec3f> circles, int N){
double ssem=0, sum = 0;
double mean;
for(unsigned int j=0;j<N;j++){
sum = sum + circles[j][2];
}
mean = sum/N;
for(unsigned int j=0;j<N;j++){
double em = mean - circles[j][2];
ssem = 1/(ssem + em*em);
}
return ssem;
}
我通过执行第二次传递达到了更高的准确度,其中我重复了这个算法,使用第一次传递的结果缩小[minRad:maxRad]间隔。
例如minRad2 = 0.95 *最佳圆的平均半径,maxRad2 = 1.05 *最佳圆的平均半径。
到目前为止,我使用此方法获得了相当不错的结果。但是,它很慢而且很脏。 我的问题是:
感谢您的回答和建议。
答案 0 :(得分:10)
以下方法适用于您的情况:
按圆度过滤轮廓:
double area = moms.m00;
double perimeter = arcLength(Mat(contours[contourIdx]), true);
double ratio = 4 * CV_PI * area / (perimeter * perimeter);
ratio
接近1将为您提供圈子。
计算每个圆的半径和中心
center = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);
您可以添加更多过滤器以提高稳健性。
实际上,您可以在OpenCV中找到整个过程的实现。看看如何实现SimpleBlobDetector
类和findCirclesGrid
函数。
答案 1 :(得分:5)
在当前算法中,最突出的是for(int t=5; t<400; t=t+2)
循环。尝试记录某些测试图像的分数值。图表score(t)
与t
。运气好的话,它会建议t
的较小范围,或者是一个单一最大值的smoothish曲线。在后一种情况下,您可以使用Hill Climbing方法将所有t
值的循环更改为更智能的搜索。
即使它相当嘈杂,你也可以首先在30的倍数上循环,并且在附近的2的倍数上循环最好的1或2个循环。
此外,在您的分数功能中,您应该取消任何带有重叠圆圈的结果,并且可能会对过度分散的圆圈进行惩罚。
答案 2 :(得分:3)
您没有解释为什么使用黑色背景。除非你使用远心镜头(看起来不太可能,考虑到明显的视野),并且暂时忽略径向扭曲,否则碟子的图像将是椭圆形,因此将它们估计为圆圈可能会导致严重错误。
总而言之,在我看来,你并没有遵循一个好的方法。如果目标只是删除背景,那么你可以跟踪菜肴中的错误,那么你的目标就是:找到哪些像素是背景并标记它们。最简单的方法是在相同的照明和相机下拍摄没有餐具的背景图片,并直接检测图片与图片的差异。有色背景将是优选的,其中颜色不太可能出现在餐具中(例如绿色或蓝色天鹅绒)。所以你已经把问题简化为蓝屏(或色度键控),这是机器视觉中应用于视觉效果的经典技术。谷歌搜索“matte petro vlahos假设”找到解决这个问题的经典算法。