J'ai une image comme ci-dessous:
Je dois connaître le nombre de rectangles, le centre de chaque rectangle et mesurer l’angle entre l’axe parallèle au bord le plus long du rectangle passant par le centre et mesurer l’angle dans le sens inverse des aiguilles d’une montre.J'ai découvert le nombre de Je suis frappé en découvrant le centre et l'angle de réflexion. Trouver le centre à travers des moments ne me donne pas la réponse correcte.
Mon code:
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
Voici comment vous pouvez le faire avec la fonction minAreaRect d’openCV. C'est écrit en C++ mais vous pouvez probablement l'adapter facilement, puisque seules les fonctions OpenCV ont été utilisées.
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
}
résultant en cette image:
comme vous pouvez le constater, les angles ne sont probablement pas ce que vous voulez (car ils utilisent de manière aléatoire la ligne la plus longue ou la plus petite comme référence). Vous pouvez plutôt extraire les côtés les plus longs des rectangles et calculer l'angle manuellement.
Si vous choisissez le bord le plus long des courbes en rotation et en calculez l'angle, il se présente comme suit:
// 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)));
donnant ce résultat, qui devrait être ce que vous recherchez!
EDIT: Il semble que l’opérateur n’utilise pas l’image d’entrée qu’il a postée, car les centres de rectangle de référence se situeraient en dehors de l’image.
En utilisant cette entrée (redimensionnée manuellement mais probablement pas toujours optimale):
J'obtiens ces résultats (les points bleus sont des centres de référence rectangulaires fournis par l'op):
Comparer la référence aux détections:
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)
J'aimerais bien voir l'image d'entrée REELLE, peut-être que le résultat sera encore meilleur.
Voici comment vous pouvez le faire:
approx = cv2.approxPolyDP (cnt, epsilon, True) crée un polygone approximé d'un contour fermé donné. Les segments de ligne dans le polygone ont une longueur variable, ce qui entraîne un calcul de moment incorrect car il s'attend à ce que les points soient échantillonnés à partir d'une grille régulière afin de vous donner le centre correct.
Il y a trois solutions à votre problème: