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

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

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

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

8

Решение

Основа техники Я делюсь ниже использования 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 (как, например, из второй группы в выходном изображении). Это предложение имеет гораздо больше смысла после того, как вы потратите время на оценку первого изображения, сгенерированного приложением.

Удачи.

7

Другие решения

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

4

По вопросам рекламы [email protected]