章節簡介
本章以人臉檢測爲例, 定義具體可跟蹤對象類型的數據文件, 即Haar級聯分類器, 通過對比分析相鄰圖像區域來判斷給定圖像或子圖像與已知對象是都匹配.
同時, 本章會將多個Haar級聯分類器構成一個層次結構, 即一個分類器能識別整體區域, 而其它分類器可識別更小的區域.
至於爲什麼以老掉牙的人臉檢測爲例子, 是因爲現階段人臉檢測有關的技術太成熟了…
往期博客
PythonOpencv開發 Python3.6.7+Opencv3.4.2.16環境配置
OpenCV Python開發 第一章課後 自定義實現API
Haar級聯的概念
還是以人臉爲例子, 假設有一段監控視頻, 拍到了一個人正臉步行經過的畫面. 這樣的情景下我們肯定是希望對人進行跟蹤, 對人臉進行匹配和分類(比如通過人臉來識別這個人是誰). 那麼我們必不可能侷限於這個人的整體框架, 而必須將注意力放在人臉的細節部分, 比如臉上的疤痕, 瞳孔的顏色等等. 但是通常來說, 很難保證一段視頻不受燈光, 視角, 視距, 攝像頭抖動以及任何其它噪聲的影響, 這些比可避免的影響容易使得圖像出現輕微的不穩定.
因此需要解決的問題是, 如何提取圖像的細節特徵並生成穩定的分類結果?
Haar實際上是運用了boosting算法中的Adaboost算法, 而Haar分類器利用Adaboost算法構建一個強分類器進行級聯,而在底層特徵抽取上採用的是高校的矩形特徵以及積分圖方法
可以理解爲:
其中類Haar特徵是一種用於實現實時人臉跟蹤的特徵, 最早於2001年由Paul Viola等人提出. 每個類Haar特徵都描述了相鄰圖像區域的對比模式, 比如邊和角點.
Haar分類器主要步驟如下:
- 提取類Haar特徵;
- 利用積分圖法對類Haar特徵提取進行加速;
- 使用Adaboost算法訓練強分類器,區分出人臉和非人臉;
- 使用篩選式級聯把強的分類器級聯在一起,從而提高檢測準確度。
需要注意的是, OpenCV的Haar級聯不具有旋轉不變性的特點, Haar級聯不認爲倒置的人臉圖像和直立的人臉圖像一樣.
獲取Haar級聯數據
在OpenCV3.4.2.16的源碼中, 存在一個data文件夾, 裏面包含了所有OpenCV的人臉檢測的XML文件
通過文件名我們可以得知, 這些文件分別用於檢測眼睛, 微笑, 鼻子等細節. 需要注意的是, 這些文件需要正面並且直立的人臉圖像, 但是一張輸入圖像可以包含多張人臉
這裏我們給python項目再創建一個新文件夾haarcascades, 然後在該文件夾下創建名爲cascades的子文件夾, 並將上述的所有XML文件複製進來.
使用OpenCV進行人臉檢測
靜態圖像中的人臉檢測
沒有需要特別事先說明的注意點, 這裏直接給出代碼, 往下結合代碼進行詳細的說明.
def detectFaces(img):
"""
檢測靜態圖像中的人臉並顯示
:param img: 待檢測的圖像
"""
# 首先加載Haar分類器
face_cascade = cv.CascadeClassifier('..\\haarcascades\\cascades\\haarcascade_frontalface_default.xml')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 這一行直接進行檢測, 返回的結果是一個個矩形, 每一個矩形代表了一個人臉
faces = face_cascade.detectMultiScale(img_gray, 1.3, 5)
for (x, y, w, h) in faces:
img = cv.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.title('人臉檢測')
plt.xticks([]), plt.yticks([])
plt.show()
主要的流程其實只有三個部分:
- 首先利用
cv.CascadeClassifier()
來加載一個Haar分類器, 返回一個類對象 - 其次利用上一步得到的類對象, 調用其成員函數
.detectMultiScale()
來進行人臉檢測, 每一張人臉返回一個矩形 - 最後在原圖上標出這些矩形即可.
查看opencv-python的源碼可以看到, 函數detectMultiScale()
有3個重載, 經過翻譯後, 分別是:
def detectMultiScale(self, image, scaleFactor=None, minNeighbors=None, flags=None, minSize=None, maxSize=None):
"""
detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) -> objects
. @brief 檢測輸入圖像中的不同尺度的目標, 被檢測到的目標將作爲一個矩形列表被返回
.
. @param image 輸入圖像,包含了待檢測的目標, 像素值類型CV_8U, 即0~255的整數.
. @param objects 存放檢測結果的列表, 每個元素都是一個矩形, 需要注意的是, 這個矩形有可能超出原圖像的範圍
. @param scaleFactor 每次圖像縮小的比例
. @param minNeighbors 匹配成功所需要的周圍矩形框的數目,每一個特徵匹配到的區域都是一個矩形框,只有多個矩形框同時存在的時候,才認爲是匹配成功,默認值是3, 也就是人臉檢測時常用的值
. @param flags 可以取如下這些值:
. 1, 利用canny邊緣檢測來排除一些邊緣很少或者很多的圖像區域
. 2, 正常比例檢測
. 4, 只檢測最大的物體
. 8, 粗略的檢測
. @param minSize 最小目標大小, 小於這個大小的目標被丟棄.
. @param maxSize 最大目標大小, 大於這個大小的目標被丟棄. 如果minSize == maxSize, 那麼只檢測大小正好爲minSize的目標, 其它大小的目標一律被丟棄.
"""
pass
def detectMultiScale2(self, image, scaleFactor=None, minNeighbors=None, flags=None, minSize=None, maxSize=None):
"""
detectMultiScale2(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) -> objects, numDetections
. @overload
.
. @param numDetections 對應目標的檢測數目
. 其餘參數同detectMultiScale()
.
"""
pass
def detectMultiScale3(self, image, scaleFactor=None, minNeighbors=None, flags=None, minSize=None, maxSize=None, outputRejectLevels=None):
"""
detectMultiScale3(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize[, outputRejectLevels]]]]]]) -> objects, rejectLevels, levelWeights
. @overload
. 這個重載會同時返回rejectLevels, levelWeights, (需要設置outputRejectLevels=True).
. 以下爲c++版本的一個示例
. @code
. Mat img;
. vector<double> weights;
. vector<int> levels;
. vector<Rect> detections;
. CascadeClassifier model("/path/to/your/model.xml");
. model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true);
. cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl;
. @endcode
"""
pass
可以看到, 三個函數的整體是一樣的, 唯一的區別就是在返回值.
視頻中的人臉檢測
熟悉的話, 這裏其實沒什麼好說的, 將靜態檢測的代碼封裝成函數, 一幀一幀讀取視頻調用就可以了. 大致就是這樣:
def staticDetect(img):
pass
video = cv.VideoCapture(0)
ret, frame = video.read() # 丟棄第一幀
while ret:
ret, frame = video.read()
staticDetect(frame)
章節總結
這章比較簡短, 以人臉檢測爲例, 介紹了Haar級聯和如何運用級聯去檢測目標. 檢測眼睛, 鼻子等任何目標都是同樣的邏輯:
- 讀取類Haar特徵
- 檢測
- 標註
新的API:
"""
opencv
"""
# 提取類Haar特徵
cv.CascadeClassifier()
# 檢測人臉, 注意該函數只接受灰度圖
face_cascade.detectMultiScale()
章節代碼
本章代碼非常少, 就是靜態人臉檢測的所有代碼, 已經在上面貼出了.