2

我试图在这张图片中找到居中框的边缘: 黑白盒子

我尝试过使用 dRho=img_width/1000、dTheta=pi/180 和 threshold=250 的 HoughLines 它在此图像上效果很好,缩放到大小的 1/3,但在全尺寸图像上它只是到处都有线条在各个方向...

我能做些什么来调整这个更准确?

4

3 回答 3

5

The code to achieve the result below is a slight modification of the one presented in this answer: how to detect a square:

enter image description here

The original program can be found inside OpenCV, it's called squares.cpp. The code below was modified to search squares only in the first color plane, but as it still detects many squares, at the end of the program I discard all of them except the first, and then call draw_squares() to show what was detected. You can change this easilly to draw all of them and see everything that was detected.

You can do all sorts of thing from now own, including setting a (ROI) region of interest to extract the area that's inside the square (ignore everything else around it).

You can see that the detected rectangle is not perfectly aligned with the lines in the image. You should perform some pre-processing (erode?) operations in the image to decrease the thickness of lines and improve the detection. But from here on it's all on you:

#include <cv.h>
#include <highgui.h>

using namespace cv;

double angle( cv::Point pt1, cv::Point pt2, cv::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);
}

void find_squares(Mat& image, vector<vector<Point> >& squares)
{
    // TODO: pre-processing

    // 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 the first color plane.
    for (int c = 0; c < 1; 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);
                    }
            }
        }
    }
}

void draw_squares(Mat& img, vector<vector<Point> > squares)
{
    for (int i = 0; i < squares.size(); i++)
    {
        for (int j = 0; j < squares[i].size(); j++)
        {
            cv::line(img, squares[i][j], squares[i][(j+1) % 4], cv::Scalar(0, 255, 0), 1, CV_AA);
        }
    }
}


int main(int argc, char* argv[])
{
    Mat img = imread(argv[1]);

    vector<vector<Point> > squares;
    find_squares(img, squares);

    std::cout << "* " << squares.size() << " squares were found." << std::endl;

    // Ignore all the detected squares and draw just the first found
    vector<vector<Point> > tmp;
    if (squares.size() > 0)
    {
        tmp.push_back(squares[0]);
        draw_squares(img, tmp);
    }
    //imshow("squares", img);
    //cvWaitKey(0);

    imwrite("out.png", img);

    return 0;
}
于 2012-04-30T12:58:36.177 回答
3

在调整图像大小时,通常首先使用滤波器(例如高斯)对图像进行模糊处理,以去除高频。调整大小效果更好的事实可能是因为您的原始图像有点嘈杂。

首先尝试模糊图像,例如使用cv::GaussianBlur(src, target, Size(0,0), 1.5),然后它应该相当于调整大小。(理论忘记了,如果不行就试试3和6)

于 2012-04-30T07:27:42.320 回答
3

尝试使用带有侵蚀过滤器的预处理通道。它会给你与缩小相同的效果 - 线条会变得更细并且不会同时消失。

正如 chaiy 所说,“模糊”滤镜也是一个好主意。

这样(模糊)它将变成类似http://www.ic.uff.br/~laffernandes/projects/kht/index.html (基于内核的霍夫变换)

于 2012-04-30T07:19:17.947 回答