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