ND processing & conversions

ND processing & conversions

ND processing refers to functions that operate on a stack of frames – either on a specific dimension type or on any type of dimension. Conversions on the other hand is a heterogeneous set of nodes that converts between channels, binaries and tables or changes their fundamental aspects (e.g. bit-depth or size).

Stack reduction

Stack reduction nodes reduce the selected dimension into a single frame.

Most of the nodes have the same dialog as the Average node where one can select:

  1. Method:

    • All frames - reduces all frames into a single frame.
    • Rolling - uses a window around current frame to for the operation; it keeps the dimension size.
    • Piecewise - divides the dimension into pieces and reduces them separately.
  2. Loop/dimension to be reduced

  3. Number of frames for Rolling and piecewise

Node UI
Node's control dialog

Average

2D/3D

Calculates average per-pixel value over a loop/dimension (e.g. time-lapse).

Average schematic
Stack reduction: Schematic

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: Average (in workflows), Stack reduction (group)

Best Focus Plane

2D/3D

Selects the most in-focus image from a loop.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

  • FocusCriterion (Number)

  • ChannelIndex (Number)

See also: Stack reduction (group)

EDF

2D/3D
Advanced

Creates focused image by taking small in-focus regions from all z-planes.

This node does’t have a control dialog.

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • MakeZMap (Number)

See also: EDF (in workflows), Stack reduction (group)

Integrate (Sum)

2D/3D

Sums per-pixel values over a loop/dimension (e.g. time-lapse)

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: Stack reduction (group)

Max IP

2D/3D

Finds per-pixel maximum value over a loop/dimension (e.g. time-lapse).

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: MaxIP (in workflows), Stack reduction (group)

Max IP Ref

2D/3D

Finds per-pixel maximum value and takes the value from a ref image over a loop/dimension (e.g. time-lapse)

Parameters
Input
  • A (Channel)

  • B (Channel)

Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: Stack reduction (group)

Median

2D/3D

Finds per-pixel median value over a loop/dimension (e.g. time-lapse).

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: Median (in workflows), Stack reduction (group)

Quantile Image

2D/3D

Finds per-pixel quantile value over a loop/dimension (e.g. time-lapse).

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

  • Quantile (Number)

See also: Stack reduction (group)

Min IP

2D/3D

Finds per-pixel minimum value over a loop/dimension (e.g. time-lapse)

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: Stack reduction (group)

Select Frame

2D/3D

Selects subset of frames from a loop/dimension (e.g. time-lapse).

  • Loop - dimension the frames are selected from
  • Select frame - selection type
    • Previous, Next - takes one relative frame to the current
    • First, Middle, Last - takes one absolute frame
    • Relative, Absolute - takes arbitrary number of frames based on the settings below
  • Start/Count/Step - determines which and how many frames are taken (enabled only for Relative and Absolute)
The preview always shows all frames (for efficiency).

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • LoopName (Text)

  • SelectMode (Number)

  • Start (Number)

  • Count (Number)

  • Step (Number)

See also: Select frame (in workflows), Stack reduction (group)

Select Single Frame

2D/3D

Selects one image at given index from a loop/dimension (e.g. time-lapse).

Simplified version of Select Frame that guarantees only one output frame. As a result dynamic parameter can be attached to the Index.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • LoopName (Text)

  • LoopIndex (Number)

See also: Stack reduction (group)

Shading Image

2D/3D

Creates shading image by analysing images from a loop/dimension (e.g. time-lapse).

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

  • Type (Number)

See also: Stack reduction (group)

Stitch Multi Points

2D/3D

Stitches multiple images from a multi-point loop into a single larger image.

Stitching method
how the overlapping area is handled
  • Blending: Overlapping image parts will be blended
  • Multi-pass blending: improved blending algorithm
  • Optimal path: A contour where the two overlapping images differ the least is computed. The images are then stitched along this contour.
  • Nearest
  • Overlap
BlendingOptimal path
blendingschematic
Precise Stitching
Apply image registration
Automatic shading correction
Optionally use the Automatic Shading Correction and choose the type which best represents your sample - Brightfield, DIC like or Fluorescence with offset enabling to heighten brightness level of the corrected image to make objects in dark areas more visible.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control

See also: Stack reduction (group), Stitch multi-points (in workflows)

Stack reduction Binary

Binary stack reduction nodes are analogous to the Stack reductions operating on channels.

And (intersection)

2D/3D

Makes intersection of all binaries over loop/dimension

Parameters
Input
  • A (Binary)
Output
  • R (Binary)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: Stack reduction Binary (group)

Or (union)

2D/3D

Makes union of all binaries over loop/dimension

Parameters
Input
  • A (Binary)
Output
  • R (Binary)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

See also: Stack reduction Binary (group)

Select Single Binary

2D/3D

Selects one binary at given index from a loop/dimension (e.g. time-lapse)

Parameters
Input
  • A (Binary)
Output
  • R (Binary)
Control
  • LoopName (Text)

  • LoopIndex (Number)

See also: Stack reduction Binary (group)

Processing

Align

2D/3D

Move frames in order to stabilize the motion of the sequence.

  • Align to Previous / First frame.
  • Loop to be stabilized.
  • Channel - In case there are more channels (All) select the one to use for calculation.
  • Smooth Movement Correction to even out the harsh movements
  • Correction for Heavily Noised Images applies denoising
  • Super Resolution Alignment performs subpixel registration

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

  • AlignTo (Number)

  • HeavyNoiseCorrection (Number)

  • SmoothMovementCorrection (Number)

  • SuperResolution (Number)

  • ChannelIndex (Number)

See also: Align frames (in workflows)

Equalize Intensity

2D/3D

Analyze image, calculate value of intensity and processes all frames.

This command enhances the dynamic range of any connected channel of an image which contains at least a Z dimension. It analyses the image, calculates values similar to auto-contrast values common for the whole time sequence and then processes all frames of the ND2 file. This method is robust to noise and preserves the original trends of the histogram.

  • Histogram of First Frame Histograms are reshaped to match the histogram of the first frame.
  • Histogram Stretching For every frame a local Bottom and Top is computed, then the global bounds are computed as minimum of bottoms and maximum of tops. Finally all histograms are stretched so their bottom and top match the global bounds.
  • Loop Defines the document loop over which the histograms are equalized (T, Z, MP).
  • Bottom Select whether to match the minimum of intensity (Minimal Intensity) or ignore (Zero Value).
  • Top Select whether to match the mean of intensity (Mean Intensity) or the given quantile (Quantile).
  • Quantile Defines the upper intensity limit based on a user-specified percentile of pixel values. For example, setting the quantile to 95 ignores the top 5% of the brightest pixels when stretching the histogram. This option is enabled only if Quantile is selected in Top.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • Frames (Number)

  • LoopName (Text)

  • Mode (Number)

  • HistogramMode (Number)

  • MethodBottom (Number)

  • MethodTop (Number)

  • Quantile (Number)

Transformations

Nodes in this group can accept both Channels and Binaries and perform the same operation on all inputs.

Resize

2D/3D

Resize images and binaries.

  • Pixel size Resize the image by entering an exact Width and Height in pixels.
  • Percentage of original Resizes the image by a percentage of its current size.
  • Scale down the maximum dimension to Resizes the image so that the largest dimension (width or height) fits within a given value, maintaining proportions.
  • Maintain aspect ratio of The checkbox toggles whether the image aspect ratio is locked, while the edit box lets you type in a ratio value to preserve.
  • Resize method Determines the algorithm used for resizing, which affects quality and smoothness.

Node UI
Node's control dialog

Parameters
Input
  • A, A1, … (Channel, Multiple)
Output
Control
  • WidthPx (Number)

  • HeightPx (Number)

  • WidthFactor (Number)

  • HeightFactor (Number)

  • MaxSizePx (Number)

  • Method (Number)

  • Type (Number)

  • AspectRatio (Number)

  • MaintainAspectRatio (Number)

See also: Transformations (group)

Rotate

2D/3D

Rotate images and binaries.

Rotates the image using the following tools:

  • Draw a horizontal reference line Draw a horizontal reference line in the image to set the proper angle.
  • Draw a vertical reference line Draw a vertical reference line in the image to set the proper angle.
  • Rotate counterclockwise Rotates the image counterclockwise.
  • Rotate clockwise Rotates the image clockwise.
  • Angle Manually adjust the angle [°].
  • Output is Specifies how the output image is cropped/preserved.

Node UI
Node's control dialog

Parameters
Input
  • A, A1, … (Channel, Multiple)
Output
Control
  • Angle (Number)

  • CropType (Number)

See also: Transformations (group)

Binary to Color

Convert

2D/3D

Convert binary into channel having one (1) in place of the objects and zero (0) in the background.

For getting the ID (or any other value) into the channel use the Convert using Table or Convert using Table (3D).

This node does’t have a control dialog.

Parameters
Input
  • A (Binary)
Output
  • R (Channel)
Control

Convert Using Table

2D
3D

Convert binary into floating-point channel having a value (ID by default) in place of the objects and zero (0.0) in the background.

Node UI
Node's control dialog

Parameters
Input
  • A (Binary)

  • B (Table, Optional)

Output
  • R (Channel)
Control
  • Background (Number)

  • ColumnName (Text)

Bitdepth

Change Bit Depth

2D
3D

Change the bit-depth and optionally rescale pixel values.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control
  • BitDepth (Number)

  • RescaleValues (Number)

  • CustomBitDepth (Number)

Convert To Ref

2D/3D

Convert the bit-depth (with optional rescale) back to match the reference.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)

  • B (Channel)

Output
  • R (Channel)
Control
  • RescaleValues (Number)

Channels

Merge Channels

2D/3D

Merge all connected channels into single output.

The node is obsolete after the input Channels node has been upgraded to contain All channels and individual channels as well.
Parameters
Input
  • A1 (Channel, Optional)
Output
  • R (Channel)
Control

Split Channels

2D/3D

Separate a channel that contains multiple components.

The node is obsolete after the input Channels node has been upgraded to contain All channels and individual channels as well.
Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control

Channel to Intensity

2D/3D

Convert input channels into intensity by averaging them

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control

Labels & Classes

Binaries to Color

2D/3D

Convert binary inputs into intensity where values represent a binary layer ID (class)

Parameters
Input
  • A, B, … (Binary, Multiple)
Output
  • R (Channel)
Control

Color to Binaries

2D/3D

Convert intensities representing a binary layer ID (class) into distinct binary layers

Parameters
Input
  • A (Channel)
Output
  • R (Binary)
Control
  • Count (Number)

Render to RGB

The nodes in this group render image (optionally with binary) into RGB channel. Typical use-case is to save the RGB into a separate file such as Color to Single TIFs.

In general it is not useful to save RGB together with other channels as ND2 file must have same size and bit-depth for all channels.

Color

2D/3D

Render channel input into RGB channel

Parameters
Input
  • A, B, … (Channel, Multiple)
Output
  • R (Channel)
Control

See also: Render to RGB (group)

Binary

2D/3D

Render a binary input into RGB channel

Parameters
Input
  • A, B, … (Binary, Multiple)
Output
  • R (Channel)
Control

See also: Render to RGB (group)

Overlay

2D/3D

Render a channel image with a binary overlay into RGB channel.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)

  • B, C, … (Binary, Multiple)

Output
  • R (Channel)
Control
  • Opacity (Number)

See also: Render to RGB (group)

Render to Table

The nodes in this group render image (optionally with binary) into a smaller RGB picture that is embedded into a result table row. It can be viewed as a tooltip or in an object catalog.

Render Frame

2D/3D

Render image frame (optionally with binary) into a RGB thumbnail stored in a table row.

  • Source Rect Size Defines the size (in microns if calibrated) of the center portion of the frame that is cropped and rendered.
  • Source Rect Center defines the center position of the crop. It can be used to move the crop away from the center.
  • Maximum Size (in pixels) is the limit to which the rendered image is scaled down if it is larger.
  • Format & Quality Specifies the image file format (jpg or png) and image quality in percent.
  • Auto-contrast Adjusts the contrast of the rendered images by stretching the intensity values within a defined range. Define the Low and High percentile of intensity values to be clipped.
  • Binary Opacity Controls the transparency of the binary objects (Fill) and their outline (Stroke). A value of 0 means fully transparent, while 1 means fully opaque.

Node UI
Node's control dialog

Parameters
Input
  • A (Channel)

  • B (Table, Optional)

  • B1, B2, … (Binary, Optional, Multiple)

Output
  • R (Table)
Control
  • sourceRectSize (Number)

  • sourceRectCenterX (Number)

  • sourceRectCenterY (Number)

  • fit (Text)

  • format (Text)

  • maxSize (Number)

  • quality (Number)

  • autocontrast (Number)

  • autocontrastThrLow (Number)

  • autocontrastThrHigh (Number)

  • binFillOpacity (Number)

  • binStrokeOpacity (Number)

See also: Render to Table (group), Well-plate view (in workflows)

Render Objects

2D/3D

Render binary objects (optionally with underlying channel) into a RGB thumbnail stored in a table row.

  • Rendered size Defines the size in pixels of the rendered objects.
  • Format & Quality Specifies the image file format (jpg or png) and image quality in percent.
  • Object scaling Select from stretched or fixed scale objects.
  • Auto-contrast Adjusts the contrast of the rendered images by stretching the intensity values within a defined range. Define the Low and High percentile of intensity values to be clipped.
  • Binary Opacity Controls the transparency of the binary objects (Fill) and their outline (Stroke). A value of 0 means fully transparent, while 1 means fully opaque.

Node UI
Node's control dialog

Parameters
Input
  • A (Binary)

  • B (Channel)

Output
  • R (Table)
Control
  • renderWidth (Number)

  • renderHeight (Number)

  • scaleOption (Number)

  • keepScaleOption (Number)

  • autoSizeOption (Number)

  • cropWidth (Number)

  • cropHeight (Number)

  • format (Text)

  • quality (Number)

  • autocontrast (Number)

  • autocontrastThrLow (Number)

  • autocontrastThrHigh (Number)

  • binFillOpacity (Number)

  • binStrokeOpacity (Number)

See also: Object catalog (in workflows), Render to Table (group)

RGB

RGB to Intensity

2D/3D

Convert RGB channel into intensity channel by averaging all three components

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control

RGB to HSI

2D/3D

Converts red-green-blue into hue-saturation-intensity

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control

HSI to RGB

2D/3D

Convert hue-saturation-intensity into red-green-blue

Parameters
Input
  • A (Channel)
Output
  • R (Channel)
Control

Python scripting

Python

2D
3D

Advanced

The generic Python node allows to process and transform any inputs into any outputs using Python interpreter. The whole ecosystem of Python packages can be used inside GA3.

To get started, see the Python workflow examples.

Python editor
Node dialog

Inputs & outputs

Inputs and outputs can be added using the buttons in the top toolbar.

  • Add image channel(s) input
  • Add binary input
  • Add table input
  • Add image channel(s) output
  • Add binary output
  • Add table output

To remove them use buttons.

The tooltip on both inputs and outputs suggests how to access it in the code (e.g. inp[0], out[1]).

Python parameter tooltips
Node inputs and outputs

User parameters

  • Edit user parameters P# that allow to customize python script by the user. The parameters are available to the script in all three function in the par tuple

Python user parameters
Setting up user parameters

The definitions for controls are provided as JSON strings:

{"control":"Text"}
{"control":"Number"}
{"control":"Slider","decimals":2,"max":100,"min":1}
{"control":"Selection","list":[{"text":"True","value":1},{"text":"False","value":0}]}

After switching to the user mode (always in the wizard), the GA3 will present a user GUI instead of the python code. To go back to developer mode click the link. The developer mode can be protected with a password.

Python user mode
User mode

This node supports three user parameter types: int, float and string. Each type has its own set of available GUI controls. The tabs below list the controls for each type and the required fields.

Controls for float type are number, slider and selection.

  • Number is a basic numeric input field.
    • control: "Number"
{"control":"Number"}
  • Slider is a range slider for continuous values with fixed precision, accompanied by an input field.
    • control: "Slider"
    • decimals — integer ≥ 0 (number of decimal places)
    • min — number (lower bound)
    • max — number (upper bound)
{"control":"Slider", "decimals": 3, "min": 0.0, "max": 100.0}
  • Selection: a dropdown with labeled choices.
    • control: "Selection"
    • list — array of items
      • each item:
        • text — string (label)
        • value — number (float value)
{"control":"Selection", "list": [{"text":"P" , "value": 1.0}, {"text":"N" , "value": -1.0}]}

Controls for int type are number and selection.

  • Number is a basic numeric input field.
    • control: "Number"
{"control":"Number"}
  • Selection is a dropdown with labeled choices.
    • control: "Selection"
    • list — array of items
    • each item:
      • text — string (label)
      • value — integer
{"control":"Selection", "list": [{"text":"True" , "value": 0}, {"text":"False" , "value": 1}]}

Controls for string type are text, selection and path.

  • Text is a basic text input field.
    • control: "Text"
{"control":"Text"}
  • Selection is a dropdown with labeled choices.
    • control: "Selection"
    • list — array of items
    • each item:
      • text — string (label)
      • value — string
{"control":"Selection", "list": [{"text":"A" , "value": "a"}, {"text":"B" , "value": "b"}]}
  • Path is an input field with a browse button that opens the file or folder dialog.

    • control: "Path"
    • type — one of "file", "folder"
    • mode — one of "open", "save"

    type chooses whether the dialog targets files or folders
    mode affects both the dialog for "file" type (Open or Save as) and how relative paths are resolved during a GA3 execution

    • "open": relative paths are resolved against the input file.
    • "save": relative paths are resolved against the output file.
{"control":"Path", "type": "file", "mode": "save"}

Python interpreter

There are three option which python is invoked and how:

  • Internal python is for when the Python scripts run inside the NIS-Express process in an embedded python interpreter. It is faster but limited to modules shipped with NIS-Express.

  • External python is for when the user specifies which python interpreter to run. This option is for when additional python packages or modules have to be installed. In order not to disrupt the NIS-Express environment a completely different python environment is created with its python interpreter. Select pythonw.exe windowless form. This option is to be used with environment managers like conda or mamba.

  • Managed environment is for when an environment has to be transferred with the GA3 node. It is en extension of the previous option. Instead of specifying which python to run, the environment definition YAML is provided. Thus it can be then installed from the node GUI. This option is for advanced users. It is intended either as a last step in development of a python node or to be used in the user-mode entirely.

NIS-Express requires Python 3.10 or higher.

Editing in VS Code

It is possible to edit and debug the code in VS Code by clicking the button. After selecting a working folder the the python file with the configuration is copied to that folder.

VS Code Init
Setting up

Another icon opens the VS Code.

VS Code Editor debugging
Debugging in VS Code

The GA3 node dialog hides the code as the editing happens elsewhere. The node can copy the code and run the code from the working folder automatically after any change change or manually. While doing so it copies the data (inp, out, …) parameters to the data folder to be accessible for the script.

Dialog with VS Code
Node dialog when in VS Code

The code in VS Code behaves as if it was called from the GA3 node.

The script

The default script looks like this:

GA3 Python Editor
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# IMPORTANT: 'limnode' must be imported like this (not from nor as)
import limnode

# defines output parameter properties
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    pass

# return Program for dimension reduction or two-pass processing
def build(par: limnode.UserParTuple, loops: limnode.LoopDefs) -> limnode.Program|None:
    return None

# called for each frame/volume
def run(inp: limnode.InDataTuple,
        out: limnode.OutDataTuple,
        par: limnode.UserParTuple,
        ctx: limnode.RunContext) -> None:
    pass

# child process initialization (when outproc is set)
if __name__ == '__main__':
    from limnode import print
    limnode.child_main(run, output, build)

At the beginning of the script, always import limnode. This ensures that key functions are executed correctly.

GA3 Python Editor
import limnode

There are three key functions that must always be present and have the correct number of input parameters:

  • output(inp, out, par): Defines the node outputs
  • build(par, loops): Specifies how the run is called (e.g., two-pass processing).
  • run(inp, out, par, ctx): Defines how each frame or volume is processed.

All important details about these functions can be seen inside:

%userprofile%\AppData\Local\Programs\NIS-Express\Python\Lib\site-packages\limnode.py

For any other option than internal python, following piece of code must be present at the end of the script:

GA3 Python Editor
if __name__ == '__main__':
    from limnode import print
    limnode.child_main(run, output, build)

Function output(...)

The purpose of this function is to define all aspects of output parameters by modifying out tuple items.

output is typically called before the run of GA3 after some user change upstream the graph. The information provided is used by the downstream nodes in GUI and run (for example table columns defined here are listed dependent nodes).

In GA3 output parameters can have assigned input or can be create new output. When they have assigned input, they inherit name, color and metadata such as the number of components or column definitions (for tables) from the assigned input. Assigned output changes as the input gets reconnected.

New outputs however, must be fully defined.

For assigning all output items have method assign which takes input of the same type. For creating new output there are dedicated methods makeNew... All these methods return self and can be safely concatenated.

By default the node tries to assign to every output the first input of the same type. If this is not possible a new output is made.

For details see the limnode.py listing.

Channels and binaries

For channel data processing the bit-depth is very important:

  • the node should accept any bit-dept on input (it can be as low as uint8)
  • for computations it may convert to higher bit-depth (e.g. float64)
  • depending on the algorithm it should output the original bit-depth or use higher if necessary

For binary processing too as:

  • UInt8 means unpainted binaries where non-zero pixel is an object and zero is background
  • Int32 means painted binaries where every pixel contains object id or zero for background

Do not take the max value from the numpy dtype as it can be misleading because of intermediate bit-depths such as 10, 12, 14 all encoded in UInt16. Always use appropriate bitsPerComponent such as:

max_val = 2 ** out[0].bitsPerComponent - 1

Example of assigning an input channel to output and changing it to float:

GA3 Python Editor
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    out[0].assign(inp[0]).makeFloat()

Example of creating a new YFP (yellow) 16bit channel:

GA3 Python Editor
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    out[0].makeNewMono("YFP",
                       "#FFFF00").makeUInt16() # color can be a RGB tuple: (255, 255, 0)

Tables

Tables must have fully defined (names, types, accumulation) columns in the output(...) function.

The simplest scenario is adding columns to an existing table that is an input.

GA3 Python Editor
# by assigning the table gets all columns from inp[0] and a new Ratio column of type float
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    out[0].assign(inp[0]).withFloatCol("Ratio")

It may not be possible because the output table:

  • has unrelated columns,
  • there is no table input or
  • it has different accumulation.

In that case the columns must be defined completely.

  1. To create a new table use:

    • makeEmpty("NewTable", inp[1]) to create a new and empty (no columns) table. The optional input reference is used to copy eventual accumulation by previous nodes.
    • makeNew("NewTable", inp[1]) to create a new table and copies columns and aggregation from the referenced input table. If reference table is not provided it adds only loop columns.
  2. Add bookkeeping columns by calling:

    • withLoopCols() to enable frame synchronization with row selection.
    • withObjectCols() or withObject3dCols() if rows are objects to enable object selection.
  3. Add feature columns by calling:

    • with[Int|Float|Str]Col("X coords", "px") to add data columns of specified type.
  4. Optionally if the new output table should be accumulated (contains more than frame/volume) call:

    • setAccumulatedOver[ZStack|TimeLapse|MultiPoint|All](...) te set appropriate flag.

The example below creates new table “Positions”. Because no reference table is provided it will have by default only bookkeeping loop columns. Then two float columns are added.

GA3 Python Editor
# by making new the table has only loop columns and the two columns added to it
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    unit_x, unit_y, _ = inp[0].units # inp[0] is InputChannelDef or InputBinaryDef
    new_table = out[0].makeNew("Positions")
    new_table.withFloatCol("X coord", unit_x).withFloatCol("Y coord", unit_y)

The same outcome but using makeEmpty(...) method.

GA3 Python Editor
# by making new the table has only loop columns and the two columns added to it
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    unit_x, unit_y, _ = inp[0].units # inp[0] is InputChannelDef or InputBinaryDef
    new_table = out[0].makeEmpty("Positions")
    new_table.withLoopCols().withFloatCol("X coord", unit_x).withFloatCol("Y coord", unit_y)

Function build(...)

It is called before run to inquire how to call the run method. By default it returns None to indicate that run will be called for each frame/volume once. Other two possibilities are for:

For stack reduction return ReduceProgram with appropriate loop overZStack or overTimeLapse method.

GA3 Python Editor
def build(par: limnode.UserParTuple, loops: limnode.LoopDefs) -> limnode.Program|None:
    return limnode.ReduceProgram(loops).overZStack()

For two-pass algorithms return TwoPassProgram with appropriate loop overZStack or overTimeLapse method.

GA3 Python Editor
def build(par: limnode.UserParTuple, loops: limnode.LoopDefs) -> limnode.Program|None:
    return limnode.TwoPassProgram(loops).overZStack()

For details see the limnode.py listing.

Function run(...)

It is called for every frame/volume (depending on return value of build) to fill the data of each out item.

Channels and binaries

For OutputChannelData and OutputBinaryData the data property is a numpy.ndarray of shape (z, y, x, c) and dtype defined by output function. By default it is filled with zeros.

GA3 Python Editor
def run(inp: limnode.InDataTuple,
        out: limnode.OutDataTuple,
        par: limnode.UserParTuple,
        ctx: limnode.RunContext) -> None:
    out[0].data[:] = inp[0].data[:] * 2
About ndarray shape and dtype

shape

In the python node the data ndarray for both Color Channels and Binaries have ndim=4 (rank of 4) and following shape:

  1. z - depth of 3D Z stack volumes (1 - for 2D)
  2. y - height of 2D image
  3. x - width of 2D image
  4. c - component (a.k.a channel) of image (1 for mono and binaries, 3 for RGB, n for all)

For example:

print(inp[0].data.shape) # output: (1, 200, 300, 1)

Means that inp[0] is:

  • 2D (depth=1),
  • 300x200 pixels (remember the order is reversed dept, height, width, components)
  • mono (one component).

dtype

Color Channel data are either:

  • unsigned integer
    • uint8 where values are in range <0, 256) maximum being 255
    • uint16 where values are in range <0, 65536) maximum being 65535
  • floating point
    • float32 where values are unrestricted

Binary data is:

  • uint8 interpreted as 0 - background and all other values as object
  • int32 interpreted as 0 - background and all other values as object ID

Tables

Output table columns are defined by output(...) function. However, it is the run(...) responsibility to fill the output out[...] tables with data.

The simplest way to work with the tables is using pandas DataFrame interface.

  1. When the output table is made from an input table and shares most of the columns:
    • get the input table using dataFrame(),
    • add column data defined in the output(...) using pandas DataFrame api and
    • set the df: DataFrame into out[...]. Assert will fail if columns in df do not match exactly to what was defined in the output(...)
GA3 Python Editor
def run(inp: limnode.InDataTuple,
        out: limnode.OutDataTuple,
        par: limnode.UserParTuple,
        ctx: limnode.RunContext) -> None:
    df = inp[0].dataFrame()                         #1
    df["Ratio"] = df["MeanOfDiO"] / df["MeanOfDiI"] #2
    out[0].withDataFrame(df)                        #3
  1. When there is no input table:
    • get an empty DataFrame dataFrame() from out[...],
    • fill the data in (note that loop indexes are filled automatically) and
    • set it back into out[...].
GA3 Python Editor
def run(inp: limnode.InDataTuple,
        out: limnode.OutDataTuple,
        par: limnode.UserParTuple,
        ctx: limnode.RunContext) -> None:
    df = out[0].dataFrame()       #1
    df.loc[0, "X coord"] =  100.0 #2
    df.loc[0, "Y coord"] =  100.0
    out[0].withDataFrame(df)      #3

There are other access patterns that use plain python type but these are deprecated. Under the hood the tables (InputTableData and OutputTableData) have data property of type LimTableDataBase defined in limtabledata.py.

Accessing DataFrame columns by ID

Calling param.dataFrame() returns a DataFrame where columns are accessed by GA3 table column titles. These column titles may change during GA3 editing or because input channels use different names. Such changes can break your script and cause it to look for non-existing columns.

To prevent this, call param.dataFrame(use_col_ids=True) which returns a DataFrame whose columns are accessed by stable GA3 table column IDs that do not change.

You can see the available column IDs in the standard output console by calling print(param.data.colIdList).

About ‘recorded data’

Each item (Channel, binary and table) has prefilled recdata (of type LimTableDataBase) with per-frame recorded data (e.g. AcqTime, X, Y, Z, …) which can be accessed and even altered.

By default they are copied from the source.

The context

The context ctx provides more information about the current run:

limnode.py
@dataclass(kw_only=True)
class RunContext:
   inpFilename: str                            # full input filename
   outFilename: str                            # full output filename (typically same as inpFilename except in Cluster Computing)
   inpParameterCoordinates: tuple[tuple[int]]  # loop coordinates for every input parameter
   outCoordinates: tuple[int]                  # output loop coordinates
   programLoopsOver: list[str]|None            # loop names the program runs over
   programCoordinateIndexes: tuple[int]|None   # inner loops: indexes of loops that must be handled in Python (not by GA3) e.g. Z-Stack loop
   programPass: int                            # 1-based, current program pass (1..N)
   programIndex: int                           # 0-based, current call index within this pass (0..programLength-1)
   programLength: int                          # total calls in this pass
   finalCall: bool

   @property
   def shouldAbort(self) -> bool:              # abort is requested
       ...

ctx.shouldAbort should be checked whenever possible. However, it is not possible to abort calls into libraries. In such cases use interpreter in External process.

For details see the limnode.py listing.

Stack reduction algorithms

When ReduceProgram was returned in build the run function must fill the out items only when ctx.lastCall == True. Before lastCall it is expected to accumulate the result like in this maximum intensity example:

GA3 Python Editor
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# IMPORTANT: 'limnode' must be imported like this (not from nor as)
import limnode, numpy as np

tmp = None

# defines output parameter properties
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    out[0].makeNewMono("DiY", (0, 0, 255))

# return Program for dimension reduction or two-pass processing
def build(par: limnode.UserParTuple, loops: limnode.LoopDefs) -> limnode.Program|None:
    return limnode.ReduceProgram(loops).overZStack()

# called for each frame/volume
def run(inp: limnode.InDataTuple,
        out: limnode.OutDataTuple,
        par: limnode.UserParTuple,
        ctx: limnode.RunContext) -> None:
    global tmp
    if (ctx.programPass == 1 and ctx.programIndex == 0) or tmp is None:
        tmp = np.zeros(inp[0].data.shape, inp[0].data.dtype)
    tmp = np.maximum(tmp, inp[0].data)
    if ctx.finalCall:
        out[0].data[:] = tmp

# child process initialization (when outproc is set)
if __name__ == '__main__':
    from limnode import print
    limnode.child_main(run, output, build)

Two-pass algorithms

When TwoPassProgram was returned in build the run function must fill the out items only when ctx.lastCall == True. The run can monitor the frame/volume being processed in ctx.inpParameterCoordinates and ctx.reducingCoordinateIndexes. During first pass the node analyses data and then during second pass if fills the out items. In the example below the node accumulates a maximum intensity projection in the first pass and during the second pass it subtracts current frame from it:

GA3 Python Editor
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import limnode, numpy as np

tmp = None

# defines output parameter properties
def output(inp: limnode.InDefTuple,
           out: limnode.OutDefTuple,
           par: limnode.UserParTuple) -> None:
    out[0].assign(inp[0])

# return Program for dimension reduction or two-pass processing
def build(par: limnode.UserParTuple, loops: limnode.LoopDefs) -> limnode.Program|None:
    return limnode.TwoPassProgram(loops).overZStack()

# called for each frame/volume
def run(inp: limnode.InDataTuple,
        out: limnode.OutDataTuple,
        par: limnode.UserParTuple,
        ctx: limnode.RunContext) -> None:
    global tmp
    if (ctx.programPass == 1 and ctx.programIndex == 0) or tmp is None:
        tmp = np.zeros(inp[0].data.shape, inp[0].data.dtype)
    if ctx.finalCall:
        out[0].data[:] = tmp - inp[0].data[:]
    else:
        tmp = np.maximum(tmp, inp[0].data)

# child process initialization (when outproc is set)
if __name__ == '__main__':
    from limnode import print
    limnode.child_main(run, output, build)

All numpy.ndarray data properties are valid only during the particular run. If the algorithm has to to store it in a variable it has to make a copy of it.

tmp = numpy.copy(inp[0].data[0, :, :, 0])

Saving and sharing

Save a recipe with only the Python node. The snippet recipe can be copied, shared and inserted in any other recipe.

Using print(...)

NIS-Express hooks into both Python stderr and stdout. The stderr is redirected into the log file and the stdout to:

  • the node dialog when called from within the output(), build() and run() functions, otherwise it goes to
  • the log file.

The log file (log_YYYY-MM-DD.txt) can be found in:

%userprofile%\AppData\Roaming\Laboratory Imaging\NIS-Express\LogFiles

When “External” is selected the print output can be redirected to the node dialog using the print() function from the limnode. It is enabled by default by importing the print function:

# child process initialization (when outproc is set)
if __name__ == '__main__':
    from limnode import print
    limnode.child_main(run, output, build)

There is also a log function:

limnode.log("Debug message:", value) # it has the same parameters as print()

which always goes into into separate log file (pylog_YYYY-MM-DD.txt) in the folder as above.

Use LLMs with Python nodes

It is possible to ask large language models (LLMs) like ChatGPT, 5 Gemini or Copilot 5 to generate python code. To simplify the interaction with the LLMs there is a button Copy prompt for a LLM which prepares a prompt ready to be pasted into the LLMs. The user has to add a query at the end below the Task provided by user.

Some examples of prompts:

  1. simple segmentation:

Add one input channel and one binary:

Threshold objects using otsu threshold.
  1. more complex:

Add one input channel and one output table:

Threshold the input using otsu threshold, then use scikit image to compute following per-object features:
- mean channel intensity under each object,
- object area in pixels,
- object area in microns,
- object perimeter in microns.

Put the features into the output table as columns after loop and object cols.
  1. Dimension reduction:

Add one input and output channel:

Create a MaxIP over time.
Python in NIS-Express

Python 3.12 is embedded inside NIS-Express in such a way that NIS-Express can call the python interpreter and pass python code to it.

The python program and all its modules are in NIS-Express folder so that it does not contaminate host environment that may be using different version of Python.

The internal python comes with the following modules installed:

  • httpx
  • Jinja2
  • limnd2
  • matplotlib
  • numpy
  • ome_types
  • openpyxl
  • pandas
  • packaging
  • paramiko
  • psutil
  • pydantic
  • pydantic-settings
  • pywin32
  • playwright
  • requests
  • scikit-image
  • scikit-learn
  • scipy
  • uvicorn[standard]
Package management
Installing other packages in the internal python may break NIS-Express. Use conda/mamba independent environments by pointing the External python to the appropriate interpreter.

Python packages can be installed and managed with pip.bat from the NIS-Express directory.

  1. Change to the NIS-Express directory

Current user:

Terminal
cd "%userprofile%\AppData\Local\Programs\NIS-Express"

All users:

Terminal
cd "C:\Program Files\NIS-Express"
  1. Install a package using pip.bat.
Terminal
pip.bat install <package>

or alternatively, use python.bat to call pip

Terminal
python.bat -m pip install <package>
  1. Run the interpreter and try to import the package.
Terminal
python.bat
>>> import <package>
Parameters
Input
  • A1, A2, … (Channel, Optional, Multiple)

  • B1, B2, … (Binary, Optional, Multiple)

  • T1, T2, … (Table, Optional, Multiple)

Output
Control
  • mode (Number)

  • code (Text)

  • refresh (Number)

  • environmentDesc (Text)

  • outprocType (Number)

  • environmentName (Text)

  • outprocPath (Text)

  • editingOutsidePath (Text)

  • editingOutsideEnabled (Number)

  • pyParDefs (Text)

  • devModeKey (Text)

  • description (Text)