且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

正方形检测没有找到正方形

更新时间:2022-10-19 12:59:12

下面的源代码展示了 Square Detector 程序的一个小变化.它并不完美,但它说明了解决您的问题的一种方法.

您可以此代码与原始代码进行比较并检查所做的所有更改,但主要的更改是:

  • 将阈值级别的数量减少到 2.

  • findSquares()的开头,扩大图像检测细小的白色方块,然后模糊整个图像,因此算法不会将大海和天空检测为单个方块.

编译后,使用以下语法运行应用程序:./app

//平方检测器"程序.//它顺序加载多个图像并尝试在其中找到正方形//每张图片#include "highgui.h"#include "cv.h"#include #include <math.h>#include 使用命名空间 cv;使用命名空间标准;无效的帮助(){cout<<"
一个使用金字塔缩放、Canny、轮廓、轮廓简化和
的程序"内存存储(所有人都可以找到)
""图像列表中的方块 pic1-6.png
""返回在图像上检测到的正方形序列.
""序列存储在指定的内存中
"呼叫:
""./平方
""使用 OpenCV 版本 %s
" <<CV_VERSION <<"
" <<结束;}整数阈值 = 50,N = 2;//karlphillip:将 N 减少到 2,是 11.const char* wndname = "平方检测演示";//辅助函数://找到向量之间夹角的余弦值//从 pt0->pt1 和从 pt0->pt2双角(点pt1,点pt2,点pt0){双 dx1 = pt1.x - pt0.x;双 dy1 = pt1.y - pt0.y;双 dx2 = pt2.x - pt0.x;双 dy2 = pt2.y - pt0.y;返回 (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);}//返回在图像上检测到的正方形序列.//序列存储在指定的内存中void findSquares( const Mat& image, vector<vector<Point> >& squares ){squares.clear();Mat pyr, timg, gray0(image.size(), CV_8U), gray;//karlphillip: 扩大图像,以便此技术可以检测白色方块,垫出(图像);扩张(出,出,垫(),点(-1,-1));//然后模糊它,使海洋/大海成为一大段,以避免将它们检测为 2 个大方块.中值模糊(出,出,7);//缩小和放大图像以滤除噪声pyrDown(out, pyr, Size(out.cols/2, out.rows/2));pyrUp(pyr, timg, out.size());矢量>轮廓;//在图像的每个颜色平面中找到正方形for( int c = 0; c = (l+1)*255/N;}//找到轮廓并将它们全部存储为列表findContours(灰色,轮廓,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);矢量<点>约;//测试每个轮廓for( size_t i = 0; i 1000 & &isContourConvex(Mat(approx)) ){双最大余弦 = 0;for( int j = 2; j "<<结束;返回-1;}//static const char* names[] = { "pic1.png", "pic2.png", "pic3.png",//"pic4.png", "pic5.png", "pic6.png", 0 };静态常量字符*名称[] = { argv[1], 0 };帮助();命名窗口( wndname, 1 );矢量>正方形;for( int i = 0; names[i] != 0; i++ ){Mat image = imread(names[i], 1);如果(图像.空()){cout<<无法加载" <<名称[i] 

输出:

I'm using the program squares.c available in the samples of OpenCV libraries. It works well with every image, but I really can't figure it out why it doesn't recognize the square drawn in that image

http://desmond.imageshack.us/Himg12/scaled.php?server=12&filename=26725680.jpg&res=medium

After CANNY:

After DILATE:

The RESULT image (in red) http://img267.imageshack.us/img267/8016/resultuq.jpg

As you can see, the square is NOT detected.

After the detection I need to extract the area contained in the square...How is it possible without a ROI?

The source code below presents a small variation of the Square Detector program. It's not perfect, but it illustrates one way to approach your problem.

You can diff this code to the original and check all the changes that were made, but the main ones are:

  • Decrease the number of threshold levels to 2.

  • In the beginning of findSquares(), dilate the image to detect the thin white square, and then blur the entire image so the algorithm doesn't detect the sea and the sky as individual squares.

Once compiled, run the application with the following syntax: ./app <image>

// The "Square Detector" program.
// It loads several images sequentially and tries to find squares in
// each image

#include "highgui.h"
#include "cv.h"

#include <iostream>
#include <math.h>
#include <string.h>

using namespace cv;
using namespace std;

void help()
{
        cout <<
        "
A program using pyramid scaling, Canny, contours, contour simpification and
"
        "memory storage (it's got it all folks) to find
"
        "squares in a list of images pic1-6.png
"
        "Returns sequence of squares detected on the image.
"
        "the sequence is stored in the specified memory storage
"
        "Call:
"
        "./squares
"
    "Using OpenCV version %s
" << CV_VERSION << "
" << endl;
}


int thresh = 50, N = 2; // karlphillip: decreased N to 2, was 11.
const char* wndname = "Square Detection Demo";

// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
double angle( Point pt1, Point pt2, Point pt0 )
{
    double dx1 = pt1.x - pt0.x;
    double dy1 = pt1.y - pt0.y;
    double dx2 = pt2.x - pt0.x;
    double dy2 = pt2.y - pt0.y;
    return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}

// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
void findSquares( const Mat& image, vector<vector<Point> >& squares )
{
    squares.clear();

    Mat pyr, timg, gray0(image.size(), CV_8U), gray;

    // karlphillip: dilate the image so this technique can detect the white square,
    Mat out(image);
    dilate(out, out, Mat(), Point(-1,-1));
    // then blur it so that the ocean/sea become one big segment to avoid detecting them as 2 big squares.
    medianBlur(out, out, 7);

    // down-scale and upscale the image to filter out the noise
    pyrDown(out, pyr, Size(out.cols/2, out.rows/2));
    pyrUp(pyr, timg, out.size());
    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(&timg, 1, &gray0, 1, ch, 1);

        // try several threshold levels
        for( int l = 0; l < N; l++ )
        {
            // hack: use Canny instead of zero threshold level.
            // Canny helps to catch squares with gradient shading
            if( l == 0 )
            {
                // apply Canny. Take the upper threshold from slider
                // and set the lower to 0 (which forces edges merging)
                Canny(gray0, gray, 0, thresh, 5);
                // dilate canny output to remove potential
                // holes between edge segments
                dilate(gray, gray, Mat(), Point(-1,-1));
            }
            else
            {
                // apply threshold if l!=0:
                //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
                gray = gray0 >= (l+1)*255/N;
            }

            // find contours and store them all as a list
            findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

            vector<Point> approx;

            // test each contour
            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);

                // square contours should have 4 vertices after approximation
                // relatively large area (to filter out noisy contours)
                // and be convex.
                // 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++ )
                    {
                        // find the maximum cosine of the angle between joint edges
                        double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
                        maxCosine = MAX(maxCosine, cosine);
                    }

                    // if cosines of all angles are small
                    // (all angles are ~90 degree) then write quandrange
                    // vertices to resultant sequence
                    if( maxCosine < 0.3 )
                        squares.push_back(approx);
                }
            }
        }
    }
}


// the function draws all the squares in the image
void drawSquares( Mat& image, const vector<vector<Point> >& squares )
{
    for( size_t i = 0; i < squares.size(); i++ )
    {
        const Point* p = &squares[i][0];
        int n = (int)squares[i].size();
        polylines(image, &p, &n, 1, true, Scalar(0,255,0), 3, CV_AA);
    }

    imshow(wndname, image);
}


int main(int argc, char** argv)
{
    if (argc < 2)
    {
        cout << "Usage: ./program <file>" << endl;
        return -1;
    }

//    static const char* names[] = { "pic1.png", "pic2.png", "pic3.png",
//        "pic4.png", "pic5.png", "pic6.png", 0 };
    static const char* names[] = { argv[1], 0 };

    help();
    namedWindow( wndname, 1 );
    vector<vector<Point> > squares;

    for( int i = 0; names[i] != 0; i++ )
    {
        Mat image = imread(names[i], 1);
        if( image.empty() )
        {
            cout << "Couldn't load " << names[i] << endl;
            continue;
        }

        findSquares(image, squares);
        drawSquares(image, squares);
        imwrite("out.jpg", image);

        int c = waitKey();
        if( (char)c == 27 )
            break;
    }

    return 0;
}

Outputs: