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

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

View File

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

View 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)
---

View File

@@ -0,0 +1,307 @@
<?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.objectdetection.ObjectDetection</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-objectdetection</artifactId>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-jni</artifactId>
<version>2.5.1-0.32.0</version>
<scope>runtime</scope>
</dependency>
<!-- windows平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu</artifactId>
<classifier>${djl.platform.windows-x86_64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
<!-- linux x86 平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu</artifactId>
<classifier>${djl.platform.linux-x86_64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
<!-- macOS M系列 平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.macosx-arm64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu</artifactId>
<classifier>${djl.platform.osx-aarch64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
<!-- linux aarch64 平台 (保留对应平台的配置,可以减小包大小)-->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>6.1.1-1.5.10</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.26-1.5.10</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.9.0-1.5.10</version>
<classifier>${javacv.platform.linux-arm64}</classifier>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-native-cpu-precxx11</artifactId>
<classifier>${djl.platform.linux-aarch64}</classifier>
<version>2.5.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${exec.mainClass}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>aliyunmaven</id>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,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);
//也支持YoloV8YOLOV8_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();
//也支持YoloV8YOLOV8_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);
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package smartai.examples.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()));
}
}
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB