简体   繁体   English

分割的3D骨架

[英]3d skeleton from segmentation

I want to create a skeleton based on an existing segmentation, similar to what is done here (from sk-image ): 我想基于现有的分割来创建骨架,类似于此处所做的操作(来自sk-image ):

我的目标

However I want to do this on 3D data. 但是我想对3D数据执行此操作。 Is there code for that somewhere out there? 在那边有代码吗? Preferably in python but any language helps. 最好使用python,但任何语言都可以。

I am aware of this great site, however I think they don't offer any code. 我知道这个很棒的网站,但是我认为他们不提供任何代码。

I am planning on using that on volumes of about 500x500x500 Pixels, so it should scale well... 我打算在约500x500x500像素的体积上使用它,因此它应该可以很好地扩展...

I am developing this tools in this link below. 我在下面的此链接中开发此工具。 The function getSkeletonize3D in program of the name convOptimize.py lets you thin your 3D data. 在名称convOptimize.py的程序功能getSkeletonize3D让你瘦的3D数据。 It took about 30 minutes to give the result for the 512 cube I have. 花了大约30分钟时间得出了我拥有的512多维数据集的结果。 Let me know if you have any problems. 如果您有任何问题,请告诉我。 https://github.com/3Scan/3scan-skeleton . https://github.com/3Scan/3scan-skeleton The paper I used for implementing is in the comments in the code below 我用于实施的论文在以下代码的注释中

Basically how this 3D skeletonization algorithm works is, in each pass it has 12 subiterations in which it removes boundaries in specific directions iteratively, until you get a skeleton in the center. 基本上,这种3D骨架化算法的工作方式是,每次遍历都有12个子迭代,在该迭代中,它会迭代地删除特定方向上的边界,直到获得中心的骨架为止。

The main python code that is needed for skeletonizing your data is as below. 骨架化数据所需的主要python代码如下。 As it requires imports from different other porgrams rotationalOperators which has an import from another file called Thin3dtemplates. 由于它需要从其他不同的项目导入,rotatingOperators从另一个文件Thin3dtemplates导入。 I recommend you downlaod rotationalOperators, Thin3dtemplates, convoptimize python scripting files and also download lookuparray.npy which is a file that is used as a lookup table in an numpy array format pre-calculated for validating a voxel for marking to be deleted or not. 我建议您对RotationOperators,Thin3dtemplates,convoptimize python脚本文件进行下载,并下载lookuparray.npy,该文件用作numpy数组格式的查找表,已预先计算用于验证要删除的标记的体素。 You need python > 3 version, scipy, numpy and pyeda modules installed to run these codes. 您需要安装python> 3版本,scipy,numpy和pyeda模块才能运行这些代码。

import numpy as np
import time
from scipy import ndimage
from scipy.ndimage.filters import convolve

"""
   the following subiteration functions are how each image is rotated to the next direction for removing
   boundary voxels in the order described in the reference paper
   us, ne, wd,..
"""
from rotationalOperators import firstSubiteration, secondSubiteration, thirdSubiteration, fourthSubiteration, fifthSubiteration, sixthSubiteration, seventhSubiteration, eighthSubiteration, ninthSubiteration, tenthSubiteration, eleventhSubiteration, twelvethSubiteration

"""
   reference paper
   http://web.inf.u-szeged.hu/ipcg/publications/papers/PalagyiKuba_GMIP1999.pdf
   input should be a binary image/ already segmented
"""


"""
   array that has calculated the validity of the 14 templates beforehand and stored each index which is
   decimal number of the binary string of 26 values (sqrt(3) connectivity) that are around a single voxel 
"""

lookUpTablearray = np.load('lookupTablearray.npy')


def _convolveImage(arr, flippedKernel):
    arr = np.ascontiguousarray(arr, dtype=np.uint64)
    result = convolve(arr, flippedKernel, mode='constant', cval=0)
    result[arr == 0] = 0
    return result


"""
each of the 12 iterations corresponds to each of the following
directions - us, ne, wd, es, uw, nd, sw, un, ed, nw, ue, sd
imported from template expressions
evaluated in advance using pyeda
https://pyeda.readthedocs.org/en/latest/expr.html
"""

sElement = ndimage.generate_binary_structure(3, 1)


def _getBouondariesOfimage(image):
    """
       function to find boundaries/border/edges of the array/image
    """

    erode_im = ndimage.morphology.binary_erosion(image, sElement)
    boundaryIm = image - erode_im
    return boundaryIm

"""
each of the 12 iterations corresponds to each of the following
directions - us, ne, wd, es, uw, nd, sw, un, ed, nw, ue, sd
imported from template expressions
evaluated in advance using pyeda
https://pyeda.readthedocs.org/en/latest/expr.html
"""

directionList = [firstSubiteration, secondSubiteration, thirdSubiteration, fourthSubiteration,
                 fifthSubiteration, sixthSubiteration, seventhSubiteration, eighthSubiteration,
                 ninthSubiteration, tenthSubiteration, eleventhSubiteration, twelvethSubiteration]


def _skeletonPass(image):
    """
        each pass consists of 12 serial subiterations and finding the
        boundaries of the padded image/array
    """
    boundaryIm = _getBouondariesOfimage(image)
    numPixelsremovedList = [] * 12
    boundaryIndices = list(set(map(tuple, list(np.transpose(np.nonzero(boundaryIm))))))
    for i in range(0, 12):
        convImage = _convolveImage(image, directionList[i])
        totalPixels, image = _applySubiter(image, boundaryIndices, convImage)
        print("number of pixels removed in the {} direction is {}". format(i, totalPixels))
        numPixelsremovedList.append(totalPixels)
    numPixelsremoved = sum(numPixelsremovedList)
    return numPixelsremoved, image


def _applySubiter(image, boundaryIndices, convImage):
    """
       each subiteration paralleley reduces the border voxels in 12 directions
       going through each voxel and marking if it can be deleted or not in a
       different image named temp_del and finally multiply it with the original
       image to delete the voxels so marked
    """
    temp_del = np.zeros_like(image)
    # boundaryIndicesCopy = copy.deepcopy(boundaryIndices)
    lenB = len(boundaryIndices)
    for k in range(0, lenB):
        temp_del[boundaryIndices[k]] = lookUpTablearray[convImage[boundaryIndices[k]]]
    numpixel_removed = np.einsum('ijk->', image * temp_del, dtype=int)
    image[temp_del == 1] = 0
    return numpixel_removed, image


def getSkeletonize3D(image):
    """
    function to skeletonize a 3D binary image with object in brighter contrast than background.
    In other words, 1 = object, 0 = background
    """
    assert np.max(image) in [0, 1]
    zOrig, yOrig, xOrig = np.shape(image)
    padImage = np.lib.pad(image, 1, 'constant', constant_values=0)
    start_skeleton = time.time()
    pass_no = 0
    numpixel_removed = 0
    while pass_no == 0 or numpixel_removed > 0:
        numpixel_removed, padImage = _skeletonPass(padImage)
        print("number of pixels removed in pass {} is {}".format(pass_no, numpixel_removed))
        pass_no += 1
    print("done %i number of pixels in %f seconds" % (np.sum(image), time.time() - start_skeleton))
    return padImage[1: zOrig + 1, 1: yOrig + 1, 1: xOrig + 1]

if __name__ == '__main__':
    sample = np.ones((5, 5, 5), dtype=np.uint8)
    resultSkel = getSkeletonize3D(sample)
    # gives a single voxel at the center
    print("resultSkel", resultSkel)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM