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:
Yanghe Sun 2024-03-18 17:40:16 +08:00 committed by GitHub
parent f5e46b5d67
commit 964e9e81b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 156 additions and 1 deletions

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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() {