基於OpenCV4.1.0實現靜態圖片人臉檢測

摘要:今天花了一天的時間完成了靜態圖片中的人臉檢測,這是對計算機視覺的第一次直觀體驗,很有成就感。這篇文章詳細的解釋了源程序每一行語句的做用,主要仍是理清本身大腦的思路。話很少說,直接開擼!html

源代碼

注:本文所用開發環境爲VS2019 Community + OpenCV4.1.0,需提早配置好環境,如需幫助,請關注公衆號【鹿談】,看個人第一篇推文。c++

//FaceRec.cpp

#include<opencv2/opencv.hpp>
//#include<opencv2/objdetect/objdetect.hpp>
//#include<opencv2/highgui/highgui.hpp>
//#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2\imgproc\types_c.h>

using namespace std;
using namespace cv;

CascadeClassifier faceCascade;                  //人臉檢測的類

int main() {     faceCascade.load("D:/openCV/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml");   //加載分類器,注意文件路徑

	Mat img = imread("3ming.jpg");    //圖片放在與FaceRec.cpp同級目錄中
	Mat imgGray;
	vector<Rect> faces;

	if (img.empty())
	{
		return 1;
	}

	if (img.channels() == 3)
	{
		cvtColor(img, imgGray, CV_RGB2GRAY);
	}
	else
	{
		imgGray = img;
	}

	faceCascade.detectMultiScale(imgGray, faces, 1.2, 6, 0, Size(0, 0));   //檢測人臉

	if (faces.size() > 0)
	{
		for (int i = 0; i < faces.size(); i++)
		{
			rectangle(img, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height),
				Scalar(0, 0, 255), 4, 8);    //框出人臉位置
		}
	}

	imshow("FacesOf3ming", img);

	waitKey(0);
	return 0;
}
複製代碼

注:本例用的明家三兄弟的照片。最好找正臉的照片,放在FaceRec.cpp同級目下便可,代碼中的圖片名換成本身的。數組

代碼詳細解析

頭文件部分

#include<opencv2/opencv.hpp>
//#include<opencv2/objdetect/objdetect.hpp>
//#include<opencv2/highgui/highgui.hpp>
//#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2\imgproc\types_c.h>
複製代碼

找到OpenCV的安裝目錄,打開D:\openCV\opencv\sources\include\opencv2,會看到opencv.hpp文件,咱們用記事本打開它,能夠發現裏邊包含了不少OpenCV的頭文件,咱們註釋掉的三個頭文件也包含於內。因此咱們能夠用#include<opencv2/opencv.hpp>一句代替下邊註釋掉的三行頭文件,有效簡化代碼。而最後的#include <opencv2\imgproc\types_c.h>位於D:\openCV\opencv\build\include\opencv2\imgproc內,不包含在opencv.hpp以內,因此這句#include <opencv2\imgproc\types_c.h>要加上。那麼爲何用到了這些頭文件呢?函數

1.把#include <opencv2\imgproc\types_c.h>註釋掉後,會提示未定義標識符"CV_RGB2GRAY",估計該標誌符位於<opencv2\imgproc\types_c.h>這個頭文件。ui

2.跟1一樣的方法,cvtColor()函數和Rectangl()函數應該位於<opencv2/imgproc/imgproc.hpp>this

3.同上,imread()imshow()waitkey()位於<opencv2/highgui/highgui.hpp>spa

4.同上,CascadeClassifier類應該位於<opencv2/objdetect/objdetect.hpp>.net

命名空間部分

using namespace std;
using namespace cv;
複製代碼

using namespace std;不用多說,C++標準程序庫的全部標識符都被聲明在std命名空間內,常常用到的cincoutendl等標識符皆如此,因此基本上全部的C++程序都有這句指令。而本段函數中的vector標識符就屬於std命名空間,故有此指令。翻譯

對於using namespace cv;OpenCV官方文檔如是說:All the OpenCV classes and functions are placed into the cv namespace. Therefore, to access this functionality from your code, use the cv:: specifier or using namespace cv; directive. (翻譯:全部的OpenCV類和函數都放在cv命名空間中。所以,要從代碼中訪問此功能,請使用說明cv::符或using namespace cv;指令),故有此指令。指針

級聯分類器類CascadeClassifier

CascadeClassifier faceCascade;  //定義一個CascadeClassifier類的對象faceCascade
複製代碼

CascadeClassifier:Cascade classifier class for object detection(用於目標檢測的級聯分類器類)。也就是說,CascadeClassifier是一個。那麼什麼是級聯分類器類呢?

理解級聯分類器

分類器: 判別某個事物是否屬於某種分類的器件。它有兩種結果: 。 級聯分類器: 能夠理解爲將N個單類的分類器串聯起來。若是一個事物能屬於這一系列串聯起來的的全部分類器,則最終結果就爲,如有一項不符,則斷定爲

好比人臉,它有不少屬性,咱們將每一個屬性作一成個分類器,若是一個模型符合了咱們定義的人臉的全部屬性,則咱們人爲這個模型就是一我的臉。那麼這些屬性是指什麼呢? 好比人臉須要有兩條眉毛,兩隻眼睛,一個鼻子,一張嘴,一個大概U形狀的下巴或者是輪廓等等。

(引用自arvik的博文opencv類簡單分析: CascadeClassifier

主函數部分

faceCascade.load("D:/openCV/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml");   //加載分類器,注意文件路徑

Mat img = imread("3ming.jpg");    //圖片放在與FaceRec.cpp同級目錄中
Mat imgGray;
vector<Rect> faces;
複製代碼
  • faceCascade.load("D:/openCV/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml");

    調用對象faceCascadeload()函數,其參數爲.xml文件的絕對路徑。

    load()函數的做用是經過load一個xml文件來初始化對象faceCascade.xml是一些分類器的格式,咱們這裏用到的haarcascade_frontalface_alt2.xml是OpenCV內置的已經訓練好的諸多分類器中的一個,它的做用是Haar特徵人臉檢測。

    .xml的路徑要寫正確,嚴格按照上面的格式。電腦上覆制路徑是右斜槓'\',但在程序裏要用左斜槓'/'。

  • Mat img = imread("3ming.jpg");

    定義一個Mat類的對象img,並用imread()函數加載指定文件3ming.jpg賦值給對象img

    Mat是一個:OpenCV庫C++實現的核心類。

    Mat 類由兩個數據部分組成:矩陣頭(包含矩陣尺寸,存儲方法,存儲地址等信息)和一個指向存儲全部像素值的矩陣(根據所選存儲方法的不一樣矩陣能夠是不一樣的維數)的指針。

    Mat 類首先要知道的是沒必要再手動(1)爲其開闢空間(2)在不須要時當即將空間釋放。即自動內存管理。

    imread()函數的功能是載入一張圖片,參數爲圖片的路徑。

  • Mat imgGray;

    定義一個Mat類的對象imgGray

  • vector<Rect> faces;

    vector定義一個元素類型爲Rect類的動態數組faces

    vector是C++標準庫提供的被封裝的動態數組,它不是一個類,而是一個類模板。用vector定義的數組對象的全部元素都會被初始化。若是數組元素爲基本類型,則以初始化;若是數組元素爲類類型,則調用默認構造函數初始化。此處,數組元素爲Rect類類型,故將調用Rect類的默認構造函數初始化數組。

    Rect類,矩形類。它有四個公共屬性,Rect(int_x,int_y,int_width,int_height),(x,y)爲左上角頂點的座標,width爲寬度,height爲高度。

if (img.empty())   {return 1;}
複製代碼

調用Mat類對象img的成員函數empty()來檢查3ming.jpg是否載入成功,如成功,則返回1。

if (img.channels() == 3)  {cvtColor(img, imgGray, CV_RGB2GRAY);}
else  {imgGray = img;}
複製代碼

調用Mat類對象img的成員函數channels(),其會返回矩陣通道的數量。若是矩陣通道數量爲3,則調用cvtColor()函數,將輸入的img從轉換爲imgGray輸出,轉換模式爲CV_RGB2GRAY,即將3通道的彩色影像轉換爲灰度圖輸出;若是矩陣通道不爲3,則直接將img賦給imgGray

cvtColor()函數:Opencv裏的顏色空間轉換函數,能夠實現RGB顏色向HSV,HSI等顏色空間的轉換,也能夠轉換爲灰度圖。第一個參數爲輸入,第二個參數爲輸出,第三個參數爲轉換模式。那麼爲何將RGB圖像轉爲灰度圖呢?

1.三通道轉爲單通道後,運算量大大減小。

2.天然界中,顏色自己很是容易受到光照的影響,RGB變化很大,反而梯度信息能提供更本質的信息。

3.Opencv的不少函數只支持單通道。

faceCascade.detectMultiScale(imgGray, faces, 1.2, 6, 0, Size(0, 0));
複製代碼

調用CascadeClassifier類對象faceCascade的成員函數detectMultiScale(),對輸入的灰度圖imgGray進行檢測,將檢測到的人臉的矩形框向量組存入faces,尺度參數爲1.2,每個目標至少要被檢測到6次纔算是人臉,flag爲0,最小可能的對象大小爲Size(0,0),小於該值的對象將被忽略。

detectMultiScale()函數:在輸入的圖片中檢測不一樣大小的目標,檢測到的目標做爲矩形列表返回。

if (faces.size() > 0){
		for (int i = 0; i < faces.size(); i++){
			rectangle(img, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height),
				Scalar(0, 0, 255), 4, 8);    //框出人臉位置
		}
}
複製代碼

調用數組元素爲Rect類類型的動態數組faces的成員函數size(),若是face.size()大於0,即矩陣數組不爲空,也就是說檢測到目標了,則執行for循環,將檢測到的目標框起來。

rectangle()函數:繪製簡單、指定粗細或帶填充的矩形。

Rectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8, int shift=0 );

1.第一個參數爲img,也就是指定在哪一個圖上畫框,對於本例則爲img

2.第二個參數爲矩形的左上角頂點的座標,對應本例爲Point(faces[i].x, faces[i].y)

3.第三個參數爲矩形對角線上的另外一個頂點,對應本例爲Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height)

4.第四個參數爲線條顏色,Scalar(B,G,R)有三個參數,分別對應Blue、Green、Red,本例將框設置爲紅色,即Scalar(0,0,255)。

5.第五個參數爲線條粗細,數越大線條越粗,本例設置爲4。

6.第六個參數彷佛是線條類型。有待考證。

imshow("FacesOf3ming", img);
waitKey(0);
return 0;
複製代碼

執行完循環,檢測到的目標已所有框出,調用imshow()函數,將框完人臉的圖片img顯示出來。

imshow()函數有兩個參數,第一個是顯示圖片窗口的名稱,第二個是儲存圖像數據的對象。

waitkey(0)函數在顯示圖像時具備延時的做用,單位爲毫秒。若參數爲0,則圖像不會自動關閉,會無限等待下去直到有按鍵按下。

程序運行結束,return 0

程序運行結果

能夠看到,明家三兄弟的臉已經被紅框框起來了。靜態圖片中人臉檢測成功實現。

總結

原本想寫不少,卻又寫不出了。就但願本身能學到想學的知識,氧氧能快快樂樂的吧。

相關文章
相關標籤/搜索