2

我正在使用形态学包来创建我们正在使用的图像的骨架化版本。我们能够检测骨架化版本中的末端,但也希望能够检测结构产生新分支的点。我们开始尝试使用 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

我们拍摄的原始图像。

图片:

骨架化的照片,我们还使用修剪使末端(分支的终点)更平滑。

4

1 回答 1

1

我不完全确定命中或未命中 - 这可能与您的 h1、h2 和 h3 数组用 0 填充的事实有关?无论如何,我将回答我如何找到不使用命中或未命中的分支点。

基本思想是任何代表一个分支点的像素都应该有 3 个或更多的邻居。相反,任何简单地沿着路径的像素都应该恰好有 2 个邻居。

如果您仅将正交邻居视为真正的邻居,则查找具有 3 个或更多邻居的所有像素将非常简单。只需将原始图像与看起来像加号的内核 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)。

但是您似乎也允许对角线连接/分支。这使它变得更复杂一些。我的第一个想法就是使用上面的代码,但是使用一个全为 1 的内核(我们称之为 K2)(即允许沿对角线连接),如下所示:

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

此处的中心像素与 K2 卷积后会显示为 4,但检查表明它不是分支点。那么这变得有点棘手。

一种解决方案是使用蛮力:沿着每个分支行进到最近的邻居,直到找到两个邻居(不计算您刚刚来自的像素)。然后继续沿着这些分支走至少 2 步,直到你确定你已经分成 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)

主代码结果

于 2018-07-10T15:47:45.470 回答