neu_glance.serving¶
Host arrays in this process and open a real neuroglancer on them.
What is hosted is a ServedLayer; this module is the
server around it.
Runs the viewer’s server inside the calling process, so an array that exists only in a
notebook (a model’s probability map, a relabelled crop) can be viewed without writing a
volume. neuroglancer is imported only inside functions (the serve extra).
A served source is not portable. Its URL is
python://volume/<token>, valid only while this process runs, soServer.state()is for inspection, not for sharing.The chunk encoding stays ``npz``. In neuroglancer 2.41.2 the
rawencoder callsndarray.tostring, removed in NumPy 2, and fails on the first chunk requested, after the viewer has opened.
Server.on_click(), Server.boxes(), Server.points() and
Server.selected_segments() read the browser’s state back into the notebook;
Server.annotate() adds a layer to draw in.
Attributes¶
Classes¶
Functions¶
|
Host |
|
Stop the viewer server, whatever started it. |
Module Contents¶
- class neu_glance.serving.Click¶
Where a click landed, and what was under it.
positionis in the served frame’s voxels,zyx, as floats — the cursor is not on a voxel centre.valuesmaps layer name to the value there, which for a segmentation is the label id.- position: tuple[float, ...]¶
- state: Any = None¶
- values: dict[str, Any]¶
- property voxel: tuple[int, ...]¶
positionfloored to whole voxels, which is the voxel actually clicked.
- class neu_glance.serving.Server(viewer, volumes: dict[str, Any], annotations: str | None)¶
A running viewer, and the handle the notebook keeps on it.
Reads the browser’s state back without needing neuroglancer’s API.
.vieweris the underlyingneuroglancer.Viewer, for anything not wrapped.- add_layer(layer: Any, *, name: str | None = None, color: Any = None) Self¶
Host one more array in this viewer, live. Returns the server, so calls chain.
The new layer appears in the tab already open, with no new URL:
srv = (serve([em]) .add_layer(ServedLayer.from_hdf5("gt.h5", "/vol_03700", "segmentation")) .add_layer(ServedLayer.from_array(pred, "probability", voxel_size=(40, 8, 8)), name="prediction"))
Takes the same inputs as
serve(). A name already in the viewer gets a numeric suffix;list(srv.volumes)[-1]is the name actually used. The view does not move, so a layer elsewhere may be off screen (seebounds()).coloroverrides the layer’s own, e.g. to add one array twice in two colours:srv.add_layer(pred, color="#00ff00")
- annotate(name: str | None = None, *, tool: str | None = 'box', select: bool = True) str¶
Add a layer to draw in, arm a tool, and return the layer’s name.
The layer
boxes()andpoints()read, added to a viewer already open:srv = serve([em, gt]) srv.annotate() # draw boxes; ctrl+mousedown0 places one srv.boxes() # -> [((lo), (hi)), ...] in zyx voxels srv.annotate(tool="point") # same layer, now placing points srv.points()
toolis what a click places:"box","point", orNonefor nothing.selectmakes it the active layer, without which the armed tool does nothing until the layer is picked by hand.Calling it again with the same name keeps what is already drawn and re-arms the tool. A name taken by a volume layer raises. The name (default: the current annotation layer, else
ANNOTATE_LAYER) becomes the default forboxes()andpoints().
- bounds(layer: str | None = None) Any¶
Where the served arrays are, as a
neu_lib.BBoxin viewer voxels.The extent of the arrays themselves, offset included: one layer by name, or the union of all. What
enclose(clip="volume")clips to.
- boxes(layer: str | None = None) list[tuple[tuple[int, ...], ...]]¶
Boxes drawn in the viewer, as
(lo, hi)pairs in whole voxels, zyx.Ready for
--crop-bbox,neu_vol.extract_roi()orneu-vol write. Corners are sorted per axis, since a box drawn up and to the left has them reversed.
- data_bounds(layer: str | None = None) Any¶
Where the non-zero voxels are, as a
neu_lib.BBoxin viewer voxels.Usually much smaller than
bounds()for a segmentation. One layer by name, or the union of all. Reads every voxel (in memory). A layer of all zeros raises.
- enclose(layer: str | None = None, *, margin: Any = 0, replace: bool = False, clip: Any = None, description: str | None = None) Any¶
Replace what is drawn with the one box containing it. Returns a
neu_lib.BBox.Place points around a region, then call this to get a box annotation in the viewer and the box in whole voxels, zyx, for
--crop-bbox,neu_vol.extract_roi()orneu-vol write:srv.annotate(tool="point") # click a point at each extreme box = srv.enclose(margin=16, clip="volume", replace=True) lo, hi = box # a BBox unpacks like the pair it is
Every annotation in the layer counts, of any type.
margingrows the box on every side, in voxels: a scalar, or one value per axis (zyx). A negative margin shrinks it; one that empties an axis raises.clipbounds the result:"volume": the served arrays’ extent (bounds());"data": their non-zero voxels (data_bounds());a
neu_lib.BBoxor(lo, hi)pair, e.g.srv.bounds("seg");None(default): no clipping, with a warning if the box starts below zero.
A clip that leaves nothing raises.
replace=Truedeletes the annotations that went into the box, leaving only the result. Withreplace=Falsethe new box is itself an annotation, so calling again with a margin grows it each time.
- on_change(callback: Callable[[], None]) None¶
Call
callback()whenever the browser changes the state.The callback takes no arguments, as in neuroglancer; it should re-read what it needs.
- on_click(callback: Callable[[Click], None], *, button: int = 0, modifiers: str = 'control', scope: str = 'data_view', name: str | None = None) str¶
Call
callback(click)when the user clicks in the data.Returns the action name. The default binding is
control+mousedown0, like neuroglancer’s own tools, since a bare click navigates. The callback is not called for a click outside the data. It runs on the server’s event-loop thread, and an exception in it is logged rather than lost.
- points(layer: str | None = None) list[tuple[int, ...]]¶
Points drawn in the viewer, in whole voxels, zyx.
- screenshot(path: str | None = None)¶
A PNG of what the browser is showing. Blocks until it replies.
With no browser connected this blocks forever: neuroglancer waits for the reply with no timeout.
neu_glance.movie.rendering.wait_for_browser()is the bounded form.
- selected_segments(layer: str | None = None) set[int]¶
The label ids currently shown in a segmentation layer.
- state() dict¶
The viewer’s current state as JSON.
For inspection only: its sources are valid only in this process. To share a link, use
neu-glance genon a published volume.
- stop() None¶
Stop the server, invalidating every viewer URL in this process.
There is one server per process, shared by every viewer, so this stops them all (like
stop_serving()).
- property position: tuple[float, ...] | None¶
Where the viewer is looking, in the served frame’s voxels (zyx).
- property url: str¶
- viewer¶
- volumes¶
- neu_glance.serving.serve(layers: Sequence[neu_glance.served_layer.ServedLayer], *, bind: str | None = None, port: int = 0, into: dict | None = None, annotations: str | bool | None = None, position: Sequence[float] | None = None, layout: str = '4panel', fit: float = 1.0) Server¶
Host
layersand return aServercarrying the viewer URL.Each entry is a
ServedLayer, aneu_lib.Piece(which arrives already named and knowing its kind), or thedictform of a ServedLayer.bind/porttake effect only before the first viewer in the process (neuroglancer reads them at server start); a later different address is ignored with a warning.bind="0.0.0.0"puts the host’s name in the URL, for a browser elsewhere.intomerges the layers into an existing state (fromneu_glance.load_state()orneu_glance.parse_url()), keeping its view.annotations(off by default) adds an empty annotation layer forServer.boxes():Truefor the default name, or a name.Server.annotate()does the same later.By default the view opens centred on the data and zoomed to fit it in each panel of
layout;positionoverrides the centre, andfitabove 1 shows more around it.
- neu_glance.serving.stop_serving() bool¶
Stop the viewer server, whatever started it.
Trueif one was running.For when the
Serverhandle is lost (a re-run cell, an unassigned result). There is one server per process, a daemon thread, and every viewer URL dies with it. The library never blocks a notebook; a stuck cell is running theneu-glance servecommand, which runs until interrupted.
- neu_glance.serving.ANNOTATE_LAYER = 'annotations'¶
- neu_glance.serving.ANNOTATION_TOOLS¶
- neu_glance.serving.ENCODING = 'npz'¶
- neu_glance.serving.logger¶