Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Op] Add Col2Im-15 implementation to Core #24197

Merged
merged 20 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/core/include/openvino/op/col2im.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

#include "openvino/op/op.hpp"

namespace ov {
namespace op {
namespace v15 {
/// \brief Operator combining sliding blocks into an image tensor
/// \ingroup ov_ops_cpp_api
class OPENVINO_API Col2Im : public ov::op::Op {
public:
OPENVINO_OP("Col2Im", "opset15", ov::op::Op);

Col2Im() = default;
/// \brief Constructs a Col2Im operation.
///
/// \param data Input tensor with data
/// \param output_size Shape of the spatial dimensions of the output image
/// \param kernel_size Size of the sliding blocks
/// \param strides Stride in the sliding blocks in the input spatial dimensions
/// \param dilations Local stride of the elements
/// \param pads_begin Paddings at the beginning of each spatial axis, if undefined no padding is applied
/// \param pads_end Paddings at the end of each spatial axis, if undefined no padding is applied
Col2Im(const Output<Node>& data,
const Output<Node>& output_size,
const Output<Node>& kernel_size,
const Strides& strides = Strides{1, 1},
const Strides& dilations = Strides{1, 1},
const Shape& pads_begin = Shape{0, 0},
const Shape& pads_end = Shape{0, 0});

bool visit_attributes(AttributeVisitor& visitor) override;
void validate_and_infer_types() override;
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;

const Strides& get_strides() const;
const Strides& get_dilations() const;
const Shape& get_pads_begin() const;
const Shape& get_pads_end() const;

private:
Strides m_strides;
Strides m_dilations;
Shape m_pads_begin;
Shape m_pads_end;
};

} // namespace v15
} // namespace op
} // namespace ov
1 change: 1 addition & 0 deletions src/core/include/openvino/op/ops.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "openvino/op/bucketize.hpp"
#include "openvino/op/ceiling.hpp"
#include "openvino/op/clamp.hpp"
#include "openvino/op/col2im.hpp"
#include "openvino/op/concat.hpp"
#include "openvino/op/constant.hpp"
#include "openvino/op/convert.hpp"
Expand Down
125 changes: 125 additions & 0 deletions src/core/shape_inference/include/col2im_shape_inference.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#pragma once

#include <cmath>

#include "openvino/op/col2im.hpp"
#include "utils.hpp"

namespace ov {
namespace op {
namespace v15 {
template <class TShape, class TRShape = result_shape_t<TShape>>
std::vector<TRShape> shape_infer(const Col2Im* op,
const std::vector<TShape>& input_shapes,
const ITensorAccessor& tensor_accessor = make_tensor_accessor()) {
NODE_SHAPE_INFER_CHECK(op,
input_shapes,
input_shapes.size() == 3,
"Number of inputs has to be equal to 3. Got: ",
input_shapes.size());
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved

const auto& data_shape = input_shapes[0];
const auto& output_size_shape = input_shapes[1];
const auto& kernel_shape = input_shapes[2];
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
const bool is_batched = data_shape.rank() == 3;
const auto output_size_val = ov::op::get_input_const_data_as<TRShape, int64_t>(op, 1, tensor_accessor);
const auto kernel_val = ov::op::get_input_const_data_as<TRShape, int64_t>(op, 2, tensor_accessor);

NODE_SHAPE_INFER_CHECK(op,
input_shapes,
data_shape.rank().compatible(2) || data_shape.rank().compatible(3),
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
"input data must be an unbatched 2D or a batched 3D input. Got: ",
data_shape);

if (output_size_shape.rank().is_static()) {
NODE_SHAPE_INFER_CHECK(op,
input_shapes,
output_size_shape.rank().compatible(1) && output_size_shape[0].compatible(2),
"output_size must be a 1D input of shape [2]. Got: ",
output_size_shape);
}
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved

if (kernel_shape.rank().is_static()) {
NODE_SHAPE_INFER_CHECK(op,
input_shapes,
kernel_shape.rank().compatible(1) && kernel_shape[0].compatible(2),
"kernel_size must be a 1D input of shape [2]. Got: ",
kernel_shape);
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
}

if (data_shape.rank().is_static()) {
auto output_shapes = std::vector<TRShape>(1);
auto& output_shape = output_shapes[0];
output_shape.resize(is_batched ? 4 : 3);
size_t idx = 0;
praasz marked this conversation as resolved.
Show resolved Hide resolved

// output_shape: (N, C, H, W)
// ^
if (is_batched) {
output_shape[idx] = data_shape[0];
idx++;
}

// output_shape: (N, C, H, W)
// ^
const size_t C_idx = is_batched ? 1 : 0;
if (kernel_val && kernel_shape[0].is_static() && data_shape.rank().is_static() &&
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
data_shape[C_idx].is_static()) {
const auto dividend = data_shape[C_idx].get_length();
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
const auto divisor = ((*kernel_val)[0] * (*kernel_val)[1]);
NODE_SHAPE_INFER_CHECK(op,
input_shapes,
dividend % divisor == 0,
"First non-batch dimension is not evenly divisible by Product(kernel_shape). Got: ",
data_shape[C_idx].get_length());
output_shape[idx] = dividend / divisor;
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
}

// output_shape: (N, C, H, W)
// ^ ^
if (output_size_val) {
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
idx++;
output_shape[idx] = (*output_size_val)[0];
idx++;
output_shape[idx] = (*output_size_val)[1];
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
const size_t L_idx = is_batched ? 2 : 1;
if (data_shape.rank().is_static() && data_shape[L_idx].is_static()) {
const auto L = data_shape[L_idx].get_length();
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
constexpr size_t spatial_dims = 2;

const auto& pads_begin = op->get_pads_begin();
const auto& pads_end = op->get_pads_end();
const auto& strides = op->get_strides();
const auto& dilations = op->get_dilations();

if (kernel_val) {
double L_calculated = 1;
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
for (size_t d = 0; d < spatial_dims; ++d) {
L_calculated *= std::floor((((*output_size_val)[d] + pads_begin[d] + pads_end[d] -
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
dilations[d] * ((*kernel_val)[d] - 1) - 1) /
strides[d]) +
1);
}

NODE_SHAPE_INFER_CHECK(
op,
input_shapes,
L == L_calculated,
"For given inputs and parameters the total number of data blocks must be equal to " +
std::to_string(static_cast<size_t>(L_calculated)) + ". Got: ",
L);
p-wysocki marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
return output_shapes;
} else {
return {PartialShape::dynamic()};
}
}
} // namespace v15
} // namespace op
} // namespace ov
94 changes: 94 additions & 0 deletions src/core/src/op/col2im.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#include "openvino/op/col2im.hpp"

#include "col2im_shape_inference.hpp"
#include "itt.hpp"
#include "openvino/core/validation_util.hpp"
#include "openvino/op/op.hpp"

namespace ov {
namespace op {
namespace v15 {

Col2Im::Col2Im(const Output<Node>& data,
const Output<Node>& output_size,
const Output<Node>& kernel_size,
const Strides& strides,
const Strides& dilations,
const Shape& pads_begin,
const Shape& pads_end)
: Op({data, output_size, kernel_size}),
m_strides(strides),
m_dilations(dilations),
m_pads_begin(pads_begin),
m_pads_end(pads_end) {
constructor_validate_and_infer_types();
}

bool Col2Im::visit_attributes(ov::AttributeVisitor& visitor) {
OV_OP_SCOPE(v15_Col2Im_visit_attributes);
visitor.on_attribute("strides", m_strides);
visitor.on_attribute("dilations", m_dilations);
visitor.on_attribute("pads_begin", m_pads_begin);
visitor.on_attribute("pads_end", m_pads_end);
return true;
}

void Col2Im::validate_and_infer_types() {
OV_OP_SCOPE(v15_Col2Im_validate_and_infer_types);

const auto& data_element_type = get_input_element_type(0);
const auto& output_size_element_type = get_input_element_type(1);
const bool is_valid_output_size_type =
output_size_element_type == element::i32 || output_size_element_type == element::i64;
NODE_VALIDATION_CHECK(this,
is_valid_output_size_type,
"The element type of the output_size tensor must be i32 or i64 type. Got: ",
output_size_element_type);

const auto& kernel_size_element_type = get_input_element_type(2);
const bool is_valid_kernel_size_type =
kernel_size_element_type == element::i32 || kernel_size_element_type == element::i64;
NODE_VALIDATION_CHECK(this,
is_valid_kernel_size_type,
"The element type of the kernel_size tensor must be i32 or i64 type. Got: ",
kernel_size_element_type);

const auto output_shapes = shape_infer(this, ov::util::get_node_input_partial_shapes(*this));
set_output_type(0, data_element_type, output_shapes[0]);
}

std::shared_ptr<Node> Col2Im::clone_with_new_inputs(const ov::OutputVector& new_args) const {
OV_OP_SCOPE(v15_Col2Im_clone_with_new_inputs);
check_new_args_count(this, new_args);
return std::make_shared<Col2Im>(new_args.at(0),
new_args.at(1),
new_args.at(2),
m_strides,
m_dilations,
m_pads_begin,
m_pads_end);
}

const Strides& Col2Im::get_strides() const {
return m_strides;
}

const Strides& Col2Im::get_dilations() const {
return m_dilations;
}

const Shape& Col2Im::get_pads_begin() const {
return m_pads_begin;
}

const Shape& Col2Im::get_pads_end() const {
return m_pads_end;
}

} // namespace v15
} // namespace op
} // namespace ov
Loading
Loading