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


    欢迎访问我的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,完整代码如下,有几处要注意的地方稍后提到:
    package com.bolingcavalry.grabpush.extend;
    
    import com.bolingcavalry.grabpush.Constants;
    import org.bytedeco.opencv.global.opencv_imgcodecs;
    import org.bytedeco.opencv.opencv_core.Mat;
    import org.bytedeco.opencv.opencv_core.MatVector;
    import org.bytedeco.opencv.opencv_core.Size;
    import org.bytedeco.opencv.opencv_face.FaceRecognizer;
    import org.bytedeco.opencv.opencv_face.FisherFaceRecognizer;
    
    import java.io.File;
    import java.io.IOException;
    import java.nio.IntBuffer;
    import java.util.LinkedList;
    import java.util.List;
    
    import static org.bytedeco.opencv.global.opencv_core.CV_32SC1;
    import static org.bytedeco.opencv.global.opencv_imgcodecs.IMREAD_GRAYSCALE;
    import static org.bytedeco.opencv.global.opencv_imgproc.resize;
    
    /**
     * @author willzhao
     * @version 1.0
     * @description 训练
     * @date 2021/12/12 18:26
     */
    public class TrainFromDirectory {
    
        /**
         * 从指定目录下
         * @param dirs
         * @param outputPath
         * @throws IOException
         */
        private void train(String[] dirs, String outputPath) throws IOException {
            int totalImageNums = 0;
    
            // 统计每个路径下的照片数,加在一起就是照片总数
            for(String dir : dirs) {
                List files = getAllFilePath(dir);
                totalImageNums += files.size();
            }
    
            System.out.println("total : " + totalImageNums);
    
            // 这里用来保存每一张照片的序号,和照片的Mat对象
            MatVector imageIndexMatMap = new MatVector(totalImageNums);
    
            Mat lables = new Mat(totalImageNums, 1, CV_32SC1);
    
            // 这里用来保存每一张照片的序号,和照片的类别
            IntBuffer lablesBuf = lables.createBuffer();
    
            // 类别序号,从1开始,dirs中的每个目录就是一个类别
            int kindIndex = 1;
    
            // 照片序号,从0开始
            int imageIndex = 0;
    
            // 每个目录下的照片都遍历
            for(String dir : dirs) {
                // 得到当前目录下所有照片的绝对路径
                List files = getAllFilePath(dir);
    
                // 处理一个目录下的每张照片,它们的序号不同,类别相同
                for(String file : files) {
                    // imageIndexMatMap放的是照片的序号和Mat对象
                    imageIndexMatMap.put(imageIndex, read(file));
                    // bablesBuf放的是照片序号和类别
                    lablesBuf.put(imageIndex, kindIndex);
                    // 照片序号加一
                    imageIndex++;
                }
    
                // 每当遍历完一个目录,才会将类别加一
                kindIndex++;
            }
    
            // 实例化人脸识别类
            FaceRecognizer faceRecognizer = FisherFaceRecognizer.create();
            // 训练,入参就是图片集合和分类集合
            faceRecognizer.train(imageIndexMatMap, lables);
            // 训练完成后,模型保存在指定位置
            faceRecognizer.save(outputPath);
            //释放资源
            faceRecognizer.close();
        }
    
        /**
         * 读取指定图片的灰度图,调整为指定大小
         * @param path
         * @return
         */
        private static Mat read(String path) {
            Mat faceMat = opencv_imgcodecs.imread(path,IMREAD_GRAYSCALE);
            resize(faceMat, faceMat, new Size(Constants.RESIZE_WIDTH, Constants.RESIZE_HEIGHT));
            return faceMat;
        }
    
        /**
         * 把指定路径下所有文件的绝对路径放入list集合中返回
         * @param path
         * @return
         */
        public static List getAllFilePath(String path) {
            List paths = new LinkedList<>();
    
            File file = new File(path);
    
            if (file.exists()) {
                // 列出该目录下的所有文件
                File[] files = file.listFiles();
    
                for (File f : files) {
                    if (!f.isDirectory()) {
                        // 把每个文件的绝对路径都放在list中
                        paths.add(f.getAbsolutePath());
                    }
                }
            }
    
            return paths;
        }
    
        public static void main(String[] args) throws IOException {
    
            String base = "E:\\temp\\202112\\18\\001\\";
    
            // 存储图片的两个目录
            // man目录下保存了群众演员A的所有人脸照片,
            // woman目录下保存了群众演员B的所有人脸照片
            String[] dirs = {base + "man", base + "woman"};
    
            // 开始训练,并指定模型输出位置
            new TrainFromDirectory().train(dirs, base + "faceRecognizer.xml");
        }
    }
    
    • 上述代码有以下几处要注意:
    1. 静态方法read用于将图片转为Mat
    2. 静态方法getAllFilePath可以遍历指定目录下的所有文件,把它们的绝对路径返回
    3. train一共获取了man和woman两个目录下的照片,man目录下的照片的类别是1,women目录下的照片类别是2
    4. 识别类是FisherFaceRecognizer,现在的训练和下一篇的识别都用这个类

    执行

    • 运行main方法,待执行完成后,如下图,可见目录E:\temp\202112\18\001下已经生成模型文件faceRecognizer.xml
      在这里插入图片描述
    • 至此,本篇任务已完成,下一篇进入终极实战,用本篇训练的模型识别摄像头中的人脸,并把识别结果展示在预览页面上;

    源码下载

    名称 链接 备注
    项目主页 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工程下:
      在这里插入图片描述

    欢迎关注博客园:程序员欣宸

    学习路上,你不孤单,欣宸原创一路相伴...

  • 相关阅读:
    PermissionError: [Errno 13] Permission denied: ‘data.csv‘
    题目0116-聚餐地点
    【uniapp】华为APP真机运行(novas系列)
    记录一次较为完整的服务打包发布流程
    【数论】博弈论 —— nim游戏
    docker方式启动一个java项目-Nginx本地有代码,并配置反向代理
    阿里云无影云电脑使用初体验:真的好用吗?
    【安装部署】DataEase 版本回退
    【三维地图】开发攻略 —— 详解“GeoJSON”技术和应用场景
    赴日开发工程师是做什么的?
  • 原文地址:https://www.cnblogs.com/bolingcavalry/p/17513059.html