opencv dnn模塊 示例(10) mask rcnn inception V2

一、opencv的示例模型文件

opencv4.0.0中暫未提供cpp代碼,使用python代碼改編,參考https://github.com/opencv/opencv/blob/master/samples/dnn/mask_rcnn.py,我們使用的模型爲
mask_rcnn_inception_v2_coco_2018_01_28.pb,選擇InceptionV2是因爲其速度更快,其他更好效果的如ResneXt-101相關模型可在tensorflow model zoo下載。

相關知識

Mask R-CNN(He et al。,ICCV 2017)是對Faster RCNN的改進,它包括一個掩碼預測與類標籤和邊界框預測分支平行的分支,如下圖所示。它只爲較快的R-CNN網絡增加了一小部分開銷,因此仍然可以在GPU上以5 fps運行。在本教程中,在Intel Core i7 CPU上運行來顯示結果,並且在CPU上每幀大約需要2秒,即使對於具有超過30個對象的幀也是如此。
在這裏插入圖片描述
第一個部分RPN:每個圖像生成大約300個區域提案,提議(ROI)中的每一個都通過二部分,即對象檢測和掩模預測網絡,如上所示。注意,由於掩模預測分支與標籤和框預測分支並行運行,因此對於每個給定的ROI,網絡預測屬於所有類的掩模。
第二個部分MASK:掩模預測分支僅處理最高得分100檢測框,因此對於100個ROI和90個對象類(這裏使用的是coco數據集),網絡的掩模預測部分輸出尺寸爲100x90x15x15的4D張量,其中每個掩模的大小爲15×15。

demo流程
例如,對於coco數據,我們預測1張圖時,目標檢測有N個目標框,描述爲Boxes [1*1*N*7],'7’代表目標檢測的數據結構 [batchId, classId, confidence, left, top, right, bottom]。同時,掩碼生成的結果爲固定的Masks [100*90*15*15],對於得分最高的100個區域,都生成類別90個15*15大小的掩碼。
1、對於檢測的第n個目標框的數據Box => Boxes[1*1*n*7] ,同時可取得其類別classId,置信度 confidence。
2、通過目標的序號n和類別,取得這個目標對應的掩碼Mask => Masks[n*classId*15*15],將Mask縮放到該目標Box大小[left, top, right, bottom]
3、繪製Box,通過Mask疊加蒙版。(實例分割時,需要爲每個對象生成不同的顏色蒙版)。

二、示例代碼

#include <fstream>
#include <sstream>

#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

#include <iostream>

using namespace cv;
using namespace dnn;

float confThreshold;
float maskThreshold;
float nmsThreshold;

std::vector<std::string> classes;
std::vector<Vec3b> colors;

void postprocess(cv::Mat& frame, const std::vector<Mat>& outs);

int main(int argc, char** argv) try
{
	//	mask rcnn

	// 根據選擇的檢測模型文件進行配置 
	confThreshold = 0.5;
	maskThreshold = 0.3;
	nmsThreshold = 0.5;

	float scale = 1.;          // UNneed
	Scalar mean = { 0,0,0 };   // UNneed
	bool swapRB = true;
	int inpWidth = 800;
	int inpHeight = 800;

	String modelPath = "../../data/testdata/dnn/mask_rcnn_inception_v2_coco_2018_01_28.pb";
	String configPath = "../../data/testdata/dnn/mask_rcnn_inception_v2_coco_2018_01_28.pbtxt";

	String framework = "";

	int backendId = cv::dnn::DNN_BACKEND_OPENCV;
	int targetId = cv::dnn::DNN_TARGET_CPU;

	String classesFile = "../../data/dnn/object_detection_classes_coco.txt";
	String colorFile = "";

	// Open file with classes names.
	if (!classesFile.empty()) {
		const std::string file = classesFile;
		std::ifstream ifs(file.c_str());
		if (!ifs.is_open())
			CV_Error(Error::StsError, "File " + file + " not found");
		std::string line;

		classes.emplace_back("background"); //使用的是object_detection_classes類

		while (std::getline(ifs, line)) {
			classes.push_back(line);
		}
	}

	std::srand(324);
	if (!colorFile.empty()) {
		const std::string& file = colorFile;
		std::ifstream ifs(file.c_str());
		if (!ifs.is_open())
			CV_Error(Error::StsError, "File " + file + " not found");
		std::string line;
		while (std::getline(ifs, line)) {
			std::istringstream colorStr(line.c_str());
			Vec3b color;
			for (int i = 0; i < 3 && !colorStr.eof(); ++i)
				colorStr >> color[i];
			colors.push_back(color);
		}
	}
	else {
		colors.emplace_back(Vec3b());			
		for (int i = 1; i < classes.size(); ++i) {
			Vec3b color;
			for (int j = 0; j < 3; ++j)
				color[j] = (colors[i - 1][j] + rand() % 256) / 2;
			colors.emplace_back(color);
		}
	}

	CV_Assert(!modelPath.empty());
	//! [Read and initialize network]
	Net net = readNet(modelPath, configPath, framework);
	net.setPreferableBackend(backendId);
	net.setPreferableTarget(targetId);
	//! [Read and initialize network]

	// Create a window
	static const std::string kWinName = "MASK-RCNN in OpenCV";
	namedWindow(kWinName, WINDOW_AUTOSIZE);

	//! [Open a video file or an image file or a camera stream]
	VideoCapture cap;
	cap.open("../../data/image/dog.jpg");                       // pascal voc 	

	if (!cap.isOpened()) {
		std::cout << "VideoCapture open failed." << std::endl;
		return 0;
	}

	//! [Open a video file or an image file or a camera stream]

	// Process frames.
	Mat frame, blob;
	while (waitKey(1) < 0) {
		cap >> frame;
		if (frame.empty()) {
			waitKey();
			break;
		}

		//! [Create a 4D blob from a frame]
		blobFromImage(frame, blob, scale, Size(inpWidth, inpHeight), mean, swapRB, false);

		//! [Create a 4D blob from a frame]

		//! [Set input blob]
		net.setInput(blob);
		//! [Set input blob]

		//! [Make forward pass]		
		std::vector<Mat> outputs;		// score, mask
		static std::vector<std::string> outputNames = { "detection_out_final" , "detection_masks" };
		net.forward(outputs, outputNames);
		//! [Make forward pass]
		
		// Draw result
		postprocess(frame, outputs);

		// Put efficiency information.
		std::vector<double> layersTimes;
		double freq = getTickFrequency() / 1000;
		double t = net.getPerfProfile(layersTimes) / freq;
		std::string label = format("Inference time: %.2f ms", t);
		putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));

		imshow(kWinName, frame);
	}
	return 0;
}
catch (std::exception & e) {
	std::cerr << e.what() << std::endl;
}

void postprocess(cv::Mat& frame, const std::vector<Mat>& outs)
{
	if (frame.empty()) return;

	Mat boxes = outs[0];  // 1x1xNx7
	Mat masks = outs[1];  // 100 * classNum * 15 * 15   掩碼得分最高的100個   mask_rcnn_inception_v2_coco_2018_01_28

	Mat tmpFrame = frame.clone();

	int frameW = frame.cols;
	int frameH = frame.rows;

	std::vector<int> classIds;
	std::vector<float> confidences;
	std::vector<Rect> predBoxes;

	// [batchId, classId, confidence, left, top, right, bottom]    1x1xNx7
	for (int i = 0; i < boxes.size[2]; ++i) {
		float* box = (float*)boxes.ptr<float>(0, 0, i);
		float score = box[2];
		if (score > confThreshold) {
			int classId = box[1];
			int boxLeft = frameW *  box[3];
			int boxTop = frameH *  box[4];
			int boxRight = frameW *  box[5];
			int boxBottom = frameH *  box[6];
			cv::Rect rect{ cv::Point{ boxLeft,boxTop },cv::Point{ boxRight,boxBottom } };
			rect &= cv::Rect({ 0,0 }, frame.size());

			classIds.emplace_back(classId);
			predBoxes.emplace_back(rect);
			confidences.emplace_back(score);
		}
	}

	// 相較於源代碼,增加了nms,避免可能某些區域生成多個目標框的情況。
	std::vector<int> indices;
	cv::dnn::NMSBoxes(predBoxes, confidences, confThreshold, nmsThreshold, indices);

	// draw results

	for (size_t i = 0; i < indices.size(); ++ i) {
		int idx = indices[i];
		Rect box = predBoxes[idx];
		int classId = classIds[idx];
		float conf = confidences[idx];

		// Draw mask     
		Scalar color = colors[(classId+1) % colors.size()];
		//int colorInd = rand() % colors.size();  //generate different instance colors
		//Scalar color = colors[colorInd];
		Mat mask(masks.size[2], masks.size[3], CV_32F, masks.ptr<float>(i, classId));
		resize(mask, mask, box.size(), 0, 0, cv::INTER_LINEAR_EXACT);
		mask = mask > maskThreshold;
		Mat coloredRoi;
		addWeighted(frame(box), 0.3, color, 0.7, 0, coloredRoi);
		coloredRoi.copyTo(frame(box), mask);

		// Draw box
		rectangle(frame, box, Scalar(0, 255, 0));
		std::string label = format("%.2f", conf);
		if (!classes.empty()) {
			CV_Assert(classId < (int)classes.size());
			label = classes[classId + 1] + ": " + label;
		}
		int baseLine;
		cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
		Rect labelRect{ box.tl() - cv::Point2i(0,baseLine + labelSize.height), labelSize + cv::Size{ 0,baseLine } };
		rectangle(frame, labelRect, cv::Scalar::all(255), cv::FILLED);
		putText(frame, label, box.tl() - cv::Point2i(0, baseLine), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar());
	}
}

三、演示

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
修改代碼,顯示實例分割的結果

//Scalar color = colors[(classId +1) % colors.size()];
int colorInd = rand() % colors.size();  //generate different instance colors
Scalar color = colors[colorInd];

在這裏插入圖片描述

附帶一張使用int backendId = cv::dnn::DNN_BACKEND_INFERENCE_ENGINE;時,使用intel OpenVINO的opencv庫測試結果,性能提高30%。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章