【问题标题】:how to get a 1 pixel wide skeleton?如何获得 1 像素宽的骨架?
【发布时间】:2021-09-15 10:24:19
【问题描述】:

我想从二叉树中得到一个 1 像素宽的骨架。应用我的代码后得到的骨架有一些小的错误分支,并且不是 1 像素宽。如何获得完美的骨架?

使用 Frangi 过滤器增强血管,并在执行阈值处理后提取二叉树。这是当时的图像:

然后,进行细化得到骨架:

% getting the skeleton
Iskel = bwmorph(Out,'thin',8);
figure,imshow(Iskel);

% because of the discontinuity in my skeleton, I've applied imdilate to create continuity 
se = strel('square',2);
Skel = imdilate(Iskel, se);
figure,imshow(Skel); title('Skel 8');

我希望输出是 1 像素宽的骨架,但实际上我不确定我的骨架是 1 像素宽,而且还有小的假分支。

【问题讨论】:

  • 能否请edit 将您的问题与minimal reproducible example 更接近?特别是,我怀疑你所拥有的和你期望的可能会有很大帮助。
  • 你试过bwskel吗?如果这不比细化效果更好,您需要上传您的图像和结果,以便我们知道您在此处讨论的内容。
  • 所以当我将代码应用到这张图片时,我看到的是细化后的 1 像素刻度骨架。当然在imdilate 之后它不再是 1 像素厚,膨胀使它变厚。您可以在细化之前应用膨胀来连接结构。

标签: matlab image-processing


【解决方案1】:

我使用张素算法从原始图像创建骨架。但结果可能不是单像素骨架。所以,我决定创建一个算法来去除多余的像素。最后,我认为得到了一个单像素骨架。您可以将我的代码视为一种解决方案。

这是我的 Python 实现(我还在下面添加了我的代码的结果):

import cv2
import numpy as np

"""
Input: image array (WxHX1)
Output: skeleton
"""
def zhang_suen_skeleton(img):
    # use zhang_suen algorithm
    skel = cv2.ximgproc.thinning(img)
    
    return skel

def check_connectivity(block_img):
    block_img[1][1] = 0
    (rows, cols) = np.nonzero(block_img)
    rows = sorted(rows)
    cols = sorted(cols)

    # condition 1
    for i in range(len(rows)-1):
        if rows[i+1] - rows[i] > 1:
            return False
    for j in range(len(cols)-1):
        if cols[j+1] - cols[j] > 1:
            return False
    
    # condition 2: check 4 corners
    if block_img[0][0] > 0:
        if block_img[0][1] == 0 and block_img[1][0] == 0:
            return False
    if block_img[2][0] > 0:
        if block_img[2][1] == 0 and block_img[1][0] == 0:
            return False
    if block_img[2][2] > 0:
        if block_img[2][1] == 0 and block_img[1][2] == 0:
            return False
    if block_img[0][2] > 0:
        if block_img[0][1] == 0 and block_img[1][2] == 0:
            return False

    return True

def single_pixelated(img):
    (rows, cols) = np.nonzero(img)
    result = img.copy()
    removed_pixels = []
    for (r, c) in zip(rows, cols):
        if (r, c) not in removed_pixels:
            block_img = result[r-1: r+2, c-1: c+2].copy()
            if np.sum(block_img) >=3*255:
                if check_connectivity(block_img):
                    result[r][c] = 0
                    removed_pixels.append((r, c))
    return result

if __name__ == "__main__":
    img = cv2.imread('sample.png', 0)
    skel = zhang_suen_skeleton(img)
    single_skel = single_pixelated(skel)

    cv2.imwrite('single_skeleton.png', single_skel)

【讨论】:

  • 作为移除的条件,您只需计算 3x3 区域中的像素数。骨架条件比这更复杂。请注意您的输出仍然有许多多余的点(删除它们不会断开骨架)。一条线中间的一个像素应该只有两个邻居(3x3 区域中的 3 个像素)。您需要检查实际的骨骼状况。
  • 通常条件存储为一个包含 256 个条目的表。您将一个像素的 8 个邻居收集到一个 8 位非数字上,然后在表格中查找它。然后,您可以手动填写表格。但其背后的逻辑是这样的:如果一个像素有 0 或 1 个邻居,则保留它。如果一个像素有更多的邻居,检查它们是否形成一个或更多的集群;如果是单个集群,则删除,否则保留。一个“单簇”意味着,当移除一个像素时,其他像素仍然是连接的。依次绕过 8 个邻居,从一个空像素开始,计算 1 的簇。
猜你喜欢
  • 2016-03-11
  • 1970-01-01
  • 2016-01-08
  • 2016-12-10
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 2018-08-19
  • 2023-01-19
相关资源
最近更新 更多