深度学习框架YOLOv3的C++调用

深度学习框架YOLOv3的C++调用

  • 深度学习框架YOLOv3的C++调用
    • (1)tensorflow版本的YOLOv3的C++调用(失败)
    • (2)darknet版本的YOLOv3的C++调用一(失败)
    • (3)darknet版本的YOLOv3的C++调用二(成功)

深度学习框架YOLOv3的C++调用

因为项目需要,我需要用C++调用YOLOv3来进行物体检测,本文记录了我尝试的几种调用方法,可能都有些旁门左道的感觉,大佬们不要见笑哈。

(1)tensorflow版本的YOLOv3的C++调用(失败)

首先按照下面步骤把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节点实现的,不过直接调用这条路是肯定也是走得通的。


(2)darknet版本的YOLOv3的C++调用一(失败)

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这个弯呢?于是就有了最后一种成功的方法


(3)darknet版本的YOLOv3的C++调用二(成功)

这个方法我从头开始讲,我的电脑的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& 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 Result)
    {
        for (vector::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 
#include 
#include 
#include 
#include 
#include 
#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& vDetectResults);
        void DrawResult( cv::Mat &Image, vector 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 

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++调用应该还是通过第一种方法实现。

你可能感兴趣的:(深度学习)