Как найти середину закругленного угла?
Есть контур прямоугольника с закругленными углами. Как можно найти устредненные координаты его top-left и bottom-left углов?
Я сделал так:
sum = np.sum(cnt, axis=-1)
tl = cnt[np.argmin(sum)]
diff = np.diff(cnt, axis=-1)
bl = cnt[np.argmax(diff)]
Код выше найдет углы, как показанно на второй фигуре. Но как найти центры углов, даже если фигура может быть повернута?
Ответы (2 шт):
Точки, показанные слева, отстоят от сторон на расстояние r*(1-SQRT(2)/2)
, где r - радиус закругления.
Если есть только массив с точками контура, то для начала нужно пройтись по массиву и найти точки сторон. Три точки лежат на одной стороне, если в парах dx = dy. Потом найти крайние точки сторон, а уже по крайним точкам сторон найти для каждой пары точку посередине между ними в массиве.
Можно даже сделать быстрее. Наверняка, начальная точка в массиве привязана к чему-то. Пусть это будет нижяя точка на правом рисунке. И направление обхода контура тоже известно, пусть будет по часовой стрелке.
Тогда для первой же пары точек определяем синус dy1 и косинус dx1. Касательная в искомой точке будет наклонена к стороне под 45 градусов (пи/4) в минус (по часовой). Т. е. идём дальше по массиву и ищем пару точек с dy2=sin(arcsin(dy1)-pi/4) и dx2=cos(arccos(dx1)-pi/4). Одну из этих точек можно взять как искомую. Следующие три пары точек ищем с поворотом на 90 (пи/2), т. е. (dx,dy): (-dу2,dх2), (-dx2,-dy2) и (dy2,-dx2).
Поскольку dx2 и dy2 в точности не найдутся, нужно рассматривать тройки точек, чтобы искомые величины лежали между парами соответствующих. И про экстремумы помнить.
Как определить, что три точки (x1,y1), (x2,y2) и (x3,y3) лежат на одной прямой? Выражение должно быть истинно:
(x3 >= x2 >= x1 or x3 <= x2 <= x1) and
((x2 - x1) == 0 and (x3 - x2) == 0 or (x2 - x1) != 0 and
(x3 - x2) != 0 and (y2 - y1)/(x2 - x1) == (y3 - y2)/(x3 - x2))
Можно для выделения скругленных углов использовать математическую морфологию. Тогда решение не будет зависить от ориентации контура.
import cv2
import numpy as np
R=120 # параметр радиуса закругления
S_min=100 # минимальная площадь объектов
im=cv2.imread('frame.png')
im_gray=cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
im_bw=cv2.threshold(im_gray, 127, 255, cv2.THRESH_BINARY)[1] # бинаризация
# выделение объекта с помощью заливки и бинарных операций
im_ff=im_bw.copy()
cv2.floodFill(im_ff, None, (0,0), 0)[1]
im_out=cv2.bitwise_xor(im_bw, im_ff)
kernel1=cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (R,R))
im_out_close=cv2.morphologyEx(im_out, cv2.MORPH_CLOSE, kernel1) # морф. закрытие
diff_im=cv2.bitwise_xor(im_out_close, im_out) # оставляем серповидные части углов
# выделяем связные компоненты
ret,_,stats,centroids=cv2.connectedComponentsWithStats(diff_im)
# фильтр по площади компонентов
idx=stats[:,4]>S_min
idx[0]=False # нулевой объект -фон
# размечаем центроиды
for coord in centroids[idx,:]:
cv2.circle(im, coord.astype('int'), 10, (0,0,255), -1)
cv2.imwrite('frame_with_angles.png', im)
Исходное изображение:
Выделеные скругленные углы:
Результат:
Примечание: использовались только встроенные функции openCV. Для точного определения координат надо использовать/написать аналоги функций bwmorph с опциями "shrink" и "skel" из Matlab/Octave.