diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..93dbf83
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1,7 @@
+.idea
+.idea/
+target
+log
+*.iml
+/.settings/
+/logging.file_IS_UNDEFINED/
diff --git a/examples/db/faces-data.db b/examples/db/faces-data.db
new file mode 100644
index 0000000..cbbc660
Binary files /dev/null and b/examples/db/faces-data.db differ
diff --git a/examples/pom.xml b/examples/pom.xml
new file mode 100644
index 0000000..e5e7ad5
--- /dev/null
+++ b/examples/pom.xml
@@ -0,0 +1,287 @@
+
+
+ 4.0.0
+
+ cn.smartjavaai
+ examples
+ 1.0.0-SNAPSHOT
+
+
+ 11
+ 11
+ UTF-8
+ 1.0.12
+ smartai.examples.face.FaceDemo2
+
+ 1.5.10
+
+ macosx-arm64
+ linux-x86_64
+ linux-arm64
+ windows-x86_64
+
+
+ win-x86_64
+ linux-x86_64
+ linux-aarch64
+ osx-aarch64
+
+
+
+
+
+ cn.smartjavaai
+ smartjavaai-bom
+ ${smartjavaai.version}
+ pom
+
+ import
+
+
+
+
+
+
+
+ commons-cli
+ commons-cli
+ 1.9.0
+
+
+ commons-io
+ commons-io
+ 2.17.0
+
+
+ org.apache.logging.log4j
+ log4j-slf4j2-impl
+ 2.24.1
+
+
+ org.testng
+ testng
+ 7.10.2
+ test
+
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.30
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.83
+
+
+
+ junit
+ junit
+ 4.13.2
+
+
+
+
+ cn.smartjavaai
+ smartjavaai-face
+
+
+
+
+ cn.smartjavaai
+ smartjavaai-objectdetection
+
+
+
+
+
+ org.bytedeco
+ javacpp
+ ${javacv.version}
+ ${javacv.platform.windows-x86_64}
+
+
+ org.bytedeco
+ ffmpeg
+ 6.1.1-1.5.10
+ ${javacv.platform.windows-x86_64}
+
+
+
+ org.bytedeco
+ openblas
+ 0.3.26-1.5.10
+ ${javacv.platform.windows-x86_64}
+
+
+
+ org.bytedeco
+ opencv
+ 4.9.0-1.5.10
+ ${javacv.platform.windows-x86_64}
+
+
+
+
+
+ org.bytedeco
+ javacpp
+ ${javacv.version}
+ ${javacv.platform.linux-x86_64}
+
+
+ org.bytedeco
+ ffmpeg
+ 6.1.1-1.5.10
+ ${javacv.platform.linux-x86_64}
+
+
+
+ org.bytedeco
+ openblas
+ 0.3.26-1.5.10
+ ${javacv.platform.linux-x86_64}
+
+
+
+ org.bytedeco
+ opencv
+ 4.9.0-1.5.10
+ ${javacv.platform.linux-x86_64}
+
+
+
+
+
+ org.bytedeco
+ javacpp
+ ${javacv.version}
+ ${javacv.platform.macosx-arm64}
+
+
+ org.bytedeco
+ ffmpeg
+ 6.1.1-1.5.10
+ ${javacv.platform.macosx-arm64}
+
+
+
+ org.bytedeco
+ openblas
+ 0.3.26-1.5.10
+ ${javacv.platform.macosx-arm64}
+
+
+
+ org.bytedeco
+ opencv
+ 4.9.0-1.5.10
+ ${javacv.platform.macosx-arm64}
+
+
+
+
+
+ org.bytedeco
+ javacpp
+ ${javacv.version}
+ ${javacv.platform.linux-arm64}
+
+
+
+ org.bytedeco
+ ffmpeg
+ 6.1.1-1.5.10
+ ${javacv.platform.linux-arm64}
+
+
+
+ org.bytedeco
+ openblas
+ 0.3.26-1.5.10
+ ${javacv.platform.linux-arm64}
+
+
+
+ org.bytedeco
+ opencv
+ 4.9.0-1.5.10
+ ${javacv.platform.linux-arm64}
+
+
+
+
+
+
+
+
+
+
+ example
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.3
+
+
+ false
+
+ jar-with-dependencies
+
+
+
+
+ true
+
+ lib/
+
+ smartai.examples.face.FaceDemo2
+
+
+
+
+
+ make-assembly
+
+ package
+
+ assembly
+
+
+
+
+
+
+
+
+
+ aliyunmaven
+ 阿里云公共仓库
+ https://maven.aliyun.com/repository/public
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/src/main/java/smartai/examples/face/attribute/FaceAttributeDetDemo.java b/examples/src/main/java/smartai/examples/face/attribute/FaceAttributeDetDemo.java
new file mode 100644
index 0000000..10a9f18
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/face/attribute/FaceAttributeDetDemo.java
@@ -0,0 +1,135 @@
+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
+ * @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 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();
+ }
+ }
+
+
+}
diff --git a/examples/src/main/java/smartai/examples/face/facerec/FaceNetDemo.java b/examples/src/main/java/smartai/examples/face/facerec/FaceNetDemo.java
new file mode 100644
index 0000000..a1dcd7c
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/face/facerec/FaceNetDemo.java
@@ -0,0 +1,155 @@
+package smartai.examples.face.facerec;
+
+import cn.smartjavaai.face.config.FaceExtractConfig;
+import cn.smartjavaai.face.config.FaceModelConfig;
+import cn.smartjavaai.face.entity.FaceResult;
+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;
+import java.util.List;
+
+/**
+ * FaceNet人脸算法模型demo
+ * 支持功能:人脸特征提取、人脸比对(1:1)
+ * @author dwj
+ * @date 2025/4/11
+ */
+@Slf4j
+public class FaceNetDemo {
+
+ /**
+ * 提取人脸特征(支持多人脸)
+ * 默认使用检测模型:FACENET_FEATURE_EXTRACTION
+ * 自动裁剪人脸 + 人脸对齐
+ */
+ @Test
+ public void testExtractFeatures(){
+ try {
+ //人脸特征提取模型
+ FaceModelConfig config = new FaceModelConfig();
+ config.setModelEnum(FaceModelEnum.FACENET_FEATURE_EXTRACTION);
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
+ List faceResult = faceModel.extractFeatures("src/main/resources/kana1.jpg");
+ log.info("人脸特征提取结果:{}", JSONObject.toJSONString(faceResult));
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 提取人脸特征(支持多人脸,自定义配置)
+ * 自动裁剪人脸 + 人脸对齐
+ */
+ @Test
+ public void testExtractFeaturesWithCustomConfig(){
+ try {
+ //人脸特征提取模型
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(
+ new FaceModelConfig(FaceModelEnum.FACENET_FEATURE_EXTRACTION));
+ //人脸特征提取参数
+ FaceExtractConfig extractConfig = new FaceExtractConfig();
+ //人脸检测模型配置
+ extractConfig.setDetectModelConfig(new FaceModelConfig(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE));
+ List faceResult = faceModel.extractFeatures("src/main/resources/kana1.jpg",extractConfig);
+ log.info("人脸特征提取结果:{}", JSONObject.toJSONString(faceResult));
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 提取人脸特征(分数最高人脸)
+ * 默认使用检测模型:FACENET_FEATURE_EXTRACTION
+ * 自动裁剪人脸 + 人脸对齐
+ */
+ @Test
+ public void testExtractTopFaceFeature(){
+ try {
+ //人脸特征提取模型
+ FaceModelConfig config = new FaceModelConfig();
+ config.setModelEnum(FaceModelEnum.FACENET_FEATURE_EXTRACTION);
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
+ float[] faceResult = faceModel.extractTopFaceFeature("src/main/resources/kana1.jpg");
+ log.info("人脸特征提取结果:{}", JSONObject.toJSONString(faceResult));
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 提取人脸特征(分数最高人脸,自定义配置)
+ * 自动裁剪人脸 + 人脸对齐
+ */
+ @Test
+ public void testExtractTopFaceFeatureWithCustomConfig(){
+ try {
+ //人脸特征提取模型
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(
+ new FaceModelConfig(FaceModelEnum.FACENET_FEATURE_EXTRACTION));
+ //人脸特征提取参数
+ FaceExtractConfig extractConfig = new FaceExtractConfig();
+ //人脸检测模型配置
+ extractConfig.setDetectModelConfig(new FaceModelConfig(FaceModelEnum.ULTRA_LIGHT_FAST_GENERIC_FACE));
+ float[] faceResult = faceModel.extractTopFaceFeature("src/main/resources/kana1.jpg");
+ log.info("人脸特征提取结果:{}", JSONObject.toJSONString(faceResult));
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 人脸比对(1:1)-在线模型
+ * 图片参数:图片路径
+ * @throws Exception
+ */
+ @Test
+ public void featureComparison(){
+ try {
+ FaceModelConfig config = new FaceModelConfig();
+ config.setModelEnum(FaceModelEnum.FACENET_FEATURE_EXTRACTION);//人脸模型
+ //config.setModelPath("/Users/xxx/Documents/develop/face_model/model_ir_se50.pth");
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
+ //自动裁剪人脸并比对人脸特征
+ float similar = faceModel.featureComparison("src/main/resources/kana1.jpg","src/main/resources/kana2.jpg");
+ log.info("相似度:{}", similar);
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 人脸比对(1:1)- 使用离线模型
+ * 图片参数:图片路径
+ * @throws Exception
+ */
+ @Test
+ public void featureComparisonOffline(){
+ try {
+ FaceModelConfig config = new FaceModelConfig();
+ config.setModelEnum(FaceModelEnum.FACENET_FEATURE_EXTRACTION);//人脸模型
+ config.setModelPath("/Users/xxx/Documents/develop/face_model/face_feature.pt");
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
+ //自动裁剪人脸并比对人脸特征
+ float similar = faceModel.featureComparison("src/main/resources/kana1.jpg","src/main/resources/kana2.jpg");
+ log.info("相似度:{}", similar);
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+}
diff --git a/examples/src/main/java/smartai/examples/face/facerec/GpuFaceDemo.java b/examples/src/main/java/smartai/examples/face/facerec/GpuFaceDemo.java
new file mode 100644
index 0000000..5c489b4
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/face/facerec/GpuFaceDemo.java
@@ -0,0 +1,35 @@
+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 人脸检测
+ * @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));
+ }
+
+}
diff --git a/examples/src/main/java/smartai/examples/face/facerec/LightFaceDemo.java b/examples/src/main/java/smartai/examples/face/facerec/LightFaceDemo.java
new file mode 100644
index 0000000..c325acc
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/face/facerec/LightFaceDemo.java
@@ -0,0 +1,98 @@
+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
+ * 支持功能:人脸检测(不支持人脸特征提取)
+ * @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();
+ }
+ }
+
+
+}
diff --git a/examples/src/main/java/smartai/examples/face/facerec/RetinaFaceDemo.java b/examples/src/main/java/smartai/examples/face/facerec/RetinaFaceDemo.java
new file mode 100644
index 0000000..afe7b89
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/face/facerec/RetinaFaceDemo.java
@@ -0,0 +1,107 @@
+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
+ * 支持功能:人脸检测(不支持人脸特征提取)
+ * @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();
+ }
+ }
+
+
+}
diff --git a/examples/src/main/java/smartai/examples/face/facerec/SeetaFace6Demo.java b/examples/src/main/java/smartai/examples/face/facerec/SeetaFace6Demo.java
new file mode 100644
index 0000000..34441e2
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/face/facerec/SeetaFace6Demo.java
@@ -0,0 +1,251 @@
+package smartai.examples.face.facerec;
+
+import cn.smartjavaai.common.entity.DetectionResponse;
+import cn.smartjavaai.face.config.FaceModelConfig;
+import cn.smartjavaai.face.entity.FaceResult;
+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;
+import java.util.List;
+
+/**
+ * SeetaFace6人脸算法模型demo
+ * 支持系统:windows 64位
+ * 支持功能:人脸检测、人脸特征提取、人脸比对(1:1)、人脸比对(1:N)、人脸注册
+ * @author dwj
+ * @date 2025/4/11
+ */
+@Slf4j
+public class SeetaFace6Demo {
+
+
+ /**
+ * 人脸检测(自定义模型参数)
+ * 图片参数:图片路径
+ */
+ @Test
+ public void testFaceDetectCustomConfig(){
+ 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);
+ 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.SEETA_FACE6_MODEL);//人脸模型
+ config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
+ 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.SEETA_FACE6_MODEL);//人脸模型
+ config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
+ 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 testExtractFeatures(){
+ try {
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(new FaceModelConfig(FaceModelEnum.SEETA_FACE6_MODEL,
+ "C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models"));
+ List faceResult = faceModel.extractFeatures("src/main/resources/kana1.jpg");
+ log.info("人脸特征提取结果:{}", JSONObject.toJSONString(faceResult));
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 提取人脸特征(分数最高人脸)
+ * 自动裁剪人脸 + 人脸对齐
+ */
+ @Test
+ public void testExtractTopFaceFeature(){
+ try {
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(new FaceModelConfig(FaceModelEnum.SEETA_FACE6_MODEL,
+ "C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models"));
+ float[] faceResult = faceModel.extractTopFaceFeature("src/main/resources/kana1.jpg");
+ log.info("人脸特征提取结果:{}", JSONObject.toJSONString(faceResult));
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+
+
+ /**
+ * 人脸比对(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/kana1.jpg","src/main/resources/kana2.jpg");
+ log.info("相似度:{}", similar);
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 人脸比对(1:1)
+ * 先特征提取,后比对人脸特征
+ * 提取人脸特征图片参数:图片路径
+ */
+ @Test
+ public void featureExtractionAndCompare(){
+ 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[] feature1 = faceModel.extractTopFaceFeature("src/main/resources/kana1.jpg");
+ float[] feature2 = faceModel.extractTopFaceFeature("src/main/resources/kana2.jpg");
+ if(feature1 != null && feature2 != null){
+ float similar = faceModel.calculSimilar(feature1, feature2);
+ log.info("相似度:{}", similar);
+ }else{
+ log.warn("人脸特征提取失败");
+ }
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+
+ /**
+ * 注册人脸
+ * 图片参数:图片路径
+ */
+ @Test
+ public void registerFace(){
+ try {
+ FaceModelConfig config = new FaceModelConfig();
+ config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);//人脸模型
+ //人脸库路径,从项目中 db/faces-data.db下载到本地
+ config.setFaceDbPath("C:/Users/Administrator/Downloads/faces-data.db");
+ config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
+ //等待人脸库加载完毕
+ Thread.sleep(1000);
+ //注册kana1人脸,参数key建议设置为人名
+ boolean isSuccss = faceModel.register("kana1","src/main/resources/kana1.jpg");
+ log.info("注册结果:{}", isSuccss);
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 搜索人脸(1:N)
+ * 图片参数:图片路径
+ * 注意事项:请先注册人脸
+ */
+ @Test
+ public void searchFace(){
+ try {
+ FaceModelConfig config = new FaceModelConfig();
+ config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);//人脸模型
+ //人脸库路径,从项目中 db/faces-data.db下载到本地
+ config.setFaceDbPath("C:/Users/Administrator/Downloads/faces-data.db");
+ config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
+ //等待人脸库加载完毕
+ Thread.sleep(1000);
+ FaceResult faceResult = faceModel.search("src/main/resources/kana1.jpg");
+ if(faceResult != null){
+ log.info("查询到人脸:{}", faceResult.toString());
+ }else{
+ log.info("未查询到人脸");
+ }
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 删除已注册人脸
+ * 注意事项:请先注册人脸
+ */
+ @Test
+ public void removeRegisterFace(){
+ try {
+ FaceModelConfig config = new FaceModelConfig();
+ config.setModelEnum(FaceModelEnum.SEETA_FACE6_MODEL);//人脸模型
+ //人脸库路径,从项目中 db/faces-data.db下载到本地
+ config.setFaceDbPath("C:/Users/Administrator/Downloads/faces-data.db");
+ config.setModelPath("C:/Users/Administrator/Downloads/sf3.0_models/sf3.0_models");
+ FaceModel faceModel = FaceModelFactory.getInstance().getModel(config);
+ //等待人脸库加载完毕
+ Thread.sleep(1000);
+ //使用注册人脸时的key值删除,可一次性删除单个
+ long num = faceModel.removeRegister("kana1");
+ //删除全部人脸
+ //long num = currentAlgorithm.clearFace();
+ log.info("删除成功数量:" + num);
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+
+}
diff --git a/examples/src/main/java/smartai/examples/face/liveness/LivenessDetDemo.java b/examples/src/main/java/smartai/examples/face/liveness/LivenessDetDemo.java
new file mode 100644
index 0000000..386d58f
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/face/liveness/LivenessDetDemo.java
@@ -0,0 +1,283 @@
+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
+ * @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 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);
+ }
+ }
+
+
+}
diff --git a/examples/src/main/java/smartai/examples/objectdetection/ObjectDetection.java b/examples/src/main/java/smartai/examples/objectdetection/ObjectDetection.java
new file mode 100644
index 0000000..f8cd784
--- /dev/null
+++ b/examples/src/main/java/smartai/examples/objectdetection/ObjectDetection.java
@@ -0,0 +1,114 @@
+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.DetectorModelConfig;
+import cn.smartjavaai.objectdetection.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
+ * 支持功能:目标检测
+ * @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));
+ }
+
+
+
+}
diff --git a/examples/src/main/resources/META-INF/MANIFEST.MF b/examples/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..91b424f
--- /dev/null
+++ b/examples/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: smartai.examples.face.SeetaFace6LinuxDemo
+
diff --git a/examples/src/main/resources/dog_bike_car.jpg b/examples/src/main/resources/dog_bike_car.jpg
new file mode 100644
index 0000000..77b0381
Binary files /dev/null and b/examples/src/main/resources/dog_bike_car.jpg differ
diff --git a/examples/src/main/resources/double_person.png b/examples/src/main/resources/double_person.png
new file mode 100644
index 0000000..b6856fb
Binary files /dev/null and b/examples/src/main/resources/double_person.png differ
diff --git a/examples/src/main/resources/girl.mp4 b/examples/src/main/resources/girl.mp4
new file mode 100644
index 0000000..5120f9c
Binary files /dev/null and b/examples/src/main/resources/girl.mp4 differ
diff --git a/examples/src/main/resources/jsy.jpg b/examples/src/main/resources/jsy.jpg
new file mode 100644
index 0000000..c4640e7
Binary files /dev/null and b/examples/src/main/resources/jsy.jpg differ
diff --git a/examples/src/main/resources/kana1.jpg b/examples/src/main/resources/kana1.jpg
new file mode 100644
index 0000000..ef364e0
Binary files /dev/null and b/examples/src/main/resources/kana1.jpg differ
diff --git a/examples/src/main/resources/kana2.jpg b/examples/src/main/resources/kana2.jpg
new file mode 100644
index 0000000..59c9e52
Binary files /dev/null and b/examples/src/main/resources/kana2.jpg differ
diff --git a/examples/src/main/resources/largest_selfie.jpg b/examples/src/main/resources/largest_selfie.jpg
new file mode 100644
index 0000000..605ec97
Binary files /dev/null and b/examples/src/main/resources/largest_selfie.jpg differ
diff --git a/examples/src/main/resources/logback.xml b/examples/src/main/resources/logback.xml
new file mode 100644
index 0000000..8f006d9
--- /dev/null
+++ b/examples/src/main/resources/logback.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n
+
+
+
+
+
+
+
diff --git a/examples/src/main/resources/object_detection.jpg b/examples/src/main/resources/object_detection.jpg
new file mode 100644
index 0000000..9eb325a
Binary files /dev/null and b/examples/src/main/resources/object_detection.jpg differ