OpenVINO IR model export - #1238
Conversation
- Resolved conflicts in src/rfdetr/detr.py - Combined OpenVINO and ExecuTorch export features - Updated format documentation to include both openvino and executorch - Updated return type to include .xml (OpenVINO) and .pte (ExecuTorch) - Both formats now use direct conversion (no ONNX intermediate) - Merged upstream changes: ExecuTorch export, dataset improvements, CI updates
- Added [openvino] optional dependency to pyproject.toml - Updated all error messages to use 'pip install "rfdetr[openvino]"' - Updated OpenVINO export documentation with new installation instructions - Consistent with other optional dependencies (onnx, tflite, executorch, tensorrt)
- Update OpenVINO IR description to mention broader hardware support - Mention CPU (x86, ARM), GPU (Intel integrated & discrete), and AI accelerators (Intel NPU) - Make documentation more neutral and less Intel-specific - Simplify installation instructions - Remove redundant advantages section
|
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #1238 +/- ##
========================================
- Coverage 87% 87% -0%
========================================
Files 114 117 +3
Lines 15128 15251 +123
========================================
+ Hits 13101 13205 +104
- Misses 2027 2046 +19 🚀 New features to boost your workflow:
|
@avbelova could you ls check ^^ 🦝 |
There was a problem hiding this comment.
Pull request overview
Adds a new OpenVINO IR export path to RF-DETR, enabling direct PyTorch → OpenVINO conversion and documenting how to use the exported artifacts.
Changes:
- Added an OpenVINO exporter and a small inference wrapper under
src/rfdetr/export/_openvino/. - Integrated
format="openvino"intoRFDETR.export()and added anopenvinooptional dependency extra. - Updated export documentation to include OpenVINO IR export usage and examples.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/rfdetr/export/_openvino/README.md | New OpenVINO export usage guide and inference examples. |
| src/rfdetr/export/_openvino/inference.py | Adds a lightweight OpenVINO IR inference wrapper. |
| src/rfdetr/export/_openvino/exporter.py | Implements direct PyTorch → OpenVINO IR conversion and saving. |
| src/rfdetr/export/_openvino/init.py | Declares the OpenVINO export utilities package. |
| src/rfdetr/detr.py | Extends RFDETR.export() to support format="openvino". |
| pyproject.toml | Adds openvino optional dependency extra. |
| docs/learn/export.md | Documents OpenVINO IR export workflow and examples. |
Comments suppressed due to low confidence (2)
src/rfdetr/export/_openvino/README.md:88
- The inference example loads
output/inference_model.xml, but forRFDETRMediumthe exporter will writeoutput/rfdetr-medium.xmlby default (becausevariant_name=self.size). This example should use the correct filename to avoid a copy/paste FileNotFoundError.
# Load the exported model
model = OpenVINOInference("output/inference_model.xml")
src/rfdetr/export/_openvino/README.md:113
- The
benchmark_appexample usesoutput/inference_model.xml, but the default filename forRFDETRMediumexport isoutput/rfdetr-medium.xml(becausevariant_name=self.size). Update the command so it works as written.
```bash
benchmark_app -m output/inference_model.xml -data_shape [1,3,576,576]
</details>
- Add path traversal sanitization for variant_name parameter - Call model.export() before OpenVINO conversion to ensure proper export mode - Handle tuple outputs explicitly in ModelWrapper to prevent incorrect nesting - Use AUTO device selection instead of hardcoded CPU for better performance - Update documentation to reflect actual output filenames (rfdetr-medium.xml)
@Borda the corporate CLA is signed between Roboflow and Intel |
Perfect! 🎉 |
OpenVINO package does not provide type stubs, causing mypy type check to fail. Add it to the list of external packages with ignore_missing_imports.
# Conflicts: # src/rfdetr/detr.py
… dispatch facade [resolve group] PR roboflow#1238 — items 9, 11, 12, 13, 14, 15, 18, 26, 29, 30, 31, 32 Adds "openvino" to _EXPORT_FORMATS (the feature was previously 100% unreachable), threads export_model/backbone_only through the new _export_openvino_format dispatch helper in _backend.py, rewrites ModelWrapper to raise on missing keys instead of silently dropping outputs, adds output_name/dynamic_batch/notes parity with sibling formats, narrows the mypy-failing model.export() call with a Protocol+cast, fixes the docformatter-corrupted export() docstring, adds openvino_precision control over save_model, reverts an unrelated ONNX import-guard restructure, and brings _openvino/__init__.py to sibling parity. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…rop dead handle [resolve group] PR roboflow#1238 — items 19, 20, 21 Copies infer-request output tensors before returning instead of handing back views onto reused OpenVINO-managed memory (was silently corrupting any caller accumulating results across frames), adds device/cache_dir constructor parameters so kernel compilation caches across process starts, and drops the uncompiled read_model handle that kept a duplicate copy of the weights resident for no reason. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
[resolve group] PR roboflow#1238 — items 16, 22 Deletes the in-package _openvino/README.md (unprecedented pattern, never ships, already drifted from docs/learn/export.md) and ports its unique accurate content into the docs page instead. Fixes the OpenVINO inference example's mean/std arrays defaulting to float64 (silently doubling per-frame conversion cost and undoing an earlier .astype(np.float32)) and adds the missing np.ascontiguousarray call before inference. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
[resolve group] PR roboflow#1238 — item 7 Zero tests existed for the openvino export backend despite every sibling format (onnx/tflite/tensorrt/executorch/coreml) having a dedicated tests/export/test_*.py. Adds registry/dispatch/dependency-missing coverage that needs no openvino install, plus round-trip parity and inference tests gated behind pytest.importorskip("openvino"). Registers the e2e_openvino pytest marker alongside the existing e2e_executorch/e2e_coreml markers. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
The item-30 ModelWrapper rewrite (module-scope, tuple/list passthrough, raises instead of silently mapping) and item-29 output_name/precision changes landed in a separate worktree after this test file was written against the old dict-mapping/output_names contract. Rewrites TestExportOpenvinoModelWrapper to TestModelWrapper against the new module-scope class (tuple passthrough, list-to-tuple coercion, dict raises NotImplementedError, unsupported type raises TypeError); replaces the stale output_names-forwarding assertions with output_name/ dynamic_batch/notes coverage matching what detr.py actually forwards; fixes the save_model assertion to expect compress_to_fp16; and updates the missing-dependency test to assert on the exception message rather than a logger call the new _check_openvino_available() no longer makes. Caught by manually re-running the suite during the Step 9 QA gate after Phase 3 merge-back — the parallel worktree split meant items roboflow#7 and roboflow#30 had no visibility into each other's changes. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…s_from_dataset() [resolve No.9] Review by copilot-pull-request-reviewer (PR roboflow#1238): "commit 18229b9's docformatter reflow corrupted the whole export() doc..." Challenge: evidence=VALID suggestion=VALID resolution=as-suggested (residual hunk missed by the prior arch-worktree commit) --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…enames [resolve No.18] Review by foundry:sw-engineer (PR roboflow#1238): "save_model(ov_model, output_xml) passes no precision argument, so stored..." Challenge: evidence=VALID suggestion=VALID resolution=as-suggested (doc-only gap: docstring already had it, export.md did not) --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…openvino dep [resolve No.25] Review by foundry:doc-scribe (PR roboflow#1238): "The OpenVINOInference docstring's Example: block carries `# doctest: +SK..." Challenge: evidence=VALID suggestion=VALID resolution=as-suggested (also applied to the analogous _backend.py block from item roboflow#30) [resolve No.28] Review by foundry:linting-expert (PR roboflow#1238): "openvino>=2024.0 has no upper bound. Every sibling caps deliberately..." Challenge: evidence=VALID suggestion=partial (capped at <2027.0, next calendar major; no python_version marker added -- openvino's own requires_python is >=3.10 with no upper bound, so a marker isn't justified) --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…into openvino_export # Conflicts: # src/rfdetr/detr.py
- [resolve No.H-1] /review finding by foundry:sw-engineer (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): guard OpenVINOInference.infer() with a per-instance lock; shared infer_request is not thread-safe per OpenVINO upstream (openvinotoolkit/openvino#2349, #24509) - [resolve No.H-2] /review finding by foundry:sw-engineer (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): hoist the openvino+dynamic_batch rejection into detr.py's existing fail-fast block, matching the executorch/coreml precedent; add a test asserting make_infer_image is not called - [resolve No.M-8] /review finding by foundry:sw-engineer (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): validate dtype/contiguity at the infer() boundary instead of silently doubling buffer size on a float64/non-contiguous input --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
[resolve No.H-3] /review finding by oss:cicd-steward (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): the e2e_openvino pytest marker was registered but wired to no CI job, so openvino.convert_model has never run against a real RF-DETR graph in any automated run. Add an openvino-parity job mirroring coreml-parity/executorch-parity (portable wheel, runs on ubuntu-latest); add it to export-priority-guardian's required jobs; add the currently-missing `and not e2e_openvino` exclusion to ci-tests-cpu.yml's default filter (latent until an env installs openvino, becomes live the moment one does). --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- [resolve No.H-4] /review finding by foundry:sw-engineer (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): add a passthrough tier (ImportError/NotImplementedError/TypeError/ValueError) before export_openvino's broad `except Exception`, mirroring export_coreml's precedent, so ModelWrapper's documented NotImplementedError/TypeError reach the caller instead of arriving relabeled as RuntimeError - [resolve No.H-5] /review finding by foundry:qa-specialist (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): extract the shared `_check_openvino_available()` choke point into inference.py's __init__ (previously an inline try/except with its own message) so both export_openvino and OpenVINOInference share one patchable check; rewrite test_raises_import_error_before_file_check to monkeypatch it instead of relying on openvino actually being absent from the environment - test-coverage follow-up for M-8 (previous commit 1ad6f4c): add TestOpenVINOInferenceInputValidation covering infer()'s float64 and non-contiguous rejection paths --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
… outputs - [resolve No.M-4] /review finding by foundry:qa-specialist (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): precision, device, and cache_dir were only ever exercised at their defaults. Add TestExportOpenvinoPrecision (float32/float16 compress_to_fp16 forwarding, invalid-precision ValueError) and TestOpenVINOInferenceDeviceAndCache (device forwarded to compile_model, CACHE_DIR set before compile, cache_dir=None skips set_property) — all stubbed, no real openvino needed - [resolve No.M-5] /review finding by foundry:qa-specialist, foundry:challenger (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): ModelWrapper's 3-tuple (masks/keypoints) path was docstring-named but untested, and the gated e2e suite covered detection + backbone-only only. Add test_three_tuple_output_passes_through_unchanged (unit) plus openvino_segmentation_export/openvino_keypoint_export fixtures and their e2e parity tests (gated behind -m e2e_openvino, now wired into CI by the previous commit) --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
… signature - [resolve No.M-1] /review finding by foundry:doc-scribe (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): the OpenVINO inference doc example resized with PIL's default (bicubic) resampling, diverging from predict()'s explicit antialias=False bilinear resize. Rewrite the example to use torchvision.transforms.functional (to_tensor/resize/normalize), matching every sibling exporter's doc example - [resolve No.M-2] /review finding by foundry:doc-scribe: "OpenVINO Model Outputs" documented boxes as "(x, y, w, h)" when the actual convention is normalized cxcywh (center_x, center_y, width, height) — fix the doc table and annotate the inference example's unpacking - [resolve No.M-3] /review finding by foundry:doc-scribe: RFDETR.export()'s Raises: block was missing the ValueError path for an invalid coreml_precision/openvino_precision value - [resolve No.M-6] /review finding by foundry:sw-engineer: export_openvino's signature diverged from every sibling converter (reversed output_dir-first arg order, no keyword-only marker, output_dir: str narrower than str | os.PathLike[str]). Realign to (model, input_tensors, output_dir, *, ...) matching export_coreml/export_executorch; update the one _backend.py call site (already all-kwargs, no behavior change) and all positional test call sites - [resolve No.M-7] /review finding by foundry:sw-engineer: the export-mode switch (hasattr/getattr + callable + .export()) was duplicated three times across _backend.py in two different idioms. Extract a single _switch_to_export_mode() helper (built on the existing _ExportableModule Protocol) and use it in all three dispatch functions - [resolve No.M-10] /review finding by codex (report: .reports/review/2026-09-07T19-00-24Z/review-report.md): "tight numeric parity" overclaimed for openvino_precision="float32" — it controls IR *storage* precision only; execution precision still depends on the compiled device. Soften the claim in detr.py, exporter.py, and docs/learn/export.md Confirmed no regression: pre-commit run --all-files clean; tests/export/test_openvino_export.py 27 passed (6 e2e skipped, no openvino installed locally — wired into CI by a prior commit); ruff/mypy clean on all touched files. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Positional max-abs-diff over all ~300 raw two-stage query-selection outputs was comparing mostly low-confidence background candidates whose near-tied objectness scores flip order across backends -- not evidence of a broken export. Detection/segmentation/keypoint fixtures now use a real photo (matching the CoreML suite's own pattern) and compare only the top-10 highest-confidence queries; masks compare in sigmoid space since mask logits span a much wider range than boxes/labels. All parity assertions now pin OpenVINO's execution precision to float32 via raw ov.Core (OpenVINO's ARM CPU plugin otherwise executes in fp16 regardless of IR storage precision -- measured 0.11 vs 0.0059 on the backbone). The backbone-only test keeps its structured input (no topk involved) with a loosened, documented tolerance covering a real, currently unexplained ~60x OpenVINO-vs-CoreML backbone precision gap that is tracked separately from this fix. --- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
What does this PR do?
This PR adds RF-DETR model export to OpenVINO IR (Intermediate Representation).
Type of Change
Testing
Checklist
ref: #1024