[OV2.0] Leftovers for Preprocessing/input/output (#7893)
* Pre-process: - Implicit conversions for element type and layout - 'convert_element_type' with default argument to network - Convert_element_type - don't add ops if dst and src types are same - Convert_layout - don't add ops if dst and src layouts are same - Custom step - use Output<Node> instead of shared_ptr<Node> - Support of addressing input by tensor name Post-process: - Avoid duplication of tensor names after post-processing * Fixed IE tests
This commit is contained in:
parent
d8c6591249
commit
2592856200
|
|
@ -181,9 +181,9 @@ static RefPreprocessParams custom_preprocessing() {
|
|||
res.function = []() {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 1, 1});
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo().preprocess(PreProcessSteps().custom([](const std::shared_ptr<Node>& node) {
|
||||
.input(InputInfo().preprocess(PreProcessSteps().custom([](const Output<Node>& node) {
|
||||
auto abs = std::make_shared<op::v0::Abs>(node);
|
||||
abs->set_friendly_name(node->get_friendly_name() + "/abs");
|
||||
abs->set_friendly_name(node.get_node_shared_ptr()->get_friendly_name() + "/abs");
|
||||
return abs;
|
||||
})))
|
||||
.build(f);
|
||||
|
|
@ -221,9 +221,9 @@ static RefPreprocessParams test_lvalue() {
|
|||
preprocessSteps.scale(2.f);
|
||||
preprocessSteps.mean({1.f, 2.f, 3.f});
|
||||
preprocessSteps.scale({2.f, 3.f, 4.f});
|
||||
preprocessSteps.custom([](const std::shared_ptr<Node> &node) {
|
||||
preprocessSteps.custom([](const Output<Node> &node) {
|
||||
auto abs = std::make_shared<op::v0::Abs>(node);
|
||||
abs->set_friendly_name(node->get_friendly_name() + "/abs");
|
||||
abs->set_friendly_name(node.get_node_shared_ptr()->get_friendly_name() + "/abs");
|
||||
return abs;
|
||||
});
|
||||
auto &same = preprocessSteps.convert_element_type(element::i8);
|
||||
|
|
@ -243,13 +243,22 @@ static RefPreprocessParams test_2_inputs_basic() {
|
|||
RefPreprocessParams res("test_2_inputs_basic");
|
||||
res.function = []() {
|
||||
auto f = create_2inputs(element::f32, Shape{1, 3, 1, 1});
|
||||
{ f = PrePostProcessor().input(InputInfo(1).preprocess(PreProcessSteps().mean(1.f).scale(2.0f))).build(f); }
|
||||
f = PrePostProcessor().input(InputInfo(0)
|
||||
.preprocess(
|
||||
PreProcessSteps()
|
||||
.mean(1.f)))
|
||||
.input(
|
||||
InputInfo("tensor_input2")
|
||||
.preprocess(PreProcessSteps()
|
||||
.mean(1.f)
|
||||
.scale(2.0f)))
|
||||
.build(f);
|
||||
return f;
|
||||
};
|
||||
|
||||
res.inputs.emplace_back(Shape{1, 3, 1, 1}, element::f32, std::vector<float>{3., 5., 7.});
|
||||
res.inputs.emplace_back(Shape{1, 3, 1, 1}, element::f32, std::vector<float>{3., 5., 7.});
|
||||
res.expected.emplace_back(Shape{1, 3, 1, 1}, element::f32, std::vector<float>{3., 5., 7.});
|
||||
res.expected.emplace_back(Shape{1, 3, 1, 1}, element::f32, std::vector<float>{2., 4., 6.});
|
||||
res.expected.emplace_back(Shape{1, 3, 1, 1}, element::f32, std::vector<float>{1., 2., 3.});
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,17 +152,17 @@ TEST_F(RTInfoDeserialization, NodeV10) {
|
|||
param->set_friendly_name("in1");
|
||||
param->get_output_tensor(0).set_names({"input_tensor"});
|
||||
|
||||
// TODO: avoid relying on internal pre-processing implementation (exact operation and exact names)
|
||||
auto convert_param = std::make_shared<opset8::Convert>(param, ngraph::element::f16);
|
||||
convert_param->get_output_tensor(0).set_names({"input_tensor/convert_element_type"});
|
||||
|
||||
auto round = std::make_shared<opset8::Round>(convert_param,
|
||||
ngraph::opset8::Round::RoundMode::HALF_TO_EVEN);
|
||||
round->set_friendly_name("Round");
|
||||
// TODO: why it has this name?
|
||||
round->get_output_tensor(0).set_names({"output_tensor"});
|
||||
round->get_output_tensor(0).set_names({"output_tensor/post_convert_element_type"});
|
||||
|
||||
auto convert_result = std::make_shared<opset8::Convert>(round, type);
|
||||
convert_result->set_friendly_name("Round/convert_element_type");
|
||||
convert_result->set_friendly_name("Round/post_convert_element_type");
|
||||
convert_result->get_output_tensor(0).set_names({"output_tensor"});
|
||||
|
||||
auto result = std::make_shared<opset8::Result>(convert_result);
|
||||
|
|
@ -310,11 +310,11 @@ TEST_F(RTInfoDeserialization, InputAndOutputV10) {
|
|||
|
||||
auto sum = std::make_shared<opset8::Add>(param, param);
|
||||
sum->set_friendly_name("sum");
|
||||
// TODO: why it has this name?
|
||||
sum->get_output_tensor(0).set_names({"output_tensor"});
|
||||
// TODO: avoid relying on internal post-processing implementation (exact operation and exact names)
|
||||
sum->get_output_tensor(0).set_names({"output_tensor/post_convert_element_type"});
|
||||
|
||||
auto convert_result = std::make_shared<opset8::Convert>(sum, ngraph::element::i32);
|
||||
convert_result->set_friendly_name("sum/convert_element_type");
|
||||
convert_result->set_friendly_name("sum/post_convert_element_type");
|
||||
convert_result->get_output_tensor(0).set_names({"output_tensor"});
|
||||
|
||||
auto result = std::make_shared<opset8::Result>(convert_result);
|
||||
|
|
@ -471,6 +471,7 @@ TEST_F(RTInfoDeserialization, NodeV11) {
|
|||
param->set_friendly_name("in1");
|
||||
param->get_output_tensor(0).set_names({"input_tensor"});
|
||||
|
||||
// TODO: avoid relying on internal pre-processing implementation (exact operations and exact names)
|
||||
auto convert_param = std::make_shared<opset8::Convert>(param, ngraph::element::f32);
|
||||
convert_param->set_friendly_name("in1/convert_element_type");
|
||||
convert_param->get_output_tensor(0).set_names({"input_tensor/convert_element_type"});
|
||||
|
|
@ -484,18 +485,17 @@ TEST_F(RTInfoDeserialization, NodeV11) {
|
|||
auto round = std::make_shared<opset8::Round>(transpose_param,
|
||||
ngraph::opset8::Round::RoundMode::HALF_TO_EVEN);
|
||||
round->set_friendly_name("Round");
|
||||
// TODO: why it has this name?
|
||||
round->get_output_tensor(0).set_names({"output_tensor"});
|
||||
round->get_output_tensor(0).set_names({"output_tensor/post_convert_layout"});
|
||||
round->get_rt_info()[VariantWrapper<ngraph::FusedNames>::get_type_info_static()] =
|
||||
std::make_shared<VariantWrapper<ngraph::FusedNames>>(ngraph::FusedNames("Round1,Round2"));
|
||||
|
||||
auto constant_result = std::make_shared<opset8::Constant>(ngraph::element::i64, ngraph::Shape{4},
|
||||
std::vector<int64_t>{0, 3, 1, 2});
|
||||
auto transpose_result = std::make_shared<opset8::Transpose>(round, constant_result);
|
||||
transpose_result->set_friendly_name("Round/convert_layout");
|
||||
transpose_result->set_friendly_name("Round/post_convert_layout");
|
||||
|
||||
auto convert_result = std::make_shared<opset8::Convert>(transpose_result, type);
|
||||
convert_result->set_friendly_name("Round/convert_layout/convert_element_type");
|
||||
convert_result->set_friendly_name("Round/post_convert_layout/post_convert_element_type");
|
||||
convert_result->get_output_tensor(0).set_names({"output_tensor"});
|
||||
|
||||
auto result = std::make_shared<opset8::Result>(convert_result);
|
||||
|
|
|
|||
|
|
@ -130,9 +130,9 @@ inline std::shared_ptr<Function> custom_preprocessing() {
|
|||
using namespace ov::preprocess;
|
||||
auto function = create_preprocess_1input(element::i32, Shape{3, 4, 10, 20});
|
||||
function = PrePostProcessor()
|
||||
.input(InputInfo().preprocess(PreProcessSteps().custom([](const std::shared_ptr<Node>& node) {
|
||||
.input(InputInfo().preprocess(PreProcessSteps().custom([](const Output<Node>& node) {
|
||||
auto abs = std::make_shared<op::v0::Abs>(node);
|
||||
abs->set_friendly_name(node->get_friendly_name() + "/abs");
|
||||
abs->set_friendly_name(node.get_node_shared_ptr()->get_friendly_name() + "/abs");
|
||||
return abs;
|
||||
})))
|
||||
.build(function);
|
||||
|
|
@ -164,9 +164,9 @@ inline std::shared_ptr<Function> lvalues_multiple_ops() {
|
|||
preprocessSteps.scale(2.f);
|
||||
preprocessSteps.mean({1.1f, 2.2f, 3.3f});
|
||||
preprocessSteps.scale({2.f, 3.f, 4.f});
|
||||
preprocessSteps.custom([](const std::shared_ptr<Node>& node) {
|
||||
preprocessSteps.custom([](const Output<Node>& node) {
|
||||
auto abs = std::make_shared<op::v0::Abs>(node);
|
||||
abs->set_friendly_name(node->get_friendly_name() + "/abs");
|
||||
abs->set_friendly_name(node.get_node_shared_ptr()->get_friendly_name() + "/abs");
|
||||
return abs;
|
||||
});
|
||||
auto& same = preprocessSteps.convert_element_type(element::u8);
|
||||
|
|
|
|||
|
|
@ -31,10 +31,15 @@ public:
|
|||
/// \brief Empty constructor. Should be used only if network will have only one input
|
||||
InputInfo();
|
||||
|
||||
/// \brief Information about info for particular input index of model
|
||||
/// \brief Constructor for particular input index of model
|
||||
///
|
||||
/// \param input_index Index to address specified input parameter of model
|
||||
InputInfo(size_t input_index);
|
||||
explicit InputInfo(size_t input_index);
|
||||
|
||||
/// \brief Constructor for particular output of model addressed by it's input name
|
||||
///
|
||||
/// \param input_tensor_name Name of input tensor name
|
||||
explicit InputInfo(const std::string& input_tensor_name);
|
||||
|
||||
/// \brief Default move constructor
|
||||
InputInfo(InputInfo&&) noexcept;
|
||||
|
|
|
|||
|
|
@ -34,12 +34,12 @@ public:
|
|||
/// \brief Constructor for particular output index of model
|
||||
///
|
||||
/// \param output_index Index to address specified output parameter of model
|
||||
OutputInfo(size_t output_index);
|
||||
explicit OutputInfo(size_t output_index);
|
||||
|
||||
/// \brief Constructor for particular output of model addressed by it's output name
|
||||
///
|
||||
/// \param output_tensor_name Name of output tensor name
|
||||
OutputInfo(const std::string& output_tensor_name);
|
||||
explicit OutputInfo(const std::string& output_tensor_name);
|
||||
|
||||
/// \brief Default move constructor
|
||||
OutputInfo(OutputInfo&&) noexcept;
|
||||
|
|
|
|||
|
|
@ -48,14 +48,14 @@ public:
|
|||
/// \param type Desired type of input.
|
||||
///
|
||||
/// \return Reference to 'this' to allow chaining with other calls in a builder-like manner
|
||||
PreProcessSteps& convert_element_type(const ov::element::Type& type) &;
|
||||
PreProcessSteps& convert_element_type(const ov::element::Type& type = {}) &;
|
||||
|
||||
/// \brief Add convert element type preprocess operation - Rvalue version
|
||||
///
|
||||
/// \param type Desired type of input.
|
||||
///
|
||||
/// \return Rvalue reference to 'this' to allow chaining with other calls in a builder-like manner
|
||||
PreProcessSteps&& convert_element_type(const ov::element::Type& type) &&;
|
||||
PreProcessSteps&& convert_element_type(const ov::element::Type& type = {}) &&;
|
||||
|
||||
/// \brief Converts color format for user's input tensor. Requires source color format to be specified by
|
||||
/// InputTensorInfo::set_color_format.
|
||||
|
|
@ -141,10 +141,10 @@ public:
|
|||
/// produces one output node. For more advanced cases, client's code can use transformation passes over ov::Function
|
||||
/// directly
|
||||
///
|
||||
/// \param node Input node for custom preprocessing operation
|
||||
/// \param node Input node for custom preprocessing operation (output of previous preprocessing operation)
|
||||
///
|
||||
/// \return New node after applying custom preprocessing operation
|
||||
using CustomPreprocessOp = std::function<std::shared_ptr<ov::Node>(const std::shared_ptr<ov::Node>& node)>;
|
||||
using CustomPreprocessOp = std::function<Output<Node>(const Output<Node>& node)>;
|
||||
|
||||
/// \brief Add custom preprocess operation - Lvalue version
|
||||
/// Client application can specify callback function for custom action
|
||||
|
|
|
|||
|
|
@ -243,6 +243,9 @@ namespace layout {
|
|||
std::vector<int64_t> find_permutation(const Layout& src_layout, const Rank& rank, const Layout& dst) {
|
||||
// Basic implementation so far, can support partially-specified layouts later (shape rank will be needed for dynamic
|
||||
// layouts)
|
||||
if (src_layout == dst) {
|
||||
return {}; // No permutation is needed
|
||||
}
|
||||
OPENVINO_ASSERT(!src_layout.m_dynamic && !dst.m_dynamic, "Conversion is not supported for dynamic layouts");
|
||||
OPENVINO_ASSERT(src_layout.m_left_size == src_layout.m_left_size,
|
||||
"Conversion is not supported for layouts with different sizes");
|
||||
|
|
|
|||
|
|
@ -155,11 +155,16 @@ class OutputNetworkInfo::OutputNetworkInfoImpl : public NetworkInfoImpl {};
|
|||
struct InputInfo::InputInfoImpl {
|
||||
InputInfoImpl() = default;
|
||||
explicit InputInfoImpl(size_t idx) : m_has_index(true), m_index(idx) {}
|
||||
explicit InputInfoImpl(std::string name) : m_has_name(true), m_name(std::move(name)) {}
|
||||
|
||||
bool has_index() const {
|
||||
return m_has_index;
|
||||
}
|
||||
|
||||
bool has_name() const {
|
||||
return m_has_name;
|
||||
}
|
||||
|
||||
void create_tensor_data(const element::Type& type, const Layout& layout) {
|
||||
auto data = std::unique_ptr<InputTensorInfo::InputTensorInfoImpl>(new InputTensorInfo::InputTensorInfoImpl());
|
||||
data->set_layout(layout);
|
||||
|
|
@ -169,6 +174,8 @@ struct InputInfo::InputInfoImpl {
|
|||
|
||||
bool m_has_index = false;
|
||||
size_t m_index = 0;
|
||||
bool m_has_name = false;
|
||||
std::string m_name;
|
||||
std::unique_ptr<InputTensorInfo::InputTensorInfoImpl> m_tensor_data;
|
||||
std::unique_ptr<PreProcessSteps::PreProcessStepsImpl> m_preprocess;
|
||||
std::unique_ptr<InputNetworkInfo::InputNetworkInfoImpl> m_network_data;
|
||||
|
|
@ -206,6 +213,8 @@ struct OutputInfo::OutputInfoImpl {
|
|||
//-------------- InputInfo ------------------
|
||||
InputInfo::InputInfo() : m_impl(std::unique_ptr<InputInfoImpl>(new InputInfoImpl)) {}
|
||||
InputInfo::InputInfo(size_t input_index) : m_impl(std::unique_ptr<InputInfoImpl>(new InputInfoImpl(input_index))) {}
|
||||
InputInfo::InputInfo(const std::string& input_tensor_name)
|
||||
: m_impl(std::unique_ptr<InputInfoImpl>(new InputInfoImpl(input_tensor_name))) {}
|
||||
InputInfo::InputInfo(InputInfo&&) noexcept = default;
|
||||
InputInfo& InputInfo::operator=(InputInfo&&) noexcept = default;
|
||||
InputInfo::~InputInfo() = default;
|
||||
|
|
@ -318,18 +327,16 @@ std::shared_ptr<Function> PrePostProcessor::build(const std::shared_ptr<Function
|
|||
bool tensor_data_updated = false;
|
||||
for (const auto& input : m_impl->in_contexts) {
|
||||
std::shared_ptr<op::v0::Parameter> param;
|
||||
Output<Node> node;
|
||||
OPENVINO_ASSERT(input, "Internal error: Invalid preprocessing input, please report a problem");
|
||||
if (input->has_index()) {
|
||||
param = function->get_parameters().at(input->m_index);
|
||||
node = function->input(input->m_index);
|
||||
} else if (input->has_name()) {
|
||||
node = function->input(input->m_name);
|
||||
} else {
|
||||
// Default case
|
||||
OPENVINO_ASSERT(function->get_parameters().size() == 1,
|
||||
std::string("Preprocessing info expects having 1 input, however function has ") +
|
||||
std::to_string(function->get_parameters().size()) +
|
||||
" inputs. Please use ov::preprocess::InputInfo constructor specifying "
|
||||
"particular input instead of default one");
|
||||
param = function->get_parameters().front();
|
||||
node = function->input();
|
||||
}
|
||||
param = std::dynamic_pointer_cast<op::v0::Parameter>(node.get_node_shared_ptr());
|
||||
// Set parameter layout from 'network' information
|
||||
if (input->m_network_data && input->m_network_data->is_layout_set() && param->get_layout().empty()) {
|
||||
param->set_layout(input->m_network_data->get_layout());
|
||||
|
|
@ -362,11 +369,13 @@ std::shared_ptr<Function> PrePostProcessor::build(const std::shared_ptr<Function
|
|||
// Find transpose between network and tensor layouts and update tensor shape
|
||||
auto net_to_tensor =
|
||||
layout::find_permutation(param->get_layout(), net_shape.rank(), input->m_tensor_data->get_layout());
|
||||
std::vector<ov::Dimension> dims(new_param_shape.size());
|
||||
std::transform(net_to_tensor.begin(), net_to_tensor.end(), dims.begin(), [&](int64_t v) {
|
||||
return new_param_shape[v];
|
||||
});
|
||||
new_param_shape = PartialShape(dims);
|
||||
if (!net_to_tensor.empty()) {
|
||||
std::vector<ov::Dimension> dims(new_param_shape.size());
|
||||
std::transform(net_to_tensor.begin(), net_to_tensor.end(), dims.begin(), [&](int64_t v) {
|
||||
return new_param_shape[v];
|
||||
});
|
||||
new_param_shape = PartialShape(dims);
|
||||
}
|
||||
}
|
||||
if (input->m_tensor_data->is_spatial_shape_set()) {
|
||||
auto height_idx = get_and_check_height_idx(input->m_tensor_data->get_layout(), new_param_shape);
|
||||
|
|
@ -382,7 +391,7 @@ std::shared_ptr<Function> PrePostProcessor::build(const std::shared_ptr<Function
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ov::Node>> nodes;
|
||||
std::vector<Output<Node>> nodes;
|
||||
std::vector<std::shared_ptr<op::v0::Parameter>> new_params;
|
||||
|
||||
// Create separate parameter for each plane. Shape and friendly name is based on color format
|
||||
|
|
@ -401,20 +410,21 @@ std::shared_ptr<Function> PrePostProcessor::build(const std::shared_ptr<Function
|
|||
plane_param->set_layout(input->m_tensor_data->get_layout());
|
||||
}
|
||||
new_params.push_back(plane_param);
|
||||
nodes.push_back(plane_param);
|
||||
nodes.emplace_back(plane_param);
|
||||
}
|
||||
|
||||
PreprocessingContext context(input->m_tensor_data->get_layout());
|
||||
context.color_format() = input->m_tensor_data->get_color_format();
|
||||
context.target_layout() = param->get_layout();
|
||||
context.network_shape() = param->get_partial_shape();
|
||||
context.target_element_type() = param->get_element_type();
|
||||
|
||||
// 2. Apply preprocessing
|
||||
if (input->m_preprocess) {
|
||||
for (const auto& action : input->m_preprocess->actions()) {
|
||||
auto node = std::get<0>(action)(nodes, function, context);
|
||||
nodes = {node};
|
||||
tensor_data_updated |= std::get<1>(action);
|
||||
auto action_result = action(nodes, function, context);
|
||||
nodes = std::get<0>(action_result);
|
||||
tensor_data_updated |= std::get<1>(action_result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -428,13 +438,27 @@ std::shared_ptr<Function> PrePostProcessor::build(const std::shared_ptr<Function
|
|||
"to convert current color format '",
|
||||
color_format_name(context.color_format()),
|
||||
"'to RGB/BGR");
|
||||
|
||||
// Implicit: Convert element type + layout to user's tensor implicitly
|
||||
PreStepsList implicit_steps;
|
||||
implicit_steps.add_convert_impl(param->get_element_type());
|
||||
if (!context.target_layout().empty()) {
|
||||
implicit_steps.add_convert_layout_impl(context.target_layout());
|
||||
}
|
||||
|
||||
for (const auto& action : implicit_steps.actions()) {
|
||||
auto action_result = action(nodes, function, context);
|
||||
nodes = std::get<0>(action_result);
|
||||
}
|
||||
|
||||
auto node = nodes[0];
|
||||
// Check final type
|
||||
OPENVINO_ASSERT(node->get_element_type() == param->get_element_type(),
|
||||
std::string("Element type after preprocessing {") + node->get_element_type().c_type_string() +
|
||||
std::string("} doesn't match with network element type {") +
|
||||
param->get_element_type().c_type_string() +
|
||||
"}. Please add 'convert_element_type' explicitly");
|
||||
|
||||
// Check final shape
|
||||
OPENVINO_ASSERT(node.get_partial_shape().refines(param->get_partial_shape()),
|
||||
"Resulting shape '",
|
||||
node.get_partial_shape(),
|
||||
"' after preprocessing is not aligned with original parameter's shape: ",
|
||||
param->get_partial_shape());
|
||||
|
||||
// Replace parameter
|
||||
for (auto consumer : consumers) {
|
||||
|
|
@ -462,6 +486,7 @@ std::shared_ptr<Function> PrePostProcessor::build(const std::shared_ptr<Function
|
|||
} else {
|
||||
node = function->output();
|
||||
}
|
||||
auto start_out_node_names = node.get_tensor().get_names();
|
||||
result = std::dynamic_pointer_cast<op::v0::Result>(node.get_node_shared_ptr());
|
||||
// Set result layout from 'network' information
|
||||
if (output->m_network_data && output->m_network_data->is_layout_set() && result->get_layout().empty()) {
|
||||
|
|
@ -506,7 +531,8 @@ std::shared_ptr<Function> PrePostProcessor::build(const std::shared_ptr<Function
|
|||
if (!context.layout().empty()) {
|
||||
new_result->set_layout(context.layout());
|
||||
}
|
||||
new_result->get_input_tensor(0).set_names(result->get_input_tensor(0).get_names());
|
||||
node.get_tensor().set_names(start_out_node_names);
|
||||
|
||||
function->add_results({new_result});
|
||||
function->remove_result(result);
|
||||
}
|
||||
|
|
@ -694,31 +720,27 @@ PreProcessSteps&& PreProcessSteps::convert_color(const ov::preprocess::ColorForm
|
|||
|
||||
PreProcessSteps& PreProcessSteps::custom(const CustomPreprocessOp& preprocess_cb) & {
|
||||
// 'true' indicates that custom preprocessing step will trigger validate_and_infer_types
|
||||
m_impl->actions().emplace_back(std::make_tuple(
|
||||
[preprocess_cb](const std::vector<std::shared_ptr<ov::Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>&,
|
||||
PreprocessingContext&) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply custom preprocessing step for multi-plane input. Suggesting to convert "
|
||||
"current image to RGB/BGR color format using 'convert_color'");
|
||||
return {preprocess_cb(nodes[0])};
|
||||
},
|
||||
true));
|
||||
m_impl->actions().emplace_back([preprocess_cb](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>&,
|
||||
PreprocessingContext&) {
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply custom preprocessing step for multi-plane input. Suggesting to convert "
|
||||
"current image to RGB/BGR color format using 'convert_color'");
|
||||
return std::make_tuple(std::vector<Output<Node>>{preprocess_cb(nodes[0])}, true);
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
PreProcessSteps&& PreProcessSteps::custom(const CustomPreprocessOp& preprocess_cb) && {
|
||||
// 'true' indicates that custom preprocessing step will trigger validate_and_infer_types
|
||||
m_impl->actions().emplace_back(std::make_tuple(
|
||||
[preprocess_cb](const std::vector<std::shared_ptr<ov::Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>&,
|
||||
PreprocessingContext&) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply custom preprocessing step for multi-plane input. Suggesting to convert "
|
||||
"current image to RGB/BGR color format using 'convert_color'");
|
||||
return {preprocess_cb(nodes[0])};
|
||||
},
|
||||
true));
|
||||
m_impl->actions().emplace_back([preprocess_cb](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>&,
|
||||
PreprocessingContext&) {
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply custom preprocessing step for multi-plane input. Suggesting to convert "
|
||||
"current image to RGB/BGR color format using 'convert_color'");
|
||||
return std::make_tuple(std::vector<Output<Node>>{preprocess_cb(nodes[0])}, true);
|
||||
});
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,10 @@
|
|||
namespace ov {
|
||||
namespace preprocess {
|
||||
|
||||
static Shape construct_mean_scale_shape(const std::shared_ptr<Node>& node,
|
||||
static Shape construct_mean_scale_shape(const Output<Node>& node,
|
||||
size_t values_size,
|
||||
const PreprocessingContext& context) {
|
||||
// TODO: support also Mean/Scale image case
|
||||
auto node_shape = node->get_output_partial_shape(0);
|
||||
auto node_shape = node.get_partial_shape();
|
||||
auto node_rank = node_shape.rank();
|
||||
auto channels_index = get_and_check_channels_idx(context.layout(), node_shape);
|
||||
std::vector<std::size_t> v(node_rank.get_length(), 1);
|
||||
|
|
@ -32,218 +31,216 @@ static Shape construct_mean_scale_shape(const std::shared_ptr<Node>& node,
|
|||
return {v};
|
||||
}
|
||||
|
||||
void PreProcessSteps::PreProcessStepsImpl::add_scale_impl(const std::vector<float>& values) {
|
||||
m_actions.emplace_back(std::make_tuple(
|
||||
[values](const std::vector<std::shared_ptr<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& context) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't apply scale preprocessing for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply scale preprocessing for multi-plane input. Suggesting to convert current "
|
||||
"image to RGB/BGR color format using 'convert_color'");
|
||||
Shape shape;
|
||||
if (values.size() == 1) {
|
||||
shape = Shape{1};
|
||||
void PreStepsList::add_scale_impl(const std::vector<float>& values) {
|
||||
m_actions.emplace_back([values](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& context) -> std::tuple<std::vector<Output<Node>>, bool> {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't apply scale preprocessing for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply scale preprocessing for multi-plane input. Suggesting to convert current "
|
||||
"image to RGB/BGR color format using 'convert_color'");
|
||||
Shape shape;
|
||||
if (values.size() == 1) {
|
||||
shape = Shape{1};
|
||||
} else {
|
||||
shape = construct_mean_scale_shape(nodes[0].get_node_shared_ptr(), values.size(), context);
|
||||
}
|
||||
auto constant = op::v0::Constant::create(element::f32, shape, values);
|
||||
inherit_friendly_names(function, nodes[0].get_node_shared_ptr(), constant, "/scale/Divide_Factor");
|
||||
|
||||
auto new_op = std::make_shared<op::v1::Divide>(nodes[0], constant);
|
||||
inherit_friendly_names(function, nodes[0].get_node_shared_ptr(), new_op, "/scale/Divide");
|
||||
return std::make_tuple(std::vector<Output<Node>>{new_op}, false);
|
||||
});
|
||||
}
|
||||
|
||||
void PreStepsList::add_mean_impl(const std::vector<float>& values) {
|
||||
m_actions.emplace_back([values](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& context) {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't apply mean preprocessing for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply scale preprocessing for multi-plane input. Suggesting to convert current "
|
||||
"image to RGB/BGR color format using 'convert_color'");
|
||||
Shape shape;
|
||||
if (values.size() == 1) {
|
||||
shape = Shape{1};
|
||||
} else {
|
||||
shape = construct_mean_scale_shape(nodes[0], values.size(), context);
|
||||
}
|
||||
auto constant = op::v0::Constant::create(element::f32, shape, values);
|
||||
inherit_friendly_names(function, nodes[0], constant, "/mean/Mean_Const");
|
||||
|
||||
auto new_op = std::make_shared<op::v1::Subtract>(nodes[0], constant);
|
||||
inherit_friendly_names(function, nodes[0], new_op, "/mean/Subtract");
|
||||
return std::make_tuple(std::vector<Output<Node>>{new_op}, false);
|
||||
});
|
||||
}
|
||||
|
||||
void PreStepsList::add_convert_impl(const element::Type& type) {
|
||||
m_actions.emplace_back([type](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<Function>& function,
|
||||
PreprocessingContext& ctxt) {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't set element type for empty input.");
|
||||
std::vector<Output<Node>> res;
|
||||
element::Type t = type;
|
||||
if (t == element::Type{}) {
|
||||
t = ctxt.target_element_type();
|
||||
}
|
||||
bool convert_added = false;
|
||||
for (const auto& node : nodes) {
|
||||
OPENVINO_ASSERT(node.get_element_type().is_static(),
|
||||
"Can't insert 'convert_element_type' for dynamic source tensor type.");
|
||||
if (t != node.get_element_type()) {
|
||||
auto convert = std::make_shared<op::v0::Convert>(node, t);
|
||||
inherit_friendly_names(function, node, convert, "/convert_element_type");
|
||||
res.emplace_back(convert);
|
||||
convert_added = true;
|
||||
} else {
|
||||
shape = construct_mean_scale_shape(nodes[0], values.size(), context);
|
||||
res.emplace_back(node);
|
||||
}
|
||||
auto constant = op::v0::Constant::create(element::f32, shape, values);
|
||||
inherit_friendly_names(function, nodes[0], constant, "/scale/Divide_Factor");
|
||||
|
||||
auto new_op = std::make_shared<op::v1::Divide>(nodes[0], constant);
|
||||
inherit_friendly_names(function, nodes[0], new_op, "/scale/Divide");
|
||||
return {new_op};
|
||||
},
|
||||
false));
|
||||
}
|
||||
return std::make_tuple(res, convert_added);
|
||||
});
|
||||
}
|
||||
|
||||
void PreProcessSteps::PreProcessStepsImpl::add_mean_impl(const std::vector<float>& values) {
|
||||
m_actions.emplace_back(std::make_tuple(
|
||||
[values](const std::vector<std::shared_ptr<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& context) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't apply mean preprocessing for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't apply scale preprocessing for multi-plane input. Suggesting to convert current "
|
||||
"image to RGB/BGR color format using 'convert_color'");
|
||||
Shape shape;
|
||||
if (values.size() == 1) {
|
||||
shape = Shape{1};
|
||||
} else {
|
||||
shape = construct_mean_scale_shape(nodes[0], values.size(), context);
|
||||
}
|
||||
auto constant = op::v0::Constant::create(element::f32, shape, values);
|
||||
inherit_friendly_names(function, nodes[0], constant, "/mean/Mean_Const");
|
||||
|
||||
auto new_op = std::make_shared<op::v1::Subtract>(nodes[0], constant);
|
||||
inherit_friendly_names(function, nodes[0], new_op, "/mean/Subtract");
|
||||
return {new_op};
|
||||
},
|
||||
false));
|
||||
}
|
||||
|
||||
void PreProcessSteps::PreProcessStepsImpl::add_convert_impl(const ov::element::Type& type) {
|
||||
m_actions.emplace_back(std::make_tuple(
|
||||
[type](const std::vector<std::shared_ptr<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext&) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't set element type for empty input.");
|
||||
std::vector<std::shared_ptr<ov::Node>> res;
|
||||
for (const auto& node : nodes) {
|
||||
OPENVINO_ASSERT(node->get_element_type().is_static(),
|
||||
"Can't insert 'convert_element_type' for dynamic source tensor type.");
|
||||
if (node->get_element_type() != type) {
|
||||
auto convert = std::make_shared<op::v0::Convert>(node, type);
|
||||
inherit_friendly_names(function, node, convert, "/convert_element_type");
|
||||
res.emplace_back(convert);
|
||||
} else {
|
||||
res.emplace_back(node);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
true));
|
||||
}
|
||||
|
||||
void PreProcessSteps::PreProcessStepsImpl::add_resize_impl(ResizeAlgorithm alg, int dst_height, int dst_width) {
|
||||
void PreStepsList::add_resize_impl(ResizeAlgorithm alg, int dst_height, int dst_width) {
|
||||
using InterpolateMode = op::v4::Interpolate::InterpolateMode;
|
||||
m_actions.emplace_back(std::make_tuple(
|
||||
[alg, dst_width, dst_height](const std::vector<std::shared_ptr<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& ctxt) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't add resize for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't resize multi-plane input. Suggesting to convert current image to "
|
||||
"RGB/BGR color format using 'PreProcessSteps::convert_color'");
|
||||
auto to_mode = [](ResizeAlgorithm alg) -> InterpolateMode {
|
||||
switch (alg) {
|
||||
case ResizeAlgorithm::RESIZE_NEAREST:
|
||||
return InterpolateMode::NEAREST;
|
||||
case ResizeAlgorithm::RESIZE_CUBIC:
|
||||
return InterpolateMode::CUBIC;
|
||||
case ResizeAlgorithm::RESIZE_LINEAR:
|
||||
default:
|
||||
return InterpolateMode::LINEAR;
|
||||
}
|
||||
};
|
||||
auto node = nodes.front();
|
||||
auto layout = ctxt.layout();
|
||||
OPENVINO_ASSERT(ov::layout::has_height(layout) && ov::layout::has_width(layout),
|
||||
"Can't add resize for layout without W/H specified. Use 'set_layout' API to define layout "
|
||||
"of image data, like `NCHW`");
|
||||
auto node_rank = node->get_output_partial_shape(0).rank();
|
||||
OPENVINO_ASSERT(node_rank.is_static(), "Resize operation is not supported for fully dynamic shape");
|
||||
|
||||
auto height_idx = static_cast<int64_t>(get_and_check_height_idx(layout, node->get_output_partial_shape(0)));
|
||||
auto width_idx = static_cast<int64_t>(get_and_check_width_idx(layout, node->get_output_partial_shape(0)));
|
||||
if (dst_height < 0 || dst_width < 0) {
|
||||
OPENVINO_ASSERT(ctxt.network_shape().rank().is_static(),
|
||||
"Resize is not fully specified while target network shape is dynamic");
|
||||
m_actions.emplace_back([alg, dst_width, dst_height](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<Function>& function,
|
||||
PreprocessingContext& ctxt) {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't add resize for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't resize multi-plane input. Suggesting to convert current image to "
|
||||
"RGB/BGR color format using 'PreProcessSteps::convert_color'");
|
||||
auto to_mode = [](ResizeAlgorithm alg) -> InterpolateMode {
|
||||
switch (alg) {
|
||||
case ResizeAlgorithm::RESIZE_NEAREST:
|
||||
return InterpolateMode::NEAREST;
|
||||
case ResizeAlgorithm::RESIZE_CUBIC:
|
||||
return InterpolateMode::CUBIC;
|
||||
case ResizeAlgorithm::RESIZE_LINEAR:
|
||||
default:
|
||||
return InterpolateMode::LINEAR;
|
||||
}
|
||||
int new_image_width = dst_width < 0 ? static_cast<int>(ctxt.get_network_width_for_resize()) : dst_width;
|
||||
int new_image_height = dst_height < 0 ? static_cast<int>(ctxt.get_network_height_for_resize()) : dst_height;
|
||||
};
|
||||
auto node = nodes.front();
|
||||
auto layout = ctxt.layout();
|
||||
OPENVINO_ASSERT(ov::layout::has_height(layout) && ov::layout::has_width(layout),
|
||||
"Can't add resize for layout without W/H specified. Use 'set_layout' API to define layout "
|
||||
"of image data, like `NCHW`");
|
||||
auto node_rank = node.get_partial_shape().rank();
|
||||
OPENVINO_ASSERT(node_rank.is_static(), "Resize operation is not supported for fully dynamic shape");
|
||||
|
||||
auto target_spatial_shape =
|
||||
op::v0::Constant::create<int64_t>(element::i64, Shape{2}, {new_image_height, new_image_width});
|
||||
auto scales = op::v0::Constant::create<float>(element::f32, Shape{2}, {1, 1});
|
||||
// In future consider replacing this to set of new OV operations like `getDimByName(node, "H")`
|
||||
// This is to allow specifying layout on 'evaluation' stage
|
||||
auto axes = op::v0::Constant::create<int64_t>(element::i64, Shape{2}, {height_idx, width_idx});
|
||||
auto height_idx = static_cast<int64_t>(get_and_check_height_idx(layout, node.get_partial_shape()));
|
||||
auto width_idx = static_cast<int64_t>(get_and_check_width_idx(layout, node.get_partial_shape()));
|
||||
if (dst_height < 0 || dst_width < 0) {
|
||||
OPENVINO_ASSERT(ctxt.network_shape().rank().is_static(),
|
||||
"Resize is not fully specified while target network shape is dynamic");
|
||||
}
|
||||
int new_image_width = dst_width < 0 ? static_cast<int>(ctxt.get_network_width_for_resize()) : dst_width;
|
||||
int new_image_height = dst_height < 0 ? static_cast<int>(ctxt.get_network_height_for_resize()) : dst_height;
|
||||
|
||||
op::v4::Interpolate::InterpolateAttrs attrs(to_mode(alg),
|
||||
op::v4::Interpolate::ShapeCalcMode::SIZES,
|
||||
{0, 0},
|
||||
{0, 0});
|
||||
auto target_spatial_shape =
|
||||
op::v0::Constant::create<int64_t>(element::i64, Shape{2}, {new_image_height, new_image_width});
|
||||
auto scales = op::v0::Constant::create<float>(element::f32, Shape{2}, {1, 1});
|
||||
// In future consider replacing this to set of new OV operations like `getDimByName(node, "H")`
|
||||
// This is to allow specifying layout on 'evaluation' stage
|
||||
auto axes = op::v0::Constant::create<int64_t>(element::i64, Shape{2}, {height_idx, width_idx});
|
||||
|
||||
auto interp = std::make_shared<op::v4::Interpolate>(node, target_spatial_shape, scales, axes, attrs);
|
||||
inherit_friendly_names(function, nodes[0], interp, "/resize");
|
||||
return {interp};
|
||||
},
|
||||
true));
|
||||
op::v4::Interpolate::InterpolateAttrs attrs(to_mode(alg),
|
||||
op::v4::Interpolate::ShapeCalcMode::SIZES,
|
||||
{0, 0},
|
||||
{0, 0});
|
||||
|
||||
auto interp = std::make_shared<op::v4::Interpolate>(node, target_spatial_shape, scales, axes, attrs);
|
||||
inherit_friendly_names(function, nodes[0], interp, "/resize");
|
||||
return std::make_tuple(std::vector<Output<Node>>{interp}, true);
|
||||
});
|
||||
}
|
||||
|
||||
void PreProcessSteps::PreProcessStepsImpl::add_convert_layout_impl(const Layout& layout) {
|
||||
m_actions.emplace_back(std::make_tuple(
|
||||
[layout](const std::vector<std::shared_ptr<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& context) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't convert layout for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't convert layout for multi-plane input. Suggesting to convert current image to "
|
||||
"RGB/BGR color format using 'convert_color'");
|
||||
Layout dst_layout = layout.empty() ? context.target_layout() : layout;
|
||||
auto permutation =
|
||||
layout::find_permutation(context.layout(), nodes[0]->get_output_partial_shape(0).rank(), dst_layout);
|
||||
auto perm_constant =
|
||||
op::v0::Constant::create<int64_t>(element::i64, Shape{permutation.size()}, permutation);
|
||||
auto transpose = std::make_shared<op::v1::Transpose>(nodes[0], perm_constant);
|
||||
inherit_friendly_names(function, nodes[0], transpose, "/convert_layout");
|
||||
context.layout() = dst_layout; // Update context's current layout
|
||||
return {transpose};
|
||||
},
|
||||
true));
|
||||
void PreStepsList::add_convert_layout_impl(const Layout& layout) {
|
||||
m_actions.emplace_back([layout](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<Function>& function,
|
||||
PreprocessingContext& context) {
|
||||
OPENVINO_ASSERT(!nodes.empty(), "Internal error: Can't convert layout for empty input.");
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't convert layout for multi-plane input. Suggesting to convert current image to "
|
||||
"RGB/BGR color format using 'convert_color'");
|
||||
Layout dst_layout = layout.empty() ? context.target_layout() : layout;
|
||||
auto permutation = layout::find_permutation(context.layout(), nodes[0].get_partial_shape().rank(), dst_layout);
|
||||
if (permutation.empty()) {
|
||||
// No transpose is needed, just update layout
|
||||
if (!layout.empty()) {
|
||||
context.layout() = layout;
|
||||
}
|
||||
return std::make_tuple(nodes, false);
|
||||
}
|
||||
auto perm_constant = op::v0::Constant::create<int64_t>(element::i64, Shape{permutation.size()}, permutation);
|
||||
auto transpose = std::make_shared<op::v1::Transpose>(nodes[0], perm_constant);
|
||||
inherit_friendly_names(function, nodes[0], transpose, "/convert_layout");
|
||||
context.layout() = dst_layout; // Update context's current layout
|
||||
return std::make_tuple(std::vector<Output<Node>>{transpose}, true);
|
||||
});
|
||||
}
|
||||
|
||||
void PreProcessSteps::PreProcessStepsImpl::add_convert_color_impl(const ColorFormat& dst_format) {
|
||||
m_actions.emplace_back(std::make_tuple(
|
||||
[&, dst_format](const std::vector<std::shared_ptr<Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& context) -> std::vector<std::shared_ptr<ov::Node>> {
|
||||
if (context.color_format() == dst_format) {
|
||||
return nodes;
|
||||
void PreStepsList::add_convert_color_impl(const ColorFormat& dst_format) {
|
||||
m_actions.emplace_back([&, dst_format](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<Function>& function,
|
||||
PreprocessingContext& context) {
|
||||
if (context.color_format() == dst_format) {
|
||||
return std::make_tuple(nodes, false);
|
||||
}
|
||||
if (context.color_format() == ColorFormat::NV12_SINGLE_PLANE) {
|
||||
OPENVINO_ASSERT(nodes.size() == 1, "Internal error: single plane NV12 image can't have multiple inputs");
|
||||
std::shared_ptr<Node> convert;
|
||||
switch (dst_format) {
|
||||
case ColorFormat::RGB:
|
||||
convert = std::make_shared<op::v8::NV12toRGB>(nodes[0]);
|
||||
break;
|
||||
case ColorFormat::BGR:
|
||||
convert = std::make_shared<op::v8::NV12toBGR>(nodes[0]);
|
||||
break;
|
||||
default:
|
||||
OPENVINO_ASSERT(false,
|
||||
"Unsupported conversion from NV12 to '",
|
||||
color_format_name(dst_format),
|
||||
"' format:");
|
||||
}
|
||||
if (context.color_format() == ColorFormat::NV12_SINGLE_PLANE) {
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Internal error: single plane NV12 image can't have multiple inputs");
|
||||
std::shared_ptr<Node> convert;
|
||||
switch (dst_format) {
|
||||
case ColorFormat::RGB:
|
||||
convert = std::make_shared<op::v8::NV12toRGB>(nodes[0]);
|
||||
break;
|
||||
case ColorFormat::BGR:
|
||||
convert = std::make_shared<op::v8::NV12toBGR>(nodes[0]);
|
||||
break;
|
||||
default:
|
||||
OPENVINO_ASSERT(false,
|
||||
"Unsupported conversion from NV12 to '",
|
||||
color_format_name(dst_format),
|
||||
"' format:");
|
||||
}
|
||||
inherit_friendly_names(function, nodes[0], convert, "/convert_color_nv12_single");
|
||||
context.color_format() = dst_format;
|
||||
return {convert};
|
||||
} else if (context.color_format() == ColorFormat::NV12_TWO_PLANES) {
|
||||
OPENVINO_ASSERT(nodes.size() == 2, "Internal error: two-plane NV12 image must have exactly two inputs");
|
||||
std::shared_ptr<Node> convert;
|
||||
switch (dst_format) {
|
||||
case ColorFormat::RGB:
|
||||
convert = std::make_shared<op::v8::NV12toRGB>(nodes[0], nodes[1]);
|
||||
break;
|
||||
case ColorFormat::BGR:
|
||||
convert = std::make_shared<op::v8::NV12toBGR>(nodes[0], nodes[1]);
|
||||
break;
|
||||
default:
|
||||
OPENVINO_ASSERT(false,
|
||||
"Unsupported conversion from NV12 to '",
|
||||
color_format_name(dst_format),
|
||||
"' format:");
|
||||
}
|
||||
inherit_friendly_names(function, nodes[0], convert, "/convert_color_nv12_two_planes");
|
||||
context.color_format() = dst_format;
|
||||
return {convert};
|
||||
inherit_friendly_names(function, nodes[0], convert, "/convert_color_nv12_single");
|
||||
context.color_format() = dst_format;
|
||||
return std::make_tuple(std::vector<Output<Node>>{convert}, true);
|
||||
} else if (context.color_format() == ColorFormat::NV12_TWO_PLANES) {
|
||||
OPENVINO_ASSERT(nodes.size() == 2, "Internal error: two-plane NV12 image must have exactly two inputs");
|
||||
std::shared_ptr<Node> convert;
|
||||
switch (dst_format) {
|
||||
case ColorFormat::RGB:
|
||||
convert = std::make_shared<op::v8::NV12toRGB>(nodes[0], nodes[1]);
|
||||
break;
|
||||
case ColorFormat::BGR:
|
||||
convert = std::make_shared<op::v8::NV12toBGR>(nodes[0], nodes[1]);
|
||||
break;
|
||||
default:
|
||||
OPENVINO_ASSERT(false,
|
||||
"Unsupported conversion from NV12 to '",
|
||||
color_format_name(dst_format),
|
||||
"' format:");
|
||||
}
|
||||
OPENVINO_ASSERT(false,
|
||||
"Source color format '",
|
||||
color_format_name(context.color_format()),
|
||||
"' is not convertible to any other");
|
||||
},
|
||||
true));
|
||||
inherit_friendly_names(function, nodes[0], convert, "/convert_color_nv12_two_planes");
|
||||
context.color_format() = dst_format;
|
||||
return std::make_tuple(std::vector<Output<Node>>{convert}, true);
|
||||
}
|
||||
OPENVINO_ASSERT(false,
|
||||
"Source color format '",
|
||||
color_format_name(context.color_format()),
|
||||
"' is not convertible to any other");
|
||||
});
|
||||
}
|
||||
|
||||
//------------- Post processing ------
|
||||
void PostStepsList::add_convert_impl(const ov::element::Type& type) {
|
||||
m_actions.emplace_back([type](const ov::Output<Node>& node, PostprocessingContext& ctxt) {
|
||||
ov::element::Type t = type;
|
||||
void PostStepsList::add_convert_impl(const element::Type& type) {
|
||||
m_actions.emplace_back([type](const Output<Node>& node, PostprocessingContext& ctxt) {
|
||||
element::Type t = type;
|
||||
if (t == element::Type{}) {
|
||||
t = ctxt.target_element_type();
|
||||
}
|
||||
|
|
@ -254,23 +251,27 @@ void PostStepsList::add_convert_impl(const ov::element::Type& type) {
|
|||
!t.is_dynamic() && t != element::undefined,
|
||||
"Can't convert to dynamic/unknown element type, consider using of InputTensorInfo::set_element_type");
|
||||
auto convert = std::make_shared<op::v0::Convert>(node, t);
|
||||
convert->set_friendly_name(node.get_node()->get_friendly_name() + "/convert_element_type");
|
||||
return std::make_tuple(ov::Output<ov::Node>(convert), true);
|
||||
inherit_friendly_names_postprocess(convert, node, "/post_convert_element_type");
|
||||
return std::make_tuple(Output<Node>(convert), true);
|
||||
});
|
||||
}
|
||||
|
||||
void PostStepsList::add_convert_layout_impl(const Layout& layout) {
|
||||
m_actions.emplace_back([layout](const ov::Output<Node>& node, PostprocessingContext& context) {
|
||||
m_actions.emplace_back([layout](const Output<Node>& node, PostprocessingContext& context) {
|
||||
Layout dst_layout = layout.empty() ? context.target_layout() : layout;
|
||||
if (dst_layout == context.layout()) {
|
||||
auto permutation = layout::find_permutation(context.layout(), node.get_partial_shape().rank(), dst_layout);
|
||||
if (permutation.empty()) {
|
||||
// No transpose is needed, just update layout
|
||||
if (!layout.empty()) {
|
||||
context.layout() = layout;
|
||||
}
|
||||
return std::make_tuple(node, false);
|
||||
}
|
||||
auto permutation = layout::find_permutation(context.layout(), node.get_partial_shape().rank(), dst_layout);
|
||||
auto perm_constant = op::v0::Constant::create<int64_t>(element::i64, Shape{permutation.size()}, permutation);
|
||||
auto transpose = std::make_shared<op::v1::Transpose>(node, perm_constant);
|
||||
transpose->set_friendly_name(node.get_node()->get_friendly_name() + "/convert_layout");
|
||||
inherit_friendly_names_postprocess(transpose, node, "/post_convert_layout");
|
||||
context.layout() = dst_layout; // Update context's current layout
|
||||
return std::make_tuple(ov::Output<ov::Node>(transpose), true);
|
||||
return std::make_tuple(Output<Node>(transpose), true);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,15 +60,13 @@ inline size_t get_and_check_channels_idx(const Layout& layout, const PartialShap
|
|||
}
|
||||
|
||||
inline void inherit_friendly_names(const std::shared_ptr<ov::Function>& function,
|
||||
const std::shared_ptr<ov::Node>& src_node,
|
||||
const std::shared_ptr<ov::Node>& dst_node,
|
||||
const Output<ov::Node>& src_node,
|
||||
const Output<ov::Node>& dst_node,
|
||||
const std::string& suffix,
|
||||
bool search_for_available_name = true) {
|
||||
OPENVINO_ASSERT(src_node->get_output_size() == 1 && dst_node->get_output_size() == 1,
|
||||
"Internal error. Preprocessing steps must contain nodes with one output");
|
||||
dst_node->set_friendly_name(src_node->get_friendly_name() + suffix);
|
||||
dst_node.get_node_shared_ptr()->set_friendly_name(src_node.get_node_shared_ptr()->get_friendly_name() + suffix);
|
||||
std::unordered_set<std::string> new_names;
|
||||
for (const auto& tensor_name : src_node->output(0).get_tensor().get_names()) {
|
||||
for (const auto& tensor_name : src_node.get_tensor().get_names()) {
|
||||
auto new_tensor_name = tensor_name + suffix;
|
||||
if (!suffix.empty()) {
|
||||
// Verify that new names are unique for a function
|
||||
|
|
@ -82,7 +80,21 @@ inline void inherit_friendly_names(const std::shared_ptr<ov::Function>& function
|
|||
}
|
||||
new_names.emplace(new_tensor_name);
|
||||
}
|
||||
dst_node->output(0).get_tensor().set_names(new_names);
|
||||
dst_node.get_tensor().set_names(new_names);
|
||||
}
|
||||
|
||||
// TODO: add uniqueness check like for preprocessing (or remove from pre-processing)
|
||||
inline void inherit_friendly_names_postprocess(const Output<ov::Node>& inserted_output,
|
||||
const Output<ov::Node>& previous_output,
|
||||
const std::string& suffix) {
|
||||
inserted_output.get_node_shared_ptr()->set_friendly_name(
|
||||
previous_output.get_node_shared_ptr()->get_friendly_name() + suffix);
|
||||
std::unordered_set<std::string> new_names; // New name for previous node
|
||||
for (const auto& tensor_name : previous_output.get_tensor().get_names()) {
|
||||
auto new_tensor_name = tensor_name + suffix;
|
||||
new_names.emplace(new_tensor_name);
|
||||
}
|
||||
previous_output.get_tensor().set_names(new_names);
|
||||
}
|
||||
|
||||
/// \brief Context passed to each pre/post-processing operation.
|
||||
|
|
@ -167,12 +179,12 @@ private:
|
|||
};
|
||||
|
||||
using InternalPreprocessOp =
|
||||
std::function<std::vector<std::shared_ptr<ov::Node>>(const std::vector<std::shared_ptr<ov::Node>>& nodes,
|
||||
const std::shared_ptr<ov::Function>& function,
|
||||
PreprocessingContext& context)>;
|
||||
std::function<std::tuple<std::vector<Output<Node>>, bool>(const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<Function>& function,
|
||||
PreprocessingContext& context)>;
|
||||
|
||||
/// \brief PreProcessStepsImpl - internal data structure
|
||||
class PreProcessSteps::PreProcessStepsImpl {
|
||||
class PreStepsList {
|
||||
public:
|
||||
void add_scale_impl(const std::vector<float>& values);
|
||||
void add_mean_impl(const std::vector<float>& values);
|
||||
|
|
@ -181,17 +193,19 @@ public:
|
|||
void add_convert_layout_impl(const Layout& layout);
|
||||
void add_convert_color_impl(const ColorFormat& dst_format);
|
||||
|
||||
const std::list<std::tuple<InternalPreprocessOp, bool>>& actions() const {
|
||||
const std::list<InternalPreprocessOp>& actions() const {
|
||||
return m_actions;
|
||||
}
|
||||
std::list<std::tuple<InternalPreprocessOp, bool>>& actions() {
|
||||
std::list<InternalPreprocessOp>& actions() {
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<std::tuple<InternalPreprocessOp, bool>> m_actions;
|
||||
std::list<InternalPreprocessOp> m_actions;
|
||||
};
|
||||
|
||||
class PreProcessSteps::PreProcessStepsImpl : public PreStepsList {};
|
||||
|
||||
//------ Post process -----
|
||||
class PostprocessingContext : public PrePostProcessingContextBase {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ static std::shared_ptr<Function> create_simple_function(element::Type type, cons
|
|||
data1->get_output_tensor(0).set_names({"tensor_input1"});
|
||||
auto op = std::make_shared<op::v0::Relu>(data1);
|
||||
op->set_friendly_name("Relu");
|
||||
op->get_output_tensor(0).set_names({"tensor_Relu"});
|
||||
auto res = std::make_shared<op::v0::Result>(op);
|
||||
res->set_friendly_name("Result1");
|
||||
res->get_output_tensor(0).set_names({"tensor_output1"});
|
||||
|
|
@ -66,6 +67,49 @@ TEST(pre_post_process, convert_element_type_and_scale) {
|
|||
EXPECT_EQ(f->get_output_element_type(0), element::i8);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, convert_element_type_implicit) {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 224, 224});
|
||||
f = PrePostProcessor().input(InputInfo().tensor(InputTensorInfo().set_element_type(element::f32))).build(f);
|
||||
EXPECT_EQ(f->get_parameters().front()->get_element_type(), element::f32);
|
||||
EXPECT_EQ(f->get_results().front()->get_element_type(), element::i32);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, convert_element_type_same) {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 224, 224});
|
||||
auto old_size = f->get_ops().size();
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo("tensor_input1")
|
||||
.tensor(InputTensorInfo().set_element_type(element::i32))
|
||||
.preprocess(PreProcessSteps().convert_element_type(element::i32)))
|
||||
.build(f);
|
||||
EXPECT_EQ(f->get_parameters().front()->get_element_type(), element::i32);
|
||||
EXPECT_EQ(old_size, f->get_ops().size());
|
||||
}
|
||||
|
||||
TEST(pre_post_process, convert_element_type_default) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 224, 224});
|
||||
auto type_custom1 = element::Type();
|
||||
auto type_custom2 = element::Type();
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_element_type(element::i32))
|
||||
.preprocess(PreProcessSteps()
|
||||
.custom([&type_custom1](const Output<Node>& node) {
|
||||
type_custom1 = node.get_element_type();
|
||||
return node;
|
||||
})
|
||||
.convert_element_type()
|
||||
.custom([&type_custom2](const Output<Node>& node) {
|
||||
type_custom2 = node.get_element_type();
|
||||
return node;
|
||||
})))
|
||||
.build(f);
|
||||
EXPECT_EQ(type_custom1, element::i32);
|
||||
EXPECT_EQ(type_custom2, element::f32);
|
||||
EXPECT_EQ(f->get_parameters().front()->get_element_type(), element::i32);
|
||||
EXPECT_EQ(f->get_results().front()->get_element_type(), element::f32);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, empty_preprocess) {
|
||||
auto f = create_simple_function(element::i8, Shape{1, 3, 2, 2});
|
||||
f = PrePostProcessor().input(InputInfo().tensor(InputTensorInfo().set_element_type(element::i8))).build(f);
|
||||
|
|
@ -73,6 +117,14 @@ TEST(pre_post_process, empty_preprocess) {
|
|||
EXPECT_EQ(f->get_output_element_type(0), element::i8);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, preprocess_assert_input_without_index) {
|
||||
auto f = create_2inputs(element::f32, Shape{1, 3, 2, 2});
|
||||
auto inp = InputInfo();
|
||||
EXPECT_ANY_THROW(f = PrePostProcessor().input(std::move(inp)).build(f));
|
||||
inp = InputInfo("some_non_existing_name");
|
||||
EXPECT_ANY_THROW(f = PrePostProcessor().input(std::move(inp)).build(f));
|
||||
}
|
||||
|
||||
TEST(pre_post_process, convert_element_type_from_unknown) {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 224, 224});
|
||||
ASSERT_THROW(
|
||||
|
|
@ -83,29 +135,19 @@ TEST(pre_post_process, convert_element_type_from_unknown) {
|
|||
ov::AssertFailure);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, convert_element_type_no_match) {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 224, 224});
|
||||
ASSERT_THROW(f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_element_type(element::i32))
|
||||
.preprocess(PreProcessSteps().convert_element_type(element::f32).scale(2.0f)))
|
||||
.build(f),
|
||||
ov::AssertFailure);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, scale_not_float) {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 224, 224});
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 224, 224});
|
||||
ASSERT_THROW(
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo().preprocess(PreProcessSteps().convert_element_type(element::f32).scale(2.0f)))
|
||||
.input(InputInfo().preprocess(PreProcessSteps().convert_element_type(element::i32).scale(2.0f)))
|
||||
.build(f),
|
||||
ov::AssertFailure);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, mean_not_float) {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 224, 224});
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 224, 224});
|
||||
ASSERT_THROW(f = PrePostProcessor()
|
||||
.input(InputInfo().preprocess(PreProcessSteps().convert_element_type(element::f32).mean(2.0f)))
|
||||
.input(InputInfo().preprocess(PreProcessSteps().convert_element_type(element::i32).mean(2.0f)))
|
||||
.build(f),
|
||||
ov::AssertFailure);
|
||||
}
|
||||
|
|
@ -232,7 +274,7 @@ TEST(pre_post_process, convert_color_nv12_bgr_2_planes_el_type) {
|
|||
PreProcessSteps().convert_element_type(element::u8).convert_color(ColorFormat::BGR)))
|
||||
.build(f));
|
||||
|
||||
EXPECT_EQ(f->get_parameters().size(), 2);
|
||||
ASSERT_EQ(f->get_parameters().size(), 2);
|
||||
EXPECT_EQ(f->get_parameters()[0]->get_element_type(), element::f32);
|
||||
EXPECT_EQ(f->get_parameters()[1]->get_element_type(), element::f32);
|
||||
}
|
||||
|
|
@ -333,18 +375,28 @@ TEST(pre_post_process, convert_color_duplicate_internal_subnames_mean) {
|
|||
.input(InputInfo().preprocess(
|
||||
PreProcessSteps().convert_element_type(element::u8).convert_element_type(element::f32)))
|
||||
.build(f));
|
||||
EXPECT_NO_THROW(f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_layout("NHWC"))
|
||||
.preprocess(PreProcessSteps().convert_layout("NCHW")))
|
||||
.build(f));
|
||||
EXPECT_NO_THROW(
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_layout("NHWC").set_spatial_static_shape(480, 640))
|
||||
.preprocess(PreProcessSteps().resize(ResizeAlgorithm::RESIZE_LINEAR)))
|
||||
.build(f));
|
||||
}
|
||||
f = create_simple_function(element::f32, PartialShape{1, 2, 2, 3});
|
||||
for (int i = 0; i < 10; i++) {
|
||||
(f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_layout("NHWC"))
|
||||
.preprocess(PreProcessSteps().convert_layout("NCHW"))
|
||||
.network(InputNetworkInfo().set_layout("NHWC")))
|
||||
.build(f));
|
||||
}
|
||||
f = create_simple_function(element::f32, PartialShape{1, 2, 2, 3});
|
||||
auto p = PreProcessSteps();
|
||||
for (int i = 10; i < 20; i++) {
|
||||
p.resize(ResizeAlgorithm::RESIZE_LINEAR, i, i);
|
||||
}
|
||||
p.resize(ResizeAlgorithm::RESIZE_LINEAR);
|
||||
EXPECT_NO_THROW(f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_spatial_static_shape(480, 640))
|
||||
.preprocess(std::move(p))
|
||||
.network(InputNetworkInfo().set_layout("NHWC")))
|
||||
.build(f));
|
||||
}
|
||||
|
||||
TEST(pre_post_process, unsupported_network_color_format) {
|
||||
|
|
@ -385,9 +437,9 @@ TEST(pre_post_process, unsupported_network_color_format) {
|
|||
TEST(pre_post_process, custom_preprocessing) {
|
||||
auto f = create_simple_function(element::i32, Shape{1, 3, 1, 1});
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo().preprocess(PreProcessSteps().custom([](const std::shared_ptr<Node>& node) {
|
||||
.input(InputInfo().preprocess(PreProcessSteps().custom([](const Output<Node>& node) {
|
||||
auto abs = std::make_shared<op::v0::Abs>(node);
|
||||
abs->set_friendly_name(node->get_friendly_name() + "/abs");
|
||||
abs->set_friendly_name(node.get_node_shared_ptr()->get_friendly_name() + "/abs");
|
||||
return abs;
|
||||
})))
|
||||
.build(f);
|
||||
|
|
@ -420,9 +472,9 @@ TEST(pre_post_process, test_lvalue) {
|
|||
preprocessSteps.scale(2.f);
|
||||
preprocessSteps.mean({1.f, 2.f, 3.f});
|
||||
preprocessSteps.scale({2.f, 3.f, 4.f});
|
||||
preprocessSteps.custom([](const std::shared_ptr<Node>& node) {
|
||||
preprocessSteps.custom([](const Output<Node>& node) {
|
||||
auto abs = std::make_shared<op::v0::Abs>(node);
|
||||
abs->set_friendly_name(node->get_friendly_name() + "/abs");
|
||||
abs->set_friendly_name(node.get_node_shared_ptr()->get_friendly_name() + "/abs");
|
||||
return abs;
|
||||
});
|
||||
auto& same = preprocessSteps.convert_element_type(element::i8);
|
||||
|
|
@ -603,20 +655,67 @@ TEST(pre_post_process, resize_no_tensor_width) {
|
|||
ov::AssertFailure);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, preprocess_convert_layout_implicit) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 2, 2});
|
||||
|
||||
f = PrePostProcessor()
|
||||
.input(
|
||||
InputInfo().tensor(InputTensorInfo().set_layout("NHWC")).network(InputNetworkInfo().set_layout("NCHW")))
|
||||
.build(f);
|
||||
EXPECT_EQ(f->get_parameters()[0]->get_layout(), "NHWC");
|
||||
EXPECT_EQ(f->get_parameters()[0]->get_output_tensor(0).get_partial_shape(), (PartialShape{1, 2, 2, 3}));
|
||||
}
|
||||
|
||||
TEST(pre_post_process, preprocess_convert_layout_default) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 2, 2});
|
||||
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_layout("NHWC"))
|
||||
.preprocess(PreProcessSteps().convert_layout())
|
||||
.network(InputNetworkInfo().set_layout("NCHW")))
|
||||
.build(f);
|
||||
EXPECT_EQ(f->get_parameters()[0]->get_layout(), "NHWC");
|
||||
EXPECT_EQ(f->get_parameters()[0]->get_output_tensor(0).get_partial_shape(), (PartialShape{1, 2, 2, 3}));
|
||||
}
|
||||
|
||||
TEST(pre_post_process, preprocess_convert_layout_same) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 2, 2});
|
||||
auto size_old = f->get_ordered_ops().size();
|
||||
|
||||
f = PrePostProcessor()
|
||||
.input(InputInfo()
|
||||
.tensor(InputTensorInfo().set_layout("NCHW"))
|
||||
.preprocess(PreProcessSteps().convert_layout("NCHW"))
|
||||
.network(InputNetworkInfo().set_layout("NCHW")))
|
||||
.build(f);
|
||||
EXPECT_EQ(f->get_parameters()[0]->get_layout(), "NCHW");
|
||||
EXPECT_EQ(f->get_parameters()[0]->get_output_tensor(0).get_partial_shape(), (PartialShape{1, 3, 2, 2}));
|
||||
// Verify that redundant ops were not added
|
||||
EXPECT_EQ(size_old, f->get_ordered_ops().size());
|
||||
}
|
||||
|
||||
// --- PostProcess - set/convert element type ---
|
||||
|
||||
TEST(pre_post_process, postprocess_convert_element_type_explicit) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 2, 2});
|
||||
auto old_names = f->output().get_tensor().get_names();
|
||||
f = PrePostProcessor()
|
||||
.output(OutputInfo().postprocess(PostProcessSteps().convert_element_type(element::u8)))
|
||||
.build(f);
|
||||
EXPECT_EQ(f->get_results().size(), 1);
|
||||
EXPECT_EQ(f->get_results()[0]->get_element_type(), element::u8);
|
||||
EXPECT_EQ(f->output().get_tensor().get_names(), old_names);
|
||||
EXPECT_EQ(old_names.count("tensor_output1"), 1);
|
||||
auto ops = f->get_ordered_ops();
|
||||
auto res_count = std::count_if(ops.begin(), ops.end(), [](std::shared_ptr<ov::Node> n) {
|
||||
return std::dynamic_pointer_cast<ov::op::v0::Result>(n) != nullptr;
|
||||
});
|
||||
EXPECT_EQ(res_count, 1);
|
||||
auto names_count = std::count_if(ops.begin(), ops.end(), [](std::shared_ptr<ov::Node> n) {
|
||||
return n->output(0).get_tensor().get_names().count("tensor_output1") > 0;
|
||||
});
|
||||
EXPECT_EQ(names_count, 2); // last node + result referencing to it
|
||||
}
|
||||
|
||||
TEST(pre_post_process, postprocess_convert_element_type_default) {
|
||||
|
|
@ -822,10 +921,9 @@ TEST(pre_post_process, exception_safety) {
|
|||
.preprocess(PreProcessSteps().convert_element_type(element::f32)))
|
||||
.input(InputInfo(1) // This one is not
|
||||
.tensor(InputTensorInfo().set_color_format(ColorFormat::NV12_TWO_PLANES))
|
||||
.preprocess(PreProcessSteps().custom(
|
||||
[](const std::shared_ptr<Node>& node) -> std::shared_ptr<Node> {
|
||||
throw ngraph::ngraph_error("test error");
|
||||
})))
|
||||
.preprocess(PreProcessSteps().custom([](const Output<Node>& node) -> Output<Node> {
|
||||
throw ngraph::ngraph_error("test error");
|
||||
})))
|
||||
.build(f),
|
||||
ov::AssertFailure);
|
||||
EXPECT_EQ(f->get_parameters().size(), 2);
|
||||
|
|
|
|||
Loading…
Reference in New Issue