Skip to content

Commit

Permalink
add some neuroglancer functionality to visualize pipelines
Browse files Browse the repository at this point in the history
  • Loading branch information
pattonw committed Jan 22, 2024
1 parent 2501064 commit 8b9d59f
Show file tree
Hide file tree
Showing 10 changed files with 572 additions and 3 deletions.
2 changes: 1 addition & 1 deletion gunpowder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .array_spec import ArraySpec
from .batch import Batch
from .batch_request import BatchRequest
from .build import build
from .build import build, build_neuroglancer
from .coordinate import Coordinate
from .graph import Graph, Node, Edge, GraphKey, GraphKeys
from .graph_spec import GraphSpec
Expand Down
47 changes: 47 additions & 0 deletions gunpowder/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,50 @@ def __exit__(self, type, value, traceback):
logger.debug("leaving context, tearing down pipeline")
self.pipeline.internal_teardown()
logger.debug("tear down completed")


import neuroglancer
from .neuroglancer.event import step_next


class build_neuroglancer(object):
def __init__(self, pipeline):
self.pipeline = pipeline

def __enter__(self):
neuroglancer.set_server_bind_address("0.0.0.0")
viewer = neuroglancer.Viewer()

viewer.actions.add("continue", step_next)

with viewer.config_state.txn() as s:
s.input_event_bindings.data_view["keyt"] = "continue"
with viewer.txn() as s:
s.layout = neuroglancer.row_layout(
[
neuroglancer.column_layout(
[
neuroglancer.LayerGroupViewer(layers=[]),
neuroglancer.LayerGroupViewer(layers=[]),
]
),
]
)

try:
self.pipeline.setup(viewer)
except:
logger.error(
"something went wrong during the setup of the pipeline, calling tear down"
)
self.pipeline.internal_teardown()
logger.debug("tear down completed")
raise

print(viewer)
return self.pipeline

def __exit__(self, type, value, traceback):
logger.debug("leaving context, tearing down pipeline")
self.pipeline.internal_teardown()
logger.debug("tear down completed")
Empty file.
296 changes: 296 additions & 0 deletions gunpowder/neuroglancer/add_layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
from .scale_pyramid import ScalePyramid
import neuroglancer

"""
TAKEN FROM funlib.show.neuroglancer
funlib.show.neuroglancer should probably be made installable
and used as an optional dependency
"""


rgb_shader_code = '''
void main() {
emitRGB(
%f*vec3(
toNormalized(getDataValue(%i)),
toNormalized(getDataValue(%i)),
toNormalized(getDataValue(%i)))
);
}'''

color_shader_code = '''
void main() {
emitRGBA(
vec4(
%f, %f, %f,
toNormalized(getDataValue()))
);
}'''

binary_shader_code = '''
void main() {
emitGrayscale(255.0*toNormalized(getDataValue()));
}'''

heatmap_shader_code = '''
void main() {
float v = toNormalized(getDataValue(0));
vec4 rgba = vec4(0,0,0,0);
if (v != 0.0) {
rgba = vec4(colormapJet(v), 1.0);
}
emitRGBA(rgba);
}'''


def parse_dims(array):

if type(array) == list:
array = array[0]

dims = len(array.data.shape)
spatial_dims = array.spec.roi.dims
channel_dims = dims - spatial_dims

print("dims :", dims)
print("spatial dims:", spatial_dims)
print("channel dims:", channel_dims)

return dims, spatial_dims, channel_dims


def create_coordinate_space(array, spatial_dim_names, channel_dim_names, unit):

dims, spatial_dims, channel_dims = parse_dims(array)
assert spatial_dims > 0

if channel_dims > 0:
channel_names = channel_dim_names[-channel_dims:]
else:
channel_names = []
spatial_names = spatial_dim_names[-spatial_dims:]
names = channel_names + spatial_names
units = [""] * channel_dims + [unit] * spatial_dims
scales = [1] * channel_dims + list(array.spec.voxel_size)

print("Names :", names)
print("Units :", units)
print("Scales :", scales)

return neuroglancer.CoordinateSpace(
names=names,
units=units,
scales=scales)


def create_shader_code(
shader,
channel_dims,
rgb_channels=None,
color=None,
scale_factor=1.0):

if shader is None:
if channel_dims > 1:
shader = 'rgb'
else:
return None

if rgb_channels is None:
rgb_channels = [0, 1, 2]

if shader == 'rgb':
return rgb_shader_code % (
scale_factor,
rgb_channels[0],
rgb_channels[1],
rgb_channels[2])

if shader == 'color':
assert color is not None, \
"You have to pass argument 'color' to use the color shader"
return color_shader_code % (
color[0],
color[1],
color[2],
)

if shader == 'binary':
return binary_shader_code

if shader == 'heatmap':
return heatmap_shader_code


def add_layer(
context,
array,
name,
spatial_dim_names=None,
channel_dim_names=None,
opacity=None,
shader=None,
rgb_channels=None,
color=None,
visible=True,
value_scale_factor=1.0,
units='nm'):

"""Add a layer to a neuroglancer context.
Args:
context:
The neuroglancer context to add a layer to, as obtained by
``viewer.txn()``.
array:
A ``daisy``-like array, containing attributes ``roi``,
``voxel_size``, and ``data``. If a list of arrays is given, a
``ScalePyramid`` layer is generated.
name:
The name of the layer.
spatial_dim_names:
The names of the spatial dimensions. Defaults to ``['t', 'z', 'y',
'x']``. The last elements of this list will be used (e.g., if your
data is 2D, the channels will be ``['y', 'x']``).
channel_dim_names:
The names of the non-spatial (channel) dimensions. Defaults to
``['b^', 'c^']``. The last elements of this list will be used
(e.g., if your data is 2D but the shape of the array is 3D, the
channels will be ``['c^']``).
opacity:
A float to define the layer opacity between 0 and 1.
shader:
A string to be used as the shader. Possible values are:
None : neuroglancer's default shader
'rgb' : An RGB shader on dimension `'c^'`. See argument
``rgb_channels``.
'color' : Shows intensities as a constant color. See argument
``color``.
'binary' : Shows a binary image as black/white.
'heatmap': Shows an intensity image as a jet color map.
rgb_channels:
Which channels to use for RGB (default is ``[0, 1, 2]``).
color:
A list of floats representing the RGB values for the constant color
shader.
visible:
A bool which defines the initial layer visibility.
value_scale_factor:
A float to scale array values with for visualization.
units:
The units used for resolution and offset.
"""

if channel_dim_names is None:
channel_dim_names = ["b", "c^"]
if spatial_dim_names is None:
spatial_dim_names = ["t", "z", "y", "x"]

if rgb_channels is None:
rgb_channels = [0, 1, 2]

is_multiscale = type(array) == list

dims, spatial_dims, channel_dims = parse_dims(array)

if is_multiscale:

dimensions = []
for a in array:
dimensions.append(
create_coordinate_space(
a,
spatial_dim_names,
channel_dim_names,
units))

# why only one offset, shouldn't that be a list?
voxel_offset = [0] * channel_dims + \
list(array[0].roi.offset / array[0].voxel_size)

layer = ScalePyramid(
[
neuroglancer.LocalVolume(
data=a.data,
voxel_offset=voxel_offset,
dimensions=array_dims
)
for a, array_dims in zip(array, dimensions)
]
)

else:

voxel_offset = [0] * channel_dims + \
list(array.spec.roi.offset / array.spec.voxel_size)

dimensions = create_coordinate_space(
array,
spatial_dim_names,
channel_dim_names,
units)

layer = neuroglancer.LocalVolume(
data=array.data,
voxel_offset=voxel_offset,
dimensions=dimensions,
)

shader_code = create_shader_code(
shader,
channel_dims,
rgb_channels,
color,
value_scale_factor)

if opacity is not None:
if shader_code is None:
context.layers.append(
name=name,
layer=layer,
visible=visible,
opacity=opacity)
else:
context.layers.append(
name=name,
layer=layer,
visible=visible,
shader=shader_code,
opacity=opacity)
else:
if shader_code is None:
context.layers.append(
name=name,
layer=layer,
visible=visible)
else:
context.layers.append(
name=name,
layer=layer,
visible=visible,
shader=shader_code)
10 changes: 10 additions & 0 deletions gunpowder/neuroglancer/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import threading

BATCH_STEP = threading.Event()

def step_next(event):
BATCH_STEP.set()

def wait_for_step():
BATCH_STEP.wait()
BATCH_STEP.clear()
Loading

0 comments on commit 8b9d59f

Please sign in to comment.