Как уже упоминалось в комментариях, кажется, что здесь проще всего сделать так, чтобы свертить ваше изображение с помощью блочного фильтра (или аналогичного, но с эллиптической формой), который даст вам оконные средние по всему изображению.Вы можете просто индексировать этот результат свертки в ваших угловых точках.Если результат свертки в этих точках превышает 50%, вокруг этой точки больше белого, следовательно, это вогнутая точка.В противном случае он выпуклый.Вот как это может выглядеть в коде.
import cv2
import numpy as np
from itertools import tee
def pairwise(iterable):
a, b = tee(iterable)
next(b, None)
return zip(a, b)
# read image as grayscale
img = cv2.imread('example.png', 0)
# get corner points, remove duplicate/nearby points
contours = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]
contour = contours[0]
pts = np.array([contour[0]] + [pt1 for pt0, pt1 in pairwise(contour) if not (abs(pt0 - pt1) <= 1).all()])
x, y = pts[:, -1, 0], pts[:, -1, 1]
# get the kernel that you will sum around your corner points
kernel = np.float64(cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (13, 13)))
kernel /= np.sum(kernel)
# convolve the image with the kernel, and pull out the sums at the corner points
conv = cv2.filter2D(img/255, cv2.CV_64F, kernel)
neighborhood_sums = conv[y, x]
# concave indices have more white than black around them, so convolution will be >= 1/2
concave_indices = neighborhood_sums >= 0.5
# draw markers
marked = cv2.merge([img, img, img])
for pt, concave in zip(pts, concave_indices):
color = (255, 0, 255) if concave else (0, 255, 0)
marker = cv2.MARKER_TRIANGLE_UP if concave else cv2.MARKER_TRIANGLE_DOWN
cv2.drawMarker(marked, tuple(pt[0]), color, markerType=marker, markerSize=10, thickness=3)
После импорта я определил один из itertools
рецептов для итерации в парах (например, s -> (s0, s1), (s1, s2), ...
).Это не очень важно для проблемы, но просто полезно для меня, чтобы избавиться от повторяющихся точек, которые были получены из findContours()
.После этого все остальное продолжается, как описано ранее.Вы можете нарисовать свое собственное ядро или что угодно, но я только что вытащил одно из getStructuringElement()
, так как вы можете делать эллипсы произвольного размера (хотя обратите внимание, что это возвращает ядро странной формы, вы, вероятно, могли бы определить круг лучше).Обратите внимание, что размер ядра здесь указывается в общей ширине, а не просто в радиусе, и он нормализуется числом 1 в нем, так что результат всегда находится между 0 и 1.
А вот результатвыше на вашем первом изображении: