Skip to content

Commit 94303c7

Browse files
committed
wip: multiscale manifest
1 parent 9eb5cc0 commit 94303c7

File tree

4 files changed

+1524
-32
lines changed

4 files changed

+1524
-32
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# pylint: disable=invalid-name, missing-docstring, line-too-long
2+
3+
from collections import deque
4+
from typing import Dict
5+
6+
import numpy as np
7+
8+
from pychunkedgraph.graph import ChunkedGraph
9+
from pychunkedgraph.graph.types import empty_1d
10+
from pychunkedgraph.graph.utils.basetypes import NODE_ID
11+
12+
13+
def _get_hierarchy(cg: ChunkedGraph, node_id: NODE_ID) -> Dict:
14+
node_children = {}
15+
node_ids = np.array([node_id], dtype=NODE_ID)
16+
while node_ids.size > 0:
17+
children = cg.get_children(node_ids)
18+
node_children.update(children)
19+
20+
_ids = np.concatenate(list(children.values())) if children else empty_1d.copy()
21+
node_layers = cg.get_chunk_layers(_ids)
22+
node_ids = _ids[node_layers > 2]
23+
24+
for l2id in _ids[node_layers == 2]:
25+
node_children[l2id] = empty_1d.copy()
26+
return node_children
27+
28+
29+
def build_octree(cg: ChunkedGraph, node_id: NODE_ID):
30+
"""
31+
From neuroglancer multiscale specification:
32+
Row-major `[n, 5]` array where each row is of the form `[x, y, z, start, end_and_empty]`, where
33+
`x`, `y`, and `z` are the chunk grid coordinates of the entry at a particular level of detail.
34+
Row `n-1` corresponds to level of detail `lodScales.length - 1`, the root of the octree. Given
35+
a row corresponding to an octree node at level of detail `lod`, bits `start` specifies the row
36+
number of the first child octree node at level of detail `lod-1`, and bits `[0,30]` of
37+
`end_and_empty` specify one past the row number of the last child octree node. Bit `31` of
38+
`end_and_empty` is set to `1` if the mesh for the octree node is empty and should not be
39+
requested/rendered.
40+
"""
41+
node_children = _get_hierarchy(cg, node_id)
42+
node_ids = np.array(list(node_children.keys()), dtype=NODE_ID)
43+
44+
node_coords = {}
45+
node_layers = cg.get_chunk_layers(node_ids)
46+
for layer in set(node_layers):
47+
layer_mask = node_layers == layer
48+
coords = cg.get_chunk_coordinates_multiple(node_ids[layer_mask])
49+
_node_coords = dict(zip(node_ids[layer_mask], coords))
50+
node_coords.update(_node_coords)
51+
52+
ROW_TOTAL = len(node_ids)
53+
row_count = len(node_ids)
54+
octree_size = 5 * row_count
55+
octree = np.zeros(octree_size, dtype=np.uint32)
56+
57+
que = deque()
58+
que.append(node_id)
59+
rows_used = 1
60+
while len(que) > 0:
61+
row_count -= 1
62+
offset = 5 * row_count
63+
current_node = que.popleft()
64+
65+
x, y, z = node_coords[current_node]
66+
octree[offset + 0] = x
67+
octree[offset + 1] = y
68+
octree[offset + 2] = z
69+
70+
children = node_children[current_node]
71+
start = 0
72+
end_empty = 0
73+
if children.size > 0:
74+
rows_used += children.size
75+
start = ROW_TOTAL - rows_used
76+
end_empty = start + children.size
77+
78+
octree[offset + 3] = start
79+
octree[offset + 4] = end_empty
80+
81+
for child in children:
82+
que.append(child)
83+
return octree

pychunkedgraph/meshing/meshgen.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -37,38 +37,6 @@
3737
REDIS_URL = f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/0"
3838

3939

40-
def simplify(cg, new_fragment, new_fragment_id: np.uint64 = None):
41-
"""
42-
Simplify with pyfqmr; input and output flat vertices and faces.
43-
"""
44-
45-
v = new_fragment["vertices"].reshape(-1, 3)
46-
f = new_fragment["faces"].reshape(-1, 3)
47-
48-
l2factor = int(os.environ.get("l2factor", "2"))
49-
factor = int(os.environ.get("factor", "4"))
50-
aggressiveness = float(os.environ.get("aggr", "7.0"))
51-
52-
layer = cg.get_chunk_layer(new_fragment_id)
53-
if layer == 2:
54-
target_count = max(int(len(f) / l2factor), 4)
55-
else:
56-
target_count = max(int(len(f) / factor), 4)
57-
58-
simplifier = pyfqmr.Simplify()
59-
simplifier.setMesh(v, f)
60-
simplifier.simplify_mesh(
61-
target_count=target_count,
62-
aggressiveness=aggressiveness,
63-
preserve_border=True,
64-
verbose=False,
65-
)
66-
v, f, _ = simplifier.getMesh()
67-
new_fragment["vertices"] = v.flatten()
68-
new_fragment["faces"] = f.flatten()
69-
return new_fragment
70-
71-
7240
def decode_draco_mesh_buffer(fragment):
7341
try:
7442
mesh_object = DracoPy.decode_buffer_to_mesh(fragment)

0 commit comments

Comments
 (0)