Skip to content

Commit 1661084

Browse files
feat: add "repair_contacts" feature to cross_sectional_area
1 parent 3858ab9 commit 1661084

File tree

1 file changed

+42
-8
lines changed

1 file changed

+42
-8
lines changed

kimimaro/utility.py

+42-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import scipy.ndimage
55
from tqdm import tqdm
66

7-
from cloudvolume import Skeleton, Bbox
7+
from cloudvolume import Skeleton, Bbox, Vec
88
import kimimaro.skeletontricks
99

1010
import cc3d
@@ -48,6 +48,7 @@ def cross_sectional_area(
4848
progress:bool = False,
4949
in_place:bool = False,
5050
fill_holes:bool = False,
51+
repair_contacts:bool = False,
5152
) -> Union[Dict[int,Skeleton],List[Skeleton],Skeleton]:
5253
"""
5354
Given a set of skeletons, find the cross sectional area
@@ -73,6 +74,11 @@ def cross_sectional_area(
7374
The first six bits are a bitfield xxyyzz that
7475
tell you which image faces were touched and
7576
alternate from low (0) to high (size-1).
77+
78+
repair_contacts: When True, only examine vertices
79+
that have a nonzero value for
80+
skel.cross_sectional_area_contacts. This is intended
81+
to be used as a second pass after widening the image.
7682
"""
7783
prop = {
7884
"id": "cross_sectional_area",
@@ -90,11 +96,18 @@ def cross_sectional_area(
9096
else:
9197
total = len(skeletons)
9298

93-
all_labels, remapping = fastremap.renumber(all_labels, in_place=in_place)
99+
if all_labels.dtype == bool:
100+
remapping = { True: 1, False: 0, 1:1, 0:0 }
101+
else:
102+
all_labels, remapping = fastremap.renumber(all_labels, in_place=in_place)
103+
94104
all_slices = find_objects(all_labels)
95105

96106
for skel in tqdm(iterator, desc="Labels", disable=(not progress), total=total):
97-
label = skel.id
107+
if all_labels.dtype == bool:
108+
label = 1
109+
else:
110+
label = skel.id
98111

99112
if label == 0:
100113
continue
@@ -108,6 +121,10 @@ def cross_sectional_area(
108121
if roi.volume() <= 1:
109122
continue
110123

124+
roi.grow(1)
125+
roi.minpt = Vec.clamp(roi.minpt, Vec(0,0,0), roi.maxpt)
126+
slices = roi.to_slices()
127+
111128
binimg = np.asfortranarray(all_labels[slices] == label)
112129

113130
if fill_holes:
@@ -118,13 +135,19 @@ def cross_sectional_area(
118135

119136
mapping = { tuple(v): i for i, v in enumerate(all_verts) }
120137

121-
areas = np.zeros([all_verts.shape[0]], dtype=np.float32)
122-
contacts = np.zeros([all_verts.shape[0]], dtype=np.uint8)
138+
if repair_contacts:
139+
areas = skel.cross_sectional_area
140+
contacts = skel.cross_sectional_area_contacts
141+
else:
142+
areas = np.zeros([all_verts.shape[0]], dtype=np.float32)
143+
contacts = np.zeros([all_verts.shape[0]], dtype=np.uint8)
123144

124145
paths = skel.paths()
125146

126147
normal = np.array([1,0,0], dtype=np.float32)
127148

149+
shape = np.array(binimg.shape)
150+
128151
for path in paths:
129152
path = (path / anisotropy).round().astype(int)
130153
path -= roi.minpt
@@ -137,18 +160,29 @@ def cross_sectional_area(
137160
normal = normals[i,:]
138161
normal /= np.linalg.norm(normal)
139162

140-
for i, vert in enumerate(path):
163+
for i, vert in tqdm(enumerate(path)):
164+
if np.any(vert < 0) or np.any(vert > shape):
165+
continue
166+
141167
idx = mapping[tuple(vert)]
142168
normal = normals[i]
143169

144-
if areas[idx] == 0:
170+
if areas[idx] == 0 or (repair_contacts and contacts[i] > 0):
145171
areas[idx], contacts[idx] = xs3d.cross_sectional_area(
146172
binimg, vert,
147173
normal, anisotropy,
148174
return_contact=True,
149175
)
150176

151-
skel.extra_attributes.append(prop)
177+
needs_prop = True
178+
for skel_prop in skel.extra_attributes:
179+
if skel_prop["id"] == "cross_sectional_area":
180+
needs_prop = False
181+
break
182+
183+
if needs_prop:
184+
skel.extra_attributes.append(prop)
185+
152186
skel.cross_sectional_area = areas
153187
skel.cross_sectional_area_contacts = contacts
154188

0 commit comments

Comments
 (0)