目录
前置环境
前言
一,安装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下载官网: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已经安装完成。
做了前置环境的,应该比较熟悉这一步,没有做过的可以看看,编译时需要加上cuda。
如果第一次编译没有加上cuda,或者版本对应不对会出现如下报错。
已经装过opencv和opencv_contrib的朋友,需要删除之前的build文件夹,重新编译opencv和opnecv_contrib,也可参考前置环境安装。出现下图则cuda和cudnn编译成功(选项框爆红再次configure即可)。
前面步骤没有出问题的话,这一步不会有问题,不过比较费时间,之前不加cuda生成需要几十分钟,加上cuda需要三四个小时。
源码内容:
#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左右。
①这一步,首先得有自己的模型,避免踩坑提前说一下,下载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贼高,非常适合移动端的部署。
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
不使用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
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
①yolov5s运行,cpu模式只有零点几帧,cuda模式运行稳定后有3.5帧左右
②yolov5n运行,cpu有1.5帧左右,cuda模式有9.6帧左右。不得不说,yolov5n真的强。
从第一眼看到大佬源码的惊喜,再到加载自己模型始终成功不了的绝望,再到看懂大佬源码进行修改,一步一步学到了很多。不过总感觉深度学习入门,还是配置环境问题,一个模型的训练,到部署需要很多环境才能实现。进一步,就是对算法源码的理解,对模型的理解。目前对于博主来说,关于yolov5这种算法的环境部署大致弄懂。但对模型结构理解还不是很通透,所以使用别人的源码,出了很多问题,不过还好解决问题过程,也是学习的过程。
关于实现github大佬源码有什么问题,欢迎在评论区留言讨论。
ONNX export failure: No module named 'onnx'
解决方法:在环境中下载onnx
解决办法:配置cuda和cudnn
解决办法:正确配置cudnn,在cmake编译时,能在报错框显示cudnn已找到
解决办法:忽略,不用管
解决办法:不懂原理,下载zlib添加环境变量解决。解决办法链接
解决办法:更换yolov5版本,换为yolov5-6.1版本训练导出,导出时注意自己的pytorch版本,pytorch版本<=1.10才行。
解决办法:降低pytorch版本,pytorch版本<=1.10才行。
参考文章
深度学习GPU环境CUDA详细安装过程(简单快速有效) - 知乎