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

1

Я использую пакет морфологии для создания скелетонизированной версии используемого изображения. Мы можем обнаружить терминалы в скелетонированной версии, но также хотели бы иметь возможность обнаруживать точки, в которых структура создает новые ветки. Мы начали с попытки обнаружить их с 3-мя различными матрицами, названными h1, h2, h3. Пока у нас нет матрицы пропуска, заполненной для фильтрации, это то, что мы попытаемся добавить позже. Мы используем матрицы 5x5 для упрощения редактирования, если мы хотим попробовать фильтрацию позже.

Проблема в том, что, хотя шаблон в матрицах h1 и h2 присутствует в скелетонизированной версии, он не может их обнаружить. Матрица h3 действительно работает. Кажется, я не могу понять, почему это происходит.

def branches(img_pruned,cropped_image):
img = img_pruned
nbranches = 0
branches = cropped_image

h1 = [
    [0,0,0,0,0],
    [0,0,0,1,0],
    [0,1,1,0,0],
    [0,0,1,0,0],
    [0,0,0,0,0]]
h2 = [
    [0,0,0,0,0],
    [0,1,0,1,0],
    [0,0,1,0,0],
    [0,0,1,0,0],
    [0,0,0,0,0]]
h3 = [
    [0,0,0,0,0],
    [0,1,1,1,0],
    [0,0,1,0,0],
    [0,0,0,0,0],
    [0,0,0,0,0]]
m1 = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]]

hitlist = []
misslist = []

for i in range(4):
    hitlist.append(np.rot90(h1, i))
    hitlist.append(np.rot90(h2, i))
    hitlist.append(np.rot90(h3, i))
for t in range(12):
    misslist.append(m1)

for hit,miss in zip(hitlist,misslist):
    branchimg = m.binary_hit_or_miss(img,hit,miss)

for y in range(len(branchimg)):
    for x in range(len(branchimg[y])):
        if branchimg[y][x]:
            nbranches +=1
            branches[y][x] = (0,0,255)

print nbranches
return branches

Исходное изображение, которое мы взяли.

Изображение: Изображение 174551

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

Изображение 174551

Теги:
image-processing
python-2.7
image-morphology

1 ответ

1

Я не совсем уверен в хите или промахе - возможно, это связано с тем, что ваши h1, h2 и h3 массивы дополняются 0s? Во всяком случае, я отвечу на то, как я подхожу к поиску точек ветвления, которые не используют hit-or-miss.

Основная идея заключается в том, что любой пиксель, представляющий точку ветвления, должен иметь 3 или более соседей. Напротив, любой пиксель, который просто вдоль пути, должен иметь ровно 2 соседа.

Если бы вы рассматривали ортогональных соседей как реальных соседей, то найти все пиксели с тремя или более соседями это было бы довольно просто. Просто сверните исходное изображение с ядром K1, которое выглядит как знак плюса:

0 1 0
1 1 1
0 1 0

и ищите любые пиксели со значением 4 или более (3 соседства + центральный пиксель). Код будет выглядеть так:

import numpy as np
import scipy.ndimage as snd
K1 = snd.generate_binary_structure(2,1)
A = your_binary_image
B = snd.convolve(1*A,K1, mode='constant')
image_of_branch_points = B>=4

(мы используем mode = 'constant' в свертке, чтобы избежать по умолчанию зеркального граничного условия, дающего нам ложные срабатывания на краю набора данных... это, похоже, не вызывает беспокойства в вашем наборе данных, поскольку оно не пойдите прямо к краю, но будьте в курсе) (также еще одно быстрое примечание: обратите внимание на 1 * A в свертке... если вы оставите A в виде булевского типа данных, он не будет генерировать никаких результатов больше 1, поэтому мы должен преобразовать в int, если он еще не был).

Но, похоже, вы также допускаете диагональные соединения/разветвления. Это делает его немного сложнее. Моя первая мысль - просто использовать приведенный выше код, но с ядром (пусть его называют K2) всех 1s (т.е. Возможность подключения по диагонали) следующим образом:

1 1 1
1 1 1
1 1 1

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

0 0 0 1 0
0 0 0 1 0
0 0 1 1 0
0 1 0 0 0
0 1 0 0 0

Центральный пиксель здесь выйдет как 4, когда свернут с K2, но проверка показывает, что это не точка ветвления. Тогда это становится немного сложнее.

Одно из решений использует грубую силу: марш вдоль каждой ветки до ближайшего соседа, пока не будут найдены два соседа (не считая пиксель, из которого вы только что пришли). Затем продолжайте идти по этим ветвям, по крайней мере, на 2 шага, пока не убедитесь, что вы расходились на две отдельные ветки. Если это так, отметьте точку расхождения как точку ветвления. Я не буду вдаваться в решение грубой силы, но это возможно.

Другое решение - использовать вышеописанный метод с ядром K2 и вручную проверять каждую точку ветвления на исходное изображение, отбрасывая любые, которые визуально не являются точкой ветвления. Я предполагаю, что большинство пикселей, помеченных как точка ветвления, будут правильными, и вам нужно будет только отбросить несколько. Если вы обрабатываете только несколько наборов данных, это может быть не плохой идеей. Но если вы разрабатываете код, который вы планируете обрабатывать в будущем, это не является отличным решением.

Третьим решением является использование этого метода свертки по отдельности с ядрами любой возможной перестановки ветвящейся структуры. Вы, кажется, уже несколько писали (h1, h2 и h3). Если у вас уже есть все возможные возможности разветвления, то это, вероятно, быстрее всего реализовать:

image_of_branch_points = np.zeros_like(A)
list_of_kernels = [h1, h2, h3, ......]
for h in list_of_kernels:
    B = snd.convolve(A,h)
    image_of_branch_points += B

Наконец, четвертое решение и то, что я предпочитаю больше всего: массируйте ваши данные так, чтобы ядро K1 работало, т.е. Чтобы все "истинные" соседние пиксели были ортогонально связаны, а не диагонально. Вот грубый метод, который, вероятно, будет работать для этого. Мне жаль, что у меня не было оригинального изображения, чтобы я мог его протестировать.

В основном путем свертки следующих двух ядер, отдельно, с изображением:

0 0 0    0 0 0
0 0 1    1 0 0
0 1 0    0 1 0

где результат в центральном пикселе равен 2, вы знаете, что вы сидите по диагональному пути - и мы покрываем оба возможных наклона диагонального пути. Путем записи 1 в центральный пиксель в исходном изображении, мы фиксируем диагональный путь, чтобы иметь ортогональное соединение (так, чтобы, например, длинная диагональная линия стала лестницей). Даже если центральный пиксель был уже 1, вы можете перезаписать его как 1 снова, это не имеет значения. Поэтому этот код должен это сделать:

import numpy as np
import scipy.ndimage as snd

A = your_binary_image_as_ndarray
A_orthog = A.copy() #this will be the copy we update to have orthogonal neighbors
#Two diagonal kernels
K_diag_upslope = np.array([[0,0,0],[0,0,1],[0,1,0]])
K_diag_downslope = np.array([[0,0,0],[1,0,0],[0,1,0]])
#Convolve each one, one at a time, and save new points to A_orthog
#Note: the variable B is always an intermediary so I overwrite it every time
B = snd.convolve(1*A,K_diag_upslope, mode='constant')
A_orthog[B==2] = 1
B = snd.convolve(1*A,K_diag_downslope, mode='constant')
A_orthog[B==2] = 1

#Now A_orthog should be ready for the method shown in my first bit of code up above

K1 = snd.generate_binary_structure(2,1)
B = snd.convolve(1*A_orthog,K1, mode='constant')
image_of_branch_points = B>=4

Я не могу гарантировать, что он будет работать в каждом случае, но он работал в простых тестовых случаях для меня - например, процедура получения A_orthog примененная к идентификационной матрице np.identity(5) дала следующее:

1, 0, 0, 0, 0
1, 1, 0, 0, 0
0, 1, 1, 0, 0
0, 0, 1, 1, 0
0, 0, 0, 1, 1

Вы должны, конечно, сохранить исходное A вокруг - A_orthog - всего лишь посредник для поиска точек ветвления, а image_of_branch_points все равно должны соответствовать правильным точкам ветвления на A.

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

редактировать:

Я схватил ваше jpg-изображение и породил его, чтобы получить двоичное изображение для проверки моего кода. Он в основном работает, думал, что он дает некоторые кластеры из более чем одной точки ветвления - не необоснованно, в местах, где есть целый беспорядок ветвей. JPG не лучший формат для двоичных данных, поэтому я также не уверен на 100%, если были какие-либо артефакты JPG после того, как я был установлен. Во всяком случае, вот результат, который я получил, построил так:

Abranch = 120*A + 240*image_of_branch_points

import matplotlib.pyplot as plt

cmap = plt.cmap.OrRd
cmap.set_under(color='black')
plt.imshow(Abranch, interpolation='none', cmap=cmap, vmin=0.01)

Изображение 174551

Ещё вопросы

Сообщество Overcoder
Наверх
Меню