从零玩转人脸识别验证3

简介: 从零玩转人脸识别验证

一、编写人脸识别业务接口

在service目录下创建-> FaceEngineService

package top.yangbuyi.service;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.toolkit.ImageInfo;
import top.yangbuyi.dto.FaceUserInfo;
import top.yangbuyi.dto.ProcessInfo;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
 * 人脸识别接口
 */
public interface FaceEngineService {
    /**
     * 人脸检测
     * @param imageInfo
     * @return
     */
    List<FaceInfo> detectFaces (ImageInfo imageInfo);
    /**
     * 提取年龄-性别
     * @param imageInfo
     * @return
     */
    List<ProcessInfo> process (ImageInfo imageInfo);
    /**
     * 人脸特征
     * @param imageInfo
     * @return
     */
    byte[] extractFaceFeature (ImageInfo imageInfo) throws InterruptedException;
    /**
     * 人脸比对
     * @param groupId
     * @param faceFeature
     * @return
     */
    List<FaceUserInfo> compareFaceFeature (byte[] faceFeature, Integer groupId) throws InterruptedException, ExecutionException;
}

service.impl 包下创建 FaceEngineServiceImpl 实现类

import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.toolkit.ImageInfo;
import com.google.common.collect.Lists;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import top.yangbuyi.dto.FaceUserInfo;
import top.yangbuyi.dto.ProcessInfo;
import top.yangbuyi.factory.FaceEngineFactory;
import top.yangbuyi.mapper.SysUserFaceInfoMapper;
import top.yangbuyi.service.FaceEngineService;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
 * @author yby6.com
 */
@Service
public class FaceEngineServiceImpl implements FaceEngineService {
    public final static Logger logger = LoggerFactory.getLogger(FaceEngineServiceImpl.class);
    @Value("${config.sdk-lib-path}")
    public String sdkLibPath;
    @Value("${config.app-id}")
    public String appId;
    @Value("${config.sdk-key}")
    public String sdkKey;
    @Value("${config.thread-pool-size}")
    public Integer threadPoolSize;
    /**
     * 人脸识别引擎
     */
    private static FaceEngine faceEngine;
    /**
     * 相似度
     */
    private final Integer passRate = 95;
    private ExecutorService executorService;
    @Autowired
    private SysUserFaceInfoMapper userFaceInfoMapper;
    /**
     * 线程池
     */
    private GenericObjectPool<FaceEngine> faceEngineObjectPool;
    /**
     * 项目启动时初始化线程池与人脸识别引擎配置
     */
    @PostConstruct
    public void init () {
        executorService = Executors.newFixedThreadPool(threadPoolSize);
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxIdle(threadPoolSize);
        poolConfig.setMaxTotal(threadPoolSize);
        poolConfig.setMinIdle(threadPoolSize);
        poolConfig.setLifo(false);
        //引擎配置
        EngineConfiguration engineConfiguration = new EngineConfiguration();
        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
        //功能配置 对应的功能请查看文档
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
        functionConfiguration.setSupportAge(true);
        functionConfiguration.setSupportFace3dAngle(true);
        functionConfiguration.setSupportFaceDetect(true);
        functionConfiguration.setSupportFaceRecognition(true);
        functionConfiguration.setSupportGender(true);
        functionConfiguration.setSupportLiveness(true);
        functionConfiguration.setSupportIRLiveness(true);
        engineConfiguration.setFunctionConfiguration(functionConfiguration);
        // 底层库算法对象池
        faceEngineObjectPool = new GenericObjectPool(new FaceEngineFactory(sdkLibPath, appId, sdkKey, engineConfiguration), poolConfig);
        try {
            faceEngine = faceEngineObjectPool.borrowObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 确保精度
     * @param value
     * @return
     */
    private int plusHundred (Float value) {
        BigDecimal target = new BigDecimal(value);
        BigDecimal hundred = new BigDecimal(100f);
        return target.multiply(hundred).intValue();
    }
}

人脸识别逻辑

1、必须进行人脸特征获取 -> 特征获取成功 -> 进入人脸对比 -> 人脸检测 -> 返回人脸数据

2、必须进行人脸特征获取 -> 特征获取失败 -> 直接跳出返回 未检出到人脸

编写人脸特征获取逻辑

/**
 * 人脸特征
 *
 * @param imageInfo
 * @return
 */
@Override
public byte[]extractFaceFeature(ImageInfo imageInfo)throws InterruptedException{
        FaceEngine faceEngine=null;
        try{
        //获取引擎对象
        faceEngine=faceEngineObjectPool.borrowObject();
        //人脸检测得到人脸列表
        List<FaceInfo> faceInfoList=new ArrayList<FaceInfo>();
        //人脸检测
        int i=faceEngine.detectFaces(imageInfo.getImageData(),imageInfo.getWidth(),imageInfo.getHeight(),imageInfo.getImageFormat(),faceInfoList);
        if(CollectionUtil.isNotEmpty(faceInfoList)){
        FaceFeature faceFeature=new FaceFeature();
        //提取人脸特征
        faceEngine.extractFaceFeature(imageInfo.getImageData(),imageInfo.getWidth(),imageInfo.getHeight(),imageInfo.getImageFormat(),faceInfoList.get(0),faceFeature);
        return faceFeature.getFeatureData();
        }
        }catch(Exception e){
        logger.error("",e);
        }finally{
        if(faceEngine!=null){
        //释放引擎对象
        faceEngineObjectPool.returnObject(faceEngine);
        }
        }
        return null;
        }

编写人脸对比逻辑

/**
 * 人脸比对
 * @param groupId
 * @param faceFeature
 * @return 人脸组
 */
@Override
public List<FaceUserInfo> compareFaceFeature(byte[]faceFeature,Integer groupId)throws InterruptedException,ExecutionException{
        // 识别到的人脸列表
        List<FaceUserInfo> resultFaceInfoList=Lists.newLinkedList();
        // 创建人脸特征对象
        FaceFeature targetFaceFeature=new FaceFeature();
        targetFaceFeature.setFeatureData(faceFeature);
        // 根据分组拿人脸库,从数据库中取出人脸库
        List<FaceUserInfo> faceInfoList=userFaceInfoMapper.getUserFaceInfoByGroupId(groupId);
        // 分成50一组,多线程处理, 数据量大1000组
        List<List<FaceUserInfo>>faceUserInfoPartList=Lists.partition(faceInfoList,50);
        // 多线程
        CompletionService<List<FaceUserInfo>>completionService=new ExecutorCompletionService(executorService);
        for(List<FaceUserInfo> part:faceUserInfoPartList){
        // 开始线程扫描人脸匹配度
        completionService.submit(new CompareFaceTask(part,targetFaceFeature));
        }
        // 获取线程任务参数
        for(List<FaceUserInfo> faceUserInfos:faceUserInfoPartList){
        List<FaceUserInfo> faceUserInfoList=completionService.take().get();
        if(CollectionUtil.isNotEmpty(faceInfoList)){
        resultFaceInfoList.addAll(faceUserInfoList);
        }
        }
        // 从大到小排序
        resultFaceInfoList.sort((h1,h2)->h2.getSimilarValue().compareTo(h1.getSimilarValue()));
        return resultFaceInfoList;
        }
/**
 * 多线程跑人脸对比逻辑
 */
private class CompareFaceTask implements Callable<List<FaceUserInfo>> {
    private final List<FaceUserInfo> faceUserInfoList;
    private final FaceFeature targetFaceFeature;
    public CompareFaceTask (List<FaceUserInfo> faceUserInfoList, FaceFeature targetFaceFeature) {
        this.faceUserInfoList = faceUserInfoList;
        this.targetFaceFeature = targetFaceFeature;
    }
    @Override
    public List<FaceUserInfo> call () throws Exception {
        FaceEngine faceEngine = null;
        //识别到的人脸列表
        List<FaceUserInfo> resultFaceInfoList = Lists.newLinkedList();
        try {
            faceEngine = faceEngineObjectPool.borrowObject();
            for (FaceUserInfo faceUserInfo : faceUserInfoList) {
                FaceFeature sourceFaceFeature = new FaceFeature();
                // 设置人脸特征
                sourceFaceFeature.setFeatureData(faceUserInfo.getFaceFeature());
                FaceSimilar faceSimilar = new FaceSimilar();
                faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
                //获取相似值
                Integer similarValue = plusHundred(faceSimilar.getScore());
                //相似值大于配置预期,加入到识别到人脸的列表
                if (similarValue > passRate) {
                    FaceUserInfo info = new FaceUserInfo();
                    info.setName(faceUserInfo.getName());
                    info.setFaceId(faceUserInfo.getFaceId());
                    info.setSimilarValue(similarValue);
                    resultFaceInfoList.add(info);
                }
            }
        } catch (Exception e) {
            logger.error("", e);
        } finally {
            if (faceEngine != null) {
                faceEngineObjectPool.returnObject(faceEngine);
            }
        }
        return resultFaceInfoList;
    }
}

编写根据根据分组ID获取人脸库数据

在mapper下SysUserFaceInfoMapper -> 创建接口 getUserFaceInfoByGroupId

/**
 * 根据分组Id
 * 从数据库中取出人脸库
 *
 * @param groupId
 * @return 人脸库
 */
    List<FaceUserInfo> getUserFaceInfoByGroupId(Integer groupId);

sql实现

<resultMap id="userFace2" type="top.yangbuyi.dto.FaceUserInfo">
<id column="id" property="id" javaType="int"/>
<result column="group_id" property="groupId" javaType="java.lang.Integer"/>
<result column="name" property="name" javaType="java.lang.String"/>
<result column="face_id" property="faceId" javaType="String"/>
<result column="face_feature" property="faceFeature"/>
</resultMap>
<
select id = "getUserFaceInfoByGroupId" resultMap="userFace2" parameterType="java.lang.Integer"
        resultType="top.yangbuyi.dto.FaceUserInfo">
select id, group_id, face_id, name, face_feature
from `sys_user_face_info`
where group_id = #{groupId}
          < /
select>

二、编写人脸添加业务接口

userFaceInfoService 新增人脸业务接口

/**
 * 新增用户人脸识别
 *
 * @param sysUserFaceInfo 用户人脸识别
 * @return 结果
 */
public int insertSysUserFaceInfo(SysUserFaceInfo sysUserFaceInfo);
就是一共简简单单的新增插入数据 这个就不用我教了吧?????????

image-1652677051101.png

三、 编写Controller业务控制层

import top.yangbuyi.domain.AjaxResult;
/**
 * (sys_user_face_info)表控制层
 *
 * @author yby6.com
 */
@RestController
@Slf4j
@RequestMapping("face")
@RequiredArgsConstructor
public class SysUserFaceInfoController {
    public final static Logger logger = LoggerFactory.getLogger(SysUserFaceInfoController.class);
    private final FaceEngineService faceEngineService;
    private final SysUserFaceInfoService userFaceInfoService;
    /**
     * 人脸添加
     *
     * @param file    人脸附件
     * @param groupId 分组id
     * @param name    用户登录名称
     */
    @RequestMapping(value = "/faceAdd", method = RequestMethod.POST)
    public AjaxResult faceAdd (@RequestBody Map<String, Object> map) {
        // 业务.... yby6.com 版权所有
        return AjaxResult.success();
    }
    /**
     * 人脸识别
     *
     * @param file    人脸附件
     * @param groupId 分组ID 方便快速识别
     */
    @RequestMapping(value = "/faceSearch", method = RequestMethod.POST)
    public AjaxResult faceSearch (@RequestBody Map<String, Object> map) throws Exception {
        // 业务... yby6.com 版权所有
        return AjaxResult.success();
    }

faceAdd 具体业务编写

1.根据前端传递的

file -> 人脸图片

groupId -> 用户分组(用于缩小范围查询该人员的位置有效减少数据量大的问题)

name -> 当前用户名称(实际开发当中应该是存储id)

String file = String.valueOf(map.get("file"));
        String groupId = String.valueOf(map.get("groupId"));
        String name = String.valueOf(map.get("name"));
        try {
        if (file == null) {
        return AjaxResult.error("file is null");
        }
        if (groupId == null) {
        return AjaxResult.error("file is null");
        }
        if (name == null) {
        return AjaxResult.error("file is null");
        }
        // 转换实体
        byte[] decode = Base64.decode(base64Process(file));
        ImageInfo imageInfo = ImageFactory.getRGBData(decode);
        //人脸特征获取
        byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
        if (bytes == null) {
        return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
        }
        List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
// 开始将人脸识别Base64转换文件流->用于文件上传
// final MultipartFile multipartFile = ImageUtils.base64ToMultipartFile(file);
final SysUserFaceInfo one = this.userFaceInfoService.lambdaQuery().eq(SysUserFaceInfo::getName, name).one();
        // 如果存在则更新人脸和特征
        if (null != one) {
        this.userFaceInfoService.lambdaUpdate().set(SysUserFaceInfo::getFaceFeature, bytes)
        .set(SysUserFaceInfo::getFpath, "存储头像地址")
        .eq(SysUserFaceInfo::getFaceId, name).update();
        } else {
        // 组装人脸实体
        SysUserFaceInfo userFaceInfo = new SysUserFaceInfo();
        userFaceInfo.setName(name);
        userFaceInfo.setAge(processInfoList.get(0).getAge());
        userFaceInfo.setGender(processInfoList.get(0).getGender().shortValue());
        userFaceInfo.setGroupId(Integer.valueOf(groupId));
        userFaceInfo.setFaceFeature(bytes);
        userFaceInfo.setFpath("存储头像地址");
        // 存储用户ID -> 我这里先使用name代替 -> 假如是唯一性
        userFaceInfo.setFaceId(name);
        //人脸特征插入到数据库
        userFaceInfoService.insertSysUserFaceInfo(userFaceInfo);
        }
        logger.info("faceAdd:" + name);
        return AjaxResult.success("人脸绑定成功!");
        } catch (Exception e) {
        logger.error("", e);
        }
        // 错误返回
        return AjaxResult.error(ErrorCodeEnum.UNKNOWN.getDescription());

faceSearch 具体业务编写

1.根据前端传递的

file -> 人脸图片

groupId -> 用户分组(用于缩小范围查询该人员的位置有效减少数据量大的问题)

String file = String.valueOf(map.get("file"));
        String groupId = String.valueOf(map.get("groupId"));
        if (groupId == null) {
        return AjaxResult.error("groupId is null");
        }
        byte[] decode = Base64.decode(base64Process(file));
        BufferedImage bufImage = ImageIO.read(new ByteArrayInputStream(decode));
        ImageInfo imageInfo = ImageFactory.bufferedImage2ImageInfo(bufImage);
        //人脸特征获取
        byte[] bytes = faceEngineService.extractFaceFeature(imageInfo);
        // 校验是否显示出人脸
        if (bytes == null) {
        return AjaxResult.error(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
        }
        //人脸比对,获取比对结果
        List<FaceUserInfo> userFaceInfoList = faceEngineService.compareFaceFeature(bytes, Integer.valueOf(groupId));
        if (CollectionUtil.isNotEmpty(userFaceInfoList)) {
        FaceUserInfo faceUserInfo = userFaceInfoList.get(0);
        FaceSearchResDto faceSearchResDto = new FaceSearchResDto();
        BeanUtil.copyProperties(faceUserInfo, faceSearchResDto);
        List<ProcessInfo> processInfoList = faceEngineService.process(imageInfo);
        if (CollectionUtil.isNotEmpty(processInfoList)) {
        //人脸检测
        List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
        int left = faceInfoList.get(0).getRect().getLeft();
        int top = faceInfoList.get(0).getRect().getTop();
        int width = faceInfoList.get(0).getRect().getRight() - left;
        int height = faceInfoList.get(0).getRect().getBottom() - top;
        Graphics2D graphics2D = bufImage.createGraphics();
        // 红色
        graphics2D.setColor(Color.RED);
        BasicStroke stroke = new BasicStroke(5f);
        graphics2D.setStroke(stroke);
        graphics2D.drawRect(left, top, width, height);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(bufImage, "jpg", outputStream);
        byte[] bytes1 = outputStream.toByteArray();
        faceSearchResDto.setImage("data:image/jpeg;base64," + Base64Utils.encodeToString(bytes1));
        faceSearchResDto.setAge(processInfoList.get(0).getAge());
        faceSearchResDto.setGender(processInfoList.get(0).getGender().equals(1) ? "女" : "男");
        }
        return AjaxResult.success(faceSearchResDto);
        }
        return AjaxResult.error(ErrorCodeEnum.FACE_DOES_NOT_MATCH.getDescription());

测试接口流程

准备两张图片

百度随便搜索个在线转换 Base64

(在线图片转Base64)

image-1652703631886.png

1、人脸添加

新增有脸的

image-1652702349484.png

新增测试无脸的

image-1652703497056.png

2、人脸识别

有脸的

image-1652703318357.png

无脸的

image-1652703532069.png

结尾在线演示

(前往享受人脸识别)

image-1652677885135.png


相关文章
|
7月前
|
Shell Linux 计算机视觉
【Dlib】动作检测:以常见的人脸识别验证为例讲解张嘴与闭眼
【Dlib】动作检测:以常见的人脸识别验证为例讲解张嘴与闭眼
284 0
|
7月前
|
算法 计算机视觉 异构计算
基于肤色模型的人脸识别FPGA实现,包含tb测试文件和MATLAB辅助验证
这是一个关于肤色检测算法的摘要:使用MATLAB 2022a和Vivado 2019.2进行测试和仿真,涉及图像预处理、RGB到YCbCr转换、肤色模型(基于阈值或概率)以及人脸检测。核心程序展示了如何读取图像数据并输入到FPGA处理,通过`tops`模块进行中值滤波、颜色空间转换及人脸检测,最终结果输出到&quot;face.txt&quot;。
|
安全 Java 开发工具
从零玩转人脸识别验证1
从零玩转人脸识别验证
115 0
从零玩转人脸识别验证1
|
Java 数据库连接 计算机视觉
从零玩转人脸识别验证2
从零玩转人脸识别验证
102 0
|
API 数据库
身份证实名认证接口验证不一致的原因
身份证是每个公民最常用的身份证明,随着当今互联网的快速发展,日常生活中越来越多的场景需要进行身份核验,以身份证实名认证为基础的网络实名制也引起了各行业的关注,随着实名制认证应用情景的多元化,众多网络平台对实名制验证机制的要求也越发严格。
611 0
身份证实名认证接口验证不一致的原因
|
安全
实人认证和活体人脸验证可以用于哪些场景?有什么场景特点?
实人认证和活体人脸验证可以用于哪些场景?有什么场景特点?
380 0
|
新零售 人工智能 大数据
农业全产业链人工智能工程“农业大脑”亮相北京;青岛工商登记实现全程电子化,用人脸识别验证申请人身份
据悉,“农业大脑”以传感器、物联网、云计算、大数据、超级人工智能为技术支撑,通过传感器嵌入到农业生产销售各个环节中,基于RS(遥感)、GIS(地理信息系统)和GPS(全球定位系统)分析土壤和气候等数据,再通过云计算和大数据处理和运算,最终帮助农户作出经济、高效的生产决策。
1926 0
|
7月前
|
弹性计算 Java PHP
新手用户注册阿里云账号、实名认证、购买云服务器图文教程参考
对于初次购买阿里云产品的用户来说,第一步要做的是注册账号并完成实名认证,然后才是购买阿里云服务器或者其他云产品,本文为大家以图文形式展示一下新手用户从注册阿里云账号、实名认证到购买云服务器完整详细教程,以供参考。
新手用户注册阿里云账号、实名认证、购买云服务器图文教程参考
|
6月前
|
文字识别 算法 API
视觉智能开放平台产品使用合集之uniapp框架如何使用阿里云金融级人脸识别
视觉智能开放平台是指提供一系列基于视觉识别技术的API和服务的平台,这些服务通常包括图像识别、人脸识别、物体检测、文字识别、场景理解等。企业或开发者可以通过调用这些API,快速将视觉智能功能集成到自己的应用或服务中,而无需从零开始研发相关算法和技术。以下是一些常见的视觉智能开放平台产品及其应用场景的概览。
150 0