4
4
import scipy .ndimage
5
5
from tqdm import tqdm
6
6
7
- from cloudvolume import Skeleton , Bbox
7
+ from cloudvolume import Skeleton , Bbox , Vec
8
8
import kimimaro .skeletontricks
9
9
10
10
import cc3d
@@ -48,6 +48,7 @@ def cross_sectional_area(
48
48
progress :bool = False ,
49
49
in_place :bool = False ,
50
50
fill_holes :bool = False ,
51
+ repair_contacts :bool = False ,
51
52
) -> Union [Dict [int ,Skeleton ],List [Skeleton ],Skeleton ]:
52
53
"""
53
54
Given a set of skeletons, find the cross sectional area
@@ -73,6 +74,11 @@ def cross_sectional_area(
73
74
The first six bits are a bitfield xxyyzz that
74
75
tell you which image faces were touched and
75
76
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.
76
82
"""
77
83
prop = {
78
84
"id" : "cross_sectional_area" ,
@@ -90,11 +96,18 @@ def cross_sectional_area(
90
96
else :
91
97
total = len (skeletons )
92
98
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
+
94
104
all_slices = find_objects (all_labels )
95
105
96
106
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
98
111
99
112
if label == 0 :
100
113
continue
@@ -108,6 +121,10 @@ def cross_sectional_area(
108
121
if roi .volume () <= 1 :
109
122
continue
110
123
124
+ roi .grow (1 )
125
+ roi .minpt = Vec .clamp (roi .minpt , Vec (0 ,0 ,0 ), roi .maxpt )
126
+ slices = roi .to_slices ()
127
+
111
128
binimg = np .asfortranarray (all_labels [slices ] == label )
112
129
113
130
if fill_holes :
@@ -118,13 +135,19 @@ def cross_sectional_area(
118
135
119
136
mapping = { tuple (v ): i for i , v in enumerate (all_verts ) }
120
137
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 )
123
144
124
145
paths = skel .paths ()
125
146
126
147
normal = np .array ([1 ,0 ,0 ], dtype = np .float32 )
127
148
149
+ shape = np .array (binimg .shape )
150
+
128
151
for path in paths :
129
152
path = (path / anisotropy ).round ().astype (int )
130
153
path -= roi .minpt
@@ -137,18 +160,29 @@ def cross_sectional_area(
137
160
normal = normals [i ,:]
138
161
normal /= np .linalg .norm (normal )
139
162
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
+
141
167
idx = mapping [tuple (vert )]
142
168
normal = normals [i ]
143
169
144
- if areas [idx ] == 0 :
170
+ if areas [idx ] == 0 or ( repair_contacts and contacts [ i ] > 0 ) :
145
171
areas [idx ], contacts [idx ] = xs3d .cross_sectional_area (
146
172
binimg , vert ,
147
173
normal , anisotropy ,
148
174
return_contact = True ,
149
175
)
150
176
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
+
152
186
skel .cross_sectional_area = areas
153
187
skel .cross_sectional_area_contacts = contacts
154
188
0 commit comments