Add 'pad' operator support for ov::preprocess::PrePostProcessor (#23093)
### Details: - Add 'pad' preprocessor operator - #23068 ### Tickets: - [CVS-121548](https://jira.devtools.intel.com/browse/CVS-121548)
This commit is contained in:
parent
f5e46b5d67
commit
964e9e81b3
|
|
@ -49,6 +49,7 @@
|
|||
#include "openvino/core/preprocess/output_info.hpp"
|
||||
#include "openvino/core/preprocess/output_model_info.hpp"
|
||||
#include "openvino/core/preprocess/output_tensor_info.hpp"
|
||||
#include "openvino/core/preprocess/padding_mode.hpp"
|
||||
#include "openvino/core/preprocess/postprocess_steps.hpp"
|
||||
#include "openvino/core/preprocess/pre_post_process.hpp"
|
||||
#include "openvino/core/preprocess/preprocess_steps.hpp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ov {
|
||||
namespace preprocess {
|
||||
|
||||
using PaddingMode = ov::op::PadMode;
|
||||
|
||||
} // namespace preprocess
|
||||
} // namespace ov
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "openvino/core/core_visibility.hpp"
|
||||
#include "openvino/core/preprocess/color_format.hpp"
|
||||
#include "openvino/core/preprocess/padding_mode.hpp"
|
||||
#include "openvino/core/preprocess/resize_algorithm.hpp"
|
||||
#include "openvino/core/type/element_type.hpp"
|
||||
|
||||
|
|
@ -80,6 +81,32 @@ public:
|
|||
/// \return Reference to 'this' to allow chaining with other calls in a builder-like manner
|
||||
PreProcessSteps& mean(const std::vector<float>& values);
|
||||
|
||||
/// \brief Add pad preprocess operation
|
||||
/// Extends an input tensor on edges with constants
|
||||
///
|
||||
/// \param pads_begin Number of padding elements to add at the beginning of each axis.
|
||||
/// \param pads_end Number of padding elements to add at the end of each axis.
|
||||
/// \param value Value to be populated in the padded area
|
||||
///
|
||||
/// \return Reference to 'this' to allow chaining with other calls in a builder-like manner
|
||||
PreProcessSteps& pad(const std::vector<int>& pads_begin,
|
||||
const std::vector<int>& pads_end,
|
||||
float value,
|
||||
PaddingMode mode);
|
||||
|
||||
/// \brief Add pad preprocess operation
|
||||
/// Extends an input tensor on edges with constants
|
||||
///
|
||||
/// \param pads_begin Number of padding elements to add at the beginning of each axis.
|
||||
/// \param pads_end Number of padding elements to add at the end of each axis.
|
||||
/// \param values Values to be populated in the padded area
|
||||
///
|
||||
/// \return Reference to 'this' to allow chaining with other calls in a builder-like manner
|
||||
PreProcessSteps& pad(const std::vector<int>& pads_begin,
|
||||
const std::vector<int>& pads_end,
|
||||
const std::vector<float>& values,
|
||||
PaddingMode mode);
|
||||
|
||||
/// \brief Signature for custom preprocessing operation. Custom preprocessing operation takes one input node and
|
||||
/// produces one output node. For more advanced cases, client's code can use transformation passes over ov::Model
|
||||
/// directly
|
||||
|
|
|
|||
|
|
@ -279,6 +279,22 @@ PreProcessSteps& PreProcessSteps::mean(const std::vector<float>& values) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
PreProcessSteps& PreProcessSteps::pad(const std::vector<int>& pads_begin,
|
||||
const std::vector<int>& pads_end,
|
||||
float value,
|
||||
PaddingMode mode) {
|
||||
m_impl->add_pad_impl(pads_begin, pads_end, std::vector<float>{value}, mode);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PreProcessSteps& PreProcessSteps::pad(const std::vector<int>& pads_begin,
|
||||
const std::vector<int>& pads_end,
|
||||
const std::vector<float>& values,
|
||||
PaddingMode mode) {
|
||||
m_impl->add_pad_impl(pads_begin, pads_end, values, mode);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PreProcessSteps& PreProcessSteps::convert_element_type(const element::Type& type) {
|
||||
m_impl->add_convert_impl(type);
|
||||
return *this;
|
||||
|
|
|
|||
|
|
@ -130,6 +130,54 @@ void PreStepsList::add_mean_impl(const std::vector<float>& values) {
|
|||
"mean " + vector_to_string(values));
|
||||
}
|
||||
|
||||
void PreStepsList::add_pad_impl(const std::vector<int>& pads_begin,
|
||||
const std::vector<int>& pads_end,
|
||||
const std::vector<float>& pad_values,
|
||||
PaddingMode mode) {
|
||||
std::string name;
|
||||
name = "pad(begin " + vector_to_string(pads_begin) + ", end " + vector_to_string(pads_end);
|
||||
switch (mode) {
|
||||
case PaddingMode::CONSTANT:
|
||||
name += ", with " + vector_to_string(pad_values) + ")";
|
||||
break;
|
||||
case PaddingMode::EDGE:
|
||||
name += ", copied from edge)";
|
||||
break;
|
||||
case PaddingMode::REFLECT:
|
||||
name += ", reflected from tensor)";
|
||||
break;
|
||||
case PaddingMode::SYMMETRIC:
|
||||
name += ", symmetrically added from tensor)";
|
||||
break;
|
||||
}
|
||||
|
||||
m_actions.emplace_back(
|
||||
[pads_begin, pads_end, pad_values, mode](const std::vector<Output<Node>>& nodes,
|
||||
const std::shared_ptr<Model>& function,
|
||||
PreprocessingContext& ctxt) {
|
||||
OPENVINO_ASSERT(nodes.size() == 1,
|
||||
"Can't pad multi-plane input. Suggesting to convert current image to "
|
||||
"RGB/BGR color format using 'PreProcessSteps::convert_color'");
|
||||
|
||||
const auto& node = nodes[0];
|
||||
auto element_type = nodes[0].get_element_type();
|
||||
OPENVINO_ASSERT(element_type.is_real(),
|
||||
"Pad preprocessing can be applied to 'float' inputs. Consider using of "
|
||||
"'convert_element_type' before padding. Current type is: ",
|
||||
element_type);
|
||||
|
||||
auto pad_value = opset8::Constant::create(node.get_element_type(), Shape{}, pad_values);
|
||||
|
||||
auto npads_begin = opset8::Constant::create(element::i64, Shape{pads_begin.size()}, pads_begin);
|
||||
auto npads_end = opset8::Constant::create(element::i64, Shape{pads_end.size()}, pads_end);
|
||||
auto npad_value = opset8::Constant::create(element_type, Shape{}, pad_values);
|
||||
|
||||
auto pad = std::make_shared<opset8::Pad>(node, npads_begin, npads_end, npad_value, mode);
|
||||
return std::make_tuple(std::vector<Output<Node>>{pad}, true);
|
||||
},
|
||||
name);
|
||||
}
|
||||
|
||||
void PreStepsList::add_convert_impl(const element::Type& type) {
|
||||
m_actions.emplace_back(
|
||||
[type](const std::vector<Output<Node>>& nodes,
|
||||
|
|
|
|||
|
|
@ -158,6 +158,10 @@ class PreStepsList {
|
|||
public:
|
||||
void add_scale_impl(const std::vector<float>& values);
|
||||
void add_mean_impl(const std::vector<float>& values);
|
||||
void add_pad_impl(const std::vector<int>& pads_begin,
|
||||
const std::vector<int>& pads_end,
|
||||
const std::vector<float>& values,
|
||||
PaddingMode mode);
|
||||
void add_convert_impl(const element::Type& type);
|
||||
void add_crop_impl(const std::vector<int>& begin, const std::vector<int>& end);
|
||||
void add_resize_impl(ResizeAlgorithm alg, int dst_height, int dst_width);
|
||||
|
|
|
|||
|
|
@ -914,6 +914,31 @@ TEST(pre_post_process, mean_vector_dynamic_channels_shape) {
|
|||
EXPECT_EQ(f->get_output_element_type(0), element::f32);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, pad_vector_constant_layout) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 200, 200});
|
||||
auto p = PrePostProcessor(f);
|
||||
|
||||
p.input().tensor().set_shape({1, 3, 199, 199});
|
||||
p.input().preprocess().pad({0, 0, 0, 0}, {0, 0, 1, 1}, 0, PaddingMode::CONSTANT);
|
||||
EXPECT_NO_THROW(p.build());
|
||||
}
|
||||
|
||||
TEST(pre_post_process, pad_vector_out_of_range) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 5, 5});
|
||||
auto p = PrePostProcessor(f);
|
||||
|
||||
ASSERT_THROW(p.input().preprocess().pad({0, 0, -2, 0}, {0, 0, -4, 1}, 0, PaddingMode::CONSTANT);
|
||||
p.build(), ov::AssertFailure);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, pad_vector_dim_mismatch) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 5, 5});
|
||||
auto p = PrePostProcessor(f);
|
||||
|
||||
ASSERT_THROW(p.input().preprocess().pad({0, 0, 2, 0, 1}, {0, 0, 4, 1, 1}, 0, PaddingMode::CONSTANT);
|
||||
p.build(), ov::AssertFailure);
|
||||
}
|
||||
|
||||
TEST(pre_post_process, resize_no_model_layout) {
|
||||
auto f = create_simple_function(element::f32, Shape{1, 3, 224, 224});
|
||||
auto p = PrePostProcessor(f);
|
||||
|
|
|
|||
|
|
@ -443,6 +443,26 @@ inline std::shared_ptr<Model> crop_dynamic() {
|
|||
return function;
|
||||
}
|
||||
|
||||
inline std::shared_ptr<Model> pad_constant() {
|
||||
using namespace ov::preprocess;
|
||||
auto function = create_preprocess_1input(ov::element::f32, PartialShape{1, 3, 10, 10});
|
||||
auto p = PrePostProcessor(function);
|
||||
p.input().tensor().set_shape({1, 3, 9, 5});
|
||||
p.input().preprocess().pad({0, 0, 2, 2}, {0, 0, -1, 3}, 0, PaddingMode::CONSTANT);
|
||||
function = p.build();
|
||||
return function;
|
||||
}
|
||||
|
||||
inline std::shared_ptr<Model> pad_edge() {
|
||||
using namespace ov::preprocess;
|
||||
auto function = create_preprocess_1input(ov::element::f32, PartialShape{1, 3, 10, 10});
|
||||
auto p = PrePostProcessor(function);
|
||||
p.input().tensor().set_shape({1, 3, 9, 5});
|
||||
p.input().preprocess().pad({0, 0, 2, 2}, {0, 0, -1, 3}, 0, PaddingMode::EDGE);
|
||||
function = p.build();
|
||||
return function;
|
||||
}
|
||||
|
||||
inline std::vector<preprocess_func> generic_preprocess_functions() {
|
||||
return std::vector<preprocess_func>{
|
||||
preprocess_func(mean_only, "mean_only", 0.01f),
|
||||
|
|
@ -477,7 +497,8 @@ inline std::vector<preprocess_func> generic_preprocess_functions() {
|
|||
preprocess_func(cvt_color_i420_to_rgb_single_plane, "cvt_color_i420_to_rgb_single_plane", 1.f),
|
||||
preprocess_func(cvt_color_i420_to_bgr_three_planes, "cvt_color_i420_to_bgr_three_planes", 1.f),
|
||||
preprocess_func(cvt_color_bgrx_to_bgr, "cvt_color_bgrx_to_bgr", 0.01f),
|
||||
};
|
||||
preprocess_func(pad_constant, "pad_constant", 0.01f),
|
||||
preprocess_func(pad_edge, "pad_edge", 0.01f)};
|
||||
}
|
||||
|
||||
inline std::shared_ptr<Model> cvt_color_rgb_to_bgr() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue