Skip to content

EngineOutput to normalize engine outputs#16

Open
dtronmans wants to merge 19 commits into
develop-v0.1.0from
feat/abstract-engines
Open

EngineOutput to normalize engine outputs#16
dtronmans wants to merge 19 commits into
develop-v0.1.0from
feat/abstract-engines

Conversation

@dtronmans

@dtronmans dtronmans commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Purpose

  • Two main abstractions: EngineOutput (parser-facing, now the parsers have no information regarding which backend is supplying them) and TensorSpec / ModelSpec for resolved model I/O metadata.
  • ONNX engine and depthai engine now both return EngineOutput instead of their raw data they were previously returning (raw tensors or dai.NNData) and new implemented engines have to adapt this.
  • parsers now consume EngineOutput + ModelSpec: no longer dependent on engine-specific raw output types
  • luxonis-eval parsers call the compute() function of parsers in depthai-nodes instead of hand-written computation logic
  • Relies on this depthai-nodes code
  • Added example configs for the main tasks (keypoints, detection, semantic segmentation, classification)

Set to draft: 1) depends on depthai-nodes PR and 2) I need to generate different models by training them with LuxonisTrain, converting them to ONNX NNArchive and RVC4 NNArchive (FP16) and making sure the results are coherent between luxonis_train test ... and luxonis_eval eval .... Although this PR is supposed to only be architectural shouldn't change anything functionally, this is just to make sure since the computation now relies on the depthai-nodes parsers' compute. Then we can use these .ckpt/.onnx.tar.xz/.rvc4.tar.xz files later on for automatic E2E/regression testing.

Specification

None / not applicable

Dependencies & Potential Impact

None / not applicable

Deployment Plan

None / not applicable

Testing & Validation

None / not applicable

AI Usage

Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]

Submitted code was reviewed by a human: YES/NO

The author is taking the responsibility for the contribution: YES/NO

Summary by CodeRabbit

  • New Features

    • Added example configurations for classification, object detection, pose estimation, and semantic segmentation evaluations.
    • Added a unified YOLO evaluation parser supporting detection, instance segmentation, and pose estimation.
    • Added support for selecting specific model outputs during evaluation.
  • Bug Fixes

    • Improved segmentation metric handling for binary and multiclass masks.
    • Enhanced model output validation and compatibility across inference backends.
  • Documentation

    • Updated evaluator examples and parser references to reflect the current configuration options.

@dtronmans
dtronmans marked this pull request as draft July 1, 2026 11:18
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b3c575d1-c5ab-4b28-b055-1f4b59aa06fb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/abstract-engines

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dtronmans dtronmans changed the title Feat/abstract engines EngineOutput to normalize engine outputs Jul 1, 2026
Comment thread luxonis_eval/engines/io.py Outdated
Comment thread luxonis_eval/parsers/detection.py Outdated
@dtronmans
dtronmans marked this pull request as ready for review July 15, 2026 09:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
luxonis_eval/config/resolver.py (1)

312-321: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Include missing keypoint and segmentation parameters during YOLO parser config construction.

YOLOExtendedParser supports mask_conf, keypoint_label_names, and keypoint_edges, but these keys are missing from this metadata extraction loop. If they are provided in an NNArchive for a pose or instance segmentation model, they will be ignored, causing the parser to fall back to defaults and potentially breaking the evaluation pipeline.

🐛 Proposed fix
     for key in (
         "subtype",
         "n_classes",
         "anchors",
         "conf_threshold",
         "iou_threshold",
         "max_det",
+        "mask_conf",
+        "keypoint_label_names",
+        "keypoint_edges",
     ):
         if key in metadata:
             params[key] = metadata[key]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@luxonis_eval/config/resolver.py` around lines 312 - 321, Extend the metadata
extraction loop in the YOLO parser configuration construction to include
mask_conf, keypoint_label_names, and keypoint_edges alongside the existing
parameters. Ensure provided values from NNArchive metadata are copied into
params so YOLOExtendedParser receives them instead of using defaults.
luxonis_eval/engines/depthai_engine.py (1)

79-99: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Wire nn_archive_cfg into NNArchive loading. The constructor stores it, but _load_nn_archive() still builds dai.NNArchive from model_path alone, so the option has no effect. Pass the config through the supported archive-loading path or remove the argument.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@luxonis_eval/engines/depthai_engine.py` around lines 79 - 99, Update
_load_nn_archive() to use the stored self.nn_archive_cfg when constructing or
loading dai.NNArchive, passing it through the supported archive-loading API
alongside model_path. If that API cannot accept the configuration, remove
nn_archive_cfg from __init__ and stop storing it rather than leaving an unused
option.
🧹 Nitpick comments (2)
luxonis_eval/parsers/segmentation.py (1)

34-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename class_map to parsed_mask to prevent confusion.

The output of DepthAINodesSegmentationParser.compute is assigned to a variable named class_map. Since class_map conventionally refers to a dictionary mapping class indices to names (as used throughout the rest of the codebase), using it here for the parsed segmentation mask is misleading.

♻️ Proposed refactor
-        class_map = DepthAINodesSegmentationParser.compute(
+        parsed_mask = DepthAINodesSegmentationParser.compute(
             np.asarray(segmentation_mask),
             classes_in_one_layer=classes_in_one_layer,
         )
 
-        return create_segmentation_message(class_map)
+        return create_segmentation_message(parsed_mask)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@luxonis_eval/parsers/segmentation.py` around lines 34 - 39, Rename the local
variable assigned from DepthAINodesSegmentationParser.compute in the
segmentation parsing flow from class_map to parsed_mask, and pass parsed_mask to
create_segmentation_message. Do not change the computation or message behavior.
luxonis_eval/metrics/dice_coef.py (1)

112-115: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove redundant astype(np.int64) conversions.

In both metrics, pred_mask and target_mask are already explicitly cast and returned as int64 arrays by normalize_prediction_segmentation_mask and target_segmentation_to_index_mask. Since NumPy's .astype() copies the array by default, calling .astype(np.int64) during the torch.from_numpy conversion creates unnecessary full copies of the segmentation masks on every batch iteration, needlessly increasing memory allocation and degrading evaluation performance.

  • luxonis_eval/metrics/dice_coef.py#L112-L115: Remove .astype(np.int64) from both tensor conversions (use torch.from_numpy(pred_mask)).
  • luxonis_eval/metrics/mIoU.py#L113-L116: Remove .astype(np.int64) from both tensor conversions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@luxonis_eval/metrics/dice_coef.py` around lines 112 - 115, Remove the
redundant astype(np.int64) calls from both torch.from_numpy conversions in
luxonis_eval/metrics/dice_coef.py lines 112-115 and luxonis_eval/metrics/mIoU.py
lines 113-116, passing pred_mask and target_mask directly. The existing
normalization functions already return int64 arrays, so preserve that dtype
without creating per-batch copies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@luxonis_eval/config/resolver.py`:
- Around line 285-286: Update the parser-name set in the resolver branch that
calls construct_yolo_parser_config to include the legacy names
YOLODetectionParser, YOLOInstanceSegmentationParser, and
YOLOKeypointDetectionParser, ensuring archives using these names follow the
existing YOLO migration path instead of reaching _map_archive_parser_name and
raising NotImplementedError.

In `@luxonis_eval/engines/onnx_engine.py`:
- Around line 23-29: The ONNXEngineOutput.get method currently discards the
layout hint, allowing YOLO tensors to be interpreted with the wrong channel
axis. Update get to either normalize outputs to the requested layout or
explicitly reject any layout other than NCHW, while preserving existing behavior
when no layout is provided.

In `@luxonis_eval/metrics/dice_coef.py`:
- Around line 103-110: The conditional masking around the Dice and MIoU
calculations incorrectly treats the background as ignored when
include_background is false, removing foreground false positives. In
luxonis_eval/metrics/dice_coef.py lines 103-110 and luxonis_eval/metrics/mIoU.py
lines 104-111, remove this masking when target_bg is the true background class;
if target_bg denotes a void class, decouple masking from include_background and
pass it through the underlying metric’s ignore_index handling where supported.

In `@luxonis_eval/metrics/metrics_utils.py`:
- Around line 126-127: The 2D mask branch currently always returns binary_target
as false, so binary index masks bypass required normalization. Update the mask
handling logic around the 2D mask conversion to determine binary_target using
the metric’s num_classes configuration or the mask’s value range, ensuring 2D
masks containing only 0 and 1 are identified as binary while preserving
multiclass behavior.

In `@luxonis_eval/utils/depthai_nodes.py`:
- Around line 167-180: The class-count calculation in the
final_anchors/inferred_n_classes logic uses the number of heads instead of
anchors per output. Preserve anchors as a nested tensor, derive the per-head
anchor count from that structure, and use it when computing inferred_n_classes;
leave the no-anchors path unchanged.

---

Outside diff comments:
In `@luxonis_eval/config/resolver.py`:
- Around line 312-321: Extend the metadata extraction loop in the YOLO parser
configuration construction to include mask_conf, keypoint_label_names, and
keypoint_edges alongside the existing parameters. Ensure provided values from
NNArchive metadata are copied into params so YOLOExtendedParser receives them
instead of using defaults.

In `@luxonis_eval/engines/depthai_engine.py`:
- Around line 79-99: Update _load_nn_archive() to use the stored
self.nn_archive_cfg when constructing or loading dai.NNArchive, passing it
through the supported archive-loading API alongside model_path. If that API
cannot accept the configuration, remove nn_archive_cfg from __init__ and stop
storing it rather than leaving an unused option.

---

Nitpick comments:
In `@luxonis_eval/metrics/dice_coef.py`:
- Around line 112-115: Remove the redundant astype(np.int64) calls from both
torch.from_numpy conversions in luxonis_eval/metrics/dice_coef.py lines 112-115
and luxonis_eval/metrics/mIoU.py lines 113-116, passing pred_mask and
target_mask directly. The existing normalization functions already return int64
arrays, so preserve that dtype without creating per-batch copies.

In `@luxonis_eval/parsers/segmentation.py`:
- Around line 34-39: Rename the local variable assigned from
DepthAINodesSegmentationParser.compute in the segmentation parsing flow from
class_map to parsed_mask, and pass parsed_mask to create_segmentation_message.
Do not change the computation or message behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b701c498-f70b-42a5-84bc-884a6c21cc8b

📥 Commits

Reviewing files that changed from the base of the PR and between 7b51429 and 8dc26ed.

📒 Files selected for processing (28)
  • README.md
  • configs/classification_config.yaml
  • configs/detection_config.yaml
  • configs/pose_config.yaml
  • configs/semantic_segmentation_config.yaml
  • configs/yolov8n_inst_seg_config.yaml
  • luxonis_eval/config/resolver.py
  • luxonis_eval/core/core.py
  • luxonis_eval/core/factories.py
  • luxonis_eval/core/runtime.py
  • luxonis_eval/engines/__init__.py
  • luxonis_eval/engines/base_engine.py
  • luxonis_eval/engines/depthai_engine.py
  • luxonis_eval/engines/io.py
  • luxonis_eval/engines/onnx_engine.py
  • luxonis_eval/metrics/dice_coef.py
  • luxonis_eval/metrics/mIoU.py
  • luxonis_eval/metrics/metrics_utils.py
  • luxonis_eval/parsers/__init__.py
  • luxonis_eval/parsers/base_parser.py
  • luxonis_eval/parsers/classification.py
  • luxonis_eval/parsers/detection.py
  • luxonis_eval/parsers/instance_seg.py
  • luxonis_eval/parsers/keypoint_detection.py
  • luxonis_eval/parsers/segmentation.py
  • luxonis_eval/parsers/semantic_seg.py
  • luxonis_eval/parsers/yolo.py
  • luxonis_eval/utils/depthai_nodes.py
💤 Files with no reviewable changes (4)
  • luxonis_eval/parsers/keypoint_detection.py
  • luxonis_eval/parsers/detection.py
  • luxonis_eval/parsers/instance_seg.py
  • luxonis_eval/parsers/semantic_seg.py

Comment thread luxonis_eval/config/resolver.py
Comment thread luxonis_eval/engines/onnx_engine.py Outdated
Comment thread luxonis_eval/metrics/dice_coef.py
Comment thread luxonis_eval/metrics/metrics_utils.py
Comment thread luxonis_eval/utils/depthai_nodes.py Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants