摘要:今天花了一天的時間完成了靜態圖片中的人臉檢測,這是對計算機視覺的第一次直觀體驗,很有成就感。這篇文章詳細的解釋了源程序每一行語句的做用,主要仍是理清本身大腦的思路。話很少說,直接開擼!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>
這個頭文件。ui2.跟1一樣的方法,
cvtColor()
函數和Rectangl()
函數應該位於<opencv2/imgproc/imgproc.hpp>
。this3.同上,
imread()
,imshow()
,waitkey()
位於<opencv2/highgui/highgui.hpp>
。spa4.同上,
CascadeClassifier
類應該位於<opencv2/objdetect/objdetect.hpp>
。.net
using namespace std;
using namespace cv;
複製代碼
using namespace std;
不用多說,C++標準程序庫的全部標識符都被聲明在std
命名空間內,常常用到的cin
、cout
、endl
等標識符皆如此,因此基本上全部的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 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");
調用對象faceCascade
的load()
函數,其參數爲.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
。
能夠看到,明家三兄弟的臉已經被紅框框起來了。靜態圖片中人臉檢測成功實現。
原本想寫不少,卻又寫不出了。就但願本身能學到想學的知識,氧氧能快快樂樂的吧。