OpenCV4學習筆記(65)——基於dnn與ml模塊實現口罩識別

昨天在GitHub上看到了一個開源的項目,是利用深度學習來檢測是否有佩戴口罩的,感覺還挺好玩的,於是就去下載了訓練好的模型,打算用OpenCV的dnn模塊來跑一跑。然而,在經過前向傳播後,得到的推理矩陣prob是一個 1x5972x2 的Mat矩陣,和之前遇到過的推理結果都不太一樣,在經過多種解碼方式的嘗試後,還是沒能夠對這個推理結果正確得解碼。並且在網上搜索也沒有找到相關的內容,幾乎沒有網友使用OpenCV來運行這個模型,基本都是使用深度學習的框架來運行。這就很無奈了,現在只能暫時把這個模型放一邊,等其他時候再來研究一下該怎麼對它的推理結果進行解碼。

然而,我還是想嘗試一下做有無佩戴口罩的檢測,因爲被勾起了好奇心哈哈哈哈哈哈哈哈哈哈,然後又因爲使用開源項目的預訓練模型解碼失敗,一氣之下,我就想要不自己試一試搞一個。說搞就搞,由於本人對深度學習的涉及面並不深入,所以我的思路是:使用OpenCV的dnn模塊來進行人臉檢測及定位,然後將檢測出的人臉利用OpenCV的ml模塊進行識別是否佩戴口罩。

那麼要做的第一步,就是訓練出我們需要的分類器,我選用OpenCV中ml模塊的SVM分類器來訓練口罩識別分類器。訓練部分的代碼如下:

/***********************************************訓練***********************************************/
	//加載正負樣本
	string positive_path = "D:\\opencv_c++\\opencv_tutorial\\data\\test\\positive\\";
	string negative_path = "D:\\opencv_c++\\opencv_tutorial\\data\\test\\negative\\";
	//通過glob()將路徑下的所有圖像文件以string類型讀取進來
	vector<string> positive_images_str, negative_images_str;
	glob(positive_path, positive_images_str);
	glob(negative_path, negative_images_str);
	//將string類型的圖像數據轉換爲Mat類型
	vector<Mat>positive_images, negative_images;
	for (int i = 0; i < positive_images_str.size(); i++)
	{
		Mat positive_image = imread(positive_images_str[i]);
		//resize(positive_image, positive_image, Size(64, 128));
		positive_images.push_back(positive_image);
	}
	for (int j = 0; j < negative_images_str.size(); j++)
	{
		Mat negative_image = imread(negative_images_str[j]);
		//resize(negative_image, negative_image, Size(64, 128));
		negative_images.push_back(negative_image);
	}
	string savePath = "face_mask_detection.xml";
	trainSVM(positive_images, negative_images, savePath);

首先讀取所有的訓練圖像,包含正樣本(戴口罩)圖像和負樣本(不戴口罩)圖像,然後分別將正負樣本集打包成vector<Mat>類型,傳入訓練函數trainSVM()中,這個函數定義在頭文件 “face_mask.h” 中。

在訓練過程中,我們不是把圖像完全展開進行訓練,而是通過特徵提取, 得到每個樣本圖像的HOG特徵,再計算每個HOG特徵的特徵描述子,通過特徵描述子來訓練SVM分類器。

要注意的是,我們並不是對完整的樣本圖像進行HOG特徵的提取與描述,而是對樣本圖像先進行人臉區域的提取,將提取出來的人臉區域圖像再進行HOG特徵提取與描述並進行訓練。

同時,還需要對正負樣本集進行標註,正樣本標記爲1,負樣本標記爲-1。

代碼如下:

	for (int i = 0; i < positive_num; i++)
	{
		Mat positive_face;
		Rect positive_faceBox;
		if (faceDetected(positive_images[i], positive_face, positive_faceBox))
		{
			resize(positive_face, positive_face, Size(64, 128));
			Mat gray;
			cvtColor(positive_face, gray, COLOR_BGR2GRAY);			//計算HOG描述子時需要使用灰度圖像
			vector<float> descriptor;
			hog_train->compute(gray, descriptor);
			train_descriptors.push_back(descriptor);
			labels.push_back(1);
		}
	}
	for (int j = 0; j < negative_num; j++)
	{
		Mat negative_face;
		Rect negative_faceBox;
		if (faceDetected(negative_images[j], negative_face, negative_faceBox))
		{
			resize(negative_face, negative_face, Size(64, 128));
			Mat gray;
			cvtColor(negative_face, gray, COLOR_BGR2GRAY);
			vector<float> descriptor;
			hog_train->compute(gray, descriptor);
			train_descriptors.push_back(descriptor);
			labels.push_back(-1);
		}
	}
	//將訓練數據vector轉換爲Mat對象,每一行爲一個描述子,行數即爲樣本數
	int width = train_descriptors[0].size();
	int height = train_descriptors.size();
	Mat train_data = Mat::zeros(Size(width, height), CV_32F);
	for (int r = 0; r < height; r++)
	{
		for (int c = 0; c < width; c++)
		{
			train_data.at<float>(r, c) = train_descriptors[r][c];
		}
	}
	auto train_svm = ml::SVM::create();
	train_svm->trainAuto(train_data, ml::ROW_SAMPLE, labels);
	train_svm->save(path);
	hog_train->~HOGDescriptor();
	train_svm->clear();

其中進行人臉提取的函數faceDetected()定義在頭文件 “face.h” 中。在這裏我們使用的是OpenCV4學習筆記(64)中記錄過的opencv_face_detector_uint8.pb人臉檢測模型。

那麼到這一步,就實現了檢測是否佩戴口罩的SVM分類器的訓練工作,訓練得到的模型文件如下:
在這裏插入圖片描述

接下來,我們就要加載這個xml文件並且對輸入的圖像進行檢測啦。其中,檢測用的的函數是FaceMaskDetect(),這個函數定義在 “face_mask.h” 頭文件中。

	auto detecModel = ml::SVM::load("face_mask_detection.xml");
	Mat test_image = imread("D:/BaiduNetdiskDownload/人臉口罩檢測數據集/val/test_00004577.jpg");
	FaceMaskDetect(test_image, detecModel);
	//namedWindow("test_image", WINDOW_FREERATIO);
	imshow("test_image", test_image);

到這裏,我們就實現了從訓練,到運行檢測的過程,下面來看一下運行的效果怎樣:
先看下沒帶口罩的圖像,如果檢測到沒佩戴口罩,那麼人臉就用紅色框框出,而且標記紅色的 “ Not Face Mask ” 字樣:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

如果是有佩戴口罩,那麼就用綠色框框出人臉,並且標記 “ Face Mask ” :
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

從效果上來看,所採用的測試圖像都不在訓練集之內,對單個人臉的照片識別成功率還是可以的,但是肯定沒有開源項目裏神經網絡模型的識別正確率高。而且我這裏訓練的時候,正負樣本比例大約是1:2,總樣本集是四百多張訓練圖像,相比起開源項目裏八千多張圖像的訓練集來說簡直是毛都不算。。。

不過由於人臉檢測那一部分中,並沒有對同一幅圖像中出現多個人臉這種情況進行處理,以至於當一副圖像中出現多個人臉時,只會對其中人臉置信度最高的那個人進行佩戴口罩檢測,所以這個部分還需要進一步優化。emmmm看心情,看空不空閒再玩吧哈哈哈哈哈哈,現在就只是做一個小demo試一試而已,日常娛樂。

當然了,只對一張圖像進行檢測就沒啥意思了,我們同樣可以聯合攝像頭來實現實時檢測,演示代碼如下:

	VideoCapture capture;
	capture.open(0);
	if (!capture.isOpened())
	{
		cout << "can't open camera" << endl;
		exit(-1);
	}
	Mat frame;
	while (capture.read(frame))
	{
		FaceMaskDetect(frame, detecModel);
		//namedWindow("test_image", WINDOW_FREERATIO);
		imshow("test_image", frame);

		char ch = waitKey(1);
		if (ch == 27)
		{
			break;
		}
	}

這一步就不截圖了吧~

好的,總結一下本次筆記的內容:利用上次筆記記錄的人臉檢測tensorflow模型來提取人臉區域,然後使用自己訓練的SVM分類器來判斷該人臉是否有佩戴口罩,並進行不同表示。同時,可以聯合攝像頭進行實時的佩戴口罩檢測。當然了,目前只是搭建了一個demo來實現基礎的功能,仍然還有許多需要優化的地方,至於優不優化、啥時候優化,就看我啥時候有空了哈哈哈哈哈哈哈哈哈,畢竟校內課程可太難了。。。。。。

那麼本次筆記就到此結束啦,謝謝閱讀~

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

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