web-dev-qa-db-ja.com

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
12

これは、openCVのminAreaRect関数でそれを行う方法です。これは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

ご覧のとおり、角度はおそらく望んでいるものではありません(参照としてランダムに長い線または小さい線を使用しているため)。代わりに、長方形の長い方の辺を抽出して、手動で角度を計算できます。

回転した四角形の長い方のEdgeを選択し、そこから角度を計算すると、次のようになります。

// 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

編集:参照長方形の中心が画像の外側にあるため、opは彼が投稿した入力画像を使用していないようです。

この入力を使用する(手動で再スケーリングされますが、おそらくまだ最適ではありません):

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)

REALの入力画像を見たいのですが、多分結果はもっと良くなるでしょう。

15
Micka

これを行う方法は次のとおりです。

  1. 各パターン(あなたの場合は長方形)を検出するための接続コンポーネントのラベル付け
  2. 異なる画像のパターンを分離する
  3. (オプション)パターンがすべての長方形ではない場合、形状インデックスを使用してそれらを区別します
  4. 主成分分析(PCA)を使用して主軸を計算すると、目的の角度が得られます。
2
FiReTiTi

approx = cv2.approxPolyDP(cnt、epsilon、True)は、指定された閉じた輪郭の近似ポリゴンを作成します。ポリゴンのラインセグメントは可変長であるため、正しいグリッドが得られるようにポイントが通常のグリッドからサンプリングされることが期待されているため、モーメントの計算が正しくありません。

問題に対する3つの解決策があります。

  1. ポリゴン近似のメソッドを呼び出す前に、元の輪郭のモーメントを使用してください。
  2. DrawContoursを使用して各閉じた輪郭の内側の領域のマスクを生成し、生成されたマスクのモーメントを使用して中心を計算します。
  3. 閉じたポリゴンの各ラインセグメントに沿って単位距離でポイントをサンプリングし、得られたポイントのコレクションを使用してモーメントを計算します。これで同じ中心が得られます。
1
Ajay