赞
踩
因为项目需要,我需要用C++调用YOLOv3来进行物体检测,本文记录了我尝试的几种调用方法,可能都有些旁门左道的感觉,大佬们不要见笑哈。
首先按照下面步骤把tensorflow版本的YOLOv3跑起来
(1)下载项目代码
git clone https://github.com/qqwweee/keras-yolo3.git
(2)下载完成后进到项目目录里:
cd keras-yolo3
(3)YOLO官网下载weights文件或者执行:
wget https://pjreddie.com/media/files/yolov3.weights
(4)转换YOLO的weights文件格式为Keras的格式:
python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5
(5) 缺什么装什么,其中注意:ImportError: No module named PIL 错误 的解决方法:
pip install Pillow
(6)进行测试
测试图片:
python yolo_video.py --image --input ''
测试视频:
python yolo_video.py --input videos/traffic.mp4 --output videos/traffic_p.mp4
下边的命令不保存视频:
python yolo_video.py --input videos/traffic.mp4
启动摄像头
python yolo_video.py --input /dev/video0
前面都是成功的,然后我的思路是先用python写一个调用上述YOLOv3的接口,然后用通过C++调用Python函数的方式滴调用这个接口,具体代码就不贴了,实现在 我的GIthub 里面,反正是不好使的,会遇到如下的问题:
‘’’ File “/home/leo/anaconda2/envs/yolo/lib/python3.5/threading.py” assert tlock.locked() ‘’’
感觉应该是c++调用anaconda里面的python3.5或者tensorflow的问题。因为除了YOLOv3这个框架,还有那么多框架是基于tensorflow实现的,之前实现过是通过ROS节点实现的,不过直接调用这条路是肯定也是走得通的。
darknet是YOLO的作者基于C写的一个深度学习框架(牛逼!),通过python调用C编译生成的动态库(.so文件),我的思路是还是通过C++调用python接口,代码同样在 我的GIthub 里面,然后惨痛经历如下:
(1)首先我尝试了用c++给python传mat数据 失败!因为darknet压根就没有提供mat的数据接口,好坑啊,为什么!
(2)然后我尝试了用c++给python传一个float的指针,因为image的data数据就是float 失败!python的拓展接口里面没有float*,没法直接传,因此得分装成结构体再强转,太麻烦,放弃吧
(2)最后我尝试了修改darknet的接口,希望提供一个mat_to_image的接口,但是又遇到了c调用c++接口的namespace问题,刚刚好我的电脑装的有事3.4.1版本的opencv,这一版opencv里面提供了c的接口,但是却不能用c调用,3.4.0的好像就可以,哇,自己被自己坑到了
后来我幡然醒悟,.so文件不是可以直接通过C++调用吗,为啥我要绕python这个弯呢?于是就有了最后一种成功的方法
这个方法我从头开始讲,我的电脑的GPU是750Ti的(比较渣),下面的配置我都是按照我的电脑配置的,首先你要装好cuda以及opencv,我装的是cuda8.0和opencv3.4.1,接下来就可以按照下面步骤进行编译了:
(1)首先下载YOLOv3
git clone https://github.com/pjreddie/darknet
(2)下载权重
wget https://pjreddie.com/media/files/yolov3.weights
(3)打开yolo.clf文件,按照如下修改对应代码,修改了batch、subdivisions、width、height(width,height越大精度会越高,但计算量也会越大,这个取决于你的GPU)
[net]
# Testing
batch=1
subdivisions=1
# Training
#batch=64
#subdivisions=16
width=416
height=416
(4)打开makefile文件找到对应代码进行如下修改
GPU=1
CUDNN=1
OPENCV=1
OPENMP=0
DEBUG=0
...
ARCH= -gencode arch=compute_50,code=sm_50 \
...
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda-8.0/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda-8.0/lib64 -lcuda -lcudart -lcublas -lcurand
endif
...
NVCC=/usr/local/cuda-8.0/bin/nvcc
这里由于两点要注意
1)下面这个配置是根据你的GPU的算力确定的,算力越高对应数字越大,具体的GPU的算力可以再英伟达官网查到的
ARCH= -gencode arch=compute_50,code=sm_50 \
2)如果你没有把opencv安装在默认路径可能会遇到找不到opencv各种文件的问题,例如我之前只装了ROS Kinetic,我希望用ROS Kinetic自带的opencv编译文件,然后就倒腾了下makefile的写法,进行如下修改链接到opencv即可
ifeq ($(OPENCV), 1)
COMMON+= -DOPENCV -I/opt/ros/kinetic/include/opencv-3.3.1-dev
CFLAGS+= -DOPENCV -I/opt/ros/kinetic/include/opencv-3.3.1-dev
LDFLAGS+= -L/opt/ros/kinetic/lib/x86_64-linux-gnu -lopencv_core3 -lopencv_highgui3 -lopencv_videoio3 -lopencv_imgcodecs3
COMMON+= -I/opt/ros/kinetic/include/opencv-3.3.1-dev
endif
其实思路和cmakelist是差不多的
COMMON+= 后面加的是头文件
LDFLAGS+= 后面加的lib库, -L指的路径, -l指的lib文件, 然后libopencv_core3.so链接进来应该改成-lopencv_core3,就这样
(5)建立你的工程,把 libdarnet.so文件,darket.h,yolov3.cfg,coco.names,coco.data放到你的工程,然后写个类把它调用起来就好了,下面一部分代码是我从工程里摘抄出来的,能体现如何是调用接口的,但是并不能直接运行起来哈,需要进行一部分修改
Detecting.cpp
#include "Detecting.h" namespace YOLO { Detecting::Detecting() { string ConfigPath = "/home/leo/Desktop/sematic_slam_project/src/sematic_slam/YOLO_V3/config/yolov3.cfg"; string WeightsPath = "/home/leo/Desktop/Data/yolov3.weights"; string MetaDataPath = "/home/leo/Desktop/sematic_slam_project/src/sematic_slam/YOLO_V3/config/coco.data"; mpNetwork = load_network((char *) ConfigPath.data(), (char *) WeightsPath.data(), 0); mData = get_metadata((char *) MetaDataPath.data()); mpTransMethod = new TransMethod; } void Detecting::Detect(cv::Mat Frame, vector<DetectResult>& vDetectResults) { vDetectResults.clear(); image Image = mpTransMethod->MattoImage(Frame);//讲Mat数据转成Image类型 //下面的检测过程是仿照python接口写的,还没有太弄明白是怎么回事,具体可能需要花时间看paper了,先把框架搭起来吧 int *pCount = new int(0); network_predict_image(mpNetwork, Image); detection *pDetection = get_network_boxes(mpNetwork, Image.w, Image.h, 0.5, 0.5, nullptr, 0, pCount);//第一步:get_network_boxes do_nms_obj(pDetection, *pCount, mData.classes, 0.45);//第二步:do_nms_obj //获取检测结果 for (size_t j = 0; j < *pCount; j++) { for (size_t i = 0; i < mData.classes; i++) { if (pDetection[j].prob[i] > 0) { DetectResult Result; Result.mName = mData.names[i]; Result.mConfidence = pDetection[j].prob[i]; Result.mTop = (pDetection[j].bbox.y - pDetection[j].bbox.h / 2); Result.mBottom = (pDetection[j].bbox.y + pDetection[j].bbox.h / 2); Result.mLeft = (pDetection[j].bbox.x - pDetection[j].bbox.w / 2); Result.mRight = (pDetection[j].bbox.x + pDetection[j].bbox.w / 2); vDetectResults.push_back(Result); } } } } void Detecting::DrawResult( cv::Mat &Image, vector<DetectResult> Result) { for (vector<DetectResult>::iterator it = Result.begin(); it != Result.end(); it++) { cv::Point2f PointA(it->mLeft, it->mTop); cv::Point2f PointB(it->mRight, it->mBottom); cv::rectangle(Image, PointA, PointB, cv::Scalar(5, 100, 255), 5); } } }
Detecting.h
// // Created by leo on 18-11-13. // #ifndef PROJECT_DETECTING_H #define PROJECT_DETECTING_H #include <string> #include <YOLO_V3/include/darknet.h> #include <opencv2/opencv.hpp> #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <condition_variable> #include "DetectResult.h" #include "ORB_SLAM2/include/Tracking.h" using namespace std; namespace YOLO { class TransMethod; class Tracking; class Detecting { public: Detecting(); void Detect(cv::Mat Frame, vector<DetectResult>& vDetectResults); void DrawResult( cv::Mat &Image, vector<DetectResult> Result); private: network *mpNetwork; metadata mData; TransMethod *mpTransMethod; }; class TransMethod { public: image MattoImage(cv::Mat m) { IplImage ipl = m; image im = IpltoImage(&ipl); rgbgr_image(im); return im; } private: image IpltoImage(IplImage *src) { int h = src->height; int w = src->width; int c = src->nChannels; image im = make_image(w, h, c); unsigned char *data = (unsigned char *) src->imageData; int step = src->widthStep; int i, j, k; for (i = 0; i < h; ++i) { for (k = 0; k < c; ++k) { for (j = 0; j < w; ++j) { im.data[k * w * h + i * w + j] = data[i * step + j * c + k] / 255.; } } } return im; } }; } #endif //PROJECT_DETECTING_H
DetectResult.h
// // Created by leo on 18-11-20. // #ifndef PROJECT_DETECTRESULT_H #define PROJECT_DETECTRESULT_H #include <string> using namespace std; //把这个类单独放一个h文件是因为Frame类的编译链接问题 class DetectResult { public: string mName; float mConfidence; float mTop; float mBottom; float mLeft; float mRight; bool mbGoodFlag = false;//不是好的检测结果 }; #endif //PROJECT_DETECTRESULT_H
这部分代码其实是我做语义SLAM中间调用YOLOv3的一部分,参考代码在 我的Github中,通过上面的接口就能调用起来YOLOv3了,这种方法主要是因为YOLOv3是基于c实现的,其他的深度学习框架的C++调用应该还是通过第一种方法实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。