Skip to content

Commit 6774f62

Browse files
committed
fix
1 parent 08b70d7 commit 6774f62

File tree

3 files changed

+116
-43
lines changed

3 files changed

+116
-43
lines changed

src/model_api/adapters/onnx_adapter.py

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
#
2-
# Copyright (C) 2020-2024 Intel Corporation
2+
# Copyright (C) 2020-2025 Intel Corporation
33
# SPDX-License-Identifier: Apache-2.0
44
#
55

66
from __future__ import annotations
77

88
import sys
9-
from functools import partial, reduce
109
from typing import Any, Callable
1110

1211
import numpy as np
1312

14-
from .utils import INTERPOLATION_TYPES, RESIZE_TYPES, InputTransform
13+
from .utils import setup_python_preprocessing_pipeline
1514

1615
try:
1716
import onnx
@@ -145,30 +144,17 @@ def embed_preprocessing(
145144
"""
146145
Adds external preprocessing steps done before ONNX model execution.
147146
"""
148-
preproc_funcs = [np.squeeze]
149-
if resize_mode != "crop":
150-
if resize_mode == "fit_to_window_letterbox":
151-
resize_fn = partial(
152-
RESIZE_TYPES[resize_mode],
153-
size=target_shape,
154-
interpolation=INTERPOLATION_TYPES[interpolation_mode],
155-
pad_value=pad_value,
156-
)
157-
else:
158-
resize_fn = partial(
159-
RESIZE_TYPES[resize_mode],
160-
size=target_shape,
161-
interpolation=INTERPOLATION_TYPES[interpolation_mode],
162-
)
163-
else:
164-
resize_fn = partial(RESIZE_TYPES[resize_mode], size=target_shape)
165-
preproc_funcs.append(resize_fn)
166-
input_transform = InputTransform(brg2rgb, mean, scale)
167-
preproc_funcs.extend((input_transform.__call__, partial(change_layout, layout=layout)))
168-
169-
self.preprocessor = reduce(
170-
lambda f, g: lambda x: f(g(x)),
171-
reversed(preproc_funcs),
147+
self.preprocessor = setup_python_preprocessing_pipeline(
148+
layout=layout,
149+
resize_mode=resize_mode,
150+
interpolation_mode=interpolation_mode,
151+
target_shape=target_shape,
152+
pad_value=pad_value,
153+
dtype=dtype,
154+
brg2rgb=brg2rgb,
155+
mean=mean,
156+
scale=scale,
157+
input_idx=input_idx,
172158
)
173159

174160
def get_model(self):
@@ -227,18 +213,3 @@ def get_shape_from_onnx(onnx_shape):
227213
if isinstance(item, str):
228214
onnx_shape[i] = -1
229215
return tuple(onnx_shape)
230-
231-
232-
def change_layout(image, layout):
233-
"""Changes the input image layout to fit the layout of the model input layer.
234-
235-
Args:
236-
inputs (ndarray): a single image as 3D array in HWC layout
237-
238-
Returns:
239-
- the image with layout aligned with the model layout
240-
"""
241-
if "CHW" in layout:
242-
image = image.transpose((2, 0, 1)) # HWC->CHW
243-
image = image.reshape((1, *image.shape))
244-
return image

src/model_api/adapters/openvino_adapter.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
resize_image,
4242
resize_image_letterbox,
4343
resize_image_with_aspect,
44+
setup_python_preprocessing_pipeline,
4445
)
4546

4647

@@ -143,6 +144,8 @@ def __init__(
143144
)
144145
self.is_onnx_file = False
145146
self.onnx_metadata = {}
147+
self.preprocessor = lambda arg: arg
148+
self.use_python_preprocessing = False
146149

147150
if isinstance(self.model_path, (str, Path)):
148151
if Path(self.model_path).suffix == ".onnx" and weights_path:
@@ -280,11 +283,17 @@ def copy_raw_result(self, request):
280283
return {key: request.get_tensor(key).data.copy() for key in self.get_output_layers()}
281284

282285
def infer_sync(self, dict_data: dict[str, ndarray]) -> dict[str, ndarray]:
286+
if self.use_python_preprocessing:
287+
for key in dict_data:
288+
dict_data[key] = self.preprocessor(dict_data[key])
283289
self.infer_request = self.async_queue[self.async_queue.get_idle_request_id()]
284290
self.infer_request.infer(dict_data)
285291
return self.get_raw_result(self.infer_request)
286292

287293
def infer_async(self, dict_data, callback_data) -> None:
294+
if self.use_python_preprocessing:
295+
for key in dict_data:
296+
dict_data[key] = self.preprocessor(dict_data[key])
288297
self.async_queue.start_async(dict_data, callback_data)
289298

290299
def set_callback(self, callback_fn: Callable):
@@ -347,8 +356,26 @@ def embed_preprocessing(
347356
input_idx: int = 0,
348357
) -> None:
349358
"""
350-
Embeds OpenVINO PrePostProcessor module into the model.
359+
Embeds preprocessing into the model, or sets up Python preprocessing for NPU devices.
351360
"""
361+
# Check if we should use Python preprocessing for NPU devices
362+
devices = parse_devices(self.device)
363+
if any("NPU" in dev.upper() for dev in devices):
364+
self.preprocessor = setup_python_preprocessing_pipeline(
365+
layout=layout,
366+
resize_mode=resize_mode,
367+
interpolation_mode=interpolation_mode,
368+
target_shape=target_shape,
369+
pad_value=pad_value,
370+
dtype=dtype,
371+
brg2rgb=brg2rgb,
372+
mean=mean,
373+
scale=scale,
374+
input_idx=input_idx,
375+
)
376+
self.use_python_preprocessing = True
377+
return
378+
352379
ppp = PrePostProcessor(self.model)
353380

354381
# Change the input type to the 8-bit image

src/model_api/adapters/utils.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,81 @@ def crop_resize_ocv(image: np.ndarray, size: tuple[int, int]) -> np.ndarray:
517517
return cv2.resize(cropped_frame, size)
518518

519519

520+
def setup_python_preprocessing_pipeline(
521+
layout: str,
522+
resize_mode: str,
523+
interpolation_mode: str,
524+
target_shape: tuple[int, ...],
525+
pad_value: int,
526+
dtype: type = int,
527+
brg2rgb: bool = False,
528+
mean: list[Any] | None = None,
529+
scale: list[Any] | None = None,
530+
input_idx: int = 0,
531+
):
532+
"""
533+
Sets up a Python preprocessing pipeline for model adapters.
534+
535+
Args:
536+
layout: Target layout for the input (e.g., "NCHW", "NHWC")
537+
resize_mode: Type of resizing ("crop", "standard", "fit_to_window", "fit_to_window_letterbox")
538+
interpolation_mode: Interpolation method ("LINEAR", "CUBIC", "NEAREST")
539+
target_shape: Target shape for resizing
540+
pad_value: Padding value for letterbox resizing
541+
dtype: Data type for preprocessing
542+
brg2rgb: Whether to convert BGR to RGB
543+
mean: Mean values for normalization
544+
scale: Scale values for normalization
545+
input_idx: Input index (unused but kept for compatibility)
546+
547+
Returns:
548+
Callable: A preprocessing function that can be applied to input data
549+
"""
550+
from functools import partial, reduce
551+
552+
preproc_funcs = [np.squeeze]
553+
if resize_mode != "crop":
554+
if resize_mode == "fit_to_window_letterbox":
555+
resize_fn = partial(
556+
RESIZE_TYPES[resize_mode],
557+
size=target_shape,
558+
interpolation=INTERPOLATION_TYPES[interpolation_mode],
559+
pad_value=pad_value,
560+
)
561+
else:
562+
resize_fn = partial(
563+
RESIZE_TYPES[resize_mode],
564+
size=target_shape,
565+
interpolation=INTERPOLATION_TYPES[interpolation_mode],
566+
)
567+
else:
568+
resize_fn = partial(RESIZE_TYPES[resize_mode], size=target_shape)
569+
preproc_funcs.append(resize_fn)
570+
input_transform = InputTransform(brg2rgb, mean, scale)
571+
preproc_funcs.extend((input_transform.__call__, partial(change_layout, layout=layout)))
572+
573+
return reduce(
574+
lambda f, g: lambda x: f(g(x)),
575+
reversed(preproc_funcs),
576+
)
577+
578+
579+
def change_layout(image, layout):
580+
"""Changes the input image layout to fit the layout of the model input layer.
581+
582+
Args:
583+
image (ndarray): a single image as 3D array in HWC layout
584+
layout (str): target layout
585+
586+
Returns:
587+
ndarray: the image with layout aligned with the model layout
588+
"""
589+
if "CHW" in layout:
590+
image = image.transpose((2, 0, 1)) # HWC->CHW
591+
image = image.reshape((1, *image.shape))
592+
return image
593+
594+
520595
RESIZE_TYPES: dict[str, Callable] = {
521596
"crop": crop_resize_ocv,
522597
"standard": resize_image_ocv,

0 commit comments

Comments
 (0)