Соответствие формы OpenCV между двумя похожими формами - PullRequest
2 голосов
/ 05 апреля 2019

Я пытаюсь сопоставить слегка неправильную форму с базой данных фигур. Например, вот контур, который я пытаюсь сопоставить:

enter image description here

Для получения дополнительной информации это контур разъема HDMI, представленного в виде контура. Это немного грубо, так как это было сделано с телефоном, удерживая HDMI.

Это моя база данных разъемов:

HDMI: enter image description here

DVI: enter image description here

5PinDIN: enter image description here

DB25: enter image description here

Это намного понятнее, так как это контуры, собранные из изображений разъемов из Интернета.

За то, что я пробовал:

cv2.matchShapes ()

Поскольку все это просто контуры, я попытался напрямую сравнить их с помощью метода matchShapes (), и он не дал хороших результатов. Сходство между нерегулярным контуром и моей базой данных было:

HDMI: 0,90

DB25: 0,84

5-контактный DIN: 0,5

DVI: 0,21

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

ORB:

Будучи похожим на SIFT, я попытался сопоставить ключевые точки. Усреднение расстояния между различными совпадениями в моей базе данных (после нахождения 15% лучших совпадений):

mean([m.distance for m in matches])

Расстояния составили:

5-контактный DIN: 7,6

DB25: 11,7

DVI: 12,1

HDMI: 19,6

Поскольку это классифицировало круг как форму, наиболее похожую на мой контур, это тоже не удалось.

Вот соответствующие ключевые точки из ORB реального слота HDMI против моего слота HDMI примера для получения дополнительной информации: enter image description here

Есть какие-нибудь идеи / другие алгоритмы, которые я должен попробовать? Или CNN - мой единственный выбор (которого я бы предпочел избежать, поскольку у меня нет соответствующего количества данных).

Ответы [ 2 ]

2 голосов
/ 05 апреля 2019

Есть несколько шагов, которые можно выполнить, чтобы получить лучшие результаты.И нет необходимости в CNN или в сопоставлении некоторых сложных функций, давайте попробуем решить эту проблему, используя очень простой подход.

1.Нормализуйте изображение запроса и изображения базы данных.

Это можно сделать, аккуратно обрезав входной контур, а затем изменив размеры всех изображений до одинаковой высоты или ширины.Я выберу ширину здесь, скажем, 300px.Давайте определим для этого вспомогательный метод:

def normalize_contour(img):
    im, cnt, _ = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    bounding_rect = cv2.boundingRect(cnt[0])
    img_cropped_bounding_rect = img[bounding_rect[1]:bounding_rect[1] + bounding_rect[3],
                                bounding_rect[0]:bounding_rect[0] + bounding_rect[2]]

    new_height = int((1.0 * img.shape[0])/img.shape[1] * 300.0)
    img_resized = cv2.resize(img_cropped_bounding_rect, (300, new_height))
    return img_resized

Этот фрагмент кода вернет красиво обрезанный контур с фиксированной шириной 300. Примените этот метод ко всем изображениям базы данных, а также к изображению входного запроса.

2.Фильтруйте просто, используя высоту нормализованного входного изображения.

Поскольку мы нормализовали входное изображение до 300 пикселей, мы можем отклонить всех кандидатов, высота которых не близка к нормализованной высоте изображения.Это исключит 5PinDIN.

3.Сравнить область

Теперь вы можете попробовать отсортировать результаты с максимальным перекрытием, вы можете cv2.contourArea() получить область контура и отсортировать всех оставшихся кандидатов, чтобы получить максимально возможное совпадение.

1 голос
/ 29 июня 2019

Этот ответ основан на ответе ZdaR здесь https://stackoverflow.com/a/55530040/1787145. Я попробовал некоторые варианты в надежде использовать один критерий различения (cv2.matchShapes()), включив больше в предварительную обработку.

1.Сравнение изображений вместо контуров

Мне нравится идея нормализации (обрезка и изменение размера).Но после сжатия изображения его изначально замкнутый контур может быть разбит на несколько разрозненных частей из-за низкого разрешения пикселей.Результат cv2.matchShapes() ненадежен.Сравнивая изображения в полном размере, я получаю следующие результаты.Это говорит, что круг является самым подобным.Не хорошо!

enter image description here

2.Заполните форму

Заполняя форму, мы учитываем площадь.Результат выглядит лучше, но DVI по-прежнему превосходит HDMI, так как имеет более похожее соотношение высоты и высоты / ширины.Мы хотим игнорировать это.

enter image description here

3.Изменение размера каждого изображения до одинакового размера

Изменение размера до одного и того же размера позволяет исключить некоторое соотношение размеров.(300, 300) хорошо работает здесь.

enter image description here

4.Код

def normalize_filled(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    im, cnt, _ = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    # fill shape
    cv2.fillPoly(img, pts=cnt, color=(255,255,255))
    bounding_rect = cv2.boundingRect(cnt[0])
    img_cropped_bounding_rect = img[bounding_rect[1]:bounding_rect[1] + bounding_rect[3], bounding_rect[0]:bounding_rect[0] + bounding_rect[2]]
    # resize all to same size
    img_resized = cv2.resize(img_cropped_bounding_rect, (300, 300))
    return img_resized

imgs = [imgQuery, imgHDMI, imgDVI, img5PinDin, imgDB25]
imgs = [normalize_filled(i) for i in imgs]

for i in range(1, 6):
    plt.subplot(2, 3, i), plt.imshow(imgs[i - 1], cmap='gray')
    print(cv2.matchShapes(imgs[0], imgs[i - 1], 1, 0.0))
...