SpringBoot整合百度人脸识别SDK离线版操作步骤,Windows发布打包SpringBoot百度人脸识别SDK项目,以及解决百度人脸识别SDK离线版遇到的问题

2 篇文章 0 订阅
1 篇文章 0 订阅

前言

1、下载百度人脸识别SDK离线版。
2、开发工具:IntelliJ IDEA
百度人脸识别官网:https://cloud.baidu.com/doc/FACE/s/Ol0rre5u5
注意:最后面有基于SDK的简单人脸库实现,可添加修改人脸库,且可以分析比对分数

步骤

一、基本测试

1、解压下载的百度人脸识别SDK包(x64内带教程文档)。

在这里插入图片描述

2、IDEA中直接打开FaceOfflineSdk

3、然后引入当前项目下的opencv-jar目录下的jar包

  1. 引入点这里,idea右上角
    在这里插入图片描述

  2. 跟着操作即可,然后引入当前项目下的opencv-jar目录下的jar包
    在这里插入图片描述

  3. 一般引入后应用,在modules那边的dependencies就可以看到。

  4. 打开这个工具,然后将百度申请的16位激活码填充,点击激活后会生成license文件夹,然后复制这个文件夹替换对应的license文件。
    在这里插入图片描述

  5. 再到idea找到com.jni.face.Face.java然后执行main方法就可以开始测试了,这个类中的方法都有注释,根据需求调用就好了。

二、整合项目

创建一个项目直接FaceOfflineSdk目录下的几个文件夹复制到新项目中
在这里插入图片描述

在这里插入图片描述
同上面一样引入opencv-320.jar包即可

三、打包,及打包后遇到的问题

打包/后

  1. 打包后发布将项目下的所有dll文件放到 C:\windows\system32 或 C:\Program Files\Java\jre1.8.0_181\bin 目录下(这个目录下可以找 face_sdk.log 查看日志)。
  2. 打包后需要自己在代码上动态的将16位激活码替换license下的license.key文件,联网情况下自动激活的。
  3. 将license文件夹和models文件夹放到一个新的文件夹中,然后将这个新的文件夹路径替换下面代码中的那个模型路径。
	Face api = new Face();
    // model_path为模型文件夹路径,即models文件夹(里面存的是人脸识别的模型文件)
    String modelPath = ""; // D:\\FaceOfflineSdk\\
    int res = api.sdkInit(modelPath);
    if (res != 0) {
        System.out.printf("sdk init fail and error =%d\n", res);
        return;
    }
    
    // sdk销毁,释放内存防内存泄漏
    api.sdkDestroy();

打包遇到的问题:

  1. 打包后Windows下一直报 -4 错误(一直找不到模型)。

    解决:需要使用双斜杠,单斜杠虽然通用但是Windows下无效。(找了一天问题差点气嘎了,后面问百度技术才解决的)
    在这里插入图片描述

  2. 打包war后报 java.lang.NoClassDefFoundError: org/opencv/imgcodecs/Imgcodecs 错误。

解决:操作pom.xml,打包后没有将opencv-320.jar生成到lib下,需要改成如下

<build>
    <finalName>face-analysis-service</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <includeSystemScope>true</includeSystemScope>
            </configuration>
        </plugin>
        <!-- 打包war防止上面引入的本地jar添加到 lib-provided 文件夹中而读取不了-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <configuration>
                <webResources>
                    <resource>
                        <directory>${project.basedir}/opencv-jar</directory>
                        <targetPath>WEB-INF/lib/</targetPath>
                        <includes>
                            <include>**/*.jar</include>
                        </includes>
                    </resource>
                </webResources>
            </configuration>
        </plugin>
    </plugins>
</build>

以上即可!

其他博客参考:https://www.jianshu.com/p/f4e6de80e72a

基于百度SDK重写了人脸库用来分析比对

实现注意:
1、更改com.jni.struct.Feature对象,将这个对象加入实现序列化(因为要存到文件中)。
2、实现比较简单,利用Face.java下的几个方法实现如下逻辑
3、我这里使用了文件形式存储,也可写成数据库存储,根据需求分组查询就好;每个特征值是 512字节(0.5kb)比较小,所以存数据库也是可以的

准备一个ArrayList<Map<String, Feature>> featureList;

一、人脸校验是否是没有人脸,或者是否多人脸,如果是则返回错误;如果就一个人脸就正常进入下一步(方法:detect)。

二、校验featureList中是否存在这个人脸,如果没有则添加,有就修改。

三、将照片转成特征值(方法:faceFeature),然后添加/修改成本地文件,并添加/修改到featureList。

四、如果是删除人员照片则,删除featureList中特征值,再删除本地文件。

五、分析校验:将当前通行照片转成特征值,然后循环featureList拿出特征值比较(比较方法:compareFeature)并然后float的分数和map的key工号。

com.jni.struct.Feature对象修改如下

在这里插入图片描述

新建一个类,这里定义FaceEntity.java

package com.common.entity;

import lombok.Data;

import java.io.Serializable;

/**
 * 人脸识别分数
 *
 * @author yyq
 */
@Data
public class FaceEntity implements Serializable {

    /**
     * ID
     */
    private String id;
    /**
     * 人脸识别分数
     */
    private float score;


}

然后就是实现了,创建FaceUtil.java类

比较简单的实现,复制过去就可以用,需要注意 ImageUtil.NGINXPATH 等于 D://face_file//,把它改成自己的路径就可以了

package com.common.util;

import com.common.entity.FaceEntity;
import com.jni.face.Face;
import com.jni.face.TimeUtil;
import com.jni.struct.FaceBox;
import com.jni.struct.Feature;
import com.jni.struct.FeatureInfo;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 人脸识别自定义分析库
 *
 * @author yyq
 * @date 2023-04-01
 */
public class FaceUtil {

    /**
     * 人脸库
     */
    public static ArrayList<Map<String, Feature>> list = new ArrayList<>();
    /**
     * 人脸特征值文件存储路径
     */
    public static String filePath = "/upload/face_datebase/";
    /**
     * 人脸库加载状态:true加载完毕
     */
    public static boolean load_state = false;


    /**
     * 检测到人脸,是否只有一张
     * 0没有人脸 1有多个人脸 2只有一个人脸
     *
     * @return
     */
    public static int detect(long matAddr, int type) {
        FaceBox[] infos = Face.detect(matAddr, type);// 检测人脸
        if (infos == null) {
            return 0;
        } else if (infos.length > 1) {
            return 1;
        } else if (infos.length == 1) {
            return 2;
        }
        return 0;
    }

    /**
     * 获取人脸特征值
     *
     * @param matAddr
     * @param type
     * @return
     */
    public static Feature getFeature(long matAddr, int type) {
        FeatureInfo[] fea1List = Face.faceFeature(matAddr, type);
        if (fea1List != null && fea1List.length > 0) {
            return fea1List[0].feature;
        }
        return null;
    }

    /**
     * 添加到人脸库
     *
     * @param id
     * @param feature
     * @return
     */
    public static boolean addUser(String id, Feature feature, boolean file) {
        try {
            Map<String, Feature> featureMap = new HashMap<>();
            featureMap.put(id, feature);
            FaceUtil.list.add(featureMap);
            if (file) {// 是否存储文件
                writeObject(ImageUtil.NGINXPATH + FaceUtil.filePath, id, feature);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 修改人脸库指定下标的人脸
     *
     * @param id
     * @param feature
     * @return
     */
    public static boolean updateUser(int index, String id, Feature feature) {
        try {
            // 修改人脸库list
            FaceUtil.list.get(index).put(id, feature);
            // 重新写入
            writeObject(ImageUtil.NGINXPATH + FaceUtil.filePath, id, feature);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 添加/修改
     *
     * @param id
     * @param feature
     * @return -1添加失败 -2修改失败 0 操作失败 1添加成功 2修改成功
     */
    public static int addOrUpdateUser(String id, Feature feature) {
        try {
            int index = exists(id);
            if (index != -1) {
                boolean b = updateUser(index, id, feature);
                return b ? 2 : -2;
            } else {
                boolean b = addUser(id, feature, true);
                return b ? 1 : -1;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 删除人脸库的人脸
     *
     * @param id
     * @return
     */
    public static boolean deleteUser(String id) {
        try {
            for (int i = 0; i < FaceUtil.list.size(); i++) {
                Map<String, Feature> featureMap = FaceUtil.list.get(i);
                if (getKey(featureMap, id)) {
                    FaceUtil.list.remove(i);
                    ImageUtil.deleteFile(ImageUtil.NGINXPATH + FaceUtil.filePath + id);
                    return true;
                }
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 判断是否存在人脸库
     *
     * @param id
     * @return
     */
    public static int exists(String id) {
        try {
            for (int i = 0; i < FaceUtil.list.size(); i++) {
                Map<String, Feature> featureMap = FaceUtil.list.get(i);
                if (getKey(featureMap, id)) {
                    return i;
                }
            }
            return -1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 获取判断map中是否有这个key
     *
     * @param map
     * @param key
     * @return
     */
    public static boolean getKey(Map<String, Feature> map, String key) {
        for (Map.Entry<String, Feature> m : map.entrySet()) {
            if (m.getKey().equals(key)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 比较人脸
     *
     * @param path
     * @return
     */
    public static FaceEntity compareFace(String path) {
        try {
            // 如果库里没有特征值
            if (FaceUtil.list.size() <= 0) {
                return null;
            }
            Mat mat = Imgcodecs.imread(path);// 开始将路径图片转成特征值
            long matAddr = mat.getNativeObjAddr();
            int type = 0;// type 0: 表示rgb生活照特征值,1:表示rgb证件照特征值 2:表示nir近红外特征值
            FeatureInfo[] fea1List = Face.faceFeature(matAddr, type);
            if (fea1List == null || fea1List.length <= 0) {
                return null;
            }
            // 循环库
            FaceEntity faceEntity = new FaceEntity();
            float maxScore = 0; // 最大分数
            String maxId = null; // 最大分数的ID
            for (Map<String, Feature> featureMap : FaceUtil.list) {
                for (Map.Entry<String, Feature> m : featureMap.entrySet()) {
                    Feature feature = m.getValue();
                    for (FeatureInfo info : fea1List) {
                        float score = Face.compareFeature(feature, info.feature, type);// 比较特征值
                        if (score > maxScore) {
                            maxScore = score;
                            maxId = m.getKey();
                        }
                    }
                }
            }
            faceEntity.setId(maxId);
            faceEntity.setScore(maxScore);
            return faceEntity;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 初始化人脸库list
     */
    public static void initList() {
        try {
            String path = ImageUtil.NGINXPATH + FaceUtil.filePath;
            File file = new File(path);
            if (!file.exists()) {
                return;
            }
            File[] files = file.listFiles();
            for (File f : files) {
                String id = f.getName();
                Feature feature = (Feature) readObject(path + id);
                addUser(id, feature, false);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 写入文件
     *
     * @param path
     * @param obj
     * @throws IOException
     */
    public static void writeObject(String path, String fileName, Object obj) throws IOException {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        File f = new File(path + fileName);
        if (f.exists()) {
            ImageUtil.deleteFile(path + fileName);
            f = new File(path + fileName);
        }
        FileOutputStream out = new FileOutputStream(f);
        ObjectOutputStream objwrite = new ObjectOutputStream(out);
        objwrite.writeObject(obj);
        objwrite.flush();
        objwrite.close();
    }

    /**
     * 读取文件
     *
     * @param path
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Object readObject(String path) throws IOException, ClassNotFoundException {
        FileInputStream in = new FileInputStream(path);
        ObjectInputStream objread = new ObjectInputStream(in);
        Object obj = objread.readObject();
        objread.close();
        return obj;
    }


    public static void main(String[] args) {
        //sdk初始化
        Face api = new Face();
        // model_path为模型文件夹路径,即models文件夹(里面存的是人脸识别的模型文件)
        // 传空为采用默认路径,若想定置化路径,请填写全局路径如:d:\\face (models模型文件夹目录放置后为d:\\face\\models)
        // 若模型文件夹采用定置化路径,则激活文件(license.ini, license.key)也可采用定制化路径放置到该目录如d:\\face\\license
        // 亦可在激活文件默认生成的路径
        String modelPath = ""; // F:\Development-Tool\IDEA-SVN\FaceOfflineSdk\models
        int res = api.sdkInit(modelPath);
        if (res != 0) {
            System.out.printf("sdk init fail and error =%d\n", res);
            return;
        }
        long begin = TimeUtil.getTimeStamp();


        /**
         *
         * 加载人脸库
         *
         */
        // 初始化人脸库
        initList();
        System.out.println("---------------------人脸库总数:" + list.size());


        /**
         *
         * 新增图片到人脸库
         *
         *
         */
        // 开始将路径图片转成特征值
        Mat mat = Imgcodecs.imread("images/1.jpg");
        long matAddr = mat.getNativeObjAddr();
        int type = 0;// type 0: 表示rgb生活照特征值,1:表示rgb证件照特征值 2:表示nir近红外特征值

        int detect = detect(matAddr, type);
        if (detect == 0) {
            System.out.println("---------------------没有检测到人脸");
        } else if (detect == 1) {
            System.out.println("---------------------检测到多张人脸");
        } else {
            System.out.println("---------------------检测到一张人脸");
            Feature feature = getFeature(matAddr, type);
            if (feature != null) {
                int i = addOrUpdateUser("101", feature);
                if (i == 0) {
                    System.out.println("---------------------【人脸注册异常】");
                } else if (i == -1) {
                    System.out.println("---------------------【人脸添加失败】");
                } else if (i == -2) {
                    System.out.println("---------------------【人脸修改失败】");
                } else if (i == 1) {
                    System.out.println("---------------------【人脸添加成功】");
                } else if (i == 2) {
                    System.out.println("---------------------【人脸修改成功】");
                }
            } else {
                System.out.println("---------------------获取人脸特征值失败");
            }
        }

        long end = TimeUtil.getTimeStamp();
        System.out.println("新增耗时 ----------- :" + (end - begin));


        begin = TimeUtil.getTimeStamp();
        // 图片路径人脸比对
        FaceEntity faceEntity = compareFace(ImageUtil.NGINXPATH + "1.jpg"); // ImageUtil.NGINXPATH 等于 D://face_file//
        if (faceEntity != null) {
            System.out.println("---------------------[人脸比对返回] id:" + faceEntity.getId() + "  分数:" + faceEntity.getScore());
        } else {
            System.out.println("---------------------[人脸比对-人脸库为空]");
        }
        end = TimeUtil.getTimeStamp();
        System.out.println("比对耗时 ----------- :" + (end - begin));

        // sdk销毁,释放内存防内存泄漏
        api.sdkDestroy();


    }


}

执行人脸注册的时候服务突然关闭/停止问题解决

问题:通过接口注册人脸照片到人脸库的时候,突然服务就停止了!
解决:由于百度算法不支持主线程操作,所以我们写个子线程就好了。

如下
声明子线程处理

package com.common.thread;

import com.common.util.FaceUtil;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;

import java.util.concurrent.Callable;

public class FaceDetectTask implements Callable<Integer> {

    /** 人脸照片地址*/
    private String path;

    public FaceDetectTask(String path) {
        this.path=path;
    }

    /** 人脸检测校验*/
    @Override
    public Integer call() throws Exception {
        Mat mat = Imgcodecs.imread(path);
        if (mat.empty()) {
            System.out.println("image not exist or empty");
            return 0;
        }
        long matAddr = mat.getNativeObjAddr();
        // type 0: 表示rgb 人脸检测 1:表示nir人脸检测
        int type = 0;
        return FaceUtil.detect(matAddr, type);// 这个是上面自定义封装的
    }

}

调用

	// 人脸检测
    FaceDetectTask detectTask = new FaceDetectTask(path);// path图片路径
    Future<Integer> future = pool.submit(detectTask);
    Integer detect = 0;
    while (true){// 防止没有执行完返回空值
        if (future.isDone()){
            detect = future.get();
            break;
        }
    }
    if (detect == 0) {
        remarks = "照片没有检测到人脸-注册失败";
    } else if (detect == 1) {
        remarks = "照片检测到多张人脸-注册失败";
    } else {
    	// 逻辑处理
    }

其他

定制化说明

检测多人脸

一张照片多人脸检测,默认只会返回一条记录,这个时候就需要定制化了。
1、将SDK目录下的conf文件夹名称改为config;
2、然后api.sdkInit(“F:\SDK”),初始化模型路径需要改成绝对路径,不然不生效,不知道为啥不能用空字符串(也就是当前目录)
3、根据对应使用修改config目录下的json文件的值(参考官方文档“能力定制化说明”)

模型路径指向

把models文件夹丢到F:\SDK,初始化的时候使用:api.sdkInit(“F:\SDK”)

激活指向

把license文件夹丢到F:\SDK,初始化的时候使用:api.sdkInit(“F:\SDK”)

说明
1、将这三个文件包丢到同一个目录下,然后初始化的时候一步加载完成
2、Windows下文件夹路径使用双斜杠,这里只显示单斜杠而已
3、如果觉得项目下太多文件,可以把config、models、license这三个文件夹单独拎出来放到一个文件夹里(如,放到“F:\SDK”下,像上面那样初始化设置路径即可);还有dll文件开发时可以把全部丢到 C:\Program Files\Java\jdk1.8.0_181\bin 下,这样就看起来整洁了许多,dll发布到时候再丢到 C:\Program Files\Java\jre1.8.0_181\bin 即可

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值