OpenCV Java 采集人脸并训练模型,网上没有 OpenCV Java 最新训练模型的教程,经过自己看文档和一次次尝试,总结的教程。
环境
1 2 3
| OS: macOS 10.13.3 OpenCV: 3.4.0_1 IDE: IntelliJ IDEA 2017.2.6
|
步骤
第一步:准备数据
下载数据库
官网教程里表示有三个数据库可以下载,我们这里用第一种
- AT&T Facedatabase(有时也称为ORL面孔数据库)包含40个不同的人,其中每个人10种不同图像。图像是在不同的时间拍摄的,改变了照明,面部表情(开放/闭眼,微笑/不微笑)和面部细节(眼镜/没有眼镜)。所有图像都是在黑暗的均匀背景下拍摄的,拍摄对象处于直立的正面位置(对某些侧面运动具有宽容度)。
下载下来我放到了桌面 /Users/limbang/Desktop/orl_faces/
该文件夹下有 s1 到 s40 每个文件夹代表一个人。
读取图片人脸收据数据,结合前面的,就可以用摄像头来创建数据
和前面一样先检测出人脸,代码重复的我就省略了,主要就是把图片缩放成和下载的数据一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| CascadeClassifier ... 省略...
face_cascade.detectMultiScale(...
int i = 0;
for (Rect rect : faces.toArray()) { Mat faceROI = new Mat(grayImg, rect);
if (faceROI.cols() > 100) { Imgproc.resize(faceROI, faceROI, new Size(92, 112)); Imgcodecs.imwrite("/Users/limbang/Desktop/orl_faces/s42/" + i + ".pgm", faceROI); i++; } }
|
用上面那张图片试试 
第二步:训练模型
用FaceRecognizer
的train
方法来训练模型,有三种方式LBPHFaceRecognizer
EigenFaceRecognizer
FisherFaceRecognizer
,这里我们使用 LBPH算法来训练模型。
可以看到 train(List src, Mat labels) ,可以看到标签也是 Mat,这里就是难找的网上没有教程,还是用 C++生成的一份 xml 对比才知道有问题,下面的代码只用6张图片来演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| List<Mat> images = new ArrayList<>(); Mat labels = new Mat(6,1,CvType.CV_32SC1); int[] labelsArray={0, 1};
images.add(Imgcodecs.imread("/Users/limbang/Desktop/orl_faces/s1/1.pgm", Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)); labels.put(0,0,labelsArray[0]); images.add(Imgcodecs.imread("/Users/limbang/Desktop/orl_faces/s1/2.pgm", Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)); labels.put(1,0,labelsArray[0]); images.add(Imgcodecs.imread("/Users/limbang/Desktop/orl_faces/s1/3.pgm", Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)); labels.put(2,0,labelsArray[0]);
images.add(Imgcodecs.imread("/Users/limbang/Desktop/orl_faces/s2/1.pgm", Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)); labels.put(3,0,labelsArray[1]); images.add(Imgcodecs.imread("/Users/limbang/Desktop/orl_faces/s2/2.pgm", Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)); labels.put(4,0,labelsArray[1]); images.add(Imgcodecs.imread("/Users/limbang/Desktop/orl_faces/s2/3.pgm", Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE)); labels.put(5,0,labelsArray[1]);
FaceRecognizer faceRecognizer = LBPHFaceRecognizer.create();
faceRecognizer.train(images,labels);
faceRecognizer.write("/Users/limbang/Desktop/face.xml");
|
打开 XML 文件可以看到,之前是按照这个问题里面创建的模型,也和他遇到的问题一样,标签会是一个疯狂大的数值或是负数(不是预想的0和1),最后看到这篇文章里的“神经网络java+opencv2.X ” 才知道标签应该这样创建。
1 2 3 4 5 6
| <labels type_id="opencv-matrix"> <rows>6</rows> <cols>1</cols> <dt>i</dt> <data> 0 0 0 1 1 1</data></labels>
|
第三步:识别人脸
就是把前面的几部分代码结合,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| CascadeClassifier ... 省略...
face_cascade.detectMultiScale(...
FaceRecognizer faceRecognizer = LBPHFaceRecognizer.create();
faceRecognizer.read("/Users/limbang/Desktop/face.xml");
for (Rect rect : faces.toArray()) { Mat faceROI = new Mat(grayImg, rect); if (faceROI.cols() > 100) { Imgproc.resize(faceROI, faceROI, new Size(92, 112));
int[] predictedLabel = {-1}; double[] confidence = {0.0};
faceRecognizer.predict(faceROI, predictedLabel, confidence); System.out.println(String.format("预测的标签 = %s / 可信度 = %s.", Arrays.toString(predictedLabel), Arrays.toString(confidence))); } }
|
后记
遍历文件夹读取图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
private static Map<String, List<File>> traverseFile(Path path) throws IOException {
final Map<String, List<File>> images = new HashMap<>();
SimpleFileVisitor<Path> finder = new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String name = file.toString(); name = name.toLowerCase(); if (name.endsWith(".jpg") || name.endsWith(".pgm") || name.endsWith(".png")) { String paremtName = file.getParent().getFileName().toString(); if (images.get(paremtName) == null) { images.put(paremtName, new ArrayList<>()); } images.get(paremtName).add(file.toFile()); } return super.visitFile(file, attrs); } }; Files.walkFileTree(path, finder);
return images; }
|