稳健地找到具有相同直径的N个圆圈:替代强制Hough变换阈值

时间:2012-06-03 20:27:56

标签: c++ algorithm image-processing opencv computer-vision

我正在开发用于跟踪培养皿(或其他圆形容器)中的小动物的应用程序。 在进行任何跟踪之前,前几帧用于定义区域。 每个碟子将匹配一个圆形的独立静态区域(即在跟踪期间不会更新)。 用户可以请求程序尝试从原始图像中查找菜肴并将其用作区域。

以下是示例: enter image description here enter image description here

为了执行此任务,我正在使用 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 *最佳圆的平均半径。

到目前为止,我使用此方法获得了相当不错的结果。但是,它很慢而且很脏。 我的问题是:

  • 你能用更清洁/更快的方式解决这个问题吗?
  • 或者您建议改进此算法?
  • 您认为我应该调查广义Hough变换吗?

感谢您的回答和建议。

3 个答案:

答案 0 :(得分:10)

以下方法适用于您的情况:

  1. 对图像进行二值化(您可能需要在几个阈值级别上执行此操作,以使算法独立于光照条件)
  2. 查找轮廓
  3. 对于每个轮廓计算力矩
  4. 按区域过滤它们以移除太小的轮廓
  5. 按圆度过滤轮廓:

    double area = moms.m00;
    double perimeter = arcLength(Mat(contours[contourIdx]), true);
    double ratio = 4 * CV_PI * area / (perimeter * perimeter);
    

    ratio接近1将为您提供圈子。

  6. 计算每个圆的半径和中心

    center = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);
    
  7. 您可以添加更多过滤器以提高稳健性。

    实际上,您可以在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假设”找到解决这个问题的经典算法。