modelconv 0.5.2__tar.gz → 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {modelconv-0.5.2 → modelconv-0.5.3}/PKG-INFO +6 -4
  2. {modelconv-0.5.2 → modelconv-0.5.3}/modelconv.egg-info/PKG-INFO +6 -4
  3. {modelconv-0.5.2 → modelconv-0.5.3}/modelconv.egg-info/SOURCES.txt +6 -1
  4. {modelconv-0.5.2 → modelconv-0.5.3}/modelconv.egg-info/requires.txt +5 -3
  5. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/__init__.py +1 -1
  6. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/__main__.py +19 -5
  7. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/cli/utils.py +4 -2
  8. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/base_exporter.py +16 -11
  9. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/hailo/requirements.txt +1 -0
  10. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc3/exporter.py +4 -1
  11. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc4/exporter.py +31 -17
  12. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc4/requirements.txt +1 -0
  13. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/__init__.py +4 -0
  14. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/calibration_data.py +25 -2
  15. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/config.py +90 -38
  16. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/docker_utils.py +44 -1
  17. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/nn_archive.py +10 -1
  18. modelconv-0.5.3/modelconverter/utils/onnx_compatibility.py +64 -0
  19. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/onnx_tools.py +24 -20
  20. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/types.py +8 -0
  21. {modelconv-0.5.2 → modelconv-0.5.3}/requirements.txt +3 -3
  22. modelconv-0.5.3/tests/test_benchmark/conftest.py +36 -0
  23. modelconv-0.5.3/tests/test_benchmark/test_benchmark_regression.py +74 -0
  24. modelconv-0.5.3/tests/test_utils/__init__.py +0 -0
  25. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_utils/test_config.py +5 -2
  26. modelconv-0.5.3/tests/test_utils/test_onnx_compatibility.py +112 -0
  27. {modelconv-0.5.2 → modelconv-0.5.3}/LICENSE +0 -0
  28. {modelconv-0.5.2 → modelconv-0.5.3}/README.md +0 -0
  29. {modelconv-0.5.2 → modelconv-0.5.3}/modelconv.egg-info/dependency_links.txt +0 -0
  30. {modelconv-0.5.2 → modelconv-0.5.3}/modelconv.egg-info/entry_points.txt +0 -0
  31. {modelconv-0.5.2 → modelconv-0.5.3}/modelconv.egg-info/top_level.txt +0 -0
  32. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/cli/__init__.py +0 -0
  33. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/__init__.py +0 -0
  34. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/base_analyze.py +0 -0
  35. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/base_benchmark.py +0 -0
  36. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/base_inferer.py +0 -0
  37. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/base_visualize.py +0 -0
  38. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/getters.py +0 -0
  39. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/hailo/__init__.py +0 -0
  40. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/hailo/exporter.py +0 -0
  41. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/hailo/inferer.py +0 -0
  42. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/multistage_exporter.py +0 -0
  43. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc2/__init__.py +0 -0
  44. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc2/benchmark.py +0 -0
  45. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc2/exporter.py +0 -0
  46. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc2/inferer.py +0 -0
  47. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc2/requirements.txt +0 -0
  48. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc3/__init__.py +0 -0
  49. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc3/benchmark.py +0 -0
  50. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc3/inferer.py +0 -0
  51. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc3/requirements.txt +0 -0
  52. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc4/__init__.py +0 -0
  53. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc4/analyze.py +0 -0
  54. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc4/benchmark.py +0 -0
  55. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc4/inferer.py +0 -0
  56. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/packages/rvc4/visualize.py +0 -0
  57. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/adb_handler.py +0 -0
  58. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/adb_monitor.py +0 -0
  59. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/constants.py +0 -0
  60. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/environ.py +0 -0
  61. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/exceptions.py +0 -0
  62. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/filesystem_utils.py +0 -0
  63. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/general.py +0 -0
  64. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/hub_requests.py +0 -0
  65. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/hubai_utils.py +0 -0
  66. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/image.py +0 -0
  67. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/layout.py +0 -0
  68. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/metadata.py +0 -0
  69. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/progress_handler.py +0 -0
  70. {modelconv-0.5.2 → modelconv-0.5.3}/modelconverter/utils/subprocess.py +0 -0
  71. {modelconv-0.5.2 → modelconv-0.5.3}/pyproject.toml +0 -0
  72. {modelconv-0.5.2 → modelconv-0.5.3}/requirements-analysis.txt +0 -0
  73. {modelconv-0.5.2 → modelconv-0.5.3}/requirements-bench.txt +0 -0
  74. {modelconv-0.5.2 → modelconv-0.5.3}/requirements-dev.txt +0 -0
  75. {modelconv-0.5.2 → modelconv-0.5.3}/setup.cfg +0 -0
  76. {modelconv-0.5.2 → modelconv-0.5.3}/tests/__init__.py +0 -0
  77. {modelconv-0.5.2 → modelconv-0.5.3}/tests/conftest.py +0 -0
  78. {modelconv-0.5.2/tests/test_packages → modelconv-0.5.3/tests/test_benchmark}/__init__.py +0 -0
  79. {modelconv-0.5.2/tests/test_utils → modelconv-0.5.3/tests/test_packages}/__init__.py +0 -0
  80. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/common.py +0 -0
  81. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/metrics/__init__.py +0 -0
  82. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/metrics/base_metric.py +0 -0
  83. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/metrics/mnist_metric.py +0 -0
  84. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/metrics/resnet_metric.py +0 -0
  85. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/metrics/yolov6_metric.py +0 -0
  86. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/onnx_inferer.py +0 -0
  87. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/test_cross_format_export.py +0 -0
  88. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/test_hailo.py +0 -0
  89. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/test_rvc2.py +0 -0
  90. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/test_rvc3.py +0 -0
  91. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_packages/test_rvc4.py +0 -0
  92. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_utils/conftest.py +0 -0
  93. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_utils/test_filesystem.py +0 -0
  94. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_utils/test_image.py +0 -0
  95. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_utils/test_layout.py +0 -0
  96. {modelconv-0.5.2 → modelconv-0.5.3}/tests/test_utils/test_modifier.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modelconv
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: Converter for neural models into various formats.
5
5
  Author-email: Luxonis <support@luxonis.com>
6
6
  Maintainer-email: Luxonis <support@luxonis.com>
@@ -17,9 +17,9 @@ Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: Pillow
20
- Requires-Dist: luxonis-ml[data,gcs,nn_archive,s3]>=0.8.2
21
- Requires-Dist: cyclopts
22
- Requires-Dist: onnx<1.19.0,>=1.17.0
20
+ Requires-Dist: luxonis-ml[data,gcs,nn_archive,s3]>=0.8.4
21
+ Requires-Dist: cyclopts==4.8.0
22
+ Requires-Dist: onnx==1.21.0
23
23
  Requires-Dist: onnxruntime
24
24
  Requires-Dist: onnxsim
25
25
  Requires-Dist: docker
@@ -49,12 +49,14 @@ Requires-Dist: psutil; extra == "rvc4"
49
49
  Requires-Dist: numpy<2; extra == "rvc4"
50
50
  Requires-Dist: polars; extra == "rvc4"
51
51
  Requires-Dist: pytest; extra == "rvc4"
52
+ Requires-Dist: onnx==1.18.0; extra == "rvc4"
52
53
  Provides-Extra: hailo
53
54
  Requires-Dist: nvidia-dali-cuda120==1.49.0; extra == "hailo"
54
55
  Requires-Dist: nvidia-dali-tf-plugin-cuda120==1.49.0; extra == "hailo"
55
56
  Requires-Dist: protobuf==3.20.3; extra == "hailo"
56
57
  Requires-Dist: matplotlib==3.10.6; extra == "hailo"
57
58
  Requires-Dist: pyparsing==2.4.7; extra == "hailo"
59
+ Requires-Dist: onnx==1.17.0; extra == "hailo"
58
60
  Dynamic: license-file
59
61
 
60
62
  # ModelConverter - Compilation Library
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modelconv
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: Converter for neural models into various formats.
5
5
  Author-email: Luxonis <support@luxonis.com>
6
6
  Maintainer-email: Luxonis <support@luxonis.com>
@@ -17,9 +17,9 @@ Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: Pillow
20
- Requires-Dist: luxonis-ml[data,gcs,nn_archive,s3]>=0.8.2
21
- Requires-Dist: cyclopts
22
- Requires-Dist: onnx<1.19.0,>=1.17.0
20
+ Requires-Dist: luxonis-ml[data,gcs,nn_archive,s3]>=0.8.4
21
+ Requires-Dist: cyclopts==4.8.0
22
+ Requires-Dist: onnx==1.21.0
23
23
  Requires-Dist: onnxruntime
24
24
  Requires-Dist: onnxsim
25
25
  Requires-Dist: docker
@@ -49,12 +49,14 @@ Requires-Dist: psutil; extra == "rvc4"
49
49
  Requires-Dist: numpy<2; extra == "rvc4"
50
50
  Requires-Dist: polars; extra == "rvc4"
51
51
  Requires-Dist: pytest; extra == "rvc4"
52
+ Requires-Dist: onnx==1.18.0; extra == "rvc4"
52
53
  Provides-Extra: hailo
53
54
  Requires-Dist: nvidia-dali-cuda120==1.49.0; extra == "hailo"
54
55
  Requires-Dist: nvidia-dali-tf-plugin-cuda120==1.49.0; extra == "hailo"
55
56
  Requires-Dist: protobuf==3.20.3; extra == "hailo"
56
57
  Requires-Dist: matplotlib==3.10.6; extra == "hailo"
57
58
  Requires-Dist: pyparsing==2.4.7; extra == "hailo"
59
+ Requires-Dist: onnx==1.17.0; extra == "hailo"
58
60
  Dynamic: license-file
59
61
 
60
62
  # ModelConverter - Compilation Library
@@ -61,12 +61,16 @@ modelconverter/utils/image.py
61
61
  modelconverter/utils/layout.py
62
62
  modelconverter/utils/metadata.py
63
63
  modelconverter/utils/nn_archive.py
64
+ modelconverter/utils/onnx_compatibility.py
64
65
  modelconverter/utils/onnx_tools.py
65
66
  modelconverter/utils/progress_handler.py
66
67
  modelconverter/utils/subprocess.py
67
68
  modelconverter/utils/types.py
68
69
  tests/__init__.py
69
70
  tests/conftest.py
71
+ tests/test_benchmark/__init__.py
72
+ tests/test_benchmark/conftest.py
73
+ tests/test_benchmark/test_benchmark_regression.py
70
74
  tests/test_packages/__init__.py
71
75
  tests/test_packages/common.py
72
76
  tests/test_packages/onnx_inferer.py
@@ -86,4 +90,5 @@ tests/test_utils/test_config.py
86
90
  tests/test_utils/test_filesystem.py
87
91
  tests/test_utils/test_image.py
88
92
  tests/test_utils/test_layout.py
89
- tests/test_utils/test_modifier.py
93
+ tests/test_utils/test_modifier.py
94
+ tests/test_utils/test_onnx_compatibility.py
@@ -1,7 +1,7 @@
1
1
  Pillow
2
- luxonis-ml[data,gcs,nn_archive,s3]>=0.8.2
3
- cyclopts
4
- onnx<1.19.0,>=1.17.0
2
+ luxonis-ml[data,gcs,nn_archive,s3]>=0.8.4
3
+ cyclopts==4.8.0
4
+ onnx==1.21.0
5
5
  onnxruntime
6
6
  onnxsim
7
7
  docker
@@ -29,6 +29,7 @@ nvidia-dali-tf-plugin-cuda120==1.49.0
29
29
  protobuf==3.20.3
30
30
  matplotlib==3.10.6
31
31
  pyparsing==2.4.7
32
+ onnx==1.17.0
32
33
 
33
34
  [rvc2]
34
35
  tflite2onnx
@@ -44,3 +45,4 @@ psutil
44
45
  numpy<2
45
46
  polars
46
47
  pytest
48
+ onnx==1.18.0
@@ -4,7 +4,7 @@ from typing import Final
4
4
  from luxonis_ml.utils import PUT_FILE_REGISTRY
5
5
  from pydantic_extra_types.semantic_version import SemanticVersion
6
6
 
7
- __version__: Final[str] = "0.5.2"
7
+ __version__: Final[str] = "0.5.3"
8
8
  __semver__: Final[SemanticVersion] = SemanticVersion.parse(__version__)
9
9
 
10
10
 
@@ -33,6 +33,8 @@ from modelconverter.utils import (
33
33
  archive_from_model,
34
34
  docker_build,
35
35
  docker_exec,
36
+ get_default_target_version,
37
+ get_local_docker_image,
36
38
  in_docker,
37
39
  resolve_path,
38
40
  upload_to_remote,
@@ -127,7 +129,7 @@ def convert(
127
129
 
128
130
  with catch_exceptions():
129
131
  init_dirs()
130
- cfg, archive_cfg, _main_stage = get_configs(path, opts)
132
+ cfg, archive_cfg, _main_stage = get_configs(target, path, opts)
131
133
  main_stage = main_stage or _main_stage
132
134
  is_multistage = len(cfg.stages) > 1
133
135
  if is_multistage and main_stage is None:
@@ -264,7 +266,7 @@ def infer(
264
266
  if path is not None:
265
267
  config = path
266
268
  with catch_exceptions():
267
- mult_cfg, _, _ = get_configs(str(config), opts)
269
+ mult_cfg, _, _ = get_configs(target, str(config), opts)
268
270
  cfg = mult_cfg.get_stage_config(stage)
269
271
  setup_logging(
270
272
  file="modelconverter.log", use_rich=mult_cfg.rich_logging
@@ -630,9 +632,21 @@ def launcher(
630
632
  target = bound.arguments["target"]
631
633
 
632
634
  if dev:
633
- docker_build(
634
- target.value, bare_tag=tag, version=tool_version, image=image
635
- )
635
+ version = tool_version or get_default_target_version(target.value)
636
+ # CI invokes multiple dev docker commands per job; reuse the first
637
+ # local build so later commands don't rebuild the same image again.
638
+ if not (
639
+ os.getenv("CI") == "true"
640
+ and get_local_docker_image(
641
+ target.value,
642
+ bare_tag=tag,
643
+ version=version,
644
+ image=image,
645
+ )
646
+ ):
647
+ docker_build(
648
+ target.value, bare_tag=tag, version=version, image=image
649
+ )
636
650
 
637
651
  docker_exec(
638
652
  target.value,
@@ -72,7 +72,9 @@ def init_dirs() -> None:
72
72
 
73
73
 
74
74
  def get_configs(
75
- path: str | None, opts: list[str] | dict[str, Any] | None = None
75
+ target: Target,
76
+ path: str | None,
77
+ opts: list[str] | dict[str, Any] | None = None,
76
78
  ) -> tuple[Config, NNArchiveConfig | None, str | None]:
77
79
  """Sets up the configuration.
78
80
 
@@ -99,7 +101,7 @@ def get_configs(
99
101
  if path is not None:
100
102
  path_ = resolve_path(path, MISC_DIR)
101
103
  if path_.is_dir() or is_nn_archive(path_):
102
- return process_nn_archive(path_, overrides)
104
+ return process_nn_archive(target, path_, overrides)
103
105
  cfg = Config.get_config(path, overrides)
104
106
 
105
107
  main_stage_key = None
@@ -6,7 +6,6 @@ from pathlib import Path
6
6
  from typing import Any
7
7
 
8
8
  import numpy as np
9
- import onnx
10
9
  from loguru import logger
11
10
 
12
11
  from modelconverter.utils import (
@@ -20,6 +19,7 @@ from modelconverter.utils.config import (
20
19
  RandomCalibrationConfig,
21
20
  SingleStageConfig,
22
21
  )
22
+ from modelconverter.utils.onnx_compatibility import save_onnx_model
23
23
  from modelconverter.utils.subprocess import SubprocessResult
24
24
  from modelconverter.utils.types import InputFileType, Target
25
25
 
@@ -129,7 +129,13 @@ class Exporter(ABC):
129
129
  )
130
130
  return self.input_model
131
131
 
132
- onnx_sim, check = simplify(str(self.input_model))
132
+ try:
133
+ onnx_sim, check = simplify(str(self.input_model))
134
+ except Exception as e:
135
+ logger.warning(
136
+ f"Failed to simplify ONNX: {e}. Proceeding without simplification."
137
+ )
138
+ return self.input_model
133
139
  if not check:
134
140
  logger.warning(
135
141
  "Provided ONNX could not be simplified. "
@@ -141,15 +147,14 @@ class Exporter(ABC):
141
147
  self.input_model, "simplified.onnx"
142
148
  )
143
149
  logger.info(f"Saving simplified ONNX to {onnx_sim_path}")
144
- if self.input_model.with_suffix(".onnx_data").exists():
145
- onnx.save(
146
- onnx_sim,
147
- str(onnx_sim_path),
148
- save_as_external_data=True,
149
- location=f"{onnx_sim_path.name}_data",
150
- )
151
- else:
152
- onnx.save(onnx_sim, str(onnx_sim_path))
150
+ save_onnx_model(
151
+ onnx_sim,
152
+ onnx_sim_path,
153
+ save_as_external_data=self.input_model.with_suffix(
154
+ ".onnx_data"
155
+ ).exists(),
156
+ location=f"{onnx_sim_path.name}_data",
157
+ )
153
158
  return onnx_sim_path
154
159
 
155
160
  @abstractmethod
@@ -3,3 +3,4 @@ nvidia-dali-tf-plugin-cuda120==1.49.0
3
3
  protobuf==3.20.3
4
4
  matplotlib==3.10.6
5
5
  pyparsing==2.4.7
6
+ onnx==1.17.0 # Hailo SDK still imports onnx.mapping and still pins protobuf==3.20.3.
@@ -106,7 +106,10 @@ class RVC3Exporter(RVC2Exporter):
106
106
  data_type=DataType.UINT8,
107
107
  transpose=False,
108
108
  )
109
- cv2.imwrite(str(calibration_img_dir / file.name), img)
109
+ suffix = ".png" if file.suffix == ".npy" else file.suffix
110
+ cv2.imwrite(
111
+ str((calibration_img_dir / file.stem).with_suffix(suffix)), img
112
+ )
110
113
 
111
114
  config = {
112
115
  "model": {
@@ -40,6 +40,7 @@ class RVC4Exporter(Exporter):
40
40
  super().__init__(config=config, output_dir=output_dir)
41
41
 
42
42
  rvc4_cfg = config.rvc4
43
+ self.encodings = rvc4_cfg.encodings
43
44
  self.snpe_onnx_to_dlc = rvc4_cfg.snpe_onnx_to_dlc_args
44
45
  self.snpe_dlc_quant = rvc4_cfg.snpe_dlc_quant_args
45
46
  self.snpe_dlc_graph_prepare = rvc4_cfg.snpe_dlc_graph_prepare_args
@@ -186,6 +187,7 @@ class RVC4Exporter(Exporter):
186
187
  args.append("--override_params")
187
188
  elif self.quantization_mode == QuantizationMode.INT8_16_MIX:
188
189
  self._add_args(args, ["--act_bitwidth", "16"])
190
+ elif self.encodings is not None:
189
191
  args.append("--override_params")
190
192
 
191
193
  start_time = time.time()
@@ -273,20 +275,28 @@ class RVC4Exporter(Exporter):
273
275
  return self.input_list_path
274
276
 
275
277
  def generate_io_encodings(self) -> Path:
276
- encodings_dict = {"activation_encodings": {}, "param_encodings": {}}
277
- if not (list(self.inputs.keys()) and list(self.outputs.keys())):
278
- logger.warning(
279
- "Cannot generate I/O encodings as inputs or outputs are not defined. The resulting DLC may not be compatible with DAI."
278
+ if self.encodings is not None:
279
+ encodings_dict = self.encodings.model_dump(
280
+ mode="json", exclude_none=True
280
281
  )
281
- for name in (
282
- list(self.inputs.keys())
283
- + list(self.outputs.keys())
284
- + self.extra_quant_tensors
285
- ):
286
- encodings_dict["activation_encodings"][name] = [
287
- {"bitwidth": 8, "dtype": "int"}
288
- ]
289
- encodings_path = self.intermediate_outputs_dir / "io_encodings.json"
282
+ else:
283
+ encodings_dict = {
284
+ "activation_encodings": {},
285
+ "param_encodings": {},
286
+ }
287
+ if not (list(self.inputs.keys()) and list(self.outputs.keys())):
288
+ logger.warning(
289
+ "Cannot generate I/O encodings as inputs or outputs are not defined. The resulting DLC may not be compatible with DAI."
290
+ )
291
+ for name in (
292
+ list(self.inputs.keys())
293
+ + list(self.outputs.keys())
294
+ + self.extra_quant_tensors
295
+ ):
296
+ encodings_dict["activation_encodings"][name] = [
297
+ {"bitwidth": 8, "dtype": "int"}
298
+ ]
299
+ encodings_path = self.intermediate_outputs_dir / "encodings.json"
290
300
  with open(encodings_path, "w") as encodings_file:
291
301
  json.dump(encodings_dict, encodings_file, indent=4)
292
302
  return encodings_path
@@ -345,10 +355,14 @@ class RVC4Exporter(Exporter):
345
355
 
346
356
  if self.quantization_mode == QuantizationMode.FP16_STD:
347
357
  self._add_args(args, ["--float_bitwidth", "16"])
348
- elif self.quantization_mode in [
349
- QuantizationMode.INT8_16_MIX,
350
- QuantizationMode.INT8_16_MIX_ACC,
351
- ]:
358
+ elif (
359
+ self.quantization_mode
360
+ in [
361
+ QuantizationMode.INT8_16_MIX,
362
+ QuantizationMode.INT8_16_MIX_ACC,
363
+ ]
364
+ or self.encodings is not None
365
+ ):
352
366
  io_encodings_file = self.generate_io_encodings()
353
367
  self._add_args(
354
368
  args,
@@ -3,3 +3,4 @@ psutil
3
3
  numpy<2
4
4
  polars
5
5
  pytest # this is actually required by snpe packages
6
+ onnx==1.18.0 # SNPE's ONNX importer fails with onnx==1.21.0 in CI.
@@ -7,7 +7,9 @@ from .docker_utils import (
7
7
  docker_exec,
8
8
  get_container_memory_available,
9
9
  get_container_memory_limit,
10
+ get_default_target_version,
10
11
  get_docker_image,
12
+ get_local_docker_image,
11
13
  in_docker,
12
14
  )
13
15
  from .environ import environ
@@ -64,8 +66,10 @@ __all__ = [
64
66
  "get_archive_input",
65
67
  "get_container_memory_available",
66
68
  "get_container_memory_limit",
69
+ "get_default_target_version",
67
70
  "get_docker_image",
68
71
  "get_extra_quant_tensors",
72
+ "get_local_docker_image",
69
73
  "get_metadata",
70
74
  "get_protocol",
71
75
  "guess_new_layout",
@@ -27,15 +27,38 @@ def read_img_dir(path: Path, max_images: int) -> list[Path]:
27
27
  return imgs
28
28
 
29
29
 
30
+ def _find_content_root(path: Path) -> Path:
31
+ ignored_entries = {"__MACOSX", "Thumbs.db", "desktop.ini"}
32
+
33
+ current = path
34
+ while True:
35
+ contents = [
36
+ p
37
+ for p in current.iterdir()
38
+ if p.name not in ignored_entries and not p.name.startswith(".")
39
+ ]
40
+ subdirs = [p for p in contents if p.is_dir()]
41
+ files = [p for p in contents if p.is_file()]
42
+
43
+ if files:
44
+ return current
45
+ if len(subdirs) == 1:
46
+ current = subdirs[0]
47
+ else:
48
+ # Multiple subdirectories and no files. Return current and let read_img_dir raise an error
49
+ return current
50
+
51
+
30
52
  def _get_from_remote(string: str, dest: Path, max_images: int = -1) -> Path:
31
53
  path = download_from_remote(string, dest, max_images)
32
54
  if path.suffix == ".zip":
33
55
  extracted_path = path.with_suffix("")
34
56
  if extracted_path.exists():
35
57
  shutil.rmtree(extracted_path)
58
+ extracted_path.mkdir(parents=True, exist_ok=True)
36
59
  with zipfile.ZipFile(path, "r") as zip_ref:
37
- zip_ref.extractall(path.parent)
38
- return extracted_path
60
+ zip_ref.extractall(extracted_path)
61
+ return _find_content_root(extracted_path)
39
62
  return path
40
63
 
41
64
 
@@ -1,27 +1,28 @@
1
+ import json
1
2
  from itertools import chain
2
3
  from pathlib import Path
3
4
  from typing import Annotated, Any, Literal, cast
4
5
 
5
6
  import onnx
6
7
  from loguru import logger
7
- from luxonis_ml.typing import PathType
8
+ from luxonis_ml.typing import BaseModelExtraForbid, PathType
8
9
  from luxonis_ml.utils import LuxonisConfig
9
10
  from onnx import TypeProto
10
11
  from pydantic import (
11
- BaseModel,
12
- ConfigDict,
13
12
  Field,
14
13
  PositiveInt,
14
+ field_serializer,
15
15
  field_validator,
16
16
  model_validator,
17
17
  )
18
18
  from typing_extensions import Self
19
19
 
20
20
  from modelconverter.utils.calibration_data import download_calibration_data
21
- from modelconverter.utils.constants import MODELS_DIR
21
+ from modelconverter.utils.constants import MISC_DIR, MODELS_DIR
22
22
  from modelconverter.utils.filesystem_utils import resolve_path
23
23
  from modelconverter.utils.layout import make_default_layout
24
24
  from modelconverter.utils.metadata import Metadata, get_metadata
25
+ from modelconverter.utils.onnx_compatibility import save_onnx_model
25
26
  from modelconverter.utils.types import (
26
27
  DataType,
27
28
  Encoding,
@@ -40,11 +41,7 @@ NAMED_VALUES = {
40
41
  }
41
42
 
42
43
 
43
- class CustomBaseModel(BaseModel):
44
- model_config = ConfigDict(extra="forbid")
45
-
46
-
47
- class LinkCalibrationConfig(CustomBaseModel):
44
+ class LinkCalibrationConfig(BaseModelExtraForbid):
48
45
  stage: str
49
46
  output: str | None = None
50
47
  script: str | None = None
@@ -68,7 +65,7 @@ class LinkCalibrationConfig(CustomBaseModel):
68
65
  return script
69
66
 
70
67
 
71
- class ImageCalibrationConfig(CustomBaseModel):
68
+ class ImageCalibrationConfig(BaseModelExtraForbid):
72
69
  path: Path
73
70
  max_images: int = -1
74
71
  resize_method: ResizeMethod = ResizeMethod.RESIZE
@@ -81,7 +78,7 @@ class ImageCalibrationConfig(CustomBaseModel):
81
78
  return download_calibration_data(str(value))
82
79
 
83
80
 
84
- class RandomCalibrationConfig(CustomBaseModel):
81
+ class RandomCalibrationConfig(BaseModelExtraForbid):
85
82
  max_images: int = 20
86
83
  min_value: float = 0.0
87
84
  max_value: float = 255.0
@@ -90,7 +87,7 @@ class RandomCalibrationConfig(CustomBaseModel):
90
87
  data_type: DataType = DataType.FLOAT32
91
88
 
92
89
 
93
- class OutputConfig(CustomBaseModel):
90
+ class OutputConfig(BaseModelExtraForbid):
94
91
  name: str
95
92
  shape: list[int] | None = None
96
93
  layout: str | None = None
@@ -123,7 +120,7 @@ class OutputConfig(CustomBaseModel):
123
120
  return self
124
121
 
125
122
 
126
- class EncodingConfig(CustomBaseModel):
123
+ class EncodingConfig(BaseModelExtraForbid):
127
124
  from_: Annotated[
128
125
  Encoding, Field(alias="from", serialization_alias="from")
129
126
  ] = Encoding.RGB
@@ -225,7 +222,7 @@ class InputConfig(OutputConfig):
225
222
  return value
226
223
 
227
224
 
228
- class TargetConfig(CustomBaseModel):
225
+ class TargetConfig(BaseModelExtraForbid):
229
226
  disable_calibration: bool = False
230
227
 
231
228
 
@@ -265,6 +262,28 @@ class RVC3Config(BlobBaseConfig):
265
262
  pot_target_device: PotDevice = PotDevice.VPU
266
263
 
267
264
 
265
+ class QuantizationOverridesItem(BaseModelExtraForbid):
266
+ bitwidth: Annotated[int, Literal[4, 8, 16, 32]] | None = None
267
+ is_symmetric: bool | None = None
268
+ dtype: Literal["int", "float"] | None = None
269
+ max: float | None = None
270
+ min: float | None = None
271
+ offset: int | None = None
272
+ scale: float | None = None
273
+
274
+ @field_serializer("is_symmetric", when_used="json")
275
+ @staticmethod
276
+ def serialize_is_symmetric(value: bool | None) -> str | None:
277
+ if value is None:
278
+ return None
279
+ return str(value)
280
+
281
+
282
+ class Encodings(BaseModelExtraForbid):
283
+ activation_encodings: dict[str, list[QuantizationOverridesItem]]
284
+ param_encodings: dict[str, list[QuantizationOverridesItem]]
285
+
286
+
268
287
  class RVC4Config(TargetConfig):
269
288
  snpe_onnx_to_dlc_args: list[str] = []
270
289
  snpe_dlc_quant_args: list[str] = []
@@ -277,6 +296,37 @@ class RVC4Config(TargetConfig):
277
296
  htp_socs: list[
278
297
  Literal["sm8350", "sm8450", "sm8550", "sm8650", "qcs6490", "qcs8550"]
279
298
  ] = ["sm8550"]
299
+ encodings: Encodings | None = None
300
+
301
+ @model_validator(mode="after")
302
+ def validate_quantization_overrides(self) -> Self:
303
+ if "--quantization_overrides" in self.snpe_onnx_to_dlc_args:
304
+ if self.encodings:
305
+ raise ValueError(
306
+ "Cannot specify both `--quantization_overrides`"
307
+ "in `rvc4.snpe_onnx_to_dlc_args` and "
308
+ "`rvc4.encodings` at the same time."
309
+ )
310
+ qo_index = self.snpe_onnx_to_dlc_args.index(
311
+ "--quantization_overrides"
312
+ )
313
+ self.snpe_onnx_to_dlc_args.pop(qo_index)
314
+ encodings_json = self.snpe_onnx_to_dlc_args.pop(qo_index)
315
+ with open(encodings_json) as f:
316
+ self.encodings = Encodings.model_validate_json(json.load(f))
317
+ return self
318
+
319
+ @field_validator("encodings", mode="before")
320
+ @staticmethod
321
+ def validate_encodings(value: Any) -> Encodings | None:
322
+ if value is None:
323
+ return None
324
+
325
+ if isinstance(value, str):
326
+ value_path = resolve_path(value, MISC_DIR)
327
+ return Encodings(**json.loads(value_path.read_text()))
328
+
329
+ return Encodings(**value)
280
330
 
281
331
  @model_validator(mode="after")
282
332
  def _validate_fp16(self) -> Self:
@@ -286,7 +336,7 @@ class RVC4Config(TargetConfig):
286
336
  return self
287
337
 
288
338
 
289
- class SingleStageConfig(CustomBaseModel):
339
+ class SingleStageConfig(BaseModelExtraForbid):
290
340
  input_model: Path
291
341
  input_bin: Path | None = None
292
342
  input_file_type: InputFileType
@@ -592,14 +642,9 @@ def _get_onnx_node_info(
592
642
  f"Output value info for node '{node_name}' not found."
593
643
  )
594
644
 
595
- shape = [
596
- dim.dim_value for dim in output_value_info.type.tensor_type.shape.dim
597
- ]
598
- if any(dim == 0 for dim in shape):
599
- raise ValueError(
600
- "Dynamic shapes are not supported. "
601
- f"Shape of node '{node_name}' is {shape}."
602
- )
645
+ shape = _get_static_onnx_shape(
646
+ output_value_info.type.tensor_type, f"node '{node_name}'"
647
+ )
603
648
  data_type = output_value_info.type.tensor_type.elem_type
604
649
 
605
650
  return shape, DataType.from_onnx_dtype(data_type)
@@ -613,12 +658,7 @@ def _get_onnx_tensor_info(
613
658
  def extract_tensor_info(
614
659
  tensor_type: TypeProto.Tensor,
615
660
  ) -> tuple[list[int], DataType]:
616
- shape = [dim.dim_value for dim in tensor_type.shape.dim]
617
- if any(dim == 0 for dim in shape):
618
- raise ValueError(
619
- "Dynamic shapes are not supported. "
620
- f"Shape of tensor '{tensor_name}' is {shape}."
621
- )
661
+ shape = _get_static_onnx_shape(tensor_type, f"tensor '{tensor_name}'")
622
662
  return shape, DataType.from_onnx_dtype(tensor_type.elem_type)
623
663
 
624
664
  for tensor in chain(model.graph.input, model.graph.output):
@@ -638,6 +678,21 @@ def _get_onnx_tensor_info(
638
678
  raise NameError(f"Tensor '{tensor_name}' not found in the ONNX model.")
639
679
 
640
680
 
681
+ def _get_static_onnx_shape(
682
+ tensor_type: TypeProto.Tensor, tensor_name: str
683
+ ) -> list[int]:
684
+ shape = []
685
+ for dim in tensor_type.shape.dim:
686
+ if dim.HasField("dim_value") and dim.dim_value > 0:
687
+ shape.append(dim.dim_value)
688
+ else:
689
+ raise ValueError(
690
+ "Dynamic shapes are not supported. "
691
+ f"Shape of {tensor_name} is {[d.dim_value for d in tensor_type.shape.dim]}."
692
+ )
693
+ return shape
694
+
695
+
641
696
  def _get_onnx_inter_info(
642
697
  model_path: Path, name: str
643
698
  ) -> tuple[list[int] | None, DataType | None]:
@@ -690,12 +745,9 @@ def generate_renamed_onnx(
690
745
  if output_name in rename_dict:
691
746
  node.output[i] = rename_dict[output_name]
692
747
 
693
- if model_data_path:
694
- onnx.save(
695
- model,
696
- str(output_path),
697
- save_as_external_data=True,
698
- location=f"{output_path.name}_data",
699
- )
700
- else:
701
- onnx.save(model, str(output_path))
748
+ save_onnx_model(
749
+ model,
750
+ output_path,
751
+ save_as_external_data=model_data_path is not None,
752
+ location=f"{output_path.name}_data",
753
+ )