[OV20] InputTensorInfo::set_shape (#9059)

* InputTensorInfo::set_shape

* Fix clang-format
This commit is contained in:
Mikhail Nosov 2021-12-07 19:13:38 +03:00 committed by GitHub
parent f0e570b30a
commit 70b5e28979
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 147 additions and 1 deletions

View File

@ -766,6 +766,36 @@ static RefPreprocessParams convert_color_i420_single_plane() {
return res;
}
static RefPreprocessParams set_shape_custom_crop() {
RefPreprocessParams res("set_shape_custom_crop");
res.function = []() {
auto f = create_simple_function(element::f32, PartialShape{2, 2, 2, 2});
auto p = PrePostProcessor(f);
p.input().tensor().set_shape({-1, -1, -1, -1});
p.input().preprocess().custom([](const Output<Node>& node) {
// Add custom crop to model's dimensions using 'Slice' operation
// Middle part 2x2x2x2 of original user's 4x4x4x4 input tensor will be extracted
auto start = opset8::Constant::create(element::i32, {4}, {1, 1, 1, 1});
auto stop = opset8::Constant::create(element::i32, {4}, {3, 3, 3, 3});
auto step = opset8::Constant::create(element::i32, {4}, {1, 1, 1, 1});
auto axis = opset8::Constant::create(element::i32, {4}, {0, 1, 2, 3});
auto slice = std::make_shared<opset8::Slice>(node, start, stop, step, axis);
return slice;
});
p.build();
return f;
};
auto input_size = 4 * 4 * 4 * 4;
std::vector<float> input_values(input_size);
std::iota(input_values.begin(), input_values.end(), 0);
res.inputs.emplace_back(element::f32, Shape{4, 4, 4, 4}, input_values);
res.expected.emplace_back(Shape{2, 2, 2, 2}, element::f32, std::vector<float>{ 85, 86, 89, 90,
101, 102, 105, 106,
149, 150, 153, 154,
165, 166, 169, 170});
return res;
}
static RefPreprocessParams postprocess_2_inputs_basic() {
RefPreprocessParams res("postprocess_2_inputs_basic");
res.function = []() {
@ -1037,6 +1067,7 @@ std::vector<RefPreprocessParams> allPreprocessTests() {
element_type_before_convert_color_nv12(),
convert_color_i420_to_bgr_three_planes(),
convert_color_i420_single_plane(),
set_shape_custom_crop(),
postprocess_2_inputs_basic(),
post_convert_layout_by_dims(),
post_convert_layout_by_dims_multi(),

View File

@ -255,7 +255,13 @@ static void regclass_graph_InputTensorInfo(py::module m) {
});
info.def("set_spatial_static_shape", [](ov::preprocess::InputTensorInfo& me, size_t height, size_t width) {
return &me.set_spatial_static_shape(height, width);
;
});
info.def("set_shape", [](ov::preprocess::InputTensorInfo& me, const ov::PartialShape& shape) {
return &me.set_shape(shape);
});
// Allow to use set_shape([1,2,3]) in Python code, not set_shape(PartialShape([1,2,3]))
info.def("set_shape", [](ov::preprocess::InputTensorInfo& me, const std::vector<int64_t>& shape) {
return &me.set_shape(shape);
});
info.def("set_color_format",
[](ov::preprocess::InputTensorInfo& me,

View File

@ -201,6 +201,37 @@ def test_ngraph_preprocess_spatial_static_shape():
assert np.equal(output, expected_output).all()
def test_ngraph_preprocess_set_shape():
shape = [1, 1, 1]
parameter_a = ops.parameter(shape, dtype=np.int32, name="A")
model = parameter_a
function = Function(model, [parameter_a], "TestFunction")
@custom_preprocess_function
def custom_crop(out_node: Output):
start = ops.constant(np.array([1, 1, 1]), dtype=np.int32)
stop = ops.constant(np.array([2, 2, 2]), dtype=np.int32)
step = ops.constant(np.array([1, 1, 1]), dtype=np.int32)
axis = ops.constant(np.array([0, 1, 2]), dtype=np.int32)
return ops.slice(out_node, start, stop, step, axis)
p = PrePostProcessor(function)
inp = p.input()
inp.tensor().set_shape([3, 3, 3])
inp.preprocess().custom(custom_crop)
function = p.build()
input_data = np.array([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
[[9, 10, 11], [12, 13, 14], [15, 16, 17]],
[[18, 19, 20], [21, 22, 23], [24, 25, 26]]]).astype(np.int32)
expected_output = np.array([[[13]]]).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),

View File

@ -105,6 +105,19 @@ public:
///
/// \return Reference to 'this' to allow chaining with other calls in a builder-like manner
InputTensorInfo& set_memory_type(const std::string& memory_type);
/// \brief By default, input shape is inherited from model's input shape. Use this method to specify different
/// input data shape. If it is needed to change only input height & width of input image, consider define layout and
/// use `set_spatial_static_shape' or 'set_spatial_dynamic_shape' instead. This method allows defining any custom
/// input shape and can be useful for custom preprocessing operations
///
/// \note Methods 'set_spatial_dynamic_shape', 'set_spatial_static_shape' are also intended to modify input shape,
/// using those methods together will throw ov::AssertFailure exception
///
/// \param shape New shape for input tensor.
///
/// \return Reference to 'this' to allow chaining with other calls in a builder-like manner.
InputTensorInfo& set_shape(const ov::PartialShape& shape);
};
} // namespace preprocess

View File

@ -69,12 +69,14 @@ public:
}
void set_spatial_dynamic_shape() {
OPENVINO_ASSERT(!m_shape_set, "'set_spatial_dynamic_shape' and 'set_shape' shall not be used together");
m_spatial_shape_set = true;
m_spatial_width = -1;
m_spatial_height = -1;
}
void set_spatial_static_shape(size_t height, size_t width) & {
OPENVINO_ASSERT(!m_shape_set, "'set_spatial_static_shape' and 'set_shape' shall not be used together");
m_spatial_shape_set = true;
m_spatial_height = static_cast<int>(height);
m_spatial_width = static_cast<int>(width);
@ -122,6 +124,22 @@ public:
return m_memory_type_set;
}
void set_shape(const PartialShape& shape) {
OPENVINO_ASSERT(
!m_spatial_shape_set,
"'set_spatial_static_shape', 'set_spatial_dynamic_shape', 'set_shape' shall not be used together");
m_shape = shape;
m_shape_set = true;
}
bool is_shape_set() const {
return m_shape_set;
}
const PartialShape& get_shape() const {
return m_shape;
}
private:
ColorFormat m_color_format = ColorFormat::UNDEFINED;
std::vector<std::string> m_planes_sub_names;
@ -138,6 +156,9 @@ private:
std::string m_memory_type = {};
bool m_memory_type_set = false;
PartialShape m_shape = {};
bool m_shape_set = false;
};
class OutputTensorInfo::OutputTensorInfoImpl : public TensorInfoImplBase {};
@ -371,6 +392,9 @@ std::shared_ptr<Function> PrePostProcessor::build() {
auto net_shape = param->get_partial_shape();
auto new_param_shape = net_shape;
if (input->get_tensor_data()->is_shape_set()) {
new_param_shape = input->get_tensor_data()->get_shape();
}
if (input->get_tensor_data()->is_layout_set() && !param->get_layout().empty() &&
param->get_layout() != input->get_tensor_data()->get_layout()) {
// Find transpose between network and tensor layouts and update tensor shape
@ -652,6 +676,11 @@ InputTensorInfo& InputTensorInfo::set_memory_type(const std::string& memory_type
return *this;
}
InputTensorInfo& InputTensorInfo::set_shape(const PartialShape& shape) {
m_impl->set_shape(shape);
return *this;
}
// --------------------- PreProcessSteps ------------------
PreProcessSteps::PreProcessSteps() : m_impl(std::unique_ptr<PreProcessStepsImpl>(new PreProcessStepsImpl())) {}

View File

@ -636,6 +636,42 @@ TEST(pre_post_process, tensor_spatial_shape_no_layout_dims) {
p.build(), ov::AssertFailure);
}
TEST(pre_post_process, tensor_set_shape_incompatible) {
auto f = create_simple_function(element::f32, Shape{1, 3, 224, 224});
auto p = PrePostProcessor(f);
EXPECT_THROW(p.input().tensor().set_shape({1, 4, 224, 224}); p.build(), ov::AssertFailure);
}
// Check that 'set_shape' shall not be used together with set_spatial_..._shape
// This test can be removed if this requirement is relaxed in future releases
TEST(pre_post_process, tensor_set_shape_with_spatial) {
auto f = create_simple_function(element::f32, PartialShape{-1, -1, -1, -1});
{
auto p = PrePostProcessor(f);
p.input().tensor().set_layout("NCHW");
EXPECT_THROW(p.input().tensor().set_shape({1, 3, 224, 224}).set_spatial_static_shape(448, 448);
p.build(), ov::AssertFailure);
}
{
auto p = PrePostProcessor(f);
p.input().tensor().set_layout("NCHW");
EXPECT_THROW(p.input().tensor().set_spatial_static_shape(448, 448).set_shape({1, 3, 224, 224});
p.build(), ov::AssertFailure);
}
{
auto p = PrePostProcessor(f);
p.input().tensor().set_layout("NCHW");
EXPECT_THROW(p.input().tensor().set_shape({1, 3, 224, 224}).set_spatial_dynamic_shape();
p.build(), ov::AssertFailure);
}
{
auto p = PrePostProcessor(f);
p.input().tensor().set_layout("NCHW");
EXPECT_THROW(p.input().tensor().set_spatial_dynamic_shape().set_shape({1, 3, 224, 224});
p.build(), ov::AssertFailure);
}
}
TEST(pre_post_process, resize_no_tensor_height) {
auto f = create_simple_function(element::f32, Shape{1, 3, 224, 224});
auto p = PrePostProcessor(f);