OpenCV Line Detection

Я пытаюсь найти края центрированной рамки на этом изображении:черно-белая коробка

Я попытался использовать HoughLines, используя dRho=img_width/1000, dTheta=pi/180 и threshold=250. Это прекрасно работает на этом изображении, масштабируется до 1/3 от размера, но на полноразмерном изображении он просто везде получает линии во всех направлениях...

Что я могу сделать, чтобы настроить это, чтобы быть более точным?

3 ответа

Решение

Код для достижения результата ниже является небольшой модификацией кода, представленного в этом ответе: как определить квадрат:

Оригинальную программу можно найти внутри OpenCV, она называется squares.cpp. Приведенный ниже код был изменен для поиска квадратов только в первой цветовой плоскости, но, поскольку он все еще обнаруживает много квадратов, в конце программы я отбрасываю все из них, кроме первого, и затем вызываю draw_squares() чтобы показать, что было обнаружено. Вы можете изменить это легко, чтобы нарисовать их все и увидеть все, что было обнаружено.

Теперь вы можете делать все что угодно, в том числе устанавливать интересующую область (ROI) для извлечения области, которая находится внутри квадрата (игнорируйте все остальное вокруг него).

Вы можете видеть, что обнаруженный прямоугольник не совсем выровнен по линиям на изображении. Вы должны выполнить некоторые операции предварительной обработки (размыть?) На изображении, чтобы уменьшить толщину линий и улучшить обнаружение. Но с этого момента все это на вас

#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;
}

Попробуйте использовать проход предварительной обработки с эрозионным фильтром. Это даст вам тот же эффект, что и уменьшение масштаба - линии станут тоньше и не исчезнут одновременно.

Фильтр "Blur" также является хорошей идеей, как говорит Чай.

Таким образом (с размытием) он станет чем-то вроде http://www.ic.uff.br/~laffernandes/projects/kht/index.html (ядро на основе преобразования Хафа)

При изменении размера изображения, как правило, изображение сначала размывается фильтром, например гауссовым, чтобы избавиться от высоких частот. Тот факт, что измененный размер работает лучше, вероятно, потому что ваше оригинальное изображение как-то шумно.

Попробуйте сначала смазать изображение, например, с помощью cv::GaussianBlur(src, target, Size(0,0), 1.5), то это должно быть эквивалентно изменению размера. (Он забыл теорию, если она не работает, попробуйте также 3 и 6)

Другие вопросы по тегам