点击上方↑↑↑“OpenCV学堂”关注我
详解mask-rcnn网络模型在OpenCV DNN调用的技术细节
Mask-RCNN架构
Mask-RCNN可以看成是在Faster-RCNN的基础上多出一个分支实现的实例分割网络二值化mask层输出,而且这个分支mask分割网络是全卷积网络,结构显示如下:
在分离出mask全卷积分支网络的时候有两种分支网络卷积架构可以使用,显示如下:
头部分别是ResNet C4与FPN作为基础网络部分。
模型输入与输出参数
Tensorflow的对象检测框架中提供了Mask-RCNN网络基于COCO的预训练模型,支持对其的迁移学习与自定义数据的对象实例分割。下载模型地址如下:
http://download.tensorflow.org/models/object_detection/mask_rcnn_inception_v2_coco_2018_01_28.tar.gz
生成OpenCV DNN模型可使用的描述文件,只有生成了描述文件之后才可以在OpenCV4 DNN模块中导入mask-rcnn模型,描述文件生成详细步骤与说明参见之前的文章:
干货 | tensorflow模型导出与OpenCV DNN中使用
模型的输入参数与格式(转换为blob输入数据时候的参数)
size:800x800 mean:0,0,0 scale: 1.0 rgb: true
模型的输出层与格式解析
- detection_out_final = [1, 1, N, 7]其中7列分别为: image_id 图像批次Id label类别id conf类别score (x_min, y_min)左上角坐标信息 (x_max, y_max)右下角坐标信息
- detection_masks = [100,C,15,15] 详解时候100跟N对应,C跟label对应表示类别信息 15x15表示mask的大小, 得分大于0.5表示对象像素 小于0.5表示非对象像素
模型调用
OpenCV4 DNN模型支持tensorflow对象检测框架模型的加载与推理使用,可以实现自定义的对象检测与实例分割模型迁移学习训练,导出模型的调用支持。
预训练COCO数据模型使用:
ROI区域的mask结果如下:
使用自定义数据,实现指针检测与实例分割得到的效果如下:
演示代码详细步骤如下:
加载模型
代码语言:javascript复制// 加载模型
Net net = readNetFromTensorflow(pb_model, pb_txt);
net.setPreferableBackend(DNN_BACKEND_OPENCV);
net.setPreferableTarget(DNN_TARGET_CPU);
获取输入数据
代码语言:javascript复制// 设置数据
Mat blobImage = blobFromImage(frame, 1.0, Size(800, 800), Scalar(0, 0, 0), true, false);
printf("blobImage width : %d, height: %dn", blobImage.size[3], blobImage.size[2]);
net.setInput(blobImage);
执行推理
代码语言:javascript复制// 推理
vector<String> out_names;
out_names.push_back("detection_out_final");
out_names.push_back("detection_masks");
vector<Mat> outs;
net.forward(outs, out_names);
Mat detection = outs[0];
int id = outs[1].size[0];
int numClasses = outs[1].size[1];
int mh = outs[1].size[2];
int mw = outs[1].size[3];
Mat masks = outs[1]; // Nx90x15x15
printf("id: %d, numClasses:%d, m:%d, s:%d n", id, numClasses, mh, mw);
解析输出
代码语言:javascript复制// 解析对象检测输出
Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
float confidence_threshold = 0.6;
for (int i = 0; i < detectionMat.rows; i ) {
float confidence = detectionMat.at<float>(i, 2);
if (confidence > confidence_threshold) {
size_t objIndex = (size_t)(detectionMat.at<float>(i, 1));
float tl_x = detectionMat.at<float>(i, 3) * frame.cols;
float tl_y = detectionMat.at<float>(i, 4) * frame.rows;
float br_x = detectionMat.at<float>(i, 5) * frame.cols;
float br_y = detectionMat.at<float>(i, 6) * frame.rows;
Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y));
rectangle(frame, object_box, Scalar(255, 0, 255), 1, 8, 0);
putText(frame, format(" confidence %.2f", confidence), Point(tl_x - 10, tl_y - 5), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(255, 0, 0), 2, 8);
// 解析mask
Mat mask(masks.size[2], masks.size[3], CV_32F, masks.ptr<float>(i, objIndex));
Mat color_mask = Mat::zeros(mask.size(), CV_8UC3);
Mat bin_mask = Mat::zeros(mask.size(), CV_8UC1);
for (int row = 0; row < color_mask.rows; row ) {
for (int col = 0; col < color_mask.cols; col ) {
float m = mask.at<float>(row, col);
if (m >= 0.5) {
color_mask.at<Vec3b>(row, col) = Vec3b(0, 0, 255);
bin_mask.at<uchar>(row, col) = 255;
}
}
}
Mat roi = frame(object_box);
resize(color_mask, color_mask, roi.size());
resize(bin_mask, bin_mask, roi.size());
Mat result;
bitwise_and(roi, roi, result, bin_mask);
imshow("mask", result);
addWeighted(roi, 0.5, color_mask, 0.5, 0, roi);
}
}
imshow("mask-rcnn-demo", frame);