Appearance
在安卓手机上实现人脸检测和人脸识别功能。
安装
花了一整天才发现的正确安装姿势:
开发环境:macOS 10.12 + Android Studio 2.3.1
groovy
compile 'org.bytedeco:javacv:1.3.1'
compile 'org.bytedeco.javacpp-presets:opencv:3.1.0-1.3:android-arm'
compile 'org.bytedeco.javacpp-presets:opencv:3.1.0-1.3:android-x86'
compile 'org.bytedeco.javacpp-presets:ffmpeg:3.2.1-1.3:android-arm'
compile 'org.bytedeco.javacpp-presets:ffmpeg:3.2.1-1.3:android-x86'
// 为了能够直接在电脑上进行单元测试
testCompile 'org.bytedeco.javacpp-presets:opencv:3.1.0-1.3:macosx-x86_64'
testCompile 'org.bytedeco.javacpp-presets:ffmpeg:3.2.1-1.3:macosx-x86_64'
其他的各种抄来抄去的文章、文档(包括官方文档)、什么手动拷包添加依赖啊、解压出 so 文件的啊都是浮云🙄️
人脸检测
java
private void faceDetect(String imageFilePath, String cascadeFilePath) {
// 图片预处理
Mat image = imread(imageFilePath);
Mat grayImage = new Mat();
cvtColor(image, grayImage, COLOR_BGR2GRAY); // 变成灰度图像
equalizeHist(grayImage, grayImage); // 直方图均衡,增加对比度以提高识别率
// 使用默认配置检测
CascadeClassifier faceDetector = new CascadeClassifier(cascadeFilePath);
RectVector faceDetections = new RectVector();
faceDetector.detectMultiScale(grayImage, faceDetections);
System.out.println(String.format("Detected %s faces", faceDetections.size()));
File file = new File(xmlPaths[0]);
String dirPath = file.getParentFile().getAbsolutePath();
// 给每张脸画框,并保存脸文件
for (int i = 0; i < faceDetections.size(); i++) {
Rect rect = faceDetections.get(i);
saveFace(image, rect, dirPath + "/face-" + i + "-" + file.getName());
opencv_core.Point point = new opencv_core.Point(rect.x(), rect.y());
opencv_core.Point point1 = new opencv_core.Point(
(rect.x() + rect.width()), rect.y() + rect.height());
rectangle(image, point, point1, new opencv_core.Scalar(0, 255, 0, 0));
}
String newFile = dirPath + "/faces-" + file.getName();
System.out.println(String.format("Writing %s", newFile));
imwrite(newFile, image);
}
private void saveFace(Mat image, Rect rect, String filePath) {
Mat sub = image.rowRange(rect.y(), rect.y() + rect.height())
.colRange(rect.x(), rect.x() + rect.width());
Mat mat = new Mat();
opencv_core.Size size = new opencv_core.Size(100, 100);
resize(sub, mat, size);
return imwrite(filePath, mat);
}
人脸识别
java
/**
* 训练人脸识别器
* @param dirPath 待训练的文件夹,文件命名按 数字分组-文件名 规则。
* 比如:1-alan.jpg 1-alan2.jpg 2-tom.jpg
* @param recognizerFilePath 备份训练结果,避免每次都重新训练
* @return 人脸识别器
*/
private FaceRecognizer trainRecognizer(String dirPath, String recognizerFilePath) {
// 获取所有训练文件
File root = new File(dirPath);
FilenameFilter imgFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
name = name.toLowerCase();
return name.endsWith(".jpg") ||
name.endsWith(".pgm") || name.endsWith(".png");
}
};
File[] imageFiles = root.listFiles(imgFilter);
MatVector images = new MatVector(imageFiles.length);
Mat labels = new Mat(imageFiles.length, 1, CV_32SC1);
IntBuffer labelsBuf = labels.createBuffer();
int counter = 0;
for (File image : imageFiles) {
Mat img = imread(image.getAbsolutePath(), CV_LOAD_IMAGE_GRAYSCALE);
int label = Integer.parseInt(image.getName().split("-")[0]);
images.put(counter, img);
labelsBuf.put(counter, label);
counter++;
}
// createFisherFaceRecognizer
// createEigenFaceRecognizer
// createLBPHFaceRecognizer 不要求图片尺寸一样
FaceRecognizer faceRecognizer = createEigenFaceRecognizer();
faceRecognizer.train(images, labels);
faceRecognizer.save(recognizerFilePath);
// faceRecognizer.load(recognizerFilePath);
// faceRecognizer.update(images, labels);
return faceRecognizer;
}
/**
* 开始识别
* @param faceRecognizer 人脸识别器
* @param filePath 待识别文件
*/
void faceRecognize(FaceRecognizer faceRecognizer, String filePath) {
Mat testImage = imread(filePath, CV_LOAD_IMAGE_GRAYSCALE);
IntPointer label = new IntPointer(1);
DoublePointer confidence = new DoublePointer(1);
faceRecognizer.predict(testImage, label, confidence);
int predictedLabel = label.get(0);
System.out.println("Predicted label: " + predictedLabel);
}
其他问题
加载图片或文件、文件夹的时候可能报错
OpenCV Error: Assertion failed (scn == 3 || scn == 4) in cvtColor
文件路径不对,试试用绝对路径。
特征分类器文件 cascade.xml 加载失败
两种方式能完成加载
- new CascadeClassifier(xmlPath);
- 使用 2.4.9 版本的文件 OpenCV 自带的特征分类器文件下载
安装完成之后构建失败
java.lang.UnsatisfiedLinkError: 又臭又长 couldn't find "libjniopencv_core.so"
虽然不知道为什么,但按照👆的正确姿势重新安装就解决了。
cvLoadImage(filename) 永远为空
给存储权限!!!给存储权限!!!给存储权限!!!
Android 6.0 以后有些权限是要在代码里请求的,单单在配置文件里写是不够的!!!