OpenCV4學習筆記(61)——dnn模塊之調用googlenet模型實現動物圖像分類

今天開始主要整理OpenCV中dnn模塊的使用,包括各種神經網絡模型的加載、調用,輸入輸出數據的組織等等內容。而今天要記錄的是OpenCV中一個自帶神經網絡模型——googlenet模型的使用,這個模型是由caffe框架訓練出來、主要針對多種野生動物的識別。下面開始通過代碼逐步整理在OpenCV中對該模型進行調用,並對圖像進行識別分類的流程。

首先我們需要加載googlenet模型的模型文件(.caffemodel)、配置文件(.prototxt),使用readNet()這個API來實現模型的加載。注意的是,在OpenCV中既可以通過readNet()對所有支持的神經網絡模型進行加載,也可以使用針對某一種網絡框架訓練出的模型進行加載,都有相應的API可以調用,這些後續再整理。在這裏我們使用readNet(),其參數含義如下:
(1)參數model:加載已訓練模型的路徑,不同模型對應不同後綴:
*.caffemodel ——Caffe
*.pb——TensorFlow
*.t7 or *.net——Torch
*.weights——Darknet
*.bin——DLDT
(2)參數config:已訓練模型的描述文件:
*.prototxt ——Caffe
*.pbtxt ——TensorFlow
Torch中沒有config文件
*.cfg ——Darknet
*.xml ——DLDT
(3)參數framework:聲明加載的模型是由哪個框架訓練出來的,也可以不加,函數會根據讀取模型的格式來自己判斷。
代碼演示如下:

	string caffe_model_path = "D:\\opencv_c++\\opencv_tutorial\\data\\models\\googlenet(animal)\\bvlc_googlenet.caffemodel";
	string caffe_config = "D:\\opencv_c++\\opencv_tutorial\\data\\models\\googlenet(animal)\\bvlc_googlenet.prototxt";
	string labels = "D:\\opencv_c++\\opencv_tutorial\\data\\models\\googlenet(animal)\\classification_classes_ILSVRC2012.txt";
	Net caffe_net = readNet(caffe_model_path, caffe_config);

然後我們獲取這個模型的相關信息,例如每一層神經層的類型、名稱等等,當然這一步實際上可以忽略,這裏只是作爲演示。代碼演示如下:

	vector<string> caffe_layer_name = caffe_net.getLayerNames();		//獲取模型每層的名稱
	for (int i = 0; i < caffe_layer_name.size(); i++)
	{
		int caffe_layer_id = caffe_net.getLayerId(caffe_layer_name[i]);		//根據每層的名稱來獲取每層的id
		Ptr<dnn::Layer> caffe_layer = caffe_net.getLayer(caffe_layer_id);			//根據id去索引到每一層
		//獲取每一層的類型、名稱
		string layer_type = caffe_layer->type;
		string layer_name = caffe_layer->name;
		cout << caffe_layer_id << "     layer_type:  " << layer_type << "; layer_name: " << layer_name << endl;
	}

然後我們需要讀取該模型的標籤集,並進行一定處理,以便後續分類ID的索引。

	//讀取分類標籤集
	ifstream fp(labels);
	vector<string>class_labels;
	if (!fp.is_open())
	{
		cout << "labels file can't open" << endl;
		exit(-1);
	}
	while (!fp.eof())				//如果指針不位於文件末尾,就繼續讀取文件
	{
		string class_name;
		getline(fp, class_name);			//讀取文件中的每一行數據
		if (0 != class_name.length())				//如果不是空行,就將這一行的數據保存起來
		{
			class_labels.push_back(class_name);
		}
	}
	fp.close();

接下來我們讀取圖像

	Mat test_image = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\tem.jpg");
	resize(test_image, test_image, Size(700, 700));
	imshow("test_image", test_image);
	Mat inputBlob = blobFromImage(test_image, 1.0, Size(224, 224), Scalar(104, 117, 123), true, false, 5);

注意無法直接將圖像作爲神經網絡的輸入,我們需要將圖像轉換爲4維的blob才能作爲輸入。通過blobFromImage()進行轉換,其參數含義如下:

(1)image:輸入圖像(具有1、3或4通道)
(2)size:輸出圖像的空間大小
(3)mean:從通道中減去平均值的標量,如果參數image具有BGR順序且參數swapRB爲true ,則值應按(平均值R,平均值G,平均值B)順序排列。該值由模型訓練時所確定,需要通過查詢模型資料得知。
(4)scalefactor:對參數image的縮放比例
(5)swapRB: 表示是否交換3通道圖像中的第一個和最後一個通道的標誌
(6)crop:表示是否在調整大小後裁剪圖像
(7)ddepth:輸出blob的深度,選擇CV_32F或CV_8U。

經過處理後將blob輸入給神經網絡,再進行前向傳播得到預測結果,也就是分類結果。

	caffe_net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
	caffe_net.setPreferableTarget(DNN_TARGET_CPU);
	caffe_net.setInput(inputBlob);
	Mat prob = caffe_net.forward();

這裏的前兩句代碼是設置神經網絡運算的計算後臺和目標設備,DNN_BACKEND_INFERENCE_ENGINE需要搭配openVINO使用,如果沒有的話就設置DNN_BACKEND_OPENCV,否則會報錯。

前向傳播得到的結果是一個1行、1000列、單通道的矩陣,其中每一列的值就是每一個分類的置信度,由於總共是1000個分類,所以有1000列。我們需要找到其中置信度最大的像素座標,那麼該座標的x值對應的就是在標籤集中分類的ID,通過這個ID去索引到預測的分類。代碼演示如下:

	prob = prob.reshape(1, 1);
	double maxval;
	Point maxloc;
	minMaxLoc(prob, NULL, &maxval, NULL, &maxloc);
	int classID = maxloc.x;
	string className = class_labels[classID];
	putText(test_image, className, Point(20, 20),FONT_HERSHEY_SIMPLEX,1, Scalar(0, 255, 0), 2);
	cout << "運行時間:  " << run_time << endl;
	imshow("test_image", test_image);

下面是運行效果:
該模型每一層的類型和名稱
在這裏插入圖片描述
在這裏插入圖片描述
識別效果
在這裏插入圖片描述
在這裏插入圖片描述

總的來說,我們需要學習瞭解的是在OpenCV中如何去調用這些預訓練好的模型,主要是輸入輸出數據的組織和轉換,今天使用的googlenet模型的數據組織其實是比較簡單的,其識別效果也不做評價了,因爲在它標籤集分類中可以看出大部分都是野生動物。。。

今天的筆記就到這吧~

PS:本人的註釋比較雜,既有自己的心得體會也有網上查閱資料時摘抄下的知識內容,所以如有雷同,純屬我向前輩學習的致敬,如果有前輩覺得我的筆記內容侵犯了您的知識產權,請和我聯繫,我會將涉及到的博文內容刪除,謝謝!

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