OpenCV Using Python——調整基於HAAR特徵的AdaBoost級聯分類器的物體識別的參數

調整基於HAAR特徵的AdaBoost級聯分類器的物體識別的參數

1. 基於HAAR特徵的AdaBoost級聯分類器的物體識別問題

        Paul A. Viola和Michael J. Jones在2001年發表文章“使用簡單特徵的提高級聯檢測器的快速物體檢測”。同時CSDN上很多博主在07年到13年也紛紛對該方法的原理,庫函數內容,XML文件的訓練以及OpenCV的實現做出很多工作。同時,調用OpenCV的庫函數很方便,能夠用很短的代碼即可實現人臉的檢測。但是,在實際使用中發現,很多訓練好的XML文件不好用

2. 調整參數的意義

        既然訓練好的XML文件不好用,是不是意味着要重新訓練分類器?如果需要檢測的物體在OpenCV中有,那麼儘量用OpenCV中自帶的分類器。因爲自帶的分類器包含了很多工作人員的心血,絕對不會那麼輕易不好用的。所以根據需要根據實際情況查找問題。主要問題包括:圖像分辨率,參數設置和物體擺放的位姿

(1)圖像分辨率

        如果圖像分辨率過低導致圖像中的物體不清晰,甚至連人眼都看不清楚,當然檢測不出來物體;如果圖像分辨率過高,導致物體的大小超出分類器的尺寸,即分類器最多隻能檢測到物體的一部分,但檢測不到整體,這樣也不會檢測出物體。

(2)參數設置

        參數設置的內容會在調整參數的步驟中詳細說明。

(3)物體擺放的位姿

        舉個極端的例子:分類器訓練的樣本都是物體的正面,而實際檢測的爲物體的反面,物體正面和反面又是截然不同的,所以也有可能檢測不出物體。簡單地說,訓練的樣本和測試的樣本雖然是同一類物體,但在圖像中表現得差異過大。有兩種解決辦法:數據庫不完備,增加數據庫樣本重新訓練;擺正樣本。

3. 代碼實現

        有人聲稱用不到25行的代碼實現人臉檢測,筆者爲說明問題,暫時不作代碼長度的考慮。下面爲筆者應用OpenCV庫中檢測人臉,左眼,右眼,鼻子,嘴巴等XML文件的實現代碼。
import cv2
import numpy as np
from matplotlib import pyplot as plt
################################################################################

print 'Load Object Cascade Classifier'

faceCascade = cv2.CascadeClassifier('D:/OpenCV 2.4.9/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
lefteyeCascade = cv2.CascadeClassifier('D:/OpenCV 2.4.9/opencv/sources/data/haarcascades/haarcascade_mcs_lefteye.xml')
righteyeCascade = cv2.CascadeClassifier('D:/OpenCV 2.4.9/opencv/sources/data/haarcascades/haarcascade_mcs_righteye.xml')
noseCascade = cv2.CascadeClassifier('D:/OpenCV 2.4.9/opencv/sources/data/haarcascades/haarcascade_mcs_nose.xml')
mouthCascade = cv2.CascadeClassifier('D:/OpenCV 2.4.9/opencv/sources/data/haarcascades/haarcascade_mcs_mouth.xml')
################################################################################

print 'Load Image'

imgFile = 'images/face.jpg'

# load an original image
img = cv2.imread(imgFile)

# convert color space from bgr to gray
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
imgGray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

faces = faceCascade.detectMultiScale(imgGray, scaleFactor = 1.3, minNeighbors = 4, minSize = (60,60), maxSize = (300,300), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)

for (x,y,w,h) in faces:
    
    cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2)
    
    faceGray = imgGray[y:y+h, x:x+w]
    faceColor = img[y:y+h, x:x+w]
    
    '''
    # for small Alice
    lefteye = lefteyeCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 5, minSize = (20,20), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    righteye = righteyeCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 5, minSize = (20,20), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    # cannot search nose successfully!
    nose = noseCascade.detectMultiScale(faceGray, scaleFactor = 1.1, minNeighbors = 0, minSize = (5,5), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)        
    # mistaken eye for mouth!
    mouth = mouthCascade.detectMultiScale(faceGray, scaleFactor = 1.05, minNeighbors = 10, minSize = (5,5), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    '''
        
    '''
    # for big Alice
    lefteye = lefteyeCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 18, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    righteye = righteyeCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 16, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    nose = noseCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 10, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)    
    mouth = mouthCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 10, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    '''
    
    # for face
    lefteye = lefteyeCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 18, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    righteye = righteyeCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 16, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    nose = noseCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 16, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
    mouth = mouthCascade.detectMultiScale(faceGray, scaleFactor = 1.3, minNeighbors = 10, minSize = (40,40), maxSize = (80,80), flags = cv2.cv.CV_HAAR_SCALE_IMAGE)          
    ############################################################################
    
    print 'Add Objects on Face'        
                            
    for (ex,ey,ew,eh) in lefteye:
        cv2.rectangle(faceColor, (ex,ey), (ex+ew,ey+eh), (0,255,0), 2)

    for (ex,ey,ew,eh) in righteye:
        cv2.rectangle(faceColor, (ex,ey), (ex+ew,ey+eh), (255,255,0), 2)
            
    for (ex,ey,ew,eh) in nose:
        cv2.rectangle(faceColor, (ex,ey), (ex+ew,ey+eh), (0,255,255), 2)
           
    for (ex,ey,ew,eh) in mouth:
        cv2.rectangle(faceColor, (ex,ey), (ex+ew,ey+eh), (0,100,100), 2)
################################################################################

print 'Display Face Image'
                
plt.subplot(1,1,1), plt.imshow(img), plt.title('Face Image'), plt.xticks([]), plt.yticks([])
plt.show()
################################################################################
            
print 'Goodbye!'

4. 調整參數的步驟

(1)默認參數:設置默認參數觀察是否檢測到物體;
(2)檢測物體的範圍:修改minSize(檢測的對象最小尺寸,單位:像素*像素)和maxSize(檢測對象的最大尺寸),使對象落在檢測器的大小範圍內;
(3)投票數:設置minNeighbor爲0,觀察所有的投票結果,投票結果最集中的位置爲最終的檢測結果。增加投票數濾除誤檢測的對象,檢測到對象的條件越苛刻;減少投票數增加檢測到對象的機率,檢測到對象的條件越寬鬆;
(4)尺度:被檢測對象的尺度變化,尺度變化的圖像的集合可以構成圖像金字塔。scaleFactor的合理的範圍在1.1~1.4之間,尺度越大,越容易漏掉檢測的對象,但檢測速度加快;尺度越小,檢測越細緻準確,但檢測速度變慢。

5. 實驗結果

        第一幅和第二幅人臉爲標準的正臉,應該說檢測的過程得還挺完美。
        第三幅人臉檢測結果也算是盡力了。剛開始用第二幅圖的參數時,檢測人臉的分類器會在左邊的蝸牛殼那邊檢測出第二個人臉。通過調整minSize和maxSize纔將背景的僞正結果給去掉了。接着是左眼的檢測,大愛麗絲的左眼比小愛麗絲的左眼範圍大太多,所以需要將左眼的minSize和maxSize調小。調整右眼的參數時,右眼的檢測結果重疊在左眼上,我認爲是左眼的位姿比較曖昧,同時滿足了左右眼的檢測標準。檢測鼻子時,一直檢測不到結果,因爲肉眼查看圖像發現小愛麗絲的鼻子爲細長狀,頭微微低下使鼻尖處的寬度不夠大。檢測嘴巴時,嘴巴的投票全部在左眼上,確實小愛麗絲的嘴由於臉部微側顯得特別小,並且相比之下,左眼的形狀也更像嘴巴。



結語

        Paul Viola大神的文章只給出了大致的框架。本來希望自己寫一遍這個實現的過程,但我只寫完了積分圖像和AdaBoost分類器。最後怎麼結合還要查找更多的材料,但有可能就到此爲止了,畢竟這個庫函數調一調速度可以很快的。

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