[Ref][Core][Opset13] NMSRotated-13 core shell and reference implementation (#19907)
* nms_r_init * Add tests * Update nms refs * Update onnx import * Add nms rotated utils * Remove soft sigma and align constructors * Fix typo * Style apply * Add namespace for iou * Update opset comment * Revert cpu changes * onnx test cleanup * Update input types validation * Align shape_infer boxes * Test update * Fix warning * Temporary evaluate support for tests * Add counterclockwise support * Remove box_encoding attr * Fix clockwise box idx * More tests * Update opset test * Update boxes shape validation * Type prop tests * HostTensor to ov Tensor migration * Update output_type set get output_type_attr * Move setters and getters to cpp * Add visitor test * Cleanup * Remove temp eval * Headers adjustment * use float for division * Fix ref tests run * Tests and style code refactor * Move type check into box_last_dim * Update visitor test namespace * Check input type loop * Remove nms_rotated namespace and rename ref function * avoid copies in filling output tensor * Update shape var name * remove static from riou func * Update nms_rot utils * Move nms rot util to ov reference namespace * use std::cos and std:::sin * Update struct name * Explain usage of postprocessing * Update element type desc in error message * Add more comments * Adjust rotated util float types * Fix name conflicts and warnings * Update opset test ops number * Move int input check to the loop * Short box_def_size init * Move remove static_output from shape_infer params * Align float zero * Update third-party-programs * Fix TensorIt for CI * Add op check test
This commit is contained in:
parent
77e043d342
commit
86bf038417
|
@ -1666,3 +1666,210 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-------------------------------------------------------------
|
||||
|
||||
31. Detectron2 (https://github.com/facebookresearch/detectron2)
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
64
src/core/include/openvino/op/nms_rotated.hpp
Normal file
64
src/core/include/openvino/op/nms_rotated.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/op/op.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace op {
|
||||
|
||||
namespace v13 {
|
||||
/// \brief NMSRotated operation
|
||||
///
|
||||
class OPENVINO_API NMSRotated : public Op {
|
||||
public:
|
||||
OPENVINO_OP("NMSRotated", "opset13", op::Op);
|
||||
|
||||
NMSRotated() = default;
|
||||
|
||||
/// \brief Constructs a NMSRotated operation.
|
||||
///
|
||||
/// \param boxes Node containing the coordinates of the bounding boxes
|
||||
/// \param scores Node containing the scores of the bounding boxes
|
||||
/// \param max_output_boxes_per_class Node containing maximum number of boxes to be
|
||||
/// selected per class
|
||||
/// \param iou_threshold Node containing intersection over union threshold
|
||||
/// \param score_threshold Node containing minimum score threshold
|
||||
/// \param sort_result_descending Specifies whether it is necessary to sort selected
|
||||
/// boxes across batches
|
||||
/// \param output_type Specifies the output type of the first and third output
|
||||
/// \param clockwise Specifies the direction of the rotation
|
||||
NMSRotated(const Output<Node>& boxes,
|
||||
const Output<Node>& scores,
|
||||
const Output<Node>& max_output_boxes_per_class,
|
||||
const Output<Node>& iou_threshold,
|
||||
const Output<Node>& score_threshold,
|
||||
const bool sort_result_descending = true,
|
||||
const ov::element::Type& output_type = ov::element::i64,
|
||||
const bool clockwise = true);
|
||||
|
||||
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;
|
||||
|
||||
bool get_sort_result_descending() const;
|
||||
void set_sort_result_descending(const bool sort_result_descending);
|
||||
|
||||
element::Type get_output_type_attr() const;
|
||||
void set_output_type_attr(const element::Type& output_type);
|
||||
|
||||
bool get_clockwise() const;
|
||||
void set_clockwise(const bool clockwise);
|
||||
|
||||
protected:
|
||||
bool m_sort_result_descending = true;
|
||||
ov::element::Type m_output_type = ov::element::i64;
|
||||
bool m_clockwise = true;
|
||||
};
|
||||
} // namespace v13
|
||||
} // namespace op
|
||||
|
||||
} // namespace ov
|
|
@ -110,6 +110,7 @@
|
|||
#include "openvino/op/multiply.hpp"
|
||||
#include "openvino/op/mvn.hpp"
|
||||
#include "openvino/op/negative.hpp"
|
||||
#include "openvino/op/nms_rotated.hpp"
|
||||
#include "openvino/op/non_max_suppression.hpp"
|
||||
#include "openvino/op/non_zero.hpp"
|
||||
#include "openvino/op/normalize_l2.hpp"
|
||||
|
|
|
@ -210,3 +210,4 @@ _OPENVINO_OP_REG(ScatterElementsUpdate, ov::op::v12)
|
|||
|
||||
// New operations added in opset13
|
||||
_OPENVINO_OP_REG(BitwiseNot, ov::op::v13)
|
||||
_OPENVINO_OP_REG(NMSRotated, ov::op::v13)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/core/shape.hpp"
|
||||
#include "openvino/reference/non_max_suppression.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace reference {
|
||||
|
||||
void nms_rotated(const float* boxes_data,
|
||||
const Shape& boxes_data_shape,
|
||||
const float* scores_data,
|
||||
const Shape& scores_data_shape,
|
||||
int64_t max_output_boxes_per_class,
|
||||
float iou_threshold,
|
||||
float score_threshold,
|
||||
float soft_nms_sigma,
|
||||
int64_t* selected_indices,
|
||||
const Shape& selected_indices_shape,
|
||||
float* selected_scores,
|
||||
const Shape& selected_scores_shape,
|
||||
int64_t* valid_outputs,
|
||||
bool sort_result_descending,
|
||||
bool clockwise = true);
|
||||
|
||||
constexpr auto nms_rotated_postprocessing = ov::reference::nms_postprocessing;
|
||||
|
||||
} // namespace reference
|
||||
} // namespace ov
|
|
@ -0,0 +1,281 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
// The implementation for rotated boxes intersection is based on the code from:
|
||||
// https://github.com/facebookresearch/detectron2/blob/v0.6/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated_utils.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
namespace ov {
|
||||
namespace reference {
|
||||
namespace iou_rotated {
|
||||
struct RotatedBox {
|
||||
float x_ctr, y_ctr, w, h, a;
|
||||
};
|
||||
struct Point2D {
|
||||
float x, y;
|
||||
Point2D(const float px = 0.f, const float py = 0.f) : x(px), y(py) {}
|
||||
Point2D operator+(const Point2D& p) const {
|
||||
return Point2D(x + p.x, y + p.y);
|
||||
}
|
||||
Point2D& operator+=(const Point2D& p) {
|
||||
x += p.x;
|
||||
y += p.y;
|
||||
return *this;
|
||||
}
|
||||
Point2D operator-(const Point2D& p) const {
|
||||
return Point2D(x - p.x, y - p.y);
|
||||
}
|
||||
Point2D operator*(const float coeff) const {
|
||||
return Point2D(x * coeff, y * coeff);
|
||||
}
|
||||
};
|
||||
|
||||
static inline float dot_2d(const Point2D& A, const Point2D& B) {
|
||||
return A.x * B.x + A.y * B.y;
|
||||
}
|
||||
|
||||
static inline float cross_2d(const Point2D& A, const Point2D& B) {
|
||||
return A.x * B.y - B.x * A.y;
|
||||
}
|
||||
|
||||
// Calculate box vertices rotated by angle (clockwise) over the box center
|
||||
static inline void get_rotated_vertices(const RotatedBox& box, Point2D (&pts)[4]) {
|
||||
// M_PI / 180. == 0.01745329251
|
||||
auto theta = box.a; // angle already in radians
|
||||
auto cosTheta2 = std::cos(theta) * 0.5f;
|
||||
auto sinTheta2 = std::sin(theta) * 0.5f;
|
||||
|
||||
// y: top --> down; x: left --> right
|
||||
// Left-Down
|
||||
pts[0].x = box.x_ctr - sinTheta2 * box.h - cosTheta2 * box.w;
|
||||
pts[0].y = box.y_ctr + cosTheta2 * box.h - sinTheta2 * box.w;
|
||||
// Left-Top
|
||||
pts[1].x = box.x_ctr + sinTheta2 * box.h - cosTheta2 * box.w;
|
||||
pts[1].y = box.y_ctr - cosTheta2 * box.h - sinTheta2 * box.w;
|
||||
// Right-Top
|
||||
pts[2].x = 2 * box.x_ctr - pts[0].x;
|
||||
pts[2].y = 2 * box.y_ctr - pts[0].y;
|
||||
// Right-Down
|
||||
pts[3].x = 2 * box.x_ctr - pts[1].x;
|
||||
pts[3].y = 2 * box.y_ctr - pts[1].y;
|
||||
}
|
||||
|
||||
// Find points defining area of the boxes intersection:
|
||||
// - Find all intersection points between edges of the boxes
|
||||
// - Find all corners of box1 within area of box2, and all corners of box2 within area of box1
|
||||
static inline int get_intersection_points(const Point2D (&pts1)[4],
|
||||
const Point2D (&pts2)[4],
|
||||
Point2D (&intersections)[24]) {
|
||||
// Line vector
|
||||
// A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1]
|
||||
Point2D vec1[4], vec2[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec1[i] = pts1[(i + 1) % 4] - pts1[i];
|
||||
vec2[i] = pts2[(i + 1) % 4] - pts2[i];
|
||||
}
|
||||
|
||||
// Line test - test all line combos for intersection
|
||||
int num = 0; // number of intersections
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
// Solve for 2x2 Ax=b
|
||||
float det = cross_2d(vec2[j], vec1[i]);
|
||||
|
||||
// This takes care of parallel lines
|
||||
if (std::abs(det) <= 1e-14f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto vec12 = pts2[j] - pts1[i];
|
||||
|
||||
auto t1 = cross_2d(vec2[j], vec12) / det;
|
||||
auto t2 = cross_2d(vec1[i], vec12) / det;
|
||||
|
||||
if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f) {
|
||||
intersections[num++] = pts1[i] + vec1[i] * t1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for vertices of rect1 inside rect2
|
||||
{
|
||||
const auto& AB = vec2[0];
|
||||
const auto& DA = vec2[3];
|
||||
auto ABdotAB = dot_2d(AB, AB);
|
||||
auto ADdotAD = dot_2d(DA, DA);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// assume ABCD is the rectangle, and P is the point to be judged
|
||||
// P is inside ABCD iff. P's projection on AB lies within AB
|
||||
// and P's projection on AD lies within AD
|
||||
|
||||
auto AP = pts1[i] - pts2[0];
|
||||
|
||||
auto APdotAB = dot_2d(AP, AB);
|
||||
auto APdotAD = -dot_2d(AP, DA);
|
||||
|
||||
if ((APdotAB >= 0) && (APdotAD >= 0) && (APdotAB <= ABdotAB) && (APdotAD <= ADdotAD)) {
|
||||
intersections[num++] = pts1[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the check - check for vertices of rect2 inside rect1
|
||||
{
|
||||
const auto& AB = vec1[0];
|
||||
const auto& DA = vec1[3];
|
||||
auto ABdotAB = dot_2d(AB, AB);
|
||||
auto ADdotAD = dot_2d(DA, DA);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto AP = pts2[i] - pts1[0];
|
||||
|
||||
auto APdotAB = dot_2d(AP, AB);
|
||||
auto APdotAD = -dot_2d(AP, DA);
|
||||
|
||||
if ((APdotAB >= 0) && (APdotAD >= 0) && (APdotAB <= ABdotAB) && (APdotAD <= ADdotAD)) {
|
||||
intersections[num++] = pts2[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static inline int convex_hull_graham(const Point2D (&p)[24],
|
||||
const int num_in,
|
||||
Point2D (&q)[24],
|
||||
bool shift_to_zero = false) {
|
||||
assert(num_in >= 2);
|
||||
|
||||
// Step 1:
|
||||
// Find point with minimum y
|
||||
// if more than 1 points have the same minimum y,
|
||||
// pick the one with the minimum x.
|
||||
int t = 0;
|
||||
for (int i = 1; i < num_in; i++) {
|
||||
if (p[i].y < p[t].y || (p[i].y == p[t].y && p[i].x < p[t].x)) {
|
||||
t = i;
|
||||
}
|
||||
}
|
||||
auto& start = p[t]; // starting point
|
||||
|
||||
// Step 2:
|
||||
// Subtract starting point from every points (for sorting in the next step)
|
||||
for (int i = 0; i < num_in; i++) {
|
||||
q[i] = p[i] - start;
|
||||
}
|
||||
|
||||
// Swap the starting point to position 0
|
||||
std::swap(q[t], q[0]);
|
||||
|
||||
// Step 3:
|
||||
// Sort point 1 ~ num_in according to their relative cross-product values
|
||||
// (essentially sorting according to angles)
|
||||
// If the angles are the same, sort according to their distance to origin
|
||||
float dist[24];
|
||||
for (int i = 0; i < num_in; i++) {
|
||||
dist[i] = dot_2d(q[i], q[i]);
|
||||
}
|
||||
|
||||
std::sort(q + 1, q + num_in, [](const Point2D& A, const Point2D& B) -> bool {
|
||||
float temp = cross_2d(A, B);
|
||||
if (std::abs(temp) < 1e-6f) {
|
||||
return dot_2d(A, A) < dot_2d(B, B);
|
||||
} else {
|
||||
return temp > 0;
|
||||
}
|
||||
});
|
||||
// compute distance to origin after sort, since the points are now different.
|
||||
for (int i = 0; i < num_in; i++) {
|
||||
dist[i] = dot_2d(q[i], q[i]);
|
||||
}
|
||||
|
||||
// Step 4:
|
||||
// Make sure there are at least 2 points (that don't overlap with each other)
|
||||
// in the stack
|
||||
int k; // index of the non-overlapped second point
|
||||
for (k = 1; k < num_in; k++) {
|
||||
if (dist[k] > 1e-8f) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == num_in) {
|
||||
// We reach the end, which means the convex hull is just one point
|
||||
q[0] = p[t];
|
||||
return 1;
|
||||
}
|
||||
q[1] = q[k];
|
||||
int m = 2; // 2 points in the stack
|
||||
// Step 5:
|
||||
// Finally we can start the scanning process.
|
||||
// When a non-convex relationship between the 3 points is found
|
||||
// (either concave shape or duplicated points),
|
||||
// we pop the previous point from the stack
|
||||
// until the 3-point relationship is convex again, or
|
||||
// until the stack only contains two points
|
||||
for (int i = k + 1; i < num_in; i++) {
|
||||
while (m > 1 && cross_2d(q[i] - q[m - 2], q[m - 1] - q[m - 2]) >= 0) {
|
||||
m--;
|
||||
}
|
||||
q[m++] = q[i];
|
||||
}
|
||||
|
||||
// Step 6 (Optional):
|
||||
// In general sense we need the original coordinates, so we
|
||||
// need to shift the points back (reverting Step 2)
|
||||
// But if we're only interested in getting the area/perimeter of the shape
|
||||
// We can simply return.
|
||||
if (!shift_to_zero) {
|
||||
for (int i = 0; i < m; i++) {
|
||||
q[i] += start;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline float polygon_area(const Point2D (&q)[24], const int& m) {
|
||||
if (m <= 2) {
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
float area = 0.f;
|
||||
for (int i = 1; i < m - 1; i++) {
|
||||
area += std::abs(cross_2d(q[i] - q[0], q[i + 1] - q[0]));
|
||||
}
|
||||
|
||||
return area / 2.0f;
|
||||
}
|
||||
|
||||
static inline float rotated_boxes_intersection(const RotatedBox& box1, const RotatedBox& box2) {
|
||||
// There are up to 4 x 4 + 4 + 4 = 24 intersections (including dups) returned
|
||||
// from get_intersection_points
|
||||
Point2D intersectPts[24], orderedPts[24];
|
||||
|
||||
Point2D pts1[4];
|
||||
Point2D pts2[4];
|
||||
get_rotated_vertices(box1, pts1);
|
||||
get_rotated_vertices(box2, pts2);
|
||||
|
||||
// Find points defining area of the boxes intersection
|
||||
int num = get_intersection_points(pts1, pts2, intersectPts);
|
||||
|
||||
if (num <= 2) {
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
// Convex Hull to order the intersection points in clockwise order and find
|
||||
// the contour area.
|
||||
int num_convex = convex_hull_graham(intersectPts, num, orderedPts, true);
|
||||
return polygon_area(orderedPts, num_convex);
|
||||
}
|
||||
|
||||
} // namespace iou_rotated
|
||||
|
||||
} // namespace reference
|
||||
} // namespace ov
|
222
src/core/reference/src/op/nms_rotated.cpp
Normal file
222
src/core/reference/src/op/nms_rotated.cpp
Normal file
|
@ -0,0 +1,222 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "openvino/reference/nms_rotated.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "openvino/reference/nms_rotated_util.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace reference {
|
||||
namespace nms_detail {
|
||||
using iou_rotated::RotatedBox;
|
||||
static float rotated_intersection_over_union(const RotatedBox& boxI, const RotatedBox& boxJ) {
|
||||
const auto intersection = iou_rotated::rotated_boxes_intersection(boxI, boxJ);
|
||||
const auto areaI = boxI.w * boxI.h;
|
||||
const auto areaJ = boxJ.w * boxJ.h;
|
||||
|
||||
if (areaI <= 0.0f || areaJ <= 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const auto union_area = areaI + areaJ - intersection;
|
||||
return intersection / union_area;
|
||||
}
|
||||
|
||||
struct SelectedIndex {
|
||||
SelectedIndex(int64_t batch_idx, int64_t class_idx, int64_t box_idx)
|
||||
: batch_index(batch_idx),
|
||||
class_index(class_idx),
|
||||
box_index(box_idx) {}
|
||||
|
||||
SelectedIndex() = default;
|
||||
|
||||
int64_t batch_index = 0;
|
||||
int64_t class_index = 0;
|
||||
int64_t box_index = 0;
|
||||
};
|
||||
|
||||
struct SelectedScore {
|
||||
SelectedScore(float batch_idx, float class_idx, float score)
|
||||
: batch_index{batch_idx},
|
||||
class_index{class_idx},
|
||||
box_score{score} {}
|
||||
|
||||
SelectedScore() = default;
|
||||
|
||||
float batch_index = 0.0f;
|
||||
float class_index = 0.0f;
|
||||
float box_score = 0.0f;
|
||||
};
|
||||
|
||||
struct BoxInfo {
|
||||
BoxInfo(const RotatedBox& r, int64_t idx, float sc, int64_t suppress_idx, int64_t batch_idx, int64_t class_idx)
|
||||
: box{r},
|
||||
index{idx},
|
||||
suppress_begin_index{suppress_idx},
|
||||
batch_index{batch_idx},
|
||||
class_index{class_idx},
|
||||
score{sc} {}
|
||||
|
||||
BoxInfo() = default;
|
||||
|
||||
inline bool operator<(const BoxInfo& rhs) const {
|
||||
return score < rhs.score || (score == rhs.score && index > rhs.index);
|
||||
}
|
||||
|
||||
RotatedBox box;
|
||||
int64_t index = 0;
|
||||
int64_t suppress_begin_index = 0;
|
||||
int64_t batch_index = 0;
|
||||
int64_t class_index = 0;
|
||||
float score = 0.0f;
|
||||
};
|
||||
} // namespace nms_detail
|
||||
|
||||
void nms_rotated(const float* boxes_data,
|
||||
const Shape& boxes_data_shape,
|
||||
const float* scores_data,
|
||||
const Shape& scores_data_shape,
|
||||
int64_t max_output_boxes_per_class,
|
||||
float iou_threshold,
|
||||
float score_threshold,
|
||||
float soft_nms_sigma,
|
||||
int64_t* selected_indices,
|
||||
const Shape& selected_indices_shape,
|
||||
float* selected_scores,
|
||||
const Shape& selected_scores_shape,
|
||||
int64_t* valid_outputs,
|
||||
const bool sort_result_descending,
|
||||
const bool clockwise) {
|
||||
using iou_rotated::RotatedBox;
|
||||
using nms_detail::BoxInfo;
|
||||
using nms_detail::SelectedIndex;
|
||||
using nms_detail::SelectedScore;
|
||||
|
||||
// The code for softsigma is kept to simplify unification with NMS code,
|
||||
// but for NMSRotated softsigma is not supported (always 0.0);
|
||||
float scale = 0.0f;
|
||||
bool soft_nms = false;
|
||||
if (soft_nms_sigma > 0.0f) {
|
||||
scale = -0.5f / soft_nms_sigma;
|
||||
soft_nms = true;
|
||||
}
|
||||
|
||||
auto get_score_scale = [iou_threshold, scale, soft_nms](float iou) {
|
||||
const float weight = std::exp(scale * iou * iou);
|
||||
return (soft_nms || iou <= iou_threshold) ? weight : 0.0f;
|
||||
};
|
||||
|
||||
// boxes shape: {num_batches, num_boxes, 5}
|
||||
// scores shape: {num_batches, num_classes, num_boxes}
|
||||
int64_t num_batches = static_cast<int64_t>(scores_data_shape[0]);
|
||||
int64_t num_classes = static_cast<int64_t>(scores_data_shape[1]);
|
||||
int64_t num_boxes = static_cast<int64_t>(boxes_data_shape[1]);
|
||||
|
||||
SelectedIndex* selected_indices_ptr = reinterpret_cast<SelectedIndex*>(selected_indices);
|
||||
SelectedScore* selected_scores_ptr = reinterpret_cast<SelectedScore*>(selected_scores);
|
||||
|
||||
size_t boxes_per_class = static_cast<size_t>(max_output_boxes_per_class);
|
||||
|
||||
std::vector<BoxInfo> filteredBoxes;
|
||||
|
||||
for (int64_t batch = 0; batch < num_batches; batch++) {
|
||||
const float* boxesPtr = boxes_data + batch * num_boxes * 5;
|
||||
RotatedBox* r = reinterpret_cast<RotatedBox*>(const_cast<float*>(boxesPtr));
|
||||
|
||||
for (int64_t class_idx = 0; class_idx < num_classes; class_idx++) {
|
||||
const float* scoresPtr = scores_data + batch * (num_classes * num_boxes) + class_idx * num_boxes;
|
||||
|
||||
std::vector<BoxInfo> candidate_boxes;
|
||||
candidate_boxes.reserve(num_boxes);
|
||||
|
||||
for (int64_t box_idx = 0; box_idx < num_boxes; box_idx++) {
|
||||
if (scoresPtr[box_idx] > score_threshold) {
|
||||
// Convert counterclockwise to clockwise
|
||||
if (!clockwise) {
|
||||
r[box_idx].a *= -1;
|
||||
}
|
||||
candidate_boxes.emplace_back(r[box_idx], box_idx, scoresPtr[box_idx], 0, batch, class_idx);
|
||||
}
|
||||
}
|
||||
|
||||
std::priority_queue<BoxInfo> sorted_boxes(std::less<BoxInfo>(), std::move(candidate_boxes));
|
||||
|
||||
std::vector<BoxInfo> selected;
|
||||
// Get the next box with top score, filter by iou_threshold
|
||||
|
||||
BoxInfo next_candidate;
|
||||
float original_score;
|
||||
|
||||
while (!sorted_boxes.empty() && selected.size() < boxes_per_class) {
|
||||
next_candidate = sorted_boxes.top();
|
||||
original_score = next_candidate.score;
|
||||
sorted_boxes.pop();
|
||||
|
||||
bool should_hard_suppress = false;
|
||||
for (int64_t j = static_cast<int64_t>(selected.size()) - 1; j >= next_candidate.suppress_begin_index;
|
||||
--j) {
|
||||
// The main difference between NMS and NMSRotated is the calculation of iou for rotated boxes
|
||||
float iou = nms_detail::rotated_intersection_over_union(next_candidate.box, selected[j].box);
|
||||
next_candidate.score *= get_score_scale(iou);
|
||||
|
||||
if ((iou > iou_threshold) && !soft_nms) {
|
||||
should_hard_suppress = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (next_candidate.score <= score_threshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
next_candidate.suppress_begin_index = selected.size();
|
||||
|
||||
if (!should_hard_suppress) {
|
||||
if (next_candidate.score == original_score) {
|
||||
selected.push_back(next_candidate);
|
||||
continue;
|
||||
}
|
||||
if (next_candidate.score > score_threshold) {
|
||||
sorted_boxes.push(next_candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& box_info : selected) {
|
||||
filteredBoxes.push_back(box_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sort_result_descending) {
|
||||
std::reverse(filteredBoxes.begin(), filteredBoxes.end());
|
||||
}
|
||||
|
||||
size_t max_num_of_selected_indices = selected_indices_shape[0];
|
||||
size_t output_size = std::min(filteredBoxes.size(), max_num_of_selected_indices);
|
||||
|
||||
*valid_outputs = output_size;
|
||||
|
||||
size_t idx;
|
||||
for (idx = 0; idx < output_size; idx++) {
|
||||
const auto& box_info = filteredBoxes[idx];
|
||||
selected_indices_ptr[idx] = SelectedIndex{box_info.batch_index, box_info.class_index, box_info.index};
|
||||
selected_scores_ptr[idx] = SelectedScore{static_cast<float>(box_info.batch_index),
|
||||
static_cast<float>(box_info.class_index),
|
||||
box_info.score};
|
||||
}
|
||||
|
||||
for (; idx < max_num_of_selected_indices; idx++) {
|
||||
selected_indices_ptr[idx] = SelectedIndex{0, 0, 0};
|
||||
selected_scores_ptr[idx] = SelectedScore{0.0f, 0.0f, 0.0f};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reference
|
||||
} // namespace ov
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "compare.hpp"
|
||||
#include "dimension_util.hpp"
|
||||
#include "openvino/op/nms_rotated.hpp"
|
||||
#include "openvino/op/non_max_suppression.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
|
@ -59,11 +60,15 @@ void num_boxes(const Node* const op, const std::vector<TShape>& input_shapes) {
|
|||
|
||||
template <class TShape>
|
||||
void boxes_last_dim(const Node* const op, const std::vector<TShape>& input_shapes) {
|
||||
using TDim = typename TShape::value_type;
|
||||
TDim box_def_size = ov::is_type<v13::NMSRotated>(op) ? 5 : 4;
|
||||
NODE_SHAPE_INFER_CHECK(op,
|
||||
input_shapes,
|
||||
input_shapes[0][2].compatible(4),
|
||||
"The last dimension of the 'boxes' input must be equal to 4");
|
||||
input_shapes[0][2].compatible(box_def_size),
|
||||
"The last dimension of the 'boxes' input must be equal to ",
|
||||
box_def_size);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void shapes(const Node* op, const std::vector<T>& input_shapes) {
|
||||
const auto inputs_size = input_shapes.size();
|
||||
|
@ -201,6 +206,7 @@ std::vector<TRShape> shape_infer(const Node* op,
|
|||
selected_boxes *= scores_shape[0].get_max_length();
|
||||
selected_boxes *= scores_shape[1].get_max_length();
|
||||
}
|
||||
|
||||
nms::validate::boxes_last_dim(op, input_shapes);
|
||||
}
|
||||
|
||||
|
@ -284,5 +290,15 @@ std::vector<TRShape> shape_infer(const NonMaxSuppression* op,
|
|||
return nms::shape_infer(op, input_shapes, ta, static_output);
|
||||
}
|
||||
} // namespace v9
|
||||
|
||||
namespace v13 {
|
||||
template <class T, class TRShape = result_shape_t<T>>
|
||||
std::vector<TRShape> shape_infer(const NMSRotated* op,
|
||||
const std::vector<T>& input_shapes,
|
||||
const ITensorAccessor& ta = make_tensor_accessor()) {
|
||||
constexpr bool static_output = !std::is_same<T, PartialShape>::value;
|
||||
return nms::shape_infer(op, input_shapes, ta, static_output);
|
||||
}
|
||||
} // namespace v13
|
||||
} // namespace op
|
||||
} // namespace ov
|
||||
|
|
121
src/core/src/op/nms_rotated.cpp
Normal file
121
src/core/src/op/nms_rotated.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "openvino/op/nms_rotated.hpp"
|
||||
|
||||
#include "itt.hpp"
|
||||
#include "nms_shape_inference.hpp"
|
||||
#include "openvino/core/attribute_visitor.hpp"
|
||||
#include "openvino/op/util/op_types.hpp"
|
||||
|
||||
namespace ov {
|
||||
|
||||
namespace op {
|
||||
namespace nms_rotated {
|
||||
namespace validate {
|
||||
namespace {
|
||||
void input_types(const Node* op) {
|
||||
const auto inputs_size = op->get_input_size();
|
||||
|
||||
NODE_VALIDATION_CHECK(op, inputs_size == 5, "Expected 5 inputs to be provided.");
|
||||
constexpr size_t integer_input_idx = 2;
|
||||
for (size_t i = 0; i < inputs_size; ++i) {
|
||||
if (i == integer_input_idx) {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
op->get_input_element_type(integer_input_idx).is_integral_number() ||
|
||||
op->get_input_element_type(integer_input_idx).is_dynamic(),
|
||||
"Expected integer type as element type for the input at: 2");
|
||||
} else {
|
||||
NODE_VALIDATION_CHECK(op,
|
||||
op->get_input_element_type(i).is_real() || op->get_input_element_type(i).is_dynamic(),
|
||||
"Expected floating point type as element type for the input at: ",
|
||||
i);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace validate
|
||||
} // namespace nms_rotated
|
||||
} // namespace op
|
||||
// ------------------------------ v13 ------------------------------
|
||||
|
||||
op::v13::NMSRotated::NMSRotated(const Output<Node>& boxes,
|
||||
const Output<Node>& scores,
|
||||
const Output<Node>& max_output_boxes_per_class,
|
||||
const Output<Node>& iou_threshold,
|
||||
const Output<Node>& score_threshold,
|
||||
const bool sort_result_descending,
|
||||
const element::Type& output_type,
|
||||
const bool clockwise)
|
||||
: Op({boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold}),
|
||||
m_sort_result_descending{sort_result_descending},
|
||||
m_output_type{output_type},
|
||||
m_clockwise{clockwise} {
|
||||
constructor_validate_and_infer_types();
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> op::v13::NMSRotated::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
OV_OP_SCOPE(v13_NMSRotated_clone_with_new_inputs);
|
||||
check_new_args_count(this, new_args);
|
||||
NODE_VALIDATION_CHECK(this, new_args.size() == 5, "Number of inputs must be 5");
|
||||
|
||||
return std::make_shared<op::v13::NMSRotated>(new_args.at(0),
|
||||
new_args.at(1),
|
||||
new_args.at(2),
|
||||
new_args.at(3),
|
||||
new_args.at(4),
|
||||
m_sort_result_descending,
|
||||
m_output_type,
|
||||
m_clockwise);
|
||||
}
|
||||
|
||||
bool op::v13::NMSRotated::visit_attributes(AttributeVisitor& visitor) {
|
||||
OV_OP_SCOPE(v13_NMSRotated_visit_attributes);
|
||||
visitor.on_attribute("sort_result_descending", m_sort_result_descending);
|
||||
visitor.on_attribute("output_type", m_output_type);
|
||||
visitor.on_attribute("clockwise", m_clockwise);
|
||||
return true;
|
||||
}
|
||||
|
||||
void op::v13::NMSRotated::validate_and_infer_types() {
|
||||
OV_OP_SCOPE(v13_NMSRotated_validate_and_infer_types);
|
||||
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
const auto input_shapes = get_node_input_partial_shapes(*this);
|
||||
OPENVINO_SUPPRESS_DEPRECATED_END
|
||||
|
||||
const auto output_shapes = shape_infer(this, input_shapes);
|
||||
|
||||
nms_rotated::validate::input_types(this);
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
m_output_type == element::i64 || m_output_type == element::i32,
|
||||
"The `output_type` attribute (related to the first and third output) must be i32 or i64.");
|
||||
|
||||
set_output_type(0, m_output_type, output_shapes[0]);
|
||||
set_output_type(1, element::f32, output_shapes[1]);
|
||||
set_output_type(2, m_output_type, output_shapes[2]);
|
||||
}
|
||||
|
||||
bool op::v13::NMSRotated::get_sort_result_descending() const {
|
||||
return m_sort_result_descending;
|
||||
}
|
||||
void op::v13::NMSRotated::set_sort_result_descending(const bool sort_result_descending) {
|
||||
m_sort_result_descending = sort_result_descending;
|
||||
}
|
||||
|
||||
element::Type op::v13::NMSRotated::get_output_type_attr() const {
|
||||
return m_output_type;
|
||||
}
|
||||
void op::v13::NMSRotated::set_output_type_attr(const element::Type& output_type) {
|
||||
m_output_type = output_type;
|
||||
}
|
||||
|
||||
bool op::v13::NMSRotated::get_clockwise() const {
|
||||
return m_clockwise;
|
||||
}
|
||||
void op::v13::NMSRotated::set_clockwise(const bool clockwise) {
|
||||
m_clockwise = clockwise;
|
||||
}
|
||||
|
||||
} // namespace ov
|
|
@ -53,10 +53,10 @@ ov::op::util::ActivationFunction ov::op::util::get_activation_func_by_name(const
|
|||
using ActivationFunctionMap = std::unordered_map<std::string, op::util::ActivationFunction>;
|
||||
|
||||
static ActivationFunctionMap func_map{
|
||||
{"sigmoid", op::util::ActivationFunction{sigmoid}},
|
||||
{"tanh", op::util::ActivationFunction{tanh}},
|
||||
{"relu", op::util::ActivationFunction{relu}},
|
||||
{"hardsigmoid", op::util::ActivationFunction{hardsigmoid, 0.2f, 0.5f}},
|
||||
{"sigmoid", op::util::ActivationFunction{::sigmoid}},
|
||||
{"tanh", op::util::ActivationFunction{::tanh}},
|
||||
{"relu", op::util::ActivationFunction{::relu}},
|
||||
{"hardsigmoid", op::util::ActivationFunction{::hardsigmoid, 0.2f, 0.5f}},
|
||||
};
|
||||
|
||||
auto func_it = func_map.find(func_name);
|
||||
|
|
|
@ -71,7 +71,7 @@ INSTANTIATE_TEST_SUITE_P(opset,
|
|||
OpsetTestParams{ov::get_opset10, 177},
|
||||
OpsetTestParams{ov::get_opset11, 177},
|
||||
OpsetTestParams{ov::get_opset12, 178},
|
||||
OpsetTestParams{ov::get_opset13, 179}),
|
||||
OpsetTestParams{ov::get_opset13, 180}),
|
||||
OpsetTestNameGenerator{});
|
||||
|
||||
class MyOpOld : public ov::op::Op {
|
||||
|
|
370
src/core/tests/type_prop/nms_rotated.cpp
Normal file
370
src/core/tests/type_prop/nms_rotated.cpp
Normal file
|
@ -0,0 +1,370 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "openvino/op/nms_rotated.hpp"
|
||||
|
||||
#include "common_test_utils/test_assertions.hpp"
|
||||
#include "common_test_utils/type_prop.hpp"
|
||||
#include "openvino/op/constant.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ov;
|
||||
using namespace testing;
|
||||
|
||||
template <class TOp>
|
||||
class NMSRotatedCommonTest : public TypePropOpTest<TOp> {};
|
||||
|
||||
TYPED_TEST_SUITE_P(NMSRotatedCommonTest);
|
||||
|
||||
TYPED_TEST_P(NMSRotatedCommonTest, incorrect_boxes_rank) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 5, 4});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 3});
|
||||
const auto scalar_int = make_shared<op::v0::Parameter>(element::i32, Shape{});
|
||||
const auto scalar_fp = make_shared<op::v0::Parameter>(element::f32, Shape{});
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, scalar_int, scalar_fp, scalar_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected a 3D tensor for the 'boxes' input"));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedCommonTest, incorrect_scores_rank) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2});
|
||||
const auto scalar_int = make_shared<op::v0::Parameter>(element::i32, Shape{});
|
||||
const auto scalar_fp = make_shared<op::v0::Parameter>(element::f32, Shape{});
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, scalar_int, scalar_fp, scalar_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected a 3D tensor for the 'scores' input"));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedCommonTest, incorrect_scheme_num_batches) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{2, 2, 3});
|
||||
const auto scalar_int = make_shared<op::v0::Parameter>(element::i32, Shape{});
|
||||
const auto scalar_fp = make_shared<op::v0::Parameter>(element::f32, Shape{});
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, scalar_int, scalar_fp, scalar_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("The first dimension of both 'boxes' and 'scores' must match"));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedCommonTest, incorrect_scheme_num_boxes) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 3});
|
||||
const auto scalar_int = make_shared<op::v0::Parameter>(element::i32, Shape{});
|
||||
const auto scalar_fp = make_shared<op::v0::Parameter>(element::f32, Shape{});
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, scalar_int, scalar_fp, scalar_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("'boxes' and 'scores' input shapes must match at the second and third "
|
||||
"dimension respectively"));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedCommonTest, incorrect_boxes_last_dim) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 3});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
const auto scalar_int = make_shared<op::v0::Parameter>(element::i32, Shape{});
|
||||
const auto scalar_fp = make_shared<op::v0::Parameter>(element::f32, Shape{});
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, scalar_int, scalar_fp, scalar_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("The last dimension of the 'boxes' input must be equal to 5"));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedCommonTest, input_types_check) {
|
||||
const auto param_fp = make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
|
||||
const auto param_int = make_shared<op::v0::Parameter>(element::i32, PartialShape::dynamic());
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(param_int, param_fp, param_int, param_fp, param_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected floating point type as element type for the input at: 0"));
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(param_fp, param_int, param_int, param_fp, param_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected floating point type as element type for the input at: 1"));
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(param_fp, param_fp, param_fp, param_fp, param_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected integer type as element type for the input at: 2"));
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(param_fp, param_fp, param_int, param_int, param_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected floating point type as element type for the input at: 3"));
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(param_fp, param_fp, param_int, param_fp, param_int),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected floating point type as element type for the input at: 4"));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedCommonTest, output_type_attr_check) {
|
||||
const auto param_fp = make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
|
||||
const auto param_int = make_shared<op::v0::Parameter>(element::i32, PartialShape::dynamic());
|
||||
|
||||
OV_EXPECT_THROW(
|
||||
ignore = this->make_op(param_fp, param_fp, param_int, param_fp, param_fp, true, element::f16),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("The `output_type` attribute (related to the first and third output) must be i32 or i64"));
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(NMSRotatedCommonTest,
|
||||
incorrect_boxes_rank,
|
||||
incorrect_scores_rank,
|
||||
incorrect_scheme_num_batches,
|
||||
incorrect_scheme_num_boxes,
|
||||
incorrect_boxes_last_dim,
|
||||
input_types_check,
|
||||
output_type_attr_check);
|
||||
|
||||
using NMSRotatedCommonTypes = testing::Types<op::v13::NMSRotated>;
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(type_prop, NMSRotatedCommonTest, NMSRotatedCommonTypes);
|
||||
|
||||
template <class TOp>
|
||||
using NMSRotatedDynamicOutputTest = NMSRotatedCommonTest<TOp>;
|
||||
TYPED_TEST_SUITE_P(NMSRotatedDynamicOutputTest);
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, scalar_inputs_check) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{1, 2, 2});
|
||||
const auto max_output_boxes_per_class = op::v0::Constant::create(element::i32, Shape{}, {1000});
|
||||
|
||||
const auto scalar_fp = make_shared<op::v0::Parameter>(element::f32, Shape{});
|
||||
const auto non_0d_or_1d = make_shared<op::v0::Parameter>(element::f32, Shape{2});
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, non_0d_or_1d, scalar_fp, scalar_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected 0D or 1D tensor for the 'max_output_boxes_per_class' input"));
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, max_output_boxes_per_class, non_0d_or_1d, scalar_fp),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected 0D or 1D tensor for the 'iou_threshold' input"));
|
||||
|
||||
OV_EXPECT_THROW(ignore = this->make_op(boxes, scores, max_output_boxes_per_class, scalar_fp, non_0d_or_1d),
|
||||
NodeValidationFailure,
|
||||
HasSubstr("Expected 0D or 1D tensor for the 'score_threshold' input"));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, boxes_scores_static_max_out_param) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{5, 2, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{5, 3, 2});
|
||||
const auto scalar_int = make_shared<op::v0::Parameter>(element::i32, Shape{});
|
||||
const auto scalar_fp = op::v0::Constant::create(element::f32, Shape{}, {0.5});
|
||||
|
||||
const auto op = this->make_op(boxes, scores, scalar_int, scalar_fp, scalar_fp);
|
||||
|
||||
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}))));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, num_boxes_gt_max_out_boxes) {
|
||||
auto boxes_shape = PartialShape{2, 7, 5};
|
||||
auto scores_shape = PartialShape{2, 5, 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::i32, 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({{0, 30}, 3})),
|
||||
Property("Scores shape", &Output<Node>::get_partial_shape, PartialShape({{0, 30}, 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(NMSRotatedDynamicOutputTest, num_boxes_lt_max_out_boxes) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{2, 7, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{2, 5, 7});
|
||||
const auto max_output_boxes_per_class = op::v0::Constant::create(element::i16, Shape{}, {1000});
|
||||
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({{0, 70}, 3})),
|
||||
Property("Scores shape", &Output<Node>::get_partial_shape, PartialShape({{0, 70}, 3})),
|
||||
Property("Outputs shape", &Output<Node>::get_partial_shape, PartialShape({1}))));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, max_out_boxes_is_zero) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{2, 7, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{2, 5, 7});
|
||||
const auto max_output_boxes_per_class = op::v0::Constant::create(element::i16, Shape{}, {0});
|
||||
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({0, 3})),
|
||||
Property("Scores shape", &Output<Node>::get_partial_shape, PartialShape({0, 3})),
|
||||
Property("Outputs shape", &Output<Node>::get_partial_shape, PartialShape({1}))));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, interval_shapes_labels) {
|
||||
auto boxes_shape = PartialShape{{0, 2}, {0, 7}, 5};
|
||||
auto scores_shape = PartialShape{{0, 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{}, {1000});
|
||||
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({{0, 70}, 3})),
|
||||
Property("Scores shape", &Output<Node>::get_partial_shape, PartialShape({{0, 70}, 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(NMSRotatedDynamicOutputTest, num_box_dynamic_dim_max_boxes_per_class_as_const) {
|
||||
auto boxes_shape = PartialShape{2, -1, 5};
|
||||
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(NMSRotatedDynamicOutputTest, output_shape_i32) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, Shape{2, 7, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, Shape{2, 5, 7});
|
||||
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, true, element::i32);
|
||||
|
||||
EXPECT_THAT(op->outputs(),
|
||||
ElementsAre(Property("Indicies type", &Output<Node>::get_element_type, element::i32),
|
||||
Property("Scores type", &Output<Node>::get_element_type, element::f32),
|
||||
Property("Outputs type", &Output<Node>::get_element_type, element::i32)));
|
||||
EXPECT_THAT(op->outputs(),
|
||||
ElementsAre(Property("Indicies shape", &Output<Node>::get_partial_shape, PartialShape({{0, 30}, 3})),
|
||||
Property("Scores shape", &Output<Node>::get_partial_shape, PartialShape({{0, 30}, 3})),
|
||||
Property("Outputs shape", &Output<Node>::get_partial_shape, PartialShape({1}))));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, dynamic_boxes_and_scores) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::f32, 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}))));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, dynamic_types) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::dynamic, Shape{5, 2, 5});
|
||||
const auto scores = make_shared<op::v0::Parameter>(element::dynamic, Shape{5, 3, 2});
|
||||
const auto scalar_int = make_shared<op::v0::Parameter>(element::i32, Shape{});
|
||||
const auto scalar_fp = make_shared<op::v0::Parameter>(element::f32, Shape{});
|
||||
|
||||
const auto op = this->make_op(boxes, scores, scalar_int, scalar_fp, scalar_fp);
|
||||
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}))));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(NMSRotatedDynamicOutputTest, scores_shape_is_dynamic_rank) {
|
||||
const auto boxes = make_shared<op::v0::Parameter>(element::dynamic, Shape{5, 2, 5});
|
||||
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(NMSRotatedDynamicOutputTest,
|
||||
scalar_inputs_check,
|
||||
boxes_scores_static_max_out_param,
|
||||
num_boxes_gt_max_out_boxes,
|
||||
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,
|
||||
scores_shape_is_dynamic_rank);
|
||||
|
||||
using NMSRotatedDynamicOutputTypes = testing::Types<op::v13::NMSRotated>;
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(type_prop, NMSRotatedDynamicOutputTest, NMSRotatedDynamicOutputTypes);
|
59
src/core/tests/visitors/op/nms_rotated.cpp
Normal file
59
src/core/tests/visitors/op/nms_rotated.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "openvino/op/nms_rotated.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "visitors/visitors.hpp"
|
||||
|
||||
using ov::Shape;
|
||||
using ov::op::v0::Parameter;
|
||||
using ov::test::NodeBuilder;
|
||||
|
||||
TEST(attributes, nms_rotated_v13_default_attributes) {
|
||||
NodeBuilder::get_ops().register_factory<ov::op::v13::NMSRotated>();
|
||||
auto boxes = std::make_shared<Parameter>(ov::element::f32, Shape{1, 1, 5});
|
||||
auto scores = std::make_shared<Parameter>(ov::element::f32, Shape{1, 1, 1});
|
||||
auto max_out = std::make_shared<Parameter>(ov::element::i32, Shape{});
|
||||
auto iou_tresh = std::make_shared<Parameter>(ov::element::f32, Shape{});
|
||||
auto score_tresh = std::make_shared<Parameter>(ov::element::f32, Shape{});
|
||||
|
||||
auto nms = std::make_shared<ov::op::v13::NMSRotated>(boxes, scores, max_out, iou_tresh, score_tresh);
|
||||
|
||||
NodeBuilder builder(nms, {boxes, scores, max_out, iou_tresh, score_tresh});
|
||||
auto g_nms = ov::as_type_ptr<ov::op::v13::NMSRotated>(builder.create());
|
||||
|
||||
EXPECT_EQ(g_nms->get_sort_result_descending(), nms->get_sort_result_descending());
|
||||
EXPECT_EQ(g_nms->get_output_type_attr(), nms->get_output_type_attr());
|
||||
EXPECT_EQ(g_nms->get_clockwise(), nms->get_clockwise());
|
||||
}
|
||||
|
||||
TEST(attributes, nms_rotated_v13_custom_attributes) {
|
||||
NodeBuilder::get_ops().register_factory<ov::op::v13::NMSRotated>();
|
||||
auto boxes = std::make_shared<Parameter>(ov::element::f32, Shape{1, 1, 5});
|
||||
auto scores = std::make_shared<Parameter>(ov::element::f32, Shape{1, 1, 1});
|
||||
auto max_out = std::make_shared<Parameter>(ov::element::i32, Shape{});
|
||||
auto iou_tresh = std::make_shared<Parameter>(ov::element::f32, Shape{});
|
||||
auto score_tresh = std::make_shared<Parameter>(ov::element::f32, Shape{});
|
||||
|
||||
auto sort_results_desc = false;
|
||||
auto output_elem_type = ov::element::i32;
|
||||
auto clockwise = false;
|
||||
auto nms = std::make_shared<ov::op::v13::NMSRotated>(boxes,
|
||||
scores,
|
||||
max_out,
|
||||
iou_tresh,
|
||||
score_tresh,
|
||||
sort_results_desc,
|
||||
output_elem_type,
|
||||
clockwise);
|
||||
|
||||
NodeBuilder builder(nms, {boxes, scores, max_out, iou_tresh, score_tresh});
|
||||
auto g_nms = ov::as_type_ptr<ov::op::v13::NMSRotated>(builder.create());
|
||||
|
||||
EXPECT_EQ(g_nms->get_sort_result_descending(), nms->get_sort_result_descending());
|
||||
EXPECT_EQ(g_nms->get_output_type_attr(), nms->get_output_type_attr());
|
||||
EXPECT_EQ(g_nms->get_clockwise(), nms->get_clockwise());
|
||||
}
|
41
src/frontends/onnx/frontend/src/op/nms_rotated.hpp
Normal file
41
src/frontends/onnx/frontend/src/op/nms_rotated.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "openvino/core/deprecated.hpp"
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
|
||||
#include "default_opset.hpp"
|
||||
#include "onnx_import/core/node.hpp"
|
||||
#include "openvino/core/node_vector.hpp"
|
||||
#include "openvino/opsets/opset13.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace onnx_import {
|
||||
namespace op {
|
||||
namespace set_1 {
|
||||
inline OutputVector nms_rotated(const Node& node) {
|
||||
auto iou_threshold = node.get_attribute_value<float>("iou_threshold");
|
||||
auto score_threshold = node.get_attribute_value<float>("score_threshold");
|
||||
auto max_output_boxes_per_class =
|
||||
default_opset::Constant::create(element::i64, Shape{1}, {std::numeric_limits<int64_t>::max()});
|
||||
auto iou_threshold_const = default_opset::Constant::create(element::f32, Shape{}, {iou_threshold});
|
||||
auto score_threshold_const = default_opset::Constant::create(element::f32, Shape{}, {score_threshold});
|
||||
|
||||
auto nms = std::make_shared<ov::opset13::NMSRotated>(node.get_ng_inputs().at(0),
|
||||
node.get_ng_inputs().at(1),
|
||||
max_output_boxes_per_class,
|
||||
iou_threshold_const,
|
||||
score_threshold_const);
|
||||
|
||||
return {nms->output(0)};
|
||||
}
|
||||
} // namespace set_1
|
||||
} // namespace op
|
||||
} // namespace onnx_import
|
||||
} // namespace ngraph
|
||||
OPENVINO_SUPPRESS_DEPRECATED_END
|
|
@ -104,6 +104,7 @@
|
|||
#include "op/mod.hpp"
|
||||
#include "op/mul.hpp"
|
||||
#include "op/neg.hpp"
|
||||
#include "op/nms_rotated.hpp"
|
||||
#include "op/non_max_suppression.hpp"
|
||||
#include "op/non_zero.hpp"
|
||||
#include "op/not.hpp"
|
||||
|
@ -310,6 +311,7 @@ void OperatorsBridge::overwrite_operator(const std::string& name, const std::str
|
|||
|
||||
static const char* const MICROSOFT_DOMAIN = "com.microsoft";
|
||||
static const char* const PYTORCH_ATEN_DOMAIN = "org.pytorch.aten";
|
||||
static const char* const MMDEPLOY_DOMAIN = "mmdeploy";
|
||||
|
||||
#define REGISTER_OPERATOR(name_, ver_, fn_) \
|
||||
m_map[""][name_].emplace(ver_, std::bind(op::set_##ver_::fn_, std::placeholders::_1));
|
||||
|
@ -561,6 +563,7 @@ OperatorsBridge::OperatorsBridge() {
|
|||
REGISTER_OPERATOR_WITH_DOMAIN(MICROSOFT_DOMAIN, "Trilu", 1, trilu);
|
||||
|
||||
REGISTER_OPERATOR_WITH_DOMAIN(PYTORCH_ATEN_DOMAIN, "adaptive_avg_pool2d", 1, adaptive_avg_pooling2d);
|
||||
REGISTER_OPERATOR_WITH_DOMAIN(MMDEPLOY_DOMAIN, "NMSRotated", 1, nms_rotated);
|
||||
}
|
||||
|
||||
#undef REGISTER_OPERATOR
|
||||
|
|
131
src/plugins/template/backend/ops/nms_rotated.cpp
Normal file
131
src/plugins/template/backend/ops/nms_rotated.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "openvino/reference/nms_rotated.hpp"
|
||||
|
||||
#include "evaluate_node.hpp"
|
||||
#include "evaluates_map.hpp"
|
||||
#include "openvino/op/nms_rotated.hpp"
|
||||
#include "openvino/reference/non_max_suppression.hpp"
|
||||
|
||||
using namespace ov;
|
||||
|
||||
namespace {
|
||||
|
||||
struct InfoForNMSRotated {
|
||||
int64_t max_output_boxes_per_class;
|
||||
float iou_threshold;
|
||||
float score_threshold;
|
||||
float soft_nms_sigma;
|
||||
Shape out_shape;
|
||||
Shape boxes_shape;
|
||||
Shape scores_shape;
|
||||
std::vector<float> boxes_data;
|
||||
std::vector<float> scores_data;
|
||||
size_t out_shape_size;
|
||||
bool sort_result_descending;
|
||||
element::Type output_type;
|
||||
bool clockwise;
|
||||
};
|
||||
|
||||
constexpr size_t boxes_port = 0;
|
||||
constexpr size_t scores_port = 1;
|
||||
|
||||
PartialShape infer_selected_indices_shape(const TensorVector& inputs, size_t max_output_boxes_per_class) {
|
||||
const auto boxes_shape = inputs[boxes_port].get_shape();
|
||||
const auto scores_shape = inputs[scores_port].get_shape();
|
||||
|
||||
// NMSRotated produces triplets
|
||||
// that have the following format: [batch_index, class_index, box_index]
|
||||
PartialShape result = {Dimension::dynamic(), 3};
|
||||
|
||||
if (boxes_shape.size() > 0 && scores_shape.size() > 0) {
|
||||
const auto num_boxes_boxes = boxes_shape[1];
|
||||
const auto num_boxes = num_boxes_boxes;
|
||||
const auto num_classes = scores_shape[1];
|
||||
|
||||
result[0] = std::min(num_boxes, max_output_boxes_per_class) * num_classes * scores_shape[0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
InfoForNMSRotated get_info_for_nms_eval(const std::shared_ptr<op::v13::NMSRotated>& nms, const TensorVector& inputs) {
|
||||
InfoForNMSRotated result;
|
||||
|
||||
result.max_output_boxes_per_class = inputs.size() > 2 ? get_integers(inputs[2], Shape({}))[0] : 0;
|
||||
result.iou_threshold = inputs.size() > 3 ? get_floats(inputs[3], Shape({}))[0] : 0.0f;
|
||||
result.score_threshold = inputs.size() > 4 ? get_floats(inputs[4], Shape({}))[0] : 0.0f;
|
||||
result.soft_nms_sigma = 0.0f;
|
||||
|
||||
auto selected_indices_shape = infer_selected_indices_shape(inputs, result.max_output_boxes_per_class);
|
||||
result.out_shape = selected_indices_shape.to_shape();
|
||||
|
||||
result.boxes_shape = inputs[boxes_port].get_shape();
|
||||
result.scores_shape = inputs[scores_port].get_shape();
|
||||
result.boxes_data = get_floats(inputs[boxes_port], result.boxes_shape);
|
||||
result.scores_data = get_floats(inputs[scores_port], result.scores_shape);
|
||||
|
||||
result.out_shape_size = shape_size(result.out_shape);
|
||||
result.sort_result_descending = nms->get_sort_result_descending();
|
||||
result.output_type = nms->get_output_type_attr();
|
||||
result.clockwise = nms->get_clockwise();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <element::Type_t ET>
|
||||
bool evaluate(const std::shared_ptr<op::v13::NMSRotated>& op, TensorVector& outputs, const TensorVector& inputs) {
|
||||
const auto& info = get_info_for_nms_eval(op, inputs);
|
||||
|
||||
std::vector<int64_t> selected_indices(info.out_shape_size);
|
||||
std::vector<float> selected_scores(info.out_shape_size);
|
||||
int64_t valid_outputs = 0;
|
||||
|
||||
reference::nms_rotated(info.boxes_data.data(),
|
||||
info.boxes_shape,
|
||||
info.scores_data.data(),
|
||||
info.scores_shape,
|
||||
info.max_output_boxes_per_class,
|
||||
info.iou_threshold,
|
||||
info.score_threshold,
|
||||
info.soft_nms_sigma,
|
||||
selected_indices.data(),
|
||||
info.out_shape,
|
||||
selected_scores.data(),
|
||||
info.out_shape,
|
||||
&valid_outputs,
|
||||
info.sort_result_descending,
|
||||
info.clockwise);
|
||||
|
||||
auto selected_scores_type = (outputs.size() < 2) ? element::f32 : outputs[1].get_element_type();
|
||||
|
||||
// Postprocessing steps are needed to align the shapes and types of the `indices` and the `scores` output.
|
||||
// The shapes of the mentioned outputs have dynamic dimension defined by the number of the selected boxes.
|
||||
// The values of `indices` are converted to the element type specified by corresponding output_type attribute.
|
||||
// The values of `scores` are converted to the same type as the second input.
|
||||
reference::nms_rotated_postprocessing(outputs,
|
||||
info.output_type,
|
||||
selected_indices,
|
||||
selected_scores,
|
||||
valid_outputs,
|
||||
selected_scores_type);
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <>
|
||||
bool evaluate_node<op::v13::NMSRotated>(std::shared_ptr<Node> node, TensorVector& outputs, const TensorVector& inputs) {
|
||||
switch (node->get_output_element_type(1)) {
|
||||
case element::Type_t::bf16:
|
||||
return evaluate<element::Type_t::bf16>(as_type_ptr<op::v13::NMSRotated>(node), outputs, inputs);
|
||||
case element::Type_t::f16:
|
||||
return evaluate<element::Type_t::f16>(as_type_ptr<op::v13::NMSRotated>(node), outputs, inputs);
|
||||
case element::Type_t::f64:
|
||||
return evaluate<element::Type_t::f64>(as_type_ptr<op::v13::NMSRotated>(node), outputs, inputs);
|
||||
case element::Type_t::f32:
|
||||
return evaluate<element::Type_t::f32>(as_type_ptr<op::v13::NMSRotated>(node), outputs, inputs);
|
||||
default:
|
||||
OPENVINO_THROW(std::string("Unhandled data type ") + node->get_output_element_type(0).get_type_name() +
|
||||
std::string("in evaluate_node()"));
|
||||
}
|
||||
}
|
|
@ -449,6 +449,10 @@ extern template bool evaluate_node<ov::op::v13::BitwiseNot>(std::shared_ptr<ov::
|
|||
ov::TensorVector& outputs,
|
||||
const ov::TensorVector& inputs);
|
||||
|
||||
extern template bool evaluate_node<ov::op::v13::NMSRotated>(std::shared_ptr<ov::Node> node,
|
||||
ov::TensorVector& outputs,
|
||||
const ov::TensorVector& inputs);
|
||||
|
||||
extern template bool evaluate_node<ov::op::internal::AUGRUCell>(std::shared_ptr<ov::Node> node,
|
||||
ov::TensorVector& outputs,
|
||||
const ov::TensorVector& inputs);
|
||||
|
|
|
@ -151,6 +151,7 @@ _OPENVINO_OP_REG(Interpolate, op::v11)
|
|||
_OPENVINO_OP_REG(GroupNormalization, ov::op::v12)
|
||||
|
||||
_OPENVINO_OP_REG(BitwiseNot, ov::op::v13)
|
||||
_OPENVINO_OP_REG(NMSRotated, ov::op::v13)
|
||||
|
||||
_OPENVINO_OP_REG(AUGRUCell, ov::op::internal)
|
||||
_OPENVINO_OP_REG(AUGRUSequence, ov::op::internal)
|
||||
|
|
|
@ -0,0 +1,495 @@
|
|||
// Copyright (C) 2018-2023 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "base_reference_test.hpp"
|
||||
#include "openvino/opsets/opset1.hpp"
|
||||
#include "openvino/opsets/opset13.hpp"
|
||||
|
||||
using namespace reference_tests;
|
||||
using namespace ov;
|
||||
|
||||
namespace {
|
||||
struct NMSRotatedParams {
|
||||
reference_tests::Tensor boxes;
|
||||
reference_tests::Tensor scores;
|
||||
reference_tests::Tensor maxOutputBoxesPerClass;
|
||||
reference_tests::Tensor iouThreshold;
|
||||
reference_tests::Tensor scoreThreshold;
|
||||
reference_tests::Tensor softNmsSigma;
|
||||
bool clockwise = true;
|
||||
reference_tests::Tensor expectedSelectedIndices;
|
||||
reference_tests::Tensor expectedSelectedScores;
|
||||
reference_tests::Tensor expectedValidOutputs;
|
||||
std::string testcaseName;
|
||||
};
|
||||
|
||||
struct Builder : ParamsBuilder<NMSRotatedParams> {
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, boxes);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, scores);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, maxOutputBoxesPerClass);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, iouThreshold);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, scoreThreshold);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, softNmsSigma);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, clockwise);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, expectedSelectedIndices);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, expectedSelectedScores);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, expectedValidOutputs);
|
||||
REFERENCE_TESTS_ADD_SET_PARAM(Builder, testcaseName);
|
||||
};
|
||||
|
||||
class ReferenceNMSRotatedTest : public testing::TestWithParam<NMSRotatedParams>, public CommonReferenceTest {
|
||||
public:
|
||||
void SetUp() override {
|
||||
const auto& params = GetParam();
|
||||
function = CreateModel(params);
|
||||
inputData = {params.boxes.data, params.scores.data};
|
||||
refOutData = {params.expectedSelectedIndices.data,
|
||||
params.expectedSelectedScores.data,
|
||||
params.expectedValidOutputs.data};
|
||||
}
|
||||
|
||||
static std::string getTestCaseName(const testing::TestParamInfo<NMSRotatedParams>& obj) {
|
||||
const auto& param = obj.param;
|
||||
std::ostringstream result;
|
||||
result << "bType=" << param.boxes.type;
|
||||
result << "_bShape=" << param.boxes.shape;
|
||||
result << "_sType=" << param.scores.type;
|
||||
result << "_sShape=" << param.scores.shape;
|
||||
result << "_esiType=" << param.expectedSelectedIndices.type;
|
||||
result << "_esiShape=" << param.expectedSelectedIndices.shape;
|
||||
result << "_escType=" << param.expectedSelectedScores.type;
|
||||
result << "_escShape=" << param.expectedSelectedScores.shape;
|
||||
result << "_evoType=" << param.expectedValidOutputs.type;
|
||||
result << "_evoShape=" << param.expectedValidOutputs.shape;
|
||||
if (param.testcaseName != "") {
|
||||
result << "_=" << param.testcaseName;
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::shared_ptr<Model> CreateModel(const NMSRotatedParams& params) {
|
||||
const auto boxes = std::make_shared<opset1::Parameter>(params.boxes.type, params.boxes.shape);
|
||||
const auto scores = std::make_shared<opset1::Parameter>(params.scores.type, params.scores.shape);
|
||||
const auto max_output_boxes_per_class =
|
||||
std::make_shared<opset1::Constant>(params.maxOutputBoxesPerClass.type,
|
||||
params.maxOutputBoxesPerClass.shape,
|
||||
params.maxOutputBoxesPerClass.data.data());
|
||||
const auto iou_threshold = std::make_shared<opset1::Constant>(params.iouThreshold.type,
|
||||
params.iouThreshold.shape,
|
||||
params.iouThreshold.data.data());
|
||||
const auto score_threshold = std::make_shared<opset1::Constant>(params.scoreThreshold.type,
|
||||
params.scoreThreshold.shape,
|
||||
params.scoreThreshold.data.data());
|
||||
const auto nms = std::make_shared<opset13::NMSRotated>(boxes,
|
||||
scores,
|
||||
max_output_boxes_per_class,
|
||||
iou_threshold,
|
||||
score_threshold,
|
||||
false,
|
||||
params.expectedSelectedIndices.type,
|
||||
params.clockwise);
|
||||
return std::make_shared<Model>(nms->outputs(), ParameterVector{boxes, scores});
|
||||
}
|
||||
};
|
||||
|
||||
class ReferenceNMSRotatedTestWithoutConstants : public ReferenceNMSRotatedTest {
|
||||
public:
|
||||
void SetUp() override {
|
||||
const auto& params = GetParam();
|
||||
function = CreateModel(params);
|
||||
inputData = {params.boxes.data,
|
||||
params.scores.data,
|
||||
params.maxOutputBoxesPerClass.data,
|
||||
params.iouThreshold.data,
|
||||
params.scoreThreshold.data};
|
||||
refOutData = {params.expectedSelectedIndices.data,
|
||||
params.expectedSelectedScores.data,
|
||||
params.expectedValidOutputs.data};
|
||||
}
|
||||
|
||||
private:
|
||||
static std::shared_ptr<Model> CreateModel(const NMSRotatedParams& params) {
|
||||
const auto boxes = std::make_shared<opset1::Parameter>(params.boxes.type, params.boxes.shape);
|
||||
const auto scores = std::make_shared<opset1::Parameter>(params.scores.type, params.scores.shape);
|
||||
const auto max_output_boxes_per_class =
|
||||
std::make_shared<opset1::Parameter>(params.maxOutputBoxesPerClass.type,
|
||||
params.maxOutputBoxesPerClass.shape);
|
||||
const auto iou_threshold =
|
||||
std::make_shared<opset1::Parameter>(params.iouThreshold.type, params.iouThreshold.shape);
|
||||
const auto score_threshold =
|
||||
std::make_shared<opset1::Parameter>(params.scoreThreshold.type, params.scoreThreshold.shape);
|
||||
const auto nms = std::make_shared<opset13::NMSRotated>(boxes,
|
||||
scores,
|
||||
max_output_boxes_per_class,
|
||||
iou_threshold,
|
||||
score_threshold,
|
||||
false,
|
||||
params.expectedSelectedIndices.type,
|
||||
params.clockwise);
|
||||
return std::make_shared<Model>(
|
||||
nms->outputs(),
|
||||
ParameterVector{boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold});
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(ReferenceNMSRotatedTest, CompareWithRefs) {
|
||||
Exec();
|
||||
}
|
||||
|
||||
TEST_P(ReferenceNMSRotatedTestWithoutConstants, CompareWithRefs) {
|
||||
Exec();
|
||||
}
|
||||
|
||||
template <element::Type_t ET, element::Type_t ET_BOX, element::Type_t ET_TH, element::Type_t ET_IND>
|
||||
std::vector<NMSRotatedParams> generateParams() {
|
||||
using T = typename element_type_traits<ET>::value_type;
|
||||
using T_BOX = typename element_type_traits<ET_BOX>::value_type;
|
||||
using T_TH = typename element_type_traits<ET_TH>::value_type;
|
||||
using T_IND = typename element_type_traits<ET_IND>::value_type;
|
||||
std::vector<NMSRotatedParams> params{
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(ET, {1, 4, 5}, std::vector<T>{/*0*/ 7.0, 4.0, 8.0, 7.0, 0.5,
|
||||
/*1*/ 4.0, 7.0, 9.0, 11.0, 0.6,
|
||||
/*2*/ 4.0, 8.0, 10.0, 12.0, 0.3,
|
||||
/*3*/ 2.0, 5.0, 13.0, 7.0, 0.6}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 4}, std::vector<T>{0.65, 0.7, 0.55, 0.96}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.5f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(
|
||||
ET_IND,
|
||||
{3, 3},
|
||||
std::vector<T_IND>{0, 0, 3, 0, 0, 1, 0, 0, 0})) // batch 0, class 0, box_id (sorted max score first)
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH,
|
||||
{3, 3},
|
||||
std::vector<T_TH>{0.0, 0.0, 0.96, 0.0, 0.0, 0.7, 0.0, 0.0, 0.65}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{3}))
|
||||
.testcaseName("NMSRotated_new_rotation_basic"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(ET, {1, 4, 5}, std::vector<T>{/*0*/ 7.0, 4.0, 8.0, 7.0, 0.5,
|
||||
/*1*/ 4.0, 7.0, 9.0, 11.0, 0.6,
|
||||
/*2*/ 4.0, 8.0, 10.0, 12.0, 0.3,
|
||||
/*3*/ 2.0, 5.0, 13.0, 7.0, 0.6}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 4}, std::vector<T>{0.65, 0.7, 0.55, 0.96}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{2}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.5f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(
|
||||
ET_IND,
|
||||
{2, 3},
|
||||
std::vector<T_IND>{0, 0, 3, 0, 0, 1})) // batch 0, class 0, box_id (sorted max score first)
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {2, 3}, std::vector<T_TH>{0.0, 0.0, 0.96, 0.0, 0.0, 0.7}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{2}))
|
||||
.testcaseName("NMSRotated_new_rotation_basic_max_out_2"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(ET, {1, 4, 5}, std::vector<T>{/*0*/ 7.0, 4.0, 8.0, 7.0, 0.5,
|
||||
/*1*/ 4.0, 7.0, 9.0, 11.0, 0.6,
|
||||
/*2*/ 4.0, 8.0, 10.0, 12.0, 0.3,
|
||||
/*3*/ 2.0, 5.0, 13.0, 7.0, 0.6}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 4}, std::vector<T>{0.65, 0.7, 0.55, 0.96}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.5f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.67f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(
|
||||
ET_IND,
|
||||
{2, 3},
|
||||
std::vector<T_IND>{0, 0, 3, 0, 0, 1})) // batch 0, class 0, box_id (sorted max score first)
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {2, 3}, std::vector<T_TH>{0.0, 0.0, 0.96, 0.0, 0.0, 0.7}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{2}))
|
||||
.testcaseName("NMSRotated_new_rotation_basic_score_tresh"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(ET, {1, 4, 5}, std::vector<T>{/*0*/ 7.0, 4.0, 8.0, 7.0, 0.5,
|
||||
/*1*/ 4.0, 7.0, 9.0, 11.0, 0.6,
|
||||
/*2*/ 4.0, 8.0, 10.0, 12.0, 0.3,
|
||||
/*3*/ 2.0, 5.0, 13.0, 7.0, 0.6}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 4}, std::vector<T>{0.65, 0.7, 0.55, 0.96}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.3f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {2, 3}, std::vector<T_IND>{0, 0, 3, 0, 0, 0}))
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {2, 3}, std::vector<T_TH>{0.0, 0.0, 0.96, 0.0, 0.0, 0.65}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{2}))
|
||||
.testcaseName("NMSRotated_new_rotation_2"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 8.0, 11.5, 4.0, 3.0, 0.5236, /*1*/ 11.0, 15.0, 8.0, 2.0, 0.7854}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.8, 0.8}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.5f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {2, 3}, std::vector<T_IND>{0, 0, 0, 0, 0, 1}))
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {2, 3}, std::vector<T_TH>{0.0, 0.0, 0.8, 0.0, 0.0, 0.8}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{2}))
|
||||
.testcaseName("NMSRotated_new_rotation_3"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 8.0, 11.5, 4.0, 3.0, 0.5236, /*1*/ 11.0, 15.0, 8.0, 2.0, 0.7854}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.8, 0.8}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.1f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {1, 3}, std::vector<T_IND>{0, 0, 0}))
|
||||
.expectedSelectedScores(reference_tests::Tensor(ET_TH, {1, 3}, std::vector<T_TH>{0.0, 0.0, 0.8}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{1}))
|
||||
.testcaseName("NMSRotated_new_rotation_4"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 8.0, 11.5, 4.0, 3.0, 0.5236, /*1*/ 11.0, 15.0, 8.0, 2.0, 0.7854}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.7, 0.8}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.1f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {1, 3}, std::vector<T_IND>{0, 0, 1}))
|
||||
.expectedSelectedScores(reference_tests::Tensor(ET_TH, {1, 3}, std::vector<T_TH>{0.0, 0.0, 0.8}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{1}))
|
||||
.testcaseName("NMSRotated_new_rotation_5"),
|
||||
Builder{}
|
||||
.boxes(
|
||||
reference_tests::Tensor(ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 23.0, 3.5, 4.0, 5.0, 2.9, /*1*/ 22.0, 3.5, 4.0, 3.0, 5.3}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.7, 0.9}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.4f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(
|
||||
ET_IND,
|
||||
{1, 3},
|
||||
std::vector<T_IND>{0, 0, 1})) // batch 0, class 0, box_id (sorted max score first)
|
||||
.expectedSelectedScores(reference_tests::Tensor(ET_TH, {1, 3}, std::vector<T_TH>{0.0, 0.0, 0.9}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{1}))
|
||||
.testcaseName("NMSRotated_new_rotation_6"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 6.0, 34.0, 4.0, 8.0, -0.7854, /*1*/ 9.0, 32, 2.0, 4.0, 0.0}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.8, 0.7}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.1f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.clockwise(true)
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {2, 3}, std::vector<T_IND>{0, 0, 0, 0, 0, 1}))
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {2, 3}, std::vector<T_TH>{0.0, 0.0, 0.8, 0.0, 0.0, 0.7}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{2}))
|
||||
.testcaseName("NMSRotated_new_rotation_negative_cw"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 6.0, 34.0, 4.0, 8.0, -0.7854, /*1*/ 9.0, 32, 2.0, 4.0, 0.0}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.8, 0.7}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.1f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.clockwise(false)
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {1, 3}, std::vector<T_IND>{0, 0, 0}))
|
||||
.expectedSelectedScores(reference_tests::Tensor(ET_TH, {1, 3}, std::vector<T_TH>{0.0, 0.0, 0.8}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{1}))
|
||||
.testcaseName("NMSRotated_new_rotation_negative_ccw"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 9.0, 32, 2.0, 4.0, 0.0, /*1*/ 6.0, 34.0, 4.0, 8.0, -0.7854}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.8, 0.7}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.1f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.clockwise(false)
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {1, 3}, std::vector<T_IND>{0, 0, 0}))
|
||||
.expectedSelectedScores(reference_tests::Tensor(ET_TH, {1, 3}, std::vector<T_TH>{0.0, 0.0, 0.8}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{1}))
|
||||
.testcaseName("NMSRotated_new_rotation_negative_ccw_reorder"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 6.0, 34.0, 4.0, 8.0, 0.7854, /*1*/ 9.0, 32, 2.0, 4.0, 0.0}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.8, 0.7}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.1f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.clockwise(false)
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {2, 3}, std::vector<T_IND>{0, 0, 0, 0, 0, 1}))
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {2, 3}, std::vector<T_TH>{0.0, 0.0, 0.8, 0.0, 0.0, 0.7}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{2}))
|
||||
.testcaseName("NMSRotated_new_rotation_positive_ccw"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(
|
||||
ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 6.0, 34.0, 4.0, 8.0, 0.7854, /*1*/ 9.0, 32, 2.0, 4.0, 0.0}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.8, 0.7}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.1f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.clockwise(true)
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {1, 3}, std::vector<T_IND>{0, 0, 0}))
|
||||
.expectedSelectedScores(reference_tests::Tensor(ET_TH, {1, 3}, std::vector<T_TH>{0.0, 0.0, 0.8}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{1}))
|
||||
.testcaseName("NMSRotated_new_rotation_positive_cw"),
|
||||
Builder{}
|
||||
.boxes(
|
||||
reference_tests::Tensor(ET,
|
||||
{1, 2, 5},
|
||||
std::vector<T>{/*0*/ 23.0, 3.5, 4.0, 5.0, 2.9, /*1*/ 22.0, 3.5, 4.0, 3.0, 5.3}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 2}, std::vector<T>{0.7, 0.9}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.4f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(ET_IND, {1, 3}, std::vector<T_IND>{0, 0, 1}))
|
||||
.expectedSelectedScores(reference_tests::Tensor(ET_TH, {1, 3}, std::vector<T_TH>{0.0, 0.0, 0.9}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{1}))
|
||||
.testcaseName("NMSRotated_new_rotation_7"),
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(ET,
|
||||
{1, 4, 5},
|
||||
std::vector<T>{
|
||||
/*0*/ 23.0, 3.5, 4.0, 5.0, 2.9, /*1*/ 11.0, 15.0, 8.0, 2.0, 0.7854,
|
||||
/*2*/ 22.0, 3.5, 4.0, 3.0, 5.3, /*3*/ 8.0, 11.5, 4.0, 3.0, 0.5236,
|
||||
}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 4}, std::vector<T>{0.9, 0.7, 0.6, 0.8}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{5000}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.4f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(reference_tests::Tensor(
|
||||
ET_IND,
|
||||
{3, 3},
|
||||
std::vector<T_IND>{0, 0, 0, 0, 0, 3, 0, 0, 1})) // batch 0, class 0, box_id (sorted max score first)
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {3, 3}, std::vector<T_TH>{0.0, 0.0, 0.9, 0.0, 0.0, 0.8, 0.0, 0.0, 0.7}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{3}))
|
||||
.testcaseName("NMSRotated_new_rotation_8"),
|
||||
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(ET, {1, 6, 5}, std::vector<T>{/*0*/ 0.5, 0.5, 1.0, 1.0, 0.0,
|
||||
/*1*/ 0.5, 0.6, 1.0, 1.0, 0.0,
|
||||
/*2*/ 0.5, 0.4, 1.0, 1.0, 0.0,
|
||||
/*3*/ 0.5, 10.5, 1.0, 1.0, 0.0,
|
||||
/*4*/ 0.5, 10.6, 1.0, 1.0, 0.0,
|
||||
/*5*/ 0.5, 100.5, 1.0, 1.0, 0.0}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 6}, std::vector<T>{0.9, 0.75, 0.6, 0.95, 0.5, 0.3}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{3}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.5f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(
|
||||
reference_tests::Tensor(ET_IND, {3, 3}, std::vector<T_IND>{0, 0, 3, 0, 0, 0, 0, 0, 5}))
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {3, 3}, std::vector<T_TH>{0.0, 0.0, 0.95, 0.0, 0.0, 0.9, 0.0, 0.0, 0.3}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{3}))
|
||||
.testcaseName("NMSRotated_center_point_zero_angle"),
|
||||
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
std::vector<NMSRotatedParams> generateCombinedParams() {
|
||||
const std::vector<std::vector<NMSRotatedParams>> generatedParams{
|
||||
generateParams<element::Type_t::f32, element::Type_t::i32, element::Type_t::f32, element::Type_t::i32>(),
|
||||
generateParams<element::Type_t::f16, element::Type_t::i32, element::Type_t::f32, element::Type_t::i64>(),
|
||||
generateParams<element::Type_t::f32, element::Type_t::i32, element::Type_t::f32, element::Type_t::i64>(),
|
||||
};
|
||||
std::vector<NMSRotatedParams> combinedParams;
|
||||
|
||||
for (const auto& params : generatedParams) {
|
||||
std::move(params.begin(), params.end(), std::back_inserter(combinedParams));
|
||||
}
|
||||
return combinedParams;
|
||||
}
|
||||
|
||||
template <element::Type_t ET, element::Type_t ET_BOX, element::Type_t ET_TH, element::Type_t ET_IND>
|
||||
std::vector<NMSRotatedParams> generateParamsWithoutConstants() {
|
||||
using T = typename element_type_traits<ET>::value_type;
|
||||
using T_BOX = typename element_type_traits<ET_BOX>::value_type;
|
||||
using T_TH = typename element_type_traits<ET_TH>::value_type;
|
||||
using T_IND = typename element_type_traits<ET_IND>::value_type;
|
||||
std::vector<NMSRotatedParams> params{
|
||||
Builder{}
|
||||
.boxes(reference_tests::Tensor(ET, {1, 6, 5}, std::vector<T>{/*0*/ 0.5, 0.5, 1.0, 1.0, 0.0,
|
||||
/*1*/ 0.5, 0.6, 1.0, 1.0, 0.0,
|
||||
/*2*/ 0.5, 0.4, 1.0, 1.0, 0.0,
|
||||
/*3*/ 0.5, 10.5, 1.0, 1.0, 0.0,
|
||||
/*4*/ 0.5, 10.6, 1.0, 1.0, 0.0,
|
||||
/*5*/ 0.5, 100.5, 1.0, 1.0, 0.0}))
|
||||
.scores(reference_tests::Tensor(ET, {1, 1, 6}, std::vector<T>{0.9, 0.75, 0.6, 0.95, 0.5, 0.3}))
|
||||
.maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector<T_BOX>{3}))
|
||||
.iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.5f}))
|
||||
.scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector<T_TH>{0.0f}))
|
||||
.expectedSelectedIndices(
|
||||
reference_tests::Tensor(ET_IND, {3, 3}, std::vector<T_IND>{0, 0, 3, 0, 0, 0, 0, 0, 5}))
|
||||
.expectedSelectedScores(
|
||||
reference_tests::Tensor(ET_TH, {3, 3}, std::vector<T_TH>{0.0, 0.0, 0.95, 0.0, 0.0, 0.9, 0.0, 0.0, 0.3}))
|
||||
.expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector<T_IND>{3}))
|
||||
.testcaseName("NMSRotated_suppress_by_IOU_and_scores_without_constants"),
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
std::vector<NMSRotatedParams> generateCombinedParamsWithoutConstants() {
|
||||
const std::vector<std::vector<NMSRotatedParams>> generatedParams{
|
||||
generateParamsWithoutConstants<element::Type_t::f32,
|
||||
element::Type_t::i32,
|
||||
element::Type_t::f32,
|
||||
element::Type_t::i32>(),
|
||||
generateParamsWithoutConstants<element::Type_t::f16,
|
||||
element::Type_t::i32,
|
||||
element::Type_t::f32,
|
||||
element::Type_t::i64>(),
|
||||
generateParamsWithoutConstants<element::Type_t::f32,
|
||||
element::Type_t::i32,
|
||||
element::Type_t::f32,
|
||||
element::Type_t::i64>(),
|
||||
};
|
||||
std::vector<NMSRotatedParams> combinedParams;
|
||||
|
||||
for (const auto& params : generatedParams) {
|
||||
combinedParams.insert(combinedParams.end(), params.begin(), params.end());
|
||||
}
|
||||
return combinedParams;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(smoke_NMSRotated_With_Hardcoded_Refs,
|
||||
ReferenceNMSRotatedTest,
|
||||
testing::ValuesIn(generateCombinedParams()),
|
||||
ReferenceNMSRotatedTest::getTestCaseName);
|
||||
INSTANTIATE_TEST_SUITE_P(smoke_NMSRotated_With_Hardcoded_Refs,
|
||||
ReferenceNMSRotatedTestWithoutConstants,
|
||||
testing::ValuesIn(generateCombinedParamsWithoutConstants()),
|
||||
ReferenceNMSRotatedTestWithoutConstants::getTestCaseName);
|
||||
|
||||
} // namespace
|
|
@ -606,6 +606,25 @@ std::shared_ptr<ov::Model> generate(const std::shared_ptr<ov::op::v0::MatMul> &n
|
|||
return std::make_shared<ov::Model>(results, params, "MatMul-1");
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Model> generate(const std::shared_ptr<ov::op::v13::NMSRotated> &node) {
|
||||
ov::ParameterVector params{std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{{1, 6, 5}}),
|
||||
std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{{1, 1, 6}}),
|
||||
std::make_shared<ov::op::v0::Parameter>(ov::element::i32, ov::Shape{}),
|
||||
std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{}),
|
||||
std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{})};
|
||||
|
||||
auto nms = std::make_shared<ov::op::v13::NMSRotated>(params[0],
|
||||
params[1],
|
||||
params[2],
|
||||
params[3],
|
||||
params[4],
|
||||
true,
|
||||
ov::element::i32,
|
||||
true);
|
||||
ov::ResultVector results{std::make_shared<ov::op::v0::Result>(nms)};
|
||||
return std::make_shared<ov::Model>(results, params, "NMSRotated-13");
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Model> generate(const std::shared_ptr<ov::op::v1::NonMaxSuppression> &node) {
|
||||
ov::ParameterVector params{std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{{1, 6, 4}}),
|
||||
std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{{1, 1, 6}}),
|
||||
|
|
Loading…
Reference in New Issue
Block a user