Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
a943ccd
Satisfy the current ruff pre-commit pins in the RVC4 analyzer
kozlov721 Aug 13, 2026
6d7caf0
Rename `target`/`packages` terminology to `platform`
kozlov721 Aug 13, 2026
6e1e827
Make implementation-detail class members private
kozlov721 Aug 13, 2026
065898f
Add `typos` pre-commit check and fix the typos it found
kozlov721 Aug 13, 2026
ea57b62
updated requirements
kozlov721 Aug 13, 2026
a9f7029
type fixes
kozlov721 Aug 14, 2026
2265af1
less verbose builds
kozlov721 Aug 14, 2026
153573d
fix ci
kozlov721 Aug 14, 2026
bb781bf
don't upload coverage on manual trigger
kozlov721 Aug 14, 2026
468d896
cleanup
kozlov721 Aug 14, 2026
b1a91c1
fix
kozlov721 Aug 14, 2026
6920d26
Merge branch 'main' into chore/pre-docs-cleanup
kozlov721 Aug 14, 2026
ce9de96
Merge branch 'main' into chore/pre-docs-cleanup
kozlov721 Aug 14, 2026
76fedbb
Restore the per-platform install extras
kozlov721 Aug 14, 2026
0178fac
Guard the channel-first transpose on the array rank
kozlov721 Aug 14, 2026
4fa29e5
Expose the exporter inputs and model name
kozlov721 Aug 14, 2026
29b51dd
Select the RVC3 tests when the RVC2 code changes
kozlov721 Aug 14, 2026
abbc05c
Run the pyright that the dev extra pins
kozlov721 Aug 14, 2026
8f84a12
Omit the new RVC4 utils module from coverage
kozlov721 Aug 14, 2026
908d6ee
Move the private members to their conventional places
kozlov721 Aug 14, 2026
a5847e6
Correct the stale docstrings and the log message
kozlov721 Aug 14, 2026
ebf90e5
Type the subprocess output as bytes
kozlov721 Aug 14, 2026
c5ba06d
Remove the last `Any`
kozlov721 Aug 14, 2026
a36e3b0
Rename the tests that still say "target"
kozlov721 Aug 14, 2026
fae869d
Correct the stale commands and paths in the README
kozlov721 Aug 14, 2026
0f02177
Rewrap the staged-token comment
kozlov721 Aug 14, 2026
2be9311
Replace the duplicated `input_model` guard with a helper
kozlov721 Aug 15, 2026
efec58d
Stop `human_size` writing a unit `parse_size` cannot read
kozlov721 Aug 15, 2026
0fa23d4
Exclude the stat fallbacks the cache sweep cannot reach
kozlov721 Aug 15, 2026
65823db
Cover the config validators and the coercion helpers
kozlov721 Aug 15, 2026
f08f475
Cover the size and the directory helpers
kozlov721 Aug 15, 2026
da4fb6c
Cover the output directory and the slug refusals
kozlov721 Aug 15, 2026
7c0071c
Cover the TFLite tensor errors
kozlov721 Aug 15, 2026
176f5ad
Cover what the exporter run leaves behind
kozlov721 Aug 15, 2026
031267f
Cover the inferer stand-ins
kozlov721 Aug 15, 2026
c8a2328
Cover the entrypoint test helpers
kozlov721 Aug 15, 2026
c3061e5
Cover the input staging edge cases
kozlov721 Aug 15, 2026
49f7ec0
Move the type gate and its coverage to a follow-up PR
kozlov721 Aug 16, 2026
d202ca1
Type the utils and the test helpers
kozlov721 Aug 16, 2026
2c0272e
Cover the size, staging and TFLite paths
kozlov721 Aug 16, 2026
c6966bd
Add the pyright type gate
kozlov721 Aug 16, 2026
e4775e7
fix typo;
kozlov721 Aug 16, 2026
c42445d
nitpicks
kozlov721 Aug 16, 2026
b648362
Type the utils and the test helpers
kozlov721 Aug 16, 2026
6ef93d0
Cover the size, staging and TFLite paths
kozlov721 Aug 16, 2026
080c001
Add the pyright type gate
kozlov721 Aug 16, 2026
f762ddf
Merge branch 'chore/type-gate-and-coverage' of github.com:luxonis/mod…
kozlov721 Aug 16, 2026
810231b
Name the real types behind the test object annotations
kozlov721 Aug 16, 2026
6a46320
Fix the crash on a Split-Concat that feeds nothing
kozlov721 Aug 16, 2026
4148dda
Report the DSP power collapse value as optional
kozlov721 Aug 16, 2026
3834769
Name the real type of the HTTP header getter
kozlov721 Aug 16, 2026
08613eb
Correct the RVC4 analyze and visualize collection types
kozlov721 Aug 16, 2026
44dd48a
Type the config literals that hold mixed values
kozlov721 Aug 16, 2026
04a7b00
Narrow the model name that the archive supplies
kozlov721 Aug 16, 2026
f943a2b
Fix the types in the evaluation tests
kozlov721 Aug 16, 2026
9c6e690
Name the real types in the progress handler
kozlov721 Aug 16, 2026
8f4e633
remove unused and unresolvable extras
kozlov721 Aug 16, 2026
0bffbad
Merge branch 'main' into chore/type-gate-and-coverage
kozlov721 Aug 31, 2026
0543c9d
Detect the release branch on every event
kozlov721 Aug 31, 2026
3b9268a
Pin the pyright that the type gate runs
kozlov721 Aug 31, 2026
b0e804a
Name the RVC4 analyzer dtype parameter
kozlov721 Aug 31, 2026
459e03a
Skip the FIFO test where mkfifo is absent
kozlov721 Aug 31, 2026
9445cf5
Remove the unreachable model name type guard
kozlov721 Aug 31, 2026
ed062a1
Read the download response headers directly
kozlov721 Aug 31, 2026
9fffbab
Take the first successor without a generator
kozlov721 Aug 31, 2026
af76db5
Match the file's boolean assertion style
kozlov721 Aug 31, 2026
9ca89a6
Derive the release check from the checkout ref
kozlov721 Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 65 additions & 10 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ on:
type: string
default: ""
ml_ref:
description: "luxonis-ml version (branch/tag/SHA)"
description: "luxonis-ml version (branch/tag/SHA). Empty keeps the pinned release. The unit and type-check jobs then use `main`, but not on a release branch."
required: false
type: string
default: main
default: ""
modelconv_ref:
description: "modelconverter version (branch/tag/SHA)"
description: "modelconverter version (branch/tag/SHA). Empty tests the branch the run starts from."
required: false
type: string
default: main
default: ""
workflow_call:
inputs:
platforms:
Expand All @@ -38,10 +38,10 @@ on:
type: string
default: ""
ml_ref:
description: "luxonis-ml version (branch/tag/SHA)"
description: "luxonis-ml version (branch/tag/SHA). Empty keeps the pinned release. The unit and type-check jobs then use `main`, but not on a release branch."
required: false
type: string
default: main
default: ""
modelconv_ref:
description: "modelconverter version (branch/tag/SHA)"
required: false
Expand Down Expand Up @@ -75,8 +75,63 @@ env:
LUXONIS_TELEMETRY_ENABLED: "false"

jobs:
typecheck:
name: Type check
runs-on: ubuntu-latest
env:
GS_BUILD_ARTIFACTS: gs://luxonis-test-bucket/modelconverter/build-artifacts
HAILO_WHEEL: hailo_dataflow_compiler-5.3.0-py3-none-linux_x86_64.whl
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
repository: ${{ inputs.modelconv_ref && 'Luxonis/modelconverter' || github.repository }}
ref: ${{ inputs.modelconv_ref || github.head_ref || github.ref }}
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: "3.10"
cache: pip

- name: Install package
run: pip install -e .[dev]

- name: Install LuxonisML from main
if: ${{ inputs.ml_ref || !startsWith(inputs.modelconv_ref || github.head_ref || github.ref_name, 'release/') }}
Comment thread
kozlov721 marked this conversation as resolved.
env:
ML_REF: ${{ inputs.ml_ref || 'main' }}
run: |
pip uninstall luxonis-ml -y
pip install \
"luxonis-ml[data,nn_archive,s3,gcs,telemetry] @ git+https://github.com/luxonis/luxonis-ml.git@${ML_REF}" \
--upgrade --force-reinstall
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
token_format: access_token

- name: Install platform dependencies
run: |
gcloud storage cp "${GS_BUILD_ARTIFACTS}/${HAILO_WHEEL}" .
pip install --no-deps \
-r requirements-typecheck.txt \
"./${HAILO_WHEEL}"

- name: Run pyright
uses: jakebailey/pyright-action@8ec14b5cfe41f26e5f41686a31eb6012758217ef
with:
# `version: PATH` runs the pyright that `.[dev]` pins, so CI and a
# local run agree.
version: PATH
warnings: true

unit:
name: Unit tests
needs: [typecheck]
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand All @@ -95,14 +150,14 @@ jobs:
- name: Install package
run: pip install -e .[dev]

- name: Install specified LuxonisML
if: ${{ inputs.ml_ref != '' }}
- name: Install LuxonisML from main
if: ${{ inputs.ml_ref || !startsWith(inputs.modelconv_ref || github.head_ref || github.ref_name, 'release/') }}
env:
ML_REF: ${{ inputs.ml_ref }}
ML_REF: ${{ inputs.ml_ref || 'main' }}
run: |
pip uninstall luxonis-ml -y
pip install \
"luxonis-ml[data,nn-archive] @ git+https://github.com/luxonis/luxonis-ml.git@${ML_REF}" \
"luxonis-ml[data,nn_archive,s3,gcs,telemetry] @ git+https://github.com/luxonis/luxonis-ml.git@${ML_REF}" \
--upgrade --force-reinstall

- name: Run unit tests
Expand Down
3 changes: 2 additions & 1 deletion modelconverter/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
get_inferer,
get_visualizer,
)
from modelconverter.platforms.base_benchmark import Configuration
from modelconverter.platforms.multistage_exporter import MultiStageExporter
from modelconverter.utils import (
ModelconverterException,
Expand Down Expand Up @@ -559,7 +560,7 @@ def benchmark(
Whether to monitor the device performance during benchmarking and include it in the results. Only relevant for RVC4 platform.
"""
if platform in {Platform.RVC2, Platform.RVC4}:
kwargs = {
kwargs: Configuration = {
"repetitions": repetitions,
"benchmark_time": benchmark_time,
"num_threads": num_threads,
Expand Down
4 changes: 2 additions & 2 deletions modelconverter/platforms/rvc2/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def export(self) -> Path:
if "-iop" not in args:
self._add_args(args, ["-ip", "U8"])

args += ["-m", xml_path]
args += ["-m", str(xml_path)]

if self._superblob:
return self._compile_superblob(args)
Expand Down Expand Up @@ -348,7 +348,7 @@ def _superblob_compile_step(shaves: int) -> None:
blobs_directory
/ f"{self._model_name}_{DEFAULT_SUPER_SHAVES}shave.blob"
)
args += ["-o", blob_path]
args += ["-o", str(blob_path)]

logger.info(f"Compiling {shaves}-shave patch...")
with SubprocessHandle(
Expand Down
34 changes: 15 additions & 19 deletions modelconverter/platforms/rvc3/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import cv2
from loguru import logger
from luxonis_ml.typing import Params

from modelconverter.platforms.base_exporter import Exporter
from modelconverter.platforms.rvc2.exporter import RVC2Exporter
Expand Down Expand Up @@ -65,17 +66,17 @@ def export(self) -> Path:
self.output_dir
/ f"{self._model_name}-{self.platform.name.lower()}-int8"
)
args += ["-m", calibrated_xml_path]
args += ["-m", str(calibrated_xml_path)]
else:
output_path = (
self.output_dir
/ f"{self._model_name}-{self.platform.name.lower()}"
)
args += ["-m", xml_path]
args += ["-m", str(xml_path)]

if "-o" not in args:
blob_output_path = output_path.with_suffix(".blob")
args += ["-o", blob_output_path]
args += ["-o", str(blob_output_path)]
else: # pragma: no cover
blob_output_path = Path(args[args.index("-o") + 1])

Expand Down Expand Up @@ -112,6 +113,16 @@ def _calibrate(self, xml_path: Path) -> Path:
str((calibration_img_dir / file.stem).with_suffix(suffix)), img
)

dataset: Params = {
"name": "calibration",
"data_source": str(calibration_img_dir),
"reader": "opencv_imread",
}
if inp.encoding.to == Encoding.GRAY:
dataset["preprocessing"] = [{"type": "bgr_to_gray"}]
elif not self._reverse_input_channels:
dataset["preprocessing"] = [{"type": "bgr_to_rgb"}]

config = {
"model": {
"model_name": f"{xml_path.stem}-int8",
Expand All @@ -125,13 +136,7 @@ def _calibrate(self, xml_path: Path) -> Path:
"device": "CPU",
}
],
"datasets": [
{
"name": "calibration",
"data_source": str(calibration_img_dir),
"reader": "opencv_imread",
}
],
"datasets": [dataset],
},
"compression": {
"target_device": self._pot_target_device.name,
Expand All @@ -147,15 +152,6 @@ def _calibrate(self, xml_path: Path) -> Path:
},
}

if inp.encoding.to == Encoding.GRAY:
config["engine"]["datasets"][0]["preprocessing"] = [
{"type": "bgr_to_gray"}
]
elif not self._reverse_input_channels:
config["engine"]["datasets"][0]["preprocessing"] = [
{"type": "bgr_to_rgb"}
]

pot_config_path = self.intermediate_outputs_dir / "pot_config.json"

with open(pot_config_path, "w") as f:
Expand Down
18 changes: 9 additions & 9 deletions modelconverter/platforms/rvc4/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def _check_dir_sizes(self) -> None:
"Input dirs must contain at least one supported image or .npy file."
)

def _prepare_input_matcher(self) -> dict[str, dict[str, str]]:
def _prepare_input_matcher(self) -> dict[int, dict[str, str]]:
image_names = self._get_selected_image_paths()

input_matcher = {}
Expand All @@ -198,13 +198,13 @@ def _prepare_input_matcher(self) -> dict[str, dict[str, str]]:

def _prepare_raw_inputs(
self,
input_matcher: dict[str, dict[str, str]],
type: type = np.uint8,
input_matcher: dict[int, dict[str, str]],
dtype: type[np.generic] = np.uint8,
*,
verbose: bool = True,
reset_workspace: bool = True,
stable_input_names: bool = False,
) -> dict[str, str]:
) -> dict[int, str]:
if verbose:
logger.info("Preparing raw inputs for RVC4 analysis.")
if reset_workspace:
Expand All @@ -231,15 +231,15 @@ def _prepare_raw_inputs(
raise TypeError(
f"Input `{img_name}` is not a single array."
)
raw_image = loaded.astype(type)
raw_image = loaded.astype(dtype)

if raw_image.shape != tuple(width_height):
raise ValueError(
f"Input image {img_name} has incorrect shape: {raw_image.shape}, expected: {tuple(width_height)}"
)
else:
image = self._resize_image(img_path, width_height)
raw_image = image.astype(type)
raw_image = image.astype(dtype)

raw_file_name = (
f"{input_name}.raw"
Expand Down Expand Up @@ -395,8 +395,8 @@ def _collect_comparison_statistics(
output_names: list[str],
layer_names: list[str],
onnx_input_shapes: dict[str, list[int | str | None]],
input_matcher: dict[str, dict[str, str]],
dlc_matcher: dict[str, Path],
input_matcher: dict[int, dict[str, str]],
dlc_matcher: dict[int, Path],
*,
verbose: bool = True,
) -> list[list]:
Expand Down Expand Up @@ -570,7 +570,7 @@ def _run_dlc(
return str(pulled_output_dir)

def _flatten_dlc_outputs(
self, dlc_matcher: dict[str, Path], *, verbose: bool = True
self, dlc_matcher: dict[int, Path], *, verbose: bool = True
) -> None:
if verbose:
logger.info("Flattening SNPE results.")
Expand Down
2 changes: 1 addition & 1 deletion modelconverter/platforms/rvc4/visualize.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def _visualize_layer_outputs(self) -> go.Figure:

def _get_csv_paths(
self, dir_path: Path, comparison_type: str = "layer_comparison"
) -> dict[str, str]:
) -> dict[str, Path]:
dir_path = dir_path or constants.OUTPUTS_DIR / "analysis"
csv_paths = {}

Expand Down
2 changes: 1 addition & 1 deletion modelconverter/utils/device_monitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def _read_cpu(self) -> dict[str, float | None]:
"cpu_utilization": self._read_cpu_utilization(),
}

def _read_dsp(self) -> dict[str, float]:
def _read_dsp(self) -> dict[str, float | None]:

def parse_freq_file(
text: str,
Expand Down
Loading
Loading