/
_skeletonize_3d.py
77 lines (60 loc) · 2.49 KB
/
_skeletonize_3d.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from __future__ import division, print_function, absolute_import
import numpy as np
from ..util import img_as_ubyte, crop
from ._skeletonize_3d_cy import _compute_thin_image
def skeletonize_3d(img):
"""Compute the skeleton of a binary image.
Thinning is used to reduce each connected component in a binary image
to a single-pixel wide skeleton.
Parameters
----------
img : ndarray, 2D or 3D
A binary image containing the objects to be skeletonized. Zeros
represent background, nonzero values are foreground.
Returns
-------
skeleton : ndarray
The thinned image.
See also
--------
skeletonize, medial_axis
Notes
-----
The method of [Lee94]_ uses an octree data structure to examine a 3x3x3
neighborhood of a pixel. The algorithm proceeds by iteratively sweeping
over the image, and removing pixels at each iteration until the image
stops changing. Each iteration consists of two steps: first, a list of
candidates for removal is assembled; then pixels from this list are
rechecked sequentially, to better preserve connectivity of the image.
The algorithm this function implements is different from the algorithms
used by either `skeletonize` or `medial_axis`, thus for 2D images the
results produced by this function are generally different.
References
----------
.. [Lee94] T.-C. Lee, R.L. Kashyap and C.-N. Chu, Building skeleton models
via 3-D medial surface/axis thinning algorithms.
Computer Vision, Graphics, and Image Processing, 56(6):462-478, 1994.
"""
# make sure the image is 3D or 2D
if img.ndim < 2 or img.ndim > 3:
raise ValueError("skeletonize_3d can only handle 2D or 3D images; "
"got img.ndim = %s instead." % img.ndim)
img = np.ascontiguousarray(img)
img = img_as_ubyte(img, force_copy=False)
# make an in image 3D and pad it w/ zeros to simplify dealing w/ boundaries
# NB: careful here to not clobber the original *and* minimize copying
img_o = img
if img.ndim == 2:
img_o = img[np.newaxis, ...]
img_o = np.pad(img_o, pad_width=1, mode='constant')
# normalize to binary
maxval = img_o.max()
img_o[img_o != 0] = 1
# do the computation
img_o = np.asarray(_compute_thin_image(img_o))
# crop it back and restore the original intensity range
img_o = crop(img_o, crop_width=1)
if img.ndim == 2:
img_o = img_o[0]
img_o *= maxval
return img_o