2015-12-12 4 views
6

У меня есть изображение, как показано ниже:Detect центра и угла прямоугольника в изображении с помощью 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

извлечение контуров с помощью findContours и функция использования minAreaRect для вычисления RotatedRect – Micka

+0

проверить мой ответ [здесь] (http://stackoverflow.com/questions/33860019/opencv-filter-blobs-by-width-and-height/33860887# 33860887). вы найдете пример реализации C++, который указал @Micka. – sturkmen

+2

Для прямоугольников решение Micka является оптимальным, поскольку в более общем случае вы также можете посмотреть метод на основе PCA: http://docs.opencv.org/master/d1/dee/tutorial_introduction_to_pca.html#gsc.tab=0 –

ответ

8

Так вы можете сделать это с помощью функции 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: Похоже, оп не использует входное изображение, которое он отправил, потому что ссылки прямоугольник центры будут лежать вне образа.

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

enter image description here

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

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) 

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

+0

Можете ли вы рассказать мне, как найти угол между осью, параллельной более длинному краю, с горизонтальной? Это та часть, с которой я поражен. Должен ли я дегенерировать края, чтобы найти более длинный край и вычислить угол вручную? –

+0

обновленный код и результат. Это довольно просто. Не стесняйтесь повышать, если это вам поможет. – Micka

+0

Спасибо большое .. Это очень помогло. Я не правильно получаю центры прямоугольника, если я использую моменты. Эти фактические центры прямоугольников и углы (х, у, угол) (320,240,0) (75,175,90) (279.401.170) (507,379,61) (545,95,135) (307,79,37) Когда я использую моменты, чтобы найти центр контура, это дает мне другой ответ. Почему это так? –

2

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

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

Вы можете улучшить ответ с помощью фрагмента кода. Кроме того, поскольку здесь есть только прямоугольники, 'minAreaRect' будет достаточно. – Miki

1

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

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

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