3

任務:將人臉圖像分類爲女性或男性。使用標籤培訓圖像可用,從網絡攝像頭獲取測試圖像。Python,OpenCV:使用ORB特徵和KNN對性別進行分類

使用:的Python 2.7,OpenCV的2.4.4

我使用ORB來提取灰度圖像的特點,我希望用訓練的K近鄰分類。每個訓練圖像都是不同的人,因此每個圖像的關鍵點和描述符的數量明顯不同。我的問題是,我無法理解KNN和ORB的OpenCV文檔。我還看過關於ORB,KNN和FLANN的其他SO問題,但他們沒有太多幫助。

ORB給出的描述符的性質究竟是什麼?它與通過BRIEF,SURF,SIFT等獲得的描述符有什麼不同?

似乎KNN中每個訓練樣本的特徵描述符應該具有相同的大小。我如何確保每個圖像的描述符大小相同?更一般地說,應該以什麼樣的格式向KNN呈現特定的數據和標籤進行培訓?數據應該是int還是float?它可以是字符?

培訓數據可以找到here

我也使用來自OpenCV的樣品

眼下KNN模型,給出了訓練僅有10圖像0​​,看看我的程序通過,沒有這些,它沒有錯誤。

這裏是我的代碼:

import cv2 
from numpy import float32 as np.float32 

def chooseCascade(): 
    # TODO: Option for diferent cascades 
    # HAAR Classifier for frontal face 
    _cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml') 
    return _cascade 

def cropToObj(cascade,imageFile): 
    # Load as 1-channel grayscale image 
    image = cv2.imread(imageFile,0) 

    # Crop to the object of interest in the image 
    objRegion = cascade.detectMultiScale(image) # TODO: What if multiple ojbects in image? 

    x1 = objRegion[0,0] 
    y1 = objRegion[0,1] 
    x1PlusWidth = objRegion[0,0]+objRegion[0,2] 
    y1PlusHeight = objRegion[0,1]+objRegion[0,3] 

    _objImage = image[y1:y1PlusHeight,x1:x1PlusWidth] 

    return _objImage 

def recognizer(fileNames): 
    # ORB contructor 
    orb = cv2.ORB(nfeatures=100) 

    keyPoints = [] 
    descriptors = [] 

    # A cascade for face detection 
    haarFaceCascade = chooseCascade() 

    # Start processing images 
    for imageFile in fileNames: 
     # Find faces using the HAAR cascade 
     faceImage = cropToObj(haarFaceCascade,imageFile) 

     # Extract keypoints and description 
     faceKeyPoints, faceDescriptors = orb.detectAndCompute(faceImage, mask = None) 

     #print faceDescriptors.shape 
     descRow = faceDescriptors.shape[0] 
     descCol = faceDescriptors.shape[1] 

     flatFaceDescriptors = faceDescriptors.reshape(descRow*descCol).astype(np.float32) 

     keyPoints.append(faceKeyPoints) 
     descriptors.append(flatFaceDescriptors) 

    print descriptors 

    # KNN model and training on descriptors 
    responses = [] 
    for name in fileNames: 
     if name.startswith('BF'): 
      responses.append(0) # Female 
     else: 
      responses.append(1) # Male 

    knn = cv2.KNearest() 
    knnTrainSuccess = knn.train(descriptors, 
           responses, 
           isRegression = False) # isRegression = false, implies classification 

    # Obtain test face image from cam 
    capture = cv2.VideoCapture(0) 
    closeCamera = -1 
    while(closeCamera < 0): 
     _retval, _camImage = capture.retrieve()  

     # Find face in camera image 
     testFaceImage = haarFaceCascade.detectMultiScale(_camImage) # TODO: What if multiple faces? 

     # Keyponts and descriptors of test face image 
     testFaceKP, testFaceDesc = orb.detectAndCompute(testFaceImage, mask = None) 
     testDescRow = testFaceDesc.shape[0] 
     flatTestFaceDesc = testFaceDesc.reshape(1,testDescRow*testDescCol).astype(np.float32) 

     # Args in knn.find_nearest: testData, neighborhood 
     returnedValue, result, neighborResponse, distance = knn.find_nearest(flatTestFaceDesc,3) 

     print returnedValue, result, neighborResponse, distance 


     # Display results 
     # TODO: Overlay classification text 
     cv2.imshow("testImage", _camImage) 

     closeCamera = cv2.waitKey(1) 
    cv2.destroyAllWindows() 


if __name__ == '__main__': 
    fileNames = ['BF09NES_gray.jpg', 
       'BF11NES_gray.jpg', 
       'BF13NES_gray.jpg', 
       'BF14NES_gray.jpg', 
       'BF18NES_gray.jpg', 
       'BM25NES_gray.jpg', 
       'BM26NES_gray.jpg', 
       'BM29NES_gray.jpg', 
       'BM31NES_gray.jpg', 
       'BM34NES_gray.jpg'] 

    recognizer(fileNames) 

目前我得到在該行的錯誤與knn.train()其中descriptors沒有被檢測爲一個numpy的陣列。

另外,這種方法是完全錯誤的嗎?我是否應該使用其他方式進行性別分類?我對opencv facerec演示中的fisherface和eigenface示例不滿意,所以請不要直接告訴我這些。

任何其他幫助,非常感謝。謝謝。

---編輯---

我已經嘗試了一些東西,想出了一個答案。

我仍然希望SO社區中的某個人能夠通過提出一個想法來幫助我,這樣我就不必將事情硬編碼到我的解決方案中。我也懷疑knn.match_nearest()沒有做我所需要的。

正如預期的那樣,識別器並不完全準確,並且由於旋轉,照明等原因,很容易導致錯誤分類。有關改進此方法的任何建議都將得到真正的讚賞。

我使用培訓的數據庫是:Karolinska Directed Emotional Faces

+0

一個快速評論。剛剛發現了關於弓。似乎相關。 SO回答(http://stackoverflow.com/questions/15611872/bow-in-opencv-using-precomputed-features)。並且在這裏(https://groups.google.com/forum/#!topic/accord-net/u5viBhgv0Fw)它說,視覺詞彙袋只有一個目的和一個目的:將可變長度的特徵表示轉化爲固定長度的特徵,長度特徵表示。' –

回答

1

我對所描述的方法的有效性和/操作性有些疑惑。以下是您可能需要考慮的另一種方法。 gen文件夾的內容是@http://www1.datafilehost.com/d/0f263abc。正如你會注意到當數據量變大時(約10k個訓練樣本),模型的大小可能變得不可接受(〜100-200mb)。那麼你將需要尋找到PCA/LDA等

import cv2 
import numpy as np 
import os 

def feaCnt(): 
    mat = np.zeros((400,400,3),dtype=np.uint8) 
    ret = extr(mat) 
    return len(ret) 

def extr(img): 
    return sobel(img) 

def sobel(img): 
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
    klr = [[-1,0,1],[-2,0,2],[-1,0,1]] 
    kbt = [[1,2,1],[0,0,0],[-1,-2,-1]] 
    ktb = [[-1,-2,-1],[0,0,0],[1,2,1]] 
    krl = [[1,0,-1],[2,0,-2],[1,0,-1]] 
    kd1 = [[0,1,2],[-1,0,1],[-2,-1,0]] 
    kd2 = [[-2,-1,0],[-1,0,1],[0,1,2]]  
    kd3 = [[0,-1,-2],[1,0,-1],[2,1,0]] 
    kd4 = [[2,1,0],[1,0,-1],[0,-1,-2]] 
    karr = np.asanyarray([ 
     klr, 
     kbt, 
     ktb, 
     krl, 
     kd1, 
     kd2, 
     kd3, 
     kd4 
     ]) 
    gray=cv2.resize(gray,(40,40)) 
    res = np.float32([cv2.resize(cv2.filter2D(gray, -1,k),(15,15)) for k in karr]) 
    return res.flatten() 


root = 'C:/data/gen' 

model='c:/data/models/svm/gen.xml' 
imgs = [] 
idx =0 
for path, subdirs, files in os.walk(root): 
    for name in files: 
    p =path[len(root):].split('\\') 
    p.remove('') 
    lbl = p[0] 
    fpath = os.path.join(path, name) 
    imgs.append((fpath,int(lbl))) 
    idx+=1 

samples = np.zeros((len(imgs),feaCnt()),dtype = np.float32) 
labels = np.zeros(len(imgs),dtype = np.float32) 

i=0. 
for f,l in imgs: 
    print i 
    img = cv2.imread(f) 
    samples[i]=extr(img) 
    labels[i]=l 
    i+=1 

svm = cv2.SVM() 
svmparams = dict(kernel_type = cv2.SVM_POLY, 
         svm_type = cv2.SVM_C_SVC, 
         degree=3.43, 
         gamma=1.5e-4, 
         coef0=1e-1, 
         ) 
print 'svm train' 
svm.train(samples,labels,params=svmparams) 
svm.save(model) 
print 'done' 

result = np.float32([(svm.predict(s)) for s in samples]) 
correct=0. 
total=0. 

for i,j in zip(result,labels): 
    total+=1 
    if i==j: 
     correct+=1 
    print '%f'%(correct/total) 
+0

非常感謝您的解決方案。我認爲你注意到使用ORB和KNN的模型對於更大的訓練數據不可行是正確的。你能告訴我關於你用於sobel過濾器和SVM的參數嗎?你是從期刊論文還是自己的研究中獲得的?另外,你能告訴我爲什麼你調整圖像大小(40,40)? – samkhan13

+0

在評論數據大小時,我並沒有提及orb/knn。我指的是發佈的方法。 svm參數來自train_auto方法,在總大小約12k的情況下進行10次交叉驗證。索貝爾可能不是很科學,但直覺上我認爲它使用8的濾波器組提取8個相同的方向指令(0,45,90度等)(這不完全是它在做但是足夠接近)。這只是一個愚蠢的方式來獲取圖像中的相位信息。你可以用其他東西來代替索貝爾。那只是爲了讓你開始。調整大小40x40是爲了減少處理時間 –

+0

還有另一個重新調整(15,15)。那是因爲你需要svm的固定長度矢量,並且還有助於減少最終模型的大小。每個矢量的總大小是15x15x8(1800)。這相當大!所以當訓練數據的大小增加時,存儲的支持向量(每個1800的長度!)將炸燬存儲的模型大小。這就是爲什麼你可能需要在svm之前做pca/lda的原因。如果你需要數據,搜索'變形'。這個任務對種族特徵非常敏感,所以你可能想要確保你的訓練數據只包含一種種族(西方,非洲,亞洲等)的主題,如果可能的話 –

1

以前,我苦苦尋找ORB,SIFT,SURF等之間的差異的技術,我發現這些所謂的職位有所幫助:

最重要的是要注意的是,opencv中的這些特徵檢測算法需要單個通道(通常爲8位)的灰度圖像。

事實證明,knn.train()只能接受「陣列」與數據類型爲'32位浮點」。我相信opencv中的SVM培訓也有這個要求。在Python中,numpy數組需要在每一行中具有相同類型的數據,並且所有行都需要具有相同的形狀,而不像Python列表可以具有任何類型和大小的數據。

所以生長所述描述符列表後我轉換的列表的數組。

但是!在此之前,我硬編碼的ORB nfeatures參數爲25。我所有的訓練數據圖像是大致相同的分辨率,並且我能夠手動驗證每個圖像可使用ORB產生至少25關鍵點。每個關鍵點有32個描述符,因此25 * 32爲每個臉部圖像提供800個描述符。 ORB返回一個數組,其元素是整數類型,行數等於關鍵點的數量。我將其重新塑造成一行描述符,以產生尺寸爲800的「矢量」。

下一個挑戰是使用knn.find_nearest()。它需要一個'矩陣',其行的形狀與給予knn.train()的ndarray的行相同。不這樣做會產生一個錯誤:

OpenCV Error: Bad argument (Input samples must be floating-point matrix (<num_samples>x<var_count>)) in find_nearest 

即使你有需要被傳遞給knn.find_nearest()它需要在形狀1xm其中m是在矢量元素的數量的單一載體。

所以我不得不砍了一個粗暴的方式來檢查我的攝像頭拍攝的圖像是我硬編碼解決問題的方法中使用。

代碼看起來像現在這樣:

import cv2 
import numpy as np 

def chooseCascade(): 
    # TODO: Option for diferent cascades 
    # HAAR Classifier for frontal face 
    _cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml') 
    return _cascade 

def cropToObj(cascade,imageFile,flag): 
    if flag == 0: 
     # Load as 1-channel grayscale image 
     image = cv2.imread(imageFile,0) 
    elif flag == 1: 
     # Load as 3-channel color image 
     image = cv2.imread(imageFile,1) 
    elif flag == -1: 
     # Load image as is 
     image = cv2.imread(imageFile,-1) 
    elif flag == 2: 
     # Image is from camera 
     image = imageFile 
    else: 
     print 'improper arguments passed to cropToObj' 

    # Crop to the object of interest in the image 
    objRegion = cascade.detectMultiScale(image) # TODO: What if multiple ojbects in image? 

    x1 = objRegion[0,0] 
    y1 = objRegion[0,1] 
    x1PlusWidth = objRegion[0,0]+objRegion[0,2] 
    y1PlusHeight = objRegion[0,1]+objRegion[0,3] 

    objImage = image[y1:y1PlusHeight,x1:x1PlusWidth] 

    return objImage 

def recognizer(fileNames): 
    # ORB contructor 
    orb = cv2.ORB(nfeatures=25) 

    keyPoints = [] 
    descriptors = [] 

    # A cascade for face detection 
    haarFaceCascade = chooseCascade() 

    # Start processing images 
    for imageFile in fileNames: 
     # Find faces using the HAAR cascade 
     faceImage = cropToObj(haarFaceCascade,imageFile,flag) 

     # Extract keypoints and description 
     faceKeyPoints, faceDescriptors = orb.detectAndCompute(faceImage, mask = None) 

     #print faceDescriptors.shape 
     descRow = faceDescriptors.shape[0] 
     descCol = faceDescriptors.shape[1] 

     flatFaceDescriptors = faceDescriptors.reshape(descRow*descCol) 

     keyPoints.append(faceKeyPoints) 
     descriptors.append(flatFaceDescriptors) 

    descriptors = np.asarray(descriptors, dtype=np.float32) 

    # KNN model and training on descriptors 
    responses = [] 
    for name in fileNames: 
     if name.startswith('BF'): 
      responses.append(0) # Female 
     else: 
      responses.append(1) # Male 

    responses = np.asarray(responses) 

    knn = cv2.KNearest() 
    knnTrainSuccess = knn.train(descriptors, 
           responses, 
           isRegression = False) # isRegression = false, implies classification 

    # Obtain test face image from cam 
    capture = cv2.VideoCapture(0) 
    closeCamera = -1 
    while(closeCamera < 0): 
     retval, camImage = capture.read()  

     # Find face in camera image 
     try: 
      testFaceImage = cropToObj(haarFaceCascade, camImage, 2) # TODO: What if multiple faces? 
      testFaceImage = cv2.cvtColor(testFaceImage, cv2.COLOR_BGR2GRAY) 
     except TypeError: 
      print 'check if front face is visible to camera' 
      pass 

     # Keyponts and descriptors of test face image 
     testFaceKP, testFaceDesc = orb.detectAndCompute(testFaceImage, mask = None) 
     testDescRow = testFaceDesc.shape[0] 
     testDescCol = testFaceDesc.shape[1] 
     flatTestFaceDesc = testFaceDesc.reshape(1,testDescRow*testDescCol) 
     flatTestFaceDesc = np.asarray(flatTestFaceDesc,dtype=np.float32) 

     if flatTestFaceDesc.size == 800: 
      # Args in knn.find_nearest: testData, neighborhood 
      returnedValue, result, neighborResponse, distance = knn.find_nearest(flatTestFaceDesc,5) 
      if returnedValue == 0.0: 
       print 'Female' 
      else: 
       print 'Male' 
     else: 
      print 'insufficient size of image' 

     # Display results 
     # TODO: Overlay classification text 
     cv2.imshow("testImage", camImage) 

     closeCamera = cv2.waitKey(1) 
    cv2.destroyAllWindows() 


if __name__ == '__main__': 
    fileNames = ['BF09NES_gray.jpg', 
       'BF11NES_gray.jpg', 
       'BF13NES_gray.jpg', 
       'BF14NES_gray.jpg', 
       'BF18NES_gray.jpg', 
       'BM25NES_gray.jpg', 
       'BM26NES_gray.jpg', 
       'BM29NES_gray.jpg', 
       'BM31NES_gray.jpg', 
       'BM34NES_gray.jpg'] 

    recognizer(fileNames) 

我仍然希望有人在SO社區能幫助我,這意味着一個想法,這樣我就不必硬編碼的東西進入我的解決方案。我也懷疑knn.match_nearest()沒有做我所需要的。

正如預期的那樣,識別器並不完全準確,並且由於旋轉,照明等原因,很容易導致錯誤分類。有關改進此方法的任何建議都將得到真正的讚賞。

+0

快速評論。剛剛發現了關於弓。似乎相關。 SO回答(http://stackoverflow.com/questions/15611872/bow-in-opencv-using-precomputed-features)。並在這裏(https://groups.google.com/forum/#!topic/accord-net/u5viBhgv0Fw)它說:「視覺詞袋」僅用於一個目的和一個目的:將可變長度特徵表示轉換爲固定長度特徵表示,長度特徵表示。 ' –

+0

@ZawLin感謝您的評論。如果你粘貼它作爲答案,我可以接受它。 – samkhan13