Как найти соседей в двоичном изображении с заданным расстоянием по горизонтали и вертикали (Python)

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

Например, все черные точки с горизонтальным расстоянием 160 пикселей и вертикальным 40 пикселей.

Небольшая часть изображения

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

Работает, но очень медленно. На данный момент скрипт работает около 3-4 минут в зависимости от размера изображения.

Есть ли какой-нибудь простой / быстрый способ сделать это (лучше всего будет метод scikit-image, чтобы помочь здесь). Я использую Python.

edit: Я пробовал использовать scikit.measure.find_contours, теперь у меня есть массив с массивами, содержащими контуры черных пятен. Теперь мне нужно найти контуры в окрестности этих контуров.


person Kuchen    schedule 10.08.2016    source источник


Ответы (3)


Отказ от ответственности: я совсем не разбираюсь в библиотеке изображений scikit, но я решил аналогичные проблемы с помощью MATLAB, поэтому я искал эквивалентные методы в scikit и надеюсь, что мои выводы, приведенные ниже, помогут ты.

Сначала вы можете использовать skimage.measure.label, который возвращает label_image, то есть изображение, в котором все соединенные области помечены одним и тем же номером. Я считаю, что вы должны вызывать эту функцию с background=255, потому что из вашего описания кажется, что фон в ваших изображениях - это область while (отсюда значение 255).

По сути, это изображение, где background пикселям присваивается значение 0, а пикселям, составляющим каждое (соединенное) пятно, присваивается значение целочисленной метки, поэтому все пиксели одного пятна будут помечены значением 1, пиксели другого пятна будут помечены значением 2 и так далее. Ниже я буду называть «пятна» и «отмеченные регионы» как синонимы.

Затем вы можете вызвать skimage.measure.regionprops, который принимает в качестве входных данных label_image, полученный на предыдущем шаге. Эта функция возвращает список из RegionProperties (по одному для каждой помеченной области), который представляет собой сводку свойств помеченной области.

В зависимости от вашего определения

Цель состоит в том, чтобы найти все черные точки рядом друг с другом.

Существуют различные поля RegionProperties, которые можно использовать для решения вашей проблемы:

  • bbox дает вам набор координат ограничивающей рамки, которая содержит эту помеченную область,
  • centroid дает вам координаты пикселя центроида этой помеченной области,
  • local_centroid показывает центроид относительно ограничивающей рамки bbox

(Обратите внимание, что есть также свойства area и bbox_area, которые вы можете использовать, чтобы решить, выбрасывать ли очень маленькие пятна, которые могут вас не интересовать, тем самым сокращая время вычислений, когда дело доходит до сравнения близости каждой пары пятен)

Если вы ищете грубое сравнение, тогда может быть достаточно сравнения centroid или local_centroid между каждой парой помеченных регионов.

В противном случае вы можете использовать bbox координаты для измерения точного расстояния между внешними границами любых двух регионов.

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

person etsetnj    schedule 10.08.2016
comment
Спасибо за ваш ответ. Свойство coords - именно то, что мне нужно. Теперь мне просто нужно получить расстояние по вертикали / горизонтали между каждым пикселем. Но я думаю, это не должно быть проблемой. - person Kuchen; 10.08.2016

Когда вы получаете координаты различных черных пятен, вместо того, чтобы вычислять все расстояния между всеми парами черных пикселей, вы можете использовать cKDTree (в scipy.spatial, http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.cKDTree.html#scipy.spatial.cKDTree). Точный метод использования cKDTree зависит от вашего точного критерия (вы можете, например, использовать cKDTree.query_ball_tree, чтобы узнать, существует ли пара точек, принадлежащих двум разным меткам, с указанным вами максимальным расстоянием).

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

person Emmanuelle Gouillart    schedule 10.08.2016
comment
Это довольно удобно для получения расстояния между различными регионами, а также очень быстро по сравнению с моим собственным методом. Единственная проблема в том, что я получаю расстояние только в радиусе. но расстояние нужно разное по горизонтали и вертикали. Например, 100 пикселей по оси X и 40 пикселей по оси Y. Вы знаете, как так быстро найти расстояние в одном направлении? - person Kuchen; 11.08.2016

Если ваше входное изображение двоичное, вы можете разделить интересующие области следующим образом:

  1. "вырастите" все регионы на ожидаемое расстояние (фактически половину от него, если вы увеличиваете "обе стороны разрыва") с помощью binary_dilation, где structure - это ядро ​​(например, прямоугольное: http://scikit-image.org/docs/dev/api/skimage.morphology.html#skimage.morphology.rectangle), допустим, размером 20x80 пикселей;
  2. используйте полученную маску в качестве входных данных для skimage.measure.label, чтобы назначить разные значения для пикселей разных регионов;
  3. умножьте входное изображение на созданную выше маску, чтобы обнулить расширенные пиксели.

Вот результаты предлагаемого метода на вашем изображении и kernel = rectange(5,5):

Расширенное двоичное изображение (результат шага 1):

введите описание изображения здесь

Помеченная версия вышеупомянутого (результат шага 2):

введите описание изображения здесь

Результаты умножения (результат шага 3):

введите описание изображения здесь

person soupault    schedule 11.08.2016
comment
Большое Вам спасибо. Это, безусловно, самый простой способ решить эту проблему. - person Kuchen; 11.08.2016