Преобразование открытой кривой в список упорядоченных пикселей: тестовый код Python с numpy

#python #numpy #shape #curve

#python #numpy #фигуры #кривая

Вопрос:

У меня есть изображение открытой кривой в массиве numpy, и мне нужно составить список координат точек, упорядоченных в соответствии с их положением на кривой. Я написал черновик сценария, используя numpy и mahotas. Это может быть неоптимально.

Я знаю, что OpenCV может сделать это для замкнутой кривой. Может ли OpenCV сделать то же самое (быстрее) с открытой кривой?

Например, если исходная кривая равна:

 [[0 0 0 0 0 0 0]
 [0 1 0 0 1 0 0]
 [0 0 1 0 0 1 0]
 [0 0 0 1 1 0 0]
 [0 0 0 0 0 0 0]]
  

Используя np.where(myarray==1) , я могу получить индексы пикселей:

 (array([1, 1, 2, 2, 3, 3]), array([1, 4, 2, 5, 3, 4]))
  

Но это не то, что мне нужно. Мой скрипт выдает индексы с учетом порядка расположения пикселей на кривой:

 i= 0 ( 1 , 1 )
i= 1 ( 2 , 2 )
i= 2 ( 3 , 3 )
i= 3 ( 3 , 4 )
i= 4 ( 2 , 5 )
i= 5 ( 1 , 4 )
  

Я хотел бы оптимизировать свой скрипт. Есть идеи?

Комментарии:

1. Я написал другую версию: ссылка

2. Уважаемые все, я написал расширение для обработки замкнутой кривой: [ссылка] dip4fish.blogspot.com/2011/06 /…

Ответ №1:

Предполагая, что в матрице / изображении присутствует только одна кривая и что каждая точка на кривой имеет от 1 до 2 соседей, следующая функция предоставит нужные вам результаты.

Он работает, беря точку, ближайшую к верхнему левому углу, и формируя цепочку точек путем итеративного нахождения ближайшей точки, которая еще не была посещена, пока не останется других точек. Для замкнутой кривой квадрат евклидова расстояния между первой / конечной точками в цепочке будет меньше 2.

 import numpy as np

def find_chain(mat):
  locs=np.column_stack(np.nonzero(mat))
  chain=[np.array([0,0])]
  while locs.shape[0]>0:
    dists=((locs-np.vstack([chain[-1]]*locs.shape[0]))**2).sum(axis=1)
    next=dists.argmin()
    if dists.min()<=2 or len(chain)==1:
      chain.append(locs[next,:])
      locs=locs[np.arange(locs.shape[0])!=next,:]
    else:
      chain=[chain[0]] chain[1::][::-1]
  return np.vstack(chain[1::]),((chain[1]-chain[-1])**2).sum()<=2
  

Для открытой кривой:

 >>> mat1=np.array([[0, 0, 0, 0, 0, 0, 0],
...                [0, 1, 0, 0, 1, 0, 0],
...                [0, 0, 1, 0, 0, 1, 0],
...                [0, 0, 0, 1, 1, 0, 0],
...                [0, 0, 0, 0, 0, 0, 0]])
>>> points,isclosed=find_chain(mat1)
>>> points
array([[1, 1],
       [2, 2],
       [3, 3],
       [3, 4],
       [2, 5],
       [1, 4]])
>>> isclosed
False
  

И для замкнутой кривой:

 >>> mat2=np.array([[0, 0, 0, 0, 0],
...                [0, 0, 1, 0, 0],
...                [0, 1, 0, 1, 0],
...                [0, 1, 0, 1, 0],
...                [0, 0, 1, 0, 0],
...                [0, 0, 0, 0, 0]])
>>> points,isclosed=find_chain(mat2)
>>> points
array([[1, 2],
       [2, 1],
       [3, 1],
       [4, 2],
       [3, 3],
       [2, 3]])
>>> isclosed
True
  

И кривая, в которой начальная точка (ближайшая к началу координат) делит кривую надвое.

 >>> mat3=np.array([[0, 0, 0, 0, 0],
...                [0, 1, 1, 1, 0],
...                [0, 1, 0, 0, 0],
...                [0, 1, 0, 0, 0],
...                [0, 0, 0, 0, 0],
...                [0, 0, 0, 0, 0]])
>>> points,isclosed=find_chain(mat3)
>>> points
array([[1, 3],
       [1, 2],
       [1, 1],
       [2, 1],
       [3, 1]])
>>> isclosed
False
  

Комментарии:

1. Спасибо. Ваш код действительно более компактный, чем мой. Зависимость только от numpy также является преимуществом. Для обработки кривой, полученной путем морфологического утончения, необходима некоторая предварительная обработка, поскольку пиксель может иметь трех соседей.