如何使用 SVM 进行人员识别?
Posted
技术标签:
【中文标题】如何使用 SVM 进行人员识别?【英文标题】:How to use SVM for People Recognition? 【发布时间】:2012-10-23 09:27:44 【问题描述】:我正在运行 opencv 2.4.2 C++。
我正在尝试使用 opencv 进行人员识别。
我正在使用 VidTIMIT 数据集,其中包含不同方向的不同人。
我正在使用 CvSVM 对这些人进行分类。
我的问题是 svm 的输出总是一样的。
我遵循的算法是:
-
使用 Haar 进行人脸检测
调整脸部大小 (58*58)
Svm 培训
分类
现在,我想知道我是否在训练中做错了什么。
我正在尝试这种方法,考虑到 5(num_name) 个人,每个人 10(num_images) 个不同的图像。
void runFaceDetectionRecognition(vector<Mat_<uchar> > &images)
vector<vector<Rect> > faces;
for (unsigned i=0; i<images.size(); ++i)
/// detection face
vector<Rect> f;
faceDetection(images[i], f);
if (!f.empty())
faces.push_back(f);
/// I keep only the face
Mat_<uchar> roi = ( images[i](f[0]) );
/// resize
resize(roi, roi, Size(58, 58));
roi.copyTo(images[i]);
/// Set up parameters
CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::LINEAR;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
/// Set up training data
float labels[num_name][num_images];
float label = 0;
/// different label for different person
for (unsigned i=0; i<num_name; ++i)
for (unsigned j=0; j<num_images; ++j)
labels[i][j] = label;
label++;
/// labeling matrix
Mat labelsMat(num_name*num_images, 1, CV_32FC1, labels);
/// unrolling images
float data[images.size()][58*58];
for (unsigned l=0; l<images.size(); ++l)
for (unsigned i=0; i<58; ++i)
for (unsigned j=0; j<58; ++j)
data[l][j+58*i] = images[l].at<float>(i,j);
/// training matrix
Mat train((int) images.size(),58*58, CV_32FC1, data);
CvSVM svm(train, labelsMat, Mat(), Mat(), params);
/// Validation
valSVM(svm, train.rowRange(0, 1));
验证码:
void valSVM(CvSVM &svm, Mat train)
/// prediction
float response = svm.predict(train);
cout << "Response ===> " << response << " ";
/// output
if (response == 0) cout << "lea";
else if (response == 1) cout << "maria";
else if (response == 2) cout << "ramona";
else if (response == 3) cout << "teresa";
else if (response == 4) cout << "yan";
希望你能帮助我。
【问题讨论】:
【参考方案1】:这里的另一个答案是说 SVM 必须使用 PCA 才能运行是不正确的。我在没有 PCA 的 128x128 图像上使用了 SVM,并取得了很好的效果。我对 cohn-kanade 数据集做了类似的事情。下面是一些可能会有所帮助的源代码。
vector<Mat> preImages;//Fill this with your images from your dataset
vector<int> labels;//Fill this with the labels from the dataset
vector<Mat> images;
CascadeClassifier haar_cascade;
haar_cascade.load("/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml");
vector< Rect_<int> > faces;
Mat procFace;
cout << "images: " << preImages.size() << " labels: " << labels.size() << endl;
for(unsigned int i = 0; i < preImages.size(); i++)
procFace = preImages[i].clone();
//haar_cascade.detectMultiScale(procFace, faces);
haar_cascade.detectMultiScale(
procFace,
faces,
1.1,
3,
CASCADE_FIND_BIGGEST_OBJECT|CASCADE_DO_ROUGH_SEARCH,
Size(110, 110)
);
if(faces.size() > 0)
// Process face by face:
Rect face_i = faces[0];
// Crop the face from the image.
Mat face = procFace(face_i);
////You can maybe use the equalizeHist function here instead//////
face = illuminationComp(face);
//crop face
Rect cropped(face_i.width*0.18, face_i.height*0.2, int(face_i.width*0.7), int(face_i.height*0.78));
Mat Cface = face(cropped);
Mat face_resized;
resize(Cface, face_resized, Size(128, 128), 1.0, 1.0, INTER_CUBIC);
images.push_back(face_resized);
//svm parameters:
SVMParams params = SVMParams();
params.svm_type = SVM::C_SVC;
params.kernel_type = SVM::LINEAR;
params.degree = 3.43; // for poly
params.gamma = 0.00225; // for poly / rbf / sigmoid
params.coef0 = 19.6; // for poly / sigmoid
params.C = 0.5; // for CV_SVM_C_SVC , CV_SVM_EPS_SVR and CV_SVM_NU_SVR
params.nu = 0.0; // for CV_SVM_NU_SVC , CV_SVM_ONE_CLASS , and CV_SVM_NU_SVR
params.p = 0.0; // for CV_SVM_EPS_SVR
params.class_weights = NULL; // for CV_SVM_C_SVC
params.term_crit.type = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS;
params.term_crit.max_iter = 1000;
params.term_crit.epsilon = 1e-6;
if(images.size() == labels.size())
cout << "Creating SVM Classification" << endl << endl;
int rowsSize = images.size();
int trainingArea = images[0].rows * images[0].cols;
Mat trainingMat = Mat::zeros(rowsSize, trainingArea, CV_32FC1);
int counter;
for(int index = 0; index < rowsSize; index++)
counter = 0;
for(int rows = 0; rows < images[0].rows; rows++)
for(int cols = 0; cols < images[0].cols; cols++)
trainingMat.at<float>(index, counter) = images[index].at<uchar>(rows,cols);
counter++;
Mat matLabels = Mat::zeros(labels.size(),1,CV_32FC1);
for(size_t index = 0; index < labels.size(); index++)
matLabels.at<float>(index,0) = float(labels[index]);
if(trainingMat.rows == matLabels.rows)
SVM svm;
svm.train(trainingMat,matLabels,Mat(),Mat(),params);
svm.save("svm_model.yml");
【讨论】:
您是否使用原始图像进行训练?它没有特征提取部分。如果我使用 LBP 提取特征并将其用作 SVM 训练的输入呢?我是否需要将 LBP 特征转换为 LBP 直方图并进行训练? @user8430 : 提取 lpb 特征后,您需要计算相应的直方图,然后将该直方图向量传递给您的 svm 分类器。 @user8430 是的,我使用原始图像进行训练。我在训练之前做的唯一一件事就是对图像进行照明均衡和裁剪/对齐。【参考方案2】:您似乎正在用完整的 58*58 人脸训练您的 SVM。为了让 SVM 工作,您需要使用 OpenCV 中已经包含的 PCA(主成分分析)等方法来减小维度(获取主成分)。
如果您将维度从 58*58 数组减少到 n*n 数组,其中 n 是主要特征,则 SVM 的训练将仅使用主要特征,从而得到改进的解决方案。
OpenCV人脸识别的文档很多,可以入手here。
【讨论】:
【参考方案3】:我还在构建一个项目,我在其中对一个对象进行了分类。我正在使用 SVM 和 Bag of Features(BOf)/BOW 的组合。在这种方法中,首先您创建字典/码本,然后训练您的 SVM。结果相当不错。
您可以查看此链接以了解http://www.morethantechnical.com/2011/08/25/a-simple-object-classifier-with-bag-of-words-using-opencv-2-3-w-code/
【讨论】:
以上是关于如何使用 SVM 进行人员识别?的主要内容,如果未能解决你的问题,请参考以下文章