opencv为我们提供了多种机器学习方法,比如adaboost、svm、神经网络等。本文主要记录其神经网络的原理和用法(参考赵春江的“机器学习经典算法剖析基于opencv”,人民邮电出版社,214-220)
下面就以照片的方式贴出其原理:
OpenCV的人工神经网络是机器学习算法中的其中一种,使用的是多层感知器(Multi- Layer Perception,MLP),是常见的一种ANN算法。MLP算法一般包括三层,分别是一个输入层,一个输出层和一个或多个隐藏层的神经网络组成。每一层由一个或多个神经元互相连结。一个“神经元”的输出便是另一个“神经元”的输入。
OpenCV中的神经网络的训练,需要创建两个数据矩阵,一个是特征数据矩阵,一个是标签矩阵。但要注意的是标签矩阵是一个N*M的矩阵,N表示训练样本数,M是类标签。如果第i行的样本属于第j类,那么该标签矩阵的(i,j)位置为1。 OpenCV中ANN定义了CvANN_MLP类。使用ANN算法之前,必须先初始化参数,比如神经网络的层数、神经元数,激励函数、α和β。然后使用train函数进行训练,训练完成可以训练好的参数以xml的格式保存在本地文件夹。最后就可以使用predict函数来预测测试集。
如何利用opencv的神经网络api呢,很简单,可以看其官网或者按照下面的例子进行实践。
这个例子是人脸贴图利用opencv神经网络对脸部区域的位置和脸部器官位置信息进行训练,然后达到检测到脸部后通过神经网络即可定位相关器官。(这样免去了各个器官的检测,器官定位更快速)
代码说明:
先进行脸部、眼部、鼻子、嘴巴的检测(opencv提供的haar特征分类器)
都检测到后每帧的四个Rect信息存在一个文件中
读取该文件,face Rect 作为输入,其他作为输出,进行训练
源码:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
#include <iostream>
#include <fstream>
#include<string>
using namespace std;
using namespace cv;
//从眼部得到全脸 Mat eyesdata(Mat_<float>(1,3) << eyesRect.x,eyesRect.y,eyesRect.width)
cv::Mat getFace_Bpnet(char *bpxml,cv::Mat eyesdata)
{
CvANN_MLP bp; //bp网络
bp.load(bpxml);//读取模型
Mat facedata; //一组预测结果 facedata(1, 3,CV_32FC1)(Mat_<int>(1, 3) << 3,18,3)
bp.predict(eyesdata, facedata);
return facedata;
}
int test_cvNNapi(int group_data,int cmd)
{
CvANN_MLP bp; //bp网络
string datafile = "/home/jiang/Repositories/FaceDeal_Class/train_data/mydata.txt";
string testfile = "/home/jiang/Repositories/FaceDeal_Class/train_data/testdata.txt";
string resultfile = "/home/jiang/Repositories/FaceDeal_Class/train_data/resultfile.txt";
char buffer[50];
if(cmd == 1)
{ // trainning
int *IN_data = new int[3];
int *OUT_data = new int[3];
//建立一个标签矩阵
Mat labelsMat(group_data, 3, CV_32FC1);
//建立一个训练样本矩阵
Mat trainingDataMat(group_data, 3, CV_32FC1); // cols = 3 ; rows = group_data at(y,x)
fstream ifile;
int count_hang = 0;
ifile.open(datafile,ios::in);
for(int i=0;i<group_data;i++)
{
//每行格式 ,101,22,333,120,333,12,
ifile.getline(buffer, 50, '\n'); //getline(char *,int,char) 表示该行字符达到 50 个或遇到换行就结束;
int num_count = -1;
for(int j = 0; j < 50; ){
if(num_count >=5 )
break;
else if(buffer[j] == ','){
num_count++;
if(num_count < 3){
if(buffer[j+4] == ','){
IN_data[num_count] = (buffer[j+1] - '0')*100 + (buffer[j+2] - '0')*10 + (buffer[j+3] - '0')*1;
j = j+4;
}
else if (buffer[j+3] == ','){
IN_data[num_count] = (buffer[j+1] - '0')*10 + (buffer[j+2] - '0')*1;
j = j+3;
}
else if(buffer[j+2] == ','){
IN_data[num_count] = (buffer[j+1] - '0')*1;
j = j+2;
}
else
break;
}
else {
if(buffer[j+4] == ','){
OUT_data[num_count-3] = (buffer[j+1] - '0')*100 + (buffer[j+2] - '0')*10 + (buffer[j+3] - '0')*1;
j = j+4;
}
else if (buffer[j+3] == ','){
OUT_data[num_count-3] = (buffer[j+1] - '0')*10 + (buffer[j+2] - '0')*1;
j = j+3;
}
else if(buffer[j+2] == ','){
OUT_data[num_count-3] = (buffer[j+1] - '0')*1;
j = j+2;