Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在OpenCV中检测半圆_Opencv_Geometry_Hough Transform - Fatal编程技术网

在OpenCV中检测半圆

在OpenCV中检测半圆,opencv,geometry,hough-transform,Opencv,Geometry,Hough Transform,我试图检测图像中的完整圆和半圆 我遵循以下提到的程序: 处理图像(包括Canny边缘检测)。 找到轮廓并在空图像上绘制,这样我就可以消除不需要的组件 (处理后的图像正是我想要的)。 使用HoughCircles检测圆。这就是我得到的: 我尝试改变HoughCircles中的参数,但结果并不一致,因为它根据照明和图像中圆圈的位置而变化。 我根据圆的大小接受或拒绝圆。因此,结果是不可接受的。此外,我还有一长串“可接受”的圈子。所以,我需要在HoughCircle参数中获得一些零用钱。 至于完整的圆

我试图检测图像中的完整圆和半圆

我遵循以下提到的程序: 处理图像(包括Canny边缘检测)。 找到轮廓并在空图像上绘制,这样我就可以消除不需要的组件 (处理后的图像正是我想要的)。 使用HoughCircles检测圆。这就是我得到的:

我尝试改变HoughCircles中的参数,但结果并不一致,因为它根据照明和图像中圆圈的位置而变化。 我根据圆的大小接受或拒绝圆。因此,结果是不可接受的。此外,我还有一长串“可接受”的圈子。所以,我需要在HoughCircle参数中获得一些零用钱。 至于完整的圆,这很容易-我可以简单地找到轮廓的“圆”。问题是半圆


请在Hough变换之前找到编辑过的图像

Hough算法检测到的半圆很可能是正确的。这里的问题可能是,除非严格控制场景的几何体,即相机相对于目标的精确位置,以便图像轴垂直于目标平面,否则将获得投影在图像平面上的省略号而不是圆。更不用说光学系统造成的扭曲,这会使几何图形进一步退化。如果您在这里依赖精度,我建议。

直接在图像上使用
Houghcirle
,不要先提取边缘。 然后测试每个检测到的圆,图像中实际存在的百分比:

int main()
{
    cv::Mat color = cv::imread("../houghCircles.png");
    cv::namedWindow("input"); cv::imshow("input", color);

    cv::Mat canny;

    cv::Mat gray;
    /// Convert it to gray
    cv::cvtColor( color, gray, CV_BGR2GRAY );

    // compute canny (don't blur with that image quality!!)
    cv::Canny(gray, canny, 200,20);
    cv::namedWindow("canny2"); cv::imshow("canny2", canny>0);

    std::vector<cv::Vec3f> circles;

    /// Apply the Hough Transform to find the circles
    cv::HoughCircles( gray, circles, CV_HOUGH_GRADIENT, 1, 60, 200, 20, 0, 0 );

    /// Draw the circles detected
    for( size_t i = 0; i < circles.size(); i++ ) 
    {
        Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);
        cv::circle( color, center, 3, Scalar(0,255,255), -1);
        cv::circle( color, center, radius, Scalar(0,0,255), 1 );
    }

    //compute distance transform:
    cv::Mat dt;
    cv::distanceTransform(255-(canny>0), dt, CV_DIST_L2 ,3);
    cv::namedWindow("distance transform"); cv::imshow("distance transform", dt/255.0f);

    // test for semi-circles:
    float minInlierDist = 2.0f;
    for( size_t i = 0; i < circles.size(); i++ ) 
    {
        // test inlier percentage:
        // sample the circle and check for distance to the next edge
        unsigned int counter = 0;
        unsigned int inlier = 0;

        cv::Point2f center((circles[i][0]), (circles[i][1]));
        float radius = (circles[i][2]);

        // maximal distance of inlier might depend on the size of the circle
        float maxInlierDist = radius/25.0f;
        if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;

        //TODO: maybe paramter incrementation might depend on circle size!
        for(float t =0; t<2*3.14159265359f; t+= 0.1f) 
        {
            counter++;
            float cX = radius*cos(t) + circles[i][0];
            float cY = radius*sin(t) + circles[i][1];

            if(dt.at<float>(cY,cX) < maxInlierDist) 
            {
                inlier++;
                cv::circle(color, cv::Point2i(cX,cY),3, cv::Scalar(0,255,0));
            } 
           else
                cv::circle(color, cv::Point2i(cX,cY),3, cv::Scalar(255,0,0));
        }
        std::cout << 100.0f*(float)inlier/(float)counter << " % of a circle with radius " << radius << " detected" << std::endl;
    }

    cv::namedWindow("output"); cv::imshow("output", color);
    cv::imwrite("houghLinesComputed.png", color);

    cv::waitKey(-1);
    return 0;
}

如果您想测试RANSAC而不是Hough,请看一看。

这里有另一种方法,一个简单的RANSAC版本(需要进行大量优化以提高速度),可以在边缘图像上工作

该方法循环这些步骤,直到取消为止

  • 随机选择3个边缘像素
  • 从中估算圆(3个点足以识别圆)
  • 验证或证伪它确实是一个圆:计算给定边代表的圆的百分比
  • 如果圆被验证,则从输入/egdes中删除该圆

    int main()
    {
    //RANSAC
    
    //load edge image
    cv::Mat color = cv::imread("../circleDetectionEdges.png");
    
    // convert to grayscale
    cv::Mat gray;
    cv::cvtColor(color, gray, CV_RGB2GRAY);
    
    // get binary image
    cv::Mat mask = gray > 0;
    //erode the edges to obtain sharp/thin edges (undo the blur?)
    cv::erode(mask, mask, cv::Mat());
    
    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;
    
    char quitKey = 'q';
    std::cout << "press " << quitKey << " to stop" << std::endl;
    while(cv::waitKey(-1) != quitKey)
    {
        //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);
    
        float minCirclePercentage = 0.4f;
    
        // 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);
    
        if(cPerc >= minCirclePercentage)
        {
            std::cout << "accepted circle with " << cPerc*100.0f << " % inlier" << std::endl;
            // first step would be to approximate the circle iteratively from ALL INLIER to obtain a better circle center
            // but that's a TODO
    
            std::cout << "circle: " << "center: " << center << " radius: " << radius << std::endl;
            cv::circle(color, center,radius, cv::Scalar(255,255,0),1);
    
            // accept circle => remove it from the edge list
            cv::circle(mask,center,radius,cv::Scalar(0),10);
    
            //update edge positions and distance transform
            edgePositions = getPointPositions(mask);
            cv::distanceTransform(255-mask, dt,CV_DIST_L1, 3);
        }
    
        cv::Mat tmp;
        mask.copyTo(tmp);
    
        // prevent cases where no fircle could be extracted (because three points collinear or sth.)
        // filter NaN values
        if((center.x == center.x)&&(center.y == center.y)&&(radius == radius))
        {
            cv::circle(tmp,center,radius,cv::Scalar(255));
        }
        else
        {
            std::cout << "circle illegal" << std::endl;
        }
    
        ++nIterations;
        cv::namedWindow("RANSAC"); cv::imshow("RANSAC", tmp);
    }
    
    std::cout << nIterations <<  " iterations performed" << std::endl;
    
    
    cv::namedWindow("edges"); cv::imshow("edges", mask);
    cv::namedWindow("color"); cv::imshow("color", color);
    
    cv::imwrite("detectedCircles.png", color);
    cv::waitKey(-1);
    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;
    }
    
    优化应包括:

  • 使用“所有内点”来拟合更好的圆

  • 不要在每次检测到圆后计算距离变换(这相当昂贵)。直接从点/边集计算内边界,并从该列表中删除内边界

  • 如果图像中有许多小圆(和/或大量噪声),则不太可能随机命中3个边缘像素或一个圆。=>首先尝试轮廓检测,并为每个轮廓检测圆。之后,尝试检测图像中剩余的所有“其他”圆

  • 还有很多其他的东西


  • 您最好尝试使用不同的内核进行高斯模糊。这将对您有所帮助

    GaussianBlur( src_gray, src_gray, Size(11, 11), 5,5);
    

    所以改变大小(i,i),j,j)

    我知道有点晚了,但我使用了不同的方法,这更容易。 从
    cv2.hough圆(…)
    可以得到圆的中心和直径(x,y,r)。所以我只需通过所有圆心点,检查它们是否比直径离图像边缘更远

    这是我的密码:

            height, width = img.shape[:2]
    
            #test top edge
            up = (circles[0, :, 0] - circles[0, :, 2]) >= 0
    
            #test left edge
            left = (circles[0, :, 1] - circles[0, :, 2]) >= 0
    
            #test right edge
            right = (circles[0, :, 0] + circles[0, :, 2]) <= width
    
            #test bottom edge
            down = (circles[0, :, 1] + circles[0, :, 2]) <= height
    
            circles = circles[:, (up & down & right & left), :]
    
    高度,宽度=img.shape[:2] #测试上边缘 向上=(圆[0,:,0]-圆[0,:,2])>=0 #测试左边缘 左=(圆[0,:,1]-圆[0,:,2])>=0 #测试右边缘
    右=(圆[0,:,0]+圆[0,:,2])您可以检测图像中哪些边(在canny和hough之后)属于圆/半圆吗?你的问题是,Hough结果的圆位置不够好?使用所有圆边(或至少3个正确的圆边像素)拟合参数化圆如何?(三个点定义一个圆!)。。。为了使其更加健壮,您可以使用RANSAC算法(inlier/outlier counting=>巨大缺失部分=半圆)。我没试过,但可能有用?!?谢谢你,米卡!你们所说的参数圆是霍夫变换算法本身,不是吗?如果您看到Hough的结果,则检测到的圆上有3个以上的点!我对RANSAC没有任何线索,我会检查一下。在计算/绘制hough圆之前,你能提供边缘图像吗?你有没有试着用更小的线条画轮廓?啊,我明白了。。。HoughtTransform在内部使用canny,因此需要线的厚度,但对于完整的圆,最好填充轮廓。我发现我不需要在空白图像上绘制轮廓。这是Canny+高斯模糊。太棒了!它起作用了:)但是为什么它在边缘上不起作用呢?我的目标是通过设定轮廓尺寸的下限来减少错误的圆识别。。。这样我就得到了一个干净的处理过的图像。我不确定。我猜这是因为openCv的Houghcirle在内部使用canny。如果你在边缘工作,精明就不行了。如果您在厚边上工作,canny会给出两个相互靠近的圆,它们相互干扰。但这只是一个猜测。@CésarHoyos这些值是“根据经验”选择的。切换到依赖于半径的maxInlierDist的原因是,拟合边缘的圆的质量可能取决于圆的大小。好吧,你是对的。我必须寻找这个参数值到我自己的项目,然后。谢谢@这取决于你对精确性的需求。如果你的圆圈没有任何变形,并且可以很好地检测到,那么低恒定阈值就可以了。物体被放置在与相机平行的玻璃表面上。我同意图像中存在一些校准问题。。。但这更多的是因为圆心被检测为[90.3,87.5],而不是[90,87]……谢谢米卡。。。我也会试试这个。。。但是你之前的解决方案很有效。。我将尝试修改HoughCircles来消除Canny边缘检测。我会考虑使用多个高斯核和PrHAP。
    GaussianBlur( src_gray, src_gray, Size(11, 11), 5,5);
    
            height, width = img.shape[:2]
    
            #test top edge
            up = (circles[0, :, 0] - circles[0, :, 2]) >= 0
    
            #test left edge
            left = (circles[0, :, 1] - circles[0, :, 2]) >= 0
    
            #test right edge
            right = (circles[0, :, 0] + circles[0, :, 2]) <= width
    
            #test bottom edge
            down = (circles[0, :, 1] + circles[0, :, 2]) <= height
    
            circles = circles[:, (up & down & right & left), :]