1、人脸模块:新增小视科技(MiniVision)活体检测模型
2、人脸模块:新增阿里通义工作室活体检测模型 3、人脸模块:新增2个表情识别模型 4、人脸模块:新增InsightFace、ElasticFace人脸识别模型 5、人脸模块:新增Seetaface6质量评估模型 6、目标检测模块:开放更多自定义模型参数 7、人脸模块:支持base64图片 8、实现接口 AutoCloseable,支持资源的自动释放 9、OCR模块:解决加方向矫正后无法连续识别bug 10、人脸模块:解决人脸更新后缓存问题 11、优化部分功能
16
README.md
@@ -198,6 +198,7 @@ SmartJavaAI是专为JAVA 开发者打造的一个功能丰富、开箱即用的
|
||||
### ✅ 已实现功能
|
||||
|
||||
- **人脸识别**
|
||||
- 支持模型:
|
||||
- 人脸检测、人脸识别、人脸比对1:1、人脸比对1:N(支持向量数据库milvus/sqlite)、人脸库注册、人脸库删除
|
||||
- 5点人脸关键点定位
|
||||
- 人脸属性检测(性别、年龄、口罩、眼睛状态、脸部姿态)
|
||||
@@ -279,7 +280,7 @@ SmartJavaAI是专为JAVA 开发者打造的一个功能丰富、开箱即用的
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-all</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
</dependency>
|
||||
```
|
||||
### 3、完整示例代码
|
||||
@@ -318,6 +319,19 @@ SmartJavaAI是专为JAVA 开发者打造的一个功能丰富、开箱即用的
|
||||
|
||||
## 近期更新日志
|
||||
|
||||
## [v1.0.19] - 2025-07-06
|
||||
- 人脸模块:新增小视科技(MiniVision)活体检测模型
|
||||
- 人脸模块:新增阿里通义工作室活体检测模型
|
||||
- 人脸模块:新增 2 个表情识别模型
|
||||
- 人脸模块:新增 InsightFace 和 ElasticFace 人脸识别模型
|
||||
- 人脸模块:新增 Seetaface6 质量评估模型
|
||||
- 目标检测模块:支持更多自定义模型参数配置
|
||||
- 人脸模块:支持 Base64 编码图片输入
|
||||
- 通用功能:实现 AutoCloseable 接口,支持资源自动释放
|
||||
- OCR 模块:修复加方向矫正后无法连续识别的问题
|
||||
- 人脸模块:修复人脸更新后的缓存异常问题
|
||||
- 其他:优化部分功能与细节体验
|
||||
|
||||
## [v1.0.17] - 2025-06-18
|
||||
- 新增机器翻译模块:支持 200+ 种语言之间的相互翻译
|
||||
- 人脸识别模块:修复批量删除人脸数据时的异常问题
|
||||
|
||||
7
examples/face-example/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.idea
|
||||
.idea/
|
||||
target
|
||||
log
|
||||
*.iml
|
||||
/.settings/
|
||||
/logging.file_IS_UNDEFINED/
|
||||
103
examples/face-example/README.md
Normal 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)
|
||||
|
||||
---
|
||||
308
examples/face-example/pom.xml
Normal 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>
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,33 +1,30 @@
|
||||
package smartai.examples.face.facerec;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.FaceSearchResult;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.face.config.FaceExtractConfig;
|
||||
import cn.smartjavaai.face.config.FaceModelConfig;
|
||||
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.FaceResult;
|
||||
import cn.smartjavaai.face.entity.FaceSearchParams;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
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.FaceModelFactory;
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
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 io.milvus.param.MetricType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
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.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -39,11 +36,97 @@ import java.util.List;
|
||||
* @date 2025/4/11
|
||||
*/
|
||||
@Slf4j
|
||||
public class FaceNetDemo {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取人脸特征(多人脸场景)
|
||||
* 默认使用检测模型:ULTRA_LIGHT_FAST_GENERIC_FACE
|
||||
* 自动裁剪人脸(处理耗时略有增加)
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
@@ -51,14 +134,9 @@ public class FaceNetDemo {
|
||||
*/
|
||||
@Test
|
||||
public void testExtractFeatures(){
|
||||
try {
|
||||
//人脸特征提取模型
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.FACENET_MODEL);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
log.info("人脸特征提取模型加载成功");
|
||||
try (FaceRecModel faceRecModel = getFaceRecModel()){
|
||||
//提取图片中所有人脸特征
|
||||
R<DetectionResponse> faceResult = faceModel.extractFeatures("src/main/resources/face/iu_1.jpg");
|
||||
R<DetectionResponse> faceResult = faceRecModel.extractFeatures("src/main/resources/iu_1.jpg");
|
||||
if(faceResult.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(faceResult.getData()));
|
||||
}else{
|
||||
@@ -69,46 +147,6 @@ public class FaceNetDemo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取人脸特征(自定义配置)
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
* 2、若人脸朝向不正,可开启人脸对齐以提升特征提取准确度。(方法参考自定义配置人脸特征提取)
|
||||
*/
|
||||
@Test
|
||||
public void testExtractFeaturesWithCustomConfig(){
|
||||
try {
|
||||
//人脸模型参数
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.FACENET_MODEL);
|
||||
//人脸特征提取参数
|
||||
FaceExtractConfig extractConfig = new FaceExtractConfig();
|
||||
//当关闭人脸裁剪时,程序将跳过人脸检测与裁剪流程,直接进行特征提取,适用于输入已为标准人脸区域的图像,有助于提升处理效率。
|
||||
extractConfig.setCropFace(true);
|
||||
//开启人脸对齐:适用于人脸不正的场景,开启将提升人脸特征准确度,关闭可以提升性能
|
||||
extractConfig.setAlign(true);
|
||||
//人脸检测模型配置,指定人脸检测模型:ULTRA_LIGHT_FAST_GENERIC_FACE
|
||||
FaceModelConfig detectModelConfig = new FaceModelConfig(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE);
|
||||
//设置人脸检测置信度阈值
|
||||
detectModelConfig.setConfidenceThreshold(0.98);
|
||||
extractConfig.setDetectModel(FaceModelFactory.getInstance().getModel(detectModelConfig));
|
||||
config.setExtractConfig(extractConfig);
|
||||
//获取人脸模型
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult = faceModel.extractTopFaceFeature("src/main/resources/face/iu_1.jpg");
|
||||
if(featureResult.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult.getData()));
|
||||
}else{
|
||||
log.info("人脸特征提取失败:{}", featureResult.getMessage());
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 人脸比对1:1(基于图像直接比对)
|
||||
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 比对两张图片中提取的人脸特征。(接口内自动完成)
|
||||
@@ -119,14 +157,15 @@ public class FaceNetDemo {
|
||||
*/
|
||||
@Test
|
||||
public void featureComparison(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//人脸模型
|
||||
config.setModelEnum(FaceModelEnum.FACENET_MODEL);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
try (FaceRecModel faceRecModel = getFaceRecModel()){
|
||||
//基于图像直接比对人脸特征
|
||||
float similar = faceModel.featureComparison("src/main/resources/face/iu_1.jpg","src/main/resources/face/iu_2.jpg");
|
||||
log.info("相似度:{}", similar);
|
||||
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();
|
||||
@@ -143,13 +182,9 @@ public class FaceNetDemo {
|
||||
*/
|
||||
@Test
|
||||
public void featureComparison2(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//人脸模型
|
||||
config.setModelEnum(FaceModelEnum.FACENET_MODEL);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
try (FaceRecModel faceRecModel = getFaceRecModel()){
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult1 = faceModel.extractTopFaceFeature("src/main/resources/face/iu_1.jpg");
|
||||
R<float[]> featureResult1 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_1.jpg");
|
||||
if(featureResult1.isSuccess()){
|
||||
log.info("图片1人脸特征提取成功:{}", JSONObject.toJSONString(featureResult1.getData()));
|
||||
}else{
|
||||
@@ -157,7 +192,7 @@ public class FaceNetDemo {
|
||||
return;
|
||||
}
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult2 = faceModel.extractTopFaceFeature("src/main/resources/face/iu_2.jpg");
|
||||
R<float[]> featureResult2 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_2.jpg");
|
||||
if(featureResult2.isSuccess()){
|
||||
log.info("图片2人脸特征提取成功:{}", JSONObject.toJSONString(featureResult2.getData()));
|
||||
}else{
|
||||
@@ -165,7 +200,7 @@ public class FaceNetDemo {
|
||||
return;
|
||||
}
|
||||
//计算相似度
|
||||
float similar = faceModel.calculSimilar(featureResult1.getData(), featureResult2.getData());
|
||||
float similar = faceRecModel.calculSimilar(featureResult1.getData(), featureResult2.getData());
|
||||
log.info("相似度:{}", similar);
|
||||
}
|
||||
catch (Exception e){
|
||||
@@ -184,28 +219,14 @@ public class FaceNetDemo {
|
||||
*/
|
||||
@Test
|
||||
public void searchFace(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//人脸模型
|
||||
config.setModelEnum(FaceModelEnum.FACENET_MODEL);
|
||||
//初始化向量数据库: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);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
try (FaceRecModel faceRecModel = getFaceRecModelWithDbConfig()){
|
||||
//等待加载人脸库结束
|
||||
while (!faceModel.isLoadFaceCompleted()){
|
||||
while (!faceRecModel.isLoadFaceCompleted()){
|
||||
Thread.sleep(100);
|
||||
}
|
||||
log.info("====================人脸注册==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult = faceModel.extractTopFaceFeature("src/main/resources/face/iu_1.jpg");
|
||||
R<float[]> featureResult = faceRecModel.extractTopFaceFeature("src/main/resources/iu_1.jpg");
|
||||
if(featureResult.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult.getData()));
|
||||
}else{
|
||||
@@ -218,9 +239,10 @@ public class FaceNetDemo {
|
||||
JSONObject metadataJson = new JSONObject();
|
||||
metadataJson.put("name", "iu");
|
||||
metadataJson.put("age", "25");
|
||||
//faceRegisterInfo.setId("001");
|
||||
faceRegisterInfo.setMetadata(metadataJson.toJSONString());
|
||||
//人脸注册,返回人脸库ID
|
||||
R<String> registerResult = faceModel.register(faceRegisterInfo, featureResult.getData());
|
||||
R<String> registerResult = faceRecModel.register(faceRegisterInfo, featureResult.getData());
|
||||
if(registerResult.isSuccess()){
|
||||
log.info("注册成功:ID-{}", registerResult.getData());
|
||||
}else{
|
||||
@@ -236,11 +258,11 @@ public class FaceNetDemo {
|
||||
updateInfo.setMetadata(metadataJsonUpdate.toJSONString());
|
||||
//更新必须设置ID,只有
|
||||
updateInfo.setId(registerResult.getData());
|
||||
faceModel.upsertFace(updateInfo, "src/main/resources/face/iu_2.jpg");
|
||||
faceRecModel.upsertFace(updateInfo, "src/main/resources/iu_2.jpg");
|
||||
log.info("更新人脸成功");*/
|
||||
log.info("====================人脸查询==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult2 = faceModel.extractTopFaceFeature("src/main/resources/face/iu_3.jpg");
|
||||
R<float[]> featureResult2 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_3.jpg");
|
||||
if(featureResult2.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult2.getData()));
|
||||
}else{
|
||||
@@ -251,11 +273,11 @@ public class FaceNetDemo {
|
||||
faceSearchParams.setTopK(1);
|
||||
faceSearchParams.setThreshold(0.8f);
|
||||
|
||||
List<FaceSearchResult> faceSearchResults = faceModel.search(featureResult2.getData(), faceSearchParams);
|
||||
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("====================人脸删除==========================");
|
||||
faceModel.removeRegister(registerResult.getData());
|
||||
faceRecModel.removeRegister(registerResult.getData());
|
||||
log.info("人脸删除成功");
|
||||
}
|
||||
catch (Exception e){
|
||||
@@ -273,23 +295,14 @@ public class FaceNetDemo {
|
||||
*/
|
||||
@Test
|
||||
public void searchFace2(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//人脸模型
|
||||
config.setModelEnum(FaceModelEnum.FACENET_MODEL);
|
||||
//初始化向量数据库:Milvus数据库配置
|
||||
SQLiteConfig vectorDBConfig = new SQLiteConfig();
|
||||
vectorDBConfig.setDbPath("/Users/wenjie/Downloads/face.db");
|
||||
vectorDBConfig.setSimilarityType(SimilarityType.IP);
|
||||
config.setVectorDBConfig(vectorDBConfig);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
try (FaceRecModel faceRecModel = getFaceRecModelWithSQLiteConfig()){
|
||||
//等待加载人脸库结束
|
||||
while (!faceModel.isLoadFaceCompleted()){
|
||||
while (!faceRecModel.isLoadFaceCompleted()){
|
||||
Thread.sleep(100);
|
||||
}
|
||||
log.info("====================人脸注册==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult = faceModel.extractTopFaceFeature("src/main/resources/face/iu_1.jpg");
|
||||
R<float[]> featureResult = faceRecModel.extractTopFaceFeature("src/main/resources/iu_1.jpg");
|
||||
if(featureResult.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult.getData()));
|
||||
}else{
|
||||
@@ -306,7 +319,7 @@ public class FaceNetDemo {
|
||||
//可自定义 ID,若未设置则自动生成。
|
||||
//faceRegisterInfo.setId("00001");
|
||||
//人脸注册,返回人脸库ID
|
||||
R<String> registerResult = faceModel.register(faceRegisterInfo, featureResult.getData());
|
||||
R<String> registerResult = faceRecModel.register(faceRegisterInfo, featureResult.getData());
|
||||
if(registerResult.isSuccess()){
|
||||
log.info("注册成功:ID-{}", registerResult.getData());
|
||||
}else{
|
||||
@@ -321,11 +334,11 @@ public class FaceNetDemo {
|
||||
updateInfo.setMetadata(metadataJsonUpdate.toJSONString());
|
||||
//更新必须设置ID,只有
|
||||
updateInfo.setId(registerResult.getData());
|
||||
faceModel.upsertFace(updateInfo, "src/main/resources/face/iu_2.jpg");
|
||||
faceRecModel.upsertFace(updateInfo, "src/main/resources/iu_2.jpg");
|
||||
log.info("更新人脸成功");
|
||||
log.info("====================人脸查询==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult2 = faceModel.extractTopFaceFeature("src/main/resources/face/iu_3.jpg");
|
||||
R<float[]> featureResult2 = faceRecModel.extractTopFaceFeature("src/main/resources/iu_3.jpg");
|
||||
if(featureResult2.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(featureResult2.getData()));
|
||||
}else{
|
||||
@@ -335,10 +348,10 @@ public class FaceNetDemo {
|
||||
FaceSearchParams faceSearchParams = new FaceSearchParams();
|
||||
faceSearchParams.setTopK(1);
|
||||
faceSearchParams.setThreshold(0.8f);
|
||||
List<FaceSearchResult> faceSearchResults = faceModel.search(featureResult2.getData(), faceSearchParams);
|
||||
List<FaceSearchResult> faceSearchResults = faceRecModel.search(featureResult2.getData(), faceSearchParams);
|
||||
log.info("人脸查询结果:{}", JSONArray.toJSONString(faceSearchResults));
|
||||
log.info("====================人脸删除==========================");
|
||||
faceModel.removeRegister(registerResult.getData());
|
||||
faceRecModel.removeRegister(registerResult.getData());
|
||||
log.info("人脸删除成功");
|
||||
}
|
||||
catch (Exception e){
|
||||
@@ -347,31 +360,6 @@ public class FaceNetDemo {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用离线模型
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void featureComparisonOffline(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.FACENET_MODEL);//人脸模型
|
||||
//设置人脸识别模型文件路径,请根据实际情况替换为本地模型文件的绝对路径
|
||||
config.setModelPath("/Users/xxx/Documents/develop/face_model/face_feature.pt");
|
||||
//人脸特征提取参数
|
||||
FaceExtractConfig extractConfig = new FaceExtractConfig();
|
||||
FaceModelConfig detectModelConfig = new FaceModelConfig(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE);
|
||||
//设置人脸检测模型文件路径,请根据实际情况替换为本地模型文件的绝对路径
|
||||
detectModelConfig.setModelPath("/Users/xxx/Documents/develop/face_model/ultranet.pt");
|
||||
//人脸检测模型配置
|
||||
extractConfig.setDetectModel(FaceModelFactory.getInstance().getModel(detectModelConfig));
|
||||
config.setExtractConfig(extractConfig);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
BIN
examples/face-example/src/main/resources/emotion/angry.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
examples/face-example/src/main/resources/emotion/disgust.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
examples/face-example/src/main/resources/emotion/fear.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
examples/face-example/src/main/resources/emotion/happy.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
examples/face-example/src/main/resources/emotion/neutral.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
examples/face-example/src/main/resources/emotion/sad.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
examples/face-example/src/main/resources/emotion/surprise.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 463 KiB After Width: | Height: | Size: 463 KiB |
BIN
examples/face-example/src/main/resources/liveness/1.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
examples/face-example/src/main/resources/liveness/2.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
examples/face-example/src/main/resources/liveness/4.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
examples/face-example/src/main/resources/liveness/5.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
examples/face-example/src/main/resources/liveness/6.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
7
examples/objectdetection-example/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.idea
|
||||
.idea/
|
||||
target
|
||||
log
|
||||
*.iml
|
||||
/.settings/
|
||||
/logging.file_IS_UNDEFINED/
|
||||
58
examples/objectdetection-example/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# 目标检测示例
|
||||
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
|
||||
objectdetection-example/
|
||||
├── src/
|
||||
│ ├── main/
|
||||
│ │ ├── java/
|
||||
│ │ │ └── smartai/examples/objectdetection/
|
||||
│ │ │ ├── ObjectDetection.java
|
||||
│ │ │ └── ViewerFrame.java
|
||||
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🧩 功能模块说明
|
||||
|
||||
### 1. 目标检测 [ObjectDetection.java]
|
||||
- **功能**:核心目标检测类,包含多个测试方法,展示了如何使用不同的模型进行目标检测
|
||||
|
||||
---
|
||||
|
||||
|
||||
## ⚙️ 配置要求
|
||||
|
||||
- **运行环境**:
|
||||
- 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)
|
||||
|
||||
---
|
||||
@@ -12,9 +12,9 @@
|
||||
<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.17</smartjavaai.version>
|
||||
<smartjavaai.version>1.0.19</smartjavaai.version>
|
||||
<!--如果打包运行,需要替换成你的main-->
|
||||
<exec.mainClass>smartai.examples.face.facerec.RetinaFaceDemo</exec.mainClass>
|
||||
<exec.mainClass>smartai.examples.objectdetection.ObjectDetection</exec.mainClass>
|
||||
|
||||
<javacv.version>1.5.10</javacv.version>
|
||||
|
||||
@@ -91,30 +91,12 @@
|
||||
<version>4.13.2</version>
|
||||
</dependency>
|
||||
|
||||
<!--人脸识别模块-->
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--目标检测模块-->
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-objectdetection</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--OCR检测模块-->
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-ocr</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--翻译模块-->
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-translate</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>ai.djl.pytorch</groupId>
|
||||
@@ -0,0 +1,249 @@
|
||||
package smartai.examples.objectdetection;
|
||||
|
||||
import ai.djl.Application;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.*;
|
||||
import ai.djl.modality.cv.output.Rectangle;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
import ai.djl.repository.zoo.ModelNotFoundException;
|
||||
import ai.djl.repository.zoo.ModelZoo;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import ai.djl.training.util.ProgressBar;
|
||||
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.model.liveness.LivenessDetModel;
|
||||
import cn.smartjavaai.objectdetection.config.DetectorModelConfig;
|
||||
import cn.smartjavaai.objectdetection.enums.DetectorModelEnum;
|
||||
import cn.smartjavaai.objectdetection.exception.DetectionException;
|
||||
import cn.smartjavaai.objectdetection.model.DetectorModel;
|
||||
import cn.smartjavaai.objectdetection.model.ObjectDetectionModelFactory;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import nu.pattern.OpenCV;
|
||||
import org.junit.Assert;
|
||||
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 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.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* 目标检测模型demo
|
||||
* 支持功能:目标检测
|
||||
* 模型下载地址:https://pan.baidu.com/s/10aTOLBlR6EG-sq6g0OkAWg?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
*/
|
||||
@Slf4j
|
||||
public class ObjectDetection {
|
||||
|
||||
|
||||
//设备类型
|
||||
public static DeviceEnum device = DeviceEnum.CPU;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 使用默认模型检测:YOLO11N
|
||||
*/
|
||||
@Test
|
||||
public void objectDetection(){
|
||||
//默认cpu
|
||||
try (DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel()){
|
||||
DetectionResponse detectionResponse = detectorModel.detect("src/main/resources/object_detection.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detectionResponse));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定模型检测(19种模型可选)
|
||||
*/
|
||||
@Test
|
||||
public void objectDetection2(){
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
config.setModelEnum(DetectorModelEnum.SSD_300_RESNET50);//检测模型,目前支持19种预置模型
|
||||
config.setDevice(device);
|
||||
try (DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config)){
|
||||
DetectionResponse detectionResponse = detectorModel.detect("src/main/resources/dog_bike_car.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detectionResponse));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制检测结果
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionAndDraw(){
|
||||
try (DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel()){
|
||||
detectorModel.detectAndDraw("src/main/resources/object_detection.jpg","output/object_detection_detected.png");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制检测结果,返回BufferedImage
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionAndDraw2(){
|
||||
try (DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel()){
|
||||
String imagePath = "src/main/resources/object_detection.jpg";
|
||||
BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
//可以根据后续业务场景使用detectedImage
|
||||
BufferedImage detectedImage = detectorModel.detectAndDraw(image);
|
||||
Assert.assertNotNull("detectedImage null", detectedImage);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 使用yolo官方模型检测物品识别
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionWithOfficialModel(){
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
config.setThreshold(0.3f);
|
||||
//也支持YoloV8:YOLOV8_OFFICIAL 模型可以从文档中提供的地址下载
|
||||
config.setModelEnum(DetectorModelEnum.YOLOV12_OFFICIAL);//检测模型,目前支持19种模型
|
||||
// 指定模型路径,需要更改为自己的模型路径
|
||||
config.setModelPath("/Users/xxx/Documents/yolov12n.onnx");
|
||||
config.setDevice(device);
|
||||
//一定要将yolo官方的类别文件:synset.txt(文档中下载)放在模型同目录下,否则报错
|
||||
try (DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config)){
|
||||
DetectionResponse detect = detectorModel.detect("src/main/resources/dog_bike_car.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detect));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己训练的模型检测
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionWithCustomModel(){
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
//也支持YoloV8:YOLOV8_CUSTOM 模型需要自己训练,训练教程可以查看文档
|
||||
config.setModelEnum(DetectorModelEnum.YOLOV12_CUSTOM);//自定义YOLOV12模型
|
||||
// 指定模型路径,需要更改为自己的模型路径
|
||||
config.setModelPath("/Users/xxx/Documents/develop/fire_model/best.onnx");
|
||||
config.putCustomParam("width", 640);//resize 宽
|
||||
config.putCustomParam("height", 640);// resize 高
|
||||
config.putCustomParam("nmsThreshold", 0.5f);
|
||||
config.setDevice(device);
|
||||
try (DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config)){
|
||||
DetectionResponse detect = detectorModel.detect("src/main/resources/dog_bike_car.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detect));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 摄像头目标检测
|
||||
* 注意事项:如果视频比较卡,可以使用轻量的检测模型
|
||||
*/
|
||||
@Test
|
||||
public void testDetectCamera(){
|
||||
try (DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel()){
|
||||
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);
|
||||
DetectionResponse detectedResult = detectorModel.detect(bufferedImage);
|
||||
if (Objects.isNull(detectedResult) || Objects.isNull(detectedResult.getDetectionInfoList()) || detectedResult.getDetectionInfoList().size() == 0){
|
||||
log.debug("未检测到物体");
|
||||
continue;
|
||||
}
|
||||
for(DetectionInfo detectionInfo : detectedResult.getDetectionInfoList()){
|
||||
DetectionRectangle detectionRectangle = detectionInfo.getDetectionRectangle();
|
||||
String text = detectionInfo.getObjectDetInfo().getClassName();
|
||||
ImageUtils.drawImageRectWithText(bufferedImage, detectionRectangle, text, Color.RED);
|
||||
}
|
||||
frame.showImage(bufferedImage);
|
||||
}
|
||||
|
||||
capture.release();
|
||||
System.exit(0);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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.objectdetection;
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: smartai.examples.face.SeetaFace6LinuxDemo
|
||||
|
||||
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 463 KiB |
@@ -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>
|
||||
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
7
examples/ocr-examples/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.idea
|
||||
.idea/
|
||||
target
|
||||
log
|
||||
*.iml
|
||||
/.settings/
|
||||
/logging.file_IS_UNDEFINED/
|
||||
74
examples/ocr-examples/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# OCR文字识别示例
|
||||
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
|
||||
src
|
||||
├── main
|
||||
│ ├── java
|
||||
│ │ └── smartai/examples/ocr
|
||||
│ │ ├── OcrDetectionDemo.java # 文本检测示例
|
||||
│ │ ├── OcrDirectionDetDemo.java # 文本方向检测示例
|
||||
│ │ └── OcrRecognizeDemo.java # 文本识别示例
|
||||
│ └── resources
|
||||
│ ├── logback.xml # 日志配置文件
|
||||
└── test
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🧩 功能说明
|
||||
|
||||
### 1. 文本检测 - [OcrDetectionDemo]
|
||||
|
||||
- **功能**:检测图像中的文本区域,仅返回文本框位置,不识别文字内容。
|
||||
|
||||
|
||||
### 2. 文本方向检测 - [OcrDirectionDetDemo]
|
||||
|
||||
- **功能**:在文本检测基础上,判断文本整体方向(0°, 90°, 180°, 270°)。
|
||||
|
||||
### 3. 文本识别 - [OcrRecognizeDemo]
|
||||
|
||||
- **功能**:对检测到的文本区域进行文字识别,支持简体中文、繁体中文、英文、日文等。
|
||||
- **流程**:
|
||||
- 文本检测 → 文本识别(或加上方向矫正)
|
||||
|
||||
---
|
||||
|
||||
|
||||
## ⚙️ 配置要求
|
||||
|
||||
- **运行环境**:
|
||||
- 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)
|
||||
|
||||
---
|
||||
309
examples/ocr-examples/pom.xml
Normal file
@@ -0,0 +1,309 @@
|
||||
<?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>examples</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.ocr.OcrRecognizeDemo</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>
|
||||
|
||||
|
||||
|
||||
<!--OCR检测模块-->
|
||||
<dependency>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-ocr</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>
|
||||
@@ -1,8 +1,7 @@
|
||||
package smartai.examples.ocr;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.objectdetection.model.DetectorModel;
|
||||
import cn.smartjavaai.objectdetection.model.ObjectDetectionModelFactory;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.ocr.config.OcrDetModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
import cn.smartjavaai.ocr.enums.CommonDetModelEnum;
|
||||
@@ -20,14 +19,17 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* OCR 文本检测 示例
|
||||
* 模型下载地址:https://pan.baidu.com/s/1MLfd73Vjdpnuls9-oqc9uw?pwd=1234 提取码: 1234
|
||||
* 模型下载地址:https://pan.baidu.com/s/15Noz2xHQzqMQSl1B19BobQ?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
* @date 2025/5/25
|
||||
*/
|
||||
@Slf4j
|
||||
public class OcrDetectionDemo {
|
||||
|
||||
|
||||
//设备类型
|
||||
public static DeviceEnum device = DeviceEnum.CPU;
|
||||
|
||||
|
||||
/**
|
||||
* 文本检测
|
||||
* 检测图像中的文本区域,仅返回文本框位置,不识别文字内容
|
||||
@@ -40,9 +42,13 @@ public class OcrDetectionDemo {
|
||||
config.setModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
config.setDetModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
OcrCommonDetModel model = OcrModelFactory.getInstance().getDetModel(config);
|
||||
List<OcrBox> boxes = model.detect("src/main/resources/ocr_1.jpg");
|
||||
log.info("OCR检测结果:{}", JSONObject.toJSONString(boxes));
|
||||
config.setDevice(device);
|
||||
try (OcrCommonDetModel model = OcrModelFactory.getInstance().getDetModel(config)){
|
||||
List<OcrBox> boxes = model.detect("src/main/resources/ocr_1.jpg");
|
||||
log.info("OCR检测结果:{}", JSONObject.toJSONString(boxes));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,8 +63,11 @@ public class OcrDetectionDemo {
|
||||
config.setModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
config.setDetModelPath("/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
OcrCommonDetModel model = OcrModelFactory.getInstance().getDetModel(config);
|
||||
model.detectAndDraw("src/main/resources/ocr_1.jpg", "output/ocr_1_detected.jpg");
|
||||
try (OcrCommonDetModel model = OcrModelFactory.getInstance().getDetModel(config)){
|
||||
model.detectAndDraw("src/main/resources/ocr_1.jpg", "output/ocr_1_detected.jpg");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package smartai.examples.ocr;
|
||||
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.ocr.config.DirectionModelConfig;
|
||||
import cn.smartjavaai.ocr.config.OcrDetModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
@@ -24,6 +25,27 @@ import java.util.List;
|
||||
@Slf4j
|
||||
public class OcrDirectionDetDemo {
|
||||
|
||||
//设备类型
|
||||
public static DeviceEnum device = DeviceEnum.CPU;
|
||||
|
||||
/**
|
||||
* 获取方向检测模型
|
||||
* @return
|
||||
*/
|
||||
public OcrDirectionModel getDirectionModel(){
|
||||
DirectionModelConfig directionModelConfig = new DirectionModelConfig();
|
||||
//指定检测模型
|
||||
directionModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定检测模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
directionModelConfig.setDetModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
//指定文本方向检测模型
|
||||
directionModelConfig.setModelEnum(DirectionModelEnum.CH_PPOCR_MOBILE_V2_CLS);
|
||||
//指定文本方向检测模型路径,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
directionModelConfig.setModelPath("/Users/xxx/Documents/develop/ocr模型/ch_ppocr_mobile_v2.0_cls.onnx");
|
||||
directionModelConfig.setDevice(device);
|
||||
return OcrModelFactory.getInstance().getDirectionModel(directionModelConfig);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文本方向检测
|
||||
@@ -34,18 +56,13 @@ public class OcrDirectionDetDemo {
|
||||
*/
|
||||
@Test
|
||||
public void detect(){
|
||||
DirectionModelConfig directionModelConfig = new DirectionModelConfig();
|
||||
//指定检测模型
|
||||
directionModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定检测模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
directionModelConfig.setDetModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
//指定文本方向检测模型
|
||||
directionModelConfig.setModelEnum(DirectionModelEnum.CH_PPOCR_MOBILE_V2_CLS);
|
||||
//指定文本方向检测模型路径,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
directionModelConfig.setModelPath("/Users/xxx/Documents/develop/ocr模型/ch_ppocr_mobile_v2.0_cls.onnx");
|
||||
OcrDirectionModel directionModel = OcrModelFactory.getInstance().getDirectionModel(directionModelConfig);
|
||||
List<OcrItem> itemList = directionModel.detect("src/main/resources/ocr_3.jpg");
|
||||
log.info("OCR方向检测结果:{}", JSONObject.toJSONString(itemList));
|
||||
try (OcrDirectionModel directionModel = getDirectionModel()){
|
||||
List<OcrItem> itemList = directionModel.detect("src/main/resources/ocr_3.jpg");
|
||||
log.info("OCR方向检测结果:{}", JSONObject.toJSONString(itemList));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,20 +73,13 @@ public class OcrDirectionDetDemo {
|
||||
*/
|
||||
@Test
|
||||
public void detectAndDraw(){
|
||||
DirectionModelConfig directionModelConfig = new DirectionModelConfig();
|
||||
//指定检测模型
|
||||
directionModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定检测模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
directionModelConfig.setDetModelPath("/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
//指定文本方向检测模型
|
||||
directionModelConfig.setModelEnum(DirectionModelEnum.CH_PPOCR_MOBILE_V2_CLS);
|
||||
//指定文本方向检测模型路径,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
directionModelConfig.setModelPath("/cls/ch_ppocr_mobile_v2.0_cls.onnx");
|
||||
OcrDirectionModel directionModel = OcrModelFactory.getInstance().getDirectionModel(directionModelConfig);
|
||||
directionModel.detectAndDraw("src/main/resources/ocr_3.jpg", "output/ocr_3_detected.png");
|
||||
try (OcrDirectionModel directionModel = getDirectionModel()){
|
||||
directionModel.detectAndDraw("src/main/resources/ocr_3.jpg", "output/ocr_3_detected.png");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package smartai.examples.ocr;
|
||||
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.ocr.config.OcrDetModelConfig;
|
||||
import cn.smartjavaai.ocr.config.OcrRecModelConfig;
|
||||
import cn.smartjavaai.ocr.entity.OcrBox;
|
||||
@@ -14,6 +15,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -25,15 +27,14 @@ import java.util.List;
|
||||
@Slf4j
|
||||
public class OcrRecognizeDemo {
|
||||
|
||||
//设备类型
|
||||
public static DeviceEnum device = DeviceEnum.CPU;
|
||||
|
||||
/**
|
||||
* 文本识别
|
||||
* 支持简体中文、繁体中文、英文、日文四种主要语言,以及手写、竖版、拼音、生僻字
|
||||
* 流程:文本检测 -> 文本识别
|
||||
* 模型需要放在单独文件夹
|
||||
* 获取通用识别模型(不带方向矫正)
|
||||
* @return
|
||||
*/
|
||||
@Test
|
||||
public void recognize(){
|
||||
public OcrCommonRecModel getRecModel(){
|
||||
OcrRecModelConfig recModelConfig = new OcrRecModelConfig();
|
||||
//指定检测模型
|
||||
recModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
@@ -43,9 +44,47 @@ public class OcrRecognizeDemo {
|
||||
recModelConfig.setRecModelEnum(CommonRecModelEnum.PADDLEOCR_V5_REC_MODEL);
|
||||
//指定识别模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setRecModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_rec_infer/PP-OCRv5_server_rec.onnx");
|
||||
OcrCommonRecModel recModel = OcrModelFactory.getInstance().getRecModel(recModelConfig);
|
||||
OcrInfo ocrInfo = recModel.recognize("src/main/resources/ocr_1.jpg");
|
||||
log.info("OCR识别结果:{}", JSONObject.toJSONString(ocrInfo));
|
||||
recModelConfig.setDevice(device);
|
||||
return OcrModelFactory.getInstance().getRecModel(recModelConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通用识别模型(带方向矫正)
|
||||
* @return
|
||||
*/
|
||||
public OcrCommonRecModel getRecModelWithDirection() {
|
||||
OcrRecModelConfig recModelConfig = new OcrRecModelConfig();
|
||||
//指定检测模型
|
||||
recModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定检测模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setDetModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
//指定识别模型
|
||||
recModelConfig.setRecModelEnum(CommonRecModelEnum.PADDLEOCR_V5_REC_MODEL);
|
||||
//指定识别模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setRecModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_rec_infer/PP-OCRv5_server_rec.onnx");
|
||||
//指定方向检测模型
|
||||
recModelConfig.setDirectionModelEnum(DirectionModelEnum.CH_PPOCR_MOBILE_V2_CLS);
|
||||
//指定方向模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setDirectionModelPath("/Users/xxx/Documents/develop/ocr模型/ch_ppocr_mobile_v2.0_cls.onnx");
|
||||
recModelConfig.setDevice(device);
|
||||
return OcrModelFactory.getInstance().getRecModel(recModelConfig);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文本识别
|
||||
* 支持简体中文、繁体中文、英文、日文四种主要语言,以及手写、竖版、拼音、生僻字
|
||||
* 流程:文本检测 -> 文本识别
|
||||
* 模型需要放在单独文件夹
|
||||
*/
|
||||
@Test
|
||||
public void recognize(){
|
||||
try (OcrCommonRecModel recModel = getRecModel()){
|
||||
OcrInfo ocrInfo = recModel.recognize("src/main/resources/ocr_2.jpg");
|
||||
log.info("OCR识别结果:{}", JSONObject.toJSONString(ocrInfo));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,18 +96,12 @@ public class OcrRecognizeDemo {
|
||||
*/
|
||||
@Test
|
||||
public void recognizeHandWriting(){
|
||||
OcrRecModelConfig recModelConfig = new OcrRecModelConfig();
|
||||
//指定检测模型
|
||||
recModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定检测模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setDetModelPath("/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
//指定识别模型
|
||||
recModelConfig.setRecModelEnum(CommonRecModelEnum.PADDLEOCR_V5_REC_MODEL);
|
||||
//指定识别模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setRecModelPath("/PP-OCRv5_server_rec_infer/PP-OCRv5_server_rec.onnx");
|
||||
OcrCommonRecModel recModel = OcrModelFactory.getInstance().getRecModel(recModelConfig);
|
||||
OcrInfo ocrInfo = recModel.recognize("src/main/resources/handwriting_1.jpg");
|
||||
log.info("OCR识别结果:{}", JSONObject.toJSONString(ocrInfo));
|
||||
try (OcrCommonRecModel recModel = getRecModel()){
|
||||
OcrInfo ocrInfo = recModel.recognize("src/main/resources/handwriting_1.jpg");
|
||||
log.info("OCR识别结果:{}", JSONObject.toJSONString(ocrInfo));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,22 +113,12 @@ public class OcrRecognizeDemo {
|
||||
*/
|
||||
@Test
|
||||
public void recognize2(){
|
||||
OcrRecModelConfig recModelConfig = new OcrRecModelConfig();
|
||||
//指定检测模型
|
||||
recModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定检测模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setDetModelPath("/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
//指定识别模型
|
||||
recModelConfig.setRecModelEnum(CommonRecModelEnum.PADDLEOCR_V5_REC_MODEL);
|
||||
//指定识别模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setRecModelPath("/PP-OCRv5_server_rec_infer/PP-OCRv5_server_rec.onnx");
|
||||
//指定方向检测模型
|
||||
recModelConfig.setDirectionModelEnum(DirectionModelEnum.CH_PPOCR_MOBILE_V2_CLS);
|
||||
//指定方向模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setDirectionModelPath("/cls/ch_ppocr_mobile_v2.0_cls.onnx");
|
||||
OcrCommonRecModel recModel = OcrModelFactory.getInstance().getRecModel(recModelConfig);
|
||||
OcrInfo ocrInfo = recModel.recognize("src/main/resources/ocr_4.jpg");
|
||||
log.info("OCR识别结果:{}", JSONObject.toJSONString(ocrInfo));
|
||||
try (OcrCommonRecModel recModel = getRecModelWithDirection()){
|
||||
OcrInfo ocrInfo = recModel.recognize("src/main/resources/ocr_4.jpg");
|
||||
log.info("OCR识别结果:{}", JSONObject.toJSONString(ocrInfo));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,23 +131,12 @@ public class OcrRecognizeDemo {
|
||||
*/
|
||||
@Test
|
||||
public void recognizeAndDraw(){
|
||||
OcrRecModelConfig recModelConfig = new OcrRecModelConfig();
|
||||
//指定检测模型
|
||||
recModelConfig.setDetModelEnum(CommonDetModelEnum.PADDLEOCR_V5_DET_MODEL);
|
||||
//指定检测模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setDetModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
|
||||
//指定识别模型
|
||||
recModelConfig.setRecModelEnum(CommonRecModelEnum.PADDLEOCR_V5_REC_MODEL);
|
||||
//directionModelConfig.setDirectionModelEnum(DirectionModelEnum.CH_PPOCR_MOBILE_V2_CLS);
|
||||
//指定识别模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setRecModelPath("/Users/xxx/Documents/develop/ocr模型/PP-OCRv5_server_rec_infer/PP-OCRv5_server_rec.onnx");
|
||||
//指定方向检测模型
|
||||
recModelConfig.setDirectionModelEnum(DirectionModelEnum.CH_PPOCR_MOBILE_V2_CLS);
|
||||
//指定方向模型位置,需要更改为自己的模型路径(下载地址请查看文档)
|
||||
recModelConfig.setDirectionModelPath("/Users/xxx/Documents/develop/ocr模型/ch_ppocr_mobile_v2.0_cls.onnx");
|
||||
OcrCommonRecModel recModel = OcrModelFactory.getInstance().getRecModel(recModelConfig);
|
||||
int fontSize = 25;
|
||||
recModel.recognizeAndDraw("src/main/resources/ocr_4.jpg", "output/ocr_4_recognized.jpg", fontSize);
|
||||
try (OcrCommonRecModel recModel = getRecModelWithDirection()){
|
||||
int fontSize = 25;
|
||||
recModel.recognizeAndDraw("src/main/resources/ocr_4.jpg", "output/ocr_4_recognized.jpg", fontSize);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: smartai.examples.face.SeetaFace6LinuxDemo
|
||||
|
||||
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 282 KiB |
14
examples/ocr-examples/src/main/resources/logback.xml
Normal 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>
|
||||
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
@@ -1,136 +0,0 @@
|
||||
package smartai.examples.face.attribute;
|
||||
|
||||
import cn.smartjavaai.common.entity.*;
|
||||
import cn.smartjavaai.face.config.FaceModelConfig;
|
||||
import cn.smartjavaai.face.config.FaceAttributeConfig;
|
||||
import cn.smartjavaai.face.constant.LivenessConstant;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.enums.FaceAttributeModelEnum;
|
||||
import cn.smartjavaai.face.exception.FaceException;
|
||||
import cn.smartjavaai.face.factory.FaceModelFactory;
|
||||
import cn.smartjavaai.face.factory.FaceAttributeModelFactory;
|
||||
import cn.smartjavaai.face.model.attribute.FaceAttributeModel;
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
import cn.smartjavaai.face.utils.FaceUtils;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.Java2DFrameUtils;
|
||||
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
|
||||
* @date 2025/5/1
|
||||
*/
|
||||
@Slf4j
|
||||
public class FaceAttributeDetDemo {
|
||||
|
||||
|
||||
/**
|
||||
* 人脸属性检测(多人脸)
|
||||
*/
|
||||
@Test
|
||||
public void testFaceAttributeDetect(){
|
||||
FaceAttributeConfig config = new FaceAttributeConfig();
|
||||
config.setModelEnum(FaceAttributeModelEnum.SEETA_FACE6_MODEL);
|
||||
//需替换为实际模型存储路径
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
FaceAttributeModel faceAttributeModel = FaceAttributeModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectionResponse = faceAttributeModel.detect("src/main/resources/double_person.png");
|
||||
try {
|
||||
//绘制并导出人脸属性图片,小人脸仅有人脸框
|
||||
BufferedImage image = ImageIO.read(new File(Paths.get("src/main/resources/double_person.png").toAbsolutePath().toString()));
|
||||
FaceUtils.drawBoxesWithFaceAttribute(image, detectionResponse,"C:/Users/Administrator/Downloads/double_person_.png");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(detectionResponse));
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片人脸属性检测(分数最高人脸)
|
||||
*/
|
||||
@Test
|
||||
public void testFaceAttributeDetect2(){
|
||||
FaceAttributeConfig config = new FaceAttributeConfig();
|
||||
config.setModelEnum(FaceAttributeModelEnum.SEETA_FACE6_MODEL);
|
||||
//需替换为实际模型存储路径
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
FaceAttributeModel faceAttributeModel = FaceAttributeModelFactory.getInstance().getModel(config);
|
||||
FaceAttribute faceAttribute = faceAttributeModel.detectTopFace("src/main/resources/double_person.png");
|
||||
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(faceAttribute));
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片多人脸属性检测(基于已检测出的人脸区域和关键点)
|
||||
*/
|
||||
@Test
|
||||
public void testFaceAttributeDetect3(){
|
||||
//人脸检测
|
||||
//需替换为实际模型存储路径
|
||||
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){
|
||||
//人脸属性检测
|
||||
FaceAttributeConfig config = new FaceAttributeConfig();
|
||||
config.setModelEnum(FaceAttributeModelEnum.SEETA_FACE6_MODEL);
|
||||
config.setModelPath(modelPath);
|
||||
FaceAttributeModel faceAttributeModel = FaceAttributeModelFactory.getInstance().getModel(config);
|
||||
List<FaceAttribute> livenessStatusList = faceAttributeModel.detect("src/main/resources/double_person.png",detectionResponse);
|
||||
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(livenessStatusList));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片单人脸人脸属性检测(基于已检测出的人脸区域和关键点)
|
||||
*/
|
||||
@Test
|
||||
public void testFaceAttributeDetect4(){
|
||||
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){
|
||||
//人脸属性检测
|
||||
FaceAttributeConfig config = new FaceAttributeConfig();
|
||||
config.setModelEnum(FaceAttributeModelEnum.SEETA_FACE6_MODEL);
|
||||
config.setModelPath(modelPath);
|
||||
FaceAttributeModel faceAttributeModel = FaceAttributeModelFactory.getInstance().getModel(config);
|
||||
BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
for (DetectionInfo detectionInfo : detectionResponse.getDetectionInfoList()){
|
||||
FaceInfo faceInfo = detectionInfo.getFaceInfo();
|
||||
FaceAttribute faceAttribute = faceAttributeModel.detect(image, detectionInfo.getDetectionRectangle(), faceInfo.getKeyPoints());
|
||||
log.info("人脸属性检测结果:{}", JSONObject.toJSONString(faceAttribute));
|
||||
}
|
||||
}
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package smartai.examples.face.facerec;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.config.FaceModelConfig;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.factory.FaceModelFactory;
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* GPU 人脸检测
|
||||
* 模型下载地址:https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
* @date 2025/4/14
|
||||
*/
|
||||
@Slf4j
|
||||
public class GpuFaceDemo {
|
||||
|
||||
/**
|
||||
* 人脸检测(GPU)
|
||||
* 图片参数:图片路径
|
||||
*/
|
||||
@Test
|
||||
public void testFaceGpu(){
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.RETINA_FACE);//人脸模型
|
||||
config.setDevice(DeviceEnum.GPU);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectedResult = faceModel.detect("src/main/resources/largest_selfie.jpg");
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package smartai.examples.face.facerec;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.face.config.FaceModelConfig;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.factory.FaceModelFactory;
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
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.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* UltraLightFastGenericFaceModel 轻量人脸算法模型demo
|
||||
* 支持功能:人脸检测(不支持人脸特征提取)
|
||||
* 模型下载地址:https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
* @date 2025/4/11
|
||||
*/
|
||||
@Slf4j
|
||||
public class LightFaceDemo {
|
||||
|
||||
|
||||
/**
|
||||
* 人脸检测-自定义参数
|
||||
* 图片参数:图片路径
|
||||
*/
|
||||
@Test
|
||||
public void testFaceDetectCustomConfig(){
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE);//人脸模型
|
||||
//config.setConfidenceThreshold(FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
|
||||
//config.setNmsThresh(FaceConfig.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectedResult = faceModel.detect("src/main/resources/largest_selfie.jpg");
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制人脸框
|
||||
*/
|
||||
@Test
|
||||
public void testFaceDetectAndDraw(){
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE);//人脸模型
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
faceModel.detectAndDraw("src/main/resources/largest_selfie.jpg","output/largest_selfie_detected.png");
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制人脸框,返回BufferedImage
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testFaceDetectAndDraw2(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE);//人脸模型
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
BufferedImage image = null;
|
||||
String imagePath = "src/main/resources/largest_selfie.jpg";
|
||||
image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
//可以根据后续业务场景使用detectedImage
|
||||
BufferedImage detectedImage = faceModel.detectAndDraw(image);
|
||||
Assert.assertNotNull("detectedImage null", detectedImage);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测(离线模型)
|
||||
*/
|
||||
@Test
|
||||
public void testDetectFaceOffine(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE);//人脸模型
|
||||
//模型路径,不同模型下载路径请参看文档
|
||||
config.setModelPath("/Users/xxx/Documents/develop/face_model/ultranet.pt");
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectedResult = faceModel.detect("src/main/resources/largest_selfie.jpg");
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package smartai.examples.face.facerec;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.face.config.FaceModelConfig;
|
||||
import cn.smartjavaai.face.constant.FaceDetectConstant;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.exception.FaceException;
|
||||
import cn.smartjavaai.face.factory.FaceModelFactory;
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
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.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* RetinaFace人脸算法模型demo
|
||||
* 支持功能:人脸检测(不支持人脸特征提取)
|
||||
* 模型下载地址:https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
* @date 2025/4/11
|
||||
*/
|
||||
@Slf4j
|
||||
public class RetinaFaceDemo {
|
||||
|
||||
/**
|
||||
* 人脸检测(默认配置)
|
||||
* 使用默认模型参数检测,默认模型:retinaface,需联网,会自动下载模型
|
||||
* 图片参数:图片路径
|
||||
*/
|
||||
@Test
|
||||
public void testFaceDetect(){
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel();
|
||||
DetectionResponse detectedResult = faceModel.detect("src/main/resources/largest_selfie.jpg");
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测(自定义模型参数)
|
||||
* 图片参数:图片路径
|
||||
*/
|
||||
@Test
|
||||
public void testFaceDetectCustomConfig(){
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.RETINA_FACE);//人脸模型
|
||||
config.setConfidenceThreshold(FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD);//只返回相似度大于该值的人脸
|
||||
config.setNmsThresh(FaceDetectConstant.NMS_THRESHOLD);//用于去除重复的人脸框,当两个框的重叠度超过该值时,只保留一个
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectedResult = faceModel.detect("src/main/resources/largest_selfie.jpg");
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制人脸框
|
||||
*/
|
||||
@Test
|
||||
public void testFaceDetectAndDraw(){
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel();
|
||||
faceModel.detectAndDraw("src/main/resources/largest_selfie.jpg","output/largest_selfie_detected.png");
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制人脸框,返回BufferedImage
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testFaceDetectAndDraw2(){
|
||||
try {
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel();
|
||||
BufferedImage image = null;
|
||||
String imagePath = "src/main/resources/largest_selfie.jpg";
|
||||
image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
//可以根据后续业务场景使用detectedImage
|
||||
BufferedImage detectedImage = faceModel.detectAndDraw(image);
|
||||
Assert.assertNotNull("detectedImage null", detectedImage);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测(离线模型)
|
||||
*/
|
||||
@Test
|
||||
public void testDetectFaceOffine(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
config.setModelEnum(FaceModelEnum.RETINA_FACE);//人脸模型
|
||||
//模型路径,不同模型下载路径请参看文档
|
||||
config.setModelPath("/Users/xxx/Documents/develop/face_model/retinaface.pt");
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectedResult = faceModel.detect("src/main/resources/largest_selfie.jpg");
|
||||
log.info("人脸检测结果:{}", JSONObject.toJSONString(detectedResult));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
package smartai.examples.face.facerec;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.FaceSearchResult;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.face.config.FaceExtractConfig;
|
||||
import cn.smartjavaai.face.config.FaceModelConfig;
|
||||
import cn.smartjavaai.face.entity.FaceRegisterInfo;
|
||||
import cn.smartjavaai.face.entity.FaceResult;
|
||||
import cn.smartjavaai.face.entity.FaceSearchParams;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.enums.IdStrategy;
|
||||
import cn.smartjavaai.face.enums.SimilarityType;
|
||||
import cn.smartjavaai.face.factory.FaceModelFactory;
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
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 io.milvus.param.MetricType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
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.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SeetaFace6人脸算法模型demo
|
||||
* 支持系统:windows 64位,linux 64位
|
||||
* 支持功能:人脸检测、人脸特征提取、人脸比对(1:1)、人脸比对(1:N)、人脸注册
|
||||
* 模型下载地址:https://pan.baidu.com/s/10l22x5fRz_gwLr8EAHa1Jg?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
* @date 2025/4/11
|
||||
*/
|
||||
@Slf4j
|
||||
public class SeetaFace6Demo {
|
||||
|
||||
|
||||
/**
|
||||
* 提取人脸特征(多人脸场景)
|
||||
* 默认使用SEETA_FACE6_MODEL自己的检测模型
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
*/
|
||||
@Test
|
||||
public void testExtractFeatures(){
|
||||
try {
|
||||
//人脸特征提取模型
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//指定模型
|
||||
config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
|
||||
//指定模型路径:请根据实际情况替换为本地模型文件的绝对路径(模型下载地址请查看文档)
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
//提取图片中所有人脸特征
|
||||
R<DetectionResponse> faceResult = faceModel.extractFeatures("src/main/resources/face/iu_1.jpg");
|
||||
if(faceResult.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", JSONObject.toJSONString(faceResult.getData()));
|
||||
}else{
|
||||
log.info("人脸特征提取失败:{}", faceResult.getMessage());
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取人脸特征(只提取图片中分数最高人脸特征)
|
||||
* 默认使用SEETA_FACE6_MODEL自己的检测模型
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
*/
|
||||
@Test
|
||||
public void testExtractFeatures2(){
|
||||
try {
|
||||
//人脸特征提取模型
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//指定模型
|
||||
config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
|
||||
//指定模型路径:请根据实际情况替换为本地模型文件的绝对路径(模型下载地址请查看文档)
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
//提取图片中检测分数最高人脸特征
|
||||
R<float[]> faceResult = faceModel.extractTopFaceFeature("src/main/resources/face/iu_1.jpg");
|
||||
if(faceResult.isSuccess()){
|
||||
log.info("人脸特征提取成功:{}", faceResult.getData());
|
||||
}else{
|
||||
log.info("人脸特征提取失败:{}", faceResult.getMessage());
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 人脸比对1:1(基于图像直接比对)
|
||||
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 比对两张图片中提取的人脸特征。(接口内自动完成)
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void featureComparison(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//指定模型
|
||||
config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
|
||||
//指定模型路径:请根据实际情况替换为本地模型文件的绝对路径(模型下载地址请查看文档)
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
//基于图像直接比对人脸特征
|
||||
float similar = faceModel.featureComparison("src/main/resources/face/iu_1.jpg","src/main/resources/face/iu_2.jpg");
|
||||
log.info("相似度:{}", similar);
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸比对1:1(基于特征值比对)
|
||||
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 比对两张图片中提取的人脸特征。
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void featureComparison2(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//指定模型
|
||||
config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
|
||||
//指定模型路径:请根据实际情况替换为本地模型文件的绝对路径(模型下载地址请查看文档)
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult1 = faceModel.extractTopFaceFeature("src/main/resources/face/iu_1.jpg");
|
||||
if(featureResult1.isSuccess()){
|
||||
log.info("图片1人脸特征提取成功:{}", JSONObject.toJSONString(featureResult1.getData()));
|
||||
}else{
|
||||
log.info("图片1人脸特征提取失败:{}", featureResult1.getMessage());
|
||||
return;
|
||||
}
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult2 = faceModel.extractTopFaceFeature("src/main/resources/face/iu_2.jpg");
|
||||
if(featureResult2.isSuccess()){
|
||||
log.info("图片2人脸特征提取成功:{}", JSONObject.toJSONString(featureResult2.getData()));
|
||||
}else{
|
||||
log.info("图片2人脸特征提取失败:{}", featureResult2.getMessage());
|
||||
return;
|
||||
}
|
||||
//计算相似度
|
||||
float similar = faceModel.calculSimilar(featureResult1.getData(), featureResult2.getData());
|
||||
log.info("相似度:{}", similar);
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 人脸注册 + 人脸更新 + 人脸查询 + 人脸删除(使用向量数据库Milvus)
|
||||
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 注册人脸
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
* 2、若人脸朝向较正,可关闭人脸对齐以提升性能。(方法参考自定义配置人脸特征提取)
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void searchFace(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//人脸模型
|
||||
config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
//初始化向量数据库:Milvus数据库配置
|
||||
MilvusConfig vectorDBConfig = new MilvusConfig();
|
||||
vectorDBConfig.setHost("127.0.0.1");
|
||||
vectorDBConfig.setPort(19530);
|
||||
//vectorDBConfig.setCollectionName("face10");
|
||||
//ID策略:自动生成
|
||||
vectorDBConfig.setIdStrategy(IdStrategy.AUTO);
|
||||
//索引类型:内积 (Inner Product) 不建议修改
|
||||
vectorDBConfig.setMetricType(MetricType.COSINE);
|
||||
config.setVectorDBConfig(vectorDBConfig);
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
|
||||
//等待加载人脸库结束
|
||||
while (!faceModel.isLoadFaceCompleted()) {
|
||||
Thread.sleep(50); // 避免 CPU 占用过高
|
||||
}
|
||||
|
||||
log.info("====================人脸注册==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult = faceModel.extractTopFaceFeature("src/main/resources/face/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
|
||||
R<String> registerResult = faceModel.register(faceRegisterInfo, featureResult.getData());
|
||||
if(registerResult.isSuccess()){
|
||||
log.info("注册成功:ID-{}", registerResult.getData());
|
||||
}else{
|
||||
log.info("注册失败:{}", registerResult.getMessage());
|
||||
}
|
||||
/*log.info("====================人脸更新==========================");
|
||||
//更新人脸 只支持自定义ID:vectorDBConfig.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());
|
||||
faceModel.upsertFace(updateInfo, "src/main/resources/face/iu_2.jpg");
|
||||
log.info("更新人脸成功");*/
|
||||
log.info("====================人脸查询==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult2 = faceModel.extractTopFaceFeature("src/main/resources/face/iu_2.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 = faceModel.search(featureResult2.getData(), faceSearchParams);
|
||||
log.info("人脸查询结果:{}", JSONArray.toJSONString(faceSearchResults));
|
||||
log.info("====================人脸删除==========================");
|
||||
faceModel.removeRegister(registerResult.getData());
|
||||
log.info("人脸删除成功");
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸注册 + 人脸更新 + 人脸查询 + 人脸删除(使用轻量数据库SQLite)
|
||||
* 流程:从输入图像中裁剪分数最高的人脸 → 提取其人脸特征 → 注册人脸
|
||||
* 注意事项:
|
||||
* 1、首次调用接口,可能会较慢。只要不关闭程序,后续调用会明显加快。若每次重启程序,则每次首次调用都将重新加载,仍会较慢。
|
||||
* 2、若人脸朝向较正,可关闭人脸对齐以提升性能。(方法参考自定义配置人脸特征提取)
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void searchFace2(){
|
||||
try {
|
||||
FaceModelConfig config = new FaceModelConfig();
|
||||
//人脸模型
|
||||
config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
|
||||
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
|
||||
//使用轻量数据库SQLite
|
||||
config.setVectorDBConfig(new SQLiteConfig());
|
||||
FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
|
||||
log.info("====================人脸注册==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult = faceModel.extractTopFaceFeature("src/main/resources/face/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 = faceModel.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());
|
||||
faceModel.upsertFace(updateInfo, "src/main/resources/face/iu_2.jpg");
|
||||
log.info("更新人脸成功");
|
||||
log.info("====================人脸查询==========================");
|
||||
//特征提取(提取分数最高人脸特征),适用于单人脸场景
|
||||
R<float[]> featureResult2 = faceModel.extractTopFaceFeature("src/main/resources/face/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.62f);
|
||||
//等待加载人脸库结束
|
||||
while (!faceModel.isLoadFaceCompleted()) {
|
||||
Thread.sleep(50); // 避免 CPU 占用过高
|
||||
}
|
||||
List<FaceSearchResult> faceSearchResults = faceModel.search(featureResult2.getData(), faceSearchParams);
|
||||
log.info("人脸查询结果:{}", JSONArray.toJSONString(faceSearchResults));
|
||||
log.info("====================人脸删除==========================");
|
||||
faceModel.removeRegister(registerResult.getData());
|
||||
log.info("人脸删除成功");
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
package smartai.examples.face.liveness;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionInfo;
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.FaceInfo;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.common.enums.LivenessStatus;
|
||||
import cn.smartjavaai.face.config.FaceModelConfig;
|
||||
import cn.smartjavaai.face.config.LivenessConfig;
|
||||
import cn.smartjavaai.face.constant.LivenessConstant;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.enums.LivenessModelEnum;
|
||||
import cn.smartjavaai.face.exception.FaceException;
|
||||
import cn.smartjavaai.face.factory.FaceModelFactory;
|
||||
import cn.smartjavaai.face.factory.LivenessModelFactory;
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
import cn.smartjavaai.face.model.liveness.LivenessDetModel;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.Java2DFrameUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
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 {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 图片活体检测(多人脸)
|
||||
*/
|
||||
@Test
|
||||
public void testLivenessDetect(){
|
||||
LivenessConfig config = new LivenessConfig();
|
||||
config.setModelEnum(LivenessModelEnum.SEETA_FACE6_MODEL);
|
||||
config.setDevice(DeviceEnum.GPU);
|
||||
//需替换为实际模型存储路径
|
||||
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);
|
||||
LivenessDetModel livenessDetModel = LivenessModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse livenessStatusList = livenessDetModel.detect("src/main/resources/double_person.png");
|
||||
log.info("活体检测结果:{}", JSONObject.toJSONString(livenessStatusList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片活体检测(分数最高人脸)
|
||||
*/
|
||||
@Test
|
||||
public void testLivenessDetect2(){
|
||||
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);
|
||||
LivenessDetModel livenessDetModel = LivenessModelFactory.getInstance().getModel(config);
|
||||
LivenessStatus livenessStatus = livenessDetModel.detectTopFace("src/main/resources/double_person.png");
|
||||
log.info("活体检测结果:{}", JSONObject.toJSONString(livenessStatus));
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片多人脸活体检测(基于已检测出的人脸区域和关键点)
|
||||
*/
|
||||
@Test
|
||||
public void testLivenessDetect3(){
|
||||
//人脸检测
|
||||
//需替换为实际模型存储路径
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片单人脸活体检测(基于已检测出的人脸区域和关键点)
|
||||
*/
|
||||
@Test
|
||||
public void testLivenessDetect4(){
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频活体检测
|
||||
*/
|
||||
@Test
|
||||
public void testLivenessDetectVideo(){
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 视频活体检测(逐帧检测,基于已检测出的人脸区域和关键点)
|
||||
*/
|
||||
@Test
|
||||
public void testLivenessDetectVideo2(){
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频活体检测(逐帧检测)
|
||||
*/
|
||||
@Test
|
||||
public void testLivenessDetectVideo3(){
|
||||
//获取活体检测模型
|
||||
//需替换为实际模型存储路径
|
||||
String modelPath = "C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models";
|
||||
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);
|
||||
/* 视频检测帧数,可选,默认10,输出帧数超过这个number之后,就可以输出识别结果。
|
||||
这个数量相当于多帧识别结果融合的融合的帧数。当输入的帧数超过设定帧数的时候,会采用滑动窗口的方式,返回融合的最近输入的帧融合的识别结果。
|
||||
一般来说,在10以内,帧数越多,结果越稳定,相对性能越好,但是得到结果的延时越高。*/
|
||||
config.setFrameCount(LivenessConstant.DEFAULT_FRAME_COUNT);
|
||||
LivenessDetModel livenessDetModel = LivenessModelFactory.getInstance().getModel(config);
|
||||
//获取人脸检测模型
|
||||
FaceModelConfig faceDetectModelConfig = new FaceModelConfig();
|
||||
faceDetectModelConfig.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);
|
||||
faceDetectModelConfig.setModelPath(modelPath);
|
||||
FaceModel faceDetectModel = FaceModelFactory.getInstance().getModel(faceDetectModelConfig);
|
||||
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);
|
||||
//检测视频帧人脸
|
||||
DetectionResponse detectionResponse = faceDetectModel.detect(bufferedImage);
|
||||
//检测到人脸
|
||||
if(detectionResponse != null && detectionResponse.getDetectionInfoList() != null && detectionResponse.getDetectionInfoList().size() > 0){
|
||||
DetectionRectangle detectionRectangle = detectionResponse.getDetectionInfoList().get(0).getDetectionRectangle();
|
||||
FaceInfo faceInfo = detectionResponse.getDetectionInfoList().get(0).getFaceInfo();
|
||||
//使用人脸检测结果 活体检测
|
||||
LivenessStatus livenessStatusFrame = livenessDetModel.detectVideoByFrame(bufferedImage, detectionRectangle, faceInfo.getKeyPoints());
|
||||
//满足检测帧数之后停止检测
|
||||
if(livenessStatusFrame != LivenessStatus.DETECTING){
|
||||
livenessStatus = livenessStatusFrame;
|
||||
}
|
||||
}else{
|
||||
log.info("未检测到人脸");
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("视频活体检测结果:{}", JSONObject.toJSONString(livenessStatus));
|
||||
grabber.stop();
|
||||
} catch (FFmpegFrameGrabber.Exception e) {
|
||||
throw new FaceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package smartai.examples.objectdetection;
|
||||
|
||||
import ai.djl.Application;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
import ai.djl.modality.cv.output.*;
|
||||
import ai.djl.modality.cv.output.Rectangle;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
import ai.djl.repository.zoo.ModelNotFoundException;
|
||||
import ai.djl.repository.zoo.ModelZoo;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import ai.djl.training.util.ProgressBar;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.objectdetection.config.DetectorModelConfig;
|
||||
import cn.smartjavaai.objectdetection.enums.DetectorModelEnum;
|
||||
import cn.smartjavaai.objectdetection.exception.DetectionException;
|
||||
import cn.smartjavaai.objectdetection.model.DetectorModel;
|
||||
import cn.smartjavaai.objectdetection.model.ObjectDetectionModelFactory;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* 目标检测模型demo
|
||||
* 支持功能:目标检测
|
||||
* 模型下载地址:https://pan.baidu.com/s/1MeQ0oHGl8hneicUIUVJjbg?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
* @date 2025/4/11
|
||||
*/
|
||||
@Slf4j
|
||||
public class ObjectDetection {
|
||||
|
||||
/**
|
||||
* 使用默认模型检测:YOLO11N
|
||||
*/
|
||||
@Test
|
||||
public void objectDetection(){
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel();
|
||||
DetectionResponse detectionResponse = detectorModel.detect("src/main/resources/object_detection.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detectionResponse));
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定模型检测(19种模型可选)
|
||||
*/
|
||||
@Test
|
||||
public void objectDetection2(){
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
config.setModelEnum(DetectorModelEnum.SSD_300_RESNET50);//检测模型,目前支持19种预置模型
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectionResponse = detectorModel.detect("src/main/resources/dog_bike_car.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detectionResponse));
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制检测结果
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionAndDraw(){
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel();
|
||||
detectorModel.detectAndDraw("src/main/resources/object_detection.jpg","output/object_detection_detected.png");
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸检测并绘制检测结果,返回BufferedImage
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionAndDraw2(){
|
||||
try {
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel();
|
||||
BufferedImage image = null;
|
||||
String imagePath = "src/main/resources/object_detection.jpg";
|
||||
image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
//可以根据后续业务场景使用detectedImage
|
||||
BufferedImage detectedImage = detectorModel.detectAndDraw(image);
|
||||
Assert.assertNotNull("detectedImage null", detectedImage);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* GPU 目标检测
|
||||
*/
|
||||
@Test
|
||||
public void gpuObjectDetection(){
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
config.setModelEnum(DetectorModelEnum.YOLO11N);//检测模型,目前支持19种模型
|
||||
config.setDevice(DeviceEnum.GPU);
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config);
|
||||
DetectionResponse detectionResponse = detectorModel.detect("src/main/resources/dog_bike_car.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detectionResponse));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用yolo官方模型检测物品识别
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionWithOfficialModel(){
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
config.setThreshold(0.3f);
|
||||
//也支持YoloV8:YOLOV8_OFFICIAL 模型可以从文档中提供的地址下载
|
||||
config.setModelEnum(DetectorModelEnum.YOLOV12_OFFICIAL);//检测模型,目前支持19种模型
|
||||
// 指定模型路径,需要更改为自己的模型路径
|
||||
config.setModelPath("E:\\ai\\models\\yolo12m\\yolov12m.onnx");
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config);
|
||||
//一定要将yolo官方的类别文件:synset.txt(文档中下载)放在模型同目录下,否则报错
|
||||
DetectionResponse detect = detectorModel.detect("E:\\ai\\testimage\\1.jpg");
|
||||
log.info("目标检测结果:{}", JSONObject.toJSONString(detect));
|
||||
detectorModel.detectAndDraw("E:\\ai\\testimage\\1.jpg","E:\\ai\\outimage\\11.png");
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己训练的模型检测
|
||||
*/
|
||||
@Test
|
||||
public void objectDetectionWithCustomModel(){
|
||||
DetectorModelConfig config = new DetectorModelConfig();
|
||||
//也支持YoloV8:YOLOV8_CUSTOM 模型需要自己训练,训练教程可以查看文档
|
||||
config.setModelEnum(DetectorModelEnum.YOLOV12_CUSTOM);//自定义YOLOV12模型
|
||||
// 指定模型路径,需要更改为自己的模型路径
|
||||
config.setModelPath("/Users/xxx/Documents/develop/fire_model/best.onnx");
|
||||
DetectorModel detectorModel = ObjectDetectionModelFactory.getInstance().getModel(config);
|
||||
//一定要将类别文件:synset.txt 放在模型同目录下,否则报错(具体请参看文档)
|
||||
detectorModel.detectAndDraw("/Users/xxx/Downloads/test.jpg","output/test_detected.jpg");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 41 KiB |
7
examples/translation-example/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.idea
|
||||
.idea/
|
||||
target
|
||||
log
|
||||
*.iml
|
||||
/.settings/
|
||||
/logging.file_IS_UNDEFINED/
|
||||
40
examples/translation-example/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 机器翻译示例
|
||||
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
|
||||
└── main
|
||||
├── java
|
||||
│ └── smartai
|
||||
│ └── examples
|
||||
│ └── nlp
|
||||
│ └── translation 机器翻译
|
||||
│ └── TranslationDemo.java
|
||||
└── resources
|
||||
└── logback.xml
|
||||
|
||||
|
||||
```
|
||||
---
|
||||
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
1. 克隆项目到本地:
|
||||
|
||||
2. 导入项目至 IntelliJ IDEA。
|
||||
|
||||
3. 根据需要修改模型路径(见各 demo 中注释)。
|
||||
|
||||
4. 运行对应的 JUnit 测试类方法即可体验各项功能。
|
||||
|
||||
---
|
||||
|
||||
## 📄 文档
|
||||
|
||||
有关完整使用说明,请查阅 SmartJavaAI 官方文档:
|
||||
[http://doc.smartjavaai.cn](http://doc.smartjavaai.cn)
|
||||
|
||||
---
|
||||
201
examples/translation-example/pom.xml
Normal file
@@ -0,0 +1,201 @@
|
||||
<?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>examples</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.nlp.translation.TranslationDemo</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-translate</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>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>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>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>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>
|
||||
@@ -17,9 +17,8 @@ import org.junit.Test;
|
||||
* 翻译Demo
|
||||
* 支持 Meta AI 开源的 NLLB-200 模型,实现 200 多种语言之间的高质量互译。
|
||||
* NLLB-200官网地址:https://github.com/facebookresearch/fairseq/tree/nllb
|
||||
* 模型下载地址:https://pan.baidu.com/s/1_AD5QGQ6f6uOajJ-kW20rg?pwd=1234 提取码: 1234
|
||||
* 模型下载地址:https://pan.baidu.com/s/1wf7btnb4cyBFv7DB7baHnw?pwd=1234 提取码: 1234
|
||||
* @author dwj
|
||||
* @date 2025/6/17
|
||||
*/
|
||||
@Slf4j
|
||||
public class TranslationDemo {
|
||||
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: smartai.examples.face.SeetaFace6LinuxDemo
|
||||
|
||||
14
examples/translation-example/src/main/resources/logback.xml
Normal 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>
|
||||
2
pom.xml
@@ -7,7 +7,7 @@
|
||||
<name>SmartJavaAI</name>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
<packaging>pom</packaging>
|
||||
<description>SmartJavaAI</description>
|
||||
<modules>
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>smartjavaai-all</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>SmartJavaAI</description>
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
</parent>
|
||||
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
<artifactId>smartjavaai-bom</artifactId>
|
||||
<name>smartjavaai-bom</name>
|
||||
<description>统一版本管理的 BOM 包,同时支持 import 和全量依赖</description>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
</parent>
|
||||
|
||||
<name>smartjavaai-common</name>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import cn.smartjavaai.common.entity.face.FaceInfo;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import cn.smartjavaai.common.enums.GenderType;
|
||||
import cn.smartjavaai.common.enums.LivenessStatus;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 检测结果-矩形区域
|
||||
* @author dwj
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import cn.smartjavaai.common.enums.LivenessStatus;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,12 @@ public class R<T> {
|
||||
private T data;
|
||||
|
||||
|
||||
public static <T> R<T> ok() {
|
||||
R<T> r = new R<>();
|
||||
r.code = 0;
|
||||
r.message = "成功";
|
||||
return r;
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(T data) {
|
||||
R<T> r = new R<>();
|
||||
@@ -50,6 +56,7 @@ public class R<T> {
|
||||
FILE_NOT_FOUND(2, "图像文件不存在"),
|
||||
NO_FACE_DETECTED(3, "未检测到人脸"),
|
||||
PARAM_ERROR(4, "参数错误"),
|
||||
INVALID_VIDEO(5, "视频无效"),
|
||||
Unknown(-1, "未知错误");
|
||||
|
||||
private final int code;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.smartjavaai.common.entity.face;
|
||||
|
||||
import ai.djl.modality.Classifications;
|
||||
import cn.smartjavaai.common.enums.face.FacialExpression;
|
||||
import cn.smartjavaai.common.enums.face.LivenessStatus;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 人脸表情识别结果
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class ExpressionResult {
|
||||
|
||||
/**
|
||||
* 表情
|
||||
*/
|
||||
private FacialExpression expression;
|
||||
|
||||
/**
|
||||
* 分数
|
||||
*/
|
||||
private float score;
|
||||
|
||||
/**
|
||||
* 完整结果
|
||||
*/
|
||||
private Classifications classifications;
|
||||
|
||||
public ExpressionResult() {
|
||||
}
|
||||
|
||||
|
||||
public ExpressionResult(FacialExpression expression, float score) {
|
||||
this.expression = expression;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public ExpressionResult(FacialExpression expression, float score, Classifications classifications) {
|
||||
this.expression = expression;
|
||||
this.score = score;
|
||||
this.classifications = classifications;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
package cn.smartjavaai.common.entity.face;
|
||||
|
||||
import cn.smartjavaai.common.enums.EyeStatus;
|
||||
import cn.smartjavaai.common.enums.GenderType;
|
||||
import cn.smartjavaai.common.enums.face.EyeStatus;
|
||||
import cn.smartjavaai.common.enums.face.GenderType;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
package cn.smartjavaai.common.entity.face;
|
||||
|
||||
import cn.smartjavaai.common.enums.LivenessStatus;
|
||||
import cn.smartjavaai.common.entity.Point;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
@@ -26,7 +26,7 @@ public class FaceInfo {
|
||||
/**
|
||||
* 活体检测结果
|
||||
*/
|
||||
private LivenessStatus livenessStatus;
|
||||
private LivenessResult livenessStatus;
|
||||
|
||||
/**
|
||||
* 人脸查询结果
|
||||
@@ -38,6 +38,11 @@ public class FaceInfo {
|
||||
*/
|
||||
private float[] feature;
|
||||
|
||||
/**
|
||||
* 表情检测结果
|
||||
*/
|
||||
private ExpressionResult expressionResult;
|
||||
|
||||
public FaceInfo() {
|
||||
}
|
||||
|
||||
@@ -45,14 +50,16 @@ public class FaceInfo {
|
||||
this.keyPoints = keyPoints;
|
||||
}
|
||||
|
||||
public FaceInfo(List<Point> keyPoints, FaceAttribute faceAttribute, LivenessStatus livenessStatus) {
|
||||
public FaceInfo(List<Point> keyPoints, FaceAttribute faceAttribute, LivenessResult livenessStatus) {
|
||||
this.keyPoints = keyPoints;
|
||||
this.faceAttribute = faceAttribute;
|
||||
this.livenessStatus = livenessStatus;
|
||||
}
|
||||
|
||||
public FaceInfo(FaceAttribute faceAttribute, LivenessStatus livenessStatus) {
|
||||
public FaceInfo(FaceAttribute faceAttribute, LivenessResult livenessStatus) {
|
||||
this.faceAttribute = faceAttribute;
|
||||
this.livenessStatus = livenessStatus;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
package cn.smartjavaai.common.entity.face;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
package cn.smartjavaai.common.entity.face;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.smartjavaai.common.entity.face;
|
||||
|
||||
import cn.smartjavaai.common.enums.face.LivenessStatus;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 活体检测结果
|
||||
* @author dwj
|
||||
* @date 2025/6/27
|
||||
*/
|
||||
@Data
|
||||
public class LivenessResult {
|
||||
|
||||
private LivenessStatus status;
|
||||
|
||||
private float score;
|
||||
|
||||
public LivenessResult() {
|
||||
}
|
||||
|
||||
public LivenessResult(LivenessStatus status, float score) {
|
||||
this.status = status;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public LivenessResult(LivenessStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.smartjavaai.common.enums;
|
||||
package cn.smartjavaai.common.enums.face;
|
||||
|
||||
/**
|
||||
* 眼睛状态
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.smartjavaai.common.enums.face;
|
||||
|
||||
/**
|
||||
* 人脸表情枚举
|
||||
* @author dwj
|
||||
*/
|
||||
public enum FacialExpression {
|
||||
|
||||
ANGRY("angry", "愤怒"),
|
||||
DISGUST("disgust", "厌恶"),
|
||||
FEAR("fear", "害怕"),
|
||||
HAPPY("happy", "高兴"),
|
||||
SAD("sad", "伤心"),
|
||||
SURPRISE("surprise", "惊讶"),
|
||||
NEUTRAL("neutral", "中性");
|
||||
|
||||
private final String label;
|
||||
private final String description;
|
||||
|
||||
FacialExpression(String label, String description) {
|
||||
this.label = label;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static FacialExpression fromLabel(String label) {
|
||||
for (FacialExpression facialExpression : FacialExpression.values()) {
|
||||
if (facialExpression.getLabel().equals(label)) {
|
||||
return facialExpression;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid facial expression label: " + label);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.smartjavaai.common.enums;
|
||||
package cn.smartjavaai.common.enums.face;
|
||||
|
||||
/**
|
||||
* 性别枚举
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.smartjavaai.common.enums;
|
||||
package cn.smartjavaai.common.enums.face;
|
||||
|
||||
/**
|
||||
* 活体检测结果
|
||||
@@ -0,0 +1,160 @@
|
||||
package cn.smartjavaai.common.preprocess;
|
||||
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* 图片预处理
|
||||
* @author dwj
|
||||
* @date 2025/6/27
|
||||
*/
|
||||
public class BufferedImagePreprocessor {
|
||||
|
||||
private BufferedImage image;
|
||||
private DetectionRectangle rect;
|
||||
private float extendRatio = 1;
|
||||
private int targetSize = 128;
|
||||
private int centerCropSize = 80;
|
||||
|
||||
private Color paddingColor = new Color(127, 127, 127); // 默认灰色
|
||||
private boolean enableSquarePadding = true;
|
||||
private boolean enableScaling = true;
|
||||
private boolean enableCenterCrop = false;
|
||||
|
||||
|
||||
|
||||
public BufferedImagePreprocessor(BufferedImage image, DetectionRectangle rect) {
|
||||
this.image = image;
|
||||
this.rect = rect;
|
||||
}
|
||||
|
||||
public BufferedImagePreprocessor setExtendRatio(float ratio) {
|
||||
this.extendRatio = ratio;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BufferedImagePreprocessor setTargetSize(int size) {
|
||||
this.targetSize = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BufferedImagePreprocessor setCenterCropSize(int size) {
|
||||
this.centerCropSize = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BufferedImagePreprocessor enableSquarePadding(boolean enable) {
|
||||
this.enableSquarePadding = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BufferedImagePreprocessor enableScaling(boolean enable) {
|
||||
this.enableScaling = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BufferedImagePreprocessor enableCenterCrop(boolean enable) {
|
||||
this.enableCenterCrop = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BufferedImagePreprocessor setPaddingColor(Color color) {
|
||||
this.paddingColor = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public BufferedImage process() {
|
||||
// Step 1: 基于检测框扩展
|
||||
BufferedImage cropped = cropAndExtend();
|
||||
|
||||
// Step 2: 补正方形 + 背景填充
|
||||
BufferedImage squared = enableSquarePadding ? squarePadding(cropped) : cropped;
|
||||
|
||||
// Step 3: 缩放
|
||||
BufferedImage scaled = enableScaling ? scaleToTarget(squared) : squared;
|
||||
|
||||
// Step 4: CenterCrop
|
||||
BufferedImage finalResult = enableCenterCrop ? centerCrop(scaled) : scaled;
|
||||
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测框扩展及裁剪
|
||||
* @return
|
||||
*/
|
||||
private BufferedImage cropAndExtend() {
|
||||
int x = rect.x;
|
||||
int y = rect.y;
|
||||
int width = rect.width;
|
||||
int height = rect.height;
|
||||
|
||||
int extendX = Math.round(width * extendRatio);
|
||||
int extendY = Math.round(height * extendRatio);
|
||||
|
||||
// 计算扩展后的边界 (确保不超出图像范围)
|
||||
int left = Math.max(0, x - extendX);
|
||||
int right = Math.min(image.getWidth(), x + width + extendX);
|
||||
int top = Math.max(0, y - extendY);
|
||||
int bottom = Math.min(image.getHeight(), y + height + extendY);
|
||||
|
||||
// 动态计算最大可用扩展区域
|
||||
int origRoiWidth = right - left;
|
||||
int origRoiHeight = bottom - top;
|
||||
int longSide = Math.max(origRoiWidth, origRoiHeight);
|
||||
|
||||
// 计算可扩展空间(不超出原图边界)
|
||||
int extendLeft = Math.min(left, (longSide - origRoiWidth) / 2);
|
||||
int extendRight = Math.min(image.getWidth() - right, (longSide - origRoiWidth + 1) / 2);
|
||||
int extendTop = Math.min(top, (longSide - origRoiHeight) / 2);
|
||||
int extendBottom = Math.min(image.getHeight() - bottom, (longSide - origRoiHeight + 1) / 2);
|
||||
|
||||
// 计算实际扩展后的区域
|
||||
int expandedLeft = left - extendLeft;
|
||||
int expandedRight = right + extendRight;
|
||||
int expandedTop = top - extendTop;
|
||||
int expandedBottom = bottom + extendBottom;
|
||||
|
||||
int expandedWidth = expandedRight - expandedLeft;
|
||||
int expandedHeight = expandedBottom - expandedTop;
|
||||
|
||||
return image.getSubimage(expandedLeft, expandedTop, expandedWidth, expandedHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充正方形
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
private BufferedImage squarePadding(BufferedImage src) {
|
||||
int longSide = Math.max(src.getWidth(), src.getHeight());
|
||||
BufferedImage squared = new BufferedImage(longSide, longSide, BufferedImage.TYPE_3BYTE_BGR);
|
||||
Graphics2D g = squared.createGraphics();
|
||||
g.setColor(paddingColor);
|
||||
g.fillRect(0, 0, longSide, longSide);
|
||||
int xOffset = (longSide - src.getWidth()) / 2;
|
||||
int yOffset = (longSide - src.getHeight()) / 2;
|
||||
g.drawImage(src, xOffset, yOffset, null);
|
||||
g.dispose();
|
||||
return squared;
|
||||
}
|
||||
|
||||
private BufferedImage scaleToTarget(BufferedImage src) {
|
||||
Image scaled = src.getScaledInstance(targetSize, targetSize, Image.SCALE_SMOOTH);
|
||||
BufferedImage result = new BufferedImage(targetSize, targetSize, BufferedImage.TYPE_3BYTE_BGR);
|
||||
Graphics2D g = result.createGraphics();
|
||||
g.drawImage(scaled, 0, 0, null);
|
||||
g.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
private BufferedImage centerCrop(BufferedImage src) {
|
||||
int startX = (src.getWidth() - centerCropSize) / 2;
|
||||
int startY = (src.getHeight() - centerCropSize) / 2;
|
||||
return src.getSubimage(startX, startY, centerCropSize, centerCropSize);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
/**
|
||||
* 数组工具类
|
||||
* @author dwj
|
||||
* @date 2025/6/27
|
||||
*/
|
||||
public class ArrayUtils {
|
||||
|
||||
/**
|
||||
* 求和并找到最大值的索引
|
||||
* @param arr1
|
||||
* @param arr2
|
||||
* @return
|
||||
*/
|
||||
public static int sumAndFindMaxIndex(float[] arr1, float[] arr2, int length) {
|
||||
float[] sum = new float[length];
|
||||
|
||||
// 处理可能为null的情况,null当作全0数组处理
|
||||
for (int i = 0; i < length; i++) {
|
||||
float v1 = (arr1 != null && arr1.length > i) ? arr1[i] : 0f;
|
||||
float v2 = (arr2 != null && arr2.length > i) ? arr2[i] : 0f;
|
||||
sum[i] = v1 + v2;
|
||||
}
|
||||
|
||||
// 找最大值索引
|
||||
int maxIndex = 0;
|
||||
float maxValue = sum[0];
|
||||
for (int i = 1; i < length; i++) {
|
||||
if (sum[i] > maxValue) {
|
||||
maxValue = sum[i];
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回最大值的索引
|
||||
return maxIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dwj
|
||||
* @date 2025/6/28
|
||||
*/
|
||||
public class Base64ImageUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 将 Base64 字符串(可带头部)转图片
|
||||
*/
|
||||
public static byte[] base64ToImage(String base64Str){
|
||||
String cleanBase64 = stripBase64Header(base64Str);
|
||||
return Base64.decode(cleanBase64);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 Base64 字符串是否带有 Data URI 头部
|
||||
*/
|
||||
public static boolean hasBase64Header(String base64Str) {
|
||||
return base64Str != null && base64Str.startsWith("data:") && base64Str.contains(";base64,");
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除 Base64 字符串的 Data URI 头部
|
||||
*/
|
||||
public static String stripBase64Header(String base64Str) {
|
||||
if (hasBase64Header(base64Str)) {
|
||||
return base64Str.substring(base64Str.indexOf(",") + 1);
|
||||
}
|
||||
return base64Str;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -332,5 +332,44 @@ public class ImageUtils {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 画检测框(有倾斜角)和文本
|
||||
*
|
||||
* @param image
|
||||
* @param box
|
||||
* @param text
|
||||
*/
|
||||
public static void drawImageRectWithText(BufferedImage image, DetectionRectangle box, String text, Color color) {
|
||||
// 将绘制图像转换为Graphics2D
|
||||
Graphics2D graphics = (Graphics2D) image.getGraphics();
|
||||
try {
|
||||
graphics.setColor(Color.RED);// 边框颜色
|
||||
graphics.setStroke(new BasicStroke(2)); // 线宽2像素
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON); // 抗锯齿
|
||||
int stroke = 2;
|
||||
graphics.setColor(color);// 边框颜色
|
||||
graphics.drawRect(box.getX(), box.getY(), box.getWidth(), box.getHeight());
|
||||
drawText(graphics, text, box.getX(), box.getY(), stroke, 4);
|
||||
graphics.dispose();
|
||||
} finally {
|
||||
graphics.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawText(Graphics2D g, String text, int x, int y, int stroke, int padding) {
|
||||
FontMetrics metrics = g.getFontMetrics();
|
||||
x += stroke / 2;
|
||||
y += stroke / 2;
|
||||
int width = metrics.stringWidth(text) + padding * 2 - stroke / 2;
|
||||
int height = metrics.getHeight() + metrics.getDescent();
|
||||
int ascent = metrics.getAscent();
|
||||
java.awt.Rectangle background = new java.awt.Rectangle(x, y, width, height);
|
||||
g.fill(background);
|
||||
g.setPaint(Color.WHITE);
|
||||
g.drawString(text, x + padding, y + ascent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.opencv.core.Point;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
|
||||
@@ -111,7 +112,14 @@ public class OpenCVUtils {
|
||||
public static Mat image2Mat(BufferedImage img) {
|
||||
int width = img.getWidth();
|
||||
int height = img.getHeight();
|
||||
byte[] data = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
|
||||
|
||||
// 强制转换为 TYPE_3BYTE_BGR,自动去除透明通道
|
||||
BufferedImage convertedImg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
Graphics2D g2d = convertedImg.createGraphics();
|
||||
g2d.drawImage(img, 0, 0, null);
|
||||
g2d.dispose();
|
||||
|
||||
byte[] data = ((DataBufferByte) convertedImg.getRaster().getDataBuffer()).getData();
|
||||
Mat mat = new Mat(height, width, CvType.CV_8UC3);
|
||||
mat.put(0, 0, data);
|
||||
return mat;
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<parent>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<version>1.0.17</version>
|
||||
<version>1.0.19</version>
|
||||
<name>smartjavaai-face</name>
|
||||
<description>SmartJavaAI</description>
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.constant.FaceDetectConstant;
|
||||
import cn.smartjavaai.face.enums.FaceDetModelEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 人脸检测模型配置
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class FaceDetConfig {
|
||||
|
||||
/**
|
||||
* 人脸检测模型枚举
|
||||
*/
|
||||
private FaceDetModelEnum modelEnum;
|
||||
|
||||
/**
|
||||
* 置信度阈值
|
||||
*/
|
||||
private double confidenceThreshold = FaceDetectConstant.DEFAULT_CONFIDENCE_THRESHOLD;
|
||||
|
||||
|
||||
/**
|
||||
* 非极大抑制阈值 作用:消除重叠检测框,保留最优结果
|
||||
*/
|
||||
private double nmsThresh = FaceDetectConstant.NMS_THRESHOLD;
|
||||
|
||||
/**
|
||||
* 模型路径
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* 个性化配置(按模型类型动态解析)
|
||||
*/
|
||||
private Map<String, Object> customParams = new HashMap<>();
|
||||
|
||||
|
||||
public FaceDetConfig() {
|
||||
}
|
||||
|
||||
public FaceDetConfig(FaceDetModelEnum modelEnum) {
|
||||
this.modelEnum = modelEnum;
|
||||
}
|
||||
|
||||
public FaceDetConfig(FaceDetModelEnum modelEnum, String modelPath) {
|
||||
this.modelEnum = modelEnum;
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
public <T> T getCustomParam(String key, Class<T> clazz) {
|
||||
Object value = customParams.get(key);
|
||||
if (value == null) return null;
|
||||
return clazz.cast(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加个性化配置项
|
||||
*/
|
||||
public void putCustomParam(String key, Object value) {
|
||||
if (customParams == null) {
|
||||
customParams = new HashMap<>();
|
||||
}
|
||||
customParams.put(key, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.enums.ExpressionModelEnum;
|
||||
import cn.smartjavaai.face.model.facedect.FaceDetModel;
|
||||
import cn.smartjavaai.face.model.facerec.FaceRecModel;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dwj
|
||||
* @date 2025/7/1
|
||||
*/
|
||||
@Data
|
||||
public class FaceExpressionConfig {
|
||||
|
||||
/**
|
||||
* 模型枚举
|
||||
*/
|
||||
private ExpressionModelEnum modelEnum = ExpressionModelEnum.DensNet121;
|
||||
|
||||
/**
|
||||
* 模型路径
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* 人脸检测模型
|
||||
*/
|
||||
private FaceDetModel detectModel;
|
||||
|
||||
/**
|
||||
* 是否对齐人脸
|
||||
*/
|
||||
private boolean align = true;
|
||||
|
||||
/**
|
||||
* 是否裁剪人脸
|
||||
*/
|
||||
private boolean cropFace = true;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.face.model.facerec.FaceModel;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 人脸特征提取配置
|
||||
* @author dwj
|
||||
* @date 2025/4/24
|
||||
*/
|
||||
@Data
|
||||
public class FaceExtractConfig {
|
||||
|
||||
/**
|
||||
* 是否裁剪人脸
|
||||
*/
|
||||
private boolean cropFace = true;
|
||||
|
||||
/**
|
||||
* 是否对齐人脸
|
||||
*/
|
||||
private boolean align = false;
|
||||
|
||||
/**
|
||||
* 人脸检测模型
|
||||
*/
|
||||
private FaceModel detectModel;
|
||||
|
||||
public FaceExtractConfig() {
|
||||
}
|
||||
|
||||
public FaceExtractConfig(boolean cropFace, boolean align, FaceModel detectModel) {
|
||||
this.cropFace = cropFace;
|
||||
this.align = align;
|
||||
this.detectModel = detectModel;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,22 +2,26 @@ package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.constant.FaceDetectConstant;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.enums.VectorDBType;
|
||||
import cn.smartjavaai.face.enums.FaceRecModelEnum;
|
||||
import cn.smartjavaai.face.model.facedect.FaceDetModel;
|
||||
import cn.smartjavaai.face.model.facerec.FaceRecModel;
|
||||
import cn.smartjavaai.face.vector.config.VectorDBConfig;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 人脸检测识别模型配置
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class FaceModelConfig {
|
||||
public class FaceRecConfig {
|
||||
|
||||
/**
|
||||
* 人脸模型枚举
|
||||
*/
|
||||
private FaceModelEnum modelEnum;
|
||||
private FaceRecModelEnum modelEnum;
|
||||
|
||||
/**
|
||||
* 置信度阈值
|
||||
@@ -49,16 +53,6 @@ public class FaceModelConfig {
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* gpu设备ID 当device为GPU时生效
|
||||
*/
|
||||
private int gpuId = 0;
|
||||
|
||||
/**
|
||||
* 人脸特征提取配置
|
||||
*/
|
||||
private FaceExtractConfig extractConfig;
|
||||
|
||||
|
||||
/**
|
||||
* 向量数据库配置
|
||||
@@ -70,15 +64,52 @@ public class FaceModelConfig {
|
||||
*/
|
||||
private boolean isAutoLoadFace = true;
|
||||
|
||||
public FaceModelConfig() {
|
||||
|
||||
/**
|
||||
* 是否裁剪人脸
|
||||
*/
|
||||
private boolean cropFace = true;
|
||||
|
||||
/**
|
||||
* 是否对齐人脸
|
||||
*/
|
||||
private boolean align = false;
|
||||
|
||||
/**
|
||||
* 人脸检测模型
|
||||
*/
|
||||
private FaceDetModel detectModel;
|
||||
|
||||
/**
|
||||
* 个性化配置(按模型类型动态解析)
|
||||
*/
|
||||
private Map<String, Object> customParams = new HashMap<>();
|
||||
|
||||
public FaceRecConfig() {
|
||||
}
|
||||
|
||||
public FaceModelConfig(FaceModelEnum modelEnum) {
|
||||
public FaceRecConfig(FaceRecModelEnum modelEnum) {
|
||||
this.modelEnum = modelEnum;
|
||||
}
|
||||
|
||||
public FaceModelConfig(FaceModelEnum modelEnum, String modelPath) {
|
||||
public FaceRecConfig(FaceRecModelEnum modelEnum, String modelPath) {
|
||||
this.modelEnum = modelEnum;
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
public <T> T getCustomParam(String key, Class<T> clazz) {
|
||||
Object value = customParams.get(key);
|
||||
if (value == null) return null;
|
||||
return clazz.cast(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加个性化配置项
|
||||
*/
|
||||
public void putCustomParam(String key, Object value) {
|
||||
if (customParams == null) {
|
||||
customParams = new HashMap<>();
|
||||
}
|
||||
customParams.put(key, value);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.constant.FaceDetectConstant;
|
||||
import cn.smartjavaai.face.constant.LivenessConstant;
|
||||
import cn.smartjavaai.face.enums.FaceModelEnum;
|
||||
import cn.smartjavaai.face.enums.LivenessModelEnum;
|
||||
import cn.smartjavaai.face.model.facedect.FaceDetModel;
|
||||
import cn.smartjavaai.face.model.facerec.FaceRecModel;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 活体检测模型配置
|
||||
* @author dwj
|
||||
@@ -30,25 +33,26 @@ public class LivenessConfig {
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* gpu设备ID 当device为GPU时生效
|
||||
* 人脸检测模型
|
||||
*/
|
||||
private int gpuId = 0;
|
||||
private FaceDetModel detectModel;
|
||||
|
||||
/**
|
||||
* 人脸清晰度阈值
|
||||
* 个性化配置(按模型类型动态解析)
|
||||
*/
|
||||
private float faceClarityThreshold = LivenessConstant.DEFAULT_FACE_CLARITY_THRESHOLD;
|
||||
private Map<String, Object> customParams = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 活体阈值
|
||||
*/
|
||||
private float realityThreshold = LivenessConstant.DEFAULT_REALITY_THRESHOLD;
|
||||
|
||||
/**
|
||||
* 视频检测帧数
|
||||
*/
|
||||
private int frameCount = LivenessConstant.DEFAULT_FRAME_COUNT;
|
||||
|
||||
/**
|
||||
* 真人阈值
|
||||
*/
|
||||
private Float realityThreshold;
|
||||
|
||||
public LivenessConfig() {
|
||||
}
|
||||
|
||||
@@ -64,4 +68,21 @@ public class LivenessConfig {
|
||||
public LivenessConfig(String modelPath) {
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
// 可选封装方法,便于类型转换和调用
|
||||
public <T> T getCustomParam(String key, Class<T> clazz) {
|
||||
Object value = customParams.get(key);
|
||||
if (value == null) return null;
|
||||
return clazz.cast(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加个性化配置项
|
||||
*/
|
||||
public void putCustomParam(String key, Object value) {
|
||||
if (customParams == null) {
|
||||
customParams = new HashMap<>();
|
||||
}
|
||||
customParams.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||