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? 它可以是字符?

培训数据可以在这里找到。

我也使用opencv示例中的haarcascade_frontalface_alt.xml

现在KNN模型只给了10张图像进行训练,看看我的程序是否没有错误地通过,哪一个没有错误。

这是我的代码:

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示例不满意,所以请不要直接告诉我这些。

任何其他帮助非常感谢。 谢谢。

---编辑---

我已经尝试了几件事,并提出了一个答案。

我仍然希望社区中的某个人能够通过提出一个想法来帮助我,这样我就不必将事情硬编码到我的解决方案中。 我也怀疑knn.match_nearest()没有做我所需要的。

正如预期的那样,识别器并不完全准确,并且由于旋转,照明等原因很容易导致错误分类。关于改进这种方法的任何建议都将得到真正的赞赏。

我用来训练的数据库是:Karolinska Directed Emotional Faces


我对所描述的方法的有效性/可行性有一些怀疑。 以下是您可能需要考虑的另一种方法。 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)

以前,我一直在努力寻找ORB,SIFT,SURF等之间的技术差异,我发现这些SO帖子很有帮助:

  • https://stackoverflow.com/a/10169025/1463143
  • 对于尺度不变特征提取,有没有SURF和SIFT的快速替代方案?
  • OpenCV ORB特征检测器如何工作?
  • 最重要的是要注意的是,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)
    

    我仍然希望社区中的某个人能够通过提出一个想法来帮助我,这样我就不必将事情硬编码到我的解决方案中。 我也怀疑knn.match_nearest()没有做我所需要的。

    正如预期的那样,识别器并不完全准确,并且由于旋转,照明等原因很容易导致错误分类。关于改进这种方法的任何建议都将得到真正的赞赏。

    链接地址: http://www.djcxy.com/p/79579.html

    上一篇: Python, OpenCV: classify gender using ORB features and KNN

    下一篇: OpenCV detect image against a image set