1、人脸模块:新增小视科技(MiniVision)活体检测模型

2、人脸模块:新增阿里通义工作室活体检测模型
3、人脸模块:新增2个表情识别模型
4、人脸模块:新增InsightFace、ElasticFace人脸识别模型
5、人脸模块:新增Seetaface6质量评估模型
6、目标检测模块:开放更多自定义模型参数
7、人脸模块:支持base64图片
8、实现接口 AutoCloseable,支持资源的自动释放
9、OCR模块:解决加方向矫正后无法连续识别bug
10、人脸模块:解决人脸更新后缓存问题
11、优化部分功能
This commit is contained in:
dengwenjie
2025-07-07 08:45:08 +08:00
parent 3e631a060b
commit 07a8a18835
168 changed files with 8562 additions and 2850 deletions

7
examples/face-example/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.idea
.idea/
target
log
*.iml
/.settings/
/logging.file_IS_UNDEFINED/

View File

@@ -0,0 +1,103 @@
# 人脸识别示例
本项目提供了一系列关于人脸识别相关功能的 Java 示例代码,适用于图像处理、人脸检测、活体检测等场景。所有示例基于 SmartJavaAI 的 SDK 实现。
## 📁 项目结构
```
src/main/java/smartai/examples/face/
├── attribute/ # 人脸属性检测模块
│ └── FaceAttributeDetDemo.java # 检测性别、年龄等人脸属性
├── expression/ # 表情识别模块
│ └── ExpressionRecDemo.java # 识别中性、高兴、悲伤等7种表情
├── facedet/ # 人脸检测模块
│ └── FaceDetDemo.java # 检测图片或视频中的人脸并绘制人脸框
├── facerec/ # 人脸识别模块1:1, 1:N
│ └── FaceRecDemo.java # 提取人脸特征、比对、注册与搜索人脸库
├── liveness/ # 活体检测模块
│ └── LivenessDetDemo.java # 判断是否为真人(静态图或摄像头视频流)
├── quality/ # 人脸质量评估模块
│ └── FaceQualityDetDemo.java # 评估亮度、清晰度、完整性、姿态、分辨率
└── ViewerFrame.java # 图像显示窗口工具类(用于在 GUI 中展示图像)
```
---
## 🧩 功能模块说明
### 1. 人脸属性检测 ([FaceAttributeDetDemo.java](file:///Users/xxx/Documents/idea_workplace/SmartJavaAI/examples/face-example/src/main/java/smartai/examples/face/attribute/FaceAttributeDetDemo.java))
- **功能**:识别性别、年龄、眼镜佩戴状态、种族等属性。
- **使用模型**SeetaFace6 等。
---
### 2. 表情识别 ([ExpressionRecDemo.java](file:///Users/xxx/Documents/idea_workplace/SmartJavaAI/examples/face-example/src/main/java/smartai/examples/face/expression/ExpressionRecDemo.java))
- **功能**:识别 7 种面部表情:中性、高兴、悲伤、惊讶、恐惧、厌恶、愤怒。
- **支持模式**:单人、多人、摄像头实时检测。
---
### 3. 人脸检测 ([FaceDetDemo.java](file:///Users/xxx/Documents/idea_workplace/SmartJavaAI/examples/face-example/src/main/java/smartai/examples/face/facedet/FaceDetDemo.java))
- **功能**:识别图像或视频中的人脸区域,并返回人脸边界框。
- **支持模型**RetinaFace、SeetaFace6。
---
### 4. 人脸识别 ([FaceRecDemo.java](file:///Users/xxx/Documents/idea_workplace/SmartJavaAI/examples/face-example/src/main/java/smartai/examples/face/facerec/FaceRecDemo.java))
- **功能**提取人脸特征、进行人脸比对1:1、人脸搜索1:N、人脸注册管理。
- **支持数据库**SQLite、Milvus 向量数据库。
---
### 5. 活体检测 ([LivenessDetDemo.java](file:///Users/xxx/Documents/idea_workplace/SmartJavaAI/examples/face-example/src/main/java/smartai/examples/face/liveness/LivenessDetDemo.java))
- **功能**:判断输入图像中人脸是否为真实人脸(非照片、视频伪造)。
- **支持模型**IIC-FL、MiniVision双模型融合
---
### 6. 人脸质量评估 ([FaceQualityDetDemo.java](file:///Users/xxx/Documents/idea_workplace/SmartJavaAI/examples/face-example/src/main/java/smartai/examples/face/quality/FaceQualityDetDemo.java))
- **功能**:评估人脸图像的质量指标,包括:
- 亮度 (Brightness)
- 完整度 (Completeness)
- 清晰度 (Clarity)
- 姿态 (Pose)
- 分辨率 (Resolution)
---
### 7. 工具类 ([ViewerFrame.java](file:///Users/xxx/Documents/idea_workplace/SmartJavaAI/examples/face-example/src/main/java/smartai/examples/face/ViewerFrame.java))
- **功能**GUI 显示组件,用于展示图像处理结果(如人脸框、表情、活体状态等)。
- **用途**:支持摄像头实时检测时的结果可视化。
---
## ⚙️ 配置要求
- **运行环境**
- JDK 1.8 或更高版本
- IntelliJ IDEA 推荐作为开发 IDE
- **依赖库**
- OpenCV、DJL、SmartJavaAI SDK
- **模型路径**
- 所有模型需下载并配置正确的路径(参考各 demo 注释中的链接)
---
## 🚀 快速开始
1. 克隆项目到本地:
2. 导入项目至 IntelliJ IDEA。
3. 根据需要修改模型路径(见各 demo 中注释)。
4. 运行对应的 JUnit 测试类方法即可体验各项功能。
---
## 📄 文档
有关完整使用说明,请查阅 SmartJavaAI 官方文档:
[http://doc.smartjavaai.cn](http://doc.smartjavaai.cn)
---

View File

@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.smartjavaai</groupId>
<artifactId>face-example</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<smartjavaai.version>1.0.19</smartjavaai.version>
<!--如果打包运行需要替换成你的main-->
<exec.mainClass>smartai.examples.face.facedet.FaceDetDemo</exec.mainClass>
<javacv.version>1.5.10</javacv.version>
<javacv.platform.macosx-arm64>macosx-arm64</javacv.platform.macosx-arm64>
<javacv.platform.linux-x86_64>linux-x86_64</javacv.platform.linux-x86_64>
<javacv.platform.linux-arm64>linux-arm64</javacv.platform.linux-arm64>
<javacv.platform.windows-x86_64>windows-x86_64</javacv.platform.windows-x86_64>
<djl.platform.windows-x86_64>win-x86_64</djl.platform.windows-x86_64>
<djl.platform.linux-x86_64>linux-x86_64</djl.platform.linux-x86_64>
<djl.platform.linux-aarch64>linux-aarch64</djl.platform.linux-aarch64>
<djl.platform.osx-aarch64>osx-aarch64</djl.platform.osx-aarch64>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.smartjavaai</groupId>
<artifactId>smartjavaai-bom</artifactId>
<version>${smartjavaai.version}</version>
<type>pom</type>
<!-- 注意这里是import -->
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.24.1</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<!--人脸识别模块-->
<dependency>
<groupId>cn.smartjavaai</groupId>
<artifactId>smartjavaai-face</artifactId>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-jni</artifactId>
<version>2.5.1-0.32.0</version>
<scope>runtime</scope>
</dependency>
<!-- windows平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu</artifactId>
<classifier>${djl.platform.windows-x86_64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
<!-- linux x86 平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu</artifactId>
<classifier>${djl.platform.linux-x86_64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
<!-- macOS M系列 平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu</artifactId>
<classifier>${djl.platform.osx-aarch64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
<!-- linux aarch64 平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu-precxx11</artifactId>
<classifier>${djl.platform.linux-aarch64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${exec.mainClass}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>aliyunmaven</id>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package smartai.examples.face;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class ViewerFrame {
private JFrame frame;
private ImagePanel imagePanel;
public ViewerFrame(int width, int height) {
frame = new JFrame("Demo");
imagePanel = new ImagePanel();
frame.setLayout(new BorderLayout());
frame.add(BorderLayout.CENTER, imagePanel);
JOptionPane.setRootFrame(frame);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if (width > screenSize.width) {
width = screenSize.width;
}
Dimension frameSize = new Dimension(width, height);
frame.setSize(frameSize);
frame.setLocation((screenSize.width - width) / 2, (screenSize.height - height) / 2);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void showImage(BufferedImage image) {
imagePanel.setImage(image);
SwingUtilities.invokeLater(
() -> {
frame.repaint();
frame.pack();
});
}
private static final class ImagePanel extends JPanel {
private BufferedImage image;
void setImage(BufferedImage image) {
this.image = image;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (image == null) {
return;
}
g.drawImage(image, 0, 0, null);
setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
}
}
}

View File

@@ -0,0 +1,130 @@
package smartai.examples.face.attribute;
import cn.smartjavaai.common.entity.DetectionInfo;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.entity.face.FaceAttribute;
import cn.smartjavaai.common.entity.face.FaceInfo;
import cn.smartjavaai.face.config.FaceAttributeConfig;
import cn.smartjavaai.face.config.FaceDetConfig;
import cn.smartjavaai.face.enums.FaceAttributeModelEnum;
import cn.smartjavaai.face.enums.FaceDetModelEnum;
import cn.smartjavaai.face.factory.FaceAttributeModelFactory;
import cn.smartjavaai.face.factory.FaceDetModelFactory;
import cn.smartjavaai.face.model.attribute.FaceAttributeModel;
import cn.smartjavaai.face.model.facedect.FaceDetModel;
import cn.smartjavaai.face.utils.FaceUtils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
/**
* 人脸属性检测demo
* 模型下载地址https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
* @author dwj
*/
@Slf4j
public class FaceAttributeDetDemo {
public FaceAttributeModel getFaceAttributeModel() {
FaceAttributeConfig config = new FaceAttributeConfig();
config.setModelEnum(FaceAttributeModelEnum.SEETA_FACE6_MODEL);
//需替换为实际模型存储路径
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
return FaceAttributeModelFactory.getInstance().getModel(config);
}
public FaceDetModel getFaceDetModel() {
//需替换为实际模型存储路径
String modelPath = "C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models";
FaceDetConfig faceDetectModelConfig = new FaceDetConfig();
faceDetectModelConfig.setModelEnum(FaceDetModelEnum.SEETA_FACE6_MODEL);
faceDetectModelConfig.setModelPath(modelPath);
return FaceDetModelFactory.getInstance().getModel(faceDetectModelConfig);
}
/**
* 人脸属性检测(多人脸)
*/
@Test
public void testFaceAttributeDetect(){
try (FaceAttributeModel faceAttributeModel = getFaceAttributeModel()){
DetectionResponse detectionResponse = faceAttributeModel.detect("src/main/resources/iu_1.jpg");
//绘制并导出人脸属性图片,小人脸仅有人脸框
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
FaceUtils.drawBoxesWithFaceAttribute(image, detectionResponse,"C:/Users/Administrator/Downloads/double_person_.png");
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(detectionResponse));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 图片人脸属性检测(分数最高人脸)
*/
@Test
public void testFaceAttributeDetect2(){
try (FaceAttributeModel faceAttributeModel = getFaceAttributeModel()){
FaceAttribute faceAttribute = faceAttributeModel.detectTopFace("src/main/resources/iu_1.jpg");
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(faceAttribute));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 图片多人脸属性检测(基于已检测出的人脸区域和关键点)
*/
@Test
public void testFaceAttributeDetect3(){
try (FaceAttributeModel faceAttributeModel = getFaceAttributeModel()){
FaceAttribute faceAttribute = faceAttributeModel.detectTopFace("src/main/resources/iu_1.jpg");
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(faceAttribute));
} catch (Exception e) {
e.printStackTrace();
}
//人脸检测
}
/**
* 图片单人脸人脸属性检测(基于已检测出的人脸区域和关键点)
*/
@Test
public void testFaceAttributeDetect4(){
try (FaceDetModel faceDetModel = getFaceDetModel();
FaceAttributeModel faceAttributeModel = getFaceAttributeModel()){
//人脸检测
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detectionResponse = faceDetModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
for (DetectionInfo detectionInfo : detectionResponse.getData().getDetectionInfoList()){
FaceInfo faceInfo = detectionInfo.getFaceInfo();
FaceAttribute faceAttribute = faceAttributeModel.detect(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(faceAttribute));
}
}
}else{
log.info("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e){
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,253 @@
package smartai.examples.face.expression;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import cn.smartjavaai.common.entity.DetectionInfo;
import cn.smartjavaai.common.entity.DetectionRectangle;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.entity.face.ExpressionResult;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.common.enums.face.FacialExpression;
import cn.smartjavaai.common.enums.face.LivenessStatus;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.common.utils.OpenCVUtils;
import cn.smartjavaai.face.config.FaceDetConfig;
import cn.smartjavaai.face.config.FaceExpressionConfig;
import cn.smartjavaai.face.constant.FaceDetectConstant;
import cn.smartjavaai.face.enums.ExpressionModelEnum;
import cn.smartjavaai.face.enums.FaceDetModelEnum;
import cn.smartjavaai.face.exception.FaceException;
import cn.smartjavaai.face.factory.ExpressionModelFactory;
import cn.smartjavaai.face.factory.FaceDetModelFactory;
import cn.smartjavaai.face.model.expression.ExpressionModel;
import cn.smartjavaai.face.model.facedect.FaceDetModel;
import cn.smartjavaai.face.model.liveness.LivenessDetModel;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import nu.pattern.OpenCV;
import org.junit.Test;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
import smartai.examples.face.ViewerFrame;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
/**
* 表情识别demo
* 支持识别7种表情neutral中性、happy高兴、sad悲伤、surprise惊讶、fear恐惧、disgust厌恶、anger愤怒
* @author dwj
*/
@Slf4j
public class ExpressionRecDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
/**
* 获取人脸检测模型
* @return
*/
public FaceDetModel getFaceDetModel(){
FaceDetConfig config = new FaceDetConfig();
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸检测模型
config.setConfidenceThreshold(FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
config.setNmsThresh(FaceDetectConstant.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
config.setDevice(device);
return FaceDetModelFactory.getInstance().getModel(config);
}
/**
* 获取表情识别模型
* @return
*/
public ExpressionModel getExpressionModel(){
FaceExpressionConfig config = new FaceExpressionConfig();
config.setModelEnum(ExpressionModelEnum.FrEmotion);
config.setModelPath("/Users/xxx/Documents/develop/model/emotion/fr_expression.onnx");
config.setDevice(device);
config.setAlign(true);
config.setDetectModel(getFaceDetModel());
return ExpressionModelFactory.getInstance().getModel(config);
}
/**
* 表情识别(单人脸)
* 支持识别7种表情neutral中性、happy高兴、sad悲伤、surprise惊讶、fear恐惧、disgust厌恶、anger愤怒
*/
@Test
public void testExpressionDetect() {
ExpressionModel model = getExpressionModel();
R<ExpressionResult> result = model.detectTopFace("src/main/resources/emotion/happy.png");
if(result.isSuccess()){
log.info("识别结果:{}", JSONObject.toJSONString(result.getData().getExpression().getDescription()));
}else{
log.info("识别失败:{}", result.getMessage());
}
}
/**
* 表情识别(多人脸)
* 支持识别7种表情neutral中性、happy高兴、sad悲伤、surprise惊讶、fear恐惧、disgust厌恶、anger愤怒
*/
@Test
public void testExpressionDetect2() {
ExpressionModel model = getExpressionModel();
R<DetectionResponse> result = model.detect("src/main/resources/emotion/happy.png");
if(result.isSuccess()){
//log.info("识别结果:{}", JSONObject.toJSONString(result.getData()));
for (DetectionInfo detectionInfo : result.getData().getDetectionInfoList()) {
log.info("识别结果:{}", JSONObject.toJSONString(detectionInfo.getFaceInfo().getExpressionResult().getExpression().getDescription()));
}
}else{
log.info("识别失败:{}", result.getMessage());
}
}
/**
* 表情识别(基于人脸检测检测框-多人)
* 流程:人脸检测 -》表情识别
* 支持识别7种表情neutral中性、happy高兴、sad悲伤、surprise惊讶、fear恐惧、disgust厌恶、anger愤怒
*/
@Test
public void testExpressionDetect3() {
FaceDetModel faceDetModel = getFaceDetModel();
ExpressionModel model = getExpressionModel();
// 将图片路径转换为 BufferedImage
BufferedImage image = null;
try {
image = ImageIO.read(new File(Paths.get("src/main/resources/emotion/happy.png").toAbsolutePath().toString()));
} catch (IOException e) {
throw new FaceException("无效图片路径", e);
}
R<DetectionResponse> detResult = faceDetModel.detect(image);
if(detResult.isSuccess()){
R<List<ExpressionResult>> result = model.detect(image, detResult.getData());
if(result.isSuccess()){
result.getData().forEach(expressionResult -> {
log.info("识别结果:{}", JSONObject.toJSONString(expressionResult.getExpression().getDescription()));
});
}else{
log.info("识别失败:{}", result.getMessage());
}
}else{
log.info("人脸检测失败:{}", detResult.getMessage());
}
}
/**
* 表情识别(基于人脸检测检测框-单人)
* 流程:人脸检测 -》表情识别
* 支持识别7种表情neutral中性、happy高兴、sad悲伤、surprise惊讶、fear恐惧、disgust厌恶、anger愤怒
*/
@Test
public void testExpressionDetect4() {
FaceDetModel faceDetModel = getFaceDetModel();
ExpressionModel model = getExpressionModel();
// 将图片路径转换为 BufferedImage
BufferedImage image = null;
try {
image = ImageIO.read(new File(Paths.get("src/main/resources/emotion/happy.png").toAbsolutePath().toString()));
} catch (IOException e) {
throw new FaceException("无效图片路径", e);
}
R<DetectionResponse> detResult = faceDetModel.detect(image);
if(detResult.isSuccess()){
for (DetectionInfo detectionInfo : detResult.getData().getDetectionInfoList()) {
R<ExpressionResult> result = model.detect(image, detectionInfo.getDetectionRectangle(), detectionInfo.getFaceInfo().getKeyPoints());
if(result.isSuccess()){
log.info("识别结果:{}", JSONObject.toJSONString(result.getData().getExpression().getDescription()));
}else{
log.info("识别失败:{}", result.getMessage());
}
}
}else{
log.info("人脸检测失败:{}", detResult.getMessage());
}
}
/**
* 摄像头表情识别
* 注意事项:如果视频比较卡,可以使用轻量的人脸检测模型
*/
@Test
public void testLivenessDetectCamera(){
try (ExpressionModel expressionModel = getExpressionModel()){
OpenCV.loadShared();
VideoCapture capture = new VideoCapture(0);
if (!capture.isOpened()) {
System.out.println("No camera detected");
return;
}
double ratio =
capture.get(Videoio.CAP_PROP_FRAME_WIDTH)
/ capture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int height = (int) (screenSize.height * 0.65f);
int width = (int) (height * ratio);
if (width > screenSize.width) {
width = screenSize.width;
}
Mat image = new Mat();
boolean captured = false;
for (int i = 0; i < 10; ++i) {
captured = capture.read(image);
if (captured) {
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException ignore) {
// ignore
}
}
if (!captured) {
JOptionPane.showConfirmDialog(null, "Failed to capture image from WebCam.");
}
ViewerFrame frame = new ViewerFrame(width, height);
ImageFactory factory = ImageFactory.getInstance();
Size size = new Size(width, height);
while (capture.isOpened()) {
if (!capture.read(image)) {
break;
}
Mat resizeImage = new Mat();
Imgproc.resize(image, resizeImage, size);
Image img = factory.fromImage(resizeImage);
BufferedImage bufferedImage = OpenCVUtils.mat2Image(resizeImage);
R<DetectionResponse> detectedResult = expressionModel.detect(bufferedImage);
if(!detectedResult.isSuccess()){
log.debug("识别失败:{}", detectedResult.getMessage());
continue;
}
for(DetectionInfo detectionInfo : detectedResult.getData().getDetectionInfoList()){
DetectionRectangle detectionRectangle = detectionInfo.getDetectionRectangle();
String text = detectionInfo.getFaceInfo().getExpressionResult().getExpression().getDescription() + ":" + detectionInfo.getFaceInfo().getExpressionResult().getScore();
ImageUtils.drawImageRectWithText(bufferedImage, detectionRectangle, text, Color.red);
}
frame.showImage(bufferedImage);
}
capture.release();
System.exit(0);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,289 @@
package smartai.examples.face.facedet;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import cn.smartjavaai.common.entity.DetectionInfo;
import cn.smartjavaai.common.entity.DetectionRectangle;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.common.enums.face.LivenessStatus;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.common.utils.OpenCVUtils;
import cn.smartjavaai.face.config.FaceDetConfig;
import cn.smartjavaai.face.constant.FaceDetectConstant;
import cn.smartjavaai.face.enums.FaceDetModelEnum;
import cn.smartjavaai.face.factory.FaceDetModelFactory;
import cn.smartjavaai.face.model.facedect.FaceDetModel;
import cn.smartjavaai.face.model.liveness.LivenessDetModel;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import nu.pattern.OpenCV;
import org.junit.Test;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
import smartai.examples.face.ViewerFrame;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Paths;
/**
* 人脸检测模型demo
* 支持系统windows 64位linux 64位, macos M系列
* 支持功能:人脸检测
* 模型下载地址https://pan.baidu.com/s/1d2YlJ2YOdGn3Y-AegyAhmQ?pwd=1234 提取码: 1234
* @author dwj
*/
@Slf4j
public class FaceDetDemo {
public static String imgPath = "src/main/resources/iu_1.jpg";
/**
* 获取人脸检测模型
* 注意事项:高精度模型,速度较慢
* @return
*/
public FaceDetModel getFaceDetModel(){
FaceDetConfig config = new FaceDetConfig();
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸检测模型
config.setConfidenceThreshold(FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
config.setNmsThresh(FaceDetectConstant.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
return FaceDetModelFactory.getInstance().getModel(config);
}
/**
* 获取Seetaface6 人脸检测模型
* 注意不支持macos
* @return
*/
public FaceDetModel getSeetaface6DetModel(){
FaceDetConfig config = new FaceDetConfig();
//指定模型
config.setModelEnum(FaceDetModelEnum.SEETA_FACE6_MODEL);
//指定模型路径:请根据实际情况替换为本地模型文件的绝对路径(模型下载地址请查看文档)
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
return FaceDetModelFactory.getInstance().getModel(config);
}
/**
* 人脸检测(默认配置)
* 使用默认模型参数检测默认模型retinaface需联网会自动下载模型
* 图片参数:图片路径
*/
@Test
public void testFaceDetect(){
try (FaceDetModel faceModel = FaceDetModelFactory.getInstance().getModel()) {
R<DetectionResponse> detectedResult = faceModel.detect(imgPath);
if(detectedResult.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult.getData()));
}else{
log.info("人脸检测失败:{}", detectedResult.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸检测(自定义模型参数)
* 图片参数:图片路径
*/
@Test
public void testFaceDetectCustomConfig(){
try (FaceDetModel faceModel = getFaceDetModel()){
R<DetectionResponse> detectedResult = faceModel.detect(imgPath);
if(detectedResult.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult.getData()));
}else{
log.info("人脸检测失败:{}", detectedResult.getMessage());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 人脸检测并绘制人脸框
*/
@Test
public void testFaceDetectAndDraw(){
try (FaceDetModel faceModel = getFaceDetModel()){
faceModel.detectAndDraw("src/main/resources/largest_selfie.jpg","output/largest_selfie_detected.png");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 人脸检测并绘制人脸框,返回BufferedImage
*
*/
@Test
public void testFaceDetectAndDraw2(){
try (FaceDetModel faceModel = getFaceDetModel()){
BufferedImage image = null;
String imagePath = "src/main/resources/largest_selfie.jpg";
image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
//可以根据后续业务场景使用detectedImage
R<BufferedImage> detectedImage = faceModel.detectAndDraw(image);
if(detectedImage.isSuccess()){
log.info("人脸检测成功");
}else{
log.info("人脸检测失败:{}", detectedImage.getMessage());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 人脸检测(离线模型)
*/
@Test
public void testDetectFaceOffine(){
FaceDetConfig config = new FaceDetConfig();
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸模型
//模型路径,不同模型下载路径请参看文档
config.setModelPath("/Users/xxx/Documents/develop/face_model/retinaface.pt");
try (FaceDetModel faceModel = FaceDetModelFactory.getInstance().getModel(config)) {
R<DetectionResponse> detectedResult = faceModel.detect(imgPath);
if(detectedResult.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult.getData()));
}else{
log.info("人脸检测失败:{}", detectedResult.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸检测GPU模式
*/
@Test
public void testDetectFaceGPU(){
FaceDetConfig config = new FaceDetConfig();
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸模型
config.setDevice(DeviceEnum.GPU);
try (FaceDetModel faceModel = FaceDetModelFactory.getInstance().getModel(config)) {
R<DetectionResponse> detectedResult = faceModel.detect(imgPath);
if(detectedResult.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult.getData()));
}else{
log.info("人脸检测失败:{}", detectedResult.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸检测(Seetaface6)
* 图片参数:图片路径
*/
@Test
public void testFaceDetectSeetaface6(){
try (FaceDetModel faceModel = getSeetaface6DetModel()){
R<DetectionResponse> detectedResult = faceModel.detect(imgPath);
if(detectedResult.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult.getData()));
}else{
log.info("人脸检测失败:{}", detectedResult.getMessage());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 摄像头人脸检测
* 注意事项:如果视频比较卡,可以使用轻量的人脸检测模型
*/
@Test
public void testDetectCamera(){
try (FaceDetModel faceModel = getFaceDetModel()){
OpenCV.loadShared();
VideoCapture capture = new VideoCapture(0);
if (!capture.isOpened()) {
System.out.println("No camera detected");
return;
}
double ratio =
capture.get(Videoio.CAP_PROP_FRAME_WIDTH)
/ capture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int height = (int) (screenSize.height * 0.65f);
int width = (int) (height * ratio);
if (width > screenSize.width) {
width = screenSize.width;
}
Mat image = new Mat();
boolean captured = false;
for (int i = 0; i < 10; ++i) {
captured = capture.read(image);
if (captured) {
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException ignore) {
// ignore
}
}
if (!captured) {
JOptionPane.showConfirmDialog(null, "Failed to capture image from WebCam.");
}
ViewerFrame frame = new ViewerFrame(width, height);
ImageFactory factory = ImageFactory.getInstance();
Size size = new Size(width, height);
while (capture.isOpened()) {
if (!capture.read(image)) {
break;
}
Mat resizeImage = new Mat();
Imgproc.resize(image, resizeImage, size);
Image img = factory.fromImage(resizeImage);
BufferedImage bufferedImage = OpenCVUtils.mat2Image(resizeImage);
R<DetectionResponse> detectedResult = faceModel.detect(bufferedImage);
if(!detectedResult.isSuccess()){
log.debug("识别失败:{}", detectedResult.getMessage());
continue;
}
for(DetectionInfo detectionInfo : detectedResult.getData().getDetectionInfoList()){
DetectionRectangle detectionRectangle = detectionInfo.getDetectionRectangle();
String text = null;
if(detectionInfo.getScore() > 0){
text = detectionInfo.getScore() + "";
}
ImageUtils.drawImageRectWithText(bufferedImage, detectionRectangle, text, Color.red);
}
frame.showImage(bufferedImage);
}
capture.release();
System.exit(0);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,365 @@
package smartai.examples.face.facerec;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.entity.face.FaceSearchResult;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.face.config.FaceDetConfig;
import cn.smartjavaai.face.config.FaceRecConfig;
import cn.smartjavaai.face.constant.FaceDetectConstant;
import cn.smartjavaai.face.entity.FaceRegisterInfo;
import cn.smartjavaai.face.entity.FaceSearchParams;
import cn.smartjavaai.face.enums.FaceDetModelEnum;
import cn.smartjavaai.face.enums.FaceRecModelEnum;
import cn.smartjavaai.face.enums.IdStrategy;
import cn.smartjavaai.face.enums.SimilarityType;
import cn.smartjavaai.face.factory.FaceDetModelFactory;
import cn.smartjavaai.face.factory.FaceRecModelFactory;
import cn.smartjavaai.face.model.facedect.FaceDetModel;
import cn.smartjavaai.face.model.facerec.FaceRecModel;
import cn.smartjavaai.face.utils.SimilarityUtil;
import cn.smartjavaai.face.vector.config.MilvusConfig;
import cn.smartjavaai.face.vector.config.SQLiteConfig;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.List;
/**
* FaceNet人脸算法模型demo
* 支持系统windows 64位linux 64位macOS M系列芯片
* 支持功能人脸特征提取、人脸比对11、人脸比对1N、人脸注册
* 模型下载地址https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
* @author dwj
* @date 2025/4/11
*/
@Slf4j
public class FaceRecDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
/**
* 获取人脸检测模型
* @return
*/
public FaceDetModel getFaceDetModel(){
FaceDetConfig config = new FaceDetConfig();
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸检测模型
config.setConfidenceThreshold(FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
config.setNmsThresh(FaceDetectConstant.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
config.setDevice(device);
return FaceDetModelFactory.getInstance().getModel(config);
}
/**
* 获取人脸识别模型
* @return
*/
public FaceRecModel getFaceRecModel(){
FaceRecConfig config = new FaceRecConfig();
config.setModelEnum(FaceRecModelEnum.FACENET_MODEL);
// config.setModelPath("/Users/xxx/Documents/develop/model/elasticface.pt");
// config.setModelPath("/Users/xxx/Documents/develop/model/InsightFace/model_mobilefacenet.pt");
//裁剪人脸如果图片已经是裁剪过的则请将此参数设置为false
config.setCropFace(true);
//开启人脸对齐:适用于人脸不正的场景,开启将提升人脸特征准确度,关闭可以提升性能
config.setAlign(true);
config.setDevice(device);
//指定人脸检测模型
config.setDetectModel(getFaceDetModel());
return FaceRecModelFactory.getInstance().getModel(config);
}
/**
* 获取人脸识别模型(带向量数据库配置)
* @return
*/
public FaceRecModel getFaceRecModelWithDbConfig(){
FaceRecConfig config = new FaceRecConfig();
config.setModelEnum(FaceRecModelEnum.ELASTIC_FACE_MODEL);//人脸检测模型
config.setModelPath("/Users/xxx/Documents/develop/model/elasticface.pt");
//裁剪人脸如果图片已经是裁剪过的则请将此参数设置为false
config.setCropFace(true);
//开启人脸对齐:适用于人脸不正的场景,开启将提升人脸特征准确度,关闭可以提升性能
config.setAlign(true);
//指定人脸检测模型
config.setDetectModel(getFaceDetModel());
config.setDevice(device);
//初始化向量数据库Milvus数据库配置
MilvusConfig vectorDBConfig = new MilvusConfig();
vectorDBConfig.setHost("127.0.0.1");
vectorDBConfig.setPort(19530);
//vectorDBConfig.setCollectionName("face5");
//ID策略自动生成
vectorDBConfig.setIdStrategy(IdStrategy.AUTO);
//索引类型:内积 (Inner Product) 不建议修改
//vectorDBConfig.setMetricType(MetricType.IP);
config.setVectorDBConfig(vectorDBConfig);
return FaceRecModelFactory.getInstance().getModel(config);
}
/**
* 获取人脸识别模型(带SQLite数据库配置)
* @return
*/
public FaceRecModel getFaceRecModelWithSQLiteConfig(){
FaceRecConfig config = new FaceRecConfig();
config.setModelEnum(FaceRecModelEnum.FACENET_MODEL);//人脸检测模型
//裁剪人脸如果图片已经是裁剪过的则请将此参数设置为false
config.setCropFace(true);
//开启人脸对齐:适用于人脸不正的场景,开启将提升人脸特征准确度,关闭可以提升性能
config.setAlign(true);
//指定人脸检测模型
config.setDetectModel(getFaceDetModel());
config.setDevice(device);
//初始化SQLite数据库
SQLiteConfig vectorDBConfig = new SQLiteConfig();
vectorDBConfig.setSimilarityType(SimilarityType.IP);
config.setVectorDBConfig(vectorDBConfig);
return FaceRecModelFactory.getInstance().getModel(config);
}
/**
* 提取人脸特征(多人脸场景)
* 自动裁剪人脸(处理耗时略有增加)
* 注意事项:
* 1、首次调用接口可能会较慢。只要不关闭程序后续调用会明显加快。若每次重启程序则每次首次调用都将重新加载仍会较慢。
* 2、若人脸朝向不正可开启人脸对齐以提升特征提取准确度。方法参考自定义配置人脸特征提取
*/
@Test
public void testExtractFeatures(){
try (FaceRecModel faceRecModel = getFaceRecModel()){
//提取图片中所有人脸特征
R<DetectionResponse> faceResult = faceRecModel.extractFeatures("src/main/resources/iu_1.jpg");
if(faceResult.isSuccess()){
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(faceResult.getData()));
}else{
log.info("人脸特征提取失败:{}", faceResult.getMessage());
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 人脸比对11基于图像直接比对
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 比对两张图片中提取的人脸特征。(接口内自动完成)
* 注意事项:
* 1、首次调用接口可能会较慢。只要不关闭程序后续调用会明显加快。若每次重启程序则每次首次调用都将重新加载仍会较慢。
* 2、若人脸朝向不正可开启人脸对齐以提升特征提取准确度。方法参考自定义配置人脸特征提取
* @throws Exception
*/
@Test
public void featureComparison(){
try (FaceRecModel faceRecModel = getFaceRecModel()){
//基于图像直接比对人脸特征
R<Float> similarResult = faceRecModel.featureComparison("src/main/resources/iu_1.jpg","src/main/resources/iu_2.jpg");
if(similarResult.isSuccess()){
//相似度阈值不同模型不同,具体参看文档
log.info("人脸比对相似度:{}", JSONObject.toJSONString(similarResult.getData()));
}else{
log.info("人脸比对失败:{}", similarResult.getMessage());
}
}
catch (Exception e){
e.printStackTrace();
}
}
/**
* 人脸比对11基于特征值比对
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 比对两张图片中提取的人脸特征。
* 注意事项:
* 1、首次调用接口可能会较慢。只要不关闭程序后续调用会明显加快。若每次重启程序则每次首次调用都将重新加载仍会较慢。
* 2、若人脸朝向不正可开启人脸对齐以提升特征提取准确度。方法参考自定义配置人脸特征提取
* @throws Exception
*/
@Test
public void featureComparison2(){
try (FaceRecModel faceRecModel = getFaceRecModel()){
//特征提取(提取分数最高人脸特征),适用于单人脸场景
R<float[]> featureResult1 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_1.jpg");
if(featureResult1.isSuccess()){
log.info("图片1人脸特征提取成功{}", JSONObject.toJSONString(featureResult1.getData()));
}else{
log.info("图片1人脸特征提取失败{}", featureResult1.getMessage());
return;
}
//特征提取(提取分数最高人脸特征),适用于单人脸场景
R<float[]> featureResult2 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_2.jpg");
if(featureResult2.isSuccess()){
log.info("图片2人脸特征提取成功{}", JSONObject.toJSONString(featureResult2.getData()));
}else{
log.info("图片2人脸特征提取失败{}", featureResult2.getMessage());
return;
}
//计算相似度
float similar = faceRecModel.calculSimilar(featureResult1.getData(), featureResult2.getData());
log.info("相似度:{}", similar);
}
catch (Exception e){
e.printStackTrace();
}
}
/**
* 人脸注册 + 人脸更新 + 人脸查询 + 人脸删除使用向量数据库Milvus
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 注册人脸
* 注意事项:
* 1、首次调用接口可能会较慢。只要不关闭程序后续调用会明显加快。若每次重启程序则每次首次调用都将重新加载仍会较慢。
* 2、若人脸朝向不正可开启人脸对齐以提升特征提取准确度。方法参考自定义配置人脸特征提取
* @throws Exception
*/
@Test
public void searchFace(){
try (FaceRecModel faceRecModel = getFaceRecModelWithDbConfig()){
//等待加载人脸库结束
while (!faceRecModel.isLoadFaceCompleted()){
Thread.sleep(100);
}
log.info("====================人脸注册==========================");
//特征提取(提取分数最高人脸特征),适用于单人脸场景
R<float[]> featureResult = faceRecModel.extractTopFaceFeature("src/main/resources/iu_1.jpg");
if(featureResult.isSuccess()){
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult.getData()));
}else{
log.info("人脸特征提取失败:{}", featureResult.getMessage());
return;
}
//人脸注册信息
FaceRegisterInfo faceRegisterInfo = new FaceRegisterInfo();
//设置人脸注册的自定义元数据,本例中使用 JSON 格式存储用户信息
JSONObject metadataJson = new JSONObject();
metadataJson.put("name", "iu");
metadataJson.put("age", "25");
//faceRegisterInfo.setId("001");
faceRegisterInfo.setMetadata(metadataJson.toJSONString());
//人脸注册返回人脸库ID
R<String> registerResult = faceRecModel.register(faceRegisterInfo, featureResult.getData());
if(registerResult.isSuccess()){
log.info("注册成功ID-{}", registerResult.getData());
}else{
log.info("注册失败:{}", registerResult.getMessage());
}
/*log.info("====================人脸更新==========================");
//更新人脸 只支持自定义IDvectorDBConfig.setIdStrategy(IdStrategy.CUSTOM);
FaceRegisterInfo updateInfo = new FaceRegisterInfo();
//设置人脸注册的自定义元数据,本例中使用 JSON 格式存储用户信息
JSONObject metadataJsonUpdate = new JSONObject();
metadataJsonUpdate.put("name", "iu_update");
metadataJsonUpdate.put("age", "25");
updateInfo.setMetadata(metadataJsonUpdate.toJSONString());
//更新必须设置ID,只有
updateInfo.setId(registerResult.getData());
faceRecModel.upsertFace(updateInfo, "src/main/resources/iu_2.jpg");
log.info("更新人脸成功");*/
log.info("====================人脸查询==========================");
//特征提取(提取分数最高人脸特征),适用于单人脸场景
R<float[]> featureResult2 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_3.jpg");
if(featureResult2.isSuccess()){
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult2.getData()));
}else{
log.info("人脸特征提取失败:{}", featureResult2.getMessage());
return;
}
FaceSearchParams faceSearchParams = new FaceSearchParams();
faceSearchParams.setTopK(1);
faceSearchParams.setThreshold(0.8f);
List<FaceSearchResult> faceSearchResults = faceRecModel.search(featureResult2.getData(), faceSearchParams);
// R<DetectionResponse> faceSearchResults = faceModel.search("src/main/resources/face/iu_3.jpg", faceSearchParams);
log.info("人脸查询结果:{}", JSONArray.toJSONString(faceSearchResults));
log.info("====================人脸删除==========================");
faceRecModel.removeRegister(registerResult.getData());
log.info("人脸删除成功");
}
catch (Exception e){
e.printStackTrace();
}
}
/**
* 人脸注册 + 人脸更新 + 人脸查询 + 人脸删除使用轻量数据库SQLite
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 注册人脸
* 注意事项:
* 1、首次调用接口可能会较慢。只要不关闭程序后续调用会明显加快。若每次重启程序则每次首次调用都将重新加载仍会较慢。
* 2、若人脸朝向不正可开启人脸对齐以提升特征提取准确度。方法参考自定义配置人脸特征提取
* @throws Exception
*/
@Test
public void searchFace2(){
try (FaceRecModel faceRecModel = getFaceRecModelWithSQLiteConfig()){
//等待加载人脸库结束
while (!faceRecModel.isLoadFaceCompleted()){
Thread.sleep(100);
}
log.info("====================人脸注册==========================");
//特征提取(提取分数最高人脸特征),适用于单人脸场景
R<float[]> featureResult = faceRecModel.extractTopFaceFeature("src/main/resources/iu_1.jpg");
if(featureResult.isSuccess()){
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult.getData()));
}else{
log.info("人脸特征提取失败:{}", featureResult.getMessage());
return;
}
//人脸注册信息
FaceRegisterInfo faceRegisterInfo = new FaceRegisterInfo();
//设置人脸注册的自定义元数据,本例中使用 JSON 格式存储用户信息
JSONObject metadataJson = new JSONObject();
metadataJson.put("name", "iu");
metadataJson.put("age", "25");
faceRegisterInfo.setMetadata(metadataJson.toJSONString());
//可自定义 ID若未设置则自动生成。
//faceRegisterInfo.setId("00001");
//人脸注册返回人脸库ID
R<String> registerResult = faceRecModel.register(faceRegisterInfo, featureResult.getData());
if(registerResult.isSuccess()){
log.info("注册成功ID-{}", registerResult.getData());
}else{
log.info("注册失败:{}", registerResult.getMessage());
}
log.info("====================人脸更新==========================");
FaceRegisterInfo updateInfo = new FaceRegisterInfo();
//设置人脸注册的自定义元数据,本例中使用 JSON 格式存储用户信息
JSONObject metadataJsonUpdate = new JSONObject();
metadataJsonUpdate.put("name", "iu_update");
metadataJsonUpdate.put("age", "25");
updateInfo.setMetadata(metadataJsonUpdate.toJSONString());
//更新必须设置ID,只有
updateInfo.setId(registerResult.getData());
faceRecModel.upsertFace(updateInfo, "src/main/resources/iu_2.jpg");
log.info("更新人脸成功");
log.info("====================人脸查询==========================");
//特征提取(提取分数最高人脸特征),适用于单人脸场景
R<float[]> featureResult2 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_3.jpg");
if(featureResult2.isSuccess()){
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult2.getData()));
}else{
log.info("人脸特征提取失败:{}", featureResult2.getMessage());
return;
}
FaceSearchParams faceSearchParams = new FaceSearchParams();
faceSearchParams.setTopK(1);
faceSearchParams.setThreshold(0.8f);
List<FaceSearchResult> faceSearchResults = faceRecModel.search(featureResult2.getData(), faceSearchParams);
log.info("人脸查询结果:{}", JSONArray.toJSONString(faceSearchResults));
log.info("====================人脸删除==========================");
faceRecModel.removeRegister(registerResult.getData());
log.info("人脸删除成功");
}
catch (Exception e){
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,322 @@
package smartai.examples.face.liveness;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import cn.hutool.core.lang.UUID;
import cn.smartjavaai.common.entity.DetectionInfo;
import cn.smartjavaai.common.entity.DetectionRectangle;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.entity.face.ExpressionResult;
import cn.smartjavaai.common.entity.face.LivenessResult;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.common.enums.face.LivenessStatus;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.common.utils.OpenCVUtils;
import cn.smartjavaai.face.config.FaceDetConfig;
import cn.smartjavaai.face.config.LivenessConfig;
import cn.smartjavaai.face.constant.FaceDetectConstant;
import cn.smartjavaai.face.constant.LivenessConstant;
import cn.smartjavaai.face.enums.FaceDetModelEnum;
import cn.smartjavaai.face.enums.LivenessModelEnum;
import cn.smartjavaai.face.exception.FaceException;
import cn.smartjavaai.face.factory.FaceDetModelFactory;
import cn.smartjavaai.face.factory.LivenessModelFactory;
import cn.smartjavaai.face.model.expression.ExpressionModel;
import cn.smartjavaai.face.model.facedect.FaceDetModel;
import cn.smartjavaai.face.model.liveness.LivenessDetModel;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import nu.pattern.OpenCV;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameUtils;
import org.junit.Test;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
import smartai.examples.face.ViewerFrame;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
/**
* 静态活体检测demo
* 模型下载地址https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
* @author dwj
* @date 2025/5/1
*/
@Slf4j
public class LivenessDetDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
/**
* 获取活体检测模型
* @return
*/
public LivenessDetModel getLivenessDetModel(){
LivenessConfig config = new LivenessConfig();
config.setModelEnum(LivenessModelEnum.IIC_FL_MODEL);
config.setDevice(device);
//需替换为实际模型存储路径
config.setModelPath("/Users/xxx/Documents/develop/model/anti/model.onnx");
//人脸活体阈值,可选,默认0.8,超过阈值则认为是真人,低于阈值是非活体
config.setRealityThreshold(LivenessConstant.DEFAULT_REALITY_THRESHOLD);
/*视频检测帧数可选默认10输出帧数超过这个number之后就可以输出识别结果。
这个数量相当于多帧识别结果融合的融合的帧数。当输入的帧数超过设定帧数的时候,会采用滑动窗口的方式,返回融合的最近输入的帧融合的识别结果。
一般来说在10以内帧数越多结果越稳定相对性能越好但是得到结果的延时越高。*/
config.setFrameCount(LivenessConstant.DEFAULT_FRAME_COUNT);
//指定人脸检测模型
config.setDetectModel(getFaceDetModel());
return LivenessModelFactory.getInstance().getModel(config);
}
/**
* 获取活体检测模型(小视科技模型)
* 备注:小视科技活体检测是两个模型融合结果
* @return
*/
public LivenessDetModel getMiniVisionLivenessDetModel(){
LivenessConfig config = new LivenessConfig();
config.setModelEnum(LivenessModelEnum.MINI_VISION_MODEL);
config.setDevice(device);
//模型1路径需替换为实际模型存储路径
config.setModelPath("/Users/xxx/Documents/develop/model/live/2.7_80x80_MiniFASNetV2.onnx");
//SE模型路径需替换为实际模型存储路径
config.putCustomParam("seModelPath", "/Users/xxx/Documents/develop/model/live/4_0_0_80x80_MiniFASNetV1SE.onnx");
//人脸活体阈值,可选,超过阈值则认为是真人,低于阈值是非活体
config.setRealityThreshold(0.5f);
/*视频检测帧数可选默认10输出帧数超过这个number之后就可以输出识别结果。
这个数量相当于多帧识别结果融合的融合的帧数。当输入的帧数超过设定帧数的时候,会采用滑动窗口的方式,返回融合的最近输入的帧融合的识别结果。
一般来说在10以内帧数越多结果越稳定相对性能越好但是得到结果的延时越高。*/
config.setFrameCount(LivenessConstant.DEFAULT_FRAME_COUNT);
//指定人脸检测模型
config.setDetectModel(getFaceDetModel());
return LivenessModelFactory.getInstance().getModel(config);
}
/**
* 获取人脸检测模型
* @return
*/
public FaceDetModel getFaceDetModel(){
FaceDetConfig config = new FaceDetConfig();
config.setModelEnum(FaceDetModelEnum.RETINA_FACE);//人脸检测模型
config.setConfidenceThreshold(FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
config.setNmsThresh(FaceDetectConstant.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
config.setDevice(device);
return FaceDetModelFactory.getInstance().getModel(config);
}
/**
* 图片活体检测(多人脸)
*/
@Test
public void testLivenessDetect(){
try (LivenessDetModel livenessDetModel = getLivenessDetModel()){
R<DetectionResponse> response = livenessDetModel.detect("src/main/resources/liveness/1.jpg");
if(response.isSuccess()){
for (DetectionInfo detectionInfo : response.getData().getDetectionInfoList()){
log.info("活体检测结果:{}", JSONObject.toJSONString(detectionInfo.getFaceInfo().getLivenessStatus().getStatus().getDescription()));
}
}else{
log.info("活体检测失败:{}", response.getMessage());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 图片活体检测(分数最高人脸)
*/
@Test
public void testLivenessDetect2(){
try (LivenessDetModel livenessDetModel = getLivenessDetModel()){
//指定文件夹路径
File dir = new File("face-example/src/main/resources/liveness");
File[] files = dir.listFiles();
for (File file : files) {
R<LivenessResult> response = livenessDetModel.detectTopFace(ImageIO.read(file));
if(response.isSuccess()){
log.info("{}活体检测结果:{},分数:{}", file.getName(), response.getData().getStatus().getDescription(), response.getData().getScore());
}else{
log.info("{}活体检测失败:{}", file.getName(), response.getMessage());
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 图片多人脸活体检测(基于已检测出的人脸区域和关键点)
*/
@Test
public void testLivenessDetect3(){
try (FaceDetModel faceDetectModel = getFaceDetModel();
LivenessDetModel livenessDetModel = getLivenessDetModel()){
// 将图片路径转换为 BufferedImage
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/liveness/1.jpg").toAbsolutePath().toString()));
//人脸检测
R<DetectionResponse> detectionResponse = faceDetectModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
R<List<LivenessResult>> livenessResult = livenessDetModel.detect(image, detectionResponse.getData());
if(livenessResult.isSuccess()){
log.info("活体检测结果:{}", JSONObject.toJSONString(livenessResult.getData()));
}else{
log.error("活体检测失败:{}", livenessResult.getMessage());
}
}else{
log.info("未检测到人脸");
}
}else{
log.error("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 图片单人脸活体检测(基于已检测出的人脸区域和关键点)
*/
@Test
public void testLivenessDetect4(){
try (FaceDetModel faceDetModel = getFaceDetModel();
LivenessDetModel livenessDetModel = getMiniVisionLivenessDetModel()){
// 将图片路径转换为 BufferedImage
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/liveness/1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detResult = faceDetModel.detect(image);
if(detResult.isSuccess()){
for (DetectionInfo detectionInfo : detResult.getData().getDetectionInfoList()) {
//seetaface6 需要有5点人脸关键点
//R<LivenessResult> result = livenessDetModel.detect(image, detectionInfo.getDetectionRectangle(), detectionInfo.getFaceInfo().getKeyPoints());
R<LivenessResult> result = livenessDetModel.detect(image, detectionInfo.getDetectionRectangle());
if(result.isSuccess()){
log.info("识别结果:{}", JSONObject.toJSONString(result.getData()));
}else{
log.info("识别失败:{}", result.getMessage());
}
}
}else{
log.info("人脸检测失败:{}", detResult.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 视频活体检测
*/
@Test
public void testLivenessDetectVideo(){
try (LivenessDetModel livenessDetModel = getLivenessDetModel()){
//视频路径
R<LivenessResult> livenessStatus = livenessDetModel.detectVideo("video.mp4");
if (livenessStatus.isSuccess()){
log.info("识别结果:{}", JSONObject.toJSONString(livenessStatus.getData()));
}else{
log.info("识别失败:{}", livenessStatus.getMessage());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 摄像头活体检测
* 注意事项:如果视频比较卡,可以使用轻量的人脸检测模型
*/
@Test
public void testLivenessDetectCamera(){
try (LivenessDetModel livenessDetModel = getLivenessDetModel()){
OpenCV.loadShared();
VideoCapture capture = new VideoCapture(0);
if (!capture.isOpened()) {
System.out.println("No camera detected");
return;
}
double ratio =
capture.get(Videoio.CAP_PROP_FRAME_WIDTH)
/ capture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int height = (int) (screenSize.height * 0.65f);
int width = (int) (height * ratio);
if (width > screenSize.width) {
width = screenSize.width;
}
Mat image = new Mat();
boolean captured = false;
for (int i = 0; i < 10; ++i) {
captured = capture.read(image);
if (captured) {
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException ignore) {
// ignore
}
}
if (!captured) {
JOptionPane.showConfirmDialog(null, "Failed to capture image from WebCam.");
}
ViewerFrame frame = new ViewerFrame(width, height);
ImageFactory factory = ImageFactory.getInstance();
Size size = new Size(width, height);
while (capture.isOpened()) {
if (!capture.read(image)) {
break;
}
Mat resizeImage = new Mat();
Imgproc.resize(image, resizeImage, size);
Image img = factory.fromImage(resizeImage);
BufferedImage bufferedImage = OpenCVUtils.mat2Image(resizeImage);
R<DetectionResponse> detectedResult = livenessDetModel.detect(bufferedImage);
if(!detectedResult.isSuccess()){
log.debug("识别失败:{}", detectedResult.getMessage());
continue;
}
for(DetectionInfo detectionInfo : detectedResult.getData().getDetectionInfoList()){
DetectionRectangle detectionRectangle = detectionInfo.getDetectionRectangle();
Color color = detectionInfo.getFaceInfo().getLivenessStatus().getStatus() == LivenessStatus.LIVE ? Color.GREEN : Color.RED;
String text = detectionInfo.getFaceInfo().getLivenessStatus().getStatus().getDescription() + ":" + detectionInfo.getFaceInfo().getLivenessStatus().getScore();
ImageUtils.drawImageRectWithText(bufferedImage, detectionRectangle, text, color);
}
frame.showImage(bufferedImage);
}
capture.release();
System.exit(0);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,271 @@
package smartai.examples.face.quality;
import cn.smartjavaai.common.entity.DetectionInfo;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.entity.face.FaceAttribute;
import cn.smartjavaai.common.entity.face.FaceInfo;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.face.config.FaceAttributeConfig;
import cn.smartjavaai.face.config.FaceDetConfig;
import cn.smartjavaai.face.config.QualityConfig;
import cn.smartjavaai.face.entity.FaceQualityResult;
import cn.smartjavaai.face.entity.FaceQualitySummary;
import cn.smartjavaai.face.enums.FaceAttributeModelEnum;
import cn.smartjavaai.face.enums.FaceDetModelEnum;
import cn.smartjavaai.face.enums.QualityModelEnum;
import cn.smartjavaai.face.factory.FaceAttributeModelFactory;
import cn.smartjavaai.face.factory.FaceDetModelFactory;
import cn.smartjavaai.face.factory.FaceQualityModelFactory;
import cn.smartjavaai.face.model.attribute.FaceAttributeModel;
import cn.smartjavaai.face.model.facedect.FaceDetModel;
import cn.smartjavaai.face.model.quality.FaceQualityModel;
import cn.smartjavaai.face.utils.FaceUtils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Paths;
/**
* 人脸质量评估 demo
* 模型下载地址https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
* @author dwj
*/
@Slf4j
public class FaceQualityDetDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
/**
* 获取质量评估模型
* @return
*/
public FaceQualityModel getFaceQualityModel() {
QualityConfig config = new QualityConfig();
config.setModelEnum(QualityModelEnum.SEETA_FACE6_MODEL);
//需替换为实际模型存储路径
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
config.setDevice(device);
return FaceQualityModelFactory.getInstance().getModel(config);
}
/**
* 获取人脸检测模型
* @return
*/
public FaceDetModel getFaceDetModel() {
//需替换为实际模型存储路径
String modelPath = "C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models";
FaceDetConfig faceDetectModelConfig = new FaceDetConfig();
faceDetectModelConfig.setModelEnum(FaceDetModelEnum.SEETA_FACE6_MODEL);
faceDetectModelConfig.setModelPath(modelPath);
faceDetectModelConfig.setDevice(device);
return FaceDetModelFactory.getInstance().getModel(faceDetectModelConfig);
}
/**
* 人脸亮度评估
*/
@Test
public void evaluateBrightness(){
try (FaceQualityModel faceQualityModel = getFaceQualityModel();
FaceDetModel faceDetModel = getFaceDetModel()){
//人脸检测
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detectionResponse = faceDetModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
for (DetectionInfo detectionInfo : detectionResponse.getData().getDetectionInfoList()){
FaceInfo faceInfo = detectionInfo.getFaceInfo();
R<FaceQualityResult> faceQualityResultR = faceQualityModel.evaluateBrightness(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
if(faceQualityResultR.isSuccess()){
log.info("人脸亮度评估结果:{}", JSONObject.toJSONString(faceQualityResultR.getData()));
}else{
log.info("人脸亮度评估失败:{}", faceQualityResultR.getMessage());
}
}
}
}else{
log.info("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸完整度评估
*/
@Test
public void evaluateCompleteness(){
try (FaceQualityModel faceQualityModel = getFaceQualityModel();
FaceDetModel faceDetModel = getFaceDetModel()){
//人脸检测
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detectionResponse = faceDetModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
for (DetectionInfo detectionInfo : detectionResponse.getData().getDetectionInfoList()){
FaceInfo faceInfo = detectionInfo.getFaceInfo();
R<FaceQualityResult> faceQualityResultR = faceQualityModel.evaluateCompleteness(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
if(faceQualityResultR.isSuccess()){
log.info("人脸完整度评估结果:{}", JSONObject.toJSONString(faceQualityResultR.getData()));
}else{
log.info("人脸完整度评估失败:{}", faceQualityResultR.getMessage());
}
}
}
}else{
log.info("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸清晰度评估
*/
@Test
public void evaluateClarity(){
try (FaceQualityModel faceQualityModel = getFaceQualityModel();
FaceDetModel faceDetModel = getFaceDetModel()){
//人脸检测
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detectionResponse = faceDetModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
for (DetectionInfo detectionInfo : detectionResponse.getData().getDetectionInfoList()){
FaceInfo faceInfo = detectionInfo.getFaceInfo();
R<FaceQualityResult> faceQualityResultR = faceQualityModel.evaluateClarity(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
if(faceQualityResultR.isSuccess()){
log.info("人脸清晰度评估结果:{}", JSONObject.toJSONString(faceQualityResultR.getData()));
}else{
log.info("人脸清晰度评估失败:{}", faceQualityResultR.getMessage());
}
}
}
}else{
log.info("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸姿态评估
*/
@Test
public void evaluatePose(){
try (FaceQualityModel faceQualityModel = getFaceQualityModel();
FaceDetModel faceDetModel = getFaceDetModel()){
//人脸检测
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detectionResponse = faceDetModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
for (DetectionInfo detectionInfo : detectionResponse.getData().getDetectionInfoList()){
FaceInfo faceInfo = detectionInfo.getFaceInfo();
R<FaceQualityResult> faceQualityResultR = faceQualityModel.evaluatePose(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
if(faceQualityResultR.isSuccess()){
log.info("人脸姿态评估结果:{}", JSONObject.toJSONString(faceQualityResultR.getData()));
}else{
log.info("人脸姿态评估失败:{}", faceQualityResultR.getMessage());
}
}
}
}else{
log.info("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸分辨率评估
*/
@Test
public void evaluateResolution(){
try (FaceQualityModel faceQualityModel = getFaceQualityModel();
FaceDetModel faceDetModel = getFaceDetModel()){
//人脸检测
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detectionResponse = faceDetModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
for (DetectionInfo detectionInfo : detectionResponse.getData().getDetectionInfoList()){
FaceInfo faceInfo = detectionInfo.getFaceInfo();
R<FaceQualityResult> faceQualityResultR = faceQualityModel.evaluateResolution(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
if(faceQualityResultR.isSuccess()){
log.info("人脸分辨率评估结果:{}", JSONObject.toJSONString(faceQualityResultR.getData()));
}else{
log.info("人脸分辨率评估失败:{}", faceQualityResultR.getMessage());
}
}
}
}else{
log.info("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 评估所有
*/
@Test
public void evaluateAll(){
try (FaceQualityModel faceQualityModel = getFaceQualityModel();
FaceDetModel faceDetModel = getFaceDetModel()){
//人脸检测
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/iu_1.jpg").toAbsolutePath().toString()));
R<DetectionResponse> detectionResponse = faceDetModel.detect(image);
if(detectionResponse.isSuccess()){
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectionResponse.getData()));
//检测到人脸
if(detectionResponse.getData() != null && detectionResponse.getData().getDetectionInfoList() != null && detectionResponse.getData().getDetectionInfoList().size() > 0){
for (DetectionInfo detectionInfo : detectionResponse.getData().getDetectionInfoList()){
FaceInfo faceInfo = detectionInfo.getFaceInfo();
R<FaceQualitySummary> faceQualityResultR = faceQualityModel.evaluateAll(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
if(faceQualityResultR.isSuccess()){
log.info("人脸评估结果:{}", JSONObject.toJSONString(faceQualityResultR.getData()));
}else{
log.info("人脸评估失败:{}", faceQualityResultR.getMessage());
}
}
}
}else{
log.info("人脸检测失败:{}", detectionResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: smartai.examples.face.SeetaFace6LinuxDemo

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 步骤2: 配置文件 (src/main/resources/logback.xml) -->
<configuration scan="true" scanPeriod="30 seconds">
<!-- 控制台日志输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</configuration>