Fix boxes dim calculation when scores dynamic rank (#19097)

* Fix boxes dim calculation when scores dynamic rank

* NMS shape infer improve upper bound calculation

* Calculate boxes if required shapes has static rank

* Optimize shape_infer for NMS v4

* Reorder checks in nms v4 for selected boxes
This commit is contained in:
Pawel Raasz authored and GitHub committed 2023-08-28 13:27:09 +04:00
1 parent 29ad3e8c92
commit 2e78eec502
3 files changed
+71 -41

No files matched your search

@@ -115,7 +115,7 @@ std::vector<TRShape> shape_infer(const Node* op,
const auto& boxes_shape = input_shapes[0];
const auto& scores_shape = input_shapes[1];
auto output_shapes = std::vector<TRShape>{TRShape{TDim(-1), 3}};
auto output_shapes = std::vector<TRShape>{TRShape{TDim(dim::inf_bound), 3}};
if (boxes_shape.rank().is_static()) {
const auto max_out_boxes_per_class = get_input_const_data_as<TRShape, int64_t>(op, 2, ta);
auto max_out_class_boxes =
@@ -147,6 +147,7 @@ std::vector<TRShape> shape_infer(const Node* op,
NODE_VALIDATION_CHECK(op, cmp::Between<size_t>(1, 7)(inputs_size));
using TDim = typename TRShape::value_type;
using V = typename TDim::value_type;
using namespace ov::util;
nms::validate::boxes_shape(op, input_shapes);
nms::validate::scores_shape(op, input_shapes);
@@ -180,44 +181,31 @@ std::vector<TRShape> shape_infer(const Node* op,
}
const auto& boxes_shape = input_shapes[0];
const auto& scores_shape = input_shapes[1];
const auto boxes_rank = boxes_shape.rank();
const auto scores_rank = scores_shape.rank();
auto out_shape = TRShape{TDim(-1), 3};
if (boxes_rank.is_static()) {
int64_t max_out_boxes_per_class_val;
if (const auto max_out_boxes_per_class = get_input_const_data_as<TRShape, int64_t>(op, 2, ta)) {
max_out_boxes_per_class_val = max_out_boxes_per_class->front();
} else {
max_out_boxes_per_class_val = -1;
}
const auto& num_boxes = boxes_shape[1];
auto& selected_boxes = out_shape[0];
if (num_boxes.is_static()) {
const auto min_selected_boxes =
std::min(num_boxes.get_length(), static_cast<V>(max_out_boxes_per_class_val));
selected_boxes = static_output ? TDim{min_selected_boxes} : TDim{0, min_selected_boxes};
} else if (scores_rank.is_static() && num_boxes.get_max_length() != -1 &&
scores_shape[0].get_max_length() != -1 && scores_shape[1].get_max_length() != -1) {
const auto min_selected_boxes =
std::min(num_boxes.get_max_length(), static_cast<V>(max_out_boxes_per_class_val));
selected_boxes = static_output ? TDim{min_selected_boxes} : TDim{0, min_selected_boxes};
}
auto out_shape = TRShape{TDim(dim::inf_bound), 3};
if (boxes_shape.rank().is_static()) {
const auto& scores_shape = input_shapes[1];
if (scores_rank.is_static()) {
if (scores_shape.rank().is_static()) {
nms::validate::num_batches(op, input_shapes);
nms::validate::num_boxes(op, input_shapes);
auto& selected_boxes = out_shape[0];
if (const auto max_out_boxes_per_class = get_input_const_data_as<TRShape, int64_t>(op, 2, ta)) {
const auto& num_boxes = boxes_shape[1];
const auto min_selected_boxes =
std::min(num_boxes.get_max_length(), static_cast<V>(max_out_boxes_per_class->front()));
selected_boxes = static_output ? TDim{min_selected_boxes} : TDim{0, min_selected_boxes};
}
selected_boxes *= scores_shape[0].get_max_length();
selected_boxes *= scores_shape[1].get_max_length();
}
nms::validate::boxes_last_dim(op, input_shapes);
}
auto output_shapes = std::vector<TRShape>(2, out_shape);
output_shapes.emplace_back(std::initializer_list<typename TRShape::value_type>{1});
output_shapes.emplace_back(std::initializer_list<V>{1});
return output_shapes;
}
} // namespace nms
@@ -257,20 +245,17 @@ std::vector<TRShape> shape_infer(const NonMaxSuppression* op,
const auto& boxes_shape = input_shapes[0];
const auto& scores_shape = input_shapes[1];
auto output_shapes = std::vector<TRShape>{TRShape{TDim(-1), 3}};
if (boxes_shape.rank().is_static()) {
const auto max_out_boxes_per_class = get_input_const_data_as<TRShape, int64_t>(op, 2, ta);
const auto max_out_class_boxes = max_out_boxes_per_class ? max_out_boxes_per_class->front() : dim::inf_bound;
auto output_shapes = std::vector<TRShape>{TRShape{TDim(dim::inf_bound), 3}};
if (boxes_shape.rank().is_static() && scores_shape.rank().is_static()) {
const auto& num_boxes = boxes_shape[1];
auto& selected_boxes = output_shapes[0][0];
if (num_boxes.is_static()) {
selected_boxes = std::min(num_boxes.get_length(), static_cast<V>(max_out_class_boxes));
}
if (scores_shape.rank().is_static()) {
selected_boxes *= scores_shape[0].get_max_length();
selected_boxes *= scores_shape[1].get_max_length();
if (const auto max_out_boxes_per_class = get_input_const_data_as<TRShape, int64_t>(op, 2, ta)) {
auto& selected_boxes = output_shapes[0][0];
selected_boxes = std::min(num_boxes.get_length(), static_cast<V>(max_out_boxes_per_class->front()));
selected_boxes *= scores_shape[0].get_max_length();
selected_boxes *= scores_shape[1].get_max_length();
}
}
}
@@ -368,6 +368,29 @@ TYPED_TEST_P(NMSDynamicOutputTest, interval_shapes_labels) {
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(2)), Each(no_label));
}
TYPED_TEST_P(NMSDynamicOutputTest, num_box_dynamic_dim_max_boxes_per_class_as_const) {
auto boxes_shape = PartialShape{2, -1, 4};
auto scores_shape = PartialShape{2, {0, 5}, {1, 7}};
set_shape_labels(boxes_shape, 10);
set_shape_labels(scores_shape, 20);
const auto boxes = make_shared<op::v0::Parameter>(element::f32, boxes_shape);
const auto scores = make_shared<op::v0::Parameter>(element::f32, scores_shape);
const auto max_output_boxes_per_class = op::v0::Constant::create(element::i16, Shape{}, {5});
const auto iou_threshold = make_shared<op::v0::Parameter>(element::f32, Shape{});
const auto score_threshold = make_shared<op::v0::Parameter>(element::f32, Shape{});
const auto op = this->make_op(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold);
EXPECT_THAT(op->outputs(),
ElementsAre(Property("Indicies shape", &Output<Node>::get_partial_shape, PartialShape({-1, 3})),
Property("Scores shape", &Output<Node>::get_partial_shape, PartialShape({-1, 3})),
Property("Outputs shape", &Output<Node>::get_partial_shape, PartialShape({1}))));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), Each(no_label));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(1)), Each(no_label));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(2)), Each(no_label));
}
TYPED_TEST_P(NMSDynamicOutputTest, output_shape_i32) {
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{2, 7, 4});
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{2, 5, 7});
@@ -429,6 +452,25 @@ TYPED_TEST_P(NMSDynamicOutputTest, dynamic_types) {
Property("Outputs shape", &Output<Node>::get_partial_shape, PartialShape({1}))));
}
TYPED_TEST_P(NMSDynamicOutputTest, scores_shape_is_dynamic_rank) {
const auto boxes = make_shared<op::v0::Parameter>(element::dynamic, Shape{5, 2, 4});
const auto scores = make_shared<op::v0::Parameter>(element::dynamic, PartialShape::dynamic());
const auto max_output_boxes_per_class = op::v0::Constant::create(element::i16, Shape{}, {3});
const auto iou_threshold = make_shared<op::v0::Parameter>(element::f32, Shape{});
const auto score_threshold = make_shared<op::v0::Parameter>(element::f32, Shape{});
const auto op = this->make_op(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold);
EXPECT_THAT(op->outputs(),
ElementsAre(Property("Indicies type", &Output<Node>::get_element_type, element::i64),
Property("Scores type", &Output<Node>::get_element_type, element::f32),
Property("Outputs type", &Output<Node>::get_element_type, element::i64)));
EXPECT_THAT(op->outputs(),
ElementsAre(Property("Indicies shape", &Output<Node>::get_partial_shape, PartialShape({-1, 3})),
Property("Scores shape", &Output<Node>::get_partial_shape, PartialShape({-1, 3})),
Property("Outputs shape", &Output<Node>::get_partial_shape, PartialShape({1}))));
}
REGISTER_TYPED_TEST_SUITE_P(NMSDynamicOutputTest,
scalar_inputs_check,
boxes_scores_static_other_defaults,
@@ -436,8 +478,10 @@ REGISTER_TYPED_TEST_SUITE_P(NMSDynamicOutputTest,
num_boxes_lt_max_out_boxes,
max_out_boxes_is_zero,
interval_shapes_labels,
num_box_dynamic_dim_max_boxes_per_class_as_const,
output_shape_i32,
dynamic_boxes_and_scores,
dynamic_types);
dynamic_types,
scores_shape_is_dynamic_rank);
using NMSDynamicOutputTypes = testing::Types<op::v5::NonMaxSuppression, op::v9::NonMaxSuppression>;
INSTANTIATE_TYPED_TEST_SUITE_P(type_prop, NMSDynamicOutputTest, NMSDynamicOutputTypes);
@@ -35,11 +35,12 @@ namespace SubgraphTestsDefinitions {
\ /
\ /
\ /
MatMul
MatMul
|
Result
This test is needed to cover logic that allows to avoid computational error for subgraph: "[I32] -> Convert -> [F32] -> Range" due to precision lowering for floating point path inside "EnforceInferencePrecision" pass".
This test is needed to cover logic that allows to avoid computational error for subgraph: "[I32] -> Convert -> [F32]
-> Range" due to precision lowering for floating point path inside "EnforceInferencePrecision" pass".
TODO: Incorrect subgraph is generated by ONNX FE + ticket 117861.
*/