1.参考java api中的File的用法
从目录中读取图片的名字,获取训练图片的名字
ArrayList
File file = new File(filePath);
if (file.isDirectory()) {
String[] fileName = file.list();
for (int i = 0; i < fileName.length; i++) {
imageList.add(filePath+"/"+fileName[i]);
}
}else {
return null;
}
2.加载训练图片
IpIImage是openCV库中一个很重要的结构体,库中的我图像都是保存为这个结构体后再进行操作的。用cvLoadImage来获得。如cvLoadImage(image[i], 0);
CvMat* cvCreateMat(int rows, int cols, int type); type: 矩阵元素类型. 格式为CV_
// 将图像数据转换为矩阵保存
CvMat mat = cvCreateMat(image.height(),image.width(),CV_32FC1);
cvConvert(image, mat);
PCA(principal component analysis)翻译过来就是主分量分析,是一种流行的数据降维方法。通过数据降维可以实现数据的压缩,同时方便数据分析和提高算法的处理速度。PCA的原理就是通过正交变换,最大化样本协方差阵的对角元素,最小化非对角元素。
在OPENCV中使用PCA非常简单,只要几条语句就可以了。
1、初始化数据
//每一行表示一个样本
CvMat* pData = cvCreateMat( 总的样本数, 每个样本的维数, CV_32FC1 );
CvMat* pMean = cvCreateMat(1, 样本的维数, CV_32FC1);
//pEigVals中的每个数表示一个特征值
CvMat* pEigVals = cvCreateMat(1, min(总的样本数,样本的维数), CV_32FC1);
//每一行表示一个特征向量
CvMat* pEigVecs = cvCreateMat( min(总的样本数,样本的维数), 样本的维数, CV_32FC1);
2、PCA处理,计算出平均向量pMean,特征值pEigVals和特征向量pEigVecs
cvCalcPCA( pData, pMean, pEigVals, pEigVecs, CV_PCA_DATA_AS_ROW );
3、选出前P个特征向量(主成份),然后投影,结果保存在pResult中,pResult中包含了P个系数
CvMat* pResult = cvCreateMat( 总的样本数, PCA变换后的样本维数(即主成份的数目), CV_32FC1 );
cvProjectPCA( pData, pMean, pEigVecs, pResult );
计算mat间的欧式距离:
double[] num = new double[trainData.rows()];
// 获取测试样本特征向量与训练样本特征矩阵每一行的欧式距离
for (int i = 0; i < trainData.rows(); i++) {
double sum = 0;
for (int j = 0; j < trainData.cols(); j++) {
sum += Math.pow(Math.abs(trainData.get(i, j))
- Math.abs(testData.get(0, j)), 2);
}
num[i] = Math.sqrt(sum);
}
显示图像:
cvShowImage("result", image);
vWaitKey()函数的功能是是程序暂停,等待用户触发一个按键操作。但如果该函数参数设为一个正数,则程序将暂停一段时间,时间长为该整数值个毫秒单位,然后继续执行程序,即使用户没有按下任何键。
代码:
5.3.1 FaceDecetion类的内容
// 待训练的数据
private CvMat trainImagesRow;
// 待识别的数据
private CvMat testImagesRow;
// 降维后的特征矩阵
private CvMat result;
// 测试样本得到的特征向量
private CvMat result2;
private CvMat avg;
private CvMat eigenVectors;
// 训练样本图像路径的集合
private ArrayList
public FaceDecetion() {
// 初始化数据
this.trainImagesRow = null;
this.testImagesRow = null;
this.result = new CvMat();
this.result2 = new CvMat();
this.avg = new CvMat();
this.eigenVectors = new CvMat();
this.photos = new ArrayList
}
5.3.2 加载有训练样本
获取train文件夹下的训练图片路径
调用方法:
// train为训练样品保存路径
String[] photos = FileUtil.readImageFromDir(“.//train”);
实现方法:
public static String[] readImageFromDir(String filePath) {
ArrayList
File file = new File(filePath);
if (file.isDirectory()) {
// 获取文件夹下文件的名字
String[] fileName = file.list();
for (int i = 0; i < fileName.length; i++) {
imageList.add(filePath+"/"+fileName[i]);
}
}else {
return null;
}
显示结果如:
.//train/orl_039_005.bmp
5.3.3 获取训练样本特征矩阵
调用方法:
doPCA()
实现方法:
public void doPCA() {
avg = cvCreateMat(1, trainImagesRow.cols(), CV_32FC1);
// 训练特征向量
CvMat eigenValues = cvCreateMat(1,
Math.min(trainImagesRow.rows(), trainImagesRow.cols()),
CV_32FC1);
eigenVectors = cvCreateMat(
Math.min(trainImagesRow.rows(), trainImagesRow.cols()),
trainImagesRow.cols(), CV_32FC1);
// 取特征向量的前P个特征值,作为比较结果
result = cvCreateMat(trainImagesRow.rows(),
Math.min(trainImagesRow.rows(), trainImagesRow.cols()),
CV_32FC1);
cvCalcPCA(trainImagesRow, avg, eigenValues, eigenVectors,
CV_PCA_DATA_AS_ROW);
// 生成训练样本特征矩阵
cvProjectPCA(trainImagesRow, avg, eigenVectors, result);
}
5.3.4 加载测试样本图片文件
调用方法:
loadTestImageData(".//test//orl_003_009.bmp");
实现方法:
public void loadTestImageData(String imagePath) {
// 根据图像文件的路径,将文件加载为IplImage类型
IplImage image = cvLoadImage(imagePath, 0);
// 根据image生成同样大小的测试样本矩阵
testImagesRow = cvCreateMat(1, image.width() * image.height(), CV_32FC1);
// 将图像数据保存为CvMat后加载至测试样本矩阵中
CvMat mat = cvCreateMat(image.height(), image.width(), CV_32FC1);
cvConvert(image, mat);
int index = 0;
for (int i = 0; i < mat.rows(); i++) {
for (int j = 0; j < mat.cols(); j++) {
testImagesRow.put(0, index, mat.get(i, j));
index++;
}
}
// 按照测试样本的大小,生成测试样本特征向量的大小
result2 = cvCreateMat(testImagesRow.rows(), result.cols(), CV_32FC1);
// 生成测试样本特征向量
cvProjectPCA(testImagesRow, avg, eigenVectors, result2);
}
5.3.5 获取欧式距离
调用方法:
euclideanDistance(result, result2)
实现方法:
public int euclideanDistance(CvMat trainData, CvMat testData) {
double[] num = new double[trainData.rows()];
// 获取测试样本特征向量与训练样本特征矩阵每一行的欧式距离
for (int i = 0; i < trainData.rows(); i++) {
double sum = 0;
for (int j = 0; j < trainData.cols(); j++) {
sum += Math.pow(Math.abs(trainData.get(i, j))
- Math.abs(testData.get(0, j)), 2);
}
num[i] = Math.sqrt(sum);
}
// 获取欧氏距离的和中最小的类的序号
return minArrayElement(num);
}
5.3.6 显示识别结果
调用方法:
faceDecetion.showResult(faceDecetion.photos, testImage);
实现方法:
public void showResult(ArrayList
// 取得是结果的类序号
int num = euclideanDistance(result, result2);
// 按类序号乘以类中图片的数量,获取识别结果的图像路径,显示图像
cvShowImage("result", cvLoadImage(photos.get(num * 5)));
// 根据测试样本图像的路径,显示图像
cvShowImage("src", cvLoadImage(testImage));
cvWaitKey(500000);
}
5.3 测试过程
mian函数调用流程:
FaceDecetion faceDecetion = new FaceDecetion();
// 项目根目录下的train文件夹中,保存有训练样本orl图像文件200张
String str1 = ".//train";
String[] photos = FileUtil.readImageFromDir(str1);
// 加载训练图片
faceDecetion.loadTrainImageData(photos);
// 显示识别结果
faceDecetion.doPCA();
// 测试图片
String testImage = ".//test//orl_003_009.bmp";
faceDecetion.loadTestImageData(testImage);
// 显示识别结果
faceDecetion.showResult(faceDecetion.photos, testImage);
// 将训练样本的特征矩阵写入文本文件中
FileUtil.writeMatToFile(faceDecetion.getResult(),".//data//cvMat.txt");