这是我当年在安卓上用的一个人脸识别的算法。主要是用了PCA降维的方式,然后用欧式距离比较下最近的人脸。
趁热来一波刚出炉的Javacv写的特征脸算法,写得不是很好,但是也是费了一小番心思做的。我知道还有很多写得不好不对的地方,望请大家指正。不多说了,知道你们想看代码。
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.*;
import org.bytedeco.javacv.JavaCV;
import org.opencv.core.Core;
import android.util.Log;
import org.opencv.core.CvType;
import org.opencv.imgproc.Imgproc;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_highgui.*;
import static org.bytedeco.javacpp.opencv_imgproc.equalizeHist;
/**
* 特征脸算法
* @author 火烫火烫的
*
*/
public class Eigenface {
private double threshold = -1; //阈值,不设置返回最相似的距离的标签
private int num_components = -1;
private PCA pca = null;
private Mat result; //经过投影后的矩阵
private Mat labels;
public double getThreshold() {
return threshold;
}
public void setThreshold(double threshold) {
this.threshold = threshold;
}
public int getNum_components() {
return num_components;
}
public void setNum_components(int num_components) {
this.num_components = num_components;
}
public Eigenface() {
}
public Eigenface(int num_components, double threshold) {
super();
this.threshold = threshold;
this.num_components = num_components;
}
/**
* 训练方法
* @param images
* @param labels
*/
public void train(MatVector images, Mat labels) {
this.labels = labels;
Mat allsamples = new Mat(); //所有样本以行存放
Mat mat;
Mat convertMat = new Mat(); //转变后的矩阵
Mat placeholder = new Mat();
for (int i = 0; i < images.size(); ++i) { //遍历每一个Mat
mat = images.get(i);
mat.reshape(1, 1).row(0).convertTo(convertMat, CV_32FC1);
allsamples.push_back(convertMat);
}
if (num_components != -1) { //说明最大维度为设置过
pca = new PCA(allsamples, placeholder, CV_PCA_DATA_AS_ROW, num_components); //pca
} else {
pca = new PCA(allsamples, placeholder, CV_PCA_DATA_AS_ROW); //pca
}
result = pca.project(allsamples);
Log.e("The size of result", result.rows() + "|" + result.cols());
}
/**
* 进行识别
* @param testImage
* @return 识别的标签,-1代表失败,-2代表没训练
*/
public int predict(Mat testImage) {
if (pca != null) {
Mat convertMat = new Mat();
testImage.reshape(1, 1).row(0).convertTo(convertMat, CV_32FC1);
IntBuffer labelsBuf = labels.getIntBuffer();
double[] distance = new double[result.rows()];
Mat testResult = pca.project(convertMat);
Log.e("The size of testResult", testResult.rows() + "|" + testResult.cols());
double min_distance = 0;
int min_i = -1;
for (int i = 0; i < result.rows(); i++){ //对每个向量进行求欧式距离
distance[i] = norm(testResult, result.row(i));
Log.e("distance", "" + distance[i]);
if (i == 0) {
min_distance = distance[0];
min_i = i;
} else if (min_distance > distance[i]){ //说明新的这个距离更小
min_distance = distance[i];
min_i = i;
}
}
int label = -1; //识别标签
for (int i = 0; i <= min_i; ++i) { //对应着取几次
if (labelsBuf.hasRemaining()) {
label = labelsBuf.get();
} else {
label = -1;
}
}
if (threshold >= 0) { //说明阈值被设置过
if (threshold > min_distance) { //是否超过阈值
return label;
} else {
return -1;
}
} else {
return label;
}
} else {
return -2;
}
}
/**
* 用来求欧式距离的
* @param mat1
* @param mat2
* @return
*/
public double norm(Mat mat1, Mat mat2) {
return cvNorm(mat1.asCvMat(), mat2.asCvMat());
}
}