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

windows上yolov5 opencv dnn c++部署 cuda加速,第1张

目录

前置环境

前言

一,安装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解决。


        ①选择完版本后,选择网络好的话选择本地下载,网络不好的可以选择网络下载。


网络下载优点是包小,缺点是如果安装失败又得重新在安装时下载完整安装包。


         安装时,这一步不用选位置,,最后会消失的,好像是临时文件,不是安装文件。


后面的cuda toolkit才是安装文件。


(疑神疑鬼了好久,为什么cuda文件夹消失了,还重装了两次)

        

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

                        

         ②-1,VS报错的情况不要按照网上的别的教程取消勾选Visual Studio Intergration,然后添加文件到cuda路径。


我试过,这样的结果是在最后一步生成解决方案失败,然后全部重新来过。


我的解决办法是找最新cuda版本以适应我最新的VS2022版本,最后生成解决方案成功。


对于cuda版本有要求的,可降低VS版本。


             ·                                                               

                 

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

                 

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

        

        

        ④下载解压后将其中所有文件移到cuda安装目录中,我的路径为:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.5,license也移一下。


安装完成                                                

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


zip安装方式不用添加。


                        

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


二,重新编译opencv+opencv_contrib 1,cmake编译opencv+opencv_contrib

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


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


        

         已经装过opencv和opencv_contrib的朋友,需要删除之前的build文件夹,重新编译opencv和opnecv_contrib,也可参考前置环境安装。


出现下图则cuda和cudnn编译成功(选项框爆红再次configure即可)。


         

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左右。


        运行效果: 

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文件路径 --weights pt文件路径 --include onnx                                     
python export.py --weights yolov5s.pt --include onnx

        ①-1,(更新)随着对yolov5模型理解的加深,明白了之前为什么导出的onnx模型,使用不成功。


在我那一版yolov5中,他的模型结构还没有优化完全,output分支有三个,没有整合到一起。


而大佬源码处理的是整合到一起的onnx模型,也就是输出为[1.25200,85]的模型。


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

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

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


下载地址

        ②修改源码

        在8行修改为自己的分类txt文件路径。


19行修改为自己的模型文件路径,

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


 

         ②-2,132行修改输入流——路径:视频文件;0:电脑自带摄像头;1:usb摄像头;139行选择运行模型的设备为CPU还是GPU,CPU修改红框内容为0,GPU修改红框内容为1。


源码这句话是,获取ubuntu命令行运行可执行文件时手动输入cuda的信息,如终端./yolo_example cuda。


就会以GPU方式运行。


windows运行如果不修改,就会默认cpu加载模型。


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


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文件夹移到新建文件夹中。


         ②然后将这个文件夹拷贝到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帧左右

         ②yolov5n运行,cpu有1.5帧左右,cuda模式有9.6帧左右。


不得不说,yolov5n真的强。


四,总结        

        从第一眼看到大佬源码的惊喜,再到加载自己模型始终成功不了的绝望,再到看懂大佬源码进行修改,一步一步学到了很多。


不过总感觉深度学习入门,还是配置环境问题,一个模型的训练,到部署需要很多环境才能实现。


进一步,就是对算法源码的理解,对模型的理解。


目前对于博主来说,关于yolov5这种算法的环境部署大致弄懂。


但对模型结构理解还不是很通透,所以使用别人的源码,出了很多问题,不过还好解决问题过程,也是学习的过程。


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


问题记录 ①export导出onnx文件报错

ONNX export failure: No module named 'onnx'

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

②opencv dnn加载cuda失败

        解决办法:配置cuda和cudnn

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

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

                                

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

        解决办法:忽略,不用管

⑤setUpNet DNN module was not built with CUDA backend;

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


解决办法链接

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

                               

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


⑦AttributeError: 'Upsample' object has no attribute 'recompute_scale_factor'

        解决办法:降低pytorch版本,pytorch版本<=1.10才行。


参考文章

深度学习GPU环境CUDA详细安装过程(简单快速有效) - 知乎

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/564356.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-06
下一篇 2022-04-06

发表评论

登录后才能评论

评论列表(0条)

保存