Обнаружение канавок OpenCV

введите здесь описание изображенияУ меня есть изображения поверхности с множеством канавок. В большинстве случаев края канавок образуют параллельные линии, поэтому преобразование Кэнни и Хафа очень хорошо работает для обнаружения линий и получения некоторых характеристик. Однако в некоторых местах канавка повреждена, и края больше не параллельны.

Я ищу простой способ проверить, является ли определенный край прямой линией или есть ли какие-либо пробелы или отклонения от прямой линии. Я думаю о чем-то вроде параметра квадрата R в линейной интерполяции, но здесь мне нужен параметр, который больше зависит от местоположения. Есть ли у вас какие-либо другие мысли, как охарактеризовать края?

Я прикрепил изображение канавки после обнаружения хитрых краев. Здесь края прямые, а канавки в порядке. К сожалению, на данный момент у меня нет доступа к фотографиям с поврежденной канавкой. Однако на изображениях с поврежденной канавкой линии будут иметь большие разрывы (не менее 10% от размера изображения) или не будут параллельны.


person marc    schedule 23.11.2012    source источник
comment
Может быть, вы могли бы включить образец изображения?   -  person Paul R    schedule 23.11.2012
comment
Добро пожаловать в Stackoverflow. Пожалуйста, внимательно просмотрите мой ответ, а затем проголосуйте за него, если он вам помог. Вы можете установить флажок рядом с ним, чтобы выбрать его в качестве официального ответа на ваш вопрос. Делая это, вы поможете будущим посетителям, таким как вы и мы, поддерживая эту тему организованной.   -  person karlphillip    schedule 28.11.2012


Ответы (2)


Ядро метода, которым я делюсь ниже, использует cv::HoughLinesP() для поиска сегментов линий в изображении в градациях серого.

Приложение начинается с загрузки входного изображения в оттенках серого. Затем он выполняет базовую операцию предварительной обработки для улучшения определенных характеристик изображения с целью улучшения обнаружения, выполняемого cv::HoughLinesP():

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

#include <algorithm>

// Custom sort method adapted from: http://stackoverflow.com/a/328959/176769
// This is used later by std::sort()
struct sort_by_y_coord 
{
    bool operator ()(cv::Vec4i const& a, cv::Vec4i const& b) const 
    {
        if (a[1] < b[1]) return true;

        if (a[1] > b[1]) return false;

        return false;
    }
};


int main()
{
    /* Load input image as grayscale */

    cv::Mat src = cv::imread("13531682.jpg", 0);

    /* Pre-process the image to enhance the characteristics we are interested at */

    medianBlur(src, src, 5);

    int erosion_size = 2;
    cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS,
                                       cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
                                       cv::Point(erosion_size, erosion_size) );
    cv::erode(src, src, element);
    cv::dilate(src, src, element);

    /* Identify all the lines in the image */

    cv::Size size = src.size();
    std::vector<cv::Vec4i> total_lines;
    cv::HoughLinesP(src, total_lines, 1, CV_PI/180, 100, size.width / 2.f, 20);

    int n_lines = total_lines.size();
    std::cout << "* Total lines: "<< n_lines << std::endl;

    cv::Mat disp_lines(size, CV_8UC1, cv::Scalar(0, 0, 0));

    // For debugging purposes, the block below writes all the lines into disp_lines
    // for (unsigned i = 0; i < n_lines; ++i)
    // {
    //     cv::line(disp_lines, 
    //              cv::Point(total_lines[i][0], total_lines[i][2]),
    //              cv::Point(total_lines[i][3], total_lines[i][4]), 
    //              cv::Scalar(255, 0 ,0));
    // }
    // cv::imwrite("total_lines.png", disp_lines);

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

На этом этапе нам нужно отсортировать наш вектор линий, потому что cv::HoughLinesP() этого не делает, и нам нужен отсортированный вектор, чтобы иметь возможность идентифицировать группы линий, измеряя и сравнивая расстояние между линиями:

    /* Sort lines according to their Y coordinate. 
       The line closest to Y == 0 is at the first position of the vector.
    */

    sort(total_lines.begin(), total_lines.end(), sort_by_y_coord());

    /* Separate them according to their (visible) groups */ 

    // Figure out the number of groups by distance between lines
    std::vector<int> idx_of_groups;   // stores the index position where a new group starts
    idx_of_groups.push_back(0); // the first line indicates the start of the first group 

    // The loop jumps over the first line, since it was already added as a group
    int y_dist = 35; // the next groups are identified by a minimum of 35 pixels of distance  
    for (unsigned i = 1; i < n_lines; i++)  
    {
        if ((total_lines[i][5] - total_lines[i-1][6]) >= y_dist) 
        {
            // current index marks the position of a new group
            idx_of_groups.push_back(i); 
            std::cout << "* New group located at line #"<< i << std::endl;           
        }
    }

    int n_groups = idx_of_groups.size();
    std::cout << "* Total groups identified: "<< n_groups << std::endl;

Последняя часть приведенного выше кода просто сохраняет позиции индекса вектора строк в новом vector<int>, поэтому мы знаем, какие строки начинают новую группу.

Например, предположим, что в новом векторе хранятся следующие индексы: 0 4 8 12. Помните: они определяют начало каждой группы. Это означает, что конечные строки групп: 0, 4-1, 4, 8-1, 8, 12-1, 12.

Зная это, пишем следующий код:

    /* Mark the beginning and end of each group */

    for (unsigned i = 0; i < n_groups; i++)  
    {
        // To do this, we discard the X coordinates of the 2 points from the line, 
        // so we can draw a line from X=0 to X=size.width

        // beginning
        cv::line(disp_lines, 
                 cv::Point(0, total_lines[ idx_of_groups[i] ][7]),
                 cv::Point(size.width, total_lines[ idx_of_groups[i] ][8]), 
                 cv::Scalar(255, 0 ,0));

        // end      
        if (i != n_groups-1)
        {
            cv::line(disp_lines, 
                     cv::Point(0, total_lines[ idx_of_groups[i+1]-1 ][9]),
                     cv::Point(size.width, total_lines[ idx_of_groups[i+1]-1 ][10]), 
                     cv::Scalar(255, 0 ,0));
        }
    }
    // mark the end position of the last group (not done by the loop above)    
    cv::line(disp_lines, 
             cv::Point(0, total_lines[n_lines-1][11]),
             cv::Point(size.width, total_lines[n_lines-1][12]), 
             cv::Scalar(255, 0 ,0));

    /* Save the output image and display it on the screen */

    cv::imwrite("groups.png", disp_lines);

    cv::imshow("groove", disp_lines);
    cv::waitKey(0);
    cv::destroyWindow("groove");

    return 0;
}

И результирующее изображение:

Это не идеальное совпадение, но близко к нему. С небольшими изменениями здесь и там этот подход может стать намного лучше. Я бы начал с написания более разумной логики для sort_by_y_coord, которая должна отбрасывать линии с малым расстоянием между координатами X (т. е. небольшие сегменты линий), а также линии, которые не идеально выровнены по оси X (например, из второй группы). на выходном изображении). Это предложение имеет гораздо больше смысла после того, как вы потратите время на оценку первого изображения, созданного приложением.

Удачи.

person karlphillip    schedule 28.11.2012

Что сразу приходит на ум, так это преобразование Хафа. Это схема голосования в пространстве строк, которая берет каждую возможную строку и дает вам балл за нее. В коде, на который я ссылался выше, вы можете просто установить порог, который приближается к ~ 10% испорченных грувов/линий.

person thealmightygrant    schedule 23.11.2012
comment
Если линии не совсем параллельны или изогнуты, тогда Марк может использовать Хаф в NxN областях изображения, чтобы найти кусочно-прямые линии. Затем короткие сегменты линий можно было соединить, чтобы найти кривые. - person Rethunk; 26.11.2012