系列 | OpenVINO视觉加速库使用六

主要讲述如何把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啦,记得点【在看】支持

为山者基于一篑之土,以成千丈之峭

凿井者起于三寸之坎,以就万仞之深

原文发布于微信公众号 - OpenCV学堂(CVSCHOOL)

原文发表时间:2019-04-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券