Подтвердить что ты не робот

Получить однострочное представление для нескольких близких строк, сгруппированных вместе в opencv

Я обнаружил строки в изображении и нарисовал их в отдельном файле изображения в OpenCv С++ с использованием метода HoughLinesP. Ниже приводится часть полученного изображения. На самом деле есть сотни маленьких и тонких линий, которые образуют большую одиночную линию.

enter image description here

Но я хочу несколько строк, которые представляют все эти числа строк. Более строгие линии должны быть объединены вместе, чтобы сформировать одну линию. Например, выше набор строк должен быть представлен только тремя отдельными строками, как показано ниже.

enter image description here

Ожидаемый результат, как указано выше. Как выполнить эту задачу.



До сих пор получается результат ответа акарсакова.


(отдельные классы строк приведены в разных цветах). Обратите внимание, что этот результат - это исходное полное изображение, над которым я работаю, но не раздел образца, который я использовал в вопросе

enter image description here

4b9b3361

Ответ 1

Если вы не знаете количество строк на изображении, вы можете использовать функцию cv::partition для разделения строк на группу эквивалентности.

Я предлагаю вам следующую процедуру:

  • Разделите свои строки, используя cv::partition. Вам нужно указать хорошую функцию предиката. Это действительно зависит от линий, которые вы извлекаете из изображения, но я думаю, что он должен проверять следующие условия:

    • Угол между линиями должен быть довольно небольшим (например, менее 3 градусов). Используйте dot product для вычисления углового косинуса.
    • Расстояние между центрами сегментов должно быть меньше половины максимальной длины двух сегментов.

Например, он может быть реализован следующим образом:

bool isEqual(const Vec4i& _l1, const Vec4i& _l2)
{
    Vec4i l1(_l1), l2(_l2);

    float length1 = sqrtf((l1[2] - l1[0])*(l1[2] - l1[0]) + (l1[3] - l1[1])*(l1[3] - l1[1]));
    float length2 = sqrtf((l2[2] - l2[0])*(l2[2] - l2[0]) + (l2[3] - l2[1])*(l2[3] - l2[1]));

    float product = (l1[2] - l1[0])*(l2[2] - l2[0]) + (l1[3] - l1[1])*(l2[3] - l2[1]);

    if (fabs(product / (length1 * length2)) < cos(CV_PI / 30))
        return false;

    float mx1 = (l1[0] + l1[2]) * 0.5f;
    float mx2 = (l2[0] + l2[2]) * 0.5f;

    float my1 = (l1[1] + l1[3]) * 0.5f;
    float my2 = (l2[1] + l2[3]) * 0.5f;
    float dist = sqrtf((mx1 - mx2)*(mx1 - mx2) + (my1 - my2)*(my1 - my2));

    if (dist > std::max(length1, length2) * 0.5f)
        return false;

    return true;
}

Угадайте, что у вас есть свои строки в vector<Vec4i> lines;. Затем вы должны вызвать cv::partition следующим образом:

vector<Vec4i> lines;
std::vector<int> labels;
int numberOfLines = cv::partition(lines, labels, isEqual);

Вам нужно позвонить cv::partition один раз, и он будет группировать все строки. Вектор labels будет хранить для каждой метки строки кластера, к которой он принадлежит. См. документация для cv::partition

  1. После того, как вы получите все группы строк, вы должны объединить их. Я предлагаю вычислять средний угол всех линий в группе и оценивать "пограничные" точки. Например, если угол равен нулю (т.е. Все линии почти горизонтальны), это будут самые левые и самые правые точки. Остается только нарисовать линию между этими точками.

Я заметил, что все строки в ваших примерах являются горизонтальными или вертикальными. В таком случае вы можете вычислить точку, которая является средней для всех сегментных центров и "пограничных" точек, а затем просто нарисовать горизонтальную или вертикальную линию, ограниченную "пограничными" точками через центральную точку.

Обратите внимание, что cv::partition занимает время O (N ^ 2), поэтому, если вы обрабатываете огромное количество строк, это может занять много времени.

Надеюсь, это поможет. Я использовал такой подход для аналогичной задачи.

Ответ 2

Прежде всего, я хочу отметить, что ваше исходное изображение находится под небольшим углом, поэтому ваш ожидаемый результат кажется мне совсем немного. Я предполагаю, что вы в порядке с линиями, которые не являются на 100% вертикальными в вашем выходе, потому что они слегка отключены на вашем входе.

Mat image;
Mat binary = image > 125;  // Convert to binary image

// Combine similar lines
int size = 3;
Mat element = getStructuringElement( MORPH_ELLIPSE, Size( 2*size + 1, 2*size+1 ), Point( size, size ) );
morphologyEx( mask, mask, MORPH_CLOSE, element );

Пока это дает этот образ:

Эти линии не имеют угол 90 градусов, потому что исходное изображение отсутствует.

Вы также можете закрыть промежуток между строками:

Mat out = Mat::zeros(mask.size(), mask.type());

vector<Vec4i> lines;
HoughLinesP(mask, lines, 1, CV_PI/2, 50, 50, 75);
for( size_t i = 0; i < lines.size(); i++ )
{
    Vec4i l = lines[i];
    line( out, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255), 5, CV_AA);
}

Если эти линии слишком толстые, у меня был успех, истощающий их:

size = 15;
Mat eroded;
cv::Mat erodeElement = getStructuringElement( MORPH_ELLIPSE, cv::Size( size, size ) );
erode( mask, eroded, erodeElement );

Ответ 3

Я бы рекомендовал использовать HoughLines из OpenCV.

void HoughLines (изображение InputArray, строки OutputArray, double rho, double theta, int threshold, double srn = 0, double stn = 0)

Вы можете настроить с помощью rho и theta возможную ориентацию и положение линий, которые вы хотите наблюдать. В вашем случае theta = 90 ° будет прекрасным (только вертикальные и горизонтальные линии).

После этого вы можете получить уникальные уравнения линии с координатами Плюккера. И оттуда вы можете применить среднее значение K с тремя центрами, которые должны соответствовать приблизительно вашим 3 строкам во втором изображении.

PS: Я увижу, могу ли я протестировать весь процесс с вашим изображением