Skip to content

Commit

Permalink
add encoding_mismatch property on InputConfig and added a unit test f…
Browse files Browse the repository at this point in the history
…or checking modified onnx model.
  • Loading branch information
ptoupas committed Nov 11, 2024
1 parent cbfdd23 commit 9a08c90
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 18 deletions.
2 changes: 1 addition & 1 deletion modelconverter/packages/hailo/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def _get_alls(self, runner: ClientRunner) -> str:
f"{mean_values},{scale_values},{hn_name})"
)

if inp.encoding.from_ != inp.encoding.to:
if inp.encoding_mismatch:
alls.append(
f"bgr_to_rgb_{safe_name} = input_conversion("
f"{hn_name},bgr_to_rgb)"
Expand Down
18 changes: 4 additions & 14 deletions modelconverter/packages/rvc2/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,9 @@ def _export_openvino_ir(self) -> Path:
reverse_only=True,
)
for inp in self.inputs.values():
if (
inp.mean_values is not None
and inp.encoding.from_ != inp.encoding.to
):
if inp.mean_values is not None and inp.encoding_mismatch:
inp.mean_values = inp.mean_values[::-1]
if (
inp.scale_values is not None
and inp.encoding.from_ != inp.encoding.to
):
if inp.scale_values is not None and inp.encoding_mismatch:
inp.scale_values = inp.scale_values[::-1]
inp.encoding.from_ = Encoding.BGR
inp.encoding.to = Encoding.BGR
Expand Down Expand Up @@ -148,8 +142,7 @@ def _export_openvino_ir(self) -> Path:

# Append reverse_input_channels flag only once if needed
reverse_input_flag = any(
inp.encoding.from_ != inp.encoding.to
for inp in self.inputs.values()
inp.encoding_mismatch for inp in self.inputs.values()
)
if reverse_input_flag:
args.append("--reverse_input_channels")
Expand All @@ -162,10 +155,7 @@ def _export_openvino_ir(self) -> Path:
return self.input_model.with_suffix(".xml")

def _check_reverse_channels(self):
reverses = [
inp.encoding.from_ != inp.encoding.to
for inp in self.inputs.values()
]
reverses = [inp.encoding_mismatch for inp in self.inputs.values()]
return all(reverses) or not any(reverses)

@staticmethod
Expand Down
4 changes: 4 additions & 0 deletions modelconverter/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class InputConfig(OutputConfig):
frozen_value: Optional[Any] = None
encoding: EncodingConfig = EncodingConfig()

@property
def encoding_mismatch(self) -> bool:
return self.encoding.from_ != self.encoding.to

@model_validator(mode="after")
def _validate_grayscale_inputs(self) -> Self:
if self.layout is None:
Expand Down
2 changes: 1 addition & 1 deletion modelconverter/utils/onnx_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def onnx_attach_normalization_to_inputs(
last_output = input_name

# 1. Reverse channels if needed
if cfg.encoding.from_ != cfg.encoding.to:
if cfg.encoding_mismatch:
split_names = [f"split_{i}_{input_name}" for i in range(3)]
split_node = helper.make_node(
"Split",
Expand Down
182 changes: 180 additions & 2 deletions tests/test_utils/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
from pathlib import Path
from typing import Dict, List, Optional

import numpy as np
import onnx
import pytest
from onnx import checker, helper
from onnx.onnx_pb import TensorProto

from modelconverter.utils.config import Config, EncodingConfig
from modelconverter.utils.nn_archive import process_nn_archive
from modelconverter.utils.nn_archive import (
process_nn_archive,
)
from modelconverter.utils.onnx_tools import onnx_attach_normalization_to_inputs
from modelconverter.utils.types import (
DataType,
Encoding,
Expand Down Expand Up @@ -129,7 +133,7 @@ def setup():
checker.check_model(model)
onnx.save(model, str(DATA_DIR / "dummy_model.onnx"))
yield
shutil.rmtree(DATA_DIR)
# shutil.rmtree(DATA_DIR)


def set_nested_config_value(
Expand Down Expand Up @@ -616,6 +620,180 @@ def test_incorrect_type(key: str, value: str):
)


@pytest.mark.parametrize(
"key, value",
[
(
[],
[],
),
(
["encoding"],
["BGR"],
),
(
[
"inputs.0.name",
"inputs.0.encoding",
"inputs.1.name",
"inputs.1.encoding.from",
"inputs.1.encoding.to",
],
["input0", "BGR", "input1", "RGB", "BGR"],
),
(
[
"inputs.0.name",
"inputs.0.encoding.from",
"inputs.1.name",
],
["input0", "RGB", "input1"],
),
(
[
"encoding",
"mean_values",
"scale_values",
"inputs.0.name",
"inputs.1.name",
"inputs.1.encoding.from",
"inputs.1.mean_values",
],
["BGR", 0, 255, "input0", "input1", "RGB", 127],
),
(
[
"inputs.0.name",
"inputs.0.encoding",
"inputs.0.mean_values",
"inputs.0.scale_values",
"inputs.1.name",
"inputs.1.encoding.from",
"inputs.1.encoding.to",
"inputs.1.mean_values",
"inputs.1.scale_values",
],
[
"input0",
"BGR",
0,
1,
"input1",
"RGB",
"BGR",
[123.675, 116.28, 103.53],
[58.395, 57.12, 57.375],
],
),
(
[
"encoding",
"mean_values",
"scale_values",
"inputs.0.name",
"inputs.0.encoding",
"inputs.0.mean_values",
"inputs.0.scale_values",
"inputs.1.name",
"inputs.1.encoding.from",
"inputs.1.encoding.to",
"inputs.1.mean_values",
"inputs.1.scale_values",
],
[
"RGB",
0,
255,
"input0",
"BGR",
0,
1,
"input1",
"RGB",
"BGR",
[123.675, 116.28, 103.53],
[58.395, 57.12, 57.375],
],
),
],
)
def test_modified_onnx(key: List[str], value: List[str]):
overrides = {key[i]: value[i] for i in range(len(key))}
overrides["input_model"] = str(DATA_DIR / "dummy_model.onnx")
config = Config.get_config(
None,
overrides,
)
inputs = next(iter(config.stages.values())).inputs
input_configs = {inp.name: inp for inp in inputs}
print(input_configs)
modified_onnx_path = onnx_attach_normalization_to_inputs(
DATA_DIR / "dummy_model.onnx",
DATA_DIR / "dummy_model_modified.onnx",
input_configs,
reverse_only=False,
)
modified_onnx = onnx.load(modified_onnx_path)

reverse_inputs = {inp.name: False for inp in inputs}
normalize_inputs = {inp.name: [False, False] for inp in inputs}
for node in modified_onnx.graph.node:
if node.op_type == "Split":
next_node = modified_onnx.graph.node[
modified_onnx.graph.node.index(node) + 1
]
assert next_node.op_type == "Concat"
reverse_inputs[node.input[0]] = True
elif node.op_type == "Sub":
inp_name = node.input[1].split("mean_")[1]
mean_tensor = onnx.numpy_helper.to_array(
next(
t
for t in modified_onnx.graph.initializer
if t.name == node.input[1]
)
)
assert np.allclose(
np.squeeze(mean_tensor),
np.array(input_configs[inp_name].mean_values),
)
normalize_inputs[inp_name][0] = True
elif node.op_type == "Mul":
inp_name = node.input[1].split("scale_")[1]
scale_tensor = onnx.numpy_helper.to_array(
next(
t
for t in modified_onnx.graph.initializer
if t.name == node.input[1]
)
)
assert np.allclose(
np.squeeze(scale_tensor),
1 / np.array(input_configs[inp_name].scale_values),
)
normalize_inputs[inp_name][1] = True

for inp, norm in reverse_inputs.items():
if norm:
assert input_configs[inp].encoding_mismatch
else:
assert not input_configs[inp].encoding_mismatch

for inp, norm in normalize_inputs.items():
if norm[0] and norm[1]:
assert input_configs[inp].mean_values is not None
assert input_configs[inp].scale_values is not None
elif norm[0] and not norm[1]:
assert input_configs[inp].mean_values is not None
assert input_configs[inp].scale_values is None or [1, 1, 1]
elif not norm[0] and norm[1]:
assert input_configs[inp].mean_values is None or [0, 0, 0]
assert input_configs[inp].scale_values is not None
else:
assert input_configs[inp].mean_values is None or [0, 0, 0]
assert input_configs[inp].scale_values is None or [1, 1, 1]


@pytest.mark.parametrize(
"key, value, expected",
[
Expand Down

0 comments on commit 9a08c90

Please sign in to comment.