windows上yolov5 opencv dnn c++部署 cuda加速

目录

前置环境

前言

一,安装cuda,cudnn

二,重新编译opencv+opencv_contrib

1,cmake编译opencv+opencv_contrib

2,VS生成解决方案

三,VS运行opencv dnn加载onnx源码

1,加载大佬的onnx模型

2,加载自己的模型

3, jetson nano上运行

1)环境配置

2)g++方式编译

3)cmake方式编译

4)jetson nano运行结果

四,总结

问题记录

①export导出onnx文件报错

②opencv dnn加载cuda失败

③DNN: CUDA backend requires cuDNN. Please resolve dependency or disableOPENCV_DNN_CUDA=OFF

④CONFIGURATION IS NOT SUPPORTED: validate setupvars script in installdirectory

⑤setUpNet DNN module was not built with CUDA backend;

⑥环境配好后加载自己模型失败

⑦AttributeError: ‘Upsample’ object has no attribute ‘recompute_scale_factor’

前置环境

windows安装配置opencv opencv_contrib_野马AS的博客-CSDN博客

前言

GitHub大佬opencv dnn加载onnx源码源码地址

大佬的源码是在Ubuntu上运行的,但是windows上opencv环境配好后也可直接运行。由于第一次装的时候,没有编译cuda,所以只能运行cpu版本,只有5~6fps。且只能运行大佬的onnx模型,自己的yolov5模型用不了。记录一下cuda加速,opencv环境配置,以及opencv dnn调用自己yolov5训练导出生成的onnx。

一,安装cuda,cudnn

cuda下载官网:CUDA Toolkit Archive | NVIDIA Developer

cudnn下载官网:cuDNN Archive | NVIDIA Developer

前排提醒:下载之前注意一下cuda和cudnn版本对应,不对应后面会出找不到cudnn的问题。还要注意一下VS和cuda版本,cuda必须是在vs之后出的版本,否则安装cuda会提示检测不到VS,导致VS最后生成解决方案失败,生成不了opencv的dll和lib文件。不过博主由于VS和cuda不对应,我选择的是能对应的cuda11.6.1,cudnn没有出for cuda11.6,就用的最新的11.5,最后报错下载zlip解决。

①选择完版本后,选择网络好的话选择本地下载,网络不好的可以选择网络下载。网络下载优点是包小,缺点是如果安装失败又得重新在安装时下载完整安装包。

windows上yolov5 opencv dnn c++部署 cuda加速

安装时,这一步不用选位置,,最后会消失的,好像是临时文件,不是安装文件。后面的cuda toolkit才是安装文件。(疑神疑鬼了好久,为什么cuda文件夹消失了,还重装了两次)

windows上yolov5 opencv dnn c++部署 cuda加速

②cuda选择精简安装,如果不报VS的错则直接选择下一步到安装完成,开始cudann安装

windows上yolov5 opencv dnn c++部署 cuda加速

②-1,VS报错的情况 不要按照网上的别的教程取消勾选Visual Studio Intergration,然后添加文件到cuda路径。我试过,这样的结果是在最后一步生成解决方案失败,然后全部重新来过。我的解决办法是找最新cuda版本以适应我最新的VS2022版本,最后生成解决方案成功。对于cuda版本有要求的,可降低VS版本。 ·

windows上yolov5 opencv dnn c++部署 cuda加速

②-2,选择好合适的版本,点击精简安装直接出现下图,则cuda安装,VS的问题就解决了

windows上yolov5 opencv dnn c++部署 cuda加速

③安装cuda nn注意对应版本,下载时注意选择 zip安装,不要exe安装,cmake编译时找不到cudnn(血坑!!!)

windows上yolov5 opencv dnn c++部署 cuda加速

windows上yolov5 opencv dnn c++部署 cuda加速

windows上yolov5 opencv dnn c++部署 cuda加速

(zip安装好了的朋友跳过这一步)博主后知后觉,重试了几遍,对安装cuda和cudnn文件夹熟悉之后,发现cudnn的exe方式安装的文件在路径C:\Program Files\NVIDIA\CUDNN\v8.3中,如果用的这种安装方式的朋友要做 环境变量中添加该路径,则就能检测到cudnn。zip安装方式不用添加。

windows上yolov5 opencv dnn c++部署 cuda加速

⑥按照以上步骤,没有报错弄完之后,以博主五六次重试的经验担保,cuda和cudnn已经安装完成。

二,重新编译opencv+opencv_contrib

1,cmake编译opencv+opencv_contrib

做了前置环境的,应该比较熟悉这一步,没有做过的可以看看,编译时需要加上cuda。

如果第一次编译没有加上cuda,或者版本对应不对会出现如下报错。

windows上yolov5 opencv dnn c++部署 cuda加速

已经装过opencv和opencv_contrib的朋友,需要删除之前的build文件夹,重新编译opencv和opnecv_contrib,也可参考前置环境安装。出现下图则cuda和cudnn编译成功(选项框爆红再次configure即可)。

windows上yolov5 opencv dnn c++部署 cuda加速

2,VS生成解决方案

前面步骤没有出问题的话,这一步不会有问题,不过比较费时间,之前不加cuda生成需要几十分钟,加上cuda需要三四个小时。

三,VS运行opencv dnn加载onnx源码

1,加载大佬的onnx模型

源码内容:

#include

#include

std::vector load_class_list()
{
    std::vector class_list;
    std::ifstream ifs("config_files/classes.txt");
    std::string line;
    while (getline(ifs, line))
    {
        class_list.push_back(line);
    }
    return class_list;
}

void load_net(cv::dnn::Net &net, bool is_cuda)
{
    auto result = cv::dnn::readNet("config_files/yolov5s.onnx");
    if (is_cuda)
    {
        std::cout << "Attempty to use CUDA\n";
        result.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
        result.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
    }
    else
    {
        std::cout << "Running on CPU\n";
        result.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
        result.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
    }
    net = result;
}

const std::vector colors = {cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 0), cv::Scalar(0, 255, 255), cv::Scalar(255, 0, 0)};

const float INPUT_WIDTH = 640.0;
const float INPUT_HEIGHT = 640.0;
const float SCORE_THRESHOLD = 0.2;
const float NMS_THRESHOLD = 0.4;
const float CONFIDENCE_THRESHOLD = 0.4;

struct Detection
{
    int class_id;
    float confidence;
    cv::Rect box;
};

cv::Mat format_yolov5(const cv::Mat &source) {
    int col = source.cols;
    int row = source.rows;
    int _max = MAX(col, row);
    cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3);
    source.copyTo(result(cv::Rect(0, 0, col, row)));
    return result;
}

void detect(cv::Mat &image, cv::dnn::Net &net, std::vector &output, const std::vector &className) {
    cv::Mat blob;

    auto input_image = format_yolov5(image);

    cv::dnn::blobFromImage(input_image, blob, 1./255., cv::Size(INPUT_WIDTH, INPUT_HEIGHT), cv::Scalar(), true, false);
    net.setInput(blob);
    std::vector outputs;
    net.forward(outputs, net.getUnconnectedOutLayersNames());

    float x_factor = input_image.cols / INPUT_WIDTH;
    float y_factor = input_image.rows / INPUT_HEIGHT;

    float *data = (float *)outputs[0].data;

    const int dimensions = 85;
    const int rows = 25200;

    std::vector class_ids;
    std::vector confidences;
    std::vector boxes;

    for (int i = 0; i < rows; ++i) {

        float confidence = data[4];
        if (confidence >= CONFIDENCE_THRESHOLD) {

            float * classes_scores = data + 5;
            cv::Mat scores(1, className.size(), CV_32FC1, classes_scores);
            cv::Point class_id;
            double max_class_score;
            minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
            if (max_class_score > SCORE_THRESHOLD) {

                confidences.push_back(confidence);

                class_ids.push_back(class_id.x);

                float x = data[0];
                float y = data[1];
                float w = data[2];
                float h = data[3];
                int left = int((x - 0.5 * w) * x_factor);
                int top = int((y - 0.5 * h) * y_factor);
                int width = int(w * x_factor);
                int height = int(h * y_factor);
                boxes.push_back(cv::Rect(left, top, width, height));
            }

        }

        data += 85;

    }

    std::vector nms_result;
    cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, nms_result);
    for (int i = 0; i < nms_result.size(); i++) {
        int idx = nms_result[i];
        Detection result;
        result.class_id = class_ids[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx];
        output.push_back(result);
    }
}

int main(int argc, char **argv)
{

    std::vector class_list = load_class_list();

    cv::Mat frame;
    cv::VideoCapture capture("sample.mp4");
    if (!capture.isOpened())
    {
        std::cerr << "Error opening video file\n";
        return -1;
    }

    bool is_cuda = argc > 1 && strcmp(argv[1], "cuda") == 0;

    cv::dnn::Net net;
    load_net(net, is_cuda);

    auto start = std::chrono::high_resolution_clock::now();
    int frame_count = 0;
    float fps = -1;
    int total_frames = 0;

    while (true)
    {
        capture.read(frame);
        if (frame.empty())
        {
            std::cout << "End of stream\n";
            break;
        }

        std::vector output;
        detect(frame, net, output, class_list);

        frame_count++;
        total_frames++;

        int detections = output.size();

        for (int i = 0; i < detections; ++i)
        {

            auto detection = output[i];
            auto box = detection.box;
            auto classId = detection.class_id;
            const auto color = colors[classId % colors.size()];
            cv::rectangle(frame, box, color, 3);

            cv::rectangle(frame, cv::Point(box.x, box.y - 20), cv::Point(box.x + box.width, box.y), color, cv::FILLED);
            cv::putText(frame, class_list[classId].c_str(), cv::Point(box.x, box.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
        }

        if (frame_count >= 30)
        {

            auto end = std::chrono::high_resolution_clock::now();
            fps = frame_count * 1000.0 / std::chrono::duration_cast(end - start).count();

            frame_count = 0;
            start = std::chrono::high_resolution_clock::now();
        }

        if (fps > 0)
        {

            std::ostringstream fps_label;
            fps_label << std::fixed << std::setprecision(2);
            fps_label << "FPS: " << fps;
            std::string fps_label_str = fps_label.str();

            cv::putText(frame, fps_label_str.c_str(), cv::Point(10, 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2);
        }

        cv::imshow("output", frame);

        if (cv::waitKey(1) != -1)
        {
            capture.release();
            std::cout << "finished by user\n";
            break;
        }
    }

    std::cout << "Total frames: " << total_frames << "\n";

    return 0;
}

在配有opencv环境的VS项目下,大佬的源码可直接运行,如果上述步骤配置好,则会cuda加速GPU运行;没有配置好,则会cpu运行。GPU是CPU的三倍,感觉opencv的加速还是不够理想,运行起来,GPU并没有消耗太多,最高22fps,最低11fps,平均18fps左右。

windows上yolov5 opencv dnn c++部署 cuda加速

2,加载自己的模型

①这一步,首先得有自己的模型,避免踩坑提前说一下,下载yolov5 v6.1版本训练的模型导出才能使用。yolov5-6.1版本训练看这篇博客的前面部分。导出onnx模型,yolov5有export.py文件(导出onnx需要下载onnx包),可直接使用导出。yolov5 6.1需注意pytorch版本,我使用时导出onnx模型,还不支持pytorch 1.11,可conda创建一个较低的pytorch版本用于导出。

#python export&#x6587;&#x4EF6;&#x8DEF;&#x5F84; --weights pt&#x6587;&#x4EF6;&#x8DEF;&#x5F84; --include onnx
python export.py --weights yolov5s.pt --include onnx

windows上yolov5 opencv dnn c++部署 cuda加速

①-1,(更新)随着对yolov5模型理解的加深,明白了之前为什么导出的onnx模型,使用不成功。在我那一版yolov5中,他的模型结构还没有优化完全,output分支有三个,没有整合到一起。而大佬源码处理的是整合到一起的onnx模型,也就是输出为[1.25200,85]的模型。

原来的yolov5 5.0导出onnx模型结构:

windows上yolov5 opencv dnn c++部署 cuda加速

现在yolov5 6.1导出的onnx模型结构:

windows上yolov5 opencv dnn c++部署 cuda加速

①-2,查看模型结构软件Netron,大力推荐,后续进一步学习必备工具。下载地址

windows上yolov5 opencv dnn c++部署 cuda加速

②修改源码

8行修改为自己的分类txt文件路径。 19行修改为自己的模型文件路径,

windows上yolov5 opencv dnn c++部署 cuda加速

②-1, 110行修改分类,这个85=80 (COCO 数据集的类数) + 1 (confidence) + 4 (xywh),所以你的模型分类为2的话,那么这就改为data+=7。

windows上yolov5 opencv dnn c++部署 cuda加速

②-2, 132行修改输入流—— 路径:视频文件; 0:电脑自带摄像头; 1:usb摄像头; 139行选择运行模型的设备为CPU还是GPU, CPU修改红框内容为0GPU修改红框内容为1。源码这句话是,获取ubuntu命令行运行可执行文件时手动输入cuda的信息,如终端./yolo_example cuda。就会以GPU方式运行。 windows运行如果不修改,就会默认cpu加载模型。

windows上yolov5 opencv dnn c++部署 cuda加速

③点击运行即可,我使用的是yolov5n的模型,FPS贼高,非常适合移动端的部署。

windows上yolov5 opencv dnn c++部署 cuda加速

3, jetson nano上运行

1)环境配置

jetson的ubuntu系统镜像上面自带cuda,所以ubuntu安装cuda的过程就省略了(后续有机会安装,再更新一下)。openc和opencv_contrib的编译安装,由于raw网址的封禁,所以编译的时候往往会编译失败。建议在windows上编译成功了,再复制文件jetson nano上进行编译,这样由于在windows上配置的时候,缺的包已经补齐,所以可一次编译成功。

windows上编译参考前置环境安装.

①windows上编译成功后,新建opencv-4.5.5文件夹(4.5.5为版本信息,根据自己的版本命名),将能够成功编译的opencv中的source中所有文件,移到新建文件夹中。再将能够成功编译的opencv_contrib文件夹移到新建文件夹中。

windows上yolov5 opencv dnn c++部署 cuda加速

②然后将这个文件夹拷贝到jetson nano上,在这个文件夹中,新建build文件夹,新建build.sh,用执行build.sh文件的方式,可避免再次编译时重新输入参数的麻烦,也可以记录编译时选择的配置,以后可根据需求增删,重新编译

cd opencv-4.5.5 #打开opencv-4.5.5
mkdir build #新建build文件夹
cd build #打开build
touch build.sh #新建build.sh
chmod 777 build.sh #给build可执行权限,777:给文件所有权限
gedit build.sh #编辑build.sh

③在build.sh中添加cmake编译指令。

cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -DBUILD_PNG=ON \
    -DBUILD_TIFF=ON \
    -DBUILD_TBB=OFF \
    -DBUILD_JPEG=ON \
    -DBUILD_JASPER=OFF \
    -DBUILD_ZLIB=ON \
    -DBUILD_EXAMPLES=OFF \
    -DBUILD_opencv_java=OFF \
    -DBUILD_opencv_python2=OFF \
    -DBUILD_opencv_python3=ON \    #选择opencv3
    -DENABLE_PRECOMPILED_HEADERS=OFF \
    -DWITH_OPENCL=ON \
    -DWITH_OPENMP=OFF \
    -DWITH_FFMPEG=ON \
    -DWITH_GSTREAMER=ON \
    -DWITH_GSTREAMER_0_10=OFF \
    -DWITH_CUDA=ON \        #链接cuda
    -DWITH_GTK=ON \
    -DWITH_VTK=OFF \
    -DWITH_TBB=ON \
    -DWITH_1394=OFF \
    -DWITH_OPENEXR=OFF \
    -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda \
    -DCUDA_ARCH_BIN=5.3\        #根据自己显卡算力修改,jetson nano为5.3
    -DCUDA_ARCH_PTX="" \
    -DINSTALL_C_EXAMPLES=OFF \
    -DOPENCV_ENABLE_NONFREE=ON \
    -DINSTALL_TESTS=OFF \
    -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib-4.5.5/modules \  #根据自己的版本改
    -DCMAKE_INSTALL_PREFIX=/usr/ \
    ..

make -j4
sudo make install

④最后执行build

./build.sh

2)g++方式编译

不使用cuda

git clone https://github.com/doleron/yolov5-opencv-cpp-python.git
cd yolov5-opencv-cpp-python
g++ -O3 cpp/yolo.cpp -o yolo_example pkg-config --cflags --libs opencv4
./yolo_example

使用cuda( 且cuda编译成功)

git clone https://github.com/doleron/yolov5-opencv-cpp-python.git
cd yolov5-opencv-cpp-python
g++ -O3 cpp/yolo.cpp -o yolo_example pkg-config --cflags --libs opencv4
./yolo_example cuda

3)cmake方式编译

git clone https://github.com/doleron/yolov5-opencv-cpp-python.git
cd yolov5-opencv-cpp-python
touch CMakeLists.txt
gedit CMakeLists.txt

在CMakeLists.txt中添加如下内容

cmake_minimum_required(VERSION 2.8)

SET(CMAKE_CXX_STANDARD 11)

PROJECT(yolo_example)

find_package(OpenCV REQUIRED)

include_directories( ${OpenCV_INCLUDE_DIRS} )

add_executable(yolo_example cpp/yolo.cpp)

target_link_libraries(yolo_example ${OpenCV_LIBS})

然后cmake编译执行,生成可执行文件yolo_example

cmake ./
make

cpu运行

./yolo_example

cuda运行

./yolo_example cuda

4)jetson nano运行结果

①yolov5s运行,cpu模式只有零点几帧,cuda模式运行稳定后有3.5帧左右

windows上yolov5 opencv dnn c++部署 cuda加速

②yolov5n运行,cpu有1.5帧左右,cuda模式有9.6帧左右。不得不说,yolov5n真的强。

windows上yolov5 opencv dnn c++部署 cuda加速

四,总结

从第一眼看到大佬源码的惊喜,再到加载自己模型始终成功不了的绝望,再到看懂大佬源码进行修改,一步一步学到了很多。不过总感觉深度学习入门,还是配置环境问题,一个模型的训练,到部署需要很多环境才能实现。进一步,就是对算法源码的理解,对模型的理解。目前对于博主来说,关于yolov5这种算法的环境部署大致弄懂。但对模型结构理解还不是很通透,所以使用别人的源码,出了很多问题,不过还好解决问题过程,也是学习的过程。

关于实现github大佬源码有什么问题,欢迎在评论区留言讨论。

问题记录

①export导出onnx文件报错

ONNX export failure: No module named ‘onnx’

解决方法:在环境中下载onnx

windows上yolov5 opencv dnn c++部署 cuda加速

②opencv dnn加载cuda失败

解决办法:配置cuda和cudnn

③DNN: CUDA backend requires cuDNN. Please resolve dependency or disable OPENCV_DNN_CUDA=OFF

解决办法:正确配置cudnn,在cmake编译时,能在报错框显示cudnn已找到

windows上yolov5 opencv dnn c++部署 cuda加速

④CONFIGURATION IS NOT SUPPORTED: validate setupvars script in install directory

解决办法:忽略,不用管

⑤setUpNet DNN module was not built with CUDA backend;

解决办法:不懂原理,下载zlib添加环境变量解决。解决办法链接

⑥环境配好后加载自己模型失败

windows上yolov5 opencv dnn c++部署 cuda加速

解决办法:更换yolov5版本,换为yolov5-6.1版本训练导出,导出时注意自己的pytorch版本,pytorch版本

Original: https://blog.csdn.net/weixin_46596757/article/details/123409536
Author: 野马AS
Title: windows上yolov5 opencv dnn c++部署 cuda加速

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/637809/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球