1
1
# pylint: disable=invalid-name, missing-docstring, line-too-long, no-member
2
2
3
- import json
4
- import pickle
5
3
import time
6
4
import functools
7
- from collections import defaultdict , deque
5
+ from collections import defaultdict , deque , namedtuple
8
6
9
7
import numpy as np
10
8
from cloudvolume import CloudVolume
20
18
OCTREE_NODE_SIZE = 5
21
19
22
20
21
+ HierarchyInfo = namedtuple (
22
+ "HierarchyInfo" ,
23
+ [
24
+ "children_map" ,
25
+ "children_chunks_map" ,
26
+ "chunk_nodes_map" ,
27
+ "node_chunk_id_map" ,
28
+ "coords_map" ,
29
+ "layers_map" ,
30
+ ],
31
+ )
32
+
33
+
23
34
def _morton_sort (cg : ChunkedGraph , children : np .ndarray ):
24
35
"""
25
36
Sort children by their morton code.
@@ -52,7 +63,71 @@ def less_msb(x: int, y: int) -> bool:
52
63
return np .array (children , dtype = NODE_ID )
53
64
54
65
55
- def _get_hierarchy (cg : ChunkedGraph , node_id : np .uint64 ) -> dict :
66
+ def _get_node_coords_and_layers_map (
67
+ cg : ChunkedGraph , children_map : dict
68
+ ) -> tuple [dict , dict ]:
69
+ node_ids = np .fromiter (children_map .keys (), dtype = NODE_ID )
70
+ coords_map = {}
71
+ node_layers = cg .get_chunk_layers (node_ids )
72
+ for layer in set (node_layers ):
73
+ layer_mask = node_layers == layer
74
+ coords = cg .get_chunk_coordinates_multiple (node_ids [layer_mask ])
75
+ _node_coords = dict (zip (node_ids [layer_mask ], coords ))
76
+ coords_map .update (_node_coords )
77
+
78
+ chunk_id_coords_map = {}
79
+ chunk_ids = cg .get_chunk_ids_from_node_ids (node_ids )
80
+ node_chunk_id_map = dict (zip (node_ids , chunk_ids ))
81
+ for k , v in coords_map .items ():
82
+ chunk_id_coords_map [node_chunk_id_map [k ]] = v
83
+ coords_map .update (chunk_id_coords_map )
84
+ return coords_map , dict (zip (node_ids , node_layers ))
85
+
86
+
87
+ def _get_hierarchy (cg : ChunkedGraph , node_id : np .uint64 ) -> HierarchyInfo :
88
+ def _insert_skipped_nodes (cg : ChunkedGraph ):
89
+ new_children_map = {}
90
+ for node , children in children_map .items ():
91
+ nl = layers_map [node ]
92
+ if len (children ) > 1 or nl == 2 :
93
+ new_children_map [node ] = children
94
+ else :
95
+ assert (
96
+ len (children ) == 1
97
+ ), f"Skipped hierarchy must have exactly 1 child: { node } - { children } ."
98
+ cl = layers_map [children [0 ]]
99
+ height = nl - cl
100
+ if height == 1 :
101
+ new_children_map [node ] = children
102
+ continue
103
+
104
+ cx , cy , cz = coords_map [children [0 ]]
105
+ skipped_hierarchy = [node ]
106
+ count = 1
107
+ height -= 1
108
+ while height :
109
+ x , y , z = cx >> height , cy >> height , cz >> height
110
+ skipped_layer = nl - count
111
+ skipped_chunk = cg .get_chunk_id (layer = skipped_layer , x = x , y = y , z = z )
112
+ limit = cg .get_segment_id_limit (skipped_chunk )
113
+ skipped_child = skipped_chunk + (limit - np .uint64 (1 ))
114
+ while skipped_child in new_children_map :
115
+ skipped_child = skipped_child - np .uint64 (1 )
116
+
117
+ skipped_hierarchy .append (skipped_child )
118
+ coords_map [skipped_child ] = np .array ((x , y , z ), dtype = int )
119
+ layers_map [skipped_child ] = skipped_layer
120
+ node_chunk_id_map [skipped_child ] = skipped_chunk
121
+ count += 1
122
+ height -= 1
123
+ skipped_hierarchy .append (children [0 ])
124
+
125
+ for i in range (len (skipped_hierarchy ) - 1 ):
126
+ node = skipped_hierarchy [i ]
127
+ child = skipped_hierarchy [i + 1 ]
128
+ new_children_map [node ] = np .array ([child ], dtype = NODE_ID )
129
+ return new_children_map
130
+
56
131
node_chunk_id_map = {node_id : cg .get_chunk_id (node_id )}
57
132
children_map = {}
58
133
children_chunks_map = {}
@@ -81,79 +156,24 @@ def _get_hierarchy(cg: ChunkedGraph, node_id: np.uint64) -> dict:
81
156
for l2id in _ids [node_layers == 2 ]:
82
157
children_map [l2id ] = empty_1d .copy ()
83
158
84
- for k , v in children_map .items ():
85
- chunk_ids = np .array ([node_chunk_id_map [i ] for i in v ], dtype = NODE_ID )
159
+ coords_map , layers_map = _get_node_coords_and_layers_map (cg , children_map )
160
+ new_children_map = _insert_skipped_nodes (cg )
161
+ coords_map , layers_map = _get_node_coords_and_layers_map (cg , new_children_map )
162
+
163
+ for node , children in new_children_map .items ():
164
+ chunk_ids = np .array ([node_chunk_id_map [i ] for i in children ], dtype = NODE_ID )
86
165
uchunk_ids = np .unique (chunk_ids )
87
- children_chunks_map [k ] = uchunk_ids
166
+ children_chunks_map [node ] = uchunk_ids
88
167
for c in uchunk_ids :
89
- chunk_nodes_map [c ] = v [chunk_ids == c ]
90
- return children_map , children_chunks_map , chunk_nodes_map , node_chunk_id_map
91
-
92
-
93
- def _get_node_coords_and_layers_map (
94
- cg : ChunkedGraph , children_map : dict
95
- ) -> tuple [dict , dict ]:
96
- node_ids = np .fromiter (children_map .keys (), dtype = NODE_ID )
97
- coords_map = {}
98
- node_layers = cg .get_chunk_layers (node_ids )
99
- for layer in set (node_layers ):
100
- layer_mask = node_layers == layer
101
- coords = cg .get_chunk_coordinates_multiple (node_ids [layer_mask ])
102
- _node_coords = dict (zip (node_ids [layer_mask ], coords ))
103
- coords_map .update (_node_coords )
104
-
105
- chunk_id_coords_map = {}
106
- chunk_ids = cg .get_chunk_ids_from_node_ids (node_ids )
107
- node_chunk_id_map = dict (zip (node_ids , chunk_ids ))
108
- for k , v in coords_map .items ():
109
- chunk_id_coords_map [node_chunk_id_map [k ]] = v
110
- coords_map .update (chunk_id_coords_map )
111
- return coords_map , dict (zip (node_ids , node_layers ))
112
-
113
-
114
- def _insert_skipped_nodes (
115
- cg : ChunkedGraph , children_map : dict , coords_map : dict , layers_map : dict
116
- ):
117
- new_children_map = {}
118
- for node , children in children_map .items ():
119
- nl = layers_map [node ]
120
- if len (children ) > 1 or nl == 2 :
121
- new_children_map [node ] = children
122
- else :
123
- assert (
124
- len (children ) == 1
125
- ), f"Skipped hierarchy must have exactly 1 child: { node } - { children } ."
126
- cl = layers_map [children [0 ]]
127
- height = nl - cl
128
- if height == 1 :
129
- new_children_map [node ] = children
130
- continue
131
-
132
- cx , cy , cz = coords_map [children [0 ]]
133
- skipped_hierarchy = [node ]
134
- count = 1
135
- height -= 1
136
- while height :
137
- x , y , z = cx >> height , cy >> height , cz >> height
138
- skipped_layer = nl - count
139
- skipped_child = cg .get_chunk_id (layer = skipped_layer , x = x , y = y , z = z )
140
- limit = cg .get_segment_id_limit (skipped_child )
141
- skipped_child += limit - np .uint64 (1 )
142
- while skipped_child in new_children_map :
143
- skipped_child = skipped_child - np .uint64 (1 )
144
-
145
- skipped_hierarchy .append (skipped_child )
146
- coords_map [skipped_child ] = np .array ((x , y , z ), dtype = int )
147
- layers_map [skipped_child ] = skipped_layer
148
- count += 1
149
- height -= 1
150
- skipped_hierarchy .append (children [0 ])
151
-
152
- for i in range (len (skipped_hierarchy ) - 1 ):
153
- node = skipped_hierarchy [i ]
154
- child = skipped_hierarchy [i + 1 ]
155
- new_children_map [node ] = np .array ([child ], dtype = NODE_ID )
156
- return new_children_map , coords_map , layers_map
168
+ chunk_nodes_map [c ] = children [chunk_ids == c ]
169
+ return HierarchyInfo (
170
+ new_children_map ,
171
+ children_chunks_map ,
172
+ chunk_nodes_map ,
173
+ node_chunk_id_map ,
174
+ coords_map ,
175
+ layers_map ,
176
+ )
157
177
158
178
159
179
def _validate_octree (octree : np .ndarray , octree_node_ids : np .ndarray ):
@@ -199,13 +219,7 @@ def _explore_node(node: int):
199
219
200
220
201
221
def build_octree (
202
- cg : ChunkedGraph ,
203
- node_id : np .uint64 ,
204
- children_map : dict ,
205
- children_chunks_map : dict ,
206
- chunk_nodes_map : dict ,
207
- node_chunk_id_map : dict ,
208
- mesh_fragments : dict ,
222
+ cg : ChunkedGraph , node_id : np .uint64 , hinfo : HierarchyInfo , mesh_fragments : dict
209
223
):
210
224
"""
211
225
From neuroglancer multiscale specification:
@@ -219,26 +233,27 @@ def build_octree(
219
233
requested/rendered.
220
234
"""
221
235
node_q = deque ()
222
- node_q .append (node_chunk_id_map [node_id ])
223
- coords_map , _ = _get_node_coords_and_layers_map (cg , children_map )
236
+ node_q .append (hinfo .node_chunk_id_map [node_id ])
224
237
225
- all_chunks = np .concatenate (list (children_chunks_map .values ()))
238
+ all_chunks = np .concatenate (list (hinfo . children_chunks_map .values ()))
226
239
all_chunks = np .unique (all_chunks )
227
240
228
241
ROW_TOTAL = all_chunks .size + 1
229
242
row_counter = all_chunks .size + 1
230
243
octree_size = OCTREE_NODE_SIZE * ROW_TOTAL
231
244
octree = np .zeros (octree_size , dtype = np .uint32 )
232
245
233
- octree_node_ids = ROW_TOTAL * [0 ]
246
+ octree_chunks = ROW_TOTAL * [0 ]
234
247
octree_fragments = defaultdict (list )
235
248
rows_used = 1
249
+ virtual_chunk_hierarchy = {}
236
250
237
251
while len (node_q ) > 0 :
238
252
frags = []
239
253
row_counter -= 1
240
254
current_chunk = node_q .popleft ()
241
- chunk_nodes = chunk_nodes_map [current_chunk ]
255
+ chunk_nodes = hinfo .chunk_nodes_map [current_chunk ]
256
+ octree_chunks [row_counter ] = current_chunk
242
257
243
258
for k in chunk_nodes :
244
259
if k in mesh_fragments :
@@ -247,17 +262,15 @@ def build_octree(
247
262
248
263
children_chunks = set ()
249
264
for k in chunk_nodes :
250
- children_chunks .update (children_chunks_map [k ])
265
+ children_chunks .update (hinfo . children_chunks_map [k ])
251
266
252
267
children_chunks = np .array (list (children_chunks ), dtype = NODE_ID )
253
268
children_chunks = _morton_sort (cg , children_chunks )
254
269
for child_chunk in children_chunks :
255
270
node_q .append (child_chunk )
256
271
257
- octree_node_ids [row_counter ] = current_chunk
258
-
259
272
offset = OCTREE_NODE_SIZE * row_counter
260
- x , y , z = coords_map [current_chunk ]
273
+ x , y , z = hinfo . coords_map [current_chunk ]
261
274
octree [offset + 0 ] = x
262
275
octree [offset + 1 ] = y
263
276
octree [offset + 2 ] = z
@@ -269,26 +282,36 @@ def build_octree(
269
282
octree [offset + 3 ] = start
270
283
octree [offset + 4 ] = end_empty
271
284
272
- if children_chunks . size == 1 :
273
- octree [ offset + 3 ] |= 1 << 31
285
+ if len ( octree_fragments [ int ( current_chunk )]) == 0 :
286
+ virtual_chunk_hierarchy [ current_chunk ] = children_chunks [ 0 ]
274
287
if children_chunks .size == 0 :
275
288
octree [offset + 4 ] |= 1 << 31
276
289
277
- octree [5 * (ROW_TOTAL - 1 ) + 3 ] |= 1 << 31
278
290
# _validate_octree(octree, octree_node_ids)
279
291
fragments = []
280
- for node in octree_node_ids :
281
- fragments .append (octree_fragments [int (node )])
282
- return octree , octree_node_ids , fragments
292
+ for chunk in octree_chunks :
293
+ if chunk in virtual_chunk_hierarchy :
294
+ frags = []
295
+ while True :
296
+ child = virtual_chunk_hierarchy [chunk ]
297
+ if child not in virtual_chunk_hierarchy :
298
+ break
299
+ chunk = child
300
+ fragments .append (octree_fragments [int (child )])
301
+ else :
302
+ fragments .append (octree_fragments [int (chunk )])
303
+
304
+ for k in hinfo .children_chunks_map [node_id ]:
305
+ fragments [- 1 ].extend (octree_fragments [int (k )])
306
+ octree [5 * (ROW_TOTAL - 1 ) + 3 ] &= ~ (1 << 31 )
307
+ return octree , octree_chunks , fragments
283
308
284
309
285
310
def get_manifest (cg : ChunkedGraph , node_id : np .uint64 ) -> dict :
286
311
start = time .time ()
287
- children_map , children_chunks_map , chunk_nodes_map , node_chunk_id_map = (
288
- _get_hierarchy (cg , node_id )
289
- )
312
+ hierarchy_info = _get_hierarchy (cg , node_id )
290
313
291
- node_ids = np .fromiter (children_map .keys (), dtype = NODE_ID )
314
+ node_ids = np .fromiter (hierarchy_info . children_map .keys (), dtype = NODE_ID )
292
315
manifest_cache = ManifestCache (cg .graph_id , initial = True )
293
316
294
317
cv = CloudVolume (
@@ -304,24 +327,15 @@ def get_manifest(cg: ChunkedGraph, node_id: np.uint64) -> dict:
304
327
manifest_cache .set_fragments (_fragments_d )
305
328
fragments_d .update (_fragments_d )
306
329
307
- octree , node_ids , fragments = build_octree (
308
- cg ,
309
- node_id ,
310
- children_map ,
311
- children_chunks_map ,
312
- chunk_nodes_map ,
313
- node_chunk_id_map ,
314
- fragments_d ,
315
- )
316
-
330
+ octree , node_ids , fragments = build_octree (cg , node_id , hierarchy_info , fragments_d )
317
331
max_layer = min (cg .get_chunk_layer (node_id ) + 1 , cg .meta .layer_count )
318
332
chunk_shape = np .array (cg .meta .graph_config .CHUNK_SIZE , dtype = np .dtype ("<f4" ))
319
333
chunk_shape *= cg .meta .resolution
320
334
clip_bounds = cg .meta .voxel_bounds .T * cg .meta .resolution
321
335
response = {
322
336
"chunkShape" : chunk_shape ,
323
337
"chunkGridSpatialOrigin" : np .array ([0 , 0 , 0 ], dtype = np .dtype ("<f4" )),
324
- "lodScales" : np .arange (2 , max_layer , dtype = np .dtype ("<f4" )) * 1 ,
338
+ "lodScales" : np .arange (2 , max_layer + 1 , dtype = np .dtype ("<f4" )) * 1 ,
325
339
"fragments" : fragments ,
326
340
"octree" : octree ,
327
341
"clipLowerBound" : np .array (clip_bounds [0 ], dtype = np .dtype ("<f4" )),
0 commit comments