如何使用OpenCV和面部特征训练支持向量机(SVM)分类器?

9
我想使用svm分类器进行面部表情检测。我知道opencv有一个svm api,但我不知道应该将什么作为训练分类器的输入。到目前为止,我已经阅读了很多论文,所有这些论文都说在面部特征检测后训练分类器。
到目前为止,我所做的是:
1.人脸检测,
2.每个帧中计算16个面部点。下面是面部特征检测的输出!
3.保存特征点像素地址的向量
注意:我知道如何只使用正负图像来训练SVM,我在这里看到了这段代码here,但我不知道如何将面部特征信息与其结合起来。
请问有谁能帮我开始使用svm进行分类?
a.训练分类器的样本输入应该是什么?
b.如何使用这些面部特征点训练分类器?
谢谢。

嘿,把脸上的点点还回来吧 ;) (你用的是哪个OpenCV版本?) - berak
2个回答

15

OpenCV中的机器学习算法都具有类似的接口。要进行训练,您需要传入一个NxM特征矩阵(N行,每行包含长度为M的一个特征)和一个Nx1的分类标签矩阵。如下所示:

//traindata      //trainlabels

f e a t u r e    1 
f e a t u r e    -1
f e a t u r e    1
f e a t u r e    1
f e a t u r e    -1

对于预测,您需要用相同的方式填充一个只有1行的Mat,然后它将返回预测的标签。

所以,假设您的16个面部点存储在向量中,您可以这样做:

Mat trainData; // start empty
Mat labels;

for all facial_point_vecs:
{
    for( size_t i=0; i<16; i++ )
    {
        trainData.push_back(point[i]);
    }
    labels.push_back(label); // 1 or -1
}
// now here comes the magic:
// reshape it, so it has N rows, each being a flat float, x,y,x,y,x,y,x,y... 32 element array
trainData = trainData.reshape(1, 16*2); // numpoints*2 for x,y

// we have to convert to float:
trainData.convertTo(trainData,CV_32F);

SVM svm; // params omitted for simplicity (but that's where the *real* work starts..)
svm.train( trainData, labels );


//later predict:
vector<Point> points;
Mat testData = Mat(points).reshape(1,32); // flattened to 1 row
testData.convertTo(testData ,CV_32F);
float p = svm.predict( testData );

嗨Break,感谢您的回答,但我有一个问题--我如何同时提供图像和特征点?也就是说,假设我有50个正面图像和20个负面图像,每个图像都有16个特征点,那么我该如何插入哪些特征属于哪个图像的信息?在这种情况下,我应该在trainData中推入什么?-为什么在“reshape”行中要将16乘以2? - MMH
嗯,当我开始在这里打字时,看起来你想做情感检测,比如快乐/悲伤。现在你已经编辑了几次,似乎更多地想要面部识别/人物识别,这是一个不同的问题。你能澄清一下吗? - berak
哦!我只想做情感检测。目前只有快乐和悲伤。 - MMH
啊,好的。请注意,它与图像没有任何关联(它不知道图像,只知道您的地标点)。最终它只会说“高兴”或“不高兴”。 - berak
让我们在聊天中继续这个讨论 - MMH
显示剩余4条评论

3
Face gesture recognition是一个广泛研究的问题,适当的特征需要通过对现有文献的深入研究来找到。一旦您拥有了您认为好的特征描述符,您就可以用它们来训练SVM。一旦您使用交叉验证找到了最优参数并训练了SVM,您就可以开始在未知数据上测试SVM模型并报告准确性。总的来说,这就是流程。
现在是关于SVM的部分:
SVM是一个二元分类器,它可以区分两个类(尽管也可以扩展到多个类)。OpenCV在ML库中内置了一个SVM模块。SVM类有两个函数:train(..)predict(..)。为了训练分类器,您需要输入大量的样本特征描述符以及它们的类标签(通常为-1和+1)。请记住OpenCV支持的格式:每个训练样本都必须是一个行向量。每行将在标签向量中有一个对应的类标签。因此,如果您具有长度为n的描述符,并且您有m个这样的样本描述符,则您的训练矩阵将是m x n(每个长度为nm行),标签向量的长度将为m。还有一个SVMParams对象,其中包含SVM类型和参数值(如C)等属性,您需要指定。

一旦训练完成,您可以从图像中提取特征,将其转换为单行格式,并将其传递给predict(),它会告诉您它属于哪个类别(+1或-1)。

还有一个train_auto(),具有类似的参数和格式,可为您提供SVM参数的最佳值。

还可以查看此detailed SO answer以查看示例。

编辑: 假设您有一个返回特征向量的特征描述符,算法会像这样:

Mat trainingMat, labelsMat;
for each image in training database:
  feature = extractFeatures( image[i] );
  Mat feature_row = alignAsRow( feature );
  trainingMat.push_back( feature_row );
  labelsMat.push_back( -1 or 1 );  //depending upon class.
mySvmObject.train( trainingMat, labelsMat, Mat(), Mat(), mySvmParams );

我不认为extractFeatures()alignAsRow()是现有的函数,你可能需要自己编写它们。

感谢您的回复。正如我在问题中提到的,我理论上知道我需要做什么。我知道在特征提取之后,我将不得不训练SVM分类器。此外,我知道在训练之后可以使用predict()来预测面部表情。因此,我的主要问题是如何使用这些特征点来训练svm分类器?如果您能给一些代码片段,那也会有所帮助。 - MMH
再次感谢,但我只需要提供功能吗?不需要相关的图片吗?那么如何确定哪些功能属于哪个图片呢? - MMH
2
你必须从图像中提取特征来进行训练,而在测试时,你再次提取图像特征。你训练和测试的不是图像本身,而是相应的特征。请注意,除非原始图像本身是一个特征(这种情况很少见),否则不要返回图像。 - a-Jays
如何保存已训练好的 SVM "mySvmObject"?我尝试了 SvmObject.save("abc.xml");,但不起作用 :'( - MMH
它是否给你一些错误信息?它是 mySvmObject.save(..) - a-Jays
不对,加载图片出了问题,让我检查一下。 - MMH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接