Merge pull request #26124 from fengyuentau:dnn/topk_dtype

dnn(5.x): handle topk data type #26124

Resolves https://github.com/opencv/opencv/issues/26076

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Yuantao Feng 2024-09-09 20:15:04 +08:00 committed by GitHub
parent 8263c804de
commit ce5823c5eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 57 additions and 19 deletions

View File

@ -3,7 +3,6 @@
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "layers_common.hpp"
#include <opencv2/dnn/shape_utils.hpp>
@ -104,12 +103,24 @@ public:
// Assign output shape
auto output_shape = input_shape;
output_shape[axis_normalized] = K;
outputs.assign(1, output_shape);
outputs.assign(2, output_shape); // TODO: support indices of type CV_32S on 5.x
outputs.assign(2, output_shape);
return false;
}
void getTypes(const std::vector<MatType>& inputs,
const int requiredOutputs,
const int requiredInternals,
std::vector<MatType>& outputs,
std::vector<MatType>& internals) const CV_OVERRIDE {
// [TODO] Check depth of inputs[1] (K) once K becomes one of the inputs
outputs.resize(2);
outputs[0] = inputs.front();
// [TODO] Replace with inputs.back() once K becomes one of the inputs
// [TODO] OpenVINO does not support int64. Consider set type int32 instead if backend is ngraph
outputs[1] = CV_64S;
}
virtual void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE {
std::vector<Mat> inputs;
inputs_arr.getMatVector(inputs);
@ -119,7 +130,7 @@ public:
axis = normalize_axis(axis, input_shape.size());
}
template<class Comparator>
template<class Comparator, typename T>
void FindTopK(const Mat &input, Mat &output_value, Mat &output_index) {
const auto input_shape = shape(input);
size_t loops = std::accumulate(input_shape.begin(), input_shape.begin() + axis, 1, std::multiplies<int>());
@ -127,9 +138,9 @@ public:
int dim_axis = input_shape[axis];
if (loops == 1) {
auto worker = [&](const Range &r) {
const auto *input_ptr = input.ptr<const float>(); // TODO: support other input type
auto *output_value_ptr = output_value.ptr<float>();
auto *output_index_ptr = output_index.ptr<float>(); // TODO: use CV_32S on 5.x
const auto *input_ptr = input.ptr<const T>();
auto *output_value_ptr = output_value.ptr<T>();
auto *output_index_ptr = output_index.ptr<int64_t>();
Comparator cmp(input_ptr, step);
@ -155,9 +166,9 @@ public:
parallel_for_(Range(0, step), worker);
} else {
auto worker = [&](const Range &r) {
const auto *input_ptr = input.ptr<const float>();
auto *output_value_ptr = output_value.ptr<float>();
auto *output_index_ptr = output_index.ptr<float>();
const auto *input_ptr = input.ptr<const T>();
auto *output_value_ptr = output_value.ptr<T>();
auto *output_index_ptr = output_index.ptr<int64_t>();
Comparator cmp(input_ptr, step);
@ -206,9 +217,35 @@ public:
auto &output_index = outputs.back();
if (largest) {
FindTopK<ComparatorGreater<float>>(input, output_value, output_index);
switch (input.depth()) {
case CV_8U: FindTopK<ComparatorGreater<uint8_t>, uint8_t>(input, output_value, output_index); break;
case CV_8S: FindTopK<ComparatorGreater<int8_t>, int8_t>(input, output_value, output_index); break;
case CV_16U: FindTopK<ComparatorGreater<uint16_t>, uint16_t>(input, output_value, output_index); break;
case CV_16S: FindTopK<ComparatorGreater<int16_t>, int16_t>(input, output_value, output_index); break;
case CV_16F: FindTopK<ComparatorGreater<hfloat>, hfloat>(input, output_value, output_index); break;
case CV_32U: FindTopK<ComparatorGreater<unsigned>, unsigned>(input, output_value, output_index); break;
case CV_32S: FindTopK<ComparatorGreater<int>, int>(input, output_value, output_index); break;
case CV_32F: FindTopK<ComparatorGreater<float>, float>(input, output_value, output_index); break;
case CV_64U: FindTopK<ComparatorGreater<uint64_t>, uint64_t>(input, output_value, output_index); break;
case CV_64S: FindTopK<ComparatorGreater<int64_t>, int64_t>(input, output_value, output_index); break;
case CV_64F: FindTopK<ComparatorGreater<double>, double>(input, output_value, output_index); break;
default: CV_Error(Error::BadDepth, "Unsupported input data type");
}
} else {
FindTopK<ComparatorLess<float>>(input, output_value, output_index);
switch (input.depth()) {
case CV_8U: FindTopK<ComparatorLess<uint8_t>, uint8_t>(input, output_value, output_index); break;
case CV_8S: FindTopK<ComparatorLess<int8_t>, int8_t>(input, output_value, output_index); break;
case CV_16U: FindTopK<ComparatorLess<uint16_t>, uint16_t>(input, output_value, output_index); break;
case CV_16S: FindTopK<ComparatorLess<int16_t>, int16_t>(input, output_value, output_index); break;
case CV_16F: FindTopK<ComparatorLess<hfloat>, hfloat>(input, output_value, output_index); break;
case CV_32U: FindTopK<ComparatorLess<unsigned>, unsigned>(input, output_value, output_index); break;
case CV_32S: FindTopK<ComparatorLess<int>, int>(input, output_value, output_index); break;
case CV_32F: FindTopK<ComparatorLess<float>, float>(input, output_value, output_index); break;
case CV_64U: FindTopK<ComparatorLess<uint64_t>, uint64_t>(input, output_value, output_index); break;
case CV_64S: FindTopK<ComparatorLess<int64_t>, int64_t>(input, output_value, output_index); break;
case CV_64F: FindTopK<ComparatorLess<double>, double>(input, output_value, output_index); break;
default: CV_Error(Error::BadDepth, "Unsupported input data type");
}
}
}

View File

@ -3173,7 +3173,7 @@ void ONNXImporter::parseTopK(LayerParams& layerParams, const opencv_onnx::NodePr
CV_CheckTrue(K_const, "OnnxImporter/TopK: K being non-constant is not supported");
Mat input_K = getBlob(node_proto, 1);
int K = input_K.at<int>(0);
int K = static_cast<int>(input_K.at<int64_t>(0));
layerParams.set("k", K);
}

View File

@ -567,7 +567,6 @@ namespace cv { namespace dnn {
auto& mat = shared_block->host;
CV_Assert(mat.isContinuous());
CV_Assert(mat.type() == CV_32F);
if (!shared_block->d2h_event)
shared_block->d2h_event = cuda4dnn::csl::Event(true);

View File

@ -395,7 +395,7 @@
"test_tfidfvectorizer_tf_uniandbigrams_skip5", // Issue:: Parser: Can't create layer "onnx_node_output_0!Y" of type "TfIdfVectorizer" in function 'getLayerInstance'
"test_tile", // Issue:: Parser: ONNX/Tile: repeats being non-constant is not supported. in function 'parseTile' (layer parameters are dynamic)
"test_tile_precomputed", // // ---- same as above ---
"test_top_k", // Issue:: Parser: Can't create layer "onnx_node_output_0!values" of type "TopK" in function 'getLayerInstance'
"test_top_k", // Issue:: K being input is not compatible with the current engine
"test_top_k_negative_axis", // ---- same as above ---
"test_top_k_smallest", // ---- same as above ---
"test_training_dropout", // Issue::cvtest::norm::wrong data type

View File

@ -3278,8 +3278,12 @@ TEST_P(Test_ONNX_layers, ClipDivSharedConstant) {
testONNXModels("clip_div_shared_constant");
}
// Bug: https://github.com/opencv/opencv/issues/26076
TEST_P(Test_ONNX_layers, DISABLED_TopK) {
TEST_P(Test_ONNX_layers, TopK) {
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH ||
backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 ||
backend == DNN_BACKEND_INFERENCE_ENGINE) {
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE); // OpenVINO does not support int64
}
auto test = [&](const std::string &basename, double l1 = 0, double lInf = 0) {
std::string onnxmodel = _tf("models/" + basename + ".onnx", true);
Mat input = readTensorFromONNX(_tf("data/input_" + basename + ".pb"));
@ -3299,8 +3303,6 @@ TEST_P(Test_ONNX_layers, DISABLED_TopK) {
Mat output_res_val = outputs.front(),
output_res_ind = outputs.back();
output_ref_ind.convertTo(output_ref_ind, CV_32F); // TODO: revise this conversion in 5.x
normAssert(output_ref_val, output_res_val, (basename + " values").c_str(), l1 ? l1 : default_l1, lInf ? lInf : default_lInf);
normAssert(output_ref_ind, output_res_ind, (basename + " indices").c_str(), l1 ? l1 : default_l1, lInf ? lInf : default_lInf);