JavaCV人脸识别三部曲之二:训练

x33g5p2x  于2021-12-30 转载在 Java  
字(4.0k)|赞(0)|评价(0)|浏览(425)

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本文是《JavaCV人脸识别三部曲》的第二篇,前文《视频中的人脸保存为图片》咱们借助摄像头为两位群众演员生成大量人脸照片,如下图,群众演员A的照片保存在E:\temp\202112\18\001*man*,B的照片保存在E:\temp\202112\18\001*woman*:

  • 照片准备好,并且每张照片的身份都已确定,本篇要做的就是用上述照片生成模型文件,今后新的人脸就可以中这个模型来检查了
  • 关于训练,可以用下图来表示,一共六张照片两个类别,训练完成后得到模型文件faceRecognizer.xml:

编码

  • 训练的代码很简单,在一个java文件中搞定吧,simple-grab-push是整个《JavaCV的摄像头实战》系列一直再用的工程,现在该工程中新增文件TrainFromDirectory.java,完整代码如下,有几处要注意的地方稍后提到:
  1. package com.bolingcavalry.grabpush.extend;
  2. import com.bolingcavalry.grabpush.Constants;
  3. import org.bytedeco.opencv.global.opencv_imgcodecs;
  4. import org.bytedeco.opencv.opencv_core.Mat;
  5. import org.bytedeco.opencv.opencv_core.MatVector;
  6. import org.bytedeco.opencv.opencv_core.Size;
  7. import org.bytedeco.opencv.opencv_face.FaceRecognizer;
  8. import org.bytedeco.opencv.opencv_face.FisherFaceRecognizer;
  9. import java.io.File;
  10. import java.io.IOException;
  11. import java.nio.IntBuffer;
  12. import java.util.LinkedList;
  13. import java.util.List;
  14. import static org.bytedeco.opencv.global.opencv_core.CV_32SC1;
  15. import static org.bytedeco.opencv.global.opencv_imgcodecs.IMREAD_GRAYSCALE;
  16. import static org.bytedeco.opencv.global.opencv_imgproc.resize;
  17. /** * @author willzhao * @version 1.0 * @description 训练 * @date 2021/12/12 18:26 */
  18. public class TrainFromDirectory {
  19. /** * 从指定目录下 * @param dirs * @param outputPath * @throws IOException */
  20. private void train(String[] dirs, String outputPath) throws IOException {
  21. int totalImageNums = 0;
  22. // 统计每个路径下的照片数,加在一起就是照片总数
  23. for(String dir : dirs) {
  24. List<String> files = getAllFilePath(dir);
  25. totalImageNums += files.size();
  26. }
  27. System.out.println("total : " + totalImageNums);
  28. // 这里用来保存每一张照片的序号,和照片的Mat对象
  29. MatVector imageIndexMatMap = new MatVector(totalImageNums);
  30. Mat lables = new Mat(totalImageNums, 1, CV_32SC1);
  31. // 这里用来保存每一张照片的序号,和照片的类别
  32. IntBuffer lablesBuf = lables.createBuffer();
  33. // 类别序号,从1开始,dirs中的每个目录就是一个类别
  34. int kindIndex = 1;
  35. // 照片序号,从0开始
  36. int imageIndex = 0;
  37. // 每个目录下的照片都遍历
  38. for(String dir : dirs) {
  39. // 得到当前目录下所有照片的绝对路径
  40. List<String> files = getAllFilePath(dir);
  41. // 处理一个目录下的每张照片,它们的序号不同,类别相同
  42. for(String file : files) {
  43. // imageIndexMatMap放的是照片的序号和Mat对象
  44. imageIndexMatMap.put(imageIndex, read(file));
  45. // bablesBuf放的是照片序号和类别
  46. lablesBuf.put(imageIndex, kindIndex);
  47. // 照片序号加一
  48. imageIndex++;
  49. }
  50. // 每当遍历完一个目录,才会将类别加一
  51. kindIndex++;
  52. }
  53. // 实例化人脸识别类
  54. FaceRecognizer faceRecognizer = FisherFaceRecognizer.create();
  55. // 训练,入参就是图片集合和分类集合
  56. faceRecognizer.train(imageIndexMatMap, lables);
  57. // 训练完成后,模型保存在指定位置
  58. faceRecognizer.save(outputPath);
  59. //释放资源
  60. faceRecognizer.close();
  61. }
  62. /** * 读取指定图片的灰度图,调整为指定大小 * @param path * @return */
  63. private static Mat read(String path) {
  64. Mat faceMat = opencv_imgcodecs.imread(path,IMREAD_GRAYSCALE);
  65. resize(faceMat, faceMat, new Size(Constants.RESIZE_WIDTH, Constants.RESIZE_HEIGHT));
  66. return faceMat;
  67. }
  68. /** * 把指定路径下所有文件的绝对路径放入list集合中返回 * @param path * @return */
  69. public static List<String> getAllFilePath(String path) {
  70. List<String> paths = new LinkedList<>();
  71. File file = new File(path);
  72. if (file.exists()) {
  73. // 列出该目录下的所有文件
  74. File[] files = file.listFiles();
  75. for (File f : files) {
  76. if (!f.isDirectory()) {
  77. // 把每个文件的绝对路径都放在list中
  78. paths.add(f.getAbsolutePath());
  79. }
  80. }
  81. }
  82. return paths;
  83. }
  84. public static void main(String[] args) throws IOException {
  85. String base = "E:\\temp\\202112\\18\\001\\";
  86. // 存储图片的两个目录
  87. // man目录下保存了群众演员A的所有人脸照片,
  88. // woman目录下保存了群众演员B的所有人脸照片
  89. String[] dirs = {base + "man", base + "woman"};
  90. // 开始训练,并指定模型输出位置
  91. new TrainFromDirectory().train(dirs, base + "faceRecognizer.xml");
  92. }
  93. }
  • 上述代码有以下几处要注意:
  1. 静态方法read用于将图片转为Mat
  2. 静态方法getAllFilePath可以遍历指定目录下的所有文件,把它们的绝对路径返回
  3. train一共获取了man和woman两个目录下的照片,man目录下的照片的类别是1,women目录下的照片类别是2
  4. 识别类是FisherFaceRecognizer,现在的训练和下一篇的识别都用这个类

执行

  • 运行main方法,待执行完成后,如下图,可见目录E:\temp\202112\18\001下已经生成模型文件faceRecognizer.xml:

  • 至此,本篇任务已完成,下一篇进入终极实战,用本篇训练的模型识别摄像头中的人脸,并把识别结果展示在预览页面上;

源码下载

  • 《JavaCV的摄像头实战》的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
名称链接备注
项目主页https://github.com/zq2599/blog_demos该项目在GitHub上的主页
git仓库地址(https)https://github.com/zq2599/blog_demos.git该项目源码的仓库地址,https协议
git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本篇的源码在javacv-tutorials文件夹下,如下图红框所示:

  • javacv-tutorials里面有多个子工程,《JavaCV的摄像头实战》系列的代码在simple-grab-push工程下:

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

相关文章

最新文章

更多