From 685727e67c69366490ed4e9025c9343cfb3a529a Mon Sep 17 00:00:00 2001 From: dengwenjie <775747758@qq.com> Date: Wed, 26 Feb 2025 11:23:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=A6=BB=E7=BA=BF=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 20 +++ README.md | 12 +- examples/face_offline.md | 99 +++++++++++ examples/pom.xml | 4 +- .../java/smartai/examples/face/FaceDemo.java | 81 ++++++++- pom.xml | 16 +- smartjavaai-common/pom.xml | 6 +- smartjavaai-face/pom.xml | 8 +- .../face/AbstractFaceAlgorithm.java | 5 + .../cn/smartjavaai/face/FaceAlgorithm.java | 7 + .../face/FaceAlgorithmFactory.java | 39 ++++- .../java/cn/smartjavaai/face/ModelConfig.java | 14 ++ .../face/algo/FeatureExtractionAlgo.java | 159 ++++++++++++++++++ .../cn/smartjavaai/face/algo/RetinaFace.java | 151 ++--------------- .../face/algo/UltraLightFastGenericFace.java | 135 --------------- 15 files changed, 447 insertions(+), 309 deletions(-) create mode 100644 LICENSE create mode 100644 examples/face_offline.md create mode 100644 smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/FeatureExtractionAlgo.java diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..47a2dcf --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2025 DengWenJie + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 2505e08..ad20d1a 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ ink.numberone smartjavaai-face - 1.0.1 + 1.0.2 ``` @@ -134,8 +134,14 @@ if(realTimeFeature != null){ } } ``` -## 快速体验指南 -▌**人脸识别快速运行** +### 6. 离线下载模型 + +​ **SmartJavaAI**如果未指定模型地址,系统将自动下载模型至本地。因此,无论模型是否通过离线方式下载,SmartJavaAI 最终都会在离线环境下运行模型。 + +- [离线使用模型使用教程](example/face_offline.md) + +## 完整代码 + `📁 examples/src/main/java/smartai/examples/face`  └── 📄[FaceDemo.java](https://github.com/geekwenjie/SmartJavaAI/blob/master/examples/src/main/java/smartai/examples/face/FaceDemo.java) *(基于JDK11构建的完整可执行示例)* diff --git a/examples/face_offline.md b/examples/face_offline.md new file mode 100644 index 0000000..874edbb --- /dev/null +++ b/examples/face_offline.md @@ -0,0 +1,99 @@ +# SmartJavaAI离线下载模型案例 + +**SmartJavaAI**如果未指定模型地址,系统将自动下载模型至本地。因此,无论模型是否通过离线方式下载,SmartJavaAI 最终都会在离线环境下运行模型。 + +### 1. 安装人脸算法依赖 + +在 Maven 项目的 `pom.xml` 中添加 SmartJavaAI的人脸算法依赖: + +```xml + + + ink.numberone + smartjavaai-face + 1.0.2 + + +``` + +### 2. 下载模型 + +| 模型名称 | 下载地址 | 文件大小 | 适用场景 | +| :-----------------------: | :----------------------------------------------------------: | :------: | :------------: | +| retinaface | [下载](https://resources.djl.ai/test-models/pytorch/retinaface.zip) | 110MB | 高精度人脸检测 | +| ultralightfastgenericface | [下载](https://resources.djl.ai/test-models/pytorch/ultranet.zip) | 1.7MB | 高速人脸检测 | +| featureExtraction | [下载](https://resources.djl.ai/test-models/pytorch/face_feature.zip) | 104MB | 人脸特征提取 | + +### 3. 人脸检测代码示例(离线下载模型) + +```java +// 初始化配置 +ModelConfig config = new ModelConfig(); +config.setAlgorithmName("retinaface");//人脸算法模型,目前支持:retinaface及ultralightfastgenericface +//config.setAlgorithmName("ultralightfastgenericface");//轻量模型 +config.setConfidenceThreshold(FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD);//置信度阈值 +config.setMaxFaceCount(FaceConfig.MAX_FACE_LIMIT);//每张特征图保留的最大候选框数量 +//nms阈值:控制重叠框的合并程度,取值越低,合并越多重叠框(减少误检但可能漏检);取值越高,保留更多框(增加检出但可能引入冗余) +config.setNmsThresh(FaceConfig.NMS_THRESHOLD); +//模型下载地址: +//retinaface: https://resources.djl.ai/test-models/pytorch/retinaface.zip +//ultralightfastgenericface: https://resources.djl.ai/test-models/pytorch/ultranet.zip +//改为模型存放路径 +config.setModelPath("/Users/xxx/Documents/develop/face_model/retinaface.pt"); +//创建人脸算法 +FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(config); +//使用图片路径检测 +FaceDetectedResult result = currentAlgorithm.detect("src/main/resources/largest_selfie.jpg"); +logger.info("人脸检测结果:{}", JSONObject.toJSONString(result)); +//使用图片流检测 +File input = new File("src/main/resources/largest_selfie.jpg"); +//FaceDetectedResult result = currentAlgorithm.detect(new FileInputStream(input)); +//logger.info("人脸检测结果:{}", JSONObject.toJSONString(result)); +BufferedImage image = ImageIO.read(input); +//创建保存路径 +Path imagePath = Paths.get("output").resolve("retinaface_detected.jpg"); +//绘制人脸框 +ImageUtils.drawBoundingBoxes(image, result, imagePath.toAbsolutePath().toString()); +``` + +### 3. 人证核验示例(离线下载模型) + +人证核验步骤: + +(1)提取身份证人脸特征, + +(2)提取实时人脸特征 + +(3)特征比对 + +```java +// 初始化配置 +ModelConfig config = new ModelConfig(); +config.setAlgorithmName("featureExtraction"); +//模型下载地址:https://resources.djl.ai/test-models/pytorch/face_feature.zip +//改为模型存放路径 +config.setModelPath("/Users/xxx/Documents/develop/face_model/face_feature.pt"); +//创建脸算法 +FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceFeatureAlgorithm(config); +//提取身份证人脸特征(图片仅供测试) +float[] featureIdCard = currentAlgorithm.featureExtraction("src/main/resources/kana1.jpg"); +//提取身份证人脸特征(从图片流获取) +//File input = new File("src/main/resources/kana1.jpg"); +//float[] featureIdCard = currentAlgorithm.featureExtraction(new FileInputStream(input)); +logger.info("身份证人脸特征:{}", JSONObject.toJSONString(featureIdCard)); +//提取实时人脸特征(图片仅供测试) +float[] realTimeFeature = currentAlgorithm.featureExtraction("src/main/resources/kana2.jpg"); +logger.info("实时人脸特征:{}", JSONObject.toJSONString(realTimeFeature)); +if(realTimeFeature != null){ + if(currentAlgorithm.calculSimilar(featureIdCard, realTimeFeature) > 0.8){ + logger.info("人脸核验通过"); + }else{ + logger.info("人脸核验不通过"); + } +} +``` + +## 完整代码 + +`📁 examples/src/main/java/smartai/examples/face` + └── 📄[FaceDemo.java](https://github.com/geekwenjie/SmartJavaAI/blob/master/examples/src/main/java/smartai/examples/face/FaceDemo.java) *(基于JDK11构建的完整可执行示例)* diff --git a/examples/pom.xml b/examples/pom.xml index 2d9c914..7256c47 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -13,7 +13,7 @@ 11 UTF-8 1.0.0-SNAPSHOT - ai.djl.examples.inference.cv.ObjectDetection + smartai.examples.face.FaceDemo @@ -43,7 +43,7 @@ ink.numberone smartjavaai-face - 1.0.1 + 1.0.2 diff --git a/examples/src/main/java/smartai/examples/face/FaceDemo.java b/examples/src/main/java/smartai/examples/face/FaceDemo.java index 75f5857..1c5a96f 100644 --- a/examples/src/main/java/smartai/examples/face/FaceDemo.java +++ b/examples/src/main/java/smartai/examples/face/FaceDemo.java @@ -1,10 +1,7 @@ package smartai.examples.face; import cn.smartjavaai.common.entity.Rectangle; -import cn.smartjavaai.face.FaceAlgorithm; -import cn.smartjavaai.face.FaceAlgorithmFactory; -import cn.smartjavaai.face.FaceDetectedResult; -import cn.smartjavaai.face.ModelConfig; +import cn.smartjavaai.face.*; import com.alibaba.fastjson.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,14 +23,13 @@ import java.nio.file.Paths; */ public class FaceDemo { - // 创建 Logger 实例 private static final Logger logger = LoggerFactory.getLogger(FaceDemo.class); public static void main(String[] args) { try { - detectFace(); - //verifyIDCard(); + //detectFace(); + verifyIDCard(); } catch (Exception e) { e.printStackTrace(); } @@ -52,7 +48,7 @@ public class FaceDemo { FaceDetectedResult result = currentAlgorithm.detect("src/main/resources/largest_selfie.jpg"); logger.info("人脸检测结果:{}", JSONObject.toJSONString(result)); //使用图片流检测 - //File input = new File("src/main/resources/largest_selfie.jpg"); + File input = new File("src/main/resources/largest_selfie.jpg"); //FaceDetectedResult result = currentAlgorithm.detect(new FileInputStream(input)); //logger.info("人脸检测结果:{}", JSONObject.toJSONString(result)); BufferedImage image = ImageIO.read(input); @@ -92,7 +88,74 @@ public class FaceDemo { */ public static void verifyIDCard() throws Exception { //创建脸算法 - FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(); + FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceFeatureAlgorithm(); + //提取身份证人脸特征(图片仅供测试) + float[] featureIdCard = currentAlgorithm.featureExtraction("src/main/resources/kana1.jpg"); + //提取身份证人脸特征(从图片流获取) + //File input = new File("src/main/resources/kana1.jpg"); + //float[] featureIdCard = currentAlgorithm.featureExtraction(new FileInputStream(input)); + logger.info("身份证人脸特征:{}", JSONObject.toJSONString(featureIdCard)); + //提取实时人脸特征(图片仅供测试) + float[] realTimeFeature = currentAlgorithm.featureExtraction("src/main/resources/kana2.jpg"); + logger.info("实时人脸特征:{}", JSONObject.toJSONString(realTimeFeature)); + if(realTimeFeature != null){ + if(currentAlgorithm.calculSimilar(featureIdCard, realTimeFeature) > 0.8){ + logger.info("人脸核验通过"); + }else{ + logger.info("人脸核验不通过"); + } + } + } + + /** + * 人脸检测(离线模型) + * 人脸模型:retinaface + * 特点:识别精度高,高速 + * 应用场景:如监控摄像头、智能安防系统等需要高精度检测的场合 + */ + public static void detectFaceOffine() throws Exception { + // 初始化配置 + ModelConfig config = new ModelConfig(); + config.setAlgorithmName("retinaface");//人脸算法模型,目前支持:retinaface及ultralightfastgenericface + //config.setAlgorithmName("ultralightfastgenericface");//轻量模型 + config.setConfidenceThreshold(FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD);//置信度阈值 + config.setMaxFaceCount(FaceConfig.MAX_FACE_LIMIT);//每张特征图保留的最大候选框数量 + //nms阈值:控制重叠框的合并程度,取值越低,合并越多重叠框(减少误检但可能漏检);取值越高,保留更多框(增加检出但可能引入冗余) + config.setNmsThresh(FaceConfig.NMS_THRESHOLD); + //模型下载地址: + //retinaface: https://resources.djl.ai/test-models/pytorch/retinaface.zip + //ultralightfastgenericface: https://resources.djl.ai/test-models/pytorch/ultranet.zip + //改为模型存放路径 + config.setModelPath("/Users/xxx/Documents/develop/face_model/retinaface.pt"); + //创建人脸算法 + FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceAlgorithm(config); + //使用图片路径检测 + FaceDetectedResult result = currentAlgorithm.detect("src/main/resources/largest_selfie.jpg"); + logger.info("人脸检测结果:{}", JSONObject.toJSONString(result)); + //使用图片流检测 + File input = new File("src/main/resources/largest_selfie.jpg"); + //FaceDetectedResult result = currentAlgorithm.detect(new FileInputStream(input)); + //logger.info("人脸检测结果:{}", JSONObject.toJSONString(result)); + BufferedImage image = ImageIO.read(input); + //创建保存路径 + Path imagePath = Paths.get("output").resolve("retinaface_detected.jpg"); + //绘制人脸框 + ImageUtils.drawBoundingBoxes(image, result, imagePath.toAbsolutePath().toString()); + } + + /** + * 人证核验(离线模型) + * @throws Exception + */ + public static void verifyIDCardOffine() throws Exception { + // 初始化配置 + ModelConfig config = new ModelConfig(); + config.setAlgorithmName("featureExtraction"); + //模型下载地址:https://resources.djl.ai/test-models/pytorch/face_feature.zip + //改为模型存放路径 + config.setModelPath("/Users/xxx/Documents/develop/face_model/face_feature.pt"); + //创建脸算法 + FaceAlgorithm currentAlgorithm = FaceAlgorithmFactory.createFaceFeatureAlgorithm(config); //提取身份证人脸特征(图片仅供测试) float[] featureIdCard = currentAlgorithm.featureExtraction("src/main/resources/kana1.jpg"); //提取身份证人脸特征(从图片流获取) diff --git a/pom.xml b/pom.xml index 4491084..2b401bb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ink.numberone smartjavaai-parent - 1.0.1 + 1.0.2 pom SmartJavaAI @@ -34,13 +34,13 @@ ink.numberone smartjavaai-common - 1.0.1 + 1.0.2 ink.numberone smartjavaai-face - 1.0.1 + 1.0.2 @@ -127,13 +127,19 @@ commons-beanutils 1.9.4 + + + org.apache.commons + commons-lang3 + 3.9 + https://github.com/geekwenjie/SmartJavaAI - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + MIT License + https://opensource.org/licenses/MIT diff --git a/smartjavaai-common/pom.xml b/smartjavaai-common/pom.xml index 5797800..ae0ecf2 100644 --- a/smartjavaai-common/pom.xml +++ b/smartjavaai-common/pom.xml @@ -6,7 +6,7 @@ ink.numberone smartjavaai-parent - 1.0.1 + 1.0.2 smartjavaai-common @@ -14,8 +14,8 @@ https://github.com/geekwenjie/SmartJavaAI - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + MIT License + https://opensource.org/licenses/MIT diff --git a/smartjavaai-face/pom.xml b/smartjavaai-face/pom.xml index 2ab4d26..c37aaae 100644 --- a/smartjavaai-face/pom.xml +++ b/smartjavaai-face/pom.xml @@ -6,18 +6,18 @@ ink.numberone smartjavaai-parent - 1.0.1 + 1.0.2 smartjavaai-face - 1.0.1 + 1.0.2 smartjavaai-face SmartJavaAI https://github.com/geekwenjie/SmartJavaAI - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + MIT License + https://opensource.org/licenses/MIT diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/AbstractFaceAlgorithm.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/AbstractFaceAlgorithm.java index 01d3feb..0589276 100644 --- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/AbstractFaceAlgorithm.java +++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/AbstractFaceAlgorithm.java @@ -12,6 +12,11 @@ public abstract class AbstractFaceAlgorithm implements FaceAlgorithm{ throw new UnsupportedOperationException("默认不支持该功能"); } + @Override + public void loadFaceFeatureModel(ModelConfig config) throws Exception { + throw new UnsupportedOperationException("默认不支持该功能"); + } + @Override public FaceDetectedResult detect(String imagePath) throws Exception { throw new UnsupportedOperationException("默认不支持该功能"); diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithm.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithm.java index cb86889..c72c4f1 100644 --- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithm.java +++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithm.java @@ -20,6 +20,13 @@ public interface FaceAlgorithm { */ void loadModel(ModelConfig config) throws Exception; // 加载模型 + /** + * 加载人脸特征提取模型 + * @param config + * @throws Exception + */ + void loadFaceFeatureModel(ModelConfig config) throws Exception; // 加载模型 + /** * 人脸检测 * @param imagePath 图片路径 diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithmFactory.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithmFactory.java index a491c0f..5cb06fc 100644 --- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithmFactory.java +++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceAlgorithmFactory.java @@ -1,5 +1,6 @@ package cn.smartjavaai.face; +import cn.smartjavaai.face.algo.FeatureExtractionAlgo; import cn.smartjavaai.face.algo.RetinaFace; import cn.smartjavaai.face.algo.UltraLightFastGenericFace; @@ -55,13 +56,7 @@ public class FaceAlgorithmFactory { config.setConfidenceThreshold(FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD); config.setMaxFaceCount(FaceConfig.MAX_FACE_LIMIT); config.setNmsThresh(FaceConfig.NMS_THRESHOLD); - Class clazz = registry.get(config.getAlgorithmName().toLowerCase()); - if(clazz == null){ - throw new IllegalArgumentException("Unsupported algorithm"); - } - FaceAlgorithm algorithm = (FaceAlgorithm) clazz.newInstance(); - algorithm.loadModel(config); - return algorithm; + return createFaceAlgorithm(config); } /** @@ -85,10 +80,40 @@ public class FaceAlgorithmFactory { return algorithm; } + /** + * 使用ModelConfig创建人脸特征提取算法 + * @param config + * @return + * @throws Exception + */ + public static FaceAlgorithm createFaceFeatureAlgorithm(ModelConfig config) throws Exception { + Class clazz = registry.get(config.getAlgorithmName().toLowerCase()); + if(clazz == null){ + throw new IllegalArgumentException("Unsupported algorithm"); + } + FaceAlgorithm algorithm = (FaceAlgorithm) clazz.newInstance(); + algorithm.loadFaceFeatureModel(config); + return algorithm; + } + + /** + * 创建人脸特征提取算法 + * @return + * @throws Exception + */ + public static FaceAlgorithm createFaceFeatureAlgorithm() throws Exception { + // 初始化配置 + ModelConfig config = new ModelConfig(); + config.setAlgorithmName("featureExtraction"); + return createFaceFeatureAlgorithm(config); + } + // 初始化默认算法 static { registerAlgorithm("retinaface", RetinaFace.class); registerAlgorithm("ultralightfastgenericface", UltraLightFastGenericFace.class); + //人脸特征提取 + registerAlgorithm("featureExtraction", FeatureExtractionAlgo.class); } } diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/ModelConfig.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/ModelConfig.java index 8b03c2f..c3a67e5 100644 --- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/ModelConfig.java +++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/ModelConfig.java @@ -27,6 +27,12 @@ public class ModelConfig { */ private int maxFaceCount; + /** + * 模型路径 + */ + private String modelPath; + + public String getAlgorithmName() { return algorithmName; } @@ -58,4 +64,12 @@ public class ModelConfig { public void setMaxFaceCount(int maxFaceCount) { this.maxFaceCount = maxFaceCount; } + + public String getModelPath() { + return modelPath; + } + + public void setModelPath(String modelPath) { + this.modelPath = modelPath; + } } diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/FeatureExtractionAlgo.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/FeatureExtractionAlgo.java new file mode 100644 index 0000000..934c542 --- /dev/null +++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/FeatureExtractionAlgo.java @@ -0,0 +1,159 @@ +package cn.smartjavaai.face.algo; + +import ai.djl.inference.Predictor; +import ai.djl.modality.cv.Image; +import ai.djl.modality.cv.ImageFactory; +import ai.djl.modality.cv.output.DetectedObjects; +import ai.djl.modality.cv.translator.ImageFeatureExtractorFactory; +import ai.djl.repository.zoo.Criteria; +import ai.djl.repository.zoo.ZooModel; +import ai.djl.training.util.ProgressBar; +import cn.smartjavaai.common.entity.Point; +import cn.smartjavaai.common.entity.Rectangle; +import cn.smartjavaai.face.AbstractFaceAlgorithm; +import cn.smartjavaai.face.FaceDetectedResult; +import cn.smartjavaai.face.FaceDetectionTranslator; +import cn.smartjavaai.face.ModelConfig; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * RetinaFace实现 + * @author dwj + */ +public class FeatureExtractionAlgo extends AbstractFaceAlgorithm { + + + private Criteria faceFeatureCriteria; + + public static final List mean = + Arrays.asList( + 127.5f / 255.0f, + 127.5f / 255.0f, + 127.5f / 255.0f, + 128.0f / 255.0f, + 128.0f / 255.0f, + 128.0f / 255.0f); + + + /** + * 加载人脸特征提取模型 + * @param config + * @throws Exception + */ + @Override + public void loadFaceFeatureModel(ModelConfig config) throws Exception { + String normalize = mean.stream().map(Object::toString).collect(Collectors.joining(",")); + faceFeatureCriteria = Criteria.builder() + .setTypes(Image.class, float[].class) + .optModelUrls(StringUtils.isNotBlank(config.getModelPath()) ? null : + "https://resources.djl.ai/test-models/pytorch/face_feature.zip") + .optModelPath(StringUtils.isNotBlank(config.getModelPath()) ? Paths.get(config.getModelPath()) : null) + .optModelName("face_feature") // specify model file prefix + .optArgument("normalize", normalize) + .optTranslatorFactory(new ImageFeatureExtractorFactory()) + .optProgress(new ProgressBar()) + .optEngine("PyTorch") // Use PyTorch engine + .build(); + } + + + /** + * 特征提取 + * @param imagePath 图片路径 + * @return + * @throws Exception + */ + @Override + public float[] featureExtraction(String imagePath) throws Exception { + Path imageFile = Paths.get(imagePath); + Image img = ImageFactory.getInstance().fromFile(imageFile); + img.getWrappedImage(); + try (ZooModel model = faceFeatureCriteria.loadModel()) { + Predictor predictor = model.newPredictor(); + return predictor.predict(img); + } + } + + /** + * 特征提取 + * @param inputStream 输入流 + * @return + * @throws Exception + */ + @Override + public float[] featureExtraction(InputStream inputStream) throws Exception { + Image img = ImageFactory.getInstance().fromInputStream(inputStream); + img.getWrappedImage(); + try (ZooModel model = faceFeatureCriteria.loadModel()) { + Predictor predictor = model.newPredictor(); + return predictor.predict(img); + } + } + + /** + * 计算相似度 + * @param feature1 图1特征 + * @param feature2 图2特征 + * @return + * @throws Exception + */ + @Override + public float calculSimilar(float[] feature1, float[] feature2) throws Exception { + float ret = 0.0f; + float mod1 = 0.0f; + float mod2 = 0.0f; + int length = feature1.length; + for (int i = 0; i < length; ++i) { + ret += feature1[i] * feature2[i]; + mod1 += feature1[i] * feature1[i]; + mod2 += feature2[i] * feature2[i]; + } + return (float) ((ret / Math.sqrt(mod1) / Math.sqrt(mod2) + 1) / 2.0f); + } + + /** + * 特征比较 + * @param imagePath1 图1路径 + * @param imagePath2 图2路径 + * @return + * @throws Exception + */ + @Override + public float featureComparison(String imagePath1, String imagePath2) throws Exception { + float[] feature1 = featureExtraction(imagePath1); + float[] feature2 = featureExtraction(imagePath2); + return calculSimilar(feature1, feature2); + } + + /** + * 特征比较 + * @param inputStream1 图1输入流 + * @param inputStream2 图2输入流 + * @return + * @throws Exception + */ + @Override + public float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception { + float[] feature1 = featureExtraction(inputStream1); + float[] feature2 = featureExtraction(inputStream2); + return calculSimilar(feature1, feature2); + } + + /*@Override + public float[] recognize(FaceRegion region) { + return new float[0]; + }*/ + + /*@Override + public void loadModel(ModelConfig config) throws Exception { + + }*/ +} diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/RetinaFace.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/RetinaFace.java index cc0bc5d..66bd67f 100644 --- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/RetinaFace.java +++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/RetinaFace.java @@ -14,8 +14,8 @@ import ai.djl.translate.TranslateException; import cn.smartjavaai.common.entity.Point; import cn.smartjavaai.common.entity.Rectangle; import cn.smartjavaai.face.*; -import org.apache.commons.beanutils.BeanUtils; -import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang3.StringUtils; + import java.io.IOException; import java.io.InputStream; @@ -36,6 +36,8 @@ public class RetinaFace extends AbstractFaceAlgorithm { private Criteria criteria; + private Criteria faceFeatureCriteria; + /** * 特征图层的基础缩放比例 */ @@ -49,6 +51,7 @@ public class RetinaFace extends AbstractFaceAlgorithm { */ public static final double[] variance = {0.1f, 0.2f}; + /** * 加载模型 * @param config @@ -60,16 +63,18 @@ public class RetinaFace extends AbstractFaceAlgorithm { criteria = Criteria.builder() .setTypes(Image.class, DetectedObjects.class) - .optModelUrls("https://resources.djl.ai/test-models/pytorch/retinaface.zip") - //.optModelPath(modelPath) + .optModelUrls(StringUtils.isNotBlank(config.getModelPath()) ? null : "https://resources.djl.ai/test-models/pytorch/retinaface.zip") // Load model from local file, e.g: - .optModelName("retinaface") // specify model file prefix + .optModelPath(StringUtils.isNotBlank(config.getModelPath()) ? Paths.get(config.getModelPath()) : null) + .optModelName(StringUtils.isNotBlank(config.getAlgorithmName()) ? config.getAlgorithmName() : "retinaface") // specify model file prefix .optTranslator(translator) .optProgress(new ProgressBar()) .optEngine("PyTorch") // Use PyTorch engine .build(); } + + /** * 检测人脸 * @param imagePath 图片路径 @@ -138,140 +143,4 @@ public class RetinaFace extends AbstractFaceAlgorithm { faceDetectedResult.setRectangles(RectangleList); return faceDetectedResult; } - - /** - * 特征提取 - * @param imagePath 图片路径 - * @return - * @throws Exception - */ - @Override - public float[] featureExtraction(String imagePath) throws Exception { - Path imageFile = Paths.get(imagePath); - Image img = ImageFactory.getInstance().fromFile(imageFile); - img.getWrappedImage(); - List mean = - Arrays.asList( - 127.5f / 255.0f, - 127.5f / 255.0f, - 127.5f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f); - String normalize = mean.stream().map(Object::toString).collect(Collectors.joining(",")); - - Criteria criteria = - Criteria.builder() - .setTypes(Image.class, float[].class) - .optModelUrls( - "https://resources.djl.ai/test-models/pytorch/face_feature.zip") - .optModelName("face_feature") // specify model file prefix - .optArgument("normalize", normalize) - .optTranslatorFactory(new ImageFeatureExtractorFactory()) - .optProgress(new ProgressBar()) - .optEngine("PyTorch") // Use PyTorch engine - .build(); - - try (ZooModel model = criteria.loadModel()) { - Predictor predictor = model.newPredictor(); - return predictor.predict(img); - } - } - - /** - * 特征提取 - * @param inputStream 输入流 - * @return - * @throws Exception - */ - @Override - public float[] featureExtraction(InputStream inputStream) throws Exception { - Image img = ImageFactory.getInstance().fromInputStream(inputStream); - img.getWrappedImage(); - List mean = - Arrays.asList( - 127.5f / 255.0f, - 127.5f / 255.0f, - 127.5f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f); - String normalize = mean.stream().map(Object::toString).collect(Collectors.joining(",")); - - Criteria criteria = - Criteria.builder() - .setTypes(Image.class, float[].class) - .optModelUrls( - "https://resources.djl.ai/test-models/pytorch/face_feature.zip") - .optModelName("face_feature") // specify model file prefix - .optArgument("normalize", normalize) - .optTranslatorFactory(new ImageFeatureExtractorFactory()) - .optProgress(new ProgressBar()) - .optEngine("PyTorch") // Use PyTorch engine - .build(); - - try (ZooModel model = criteria.loadModel()) { - Predictor predictor = model.newPredictor(); - return predictor.predict(img); - } - } - - /** - * 计算相似度 - * @param feature1 图1特征 - * @param feature2 图2特征 - * @return - * @throws Exception - */ - @Override - public float calculSimilar(float[] feature1, float[] feature2) throws Exception { - float ret = 0.0f; - float mod1 = 0.0f; - float mod2 = 0.0f; - int length = feature1.length; - for (int i = 0; i < length; ++i) { - ret += feature1[i] * feature2[i]; - mod1 += feature1[i] * feature1[i]; - mod2 += feature2[i] * feature2[i]; - } - return (float) ((ret / Math.sqrt(mod1) / Math.sqrt(mod2) + 1) / 2.0f); - } - - /** - * 特征比较 - * @param imagePath1 图1路径 - * @param imagePath2 图2路径 - * @return - * @throws Exception - */ - @Override - public float featureComparison(String imagePath1, String imagePath2) throws Exception { - float[] feature1 = featureExtraction(imagePath1); - float[] feature2 = featureExtraction(imagePath2); - return calculSimilar(feature1, feature2); - } - - /** - * 特征比较 - * @param inputStream1 图1输入流 - * @param inputStream2 图2输入流 - * @return - * @throws Exception - */ - @Override - public float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception { - float[] feature1 = featureExtraction(inputStream1); - float[] feature2 = featureExtraction(inputStream2); - return calculSimilar(feature1, feature2); - } - - /*@Override - public float[] recognize(FaceRegion region) { - return new float[0]; - }*/ - - /*@Override - public void loadModel(ModelConfig config) throws Exception { - - }*/ } diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/UltraLightFastGenericFace.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/UltraLightFastGenericFace.java index 3240993..992823f 100644 --- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/UltraLightFastGenericFace.java +++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/algo/UltraLightFastGenericFace.java @@ -131,139 +131,4 @@ public class UltraLightFastGenericFace extends AbstractFaceAlgorithm { return faceDetectedResult; } - /** - * 特征提取 - * @param imagePath 图片路径 - * @return - * @throws Exception - */ - @Override - public float[] featureExtraction(String imagePath) throws Exception { - Path imageFile = Paths.get(imagePath); - Image img = ImageFactory.getInstance().fromFile(imageFile); - img.getWrappedImage(); - List mean = - Arrays.asList( - 127.5f / 255.0f, - 127.5f / 255.0f, - 127.5f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f); - String normalize = mean.stream().map(Object::toString).collect(Collectors.joining(",")); - - Criteria criteria = - Criteria.builder() - .setTypes(Image.class, float[].class) - .optModelUrls( - "https://resources.djl.ai/test-models/pytorch/face_feature.zip") - .optModelName("face_feature") // specify model file prefix - .optArgument("normalize", normalize) - .optTranslatorFactory(new ImageFeatureExtractorFactory()) - .optProgress(new ProgressBar()) - .optEngine("PyTorch") // Use PyTorch engine - .build(); - - try (ZooModel model = criteria.loadModel()) { - Predictor predictor = model.newPredictor(); - return predictor.predict(img); - } - } - - /** - * 特征提取 - * @param inputStream 输入流 - * @return - * @throws Exception - */ - @Override - public float[] featureExtraction(InputStream inputStream) throws Exception { - Image img = ImageFactory.getInstance().fromInputStream(inputStream); - img.getWrappedImage(); - List mean = - Arrays.asList( - 127.5f / 255.0f, - 127.5f / 255.0f, - 127.5f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f, - 128.0f / 255.0f); - String normalize = mean.stream().map(Object::toString).collect(Collectors.joining(",")); - - Criteria criteria = - Criteria.builder() - .setTypes(Image.class, float[].class) - .optModelUrls( - "https://resources.djl.ai/test-models/pytorch/face_feature.zip") - .optModelName("face_feature") // specify model file prefix - .optArgument("normalize", normalize) - .optTranslatorFactory(new ImageFeatureExtractorFactory()) - .optProgress(new ProgressBar()) - .optEngine("PyTorch") // Use PyTorch engine - .build(); - - try (ZooModel model = criteria.loadModel()) { - Predictor predictor = model.newPredictor(); - return predictor.predict(img); - } - } - - /** - * 计算相似度 - * @param feature1 图1特征 - * @param feature2 图2特征 - * @return - * @throws Exception - */ - @Override - public float calculSimilar(float[] feature1, float[] feature2) throws Exception { - float ret = 0.0f; - float mod1 = 0.0f; - float mod2 = 0.0f; - int length = feature1.length; - for (int i = 0; i < length; ++i) { - ret += feature1[i] * feature2[i]; - mod1 += feature1[i] * feature1[i]; - mod2 += feature2[i] * feature2[i]; - } - return (float) ((ret / Math.sqrt(mod1) / Math.sqrt(mod2) + 1) / 2.0f); - } - - /** - * 特征比较 - * @param imagePath1 图1路径 - * @param imagePath2 图2路径 - * @return - * @throws Exception - */ - @Override - public float featureComparison(String imagePath1, String imagePath2) throws Exception { - float[] feature1 = featureExtraction(imagePath1); - float[] feature2 = featureExtraction(imagePath2); - return calculSimilar(feature1, feature2); - } - - /** - * 特征比较 - * @param inputStream1 图1输入流 - * @param inputStream2 图2输入流 - * @return - * @throws Exception - */ - @Override - public float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception { - float[] feature1 = featureExtraction(inputStream1); - float[] feature2 = featureExtraction(inputStream2); - return calculSimilar(feature1, feature2); - } - - /*@Override - public float[] recognize(FaceRegion region) { - return new float[0]; - }*/ - - /*@Override - public void loadModel(ModelConfig config) throws Exception { - - }*/ }