實(shí)例分割概念
圖像實(shí)例分割是在對象檢測的基礎(chǔ)上進(jìn)一步細(xì)化,分離對象的前景與背景,實(shí)現(xiàn)像素級別的對象分離。所以圖像實(shí)例分割是基于對象檢測的基礎(chǔ)上進(jìn)一步提升。圖像實(shí)例分割在目標(biāo)檢測、人臉檢測、表情識別、醫(yī)學(xué)圖像處理與疾病輔助診斷、視頻監(jiān)控與對象跟蹤、零售場景的貨架空缺識別等場景下均有應(yīng)用。很多人會把圖像語義分割跟實(shí)例分割搞混淆,其實(shí)圖像的語義分割(Semantic Segmentation)與圖像的實(shí)例分割(Instance Segmentation)是兩個(gè)不同的概念,看下圖:
圖-1(來自COCO數(shù)據(jù)集論文)
左側(cè)是圖像語義分割的結(jié)果,幾個(gè)不同的類別人、羊、狗、背景分別用不同的顏色表示;右側(cè)是圖像實(shí)例分割的結(jié)果,對每只羊都用不同的顏色表示,而且把每個(gè)對象從背景中分離出來。這個(gè)就是語義分割跟實(shí)例分割的區(qū)別,直白點(diǎn)可以說就是語義分割是對每個(gè)類別、實(shí)例分割是針對每個(gè)對象(多個(gè)對象可能屬于同一個(gè)類別)。
常見的實(shí)例分割網(wǎng)絡(luò)
Mask-RCNN實(shí)例分割網(wǎng)絡(luò)
圖像實(shí)例分割是在對象檢測的基礎(chǔ)上再多出個(gè)基于ROI的分割分支,基于這樣思想的實(shí)例分割Mask-RCNN就是其經(jīng)典代表,它的網(wǎng)絡(luò)結(jié)構(gòu)如下:
圖-2(來自Mask-RCNN的論文)
Mask-RCNN可以簡單地認(rèn)為是Faster-RCNN的基礎(chǔ)上加上一個(gè)實(shí)例分割分支。
RetinaMask實(shí)例分割網(wǎng)絡(luò)
RetinaMask可以看成RetinaNet對象檢測網(wǎng)絡(luò)跟Mask-RCNN實(shí)例分割網(wǎng)絡(luò)的兩個(gè)優(yōu)勢組合,基于特征金字塔實(shí)現(xiàn)了更好的Mask預(yù)測,網(wǎng)絡(luò)結(jié)構(gòu)圖示如下:
圖-3(來自RetinaMask論文)
PANet實(shí)例分割網(wǎng)絡(luò)
PANet主要工作是基于Mask-RCNN網(wǎng)絡(luò)上改進(jìn)所得,作者通過改進(jìn)Backbone部分提升了特征提取能力,通過自適應(yīng)的池化操作得到更多融合特征,基于全鏈接融合產(chǎn)生mask,最終取得了比Mask-RCNN更好的實(shí)例分割效果,該模型的結(jié)構(gòu)如下:
圖-4(來自PANet論文)
其中全鏈接特征融合mask分支如下圖:
圖-5(來自PANet論文)
YOLACT實(shí)例分割網(wǎng)絡(luò)
該實(shí)例分割網(wǎng)絡(luò)也是基于RetinaNet對象檢測網(wǎng)絡(luò)的基礎(chǔ)上,添加一個(gè)Mask分支,不過在添加Mask分支的時(shí)候它的Mask分支設(shè)計(jì)跟RetinaMask有所不同,該網(wǎng)絡(luò)的結(jié)構(gòu)圖示如下:
圖-6(來自YOLACT作者論文)
CenterMask實(shí)例分割網(wǎng)絡(luò)
該實(shí)例網(wǎng)絡(luò)是基于FCOS對象檢測框架的基礎(chǔ)上,設(shè)計(jì)一個(gè)Mask分支輸出,該Mask分支被稱為空間注意力引導(dǎo)蒙板(Spatial Attention Guided Mask),該網(wǎng)絡(luò)的結(jié)構(gòu)如下:
圖-7(來自CenterMask論文)
OpenVINO 支持Mask-RCNN模型
OpenVINO 中支持兩種實(shí)例分割模型分別是Mask-RCNN與YOLACT模型,其中Mask-RCNN模型支持來自英特爾官方庫文件、而YOLACT則來自公開的第三方提供。我們這里以官方的Mask-RCNN模型instance-segmentation-security-0050為例說明,該模型基于COCO數(shù)據(jù)集訓(xùn)練,支持80個(gè)類別的實(shí)例分割,加上背景為81個(gè)類別。
OpenVINO 支持部署Faster-RCNN與Mask-RCNN網(wǎng)絡(luò)時(shí)候輸入的解析都是基于兩個(gè)輸入層,它們分別是:
im_data : NCHW=[1x3x480x480]
im_info: 1x3 三個(gè)值分別是H、W、Scale=1.0
輸出有四個(gè),名稱與輸出格式及解釋如下:
name: classes, shape: [100, ] 預(yù)測的100個(gè)類別可能性,值在[0~1]之間
name: scores: shape: [100, ] 預(yù)測的100個(gè)Box可能性,值在[0~1]之間
name: boxes, shape: [100, 4] 預(yù)測的100個(gè)Box坐標(biāo),左上角與右下角,基于輸入的480x480
name: raw_masks, shape: [100, 81, 28, 28] Box ROI區(qū)域的實(shí)例分割輸出,81表示類別(包含背景),28x28表示ROI大小,注意:此模型輸出大小為14x14
模型實(shí)例分割代碼演示
因?yàn)槟P偷募虞d與推理部分的代碼跟前面系列文章的非常相似,這里就不再給出。代碼演示部分重點(diǎn)在輸出的解析,為了簡化,我用了兩個(gè)for循環(huán)設(shè)置了輸入與輸出數(shù)據(jù)精度,然后直接通過hardcode的輸出層名稱來獲取推理之后各個(gè)輸出層對應(yīng)的數(shù)據(jù)部分,首先獲取類別,根據(jù)類別ID與Box的索引,直接獲取實(shí)例分割mask,然后隨機(jī)生成顏色,基于mask實(shí)現(xiàn)與原圖BOX ROI的疊加,產(chǎn)生了實(shí)例分割之后的效果輸出。解析部分的代碼首先需要獲取推理以后的數(shù)據(jù),獲取數(shù)據(jù)的代碼如下:
float w_rate = static_cast
float h_rate = static_cast
auto scores = infer_request.GetBlob("scores");
auto boxes = infer_request.GetBlob("boxes");
auto clazzes = infer_request.GetBlob("classes");
auto raw_masks = infer_request.GetBlob("raw_masks");
const float* score_data = static_cast
const float* boxes_data = static_cast
const float* clazzes_data = static_cast
const auto raw_masks_data = static_cast
const SizeVector scores_outputDims = scores->getTensorDesc().getDims();
const SizeVector boxes_outputDims = boxes->getTensorDesc().getDims();
const SizeVector mask_outputDims = raw_masks->getTensorDesc().getDims();
const int max_count = scores_outputDims[0];
const int object_size = boxes_outputDims[1];
printf("mask NCHW=[%d, %d, %d, %d] ", mask_outputDims[0], mask_outputDims[1], mask_outputDims[2], mask_outputDims[3]);
int mask_h = mask_outputDims[2];
int mask_w = mask_outputDims[3];
size_t box_stride = mask_h * mask_w * mask_outputDims[1];
然后根據(jù)輸出數(shù)據(jù)格式開始解析Box框與Mask,這部分的代碼如下:
for (int n = 0; n < max_count; n++) {
float confidence = score_data[n];
float xmin = boxes_data[n*object_size] * w_rate;
float ymin = boxes_data[n*object_size + 1] * h_rate;
float xmax = boxes_data[n*object_size + 2] * w_rate;
float ymax = boxes_data[n*object_size + 3] * h_rate;
if (confidence > 0.5) {
cv::Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
cv::Rect box;
float x1 = std::max(0.0f, xmin), static_cast
float y1 = std::max(0.0f, ymin), static_cast
float x2 = std::max(0.0f, xmax), static_cast
float y2 = std::max(0.0f, ymax), static_cast
box.x = static_cast
box.y = static_cast
box.width = static_cast
box.height = static_cast
int label = static_cast
std::cout << "confidence: " << confidence << " class name: " << coco_labels[label] << std::endl;
// 解析mask
float* mask_arr = raw_masks_data + box_stride * n + mask_h * mask_w * label;
cv::Mat mask_mat(mask_h, mask_w, CV_32FC1, mask_arr);
cv::Mat roi_img = src(box);
cv::Mat resized_mask_mat(box.height, box.width, CV_32FC1);
cv::resize(mask_mat, resized_mask_mat, cv::Size(box.width, box.height));
cv::Mat uchar_resized_mask(box.height, box.width, CV_8UC3, color);
roi_img.copyTo(uchar_resized_mask, resized_mask_mat <= 0.5);
cv::addWeighted(uchar_resized_mask, 0.7, roi_img, 0.3, 0.0f, roi_img);
cv::putText(src, coco_labels[label].c_str(), box.tl() + (box.br() - box.tl()) / 2, cv::FONT_HERSHEY_PLAIN, 1.0, cv::Scalar(0, 0, 255), 1, 8);
}
}
其中Mask部分的時(shí)候有個(gè)技巧的地方,首先獲取類別,然后根據(jù)類別,直接獲取Mask中對應(yīng)的通道數(shù)據(jù)生成二值Mask圖像,添加上顏色,加權(quán)混合到ROI區(qū)域即可得到輸出結(jié)果。
責(zé)任編輯:lq6
-
圖像
+關(guān)注
關(guān)注
2文章
1084瀏覽量
40470
原文標(biāo)題:OpenVINO? 實(shí)現(xiàn)圖像實(shí)例分割
文章出處:【微信號:英特爾物聯(lián)網(wǎng),微信公眾號:英特爾物聯(lián)網(wǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論