Какой алгоритм CV следует выбрать для обнаружения углов? (Обнаружение углов, OpenCV)

Я работаю над инструментом CV для анализа изображений и использую 4 точки для формирования матрицы преобразования (карты) для коррекции изображения (перспективная проекция — cv.four_point_transform). У меня есть набор исходных изображений, полученных с тепловизора с наличием дисторсии. Я успешно применяю конвейер стандартных функций OpenCV. Вы можете увидеть это на рисунках ниже (рис. 1). Обратите внимание, что основной пайплайн состоит из следующих шагов:

  1. Коррекция дисторсии.
  2. Сглаживание двусторонним фильтром.
  3. Порог.
  4. Угловой детектор Харриса.
  5. Эрозия.
  6. Средневзвешенное значение точки на плоскости по облаку точек после эрозии. На изображении «Цель» вы видите 4 точки, реальный размер 1 пиксель (увеличен для наглядности).
  • Какой подход, по вашему мнению, легче реализовать в будущем?
  • Как правильно подойти к удалению паразитных точек?
  • Существуют ли более простые подходы к обнаружению углов?

К сожалению, мне попадались случаи, когда детектор углов Харриса не справляется и не определяет тупые углы. Я начал тестировать различные подходы, такие как:

  1. Порог -> Контуры -> Приблизительный контур -> Точки.
  2. «Порог» -> «Обнаружение осторожных краев» -> «Расширение» -> «БЫСТРО».
  3. Порог -> Обнаружение осторожных краев -> Расширение -> SIFT.
  4. Порог -> Обнаружение хитрых краев -> Расширение -> Вероятностные линии Хафа -> Точки Бентли-Оттмана.

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

Рис. 1 — Успешное обнаружение

Рис. 2 - Неудачное обнаружение

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

Cris Luengo 30.03.2023 04:31

@CrisLuengo Я согласен с вами в том, что нет особой причины использовать какой-либо из угловых детекторов. Я выбрал это только потому, что моя форма почти всегда представляет собой простой четырехугольник, а детектор Харриса доступен здесь и сейчас в одной строке. Спасибо за ваши идеи, также планирую реализовать один из способов определения 4-х линий - подгонку прямых линий к точкам контура. К сожалению, я использовал только вероятностные линии Хафа, но не мог вручную подобрать хорошие параметры. Поскольку первый подход к упрощению контура до четырех линий вносит очень большую погрешность.

niko.zvt 30.03.2023 05:09

@CrisLuengo Подгонка четырехугольника к точкам контура - stackoverflow.com/a/41143028/12799969 Подгонка прямых линий к точкам контура - stackoverflow.com/a/59905978/12799969

niko.zvt 30.03.2023 05:10

Вы можете компенсировать дисторсию объектива. вы также можете настроить свои пороги для auxPolyDP. или вы можете отфильтровать углы «неудачного» обнаружения по углу угла.

Christoph Rackwitz 30.03.2023 10:29
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
124
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Поэтому...

Я бы подумал об использовании некоторого процесса обнаружения углов, который дает «достаточный» результат обнаружения. Где «достаточно» означает, что по крайней мере необходимые 4 угла могут быть обнаружены (не обнаружение их - самая серьезная проблема).

Другими словами, разрешение «Дополнительно могут быть обнаружены некоторые ненужные углы». (конечно, "слишком много" нельзя)

И затем я бы рассмотрел некоторый подход, подобный RANSAC, для получения матрицы преобразования.

Спасибо за вашу идею. Действительно, подход RANSAC выглядит многообещающе, когда у нас есть существенный набор точек. Теоретически для этого случая подойдет, с практической точки зрения я нашел более примитивный подход, о котором расскажу ниже.

niko.zvt 31.03.2023 14:01

Вы могли бы...

  • компенсировать дисторсию объектива. Тогда у вас вообще не было бы тупых углов, и все края были бы прямыми. Я считаю, что это лучший путь вперед.

  • см. о настройке фокуса камеры. Да, я понимаю, что это тепловизионная камера. Они могут это сделать.

  • отрегулируйте пороги для approxPolyDP, чтобы убрать некоторые тупые углы.

  • просто фильтровать углы "неудачного" обнаружения по углу угла, отбрасывая достаточно тупые. Либо сделайте это по порогу угла, либо упорядочив все углы и отбросив все, кроме четырех лучших. Вы должны быть чувствительны к длинам ребер, попадающих в угол. Если они короткие, угол в любом случае может не иметь значения.

Спасибо за подсказку интересной функции для упрощения (аппроксимации) ломаных на основе алгоритма Дугласа-Пекера. Это может пригодиться мне в будущем. Я «достаточно» успешно исправил дисторсию на основе калибровки тепловизионной камеры. К тому же мне удалось найти более простой и примитивный подход, поделюсь им в ответе.

niko.zvt 31.03.2023 14:08
Ответ принят как подходящий

Не так давно Дев Аггарвал (@dev-aggarwal) опубликовал интересный и простой подход к определению подогнанного четырехугольника. Этот подход работает для меня. Смотрите оригинальный пост по ссылке.

Теоретическая схема алгоритма представлена ​​в статье Оптимизация усеченного представления для увеличения области изображения объекта.

Дублирую исходный код здесь:

import sympy

def appx_best_fit_ngon(img, n: int = 4) -> list[(int, int)]:
    # convex hull of the input mask
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    contours, _ = cv2.findContours(
        img_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
    )
    hull = cv2.convexHull(contours[0])
    hull = np.array(hull).reshape((len(hull), 2))

    # to sympy land
    hull = [sympy.Point(*pt) for pt in hull]

    # run until we cut down to n vertices
    while len(hull) > n:
        best_candidate = None

        # for all edges in hull ( <edge_idx_1>, <edge_idx_2> ) ->
        for edge_idx_1 in range(len(hull)):
            edge_idx_2 = (edge_idx_1 + 1) % len(hull)

            adj_idx_1 = (edge_idx_1 - 1) % len(hull)
            adj_idx_2 = (edge_idx_1 + 2) % len(hull)

            edge_pt_1 = sympy.Point(*hull[edge_idx_1])
            edge_pt_2 = sympy.Point(*hull[edge_idx_2])
            adj_pt_1 = sympy.Point(*hull[adj_idx_1])
            adj_pt_2 = sympy.Point(*hull[adj_idx_2])

            subpoly = sympy.Polygon(adj_pt_1, edge_pt_1, edge_pt_2, adj_pt_2)
            angle1 = subpoly.angles[edge_pt_1]
            angle2 = subpoly.angles[edge_pt_2]

            # we need to first make sure that the sum of the interior angles the edge
            # makes with the two adjacent edges is more than 180°
            if sympy.N(angle1 + angle2) <= sympy.pi:
                continue

            # find the new vertex if we delete this edge
            adj_edge_1 = sympy.Line(adj_pt_1, edge_pt_1)
            adj_edge_2 = sympy.Line(edge_pt_2, adj_pt_2)
            intersect = adj_edge_1.intersection(adj_edge_2)[0]

            # the area of the triangle we'll be adding
            area = sympy.N(sympy.Triangle(edge_pt_1, intersect, edge_pt_2).area)
            # should be the lowest
            if best_candidate and best_candidate[1] < area:
                continue

            # delete the edge and add the intersection of adjacent edges to the hull
            better_hull = list(hull)
            better_hull[edge_idx_1] = intersect
            del better_hull[edge_idx_2]
            best_candidate = (better_hull, area)

        if not best_candidate:
            raise ValueError("Could not find the best fit n-gon!")

        hull = best_candidate[0]

    # back to python land
    hull = [(int(x), int(y)) for x, y in hull]

    return hull

Используйте следующим образом:

hull = appx_best_fit_ngon(img)

# add lines
for idx in range(len(hull)):
    next_idx = (idx + 1) % len(hull)
    cv2.line(img, hull[idx], hull[next_idx], (0, 255, 0), 1)

# add point markers
for pt in hull:
    cv2.circle(img, pt, 2, (255, 0, 0), 2)

Из минусов вижу только одну дополнительную зависимость от пакета SymPy, но это не критично.

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

Похожие вопросы