Skip to content

Commit

Permalink
wip: multiscale manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
akhileshh committed Mar 26, 2023
1 parent 3d6d180 commit 8705352
Show file tree
Hide file tree
Showing 4 changed files with 1,524 additions and 32 deletions.
83 changes: 83 additions & 0 deletions pychunkedgraph/meshing/manifest/multiscale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# pylint: disable=invalid-name, missing-docstring, line-too-long

from collections import deque
from typing import Dict

import numpy as np

from pychunkedgraph.graph import ChunkedGraph
from pychunkedgraph.graph.types import empty_1d
from pychunkedgraph.graph.utils.basetypes import NODE_ID


def _get_hierarchy(cg: ChunkedGraph, node_id: NODE_ID) -> Dict:
node_children = {}
node_ids = np.array([node_id], dtype=NODE_ID)
while node_ids.size > 0:
children = cg.get_children(node_ids)
node_children.update(children)

_ids = np.concatenate(list(children.values())) if children else empty_1d.copy()
node_layers = cg.get_chunk_layers(_ids)
node_ids = _ids[node_layers > 2]

for l2id in _ids[node_layers == 2]:
node_children[l2id] = empty_1d.copy()
return node_children


def build_octree(cg: ChunkedGraph, node_id: NODE_ID):
"""
From neuroglancer multiscale specification:
Row-major `[n, 5]` array where each row is of the form `[x, y, z, start, end_and_empty]`, where
`x`, `y`, and `z` are the chunk grid coordinates of the entry at a particular level of detail.
Row `n-1` corresponds to level of detail `lodScales.length - 1`, the root of the octree. Given
a row corresponding to an octree node at level of detail `lod`, bits `start` specifies the row
number of the first child octree node at level of detail `lod-1`, and bits `[0,30]` of
`end_and_empty` specify one past the row number of the last child octree node. Bit `31` of
`end_and_empty` is set to `1` if the mesh for the octree node is empty and should not be
requested/rendered.
"""
node_children = _get_hierarchy(cg, node_id)
node_ids = np.array(list(node_children.keys()), dtype=NODE_ID)

node_coords = {}
node_layers = cg.get_chunk_layers(node_ids)
for layer in set(node_layers):
layer_mask = node_layers == layer
coords = cg.get_chunk_coordinates_multiple(node_ids[layer_mask])
_node_coords = dict(zip(node_ids[layer_mask], coords))
node_coords.update(_node_coords)

ROW_TOTAL = len(node_ids)
row_count = len(node_ids)
octree_size = 5 * row_count
octree = np.zeros(octree_size, dtype=np.uint32)

que = deque()
que.append(node_id)
rows_used = 1
while len(que) > 0:
row_count -= 1
offset = 5 * row_count
current_node = que.popleft()

x, y, z = node_coords[current_node]
octree[offset + 0] = x
octree[offset + 1] = y
octree[offset + 2] = z

children = node_children[current_node]
start = 0
end_empty = 0
if children.size > 0:
rows_used += children.size
start = ROW_TOTAL - rows_used
end_empty = start + children.size

octree[offset + 3] = start
octree[offset + 4] = end_empty

for child in children:
que.append(child)
return octree
32 changes: 0 additions & 32 deletions pychunkedgraph/meshing/meshgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,38 +37,6 @@
REDIS_URL = f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/0"


def simplify(cg, new_fragment, new_fragment_id: np.uint64 = None):
"""
Simplify with pyfqmr; input and output flat vertices and faces.
"""

v = new_fragment["vertices"].reshape(-1, 3)
f = new_fragment["faces"].reshape(-1, 3)

l2factor = int(os.environ.get("l2factor", "2"))
factor = int(os.environ.get("factor", "4"))
aggressiveness = float(os.environ.get("aggr", "7.0"))

layer = cg.get_chunk_layer(new_fragment_id)
if layer == 2:
target_count = max(int(len(f) / l2factor), 4)
else:
target_count = max(int(len(f) / factor), 4)

simplifier = pyfqmr.Simplify()
simplifier.setMesh(v, f)
simplifier.simplify_mesh(
target_count=target_count,
aggressiveness=aggressiveness,
preserve_border=True,
verbose=False,
)
v, f, _ = simplifier.getMesh()
new_fragment["vertices"] = v.flatten()
new_fragment["faces"] = f.flatten()
return new_fragment


def decode_draco_mesh_buffer(fragment):
try:
mesh_object = DracoPy.decode_buffer_to_mesh(fragment)
Expand Down
Loading

0 comments on commit 8705352

Please sign in to comment.