Обнаружение центра и угла прямоугольников в изображении с помощью Opencv

У меня есть изображение, как показано ниже :

Sample image containing many rectangle contours

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

мой код :

import cv2
import numpy as np 
import sys

img = cv2.imread(str(sys.argv[1]),0)
ret,thresh = cv2.threshold(img,127,255,0)
contours,hierarchy = cv2.findContours(thresh,1,2)



for contour in contours:
    area = cv2.contourArea(contour)
    if area>100000:
        contours.remove(contour)




cnt = contours[0]

epsilon = 0.02*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)

print 'No of rectangles',len(approx)


#finding the centre of the contour
M = cv2.moments(cnt)

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

print cx,cy

3 ответов


вот как вы можете сделать это с помощью функции minAreaRect openCV. Он написан на C++ , но, вероятно, вы можете легко адаптироваться, так как использовались почти только функции OpenCV.

    cv::Mat input = cv::imread("../inputData/rectangles.png");

    cv::Mat gray;
    cv::cvtColor(input,gray,CV_BGR2GRAY);

    // since your image has compression artifacts, we have to threshold the image
    int threshold = 200;
    cv::Mat mask = gray > threshold;

    cv::imshow("mask", mask);

    // extract contours
    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    for(int i=0; i<contours.size(); ++i)
    {
        // fit bounding rectangle around contour
        cv::RotatedRect rotatedRect = cv::minAreaRect(contours[i]);

        // read points and angle
        cv::Point2f rect_points[4]; 
        rotatedRect.points( rect_points );

        float  angle = rotatedRect.angle; // angle

        // read center of rotated rect
        cv::Point2f center = rotatedRect.center; // center

        // draw rotated rect
        for(unsigned int j=0; j<4; ++j)
            cv::line(input, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,255,0));

        // draw center and print text
        std::stringstream ss;   ss << angle; // convert float to string
        cv::circle(input, center, 5, cv::Scalar(0,255,0)); // draw center
        cv::putText(input, ss.str(), center + cv::Point2f(-25,25), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(255,0,255)); // print angle
    }

в результате этого изображения:

enter image description here

как вы можете видеть, углы, вероятно, не то, что вы хотите (потому что они случайно использовать больше или меньше строки в качестве ссылки). Вместо этого вы можете извлечь более длинные стороны прямоугольников и вычислить угол вручную.

Если вы выбираете более длинный край повернутых прямых и вычисляете угол от него, это выглядит так:

// choose the longer edge of the rotated rect to compute the angle
        cv::Point2f edge1 = cv::Vec2f(rect_points[1].x, rect_points[1].y) - cv::Vec2f(rect_points[0].x, rect_points[0].y);
        cv::Point2f edge2 = cv::Vec2f(rect_points[2].x, rect_points[2].y) - cv::Vec2f(rect_points[1].x, rect_points[1].y);

        cv::Point2f usedEdge = edge1;
        if(cv::norm(edge2) > cv::norm(edge1))
            usedEdge = edge2;

        cv::Point2f reference = cv::Vec2f(1,0); // horizontal edge


        angle = 180.0f/CV_PI * acos((reference.x*usedEdge.x + reference.y*usedEdge.y) / (cv::norm(reference) *cv::norm(usedEdge)));

давая этот результат, который должен быть тем, что вы ищете!

enter image description here

EDIT: похоже, op не использует входное изображение, которое он опубликовал, потому что центры ссылочных прямоугольников будут находиться вне изображения.

используя этот вход (вручную масштабируется, но, вероятно, все еще нет optimal):

enter image description here

Я получаю эти результаты (синие точки являются ссылочными центрами прямоугольника, предоставляемыми op):

enter image description here

сравнение ссылки с обнаружениями:

reference (x,y,angle)    detection (x,y,angle)
(320,240,0)              (320, 240, 180) // angle 180 is equal to angle 0 for lines
(75,175,90)              (73.5, 174.5, 90)
(279,401,170)            (279.002, 401.824, 169.992)
(507,379,61)             (507.842, 379.75, 61.1443)
(545,95,135)             (545.75, 94.25, 135)
(307,79,37)              (306.756, 77.8384, 37.1042)

Я хотел бы увидеть реальное входное изображение, хотя, возможно, результат будет еще лучше.


вот как вы можете сделать это:

  1. маркировка подключенного компонента для обнаружения каждого шаблона (в вашем случае прямоугольников)
  2. разделите шаблоны в разных изображениях
  3. (необязательно) если шаблон не все прямоугольники, то используйте индексы формы, чтобы различать их
  4. вычислить основную ось с помощью анализа основных компонентов (PCA), это даст вам угол, который вы ищете.

approx = cv2.approxPolyDP (cnt,epsilon,True) создает аппроксимированный многоугольник заданного замкнутого контура. Сегменты линий в полигоне имеют переменную длину, что приводит к неправильному вычислению момента, поскольку он ожидает, что точки будут выбраны из регулярной сетки, чтобы дать вам правильный центр.

есть три решения вашей проблемы:

  1. используйте моменты исходных контуров перед вызовом метода аппроксимации полигона.
  2. использовать drawContours для создания маски областей внутри каждого замкнутого контура, а затем использовать моменты сгенерированной маски для вычисления центра.
  3. примеры точек на единичном расстоянии вдоль каждого сегмента линии вашего замкнутого многоугольника и используйте результирующие коллекции точек для вычисления моментов самостоятельно. Это должно дать вам тот же самый центр.