Ov20/preprocess base python (#7625)

* Preprocessing API - base classes

Includes API definition for trivial mean/scale operations (which don't require layout)

Mean/scale with 'layout' support will be done under separate task together
 with Layout

Current test code coverage: 100%

* Python bindings for base preprocessing API

* remove pre_post_process directory from ngraph/core

* remove files from ngraph/python dir

* move pyngraph pre_post_process files from ngraph/python to runtime

* remove pre_post_process test from CMakeList

* move include to the header

* update include path for pre_post_process

* style fix

* bind InputTensorInfo::set_layout

* cleaned test_preprocess

* fix test expected output

* remove duplicate test

* update description of set_element_type

* fix style

* move preprocess from pyngraph to pyopenvino/graph

* update test_preprocess imports and remove unnecessary test

* remove duplicate import

* update custom method

* update test

* update test

* create decorator that changes Node into Output<Node>

* create function that cast Node to Output<Node>

* update test_preprocess to use decorator for custom function

* change _cast_to_output -> _from_node

* style fix

* add tests fro scale and mean with vector input

* style fix

* add docstring for custom_preprocess_function

* bind InputInfo network method

* style fix

* bind OutputInfo

* fix description of preprocess submodule

* fix style

* update copyright year

* bind OutputTensorInfo

* bind OutputNetworkInfo and InputNetworkInfo

* bind ColorFormat and ResizeAlgorithm

* clean imports

* fix typo

* add PostProcessSteps to init

* bind PreProcessSteps

* create additional tests

* change ngraph.Type to ov.Type

* fix typo

* move _from_node to node_output.hpp

* add read_model from buffer

* update imports

* add new line

* remove bad quotes

* update imports

* style fix

* add new line

* rename functin args

* remove Type import

* update tests

* style fix

* test clean

* remove blank line

* update PrePostProcessor init and build methods

* create test with model update tests with new PrePostProcessor init and build

* move preprocess module from openvino.impl to openvino

Co-authored-by: Michael Nosov <mikhail.nosov@intel.com>
Co-authored-by: bszmelcz <bartosz.szmelczynski@intel.com>
This commit is contained in:
authored and GitHub committed 2021-11-23 02:10:53 +03:00
1 parent 75af8254cb
commit 24ad1edc4e
7 files changed
+1014 -1

No files matched your search

@@ -0,0 +1,22 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
"""
Package: ngraph
Low level wrappers for the PrePostProcessing c++ api.
"""
# flake8: noqa
# main classes
from openvino.pyopenvino.preprocess import InputInfo
from openvino.pyopenvino.preprocess import OutputInfo
from openvino.pyopenvino.preprocess import InputTensorInfo
from openvino.pyopenvino.preprocess import OutputTensorInfo
from openvino.pyopenvino.preprocess import InputNetworkInfo
from openvino.pyopenvino.preprocess import OutputNetworkInfo
from openvino.pyopenvino.preprocess import PrePostProcessor
from openvino.pyopenvino.preprocess import PreProcessSteps
from openvino.pyopenvino.preprocess import PostProcessSteps
from openvino.pyopenvino.preprocess import ColorFormat
from openvino.pyopenvino.preprocess import ResizeAlgorithm
@@ -4,7 +4,7 @@
from functools import wraps
from typing import Any, Callable
from openvino.impl import Node
from openvino.impl import Node, Output
from openvino.utils.types import NodeInput, as_node, as_nodes
@@ -50,3 +50,12 @@ def binary_op(node_factory_function: Callable) -> Callable:
return node
return wrapper
def custom_preprocess_function(custom_function: Callable) -> Callable:
"""Convert Node returned from custom_function to Output."""
@wraps(custom_function)
def wrapper(node: Node) -> Output:
return Output._from_node(custom_function(node))
return wrapper
@@ -81,6 +81,9 @@ void regclass_graph_Output(py::module m, std::string typestring)
get_target_inputs : Set[Input]
Set of Inputs.
)");
output.def("_from_node", [](const std::shared_ptr<ov::Node>& node) {
return ov::Output<ov::Node>(node);
});
output.def("get_tensor",
&ov::Output<VT>::get_tensor,
py::return_value_policy::reference_internal,
@@ -0,0 +1,540 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "pyopenvino/graph/preprocess/pre_post_process.hpp"
#include <pybind11/functional.h>
#include <pybind11/stl.h>
#include "openvino/core/function.hpp"
#include "openvino/core/node.hpp"
#include "openvino/core/preprocess/pre_post_process.hpp"
namespace py = pybind11;
static void regclass_graph_PreProcessSteps(py::module m) {
py::class_<ov::preprocess::PreProcessSteps, std::shared_ptr<ov::preprocess::PreProcessSteps>> steps(
m,
"PreProcessSteps");
steps.doc() = "openvino.impl.preprocess.PreProcessSteps wraps ov::preprocess::PreProcessSteps";
steps.def(py::init<>());
steps.def(
"mean",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, float value) {
me->mean(value);
return me;
},
py::arg("value"),
R"(
Subtracts single float value from each element in input tensor.
Input tensor must have ov.Type.f32 data type.
Parameters
----------
value : float
Value to subtract.
Returns
----------
mean : PreProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
steps.def(
"mean",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, const std::vector<float>& values) {
me->mean(values);
return me;
},
py::arg("values"),
R"(
Subtracts a given single float value from each element in a given channel from input tensor.
Input tensor must have ov.Type.f32 data type.
Parameters
----------
values : List
Values to subtract.
Returns
----------
mean : PreProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
steps.def(
"scale",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, float value) {
me->scale(value);
return me;
},
py::arg("value"),
R"(
Divides each element in input tensor by specified constant float value.
Input tensor must have ov.Type.f32 data type.
Parameters
----------
value : float
Value to divide.
Returns
----------
scale : PreProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
steps.def(
"scale",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, const std::vector<float>& values) {
me->scale(values);
return me;
},
py::arg("values"),
R"(
Divides each element in a given channel from input tensor by a given single float value.
Input tensor must have ov.Type.f32 data type.
Parameters
----------
value : List
Value to divide.
Returns
----------
scale : PreProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
steps.def(
"convert_element_type",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, ov::element::Type type) {
me->convert_element_type(type);
return me;
},
py::arg("type"),
R"(
Converts input tensor element type to specified type.
Input tensor must have openvino.Type.f32 data type.
Parameters
----------
type : Type
Destination type.
Returns
----------
convert_element_type : PreProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
steps.def(
"custom",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, py::function op) {
me->custom(op.cast<const ov::preprocess::PreProcessSteps::CustomPreprocessOp>());
return me;
},
py::arg("operation"),
R"(
Adds custom preprocessing operation.
Parameters
----------
operation : function taking Output<Node> as input argument and returning Output<Node> after preprocessing.
Returns
----------
custom : PreProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
steps.def(
"convert_color",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, const ov::preprocess::ColorFormat& dst_format) {
me->convert_color(dst_format);
return me;
},
py::arg("dst_format"));
steps.def(
"resize",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me,
ov::preprocess::ResizeAlgorithm alg,
size_t dst_height,
size_t dst_width) {
me->resize(alg, dst_height, dst_width);
return me;
},
py::arg("alg"),
py::arg("dst_height"),
py::arg("dst_width"));
steps.def(
"resize",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, ov::preprocess::ResizeAlgorithm alg) {
me->resize(alg);
return me;
},
py::arg("alg"));
steps.def(
"convert_layout",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, const ov::Layout& layout = {}) {
me->convert_layout(layout);
return me;
},
py::arg("dst_layout"));
steps.def(
"convert_layout",
[](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me, const std::vector<uint64_t>& dims) {
me->convert_layout(dims);
return me;
},
py::arg("dims"));
steps.def("reverse_channels", [](const std::shared_ptr<ov::preprocess::PreProcessSteps>& me) {
me->reverse_channels();
return me;
});
}
static void regclass_graph_PostProcessSteps(py::module m) {
py::class_<ov::preprocess::PostProcessSteps, std::shared_ptr<ov::preprocess::PostProcessSteps>> steps(
m,
"PostProcessSteps");
steps.doc() = "openvino.impl.preprocess.PostprocessSteps wraps ov::preprocess::PostProcessSteps";
steps.def(py::init<>());
steps.def(
"convert_element_type",
[](const std::shared_ptr<ov::preprocess::PostProcessSteps>& me, ov::element::Type type) {
me->convert_element_type(type);
return me;
},
py::arg("type"),
R"(
Converts tensor element type to specified type.
Tensor must have openvino.Type.f32 data type.
Parameters
----------
type : Type
Destination type.
Returns
----------
convert_element_type : PostProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
steps.def(
"convert_layout",
[](const std::shared_ptr<ov::preprocess::PostProcessSteps>& me, const ov::Layout& layout = {}) {
me->convert_layout(layout);
return me;
},
py::arg("dst_layout"));
steps.def(
"convert_layout",
[](const std::shared_ptr<ov::preprocess::PostProcessSteps>& me, const std::vector<uint64_t>& dims) {
me->convert_layout(dims);
return me;
},
py::arg("dims"));
steps.def(
"custom",
[](const std::shared_ptr<ov::preprocess::PostProcessSteps>& me, py::function op) {
me->custom(op.cast<const ov::preprocess::PostProcessSteps::CustomPostprocessOp>());
return me;
},
py::arg("operation"),
R"(
Adds custom postprocessing operation.
Parameters
----------
operation : function taking Output<Node> as input argument and returning Output<Node> after postprocessing.
Returns
----------
custom : PostProcessSteps
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
}
static void regclass_graph_InputTensorInfo(py::module m) {
py::class_<ov::preprocess::InputTensorInfo, std::shared_ptr<ov::preprocess::InputTensorInfo>> info(
m,
"InputTensorInfo");
info.doc() = "openvino.impl.preprocess.InputTensorInfo wraps ov::preprocess::InputTensorInfo";
info.def(py::init<>());
info.def(
"set_element_type",
[](const std::shared_ptr<ov::preprocess::InputTensorInfo>& me, const ov::element::Type& type) {
me->set_element_type(type);
return me;
},
py::arg("type"),
R"(
Set initial client's tensor element type. If type is not the same as network's element type,
conversion of element type will be done automatically.
Parameters
----------
type : Type
Client's input tensor element type.
Returns
----------
tensor : InputTensorInfo
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
info.def("set_layout", [](const std::shared_ptr<ov::preprocess::InputTensorInfo>& me, const ov::Layout& layout) {
me->set_layout(layout);
return me;
});
info.def("set_spatial_dynamic_shape", [](const std::shared_ptr<ov::preprocess::InputTensorInfo>& me) {
me->set_spatial_dynamic_shape();
return me;
});
info.def("set_spatial_static_shape",
[](const std::shared_ptr<ov::preprocess::InputTensorInfo>& me, size_t height, size_t width) {
me->set_spatial_static_shape(height, width);
return me;
});
info.def("set_color_format",
[](const std::shared_ptr<ov::preprocess::InputTensorInfo>& me,
const ov::preprocess::ColorFormat& format,
const std::vector<std::string>& sub_names = {}) {
me->set_color_format(format, sub_names);
return me;
});
}
static void regclass_graph_OutputTensorInfo(py::module m) {
py::class_<ov::preprocess::OutputTensorInfo, std::shared_ptr<ov::preprocess::OutputTensorInfo>> info(
m,
"OutputTensorInfo");
info.doc() = "openvino.impl.preprocess.OutputTensorInfo wraps ov::preprocess::OutputTensorInfo";
info.def(py::init<>());
info.def(
"set_element_type",
[](const std::shared_ptr<ov::preprocess::OutputTensorInfo>& me, const ov::element::Type& type) {
me->set_element_type(type);
return me;
},
py::arg("type"),
R"(
Set client's output tensor element type. If type is not the same as network's element type,
conversion of element type will be done automatically.
Parameters
----------
type : Type
Client's output tensor element type.
Returns
----------
tensor : OutputTensorInfo
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
info.def("set_layout", [](const std::shared_ptr<ov::preprocess::OutputTensorInfo>& me, const ov::Layout& layout) {
me->set_layout(layout);
return me;
});
}
static void regclass_graph_InputInfo(py::module m) {
py::class_<ov::preprocess::InputInfo, std::shared_ptr<ov::preprocess::InputInfo>> inp(m, "InputInfo");
inp.doc() = "openvino.impl.preprocess.InputInfo wraps ov::preprocess::InputInfo";
inp.def(py::init<>(), R"(Default constructor, can be used only for networks with exactly one input)");
inp.def(py::init<size_t>(), R"(Constructor with parameter index as argument)");
inp.def(py::init<const std::string&>(), R"(Constructor with input tensor name as argument)");
inp.def(
"tensor",
[](const std::shared_ptr<ov::preprocess::InputInfo>& me,
const std::shared_ptr<ov::preprocess::InputTensorInfo>& inputTensorInfo) {
me->tensor(std::move(*inputTensorInfo));
return me;
},
py::arg("tensor"),
R"(
Adds builder for actual tensor information of client's input.
Parameters
----------
tensor : InputTensorInfo
Client's input tensor information. It's internal data will be moved to parent InputInfo object.
Returns
----------
tensor : InputInfo
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
inp.def(
"preprocess",
[](const std::shared_ptr<ov::preprocess::InputInfo>& me,
const std::shared_ptr<ov::preprocess::PreProcessSteps>& preProcessSteps) {
me->preprocess(std::move(*preProcessSteps));
return me;
},
py::arg("pre_process_steps"),
R"(
Adds builder for actual preprocessing steps for input parameter.
Steps can specify various actions, like 'mean', 'scale' and others.
Parameters
----------
pre_process_steps : PreProcessSteps
Preprocessing steps. It's internal data will be moved to parent InputInfo object.
Returns
----------
preprocess : InputInfo
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
inp.def(
"network",
[](const std::shared_ptr<ov::preprocess::InputInfo>& me,
const std::shared_ptr<ov::preprocess::InputNetworkInfo>& inputNetworkInfo) {
me->network(std::move(*inputNetworkInfo));
return me;
},
py::arg("input_network_info"));
}
static void regclass_graph_OutputInfo(py::module m) {
py::class_<ov::preprocess::OutputInfo, std::shared_ptr<ov::preprocess::OutputInfo>> out(m, "OutputInfo");
out.doc() = "openvino.impl.preprocess.OutputInfo wraps ov::preprocess::OutputInfo";
out.def(py::init<>(), R"(Default constructor, can be used only for networks with exactly one output)");
out.def(py::init<size_t>(), R"(Constructor with parameter index as argument)");
out.def(py::init<const std::string&>(), R"(Constructor with tensor name as argument)");
out.def(
"tensor",
[](const std::shared_ptr<ov::preprocess::OutputInfo>& me,
const std::shared_ptr<ov::preprocess::OutputTensorInfo>& outputTensorInfo) {
me->tensor(std::move(*outputTensorInfo));
return me;
},
py::arg("tensor"),
R"(
Adds builder for actual tensor information of client's output.
Parameters
----------
tensor : OutputTensorInfo
Client's output tensor information. It's internal data will be moved to parent OutputInfo object.
Returns
----------
tensor : OutputInfo
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
out.def(
"postprocess",
[](const std::shared_ptr<ov::preprocess::OutputInfo>& me,
const std::shared_ptr<ov::preprocess::PostProcessSteps>& postProcessSteps) {
me->postprocess(std::move(*postProcessSteps));
return me;
},
py::arg("post_process_steps"),
R"(
Adds builder for actual postprocessing steps for output parameter.
Parameters
----------
post_process_steps : PostProcessSteps
Postprocessing steps. It's internal data will be moved to parent OutputInfo object.
Returns
----------
preprocess : OutputInfo
Reference to itself to allow chaining of calls in client's code in a builder-like manner.
)");
out.def(
"network",
[](const std::shared_ptr<ov::preprocess::OutputInfo>& me,
const std::shared_ptr<ov::preprocess::OutputNetworkInfo>& outputNetworkInfo) {
me->network(std::move(*outputNetworkInfo));
return me;
},
py::arg("output_network_info"));
}
static void regclass_graph_OutputNetworkInfo(py::module m) {
py::class_<ov::preprocess::OutputNetworkInfo, std::shared_ptr<ov::preprocess::OutputNetworkInfo>> info(
m,
"OutputNetworkInfo");
info.doc() = "openvino.impl.preprocess.OutputNetworkInfo wraps ov::preprocess::OutputNetworkInfo";
info.def(py::init<>());
info.def("set_layout", [](const std::shared_ptr<ov::preprocess::OutputNetworkInfo>& me, const ov::Layout& layout) {
me->set_layout(layout);
return me;
});
}
static void regclass_graph_InputNetworkInfo(py::module m) {
py::class_<ov::preprocess::InputNetworkInfo, std::shared_ptr<ov::preprocess::InputNetworkInfo>> info(
m,
"InputNetworkInfo");
info.doc() = "openvino.impl.preprocess.InputNetworkInfo wraps ov::preprocess::InputNetworkInfo";
info.def(py::init<>());
info.def("set_layout", [](const std::shared_ptr<ov::preprocess::InputNetworkInfo>& me, const ov::Layout& layout) {
me->set_layout(layout);
return me;
});
}
static void regenum_graph_ColorFormat(py::module m) {
py::enum_<ov::preprocess::ColorFormat>(m, "ColorFormat")
.value("UNDEFINED", ov::preprocess::ColorFormat::UNDEFINED)
.value("NV12_SINGLE_PLANE", ov::preprocess::ColorFormat::NV12_SINGLE_PLANE)
.value("NV12_TWO_PLANES", ov::preprocess::ColorFormat::NV12_TWO_PLANES)
.value("RGB", ov::preprocess::ColorFormat::RGB)
.value("BGR", ov::preprocess::ColorFormat::BGR)
.export_values();
}
static void regenum_graph_ResizeAlgorithm(py::module m) {
py::enum_<ov::preprocess::ResizeAlgorithm>(m, "ResizeAlgorithm")
.value("RESIZE_LINEAR", ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR)
.value("RESIZE_CUBIC", ov::preprocess::ResizeAlgorithm::RESIZE_CUBIC)
.value("RESIZE_NEAREST", ov::preprocess::ResizeAlgorithm::RESIZE_NEAREST)
.export_values();
}
void regclass_graph_PrePostProcessor(py::module m) {
regclass_graph_PreProcessSteps(m);
regclass_graph_PostProcessSteps(m);
regclass_graph_InputInfo(m);
regclass_graph_OutputInfo(m);
regclass_graph_InputTensorInfo(m);
regclass_graph_OutputTensorInfo(m);
regclass_graph_InputNetworkInfo(m);
regclass_graph_OutputNetworkInfo(m);
regenum_graph_ColorFormat(m);
regenum_graph_ResizeAlgorithm(m);
py::class_<ov::preprocess::PrePostProcessor, std::shared_ptr<ov::preprocess::PrePostProcessor>> proc(
m,
"PrePostProcessor");
proc.doc() = "openvino.impl.preprocess.PrePostProcessor wraps ov::preprocess::PrePostProcessor";
proc.def(py::init<const std::shared_ptr<ov::Function>&>());
proc.def(
"input",
[](const std::shared_ptr<ov::preprocess::PrePostProcessor>& me,
const std::shared_ptr<ov::preprocess::InputInfo>& info) {
me->input(std::move(*info));
return me;
},
py::arg("input_info"),
R"(
Adds builder for preprocessing info for input parameter.
Parameters
----------
input_info : InputInfo
Preprocessing info for input parameter. It's internal data will be moved to PreProcessing object.
Returns
----------
in : PrePostProcessor
Reference to itself to allow chaining of calls in client's code.
)");
proc.def(
"output",
[](const std::shared_ptr<ov::preprocess::PrePostProcessor>& me,
const std::shared_ptr<ov::preprocess::OutputInfo>& info) {
me->output(std::move(*info));
return me;
},
py::arg("output_info"),
R"(
Adds builder for preprocessing info for output parameter.
Parameters
----------
output_info : OutputInfo
Preprocessing info for output parameter. It's internal data will be moved to PreProcessing object.
Returns
----------
in : PrePostProcessor
Reference to itself to allow chaining of calls in client's code.
)");
proc.def("build", &ov::preprocess::PrePostProcessor::build);
}
@@ -0,0 +1,11 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <pybind11/pybind11.h>
namespace py = pybind11;
void regclass_graph_PrePostProcessor(py::module m);
@@ -40,6 +40,7 @@
#include "pyopenvino/graph/ops/util/regmodule_graph_op_util.hpp"
#include "pyopenvino/graph/partial_shape.hpp"
#include "pyopenvino/graph/passes/regmodule_graph_passes.hpp"
#include "pyopenvino/graph/preprocess/pre_post_process.hpp"
#include "pyopenvino/graph/rt_map.hpp"
#include "pyopenvino/graph/shape.hpp"
#include "pyopenvino/graph/strides.hpp"
@@ -85,6 +86,9 @@ PYBIND11_MODULE(pyopenvino, m) {
regmodule_graph_onnx_import(m);
#endif
regmodule_graph_op_util(m_op);
py::module m_preprocess =
m.def_submodule("preprocess", "Package openvino.impl.preprocess that wraps ov::preprocess");
regclass_graph_PrePostProcessor(m_preprocess);
regclass_graph_Function(m);
regmodule_graph_passes(m);
regmodule_graph_util(m);
@@ -0,0 +1,424 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import numpy as np
import pytest
import openvino as ov
import openvino.opset8 as ops
from openvino.impl import Function, Output, Type
from openvino.utils.decorators import custom_preprocess_function
from openvino import Core
from tests.runtime import get_runtime
from openvino.preprocess import PrePostProcessor, InputInfo, PreProcessSteps, InputTensorInfo, \
OutputTensorInfo, OutputNetworkInfo, InputNetworkInfo, ColorFormat, OutputInfo, \
PostProcessSteps, ResizeAlgorithm
def test_ngraph_preprocess_mean():
shape = [2, 2]
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
function = PrePostProcessor(function)\
.input(InputInfo()
.preprocess(PreProcessSteps()
.mean(1.)
)
)\
.build()
input_data = np.array([[1, 2], [3, 4]]).astype(np.float32)
expected_output = np.array([[0, 1], [2, 3]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_mean_vector():
shape = [2, 2]
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
layout = ov.Layout("NCHW")
function = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo().set_layout(layout))
.preprocess(PreProcessSteps()
.mean([1., 2.])
)
)\
.build()
input_data = np.array([[1, 2], [3, 4]]).astype(np.float32)
expected_output = np.array([[0, 0], [2, 2]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_scale_vector():
shape = [2, 2]
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
layout = ov.Layout("NCHW")
function = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo().set_layout(layout))
.preprocess(PreProcessSteps()
.scale([0.5, 2.])
)
)\
.build()
input_data = np.array([[1, 2], [3, 4]]).astype(np.float32)
expected_output = np.array([[2, 1], [6, 2]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_mean_scale_convert():
shape = [2, 2]
param1 = ops.parameter(shape, dtype=np.int32, name="A")
param2 = ops.parameter(shape, dtype=np.int32, name="B")
function = Function([param1, param2], [param1, param2], "TestFunction")
@custom_preprocess_function
def custom_preprocess(output: Output):
return ops.abs(output)
function = PrePostProcessor(function) \
.input(InputInfo(1)
.tensor(InputTensorInfo()
.set_element_type(Type.i32))
.preprocess(PreProcessSteps()
.convert_element_type(Type.f32)
.mean(1.)
.scale(2.)
)
) \
.input(InputInfo(0)
.preprocess(PreProcessSteps()
.convert_element_type(Type.f32)
.mean(1.)
.custom(custom_preprocess)
)
) \
.build()
input_data1 = np.array([[0, 1], [2, -2]]).astype(np.int32)
input_data2 = np.array([[1, 3], [5, 7]]).astype(np.int32)
expected_output1 = np.array([[1, 0], [1, 3]]).astype(np.float32)
expected_output2 = np.array([[0, 1], [2, 3]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
[output1, output2] = computation(input_data1, input_data2)
assert np.equal(output1, expected_output1).all()
assert np.equal(output2, expected_output2).all()
def test_ngraph_preprocess_output_postprocess():
shape = [2, 2]
parameter_a = ops.parameter(shape, dtype=np.int32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
layout1 = ov.Layout("NCHW")
layout2 = ov.Layout("NHWC")
layout3 = [0, 1]
@custom_preprocess_function
def custom_postprocess(output: Output):
return ops.abs(output)
function = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo().set_layout(layout1))
.preprocess(PreProcessSteps()
.convert_element_type(Type.f32)
.mean([1., 2.])
)
) \
.output(OutputInfo().postprocess(PostProcessSteps()
.convert_element_type(Type.f32)
.convert_layout(layout2)
.convert_layout(layout3)
.custom(custom_postprocess))) \
.build()
input_data = np.array([[-1, -2], [-3, -4]]).astype(np.int32)
expected_output = np.array([[2, 4], [4, 6]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_spatial_static_shape():
shape = [2, 2, 2]
parameter_a = ops.parameter(shape, dtype=np.int32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
layout = ov.Layout("CHW")
color_format = ColorFormat.RGB
function = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo()
.set_layout(layout)
.set_spatial_static_shape(2, 2)
.set_color_format(color_format, []))
.preprocess(PreProcessSteps()
.convert_element_type(Type.f32)
.mean([1., 2])
)
.network(InputNetworkInfo().set_layout(layout))
) \
.output(OutputInfo()
.tensor(OutputTensorInfo()
.set_layout(layout)
.set_element_type(Type.f32))
.network(OutputNetworkInfo().set_layout(layout))) \
.build()
input_data = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]).astype(np.int32)
expected_output = np.array([[[0, 1], [2, 3]], [[3, 4], [5, 6]]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
@pytest.mark.parametrize(
"algorithm, color_format1, color_format2, is_failing",
[(ResizeAlgorithm.RESIZE_LINEAR, ColorFormat.UNDEFINED, ColorFormat.BGR, True),
(ResizeAlgorithm.RESIZE_LINEAR, ColorFormat.RGB, ColorFormat.NV12_SINGLE_PLANE, True),
(ResizeAlgorithm.RESIZE_LINEAR, ColorFormat.RGB, ColorFormat.NV12_TWO_PLANES, True),
(ResizeAlgorithm.RESIZE_CUBIC, ColorFormat.RGB, ColorFormat.UNDEFINED, True),
(ResizeAlgorithm.RESIZE_CUBIC, ColorFormat.RGB, ColorFormat.BGR, False),
(ResizeAlgorithm.RESIZE_CUBIC, ColorFormat.BGR, ColorFormat.RGB, False),
(ResizeAlgorithm.RESIZE_NEAREST, ColorFormat.BGR, ColorFormat.NV12_SINGLE_PLANE, True),
(ResizeAlgorithm.RESIZE_NEAREST, ColorFormat.BGR, ColorFormat.NV12_TWO_PLANES, True),
(ResizeAlgorithm.RESIZE_NEAREST, ColorFormat.BGR, ColorFormat.UNDEFINED, True)])
def test_ngraph_preprocess_steps(algorithm, color_format1, color_format2, is_failing):
shape = [1, 1, 3, 3]
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
layout1 = ov.Layout("NCWH")
layout2 = ov.Layout("NCHW")
custom_processor = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo()
.set_layout(layout1)
.set_color_format(color_format1, []))
.preprocess(PreProcessSteps()
.mean(1.)
.resize(algorithm, 3, 3)
.convert_layout(layout2)
.convert_color(color_format2)))
if is_failing:
with pytest.raises(RuntimeError) as e:
function = custom_processor.build()
assert "is not convertible to" in str(e.value)
else:
function = custom_processor.build()
input_data = np.array([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]]).astype(np.float32)
expected_output = np.array([[[[0, 3, 6], [1, 4, 7], [2, 5, 8]]]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_postprocess_layout():
shape = [1, 1, 3, 3]
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
layout1 = ov.Layout("NCWH")
layout2 = ov.Layout("NCHW")
function = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo()
.set_layout(layout1))
.preprocess(PreProcessSteps()
.mean(1.)
.convert_layout(layout2)
.reverse_channels()
)
) \
.output(OutputInfo()
.postprocess(PostProcessSteps()
.convert_layout([0, 1, 2, 3]))) \
.build()
input_data = np.array([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]]).astype(np.float32)
expected_output = np.array([[[[0, 3, 6], [1, 4, 7], [2, 5, 8]]]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_reverse_channels():
shape = [1, 2, 2, 2]
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
layout1 = ov.Layout("NCWH")
function = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo()
.set_layout(layout1))
.preprocess(PreProcessSteps()
.mean(1.)
.reverse_channels()
)
) \
.build()
input_data = np.array([[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32)
expected_output = np.array([[[[4, 5], [6, 7]], [[0, 1], [2, 3]]]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_resize_algorithm():
shape = [1, 1, 3, 3]
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
resize_alg = ResizeAlgorithm.RESIZE_CUBIC
layout1 = ov.Layout("NCWH")
function = PrePostProcessor(function)\
.input(InputInfo()
.tensor(InputTensorInfo()
.set_layout(layout1))
.preprocess(PreProcessSteps()
.mean(1.)
.resize(resize_alg, 3, 3))
)\
.build()
input_data = np.array([[[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]]).astype(np.float32)
expected_output = np.array([[[[0, 1, 2], [3, 4, 5], [6, 7, 8]]]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data)
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_model():
model = bytes(b"""<net name="add_model" version="10">
<layers>
<layer id="0" name="x" type="Parameter" version="opset1">
<data element_type="i32" shape="2,2,2"/>
<output>
<port id="0" precision="FP32">
<dim>2</dim>
<dim>2</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="1" name="y" type="Parameter" version="opset1">
<data element_type="i32" shape="2,2,2"/>
<output>
<port id="0" precision="FP32">
<dim>2</dim>
<dim>2</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="2" name="sum" type="Add" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>2</dim>
<dim>2</dim>
</port>
<port id="1">
<dim>2</dim>
<dim>2</dim>
<dim>2</dim>
</port>
</input>
<output>
<port id="2" precision="FP32">
<dim>2</dim>
<dim>2</dim>
<dim>2</dim>
</port>
</output>
</layer>
<layer id="3" name="sum/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>2</dim>
<dim>2</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="2" to-port="0"/>
<edge from-layer="1" from-port="0" to-layer="2" to-port="1"/>
<edge from-layer="2" from-port="2" to-layer="3" to-port="0"/>
</edges>
</net>""")
core = Core()
function = core.read_model(model=model)
@custom_preprocess_function
def custom_preprocess(output: Output):
return ops.abs(output)
function = PrePostProcessor(function) \
.input(InputInfo(1)
.preprocess(PreProcessSteps()
.convert_element_type(Type.f32)
.scale(0.5)
)
) \
.input(InputInfo(0)
.preprocess(PreProcessSteps()
.convert_element_type(Type.f32)
.mean(5.))) \
.output(OutputInfo(0)
.postprocess(PostProcessSteps()
.custom(custom_preprocess))) \
.build()
input_data = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]).astype(np.float32)
expected_output = np.array([[[2, 1], [4, 7]], [[10, 13], [16, 19]]]).astype(np.float32)
runtime = get_runtime()
computation = runtime.computation(function)
output = computation(input_data, input_data)
assert np.equal(output, expected_output).all()