简单列举一下,自己写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的操作,所以不需要提前操作了。