手把手教你用Java实现静默活体检测(附源码)

31 阅读6分钟

🧐 什么是活体检测?

活体检测(Liveness Detection)是判断摄像头前的人脸是否为“真实活人”的技术,广泛用于防止照片、视频攻击。常见场景包括:

  • 登录认证
  • 支付安全
  • 门禁系统

SmartJavaAI 为我们封装了活体检测能力,调用简单,无需搞懂底层模型。


🚀 快速开始

我们使用SmartJavaAI 封装的 Seetaface6模型来实现活体检测 官网地址:

1、引入 Maven 依赖

在项目的pom.xml的dependencies中加入以下内容(全部功能),也可以根据需求单独引入人脸模块,具体可以查看 SmartJavaAI官方文档

<dependency>
    <groupId>cn.smartjavaai</groupId>
    <artifactId>smartjavaai-all</artifactId>
    <version>1.0.12</version>
</dependency>

2、模型下载

SmartJavaAI 默认使用的是 SeetaFace6 活体检测模型,首次使用前请先下载模型文件并解压到本地路径。

🔗 下载地址:

⚠️ 下载后请将模型路径配置到 LivenessConfig#setModelPath 中。


3、获取活体检测模型:

LivenessConfig config = new LivenessConfig();
config.setModelEnum(LivenessModelEnum.SEETA_FACE6_MODEL);
//需替换为实际模型存储路径
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
LivenessDetModel livenessDetModel = LivenessModelFactory.getInstance().getModel(config);

LivenessConfig参数说明

字段名称字段类型默认值说明
modelEnumLivenessModelEnumSEETA_FACE6_MODEL模型枚举,目前支持活体检测的模型只有seetaface6模型
modelPathStringNULL手动指定离线模型路径
faceClarityThresholdfloat0.3人脸清晰度阈值,可选
realityThresholdfloat0.8人脸活体阈值,可选
frameCountint10视频检测帧数,可选(检测视频时有效)
deviceDeviceEnumCPU指定运行设备,支持 CPU/GPU

⚠️ 注意事项 1、活体识别时,如果清晰度低的话,就会直接返回:未知(UNKNOWN)。清晰度满足阈值,则判断真实度,超过阈值则认为是真人,低于阈值是非活体。 2、在视频识别模式下,会计算视频帧数内的平均值再跟帧数比较。两个阈值都符合,越高的话,越是严格。

图片活体检测(多人脸)

DetectionResponse livenessStatusList = livenessDetModel.detect("src/main/resources/double_person.png");
log.info("活体检测结果:{}", JSONObject.toJSONString(livenessStatusList));

图片活体检测(分数最高人脸)

LivenessStatus livenessStatus = livenessDetModel.detectTopFace("src/main/resources/double_person.png");
log.info("活体检测结果:{}", JSONObject.toJSONString(livenessStatus));

图片多人脸活体检测(基于已检测出的人脸区域和关键点)

需要基于人脸检测结果

//人脸检测
//需替换为实际模型存储路径
String modelPath = "C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models";
FaceModelConfig faceDetectModelConfig = new FaceModelConfig();
faceDetectModelConfig.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
faceDetectModelConfig.setModelPath(modelPath);
FaceModel faceDetectModel = FaceModelFactory.getInstance().getModel(faceDetectModelConfig);
DetectionResponse detectionResponse = faceDetectModel.detect("src/main/resources/double_person.png");
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse));
//检测到人脸
if(detectionResponse != null && detectionResponse.getDetectionInfoList() != null && detectionResponse.getDetectionInfoList().size() > 0){
    //活体检测
    LivenessConfig config = new LivenessConfig();
    config.setModelEnum(LivenessModelEnum.SEETA_FACE6_MODEL);
    config.setModelPath(modelPath);
    //人脸清晰度阈值,可选,默认0.3,活体识别时,如果清晰度低的话,就会直接返回FUZZY,清晰度满足阈值,则判断真实度
    config.setFaceClarityThreshold(LivenessConstant.DEFAULT_FACE_CLARITY_THRESHOLD);
    //人脸活体阈值,可选,默认0.8,超过阈值则认为是真人,低于阈值是非活体
    config.setRealityThreshold(LivenessConstant.DEFAULT_REALITY_THRESHOLD);
    LivenessDetModel livenessDetModel = LivenessModelFactory.getInstance().getModel(config);
    List<LivenessStatus> livenessStatusList = livenessDetModel.detect("src/main/resources/double_person.png",detectionResponse);
    log.info("活体检测结果:{}", JSONObject.toJSONString(livenessStatusList));
}

图片单人脸活体检测(基于已检测出的人脸区域和关键点)

try {
    //人脸检测
    //需替换为实际模型存储路径
    String modelPath = "C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models";
    String imagePath = "src/main/resources/double_person.png";
    FaceModelConfig faceDetectModelConfig = new FaceModelConfig();
    faceDetectModelConfig.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
    faceDetectModelConfig.setModelPath(modelPath);
    FaceModel faceDetectModel = FaceModelFactory.getInstance().getModel(faceDetectModelConfig);
    DetectionResponse detectionResponse = faceDetectModel.detect(imagePath);
    log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse));
    //检测到人脸
    if(detectionResponse != null && detectionResponse.getDetectionInfoList() != null && detectionResponse.getDetectionInfoList().size() > 0){
        //活体检测
        LivenessConfig config = new LivenessConfig();
        config.setModelEnum(LivenessModelEnum.SEETA_FACE6_MODEL);
        config.setModelPath(modelPath);
        //人脸清晰度阈值,可选,默认0.3,活体识别时,如果清晰度低的话,就会直接返回FUZZY,清晰度满足阈值,则判断真实度
        config.setFaceClarityThreshold(LivenessConstant.DEFAULT_FACE_CLARITY_THRESHOLD);
        //人脸活体阈值,可选,默认0.8,超过阈值则认为是真人,低于阈值是非活体
        config.setRealityThreshold(LivenessConstant.DEFAULT_REALITY_THRESHOLD);
        LivenessDetModel livenessDetModel = LivenessModelFactory.getInstance().getModel(config);
        BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
        for (DetectionInfo detectionInfo : detectionResponse.getDetectionInfoList()){
            FaceInfo faceInfo = detectionInfo.getFaceInfo();
            LivenessStatus livenessStatus = livenessDetModel.detect(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
            log.info("活体检测结果:{}", JSONObject.toJSONString(livenessStatus));
        }
    }
} catch (Exception e) {
    e.printStackTrace();
}

视频活体检测

LivenessConfig config = new LivenessConfig();
config.setModelEnum(LivenessModelEnum.SEETA_FACE6_MODEL);
//需替换为实际模型存储路径
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
//人脸清晰度阈值,可选,默认0.3,活体识别时,如果清晰度低的话,就会直接返回FUZZY,清晰度满足阈值,则判断真实度
config.setFaceClarityThreshold(LivenessConstant.DEFAULT_FACE_CLARITY_THRESHOLD);
//人脸活体阈值,可选,默认0.8,超过阈值则认为是真人,低于阈值是非活体
config.setRealityThreshold(LivenessConstant.DEFAULT_REALITY_THRESHOLD);
/*视频检测帧数,可选,默认10,输出帧数超过这个number之后,就可以输出识别结果。
这个数量相当于多帧识别结果融合的融合的帧数。当输入的帧数超过设定帧数的时候,会采用滑动窗口的方式,返回融合的最近输入的帧融合的识别结果。
一般来说,在10以内,帧数越多,结果越稳定,相对性能越好,但是得到结果的延时越高。*/
config.setFrameCount(LivenessConstant.DEFAULT_FRAME_COUNT);
LivenessDetModel livenessDetModel = LivenessModelFactory.getInstance().getModel(config);
LivenessStatus livenessStatus = livenessDetModel.detectVideo("src/main/resources/girl.mp4");
log.info("视频活体检测结果:{}", JSONObject.toJSONString(livenessStatus));

视频活体检测(逐帧检测,基于已检测出的人脸区域和关键点)

try {
    FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("src/main/resources/girl.mp4");
    grabber.start();
    // 获取视频总帧数
    int totalFrames = grabber.getLengthInFrames();
    log.info("视频总帧数:{},检测帧数:{}", totalFrames, config.getFrameCount());
    //活体检测结果
    LivenessStatus livenessStatus = LivenessStatus.UNKNOWN;
    // 逐帧处理视频
    for (int frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
        // 获取当前帧
        Frame frame = grabber.grabImage();
        if (frame != null) {
            BufferedImage bufferedImage = Java2DFrameUtils.toBufferedImage(frame);
            LivenessStatus livenessStatusFrame = livenessDetModel.detectVideoByFrame(bufferedImage);
            //满足检测帧数之后停止检测
            if(livenessStatusFrame != LivenessStatus.DETECTING){
                livenessStatus = livenessStatusFrame;
            }
        }
    }
    log.info("视频活体检测结果:{}", JSONObject.toJSONString(livenessStatus));
    grabber.stop();
} catch (FFmpegFrameGrabber.Exception e) {
    throw new FaceException(e);
}

DetectionResponse字段说明

  • 返回并非json格式,仅用于字段讲解
{
  "detectionInfoList": [ // 检测信息列表
    {
      "detectionRectangle": { //矩形框
        "height": 174, // 矩形高度
        "width": 147, // 矩形宽度
        "x": 275, // 左上角横坐标
        "y": 143 // 左上角纵坐标
      },
      "faceInfo": { // 人脸信息
        "keyPoints": [ // 5个人脸关键点:循序依次为,左眼中心、右眼中心、鼻尖、左嘴角和右嘴角
          {
            "x": 339.5083751678467,
            "y": 192.76402664184573
          },
          {
            "x": 404.7374267578125,
            "y": 197.89914321899414
          },
          {
            "x": 388.9555263519287,
            "y": 231.50675201416016
          },
          {
            "x": 339.8661708831787,
            "y": 265.51241302490234
          },
          {
            "x": 397.7071800231933,
            "y": 269.7657699584961
          }
        ],
        "livenessStatus": "LIVE" //活体检测结果
      }
    }
  ]
}

⚠️ 注意事项 1、视频活体检测支持常见视频格式(推荐使用mp4),内部使用ffmpeg进行视频解析。 2、输入视频的有效帧数必须 > LivenessConfig 配置参数中 frameCount 的设定值(默认阈值:10帧) 3、若不满足帧数条件,接口将抛出异常 4、输入视频文件过大,会耗费内存,建议视频文件不要过大。 5、在视频活体检测模式下,检测帧数超过frameCount之后,就可以输出识别结果。这个数量相当于多帧识别结果融合的融合的帧数。当检测的帧数超过设定帧数的时候,会采用滑动窗口的方式,返回融合的最近检测的帧融合的识别结果。一般来说,在10以内,帧数越多,结果越稳定,相对性能越好,但是得到结果的延时越高。

LivenessStatus 活体检测返回结果枚举

枚举值说明
LIVE活体
NON_LIVE非活体
UNKNOWN未知,或者人脸清晰度低
DETECTING视频模式,检测中

项目地址

  • 完整代码地址:examples
  • SmartJavaAI 开发文档:官方文档
  • SmartJavaAI Gitee开源地址:Gitee
  • SmartJavaAI Github开源地址:Github
  • SmartJavaAI GitCode开源地址:GitCode