Darknet-Yolo的C++引用的深坑

简单列举一下,自己写c++化的yolo时候遇到的坑吧。

1.CPU与GPU的速度问题

实测用yolo官方提供的detect方法,i7-6850k的CPU,一张图花了10s~15s。

而双1080ti的GPU,只不到跑了0.01s,速度提升千倍。

2.libdarknet.a的C++引用问题

之所以选用yolo,就是因为依赖的环境少,之前使用tensorflow去实现yolo的object detection功能,遇到了bazel编译的问题,尝试了几个版本之后发现编译的问题过于复杂,不适合继续投入下去了,于是改用了yolo。

查阅了不少资料之后,发现yolo编译之后自带.so文件和.a文件,于是计划直接引用作为库,这样就可以省去不少功夫,但是代价是必须看一遍yolo的源码,darknet.h文件中的几个核心函数。

GPU编译版本的yolo,libdarknet.a文件直接引用会报错(getCUDAservice出错),具体原因不明,可能是我的主程序里没有引用CUDA?

3.改为引用libdarknet.so后的坑

一开始是参考的detector.c中test_detector的用法,打算通过network_detect方法来处理,核心源码部分如下:


        float *X = sized.data;
        time=what_time_is_it_now();
        network_predict(net, X);
        printf("%s: Predicted in %f seconds.\n", input, what_time_is_it_now()-time);
        get_region_boxes(l, im.w, im.h, net->w, net->h, thresh, probs, boxes, masks, 0, 0, hier_thresh, 1);
        //if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms);
        if (nms) do_nms_sort(boxes, probs, l.w*l.h*l.n, l.classes, nms);
        draw_detections(im, l.w*l.h*l.n, thresh, boxes, probs, masks, names, alphabet, l.classes);

sized是缩放后符合模型最后一层的image。

l.w*l.h*l.n相当于是种类数(个人理解),l是模型的最后一层layer,这里就遇到了一个问题。

在使用libdarknet.so引入的时候,发现layer的 w,h,n都是为0!

于是输出了一下前后几层的w,h,n看看,发现不是为零的,但是数据也很奇怪,不是我要的准确数据(默认:19*119*5)

于是只好作罢

解决方法:

后来参考了官方代码里的darknet.py,才考这个detect函数就可以很好的看出用法了

def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45):

    im = load_image(image, 0, 0)
    boxes = make_boxes(net)
    probs = make_probs(net)
    num =   num_boxes(net)
    network_detect(net, im, thresh, hier_thresh, nms, boxes, probs)
    res = []
    for j in range(num):
        for i in range(meta.classes):
            if probs[j][i] > 0:
                res.append((meta.names[i], probs[j][i], (boxes[j].x, boxes[j].y, boxes[j].w, boxes[j].h)))
    res = sorted(res, key=lambda x: -x[1])
    free_image(im)
    free_ptrs(cast(probs, POINTER(c_void_p)), num)
    return res

boxes是存储检测到的物体的对应边框的

probes用来存储所有classes的可能性(猜测)

num是有多少个classes(猜测)

然后通过

network_detect()

方法直接传递指针赋值就可以了,但是还是有几点需要注意的,

make_probs()

不知道为什么这个玩意darknet.h中没有,所以我直接用的malloc做的一个指针。

     float **probs = (float **)malloc(num*sizeof(float *));

幸好也能用。

核心部分也就这些代码了,小问题比如源代码的clloc缓存malloc还有小的编译问题,随便看看就搞定了。

另外,opencv的mat或者ipl转换成yolo里用的image格式,我是抄的一个wrapper的代码,这里也贴上好了。

      CV_Assert(RefImg.depth() == CV_8U);
  
      int h = RefImg.rows;
      int w = RefImg.cols;
      int channels = RefImg.channels();
      im = make_image(w, h, 3);
      int count = 0;
  switch(channels){
    case 1:{
      cv::MatIterator_ it, end;
      for (it = RefImg.begin(), end = RefImg.end(); it != end; ++it){
        im.data[count] = im.data[w*h + count] = im.data[w*h*2 + count] = (float)(*it)/255.0;
        
        ++count;
      }
      break;
    }
      
    case 3:{
      cv::MatIterator_ it, end;
      for (it = RefImg.begin(), end = RefImg.end(); it != end; ++it){
        im.data[count] = (float)(*it)[2]/255.0;
        im.data[w*h + count] = (float)(*it)[1]/255.0;
        im.data[w*h*2 + count] = (float)(*it)[0]/255.0;
        
        ++count;
      }
      break;
    }
      
    default:
      printf("Channel number not supported.\n");
      break;
    }
    	// double printingSec =what_time_is_it_now()-timess;
    	// printf("Prepare seconds: %lf ;\n",printingSec);


    //  im = load_image_color("data/sample.jpg",0,0);

      sized = letterbox_image(im, net->w, net->h);

这里面ref是采集到的Mat格式的数据,怎么改都可以。

那,就这些了。

问题记录

1.驱动问题导致:cuda version insufiicient。

        更新驱动sudo apt-get install nvidia-375(先ppa)

2.检测不到任何东西

        首先查看图象是否输入,接着把yolo的makefile里的cudnn=1去掉,只有gpu和opencv,编译用的是nvcc而不是cudnn。

3.检测出来的高度边框不对

        不要传入sized后的图片,因为network_predict_image()里面还有一次letter_image的操作,所以不需要提前操作了。

你可能感兴趣的:(人工智能)