diff --git a/modules/dnn/perf/perf_net.cpp b/modules/dnn/perf/perf_net.cpp index fc559c0ca0..cb9138ce24 100644 --- a/modules/dnn/perf/perf_net.cpp +++ b/modules/dnn/perf/perf_net.cpp @@ -124,6 +124,11 @@ PERF_TEST_P_(DNNTestNetwork, MobileNetv2_ONNX) processNet("dnn/onnx/models/mobilenetv2.onnx", "", cv::Size(224, 224)); } +PERF_TEST_P_(DNNTestNetwork, ResNet50_QDQ_ONNX) +{ + processNet("dnn/onnx/models/resnet50-v1-12-qdq.onnx", "", cv::Size(224, 224)); +} + PERF_TEST_P_(DNNTestNetwork, SqueezeNet_v1_1) { processNet("dnn/squeezenet_v1.1.caffemodel", "dnn/squeezenet_v1.1.prototxt", cv::Size(227, 227)); @@ -157,6 +162,15 @@ PERF_TEST_P_(DNNTestNetwork, MobileNet_SSD_v2_TensorFlow) processNet("dnn/ssd_mobilenet_v2_coco_2018_03_29.pb", "ssd_mobilenet_v2_coco_2018_03_29.pbtxt", cv::Size(300, 300)); } +PERF_TEST_P_(DNNTestNetwork, MobileNet_SSD_v1_ONNX) +{ + Mat image(cv::Size(300, 300), CV_8UC3); + randu(image, 0, 255); + int imsize[] = {1, image.rows, image.cols, 3}; + Mat input(4, imsize, CV_8U, image.data); + processNet("dnn/onnx/models/ssd_mobilenet_v1_12.onnx", "", input); +} + PERF_TEST_P_(DNNTestNetwork, DenseNet_121) { processNet("dnn/DenseNet_121.caffemodel", "dnn/DenseNet_121.prototxt", cv::Size(224, 224)); diff --git a/modules/dnn/src/layers/concat2_layer.cpp b/modules/dnn/src/layers/concat2_layer.cpp index 896a6a2542..8e75529a2a 100644 --- a/modules/dnn/src/layers/concat2_layer.cpp +++ b/modules/dnn/src/layers/concat2_layer.cpp @@ -40,22 +40,54 @@ static void concat(const std::vector& inps, Mat& out, int axis) totalSize += inps[i].total()*esz; } - parallel_for_(Range(0, ninputs), [&](const Range& r) { - for (int k = r.start; k < r.end; k++) { - const Mat& inp_k = inps[k]; - uchar* outptr = out.data; - const uchar* inptr_k = inp_k.data; - int sz_a; - for (int i = 0; i < k; i++) { - sz_a = inps[i].size[axis]; - outptr += sliceSize*sz_a; - } - sz_a = inp_k.size[axis]; - size_t sliceSize_k = sliceSize*sz_a; - for (int i = 0; i < nslices; i++) - memcpy(outptr + i*outStep, inptr_k + i*sliceSize_k, sliceSize_k); + // Precompute per-input destination offset and per-slice size. + std::vector dstOffset(ninputs); + std::vector sliceSize_k_vec(ninputs); + { + size_t acc = 0; + for (int k = 0; k < ninputs; k++) { + int sz_a = inps[k].size[axis]; + dstOffset[k] = acc; + sliceSize_k_vec[k] = sliceSize * sz_a; + acc += sliceSize_k_vec[k]; } - }, (totalSize > 1000000 ? ninputs : 1)); + } + const size_t CHUNK = 64 * 1024; + + // Precompute per-input chunk counts and a prefix sum for fast index decode. + std::vector chunkOff(ninputs + 1, 0); + for (int k = 0; k < ninputs; k++) + chunkOff[k + 1] = chunkOff[k] + (int)((sliceSize_k_vec[k] + CHUNK - 1) / CHUNK); + int chunksPerSlice = chunkOff[ninputs]; + int totalChunks = chunksPerSlice * nslices; + + if (totalSize > CHUNK && totalChunks > 0) { + parallel_for_(Range(0, totalChunks), [&](const Range& r) { + for (int c = r.start; c < r.end; c++) { + int s = c / chunksPerSlice; + int local = c % chunksPerSlice; + int k = 0; + while (local >= chunkOff[k + 1]) k++; + int chunkInK = local - chunkOff[k]; + size_t byteStart = (size_t)chunkInK * CHUNK; + size_t byteEnd = std::min(byteStart + CHUNK, sliceSize_k_vec[k]); + + const uchar* inptr_k = inps[k].data; + uchar* outptr = out.data + dstOffset[k]; + memcpy(outptr + (size_t)s * outStep + byteStart, + inptr_k + (size_t)s * sliceSize_k_vec[k] + byteStart, + byteEnd - byteStart); + } + }); + } else { + for (int k = 0; k < ninputs; k++) { + const uchar* inptr_k = inps[k].data; + uchar* outptr = out.data + dstOffset[k]; + size_t sliceSize_k = sliceSize_k_vec[k]; + for (int s = 0; s < nslices; s++) + memcpy(outptr + (size_t)s * outStep, inptr_k + (size_t)s * sliceSize_k, sliceSize_k); + } + } } class Concat2LayerImpl CV_FINAL : public Concat2Layer diff --git a/modules/dnn/src/layers/layers_common.cpp b/modules/dnn/src/layers/layers_common.cpp index ff063827b7..cfed3b9e1a 100644 --- a/modules/dnn/src/layers/layers_common.cpp +++ b/modules/dnn/src/layers/layers_common.cpp @@ -333,8 +333,28 @@ void reshapeAndCopyFirst(InputArrayOfArrays inputs, if (inpTotal == 0 && outTotal == 0) return; Mat inp_ = inp.reshape(0, shape); - if (inp_.data != outref[0].data) - inp_.copyTo(outref[0]); + if (inp_.data != outref[0].data) { + // Parallel memcpy for large buffers to avoid single-thread bottleneck + // on reshape-style layers that don't get in-place-allocated. + CV_Assert(inp_.isContinuous()); + CV_Assert(outref[0].isContinuous()); + size_t bytes = inpTotal * inp_.elemSize(); + const size_t CHUNK_BYTES = 64 * 1024; + if (bytes > 2 * CHUNK_BYTES) { + const uchar* src = inp_.data; + uchar* dst = outref[0].data; + int nChunks = (int)((bytes + CHUNK_BYTES - 1) / CHUNK_BYTES); + parallel_for_(Range(0, nChunks), [&](const Range& r) { + for (int i = r.start; i < r.end; i++) { + size_t off = (size_t)i * CHUNK_BYTES; + size_t len = std::min(CHUNK_BYTES, bytes - off); + memcpy(dst + off, src + off, len); + } + }); + } else { + inp_.copyTo(outref[0]); + } + } } else { UMat inp = inputs.getUMat(0); diff --git a/modules/dnn/src/layers/nary_eltwise_layers.cpp b/modules/dnn/src/layers/nary_eltwise_layers.cpp index b3946da774..52548cef5a 100644 --- a/modules/dnn/src/layers/nary_eltwise_layers.cpp +++ b/modules/dnn/src/layers/nary_eltwise_layers.cpp @@ -621,6 +621,12 @@ public: const Mat& a = inputs[0]; const Mat& b = inputs[1]; Mat& out = outputs[0]; + + if (op == OPERATION::POW && std::is_same::value && b.total() == 1) { + cv::pow(a, (double)(*(const T*)b.data), out); + return; + } + CV_Assert(helper.shapes.size() == 3 && helper.steps.size() == 3); binary_forward_impl(f, helper.max_ndims, helper.shapes[0], a.ptr(), helper.steps[1], b.ptr(), helper.steps[2], out.ptr(), helper.steps[0], block_size); diff --git a/modules/dnn/src/layers/split2_layer.cpp b/modules/dnn/src/layers/split2_layer.cpp index cbe7489004..27c4d4471c 100644 --- a/modules/dnn/src/layers/split2_layer.cpp +++ b/modules/dnn/src/layers/split2_layer.cpp @@ -42,7 +42,6 @@ static void split(const Mat& inp, std::vector& outs, int axis) size_t esz = inp.elemSize(); size_t sliceSize = esz; size_t inpStep = 0; - size_t totalSize = inp.total()*esz; int outSize_a = 0; for (int i = ndims-1; i > axis; i--) sliceSize *= inpShape[i]; @@ -68,22 +67,30 @@ static void split(const Mat& inp, std::vector& outs, int axis) CV_Assert(outSize_a == inpShape[axis]); - parallel_for_(Range(0, (int)noutputs), [&](const Range& r) { - for (int k = r.start; k < r.end; k++) { - const uchar* inptr = inp.data; - Mat& out_k = outs[k]; - uchar* outptr_k = out_k.data; - int sz_a; - for (int i = 0; i < k; i++) { - sz_a = outs[i].size[axis]; - inptr += sliceSize*sz_a; - } - sz_a = out_k.size[axis]; - size_t sliceSize_k = sliceSize*sz_a; - for (int i = 0; i < nslices; i++) - memcpy(outptr_k + i*sliceSize_k, inptr + i*inpStep, sliceSize_k); + // Precompute per-output source offset and per-slice size. + std::vector srcOffset(noutputs); + std::vector sliceSize_k_vec(noutputs); + { + size_t acc = 0; + for (size_t k = 0; k < noutputs; k++) { + int sz_a = outs[k].size[axis]; + srcOffset[k] = acc; + sliceSize_k_vec[k] = sliceSize * sz_a; + acc += sliceSize_k_vec[k]; } - }, (totalSize > 1000000 ? noutputs : 1)); + } + + int64_t nTasks = (int64_t)nslices * (int64_t)noutputs; + parallel_for_(Range(0, (int)nTasks), [&](const Range& r) { + for (int64_t idx = r.start; idx < r.end; idx++) { + int k = (int)(idx % (int64_t)noutputs); + int s = (int)(idx / (int64_t)noutputs); + uchar* outptr_k = outs[k].data; + const uchar* inptr = inp.data + srcOffset[k]; + size_t sliceSize_k = sliceSize_k_vec[k]; + memcpy(outptr_k + (size_t)s*sliceSize_k, inptr + (size_t)s*inpStep, sliceSize_k); + } + }); } class Split2LayerImpl CV_FINAL : public Split2Layer diff --git a/modules/dnn/src/layers/transpose_layer.cpp b/modules/dnn/src/layers/transpose_layer.cpp index 2d6906cbac..3cb447fca1 100644 --- a/modules/dnn/src/layers/transpose_layer.cpp +++ b/modules/dnn/src/layers/transpose_layer.cpp @@ -75,30 +75,39 @@ static void transpose(const Mat& inp, const std::vector& perm, Mat& out) size_t p4 = inpStep_[perm_[2]], p3 = inpStep_[perm_[3]]; size_t p2 = inpStep_[perm_[4]], p1 = inpStep_[perm_[5]], p0 = inpStep_[perm_[6]]; + int64_t outerTotal = (int64_t)sz6 * sz5 * sz4 * sz3 * sz2; + #undef CV_IMPLEMENT_TRANSPOSE #define CV_IMPLEMENT_TRANSPOSE(typ) \ - const typ* inptr0 = (const typ*)inp.data; \ - typ* outptr = (typ*)out.data; \ - for (int i6 = 0; i6 < sz6; i6++) { \ - for (int i5 = 0; i5 < sz5; i5++) { \ - for (int i4 = 0; i4 < sz4; i4++) { \ - for (int i3 = 0; i3 < sz3; i3++) { \ - for (int i2 = 0; i2 < sz2; i2++) { \ - for (int i1 = 0; i1 < sz1; i1++, outptr += sz0) { \ - int i0 = 0; \ - const typ* inptr = inptr0 + i6*p6 + i5*p5 + i4*p4 + i3*p3 + i2*p2 + i1*p1; \ - for (; i0 <= sz0 - 3; i0 += 3) { \ - size_t ip0 = i0*p0; \ - typ t0 = inptr[ip0]; \ - typ t1 = inptr[ip0+p0]; \ - typ t2 = inptr[ip0+p0*2]; \ - outptr[i0] = t0; \ - outptr[i0+1] = t1; \ - outptr[i0+2] = t2; \ + parallel_for_(Range(0, (int)outerTotal), [&](const Range& r) { \ + const typ* inptr0 = (const typ*)inp.data; \ + typ* outptr0 = (typ*)out.data; \ + for (int64_t idx = r.start; idx < r.end; idx++) { \ + int64_t q = idx; \ + int i2 = (int)(q % sz2); q /= sz2; \ + int i3 = (int)(q % sz3); q /= sz3; \ + int i4 = (int)(q % sz4); q /= sz4; \ + int i5 = (int)(q % sz5); q /= sz5; \ + int i6 = (int)q; \ + const typ* inptrBase = inptr0 + i6*p6 + i5*p5 + i4*p4 + i3*p3 + i2*p2; \ + typ* outptr = outptr0 + idx * ((int64_t)sz1 * sz0); \ + for (int i1 = 0; i1 < sz1; i1++, outptr += sz0) { \ + const typ* inptr = inptrBase + i1*p1; \ + int i0 = 0; \ + for (; i0 <= sz0 - 3; i0 += 3) { \ + size_t ip0 = i0*p0; \ + typ t0 = inptr[ip0]; \ + typ t1 = inptr[ip0+p0]; \ + typ t2 = inptr[ip0+p0*2]; \ + outptr[i0] = t0; \ + outptr[i0+1] = t1; \ + outptr[i0+2] = t2; \ + } \ + for (; i0 < sz0; i0++) \ + outptr[i0] = inptr[i0*p0]; \ + } \ } \ - for (; i0 < sz0; i0++) \ - outptr[i0] = inptr[i0*p0]; \ - }}}}}} + }); if (esz == 4) { CV_IMPLEMENT_TRANSPOSE(int) diff --git a/modules/dnn/test/test_model.cpp b/modules/dnn/test/test_model.cpp index 62c622e3fe..5fcf9a562c 100644 --- a/modules/dnn/test/test_model.cpp +++ b/modules/dnn/test/test_model.cpp @@ -880,23 +880,8 @@ TEST_P(Reproducibility_ResNet50_ONNX, Accuracy) false, true, CV_32F); ASSERT_TRUE(!input.empty()); - Mat out; - double min_t = 0; - const int niters = -#ifdef _DEBUG - 1; -#else - 30; -#endif - - for (int i = 0; i < niters; i++) { - double t = (double)getTickCount(); - net.setInput(input); - out = net.forward(); - t = (double)getTickCount() - t; - min_t = i == 0 ? t : std::min(min_t, t); - } - printf("run time = %.2fms\n", min_t*1000./getTickFrequency()); + net.setInput(input); + Mat out = net.forward(); std::vector > ref = {{285, 10.13}, {287, 9.68}, {283, 8.83}, {278, 8.56}, {279, 8.34}}; std::vector > res; @@ -944,23 +929,8 @@ TEST_P(Reproducibility_ResNet50_QDQ_ONNX, Accuracy) false, true, CV_32F); ASSERT_TRUE(!input.empty()); - Mat out; - double min_t = 0; - const int niters = -#ifdef _DEBUG - 1; -#else - 30; -#endif - - for (int i = 0; i < niters; i++) { - double t = (double)getTickCount(); - net.setInput(input); - out = net.forward(); - t = (double)getTickCount() - t; - min_t = i == 0 ? t : std::min(min_t, t); - } - printf("run time = %.2fms\n", min_t*1000./getTickFrequency()); + net.setInput(input); + Mat out = net.forward(); const int K = 5; std::vector > res; @@ -1012,22 +982,8 @@ TEST_P(Reproducibility_MobileNetSSD_ONNX, Accuracy) std::vector outNames = net.getUnconnectedOutLayersNames(); std::vector outs; - double min_t = 0; - const int niters = -#ifdef _DEBUG - 1; -#else - 30; -#endif - - for (int i = 0; i < niters; i++) { - double t = (double)getTickCount(); - net.setInput(input8dim4); - net.forward(outs, outNames); - t = (double)getTickCount() - t; - min_t = i == 0 ? t : std::min(min_t, t); - } - printf("run time = %.2fms\n", min_t*1000./getTickFrequency()); + net.setInput(input8dim4); + net.forward(outs, outNames); // Model outputs: detection_boxes [1,N,4], detection_classes [1,N], // detection_scores [1,N], num_detections [1] @@ -1091,4 +1047,240 @@ TEST_P(Reproducibility_MobileNetSSD_ONNX, Accuracy) INSTANTIATE_TEST_CASE_P(/**/, Reproducibility_MobileNetSSD_ONNX, testing::ValuesIn(getAvailableTargets(DNN_BACKEND_OPENCV))); + +namespace { + +enum YoloFormat { YOLO_V5, YOLO_V8, YOLO_X }; +static void YoloPostprocess(const Mat& out, YoloFormat fmt, int inputSize, + float confTh, float nmsTh, + std::vector& classIds, + std::vector& confidences, + std::vector& boxes) +{ + CV_Assert(out.dims == 3); + bool hasObj = (fmt != YOLO_V8); + int nclasses = 80; + int stride = 4 + (hasObj ? 1 : 0) + nclasses; + + const float* data = nullptr; + std::vector buf; + int N; + if (fmt == YOLO_V8) { + int C = out.size[1]; + N = out.size[2]; + CV_Assert(C == stride); + const float* src = out.ptr(); + buf.resize((size_t)N * C); + for (int i = 0; i < C; i++) + for (int j = 0; j < N; j++) + buf[j * C + i] = src[i * N + j]; + data = buf.data(); + } else { + N = out.size[1]; + CV_Assert(out.size[2] == stride); + data = out.ptr(); + } + + // YOLOX grid decode tables + std::vector gridX, gridY, strideVec; + if (fmt == YOLO_X) { + const int strides[] = {8, 16, 32}; + gridX.resize(N); gridY.resize(N); strideVec.resize(N); + int idx = 0; + for (int si = 0; si < 3; si++) { + int gs = inputSize / strides[si]; + for (int y = 0; y < gs; y++) + for (int x = 0; x < gs; x++) { + gridX[idx] = (float)x; + gridY[idx] = (float)y; + strideVec[idx] = (float)strides[si]; + idx++; + } + } + CV_Assert(idx == N); + } + + int classOff = hasObj ? 5 : 4; + double scale = 1.0 / inputSize; + std::vector intBoxes; + std::vector allCls; + std::vector allConf; + std::vector allBoxes; + + for (int i = 0; i < N; i++) { + const float* r = data + (size_t)i * stride; + float obj = hasObj ? r[4] : 1.0f; + if (obj < confTh) continue; + + int bestCls = 0; float bestScore = 0; + for (int c = 0; c < nclasses; c++) { + float s = r[classOff + c] * obj; + if (s > bestScore) { bestScore = s; bestCls = c; } + } + if (bestScore < confTh) continue; + + float cx, cy, w, h; + if (fmt == YOLO_X) { + cx = (r[0] + gridX[i]) * strideVec[i]; + cy = (r[1] + gridY[i]) * strideVec[i]; + w = std::exp(r[2]) * strideVec[i]; + h = std::exp(r[3]) * strideVec[i]; + } else { + cx = r[0]; cy = r[1]; w = r[2]; h = r[3]; + } + + intBoxes.push_back(Rect((int)(cx-w/2), (int)(cy-h/2), (int)w, (int)h)); + allConf.push_back(bestScore); + allCls.push_back(bestCls); + allBoxes.push_back(Rect2d((cx-w/2)*scale, (cy-h/2)*scale, w*scale, h*scale)); + } + + std::vector indices; + cv::dnn::NMSBoxes(intBoxes, allConf, confTh, nmsTh, indices); + for (int idx : indices) { + classIds.push_back(allCls[idx]); + confidences.push_back(allConf[idx]); + boxes.push_back(allBoxes[idx]); + } +} + +} // local namespace + +typedef testing::TestWithParam Reproducibility_YOLOv5n_ONNX; +TEST_P(Reproducibility_YOLOv5n_ONNX, Accuracy) +{ + Target targetId = GetParam(); + applyTestTag(targetId == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + ASSERT_TRUE(ocl::useOpenCL() || targetId == DNN_TARGET_CPU || targetId == DNN_TARGET_CPU_FP16); + + std::string modelname = _tf("yolov5n.onnx", false); + Net net = readNetFromONNX(modelname); + net.setPreferableBackend(DNN_BACKEND_OPENCV); + net.setPreferableTarget(targetId); + if (targetId == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + + std::string imgname = _tf("dog416.png"); + Mat image = imread(imgname); + ASSERT_TRUE(!image.empty()); + Mat input = blobFromImage(image, 1.0/255.0, Size(640, 640), Scalar(), true, false, CV_32F); + + net.setInput(input); + Mat out = net.forward(); + + if (out.type() != CV_32F) out.convertTo(out, CV_32F); + + std::vector classIds; + std::vector confidences; + std::vector testBoxes; + YoloPostprocess(out, YOLO_V5, 640, 0.25f, 0.45f, classIds, confidences, testBoxes); + + std::vector refClassIds = {16, 2, 1, 1}; + std::vector refScores = {0.711f, 0.581f, 0.344f, 0.275f}; + std::vector refBoxes = { + Rect2d(0.168262, 0.374023, 0.247852, 0.577734), // dog + Rect2d(0.605469, 0.134375, 0.286719, 0.156250), // car + Rect2d(0.186279, 0.248828, 0.148926, 0.129688), // bicycle (small) + Rect2d(0.231836, 0.277930, 0.519141, 0.483203), // bicycle (large) + }; + + normAssertDetections(refClassIds, refScores, refBoxes, + classIds, confidences, testBoxes, + "", 0.25f, /*scoreDiff=*/0.2, /*iouDiff=*/0.2); +} +INSTANTIATE_TEST_CASE_P(/**/, Reproducibility_YOLOv5n_ONNX, + testing::ValuesIn(getAvailableTargets(DNN_BACKEND_OPENCV))); + + +typedef testing::TestWithParam Reproducibility_YOLOv8n_ONNX; +TEST_P(Reproducibility_YOLOv8n_ONNX, Accuracy) +{ + Target targetId = GetParam(); + applyTestTag(targetId == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + ASSERT_TRUE(ocl::useOpenCL() || targetId == DNN_TARGET_CPU || targetId == DNN_TARGET_CPU_FP16); + + std::string modelname = _tf("yolov8n.onnx", false); + Net net = readNetFromONNX(modelname); + net.setPreferableBackend(DNN_BACKEND_OPENCV); + net.setPreferableTarget(targetId); + if (targetId == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + + std::string imgname = _tf("dog416.png"); + Mat image = imread(imgname); + ASSERT_TRUE(!image.empty()); + Mat input = blobFromImage(image, 1.0/255.0, Size(640, 640), Scalar(), true, false, CV_32F); + + net.setInput(input); + Mat out = net.forward(); + + if (out.type() != CV_32F) out.convertTo(out, CV_32F); + + std::vector classIds; + std::vector confidences; + std::vector testBoxes; + YoloPostprocess(out, YOLO_V8, 640, 0.25f, 0.45f, classIds, confidences, testBoxes); + + std::vector refClassIds = {16, 1, 7}; + std::vector refScores = {0.827f, 0.809f, 0.544f}; + std::vector refBoxes = { + Rect2d(0.171157, 0.386951, 0.231909, 0.551873), // dog + Rect2d(0.160967, 0.234788, 0.577899, 0.495077), // bicycle + Rect2d(0.608337, 0.130141, 0.291832, 0.167390), // truck + }; + + normAssertDetections(refClassIds, refScores, refBoxes, + classIds, confidences, testBoxes, + "", 0.25f, /*scoreDiff=*/0.1, /*iouDiff=*/0.1); +} +INSTANTIATE_TEST_CASE_P(/**/, Reproducibility_YOLOv8n_ONNX, + testing::ValuesIn(getAvailableTargets(DNN_BACKEND_OPENCV))); + + +typedef testing::TestWithParam Reproducibility_YOLOXS_ONNX; +TEST_P(Reproducibility_YOLOXS_ONNX, Accuracy) +{ + Target targetId = GetParam(); + applyTestTag(targetId == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + ASSERT_TRUE(ocl::useOpenCL() || targetId == DNN_TARGET_CPU || targetId == DNN_TARGET_CPU_FP16); + + std::string modelname = _tf("yolox_s.onnx", false); + Net net = readNetFromONNX(modelname); + net.setPreferableBackend(DNN_BACKEND_OPENCV); + net.setPreferableTarget(targetId); + if (targetId == DNN_TARGET_CPU_FP16) + net.enableWinograd(false); + + std::string imgname = _tf("dog416.png"); + Mat image = imread(imgname); + ASSERT_TRUE(!image.empty()); + Mat input = blobFromImage(image, 1.0, Size(640, 640), Scalar(), false, false, CV_32F); + + net.setInput(input); + Mat out = net.forward(); + + if (out.type() != CV_32F) out.convertTo(out, CV_32F); + + std::vector classIds; + std::vector confidences; + std::vector testBoxes; + YoloPostprocess(out, YOLO_X, 640, 0.25f, 0.45f, classIds, confidences, testBoxes); + + std::vector refClassIds = {1, 16, 7, 1}; + std::vector refScores = {0.962f, 0.920f, 0.833f, 0.266f}; + std::vector refBoxes = { + Rect2d(0.160787, 0.225276, 0.577830, 0.503752), // bicycle (large) + Rect2d(0.172622, 0.386773, 0.230225, 0.554768), // dog + Rect2d(0.601869, 0.128871, 0.302539, 0.168476), // truck + Rect2d(0.166281, 0.251719, 0.339791, 0.385267), // bicycle (small) + }; + + normAssertDetections(refClassIds, refScores, refBoxes, + classIds, confidences, testBoxes, + "", 0.25f, /*scoreDiff=*/0.2, /*iouDiff=*/0.2); +} +INSTANTIATE_TEST_CASE_P(/**/, Reproducibility_YOLOXS_ONNX, + testing::ValuesIn(getAvailableTargets(DNN_BACKEND_OPENCV))); + + }} // namespace