主要讲述如何把DrakNet框架下支持的YOLO系列模型通过OpenVINO模型优化器与推断引擎实现对YOLO网络模型的加速执行。完整实现YOLO模型在OpenVINO上运行可以分为两个部分
模型转换
首先需要把YOLO网络模型通过模型优化器(MO)转为中间层输出IR(xml+bin),这个过程不是很友好,原因在于openvino本身不支持darknet网络,所以只有先把YOLOv3转换为tensorflow支持的PB文件,下载YOLOv3-tiny权重与配置文件
https://pjreddie.com/media/files/yolov3-tiny.weights
https://github.com/pjreddie/darknet/blob/master/cfg/yolov3-tiny.cfg
然后使用脚本把darknet的YOLO模型转换为tensorflow模型然后再转为IR的工具下载地址如下:
https://github.com/feng1sun/YOLO-OpenVINO
运行脚本
dump.py
--class_names ../common/coco.names \
--weights_file <yolov3.weights_paht> \
--size <302 or 416 or 608>
执行如下:
转换为IR
mo_tf.py
--input_model /path/to/yolo_v3.pb
--output_dir <OUTPUT_PATH>
--tensorflow_use_custom_operations_config $MO_ROOT/extensions/front/tf/yolo_v3.json
--batch 1
运行结果如下:
IE加速执行YOLOv3
转换好的模型是我们就可以通过SDK进行加载,生成网络,然后使用它实现基于YOLO的目标检测。这部分的代码前面部分跟SSD目标检测的类似,可以参考前面OpenVINO系列文章内容与源代码,这里主要说明一下不一样的地方,YOLOv3的输出层有多个,在不同分辨率上实现对象检测,对于同一个对象可能有多个BOX重叠,这个时候必须通过计算并交比来实现非最大抑制,计算并交比的代码如下:
double IntersectionOverUnion(const DetectionObject &box_1,
const DetectionObject &box_2) {
double width_of_overlap_area = fmin(box_1.xmax, box_2.xmax) - fmax(box_1.xmin, box_2.xmin);
double height_of_overlap_area = fmin(box_1.ymax, box_2.ymax) - fmax(box_1.ymin, box_2.ymin);
double area_of_overlap;
if (width_of_overlap_area < 0 || height_of_overlap_area < 0)
area_of_overlap = 0;
else
area_of_overlap = width_of_overlap_area * height_of_overlap_area;
double box_1_area = (box_1.ymax - box_1.ymin) * (box_1.xmax - box_1.xmin);
double box_2_area = (box_2.ymax - box_2.ymin) * (box_2.xmax - box_2.xmin);
double area_of_union = box_1_area + box_2_area - area_of_overlap;
return area_of_overlap / area_of_union;
}
对每个对象输出一个BOX,所以YOLOv3的输出层,需要做多个输出层结果合并然后NMS操作,YOLOv3的三个输出层如下:
此外YOLO数层格式跟SSD与RCNN系列输出格式不同,是基于YoloRegion实现的,解析该结构的代码如下:
for (int i = 0; i < side_square; ++i) {
int row = i / side;
int col = i % side;
for (int n = 0; n < num; ++n) {
int obj_index = EntryIndex(side, coords, classes, n * side * side + i, coords);
int box_index = EntryIndex(side, coords, classes, n * side * side + i, 0);
float scale = output_blob[obj_index];
if (scale < threshold) continue;
double x = (col + output_blob[box_index + 0 * side_square]) / side * resized_im_w;
double y = (row + output_blob[box_index + 1 * side_square]) / side * resized_im_h;
double height = std::exp(output_blob[box_index + 3 * side_square]) * anchors[anchor_offset + 2 * n + 1];
double width = std::exp(output_blob[box_index + 2 * side_square]) * anchors[anchor_offset + 2 * n];
for (int j = 0; j < classes; ++j) {
int class_index = EntryIndex(side, coords, classes, n * side_square + i, coords + 1 + j);
float prob = scale * output_blob[class_index];
if (prob < threshold)
continue;
DetectionObject obj(x, y, height, width, j, prob,
static_cast<float>(original_im_h) / static_cast<float>(resized_im_h),
static_cast<float>(original_im_w) / static_cast<float>(resized_im_w));
objects.push_back(obj);
}
}
}
最终的输出Output的解析代码如下:
// 处理输出结果
std::vector<DetectionObject> objects;
printf(" \n");
for (auto &output : output_info) {
auto output_name = output.first;
printf("Output Layer Name : %s \n", output_name.c_str());
CNNLayerPtr layer = network_reader.getNetwork().getLayerByName(output_name.c_str());
Blob::Ptr blob = infer_request.GetBlob(output_name);
// 解析输出层YOLO-Region
ParseYOLOV3Output(blob, layer, IH, IW, src.rows, src.cols, 0.5, objects);
}
printf(" \n");
// 非最大抑制
std::sort(objects.begin(), objects.end());
for (int i = 0; i < objects.size(); ++i) {
if (objects[i].confidence == 0)
continue;
for (int j = i + 1; j < objects.size(); ++j)
if (IntersectionOverUnion(objects[i], objects[j]) >= 0.45)
objects[j].confidence = 0;
}
// 绘制输出结果
for (auto &object : objects) {
if (object.confidence < 0.5)
continue;
auto label = object.class_id;
float confidence = object.confidence;
if (confidence > 0.5) {
std::ostringstream conf;
conf << ":" << std::fixed << std::setprecision(3) << confidence;
cv::Point2f p1 = cv::Point2f(object.xmin, object.ymin);
cv::Point2f p2 = cv::Point2f(object.xmax, object.ymax);
cv::rectangle(src, p1, p2, cv::Scalar(255, 0, 255), 1, 8);
}
}
imshow("OpenVINO-YOLOv3-Demo", src);
运行结果如下:
从此以后YOLO与Darknet网络通过OpenVINO加速技能可以get啦,记得点【在看】支持
为山者基于一篑之土,以成千丈之峭
凿井者起于三寸之坎,以就万仞之深