Подтвердить что ты не робот

Обнаружение полукруга в opencv

Я пытаюсь обнаружить полные круги и полукруг в изображении. enter image description here

Я следую приведенной ниже процедуре: Образ процесса (включая обнаружение края канны) Найдите контуры и нарисуйте их на пустом изображении, чтобы я мог устранить нежелательные компоненты. (Обработанное изображение именно то, что я хочу.) Обнаруживать круги с помощью HoughCircles. И это то, что я получаю.

enter image description here

Я попытался изменить параметры в HoughCircles, но результаты несовместимы, поскольку они варьируются в зависимости от освещения и положения кругов в изображении. Я принимаю или отклоняю круг, основанный на его размере. Поэтому результат неприемлем. Также у меня есть длинный список "приемлемых" кругов, поэтому мне нужно некоторое пособие в параметрах HoughCircle. Что касается полных кругов, это легко - я могу просто найти "округлость" контура. Проблема состоит в полукругах!

Найдите отредактированное изображение перед преобразованием hough enter image description here

4b9b3361

Ответ 1

Используйте houghCircle прямо на вашем изображении, сначала не извлекайте края. Затем проверьте каждый обнаруженный круг, сколько процентов действительно присутствует в изображении:

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;
}

Для этого ввода:

enter image description here

Он выводит этот результат:

enter image description here

Красные круги - это результаты Hough.

Зеленые пробоотборные точки на круге - это листы.

Синие точки - это выбросы.

Выход консоли:

100 % of a circle with radius 27.5045 detected
100 % of a circle with radius 25.3476 detected
58.7302 % of a circle with radius 194.639 detected
50.7937 % of a circle with radius 23.1625 detected
79.3651 % of a circle with radius 7.64853 detected

Если вы хотите протестировать RANSAC вместо Hough, посмотрите this.

Ответ 2

Вот еще один способ сделать это: простая версия RANSAC (большая оптимизация, чтобы улучшить скорость), которая работает на Edge Image.

метод выполняет эти шаги до тех пор, пока не будет отменен

  • выберите случайно 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 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;
    }
    

ввод:

enter image description here

выход:

enter image description here

консольный вывод:

    press q to stop
    accepted circle with 50 % inlier
    circle: center: [358.511, 211.163] radius: 193.849
    accepted circle with 85.7143 % inlier
    circle: center: [45.2273, 171.591] radius: 24.6215
    accepted circle with 100 % inlier
    circle: center: [257.066, 197.066] radius: 27.819
    circle illegal
    30 iterations performed`
Оптимизация

должна включать:

  • используйте все inlier, чтобы соответствовать лучшему кругу

  • Не вычисляйте преобразование расстояния после каждого обнаруженного круга (это довольно дорого). вычислить inlier из point/edge и напрямую удалить из этого списка более строгие ребра.

  • если на изображении много мелких кругов (и/или много шума), вряд ли ударит случайно 3 крайних пиксела или круг. = > сначала попытайтесь определить контур и определите круги для каждого контура. после этого попытайтесь обнаружить все "другие" круги, оставшиеся на изображении.

  • много других вещей

Ответ 3

Полукруг, обнаруженный алгоритмом hough, скорее всего, правильный. Проблема здесь может заключаться в том, что если вы строго контролируете геометрию сцены, то есть точное положение камеры относительно цели, так что ось изображения нормальна к целевой плоскости, вы получите скорее эллипсис, чем круги, проецируемые на изображение самолет. Не говоря уже об искажениях, вызванных оптической системой, которые еще больше вырождают геометрическую фигуру. Если вы полагаетесь на точность здесь, я бы рекомендовал калибровку камеры.

Ответ 4

Лучше попробуйте использовать другое ядро ​​ для gaussian blur. Это поможет вам

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

измените size(i,i),j,j)

Ответ 5

Я знаю, что это немного поздно, но я использовал другой подход, который намного проще. Из cv2.HoughCircles(...) вы получаете центр круга и диаметр (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), :]

Ответ 6

У кого-нибудь есть идея конвертировать этот синтаксис в Python?

if (dt.at(cY, cX) <maxInlierDist)

Я пытаюсь сделать в Python, но застрял в этой строке. Спасибо.