新增目标检测功能

This commit is contained in:
dengwenjie
2025-04-13 20:33:15 +08:00
parent 4eb02c6d87
commit 241b816e7f
56 changed files with 3300 additions and 1993 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

View File

@@ -1,115 +0,0 @@
<?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>ink.numberone</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.0-SNAPSHOT</smartjavaai.version>
<exec.mainClass>smartai.examples.face.FaceDemo</exec.mainClass>
</properties>
<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>ink.numberone</groupId>
<artifactId>smartjavaai-face</artifactId>
<version>1.0.6</version>
</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>ai.djl.onnxruntime</groupId>
<artifactId>onnxruntime-engine</artifactId>
<version>0.20.0</version>
</dependency>
</dependencies>
<build>
<finalName>example</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.3</version>
<configuration>
<!--如果不想在打包的后缀加上assembly.xml中设置的id可以加上下面的配置-->
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<!-- 是否绑定依赖将外部jar包依赖加入到classPath中 -->
<addClasspath>true</addClasspath>
<!-- 依赖前缀,与之前设置的文件夹路径要匹配 -->
<classpathPrefix>lib/</classpathPrefix>
<!-- 执行的主程序入口 -->
<mainClass>smartai.examples.face.FaceDemo</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<!--绑定的maven操作-->
<phase>package</phase>
<goals>
<goal>assembly</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,278 +0,0 @@
package smartai.examples.face;
import cn.smartjavaai.common.entity.Rectangle;
import cn.smartjavaai.face.*;
import cn.smartjavaai.face.entity.FaceResult;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import smartai.examples.utils.ImageUtils;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RasterFormatException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @author dwj
*/
@Slf4j
public class FaceDemo {
public static void main(String[] args) {
try {
featureExtractionAndCompare2();
//detectFace2();
//verifyIDCard();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸检测(服务端模型)
* 人脸模型retinaface
* 特点:识别精度高,高速
* 应用场景:如监控摄像头、智能安防系统等需要高精度检测的场合
*/
public static void detectFace(){
try {
//创建人脸算法
FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm();
//使用图片路径检测
FaceDetectedResult result = currentAlgorithm.detect("src/main/resources/largest_selfie.jpg");
log.info("人脸检测结果:{}", JSONObject.toJSONString(result));
//使用图片流检测
File input = new File("src/main/resources/largest_selfie.jpg");
//FaceDetectedResult result = currentAlgorithm.detect(new FileInputStream(input));
//log.info("人脸检测结果:{}", JSONObject.toJSONString(result));
BufferedImage image = ImageIO.read(input);
//创建保存路径
Path imagePath = Paths.get("output").resolve("retinaface_detected.jpg");
//绘制人脸框
ImageUtils.drawBoundingBoxes(image, result, imagePath.toAbsolutePath().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸检测(轻量模型)
* 人脸模型Ultra-Light-Fast-Generic-Face-Detector-1MB
* 特点:高速,准确率略低
* 应用场景:如监控摄像头、智能安防系统等需要高精度检测的场合
*/
public static void detectFace2(){
try {
//创建轻量人脸算法
FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createLightFaceAlgorithm();
//使用图片路径检测
FaceDetectedResult result = currentAlgorithm.detect("src/main/resources/largest_selfie.jpg");
log.info("轻量人脸检测结果:{}", JSONObject.toJSONString(result));
//使用图片流检测
//File imageFile = new File("/Users/wenjie/Downloads/djl-master/examples/src/test/resources/largest_selfie.jpg");
//FaceDetectedResult result = currentAlgorithm.detect(new FileInputStream(imageFile));
File input = new File("src/main/resources/largest_selfie.jpg");
BufferedImage image = ImageIO.read(input);
//创建保存路径
Path imagePath = Paths.get("output").resolve("retinaface_detected.jpg");
//绘制人脸框
ImageUtils.drawBoundingBoxes(image, result, imagePath.toAbsolutePath().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸检测(离线模型)
* 人脸模型retinaface
* 特点:识别精度高,高速
* 应用场景:如监控摄像头、智能安防系统等需要高精度检测的场合
*/
public static void detectFaceOffine(){
try {
// 初始化配置
ModelConfig config = new ModelConfig();
config.setAlgorithmName("retinaface");//人脸算法模型目前支持retinaface/ultralightfastgenericface/seetaface6
//config.setAlgorithmName("ultralightfastgenericface");//轻量模型
config.setConfidenceThreshold(FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD);//置信度阈值
config.setMaxFaceCount(FaceConfig.MAX_FACE_LIMIT);//每张特征图保留的最大候选框数量
//nms阈值:控制重叠框的合并程度,取值越低,合并越多重叠框(减少误检但可能漏检);取值越高,保留更多框(增加检出但可能引入冗余)
config.setNmsThresh(FaceConfig.NMS_THRESHOLD);
//模型下载地址:
//retinaface: https://resources.djl.ai/test-models/pytorch/retinaface.zip
//ultralightfastgenericface: https://resources.djl.ai/test-models/pytorch/ultranet.zip
//改为模型存放路径
config.setModelPath("/Users/wenjie/Documents/develop/face_model/retinaface.pt");
//创建人脸算法
FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(config);
//使用图片路径检测
FaceDetectedResult result = currentAlgorithm.detect("src/main/resources/largest_selfie.jpg");
log.info("人脸检测结果:{}", JSONObject.toJSONString(result));
//使用图片流检测
File input = new File("src/main/resources/largest_selfie.jpg");
//FaceDetectedResult result = currentAlgorithm.detect(new FileInputStream(input));
//logger.info("人脸检测结果:{}", JSONObject.toJSONString(result));
BufferedImage image = ImageIO.read(input);
//创建保存路径
Path imagePath = Paths.get("output").resolve("retinaface_detected.jpg");
//绘制人脸框
ImageUtils.drawBoundingBoxes(image, result, imagePath.toAbsolutePath().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 人脸比对11
* @throws Exception
*/
public static void featureComparison(){
try {
// 初始化配置
ModelConfig config = new ModelConfig();
config.setAlgorithmName("seetaface6");//目前支持人脸比对的算法只有seetaface6
//人脸库路径 如果不指定人脸库,无法使用 1:N人脸搜索
config.setFaceDbPath("C:/Users/Administrator/Downloads/faces-data.db");
//改为模型存放路径
config.setModelPath("/opt/sf3.0_models");
//创建人脸算法
FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(config);
//自动裁剪人脸并比对人脸特征
float similar = currentAlgorithm.featureComparison("src/main/resources/kana1.jpg","src/main/resources/kana2.jpg");
log.info("相似度:{}", similar);
}
catch (Exception e){
e.printStackTrace();
}
}
/**
* seetaface6人脸特征提取及比对可人证核验
* 目前仅支持windows 64位系统如需支持其他操作系统可参考方法featureExtractionAndCompare2
*/
public static void featureExtractionAndCompare(){
try {
// 初始化配置
ModelConfig config = new ModelConfig();
config.setAlgorithmName("seetaface6");
//人脸库路径 如果不指定人脸库,无法使用 1:N人脸搜索
config.setFaceDbPath("C:/Users/Administrator/Downloads/faces-data.db");
//改为模型存放路径
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
//创建人脸算法
FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(config);
//提取图像中最大人脸的特征
float[] feature1 = currentAlgorithm.featureExtraction("src/main/resources/kana1.jpg");
float[] feature2 = currentAlgorithm.featureExtraction("src/main/resources/kana2.jpg");
if(feature1 != null && feature2 != null){
float similar = currentAlgorithm.calculSimilar(feature1, feature2);
log.info("相似度:{}", similar);
}else{
log.warn("人脸特征提取失败");
}
}
catch (Exception e){
e.printStackTrace();
}
}
/**
* facenet-pytorch 人脸特征提取及比对(可人证核验)
* 支持windowslinuxmacos
*/
public static void featureExtractionAndCompare2(){
try {
//创建脸算法
FaceAlgorithm featureAlgorithm = FaceAlgorithmFactory.createFaceFeatureAlgorithm();
//提取身份证人脸特征
float[] feature1 = featureAlgorithm.featureExtraction("src/main/resources/kana1.jpg");
float[] feature2 = featureAlgorithm.featureExtraction("src/main/resources/kana2.jpg");
if (feature1 != null && feature2 != null) {
//相似度在0.8至0.85及以上时,可判定为同一人,但具体阈值可能因图片而异,存在一定误差。
float similar = featureAlgorithm.calculSimilar(feature1, feature2);
log.info("相似度:{}", similar);
} else {
log.warn("人脸特征提取失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 注册人脸及搜索人脸1N
*/
public static void registerAndSearchFace(){
try {
// 初始化配置
ModelConfig config = new ModelConfig();
config.setAlgorithmName("seetaface6");
//人脸库路径 如果不指定人脸库,无法使用 1:N人脸搜索
config.setFaceDbPath("C:/Users/Administrator/Downloads/faces-data.db");
//改为模型存放路径
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
//创建人脸算法 自动将人脸库加载到内存中
FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(config);
//等待人脸库加载完毕
Thread.sleep(1000);
//注册kana1人脸参数key建议设置为人名
boolean isSuccss = currentAlgorithm.register("kana1","src/main/resources/kana1.jpg");
//注册jsy人脸参数key建议设置为人名
isSuccss = currentAlgorithm.register("jsy","src/main/resources/jsy.jpg");
FaceResult faceResult = currentAlgorithm.search("src/main/resources/kana2.jpg");
if(faceResult != null){
log.info("查询到人脸:{}", faceResult.toString());
}else{
log.info("未查询到人脸");
}
}
catch (Exception e){
e.printStackTrace();
}
}
/**
* 删除已注册人脸
*/
public static void removeRegisterFace(){
try {
// 初始化配置
ModelConfig config = new ModelConfig();
config.setAlgorithmName("seetaface6");
//人脸库路径 如果不指定人脸库,无法使用 1:N人脸搜索
config.setFaceDbPath("C:/Users/Administrator/Downloads/faces-data.db");
//改为模型存放路径
config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
//创建人脸算法 自动将人脸库加载到内存中
FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(config);
//等待人脸库加载完毕
Thread.sleep(1000);
//使用注册人脸时的key值删除可一次性删除单个
long num = currentAlgorithm.removeRegister("kana1");
//删除全部人脸
//long num = currentAlgorithm.clearFace();
log.info("删除成功数量:" + num);
}
catch (Exception e){
e.printStackTrace();
}
}
}

View File

@@ -1,56 +0,0 @@
package smartai.examples.utils;
import cn.smartjavaai.face.FaceDetectedResult;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @author dwj
*/
public class ImageUtils {
/**
* 绘制人脸框
* @param sourceImage
* @param faceDetectedResult
* @param savePath
* @throws IOException
*/
public static void drawBoundingBoxes(BufferedImage sourceImage, FaceDetectedResult faceDetectedResult,String savePath) throws IOException {
Graphics2D graphics = sourceImage.createGraphics();
graphics.setColor(Color.RED);// 边框颜色
graphics.setStroke(new BasicStroke(2)); // 线宽2像素
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); // 抗锯齿
int stroke = 2;
for(cn.smartjavaai.common.entity.Rectangle rectangle : faceDetectedResult.getRectangles()){
graphics.setColor(Color.RED);// 边框颜色
graphics.drawRect(rectangle.getPointList().get(0).getX(),
rectangle.getPointList().get(0).getY(), rectangle.getWidth(), rectangle.getHeight());
drawText(graphics, "face", rectangle.getPointList().get(0).getX(), rectangle.getPointList().get(0).getY(), stroke, 4);
}
graphics.dispose();
ImageIO.write(sourceImage, "jpg", new File(savePath));
}
private 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();
Rectangle background = new Rectangle(x, y, width, height);
g.fill(background);
g.setPaint(Color.WHITE);
g.drawString(text, x + padding, y + ascent);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 KiB

View File

@@ -1,14 +0,0 @@
<?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="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>