Skip to content

Commit 3c651b7

Browse files
author
Evgeny Tsykunov
authored
Test Geti integration (#4188)
* geti interaction sketch * test model meta data * mapi inference * code quality and rename * replace data, support all cls tasks * reformat configs * fix comments * mapi model construct
1 parent 4c99b6f commit 3c651b7

File tree

10 files changed

+2010
-0
lines changed

10 files changed

+2010
-0
lines changed

tests/assets/geti_config_arrow/classification/h_label_cls/config.json

Lines changed: 603 additions & 0 deletions
Large diffs are not rendered by default.
Binary file not shown.
Loading

tests/assets/geti_config_arrow/classification/multi_class_cls/config.json

Lines changed: 596 additions & 0 deletions
Large diffs are not rendered by default.
Loading

tests/assets/geti_config_arrow/classification/multi_label_cls/config.json

Lines changed: 603 additions & 0 deletions
Large diffs are not rendered by default.
Loading
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# Copyright (C) 2025 Intel Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from __future__ import annotations
5+
6+
import shutil
7+
import zipfile
8+
from pathlib import Path
9+
from tempfile import TemporaryDirectory
10+
from typing import TYPE_CHECKING, Any
11+
12+
import cv2
13+
import pytest
14+
from model_api.models import Model
15+
from otx.core.data.module import OTXDataModule
16+
from otx.core.model.base import OTXModel
17+
from otx.core.types.export import OTXExportFormatType
18+
from otx.core.types.precision import OTXPrecisionType
19+
from otx.core.types.task import OTXTaskType
20+
from otx.tools.converter import ConfigConverter
21+
22+
if TYPE_CHECKING:
23+
from otx.engine.engine import Engine
24+
25+
TEST_PATH = Path(__file__).parent.parent.parent
26+
DEFAULT_GETI_CONFIG_PER_TASK = {
27+
OTXTaskType.MULTI_CLASS_CLS: TEST_PATH / "assets" / "geti_config_arrow" / "classification" / "multi_class_cls",
28+
OTXTaskType.MULTI_LABEL_CLS: TEST_PATH / "assets" / "geti_config_arrow" / "classification" / "multi_label_cls",
29+
OTXTaskType.H_LABEL_CLS: TEST_PATH / "assets" / "geti_config_arrow" / "classification" / "h_label_cls",
30+
}
31+
32+
33+
def unzip_exportable_code(
34+
work_dir: Path,
35+
exported_path: Path,
36+
dst_dir: Path,
37+
) -> Path:
38+
"""
39+
Unzip exportable code.
40+
Copied from Geti.
41+
"""
42+
with zipfile.ZipFile(exported_path, mode="r") as zfp, TemporaryDirectory(prefix=str(work_dir)) as tmpdir:
43+
zfp.extractall(tmpdir)
44+
dirpath = Path(tmpdir)
45+
46+
shutil.move(dirpath / "model" / "model.xml", dst_dir / "exported_model.xml")
47+
shutil.move(dirpath / "model" / "model.bin", dst_dir / "exported_model.bin")
48+
49+
shutil.move(exported_path, dst_dir / exported_path.name)
50+
51+
52+
class TestEngineAPI:
53+
def __init__(
54+
self,
55+
tmp_path: Path,
56+
geti_config_path: Path,
57+
arrow_file_path: Path,
58+
image_path: Path,
59+
):
60+
self.tmp_path = tmp_path
61+
self.geti_config_path = geti_config_path
62+
self.arrow_file_path = arrow_file_path
63+
self.otx_config = self._convert_config()
64+
self.engine, self.train_kwargs = self._instantiate_engine()
65+
self.image = cv2.imread(str(image_path))
66+
67+
def _convert_config(self) -> dict:
68+
otx_config = ConfigConverter.convert(config_path=self.geti_config_path)
69+
otx_config["data"]["data_format"] = "arrow"
70+
otx_config["data"]["train_subset"]["subset_name"] = "TRAINING"
71+
otx_config["data"]["val_subset"]["subset_name"] = "VALIDATION"
72+
otx_config["data"]["test_subset"]["subset_name"] = "TESTING"
73+
return otx_config
74+
75+
def _instantiate_engine(self) -> tuple[Engine, dict[str, Any]]:
76+
return ConfigConverter.instantiate(
77+
config=self.otx_config,
78+
work_dir=self.tmp_path,
79+
data_root=self.arrow_file_path,
80+
)
81+
82+
def test_model_and_data_module(self):
83+
"""Test the instance type of the model and the datamodule."""
84+
assert isinstance(self.engine.model, OTXModel)
85+
assert isinstance(self.engine.datamodule, OTXDataModule)
86+
87+
def test_training(self):
88+
"""Test the training process."""
89+
max_epochs = 2
90+
self.train_kwargs["max_epochs"] = max_epochs
91+
train_metric = self.engine.train(**self.train_kwargs)
92+
assert len(train_metric) > 0
93+
assert self.engine.checkpoint
94+
95+
def test_predictions(self):
96+
"""Test the prediction process. This is way to check that the model is valid."""
97+
predictions = self.engine.predict()
98+
assert predictions is not None
99+
assert len(predictions) > 0
100+
101+
def test_export_and_infer_onnx(self):
102+
"""Test exporting the model to ONNX."""
103+
for precision in [OTXPrecisionType.FP16, OTXPrecisionType.FP32]:
104+
exported_path = self.engine.export(
105+
export_format=OTXExportFormatType.ONNX,
106+
export_precision=precision,
107+
explain=(precision == OTXPrecisionType.FP32),
108+
export_demo_package=False,
109+
)
110+
export_dir = exported_path.parent
111+
assert export_dir.exists()
112+
113+
# Test Model API
114+
onnx_path = export_dir / "exported_model.onnx"
115+
mapi_model = Model.create_model(onnx_path)
116+
assert mapi_model is not None
117+
118+
predictions = mapi_model(self.image)
119+
assert predictions is not None
120+
assert len(predictions.top_labels) > 0
121+
122+
exported_path.unlink(missing_ok=True)
123+
124+
def test_export_and_infer_openvino(self):
125+
"""Test exporting the model to OpenVINO."""
126+
for precision in [OTXPrecisionType.FP16, OTXPrecisionType.FP32]:
127+
exported_path = self.engine.export(
128+
export_format=OTXExportFormatType.OPENVINO,
129+
export_precision=precision,
130+
explain=(precision == OTXPrecisionType.FP32),
131+
export_demo_package=True,
132+
)
133+
export_dir = exported_path.parent
134+
assert export_dir.exists()
135+
136+
# Test Model API
137+
ov_export_dir = self.tmp_path / "ov_export"
138+
ov_export_dir.mkdir(parents=True, exist_ok=True)
139+
unzip_exportable_code(
140+
work_dir=self.tmp_path,
141+
exported_path=exported_path,
142+
dst_dir=ov_export_dir,
143+
)
144+
xml_path = ov_export_dir / "exported_model.xml"
145+
mapi_model = Model.create_model(xml_path)
146+
assert mapi_model is not None
147+
148+
predictions = mapi_model(self.image)
149+
assert predictions is not None
150+
assert len(predictions.top_labels) > 0
151+
152+
exported_path.unlink(missing_ok=True)
153+
154+
def test_optimize_and_infer_openvino_fp32(self):
155+
"""Test optimizing the OpenVINO model with FP32 precision."""
156+
fp32_export_dir = self.tmp_path / "fp32_export"
157+
fp32_export_dir.mkdir(parents=True, exist_ok=True)
158+
exported_path = self.engine.export(
159+
export_format=OTXExportFormatType.OPENVINO,
160+
export_precision=OTXPrecisionType.FP32,
161+
explain=True,
162+
export_demo_package=True,
163+
)
164+
unzip_exportable_code(
165+
work_dir=self.tmp_path,
166+
exported_path=exported_path,
167+
dst_dir=fp32_export_dir,
168+
)
169+
optimized_path = self.engine.optimize(
170+
checkpoint=fp32_export_dir / "exported_model.xml",
171+
export_demo_package=True,
172+
)
173+
assert optimized_path.exists()
174+
175+
# Test Model API
176+
ov_optimized_dir = self.tmp_path / "ov_optimize"
177+
ov_optimized_dir.mkdir(parents=True, exist_ok=True)
178+
unzip_exportable_code(
179+
work_dir=self.tmp_path,
180+
exported_path=optimized_path,
181+
dst_dir=ov_optimized_dir,
182+
)
183+
xml_path = ov_optimized_dir / "exported_model.xml"
184+
mapi_model = Model.create_model(xml_path)
185+
assert mapi_model is not None
186+
187+
predictions = mapi_model(self.image)
188+
assert predictions is not None
189+
assert len(predictions.top_labels) > 0
190+
191+
192+
@pytest.mark.parametrize("task", pytest.TASK_LIST)
193+
def test_engine_api(task: OTXTaskType, tmp_path: Path):
194+
if task not in DEFAULT_GETI_CONFIG_PER_TASK:
195+
pytest.skip("Only the Geti Tasks are tested to reduce unnecessary resource waste.")
196+
197+
config_arrow_path = DEFAULT_GETI_CONFIG_PER_TASK[task]
198+
geti_config_path = config_arrow_path / "config.json"
199+
arrow_file_path = config_arrow_path / "datum-0-of-1.arrow"
200+
image_path = config_arrow_path / "image.jpg"
201+
202+
tester = TestEngineAPI(tmp_path, geti_config_path, arrow_file_path, image_path)
203+
tester.test_model_and_data_module()
204+
tester.test_training()
205+
tester.test_predictions()
206+
tester.test_export_and_infer_onnx()
207+
tester.test_export_and_infer_openvino()
208+
tester.test_optimize_and_infer_openvino_fp32()

0 commit comments

Comments
 (0)