34

少し前に私は正方形の検出について質問しました、そしてkarlphillipはまともな結果を思いつきました。

これをさらに一歩進めて、エッジが完全に表示されていない正方形を見つけたいと思います。この例を見てください:

例

何か案は?私はkarlphillipsコードを使用しています:

void find_squares(Mat& image, vector<vector<Point> >& squares)
{
    // blur will enhance edge detection
    Mat blurred(image);
    medianBlur(image, blurred, 9);

    Mat gray0(blurred.size(), CV_8U), gray;
    vector<vector<Point> > contours;

    // find squares in every color plane of the image
    for (int c = 0; c < 3; c++)
    {
        int ch[] = {c, 0};
        mixChannels(&blurred, 1, &gray0, 1, ch, 1);

        // try several threshold levels
        const int threshold_level = 2;
        for (int l = 0; l < threshold_level; l++)
        {
            // Use Canny instead of zero threshold level!
            // Canny helps to catch squares with gradient shading
            if (l == 0)
            {
                Canny(gray0, gray, 10, 20, 3); // 

                // Dilate helps to remove potential holes between edge segments
                dilate(gray, gray, Mat(), Point(-1,-1));
            }
            else
            {
                    gray = gray0 >= (l+1) * 255 / threshold_level;
            }

            // Find contours and store them in a list
            findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

            // Test contours
            vector<Point> approx;
            for (size_t i = 0; i < contours.size(); i++)
            {
                    // approximate contour with accuracy proportional
                    // to the contour perimeter
                    approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);

                    // Note: absolute value of an area is used because
                    // area may be positive or negative - in accordance with the
                    // contour orientation
                    if (approx.size() == 4 &&
                            fabs(contourArea(Mat(approx))) > 1000 &&
                            isContourConvex(Mat(approx)))
                    {
                            double maxCosine = 0;

                            for (int j = 2; j < 5; j++)
                            {
                                    double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
                                    maxCosine = MAX(maxCosine, cosine);
                            }

                            if (maxCosine < 0.3)
                                    squares.push_back(approx);
                    }
            }
        }
    }
}
4

4 に答える 4

45

HoughLinesを使用して、正方形の 4 辺を検出してみてください。次に、コーナーを検出するために、結果として得られる 4 つの線の交点を見つけます。ハフ変換は、ノイズとオクルージョンに対してかなり堅牢であるため、ここで役立つ可能性があります。また、ハフ変換がどのように機能するかを示すインタラクティブなデモがあります (少なくともクールだと思いました:) これは、同じ数学のほとんどを示すレーザークロスセンターを検出する以前の回答の1つです(ただし、1つのコーナーを見つけるだけです)

おそらく両側に複数の線がありますが、交点を見つけることは、インライアとアウトライアを判断するのに役立ちます。コーナーの候補を見つけたら、これらの候補を面積またはポリゴンの「正方形」の度合いでフィルタリングすることもできます。

編集:コードと画像を使用したこれらすべての回答により、私の回答が少し不足していると思われました:)したがって、これを行う方法の実装を次に示します。

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

Point2f computeIntersect(Vec2f line1, Vec2f line2);
vector<Point2f> lineToPointPair(Vec2f line);
bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta);

int main(int argc, char* argv[])
{
    Mat occludedSquare = imread("Square.jpg");

    resize(occludedSquare, occludedSquare, Size(0, 0), 0.25, 0.25);

    Mat occludedSquare8u;
    cvtColor(occludedSquare, occludedSquare8u, CV_BGR2GRAY);

    Mat thresh;
    threshold(occludedSquare8u, thresh, 200.0, 255.0, THRESH_BINARY);

    GaussianBlur(thresh, thresh, Size(7, 7), 2.0, 2.0);

    Mat edges;
    Canny(thresh, edges, 66.0, 133.0, 3);

    vector<Vec2f> lines;
    HoughLines( edges, lines, 1, CV_PI/180, 50, 0, 0 );

    cout << "Detected " << lines.size() << " lines." << endl;

    // compute the intersection from the lines detected...
    vector<Point2f> intersections;
    for( size_t i = 0; i < lines.size(); i++ )
    {
        for(size_t j = 0; j < lines.size(); j++)
        {
            Vec2f line1 = lines[i];
            Vec2f line2 = lines[j];
            if(acceptLinePair(line1, line2, CV_PI / 32))
            {
                Point2f intersection = computeIntersect(line1, line2);
                intersections.push_back(intersection);
            }
        }

    }

    if(intersections.size() > 0)
    {
        vector<Point2f>::iterator i;
        for(i = intersections.begin(); i != intersections.end(); ++i)
        {
            cout << "Intersection is " << i->x << ", " << i->y << endl;
            circle(occludedSquare, *i, 1, Scalar(0, 255, 0), 3);
        }
    }

    imshow("intersect", occludedSquare);
    waitKey();

    return 0;
}

bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta)
{
    float theta1 = line1[1], theta2 = line2[1];

    if(theta1 < minTheta)
    {
        theta1 += CV_PI; // dealing with 0 and 180 ambiguities...
    }

    if(theta2 < minTheta)
    {
        theta2 += CV_PI; // dealing with 0 and 180 ambiguities...
    }

    return abs(theta1 - theta2) > minTheta;
}

// the long nasty wikipedia line-intersection equation...bleh...
Point2f computeIntersect(Vec2f line1, Vec2f line2)
{
    vector<Point2f> p1 = lineToPointPair(line1);
    vector<Point2f> p2 = lineToPointPair(line2);

    float denom = (p1[0].x - p1[1].x)*(p2[0].y - p2[1].y) - (p1[0].y - p1[1].y)*(p2[0].x - p2[1].x);
    Point2f intersect(((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].x - p2[1].x) -
                       (p1[0].x - p1[1].x)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom,
                      ((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].y - p2[1].y) -
                       (p1[0].y - p1[1].y)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom);

    return intersect;
}

vector<Point2f> lineToPointPair(Vec2f line)
{
    vector<Point2f> points;

    float r = line[0], t = line[1];
    double cos_t = cos(t), sin_t = sin(t);
    double x0 = r*cos_t, y0 = r*sin_t;
    double alpha = 1000;

    points.push_back(Point2f(x0 + alpha*(-sin_t), y0 + alpha*cos_t));
    points.push_back(Point2f(x0 - alpha*(-sin_t), y0 - alpha*cos_t));

    return points;
}

注 :画像のサイズを変更した主な理由は、画面に表示できるようにし、処理を高速化するためでした。

キャニー

これはキャニー エッジ検出を使用して、しきい値処理後に検出されるライン数を大幅に削減します。

ここに画像の説明を入力

ハフ変換

次に、ハフ変換を使用して正方形の辺を検出します。 ここに画像の説明を入力

交差点

最後に、すべての直線ペアの交点を計算します。 ここに画像の説明を入力

それが役立つことを願っています!

于 2012-05-10T13:26:46.613 に答える
29

convex hull methodとてもシンプルなものを使ってみました。

ここでは、輪郭の凸包が検出されています。紙の底部の凸状欠陥を取り除きます。

以下はコードです(OpenCV-Pythonで):

import cv2
import numpy as np

img = cv2.imread('sof.jpg')
img = cv2.resize(img,(500,500))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(gray,127,255,0)
contours,hier = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

for cnt in contours:
    if cv2.contourArea(cnt)>5000:  # remove small areas like noise etc
        hull = cv2.convexHull(cnt)    # find the convex hull of contour
        hull = cv2.approxPolyDP(hull,0.1*cv2.arcLength(hull,True),True)
        if len(hull)==4:
            cv2.drawContours(img,[hull],0,(0,255,0),2)

cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

(ここで、私はすべての面で正方形を見つけたわけではありません。必要に応じて自分でやってください。)

以下は私が得た結果です:

ここに画像の説明を入力

これがあなたが必要としていたものであることを願っています。

于 2012-05-10T14:14:07.403 に答える
6

1つ目:画像の残りの部分から白い紙のシートを分離するためのしきい値手法の実験を開始します。これは簡単な方法です。

Mat new_img = imread(argv[1]);

double thres = 200;
double color = 255;
threshold(new_img, new_img, thres, color, CV_THRESH_BINARY);

imwrite("thres.png", new_img);

しかし、より良い結果を提供できる他の選択肢があります。1つは調査 inRange()することであり、もう1つは画像をHSV色空間に変換することによって色を通して検出することです。

このスレッドはまた、主題に関する興味のある議論を提供します。

2番目:この手順の1つを実行した後、結果を直接find_squares():にフィードしてみることができます。

代わりに、バウンディングボックス手法find_squares()を実装することもできます。これは、長方形の領域をより正確に検出できる可能性があります(しきい値の完全な結果がある場合)。私はここここでそれを使用しました。OpenCVには独自のバウンディングボックスチュートリアルがあることに注意してください。

アビッドが彼の答えでfind_squares()指摘したように、以外の別のアプローチは、凸包法を使用することです。コードについては、このメソッドに関するOpenCVのC++チュートリアルを確認してください。

于 2012-05-10T13:28:30.340 に答える