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