OpenCV+darknet+yolov3实时目标检测

Posted by

OpenCV4入门系列教程:yolov3目标实时检测中我们使用OpenCV的DNN库解析yolov3的weight权重文件。

本文使用OpenCV获取视频数据,其他部分有darknet库完成。

darknet库GitHub地址:darknet

darknet主页:darknet

说明:因为部分人对darknet使用领域的无限扩张,为了爱与和平,原作者已不在更新,现在的所有内容由他人维护。

在darknet源码中由用于编译的Makefile文件,调整里面的参数用于使能openmpi/cuda等等。

编译结果为:

darknet.h
libdarknet.a
libdarknet.so

就是头文件和动态库。

首先新建一个纯c++的Qt项目(cmake也可以,我比较喜欢Qt)

主要是用Qt来链接动态库。

开发流程为:

flow

获取标签

预测结果的类别是数字,我们需要将其按照对应关系进行解码。

std::vector getClassesName(char path[]){
    std::vector names;
    if(!path){
        return names;
    }
    std::ifstream readIn(path);
    std::string str;
    while(getline(readIn,str)){
        names.push_back(str);
    }
    return names;
}

读取标签列表至vector中,这样就可以通过类似数组(names[0]=person)的方式直接访问数据值。

对象初始化

本程序主要用到两个对象:

network *net = load_network(cfgfile,weightfile,0);
set_batch_network(net,1);

cv::VideoCapture cap(0);

net用于处理网络权重,cap用于获取摄像头的数据。

图像格式转换

获取到图像之后,格式为cv::Mat,而net使用的图像格式为image,此为darknet自定义的格式,需要转换一下。当然,图像处理完显示的时候又要变为Mat,否则也不需要使用OpenCV了。

image mat2image(const cv::Mat &mat){
    cv::Mat dst;
    cv::cvtColor(mat, dst, cv::COLOR_RGB2BGR);

    int w = mat.cols;
    int h = mat.rows;
    int c = mat.channels();
    image im = make_image(w, h, c);
    unsigned char * imageData = (unsigned char *)dst.data;
    int step = dst.step;
    for (int y = 0; y < h; ++y) {
        for (int k = 0; k < c; ++k) {
            for (int x = 0; x < w; ++x) {
                im.data[k*w*h + y*w + x] = imageData[y*step + x*c + k] / 255.0f;
            }
        }
    }
    return im;
}

cv::Mat image2Mat(image im){
    image copy = copy_image(im);
    constrain_image(copy);
    if(im.c==3) rgbgr_image(copy);

    int x,y,c;
    cv::Mat m;
    switch(im.c){
    case 3:
        m = cv::Mat(im.w,im.h,CV_8UC3);
        break;
    case 4:
        m=cv::Mat(im.w,im.h,CV_8UC4);
        break;
    }

    int step = m.step;
    for(y=0;y

如果认真学习OpenCV的话应该不难理解。

获取结果

获取原数据,讲Mat处理成image,预测图像

image in = mat2image(frame);//格式转换
image in_s = letterbox_image(in,net->w,net->h);//讲图像数据转换为网络需要的尺寸
layer l = net->layers[net->n-1];//预测层

int nboxes=0;//可能的结果数量
float *X=in_s.data;//需要预测的图像数据
time=what_time_is_it_now();
network_predict(net,X);//预测中
//以下是方便人类阅读的部分
std::ostringstream str;
str<< "Prediction spent "<

结果处理

比如说在一次检测中我们获取了10个可能的检测结果,那么我们要对每个结果进行处理。

        for(i=0;ithresh){
                    //如果预测的可能性大于我们设置的阈值
                    box b = dets[i].bbox;
                    int left  = (b.x-b.w/2.)*in.w;
                    int right = (b.x+b.w/2.)*in.w;
                    int top   = (b.y-b.h/2.)*in.h;
                    int bot   = (b.y+b.h/2.)*in.h;

                    if(left < 0) left = 0;
                    if(right > in.w-1) right = in.w-1;
                    if(top < 0) top = 0;
                    if(bot > in.h-1) bot = in.h-1;

                    //根据计算的left,right,top,bot绘制矩形框
                    cv::rectangle(frame,cv::Rect(left,top,right-left,bot-top),cv::Scalar(255,255,0),2,cv::LINE_8,0);
                    //记录日志
                    std::ostringstream text;
                    text<

矩形框的计算过程来自官方的代码。

最终效果为:

result

权重/注释/完整代码下载:detect

说明:只提供tiny权重用于测试,如有需要,可以到darknet下载权重和配置文件或者自己训练。

赞赏

微信赞赏支付宝赞赏

Leave a Reply

邮箱地址不会被公开。