OpenCV检测到带有噪声的局部圆 [英] OpenCV detect partial circle with noise

查看:113
本文介绍了OpenCV检测到带有噪声的局部圆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用OpenCV HoughCircles和findContours来检测一个圆,但是该圆不够完整,或者这些算法的算法中杂乱无章.也许我们只是对OpenCV不够熟悉.附件是我需要在其上找到圆圈的图像.您应该可以用眼睛清楚地看到它,但是,圆检测算法似乎都不起作用.我发现应用中值滤波器可以清除大部分噪声,但是即使经过中值滤波后,算法也无法检测到圆.

I have tried to use OpenCV HoughCircles and findContours to detect a circle however the circle isn't complete enough or there is too much noise in the algorithm for these algorithms. Or perhaps we are just not familiar enough with the OpenCV. Attached is my image that I need to find the circle on. You should be able to see it clearly with your eyes however, none of the circle detection algorithms seem to work. I have found that applying a median filter cleans up most of the noise but even after median filtering the algorithms can't detect the circle.

请注意,我什至在这里查看并尝试了解决方案,因此它不是该问题的重复: 在opencv中检测半圆

Note I even looked at and tried the solution here so it is not a duplicate of that question: Detect semi-circle in opencv

有什么想法吗?这是我需要使用的源图像.

Any ideas? This is my source image that I need to use.

此外,我要检测圆的原因是我只想使用圆的一部分进行计算.

Also, the reason I want to detect the circle is I want to do a calculation using only the points that are a part of the circle.

原始图片: http://www.collegemobile.com/IMG_2021.JPG

中值过滤图像: http://www.collegemobile.com/IMG_2022.JPG

推荐答案

在这里:

我正在使用在opencv中检测半圆中的第二个答案稍微修改一下.现在,此版本可检测到最佳的半圆形(关于完整性).

I'm using my 2nd answer from Detect semi-circle in opencv and modify it a little. This version now detects the best found semi-circle (regarding completeness).

但是首先我想告诉你为什么接受

But first I want to tell you why the accepted answer of link to Detect semi-circle in opencv stack overflow question does not work here (beside noise): You have only edges of the circle! as stated in that question, HoughCircle function computes the gradient internally, which does not work well for edgy images.

但是现在我该怎么做:

使用此作为输入(您自己的中值滤波图像(我刚刚裁剪了它):

using this as input (your own median filtered image (I've just cropped it):

首先,我将图像标准化".我只是拉伸值,最小val为0,最大val为255,导致出现以下结果:(也许有一些真正的对比度增强效果更好)

First I "normalize" the image. I just stretch values, that smallest val is 0 and biggest val is 255, leading to this result: (maybe some real contrast enhancement is better)

此后,我使用固定的阈值计算该图像的阈值(您可能需要对其进行编辑,并找到一种动态选择阈值的方法!更好的对比度增强可能会有所帮助)

after that I compute the threshold of that image with some fixed threshold (you might need to edit that and find a way to choose the threshold dynamically! a better contrast enhancement might help there)

从这张图片中,我使用了一些简单的RANSAC圆检测(非常类似于我在链接的半圆检测问题中的回答),从而为您提供了最佳半圆的结果:

from this image, I use some simple RANSAC circle detection(very similar to my answer in the linked semi-circle detection question), giving you this result as a best semi-sircle:

这是代码:

int main()
{
    //cv::Mat color = cv::imread("../inputData/semi_circle_contrast.png");
    cv::Mat color = cv::imread("../inputData/semi_circle_median.png");
    cv::Mat gray;

    // convert to grayscale
    cv::cvtColor(color, gray, CV_BGR2GRAY);

    // now map brightest pixel to 255 and smalles pixel val to 0. this is for easier finding of threshold
    double min, max;
    cv::minMaxLoc(gray,&min,&max);
    float sub = min;
    float mult = 255.0f/(float)(max-sub);
    cv::Mat normalized = gray - sub;
    normalized = mult * normalized;
    cv::imshow("normalized" , normalized);
    //--------------------------------


    // now compute threshold
    // TODO: this might ne a tricky task if noise differs...
    cv::Mat mask;
    //cv::threshold(input, mask, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    cv::threshold(normalized, mask, 100, 255, CV_THRESH_BINARY);



    std::vector<cv::Point2f> edgePositions;
    edgePositions = getPointPositions(mask);

    // create distance transform to efficiently evaluate distance to nearest edge
    cv::Mat dt;
    cv::distanceTransform(255-mask, dt,CV_DIST_L1, 3);

    //TODO: maybe seed random variable for real random numbers.

    unsigned int nIterations = 0;

    cv::Point2f bestCircleCenter;
    float bestCircleRadius;
    float bestCirclePercentage = 0;
    float minRadius = 50;   // TODO: ADJUST THIS PARAMETER TO YOUR NEEDS, otherwise smaller circles wont be detected or "small noise circles" will have a high percentage of completion

    //float minCirclePercentage = 0.2f;
    float minCirclePercentage = 0.05f;  // at least 5% of a circle must be present? maybe more...

    int maxNrOfIterations = edgePositions.size();   // TODO: adjust this parameter or include some real ransac criteria with inlier/outlier percentages to decide when to stop

    for(unsigned int its=0; its< maxNrOfIterations; ++its)
    {
        //RANSAC: randomly choose 3 point and create a circle:
        //TODO: choose randomly but more intelligent, 
        //so that it is more likely to choose three points of a circle. 
        //For example if there are many small circles, it is unlikely to randomly choose 3 points of the same circle.
        unsigned int idx1 = rand()%edgePositions.size();
        unsigned int idx2 = rand()%edgePositions.size();
        unsigned int idx3 = rand()%edgePositions.size();

        // we need 3 different samples:
        if(idx1 == idx2) continue;
        if(idx1 == idx3) continue;
        if(idx3 == idx2) continue;

        // create circle from 3 points:
        cv::Point2f center; float radius;
        getCircle(edgePositions[idx1],edgePositions[idx2],edgePositions[idx3],center,radius);

        // inlier set unused at the moment but could be used to approximate a (more robust) circle from alle inlier
        std::vector<cv::Point2f> inlierSet;

        //verify or falsify the circle by inlier counting:
        float cPerc = verifyCircle(dt,center,radius, inlierSet);

        // update best circle information if necessary
        if(cPerc >= bestCirclePercentage)
            if(radius >= minRadius)
        {
            bestCirclePercentage = cPerc;
            bestCircleRadius = radius;
            bestCircleCenter = center;
        }

    }

    // draw if good circle was found
    if(bestCirclePercentage >= minCirclePercentage)
        if(bestCircleRadius >= minRadius);
        cv::circle(color, bestCircleCenter,bestCircleRadius, cv::Scalar(255,255,0),1);


        cv::imshow("output",color);
        cv::imshow("mask",mask);
        cv::waitKey(0);

        return 0;
    }

具有以下辅助功能:

float verifyCircle(cv::Mat dt, cv::Point2f center, float radius, std::vector<cv::Point2f> & inlierSet)
{
 unsigned int counter = 0;
 unsigned int inlier = 0;
 float minInlierDist = 2.0f;
 float maxInlierDistMax = 100.0f;
 float maxInlierDist = radius/25.0f;
 if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
 if(maxInlierDist>maxInlierDistMax) maxInlierDist = maxInlierDistMax;

 // choose samples along the circle and count inlier percentage
 for(float t =0; t<2*3.14159265359f; t+= 0.05f)
 {
     counter++;
     float cX = radius*cos(t) + center.x;
     float cY = radius*sin(t) + center.y;

     if(cX < dt.cols)
     if(cX >= 0)
     if(cY < dt.rows)
     if(cY >= 0)
     if(dt.at<float>(cY,cX) < maxInlierDist)
     {
        inlier++;
        inlierSet.push_back(cv::Point2f(cX,cY));
     }
 }

 return (float)inlier/float(counter);
}


inline void getCircle(cv::Point2f& p1,cv::Point2f& p2,cv::Point2f& p3, cv::Point2f& center, float& radius)
{
  float x1 = p1.x;
  float x2 = p2.x;
  float x3 = p3.x;

  float y1 = p1.y;
  float y2 = p2.y;
  float y3 = p3.y;

  // PLEASE CHECK FOR TYPOS IN THE FORMULA :)
  center.x = (x1*x1+y1*y1)*(y2-y3) + (x2*x2+y2*y2)*(y3-y1) + (x3*x3+y3*y3)*(y1-y2);
  center.x /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );

  center.y = (x1*x1 + y1*y1)*(x3-x2) + (x2*x2+y2*y2)*(x1-x3) + (x3*x3 + y3*y3)*(x2-x1);
  center.y /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );

  radius = sqrt((center.x-x1)*(center.x-x1) + (center.y-y1)*(center.y-y1));
}



std::vector<cv::Point2f> getPointPositions(cv::Mat binaryImage)
{
 std::vector<cv::Point2f> pointPositions;

 for(unsigned int y=0; y<binaryImage.rows; ++y)
 {
     //unsigned char* rowPtr = binaryImage.ptr<unsigned char>(y);
     for(unsigned int x=0; x<binaryImage.cols; ++x)
     {
         //if(rowPtr[x] > 0) pointPositions.push_back(cv::Point2i(x,y));
         if(binaryImage.at<unsigned char>(y,x) > 0) pointPositions.push_back(cv::Point2f(x,y));
     }
 }

 return pointPositions;
}

编辑:还有一件事:速度性能在很大程度上取决于maxNrOfIterations.如果那很重要,那么您真的应该阅读有关RANSAC的停止时间.因此,您也许可以及早确定找到的圆圈是正确的圆圈,而无需测试其他任何圆圈;)

edit : one more thing: speed performance highly depends on maxNrOfIterations. If that matters you really should read about RANSAC an when to stop it. So you might be able to decide early that a found circle is the right one and dont need to test any other ones ;)

这篇关于OpenCV检测到带有噪声的局部圆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆