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 {
-
- }*/
}