diff --git a/src/core/reference/src/op/nms_rotated.cpp b/src/core/reference/src/op/nms_rotated.cpp index 3b4f21d4431..299ef8c6f23 100644 --- a/src/core/reference/src/op/nms_rotated.cpp +++ b/src/core/reference/src/op/nms_rotated.cpp @@ -195,7 +195,11 @@ void nms_rotated(const float* boxes_data, } if (sort_result_descending) { - std::reverse(filteredBoxes.begin(), filteredBoxes.end()); + std::stable_sort(filteredBoxes.begin(), filteredBoxes.end(), [](const BoxInfo& lhs, const BoxInfo& rhs) { + return (lhs.score > rhs.score) || + ((lhs.score == rhs.score) && (std::tie(lhs.batch_index, lhs.class_index, lhs.index) < + std::tie(rhs.batch_index, rhs.class_index, rhs.index))); + }); } size_t max_num_of_selected_indices = selected_indices_shape[0]; diff --git a/src/plugins/intel_gpu/src/kernel_selector/cl_kernels/non_max_suppression_gpu_ref.cl b/src/plugins/intel_gpu/src/kernel_selector/cl_kernels/non_max_suppression_gpu_ref.cl index 3a0173a0dbc..5bd9ef8048a 100644 --- a/src/plugins/intel_gpu/src/kernel_selector/cl_kernels/non_max_suppression_gpu_ref.cl +++ b/src/plugins/intel_gpu/src/kernel_selector/cl_kernels/non_max_suppression_gpu_ref.cl @@ -566,16 +566,6 @@ inline void FUNC(swap)(__global BOX_INFO* a, __global BOX_INFO* b) *b = temp; } -#ifdef ROTATION -inline void FUNC(reverseOutputBoxList)(__global BOX_INFO *outBoxes, int boxNum) -{ - for (int i = 0; i < boxNum / 2; ++i) { - FUNC_CALL(swap)(&outBoxes[i], &outBoxes[boxNum - 1 - i]); - } -} - -#else - inline void FUNC(sortOutputBoxList)(__global BOX_INFO *outSortedBoxes, int boxNum) { for (int i = 0; i < boxNum - 1; ++i) { @@ -597,7 +587,6 @@ inline void FUNC(sortOutputBoxList)(__global BOX_INFO *outSortedBoxes, int boxNu break; } } -#endif // ROTATION #ifdef NMS_STAGE_0 @@ -880,11 +869,7 @@ KERNEL (non_max_suppression_ref_stage_3)( } #if SORT_RESULT_DESCENDING == 1 -#ifdef ROTATION - FUNC_CALL(reverseOutputBoxList)(sortedBoxList, outputIdx); -#else FUNC_CALL(sortOutputBoxList)(sortedBoxList, outputIdx); -#endif #endif unroll_for (int i = 0; i < outputIdx; i++) { diff --git a/src/plugins/template/tests/functional/op_reference/nms_rotated.cpp b/src/plugins/template/tests/functional/op_reference/nms_rotated.cpp index 34f0bc074ec..817aebd3aed 100644 --- a/src/plugins/template/tests/functional/op_reference/nms_rotated.cpp +++ b/src/plugins/template/tests/functional/op_reference/nms_rotated.cpp @@ -21,6 +21,7 @@ struct NMSRotatedParams { reference_tests::Tensor iouThreshold; reference_tests::Tensor scoreThreshold; reference_tests::Tensor softNmsSigma; + bool sortResultsDescending = true; bool clockwise = true; reference_tests::Tensor expectedSelectedIndices; reference_tests::Tensor expectedSelectedScores; @@ -35,6 +36,7 @@ struct Builder : ParamsBuilder { 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, sortResultsDescending); REFERENCE_TESTS_ADD_SET_PARAM(Builder, clockwise); REFERENCE_TESTS_ADD_SET_PARAM(Builder, expectedSelectedIndices); REFERENCE_TESTS_ADD_SET_PARAM(Builder, expectedSelectedScores); @@ -91,7 +93,7 @@ private: max_output_boxes_per_class, iou_threshold, score_threshold, - false, + params.sortResultsDescending, params.expectedSelectedIndices.type, params.clockwise); return std::make_shared(nms->outputs(), ParameterVector{boxes, scores}); @@ -129,7 +131,7 @@ private: max_output_boxes_per_class, iou_threshold, score_threshold, - false, + params.sortResultsDescending, params.expectedSelectedIndices.type, params.clockwise); return std::make_shared( @@ -146,6 +148,8 @@ TEST_P(ReferenceNMSRotatedTestWithoutConstants, CompareWithRefs) { Exec(); } +// clang-format off +// To make the test data shape more readable template std::vector generateParams() { using T = typename element_type_traits::value_type; @@ -173,6 +177,140 @@ std::vector generateParams() { std::vector{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{3})) .testcaseName("NMSRotated_new_rotation_basic"), + Builder{} + .boxes(reference_tests::Tensor(ET, {1, 4, 5}, std::vector{/*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, 2, 4}, std::vector{/*0*/ 0.65, 0.7, 0.55, 0.96, /*1*/ 0.65, 0.7, 0.55, 0.96})) + .maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector{5000})) + .iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.5f})) + .scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .sortResultsDescending(false) + .expectedSelectedIndices(reference_tests::Tensor( + ET_IND, + {6, 3}, + std::vector{0, 0, 3, 0, 0, 1, 0, 0, 0, 0, 1, 3, 0, 1, 1, 0, 1, 0})) // batch, class, box_id (sorted max score first) + .expectedSelectedScores( + reference_tests::Tensor(ET_TH, + {6, 3}, + std::vector{0.0, 0.0, 0.96, 0.0, 0.0, 0.7, 0.0, 0.0, 0.65, 0.0, 1.0, 0.96, 0.0, 1.0, 0.7, 0.0, 1.0, 0.65})) + .expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector{6})) + .testcaseName("NMSRotated_new_rotation_class_2"), + Builder{} + .boxes(reference_tests::Tensor(ET, {2, 4, 5}, std::vector{/* First batch */ + /*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, + /* Second batch */ + /*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, {2, 1, 4}, std::vector{0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96})) + .maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector{5000})) + .iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.5f})) + .scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .sortResultsDescending(false) + .expectedSelectedIndices(reference_tests::Tensor( + ET_IND, + {6, 3}, + std::vector{0, 0, 3, 0, 0, 1, 0, 0, 0, + 1, 0, 3, 1, 0, 1, 1, 0, 0})) // batch, class, box_id (sorted max score first) + .expectedSelectedScores( + reference_tests::Tensor(ET_TH, + {6, 3}, + std::vector{0.0, 0.0, 0.96, 0.0, 0.0, 0.7, 0.0, 0.0, 0.65, 1.0, 0.0, 0.96, 1.0, 0.0, 0.7, 1.0, 0.0, 0.65})) + .expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector{6})) + .testcaseName("NMSRotated_new_rotation_batch_2"), + Builder{} + .boxes(reference_tests::Tensor(ET, {2, 4, 5}, std::vector{/* First batch */ + /*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, + /* Second batch */ + /*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, {2, 2, 4}, std::vector{0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96, + 0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96, + 0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96, + 0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96})) + .maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector{5000})) + .iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.5f})) + .scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .sortResultsDescending(false) + .expectedSelectedIndices(reference_tests::Tensor( + ET_IND, + {12, 3}, + std::vector{0, 0, 3, 0, 0, 1, 0, 0, 0, + 0, 1, 3, 0, 1, 1, 0, 1, 0, + 1, 0, 3, 1, 0, 1, 1, 0, 0, + 1, 1, 3, 1, 1, 1, 1, 1, 0})) // batch, class, box_id (sorted max score first) + .expectedSelectedScores( + reference_tests::Tensor(ET_TH, + {12, 3}, + std::vector{0.0, 0.0, 0.96, 0.0, 0.0, 0.7, 0.0, 0.0, 0.65, + 0.0, 1.0, 0.96, 0.0, 1.0, 0.7, 0.0, 1.0, 0.65, + 1.0, 0.0, 0.96, 1.0, 0.0, 0.7, 1.0, 0.0, 0.65, + 1.0, 1.0, 0.96, 1.0, 1.0, 0.7, 1.0, 1.0, 0.65, + })) + + .expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector{12})) + .testcaseName("NMSRotated_new_rotation_batch_2_class_2_sort_attr_false"), + Builder{} + .boxes(reference_tests::Tensor(ET, {2, 4, 5}, std::vector{/* First batch */ + /*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, + /* Second batch */ + /*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, {2, 2, 4}, std::vector{0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96, + 0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96, + 0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96, + 0.65, 0.7, 0.55, 0.96, 0.65, 0.7, 0.55, 0.96})) + .maxOutputBoxesPerClass(reference_tests::Tensor(ET_BOX, {}, std::vector{5000})) + .iouThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.5f})) + .scoreThreshold(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .softNmsSigma(reference_tests::Tensor(ET_TH, {}, std::vector{0.0f})) + .sortResultsDescending(true) + + .expectedSelectedIndices(reference_tests::Tensor( + ET_IND, + {12, 3}, + std::vector{0, 0, 3, 0, 1, 3, 1, 0, 3, 1, 1, 3, + 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0})) // batch, class, box_id (sorted max score first) + .expectedSelectedScores( + reference_tests::Tensor(ET_TH, + {12, 3}, + std::vector{0.0, 0.0, 0.96, + 0.0, 1.0, 0.96, + 1.0, 0.0, 0.96, + 1.0, 1.0, 0.96, + + 0.0, 0.0, 0.7, + 0.0, 1.0, 0.7, + 1.0, 0.0, 0.7, + 1.0, 1.0, 0.7, + + 0.0, 0.0, 0.65, + 0.0, 1.0, 0.65, + 1.0, 0.0, 0.65, + 1.0, 1.0, 0.65 + })) + .expectedValidOutputs(reference_tests::Tensor(ET_IND, {1}, std::vector{12})) + .testcaseName("NMSRotated_new_rotation_batch_2_class_2_sort_attr_true"), Builder{} .boxes(reference_tests::Tensor(ET, {1, 4, 5}, std::vector{/*0*/ 7.0, 4.0, 8.0, 7.0, 0.5, /*1*/ 4.0, 7.0, 9.0, 11.0, 0.6, @@ -418,6 +556,7 @@ std::vector generateParams() { }; return params; } +// clang-format on std::vector generateCombinedParams() { const std::vector> generatedParams{