Status: Needs Review
This page has not been reviewed for accuracy and completeness. Content may be outdated or contain errors.
Utility Nodes¶
Utility nodes cover binary decisions, label conversion, prompt injection, and small transforms that bind larger workflows together.
Deciders¶
binary_decider
¶
Binary decision nodes for thresholding anomaly scores and logits.
This module provides threshold-based decision nodes that convert continuous anomaly scores or logits into binary decisions (anomaly/normal). Two strategies are available:
- BinaryDecider: Fixed threshold applied globally to sigmoid-transformed logits
- QuantileBinaryDecider: Adaptive per-batch thresholding using quantile statistics
Decision nodes are typically placed at the end of anomaly detection pipelines to convert detector outputs into actionable binary masks for visualization or evaluation.
BinaryDecider
¶
Bases: BinaryDecider
Simple decider node using a static threshold to classify data.
Accepts logits as input, applies sigmoid transformation to convert to probabilities [0, 1], then applies threshold to produce binary decisions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
float
|
The threshold to use for classification after sigmoid. Values >= threshold are classified as anomalies (True). Default: 0.5 |
0.5
|
Examples:
>>> from cuvis_ai.deciders.binary_decider import BinaryDecider
>>> import torch
>>>
>>> # Create decider with default threshold
>>> decider = BinaryDecider(threshold=0.5)
>>>
>>> # Apply to RX anomaly logits
>>> logits = torch.randn(4, 256, 256, 1) # [B, H, W, C]
>>> output = decider.forward(logits=logits)
>>> decisions = output["decisions"] # [4, 256, 256, 1] boolean mask
>>>
>>> # Use in pipeline
>>> pipeline.connect(
... (logit_head.logits, decider.logits),
... (decider.decisions, visualizer.mask),
... )
See Also
QuantileBinaryDecider : Adaptive per-batch thresholding ScoreToLogit : Convert scores to logits before decisioning
Source code in cuvis_ai/deciders/binary_decider.py
forward
¶
Apply sigmoid and threshold-based decisioning on channels-last data.
Args: logits: Tensor shaped (B, H, W, C) containing logits.
Returns: Dictionary with "decisions" key containing (B, H, W, 1) decision mask.
Source code in cuvis_ai/deciders/binary_decider.py
QuantileBinaryDecider
¶
Bases: BinaryDecider
Quantile-based thresholding node operating on BHWC logits or scores.
This decider computes a tensor-valued threshold per batch item using the requested quantile over one or more non-batch dimensions, then produces a binary mask where values greater than or equal to that threshold are marked as anomalies. Useful for adaptive thresholding when score distributions vary across batches.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
quantile
|
float
|
Quantile in the closed interval [0, 1] used for the threshold computation (default: 0.995). Higher values (e.g., 0.99, 0.995) are typical for anomaly detection to capture rare events. |
0.995
|
reduce_dims
|
Sequence[int] | None
|
Axes (relative to the input tensor) over which to compute the quantile.
When |
None
|
Examples:
>>> from cuvis_ai.deciders.binary_decider import QuantileBinaryDecider
>>> import torch
>>>
>>> # Create quantile-based decider (99.5th percentile)
>>> decider = QuantileBinaryDecider(quantile=0.995)
>>>
>>> # Apply to anomaly scores
>>> scores = torch.randn(4, 256, 256, 1) # [B, H, W, C]
>>> output = decider.forward(logits=scores)
>>> decisions = output["decisions"] # [4, 256, 256, 1] boolean mask
>>>
>>> # Per-channel thresholding (reduce H, W only)
>>> decider_perchannel = QuantileBinaryDecider(
... quantile=0.99,
... reduce_dims=[1, 2], # Compute threshold per channel
... )
See Also
BinaryDecider : Fixed threshold decisioning
Source code in cuvis_ai/deciders/binary_decider.py
forward
¶
Apply quantile-based thresholding to produce binary decisions.
Computes per-batch thresholds using the specified quantile over reduce_dims, then classifies values >= threshold as anomalies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Input logits or anomaly scores, shape (B, H, W, C) |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Tensor]
|
Dictionary containing:
|
Source code in cuvis_ai/deciders/binary_decider.py
resolve_reduce_dims
¶
Resolve reduction dimensions, handling negative indices.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reduce_dims
|
tuple[int, ...] | None
|
Dimension indices to reduce over (may contain negatives).
When |
required |
tensor_ndim
|
int
|
Number of dimensions in the tensor. |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, ...]
|
Sorted, deduplicated positive dimension indices. |
Source code in cuvis_ai/deciders/binary_decider.py
two_stage_decider
¶
Two-Stage Binary Decision Module.
This module provides a two-stage binary decision node that first applies an image-level anomaly gate based on top-k statistics, then applies pixel-level quantile thresholding only for images that pass the gate.
This approach reduces false positives by filtering out images with low overall anomaly scores before applying pixel-level decisions.
See Also
cuvis_ai.deciders.binary_decider : Simple threshold-based binary decisions
TwoStageBinaryDecider
¶
TwoStageBinaryDecider(
image_threshold=0.5,
top_k_fraction=0.001,
quantile=0.995,
reduce_dims=None,
**kwargs,
)
Bases: BinaryDecider
Two-stage binary decider: image-level gate + pixel quantile mask.
Source code in cuvis_ai/deciders/two_stage_decider.py
forward
¶
Apply two-stage binary decision: image-level gate + pixel quantile.
Stage 1: Compute image-level anomaly score from top-k pixel scores. If below threshold, return blank mask (no anomalies).
Stage 2: For images passing the gate, apply pixel-level quantile thresholding to create binary anomaly mask.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Anomaly scores [B, H, W, C] or [B, H, W, 1]. |
required |
**_
|
Any
|
Additional unused keyword arguments. |
{}
|
Returns:
| Type | Description |
|---|---|
dict[str, Tensor]
|
Dictionary with "decisions" key containing binary masks [B, H, W, 1]. |
Notes
The image-level score is computed as the mean of the top-k% highest pixel scores. For multi-channel inputs, the max across channels is used for each pixel.
Source code in cuvis_ai/deciders/two_stage_decider.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | |
Conversion¶
conversion
¶
Conversion nodes for anomaly and segmentation pipelines.
This module provides:
ScoreToLogit: affine conversion from anomaly scores to logitsDecisionToMask: combine binary decisions with identity IDs into masks
ScoreToLogit
¶
Bases: Node
Trainable head that converts RX scores to anomaly logits.
This node takes RX anomaly scores (typically Mahalanobis distances) and applies a learned affine transformation to produce logits suitable for binary classification with BCEWithLogitsLoss.
The transformation is: logit = scale * (score - bias)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
init_scale
|
float
|
Initial value for the scale parameter |
1.0
|
init_bias
|
float
|
Initial value for the bias parameter (threshold) |
0.0
|
Attributes:
| Name | Type | Description |
|---|---|---|
scale |
Parameter or Tensor
|
Scale factor applied to scores |
bias |
Parameter or Tensor
|
Bias (threshold) subtracted from scores before scaling |
Examples:
>>> # After RX detector
>>> rx = RXGlobal(eps=1e-6)
>>> logit_head = ScoreToLogit(init_scale=1.0, init_bias=5.0)
>>> logit_head.unfreeze() # Enable gradient training
>>> graph.connect(rx.scores, logit_head.scores)
Source code in cuvis_ai/node/conversion.py
statistical_initialization
¶
Initialize bias from statistics of RX scores using streaming approach.
Uses Welford's algorithm for numerically stable online computation of mean and standard deviation, similar to RXGlobal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_stream
|
InputStream
|
Iterator yielding dicts matching INPUT_SPECS (port-based format) Expected format: {"scores": tensor} where tensor is the RX scores |
required |
Source code in cuvis_ai/node/conversion.py
update
¶
Update running statistics with a batch of scores.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scores
|
Tensor
|
Batch of RX scores in BHWC format |
required |
Source code in cuvis_ai/node/conversion.py
finalize
¶
Finalize statistics and set bias to mean + 2*std.
This threshold (mean + 2*std) is a common heuristic for anomaly detection, capturing ~95% of normal data under Gaussian assumption.
Source code in cuvis_ai/node/conversion.py
reset
¶
Reset all statistics and accumulators.
Source code in cuvis_ai/node/conversion.py
forward
¶
Transform RX scores to logits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scores
|
Tensor
|
Input RX scores with shape (B, H, W, 1) |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Tensor]
|
Dictionary with "logits" key containing transformed scores |
Source code in cuvis_ai/node/conversion.py
get_threshold
¶
Get the current anomaly threshold (bias value).
Returns:
| Type | Description |
|---|---|
float
|
Current threshold value |
set_threshold
¶
Set the anomaly threshold (bias value).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
float
|
New threshold value |
required |
predict_anomalies
¶
Convert logits to binary anomaly predictions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
Logits from forward pass, shape (B, H, W, 1) |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Binary predictions (0=normal, 1=anomaly), shape (B, H, W, 1) |
Source code in cuvis_ai/node/conversion.py
DecisionToMask
¶
Bases: Node
Combine binary decisions and identity labels into a single int32 mask.
The output mask keeps per-pixel identity IDs where the decision is True and sets all non-matching pixels to 0.
forward
¶
Apply decisions to identities and return the final segmentation mask.
Source code in cuvis_ai/node/conversion.py
Labels¶
labels
¶
Label Mapping Nodes.
This module provides nodes for converting multi-class segmentation masks to binary anomaly labels. These nodes are useful when training with datasets that have multi-class annotations but the task requires binary anomaly detection.
The main node remaps class IDs to binary labels (0=normal, 1=anomaly) based on configurable normal and anomaly class ID lists.
See Also
cuvis_ai.deciders : Binary decision nodes for threshold-based classification
BinaryAnomalyLabelMapper
¶
Bases: Node
Convert multi-class segmentation masks to binary anomaly targets.
Masks are remapped to torch.long tensors with 0 representing normal pixels and 1 indicating anomalies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
normal_class_ids
|
Iterable[int]
|
Class IDs that should be considered normal (default: (0, 2)). |
required |
anomaly_class_ids
|
Iterable[int] | None
|
Explicit anomaly IDs. When |
None
|
Source code in cuvis_ai/node/labels.py
forward
¶
Map multi-class labels to binary anomaly labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cube
|
Tensor
|
Features/scores to pass through [B, H, W, C] |
required |
mask
|
Tensor
|
Multi-class segmentation masks [B, H, W, 1] |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Tensor]
|
Dictionary with "cube" (pass-through) and "mask" (binary bool) keys |
Source code in cuvis_ai/node/labels.py
Prompt Nodes¶
prompts
¶
Static nodes and helpers for frame-indexed text, mask, and bbox prompt schedules.
SpatialPromptSpec
dataclass
¶
One scheduled spatial (mask or bbox) prompt entry.
MaskPrompt
¶
Bases: Node
Emit a scheduled label-map prompt mask for the requested frame.
Source code in cuvis_ai/node/prompts.py
forward
¶
Emit the scheduled prompt label map for frame_id or an empty mask.
Source code in cuvis_ai/node/prompts.py
BBoxPrompt
¶
Bases: Node
Emit scheduled runtime bbox prompts plus overlay-friendly debug tensors.
Source code in cuvis_ai/node/prompts.py
forward
¶
Emit the scheduled bbox prompt list for frame_id or an empty list.
Source code in cuvis_ai/node/prompts.py
TextPrompt
¶
Bases: Node
Emit a runtime text prompt for the requested frame.
Source code in cuvis_ai/node/prompts.py
forward
¶
Emit the resolved prompt text for frame_id or an empty string.
Source code in cuvis_ai/node/prompts.py
parse_spatial_prompt_spec
¶
Parse <object_id>:<detection_id>@<frame_id> into a spatial prompt spec.
Source code in cuvis_ai/node/prompts.py
parse_text_prompt_spec
¶
Parse <text>@<frame_id> into a typed text spec.
Bare <text> is accepted as a backward-compatible alias for <text>@0.
Source code in cuvis_ai/node/prompts.py
load_text_prompt_schedule
¶
Build a per-frame text prompt schedule.
Multiple prompt frames are allowed. V1 rejects multiple distinct texts on the same frame.
Source code in cuvis_ai/node/prompts.py
normalize_text_prompt_mode
¶
Normalize and validate the text-prompt emission mode.
Source code in cuvis_ai/node/prompts.py
resolve_text_prompt_for_frame
¶
Resolve the runtime text prompt for frame_id.
scheduled emits only on exact prompt frames.
repeat keeps the latest scheduled prompt active until replaced.
Source code in cuvis_ai/node/prompts.py
load_detection_index
¶
Load a flat COCO or track-centric SAM3 detection JSON into frame-indexed metadata.
Source code in cuvis_ai/node/prompts.py
load_mask_prompt_schedule
¶
Load detection JSON and build per-frame label-map prompts.
Source code in cuvis_ai/node/prompts.py
load_bbox_prompt_schedule
¶
Load detection JSON and build per-frame bbox prompts.