mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge pull request #25794 from Abdurrahheem:ash/yolov10-support
Add sample support of YOLOv9 and YOLOv10 in OpenCV #25794 This PR adds sample support of [`YOLOv9`](https://github.com/WongKinYiu/yolov9) and [`YOLOv10`](https://github.com/THU-MIG/yolov10/tree/main)) in OpenCV. Models for this test are located in this [PR](https://github.com/opencv/opencv_extra/pull/1186). **Running YOLOv10 using OpenCV.** 1. In oder to run `YOLOv10` one needs to cut off postporcessing with dynamic shapes from torch and then convert it to ONNX. If someone is looking for ready solution, there is [this forked branch](https://github.com/Abdurrahheem/yolov10/tree/ash/opencv-export) from official YOLOv10. Particularty follow this proceduce. ```bash git clone git@github.com:Abdurrahheem/yolov10.git conda create -n yolov10 python=3.9 conda activate yolov10 pip install -r requirements.txt python export_opencv.py --model=<model-name> --imgsz=<input-img-size> ``` By default `model="yolov10s"` and `imgsz=(480,640)`. This will generate file `yolov10s.onnx`, which can be use for inference in OpenCV 2. For inference part on OpenCV. one can use `yolo_detector.cpp` [sample](https://github.com/opencv/opencv/blob/4.x/samples/dnn/yolo_detector.cpp). If you have followed above exporting procedure, then you can use following command to run the model. ``` bash build opencv from source cd build ./bin/example_dnn_yolo_detector --model=<path-to-yolov10s.onnx-file> --yolo=yolov10 --width=640 --height=480 --input=<path-to-image> --scale=0.003921568627 --padvalue=114 ``` If you do not specify `--input` argument, OpenCV will grab first camera that is avaliable on your platform. For more deatils on how to run the `yolo_detector.cpp` file see this [guide](https://docs.opencv.org/4.x/da/d9d/tutorial_dnn_yolo.html#autotoc_md443) **Running YOLOv9 using OpenCV** 1. Export model following [official guide](https://github.com/WongKinYiu/yolov9)of the YOLOv9 repository. Particularly you can do following for converting. ```bash git clone https://github.com/WongKinYiu/yolov9.git cd yolov9 conda create -n yolov9 python=3.9 conda activate yolov9 pip install -r requirements.txt wget https://github.com/WongKinYiu/yolov9/releases/download/v0.1/yolov9-t-converted.pt python export.py --weights=./yolov9-t-converted.pt --include=onnx --img-size=(480,640) ``` This will generate <yolov9-t-converted.onnx> file. 2. Inference on OpenCV. ```bash build opencv from source cd build ./bin/example_dnn_yolo_detector --model=<path-to-yolov9-t-converted.onnx> --yolo=yolov9 --width=640 --height=480 --scale=0.003921568627 --padvalue=114 --path=<path-to-image> ``` ### 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:
committed by
GitHub
parent
939cb58cd6
commit
a8d1373919
@@ -19,7 +19,8 @@ void yoloPostProcessing(
|
||||
std::vector<Rect2d>& keep_boxes,
|
||||
float conf_threshold,
|
||||
float iou_threshold,
|
||||
const std::string& test_name);
|
||||
const std::string& model_name,
|
||||
const int nc=80);
|
||||
|
||||
template<typename TString>
|
||||
static std::string _tf(TString filename, bool required = true)
|
||||
@@ -2670,7 +2671,8 @@ void yoloPostProcessing(
|
||||
std::vector<Rect2d>& keep_boxes,
|
||||
float conf_threshold,
|
||||
float iou_threshold,
|
||||
const std::string& test_name
|
||||
const std::string& model_name,
|
||||
const int nc
|
||||
){
|
||||
|
||||
// Retrieve
|
||||
@@ -2678,11 +2680,13 @@ void yoloPostProcessing(
|
||||
std::vector<float> confidences;
|
||||
std::vector<Rect2d> boxes;
|
||||
|
||||
if (test_name == "yolov8"){
|
||||
if (model_name == "yolov8" || model_name == "yolov10" ||
|
||||
model_name == "yolov9")
|
||||
{
|
||||
cv::transposeND(outs[0], {0, 2, 1}, outs[0]);
|
||||
}
|
||||
|
||||
if (test_name == "yolonas"){
|
||||
if (model_name == "yolonas"){
|
||||
// outs contains 2 elemets of shape [1, 8400, 80] and [1, 8400, 4]. Concat them to get [1, 8400, 84]
|
||||
Mat concat_out;
|
||||
// squeeze the first dimension
|
||||
@@ -2696,22 +2700,27 @@ void yoloPostProcessing(
|
||||
outs[0] = outs[0].reshape(0, std::vector<int>{1, 8400, 84});
|
||||
}
|
||||
|
||||
// assert if last dim is 85 or 84
|
||||
CV_CheckEQ(outs[0].dims, 3, "Invalid output shape. The shape should be [1, #anchors, 85 or 84]");
|
||||
CV_CheckEQ((outs[0].size[2] == nc + 5 || outs[0].size[2] == 80 + 4), true, "Invalid output shape: ");
|
||||
|
||||
for (auto preds : outs){
|
||||
|
||||
preds = preds.reshape(1, preds.size[1]); // [1, 8400, 85] -> [8400, 85]
|
||||
for (int i = 0; i < preds.rows; ++i)
|
||||
{
|
||||
// filter out non object
|
||||
float obj_conf = (test_name == "yolov8" || test_name == "yolonas") ? 1.0f : preds.at<float>(i, 4) ;
|
||||
float obj_conf = (model_name == "yolov8" || model_name == "yolonas" ||
|
||||
model_name == "yolov9" || model_name == "yolov10") ? 1.0f : preds.at<float>(i, 4) ;
|
||||
if (obj_conf < conf_threshold)
|
||||
continue;
|
||||
|
||||
Mat scores = preds.row(i).colRange((test_name == "yolov8" || test_name == "yolonas") ? 4 : 5, preds.cols);
|
||||
Mat scores = preds.row(i).colRange((model_name == "yolov8" || model_name == "yolonas" || model_name == "yolov9" || model_name == "yolov10") ? 4 : 5, preds.cols);
|
||||
double conf;
|
||||
Point maxLoc;
|
||||
minMaxLoc(scores, 0, &conf, 0, &maxLoc);
|
||||
|
||||
conf = (test_name == "yolov8" || test_name == "yolonas") ? conf : conf * obj_conf;
|
||||
conf = (model_name == "yolov8" || model_name == "yolonas" || model_name == "yolov9" || model_name == "yolov10") ? conf : conf * obj_conf;
|
||||
if (conf < conf_threshold)
|
||||
continue;
|
||||
|
||||
@@ -2722,15 +2731,14 @@ void yoloPostProcessing(
|
||||
double w = det[2];
|
||||
double h = det[3];
|
||||
|
||||
// std::cout << "cx: " << cx << " cy: " << cy << " w: " << w << " h: " << h << " conf: " << conf << " idx: " << maxLoc.x << std::endl;
|
||||
// [x1, y1, x2, y2]
|
||||
if (test_name == "yolonas"){
|
||||
if (model_name == "yolonas" || model_name == "yolov10"){
|
||||
boxes.push_back(Rect2d(cx, cy, w, h));
|
||||
} else {
|
||||
boxes.push_back(Rect2d(cx - 0.5 * w, cy - 0.5 * h,
|
||||
cx + 0.5 * w, cy + 0.5 * h));
|
||||
}
|
||||
classIds.push_back(maxLoc.x);
|
||||
classIds.push_back(maxLoc.x);
|
||||
confidences.push_back(conf);
|
||||
}
|
||||
}
|
||||
@@ -2747,7 +2755,75 @@ void yoloPostProcessing(
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_nets, YOLOv10)
|
||||
{
|
||||
|
||||
std::string weightPath = _tf("models/yolov10s.onnx", false);
|
||||
|
||||
Size targetSize{640, 480};
|
||||
float conf_threshold = 0.50;
|
||||
float iou_threshold = 0.50;
|
||||
|
||||
std::vector<int> refClassIds{1, 16, 7};
|
||||
std::vector<float> refScores{0.9510f, 0.9454f, 0.8404f};
|
||||
|
||||
std::vector<Rect2d> refBoxes{
|
||||
Rect2d(105.5014, 112.8838, 472.9274, 350.0603),
|
||||
Rect2d(109.8231, 185.7994, 258.5916, 452.9302),
|
||||
Rect2d(388.5018, 62.1034, 576.6399, 143.3986)
|
||||
};
|
||||
|
||||
Image2BlobParams imgParams(
|
||||
Scalar::all(1 / 255.0),
|
||||
targetSize,
|
||||
Scalar::all(0),
|
||||
true,
|
||||
CV_32F,
|
||||
DNN_LAYOUT_NCHW,
|
||||
DNN_PMODE_LETTERBOX,
|
||||
Scalar::all(114)
|
||||
);
|
||||
|
||||
testYOLO(
|
||||
weightPath, refClassIds, refScores, refBoxes,
|
||||
imgParams, conf_threshold, iou_threshold,
|
||||
1.0e-4, 1.0e-4, "yolov10");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_nets, YOLOv9)
|
||||
{
|
||||
|
||||
std::string weightPath = _tf("models/yolov9t.onnx", false);
|
||||
|
||||
Size targetSize{640, 480};
|
||||
float conf_threshold = 0.50;
|
||||
float iou_threshold = 0.50;
|
||||
|
||||
std::vector<int> refClassIds{1, 16, 2}; // wrong class mapping for yolov9
|
||||
std::vector<float> refScores{0.959274f, 0.901125, 0.559396f};
|
||||
|
||||
std::vector<Rect2d> refBoxes{
|
||||
Rect2d(106.255, 107.927, 472.497, 350.309),
|
||||
Rect2d(108.633, 185.256, 259.287, 450.672),
|
||||
Rect2d(390.701, 62.1454, 576.928, 141.795)
|
||||
};
|
||||
|
||||
Image2BlobParams imgParams(
|
||||
Scalar::all(1 / 255.0),
|
||||
targetSize,
|
||||
Scalar::all(0),
|
||||
true,
|
||||
CV_32F,
|
||||
DNN_LAYOUT_NCHW,
|
||||
DNN_PMODE_LETTERBOX,
|
||||
Scalar::all(114)
|
||||
);
|
||||
|
||||
testYOLO(
|
||||
weightPath, refClassIds, refScores, refBoxes,
|
||||
imgParams, conf_threshold, iou_threshold,
|
||||
1.0e-4, 1.0e-4, "yolov9");
|
||||
}
|
||||
TEST_P(Test_ONNX_nets, YOLOX)
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG);
|
||||
|
||||
Reference in New Issue
Block a user