mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-10 03:28:49 +00:00
支持离线下载模型
This commit is contained in:
20
LICENSE
Normal file
20
LICENSE
Normal file
@@ -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.
|
||||
12
README.md
12
README.md
@@ -73,7 +73,7 @@
|
||||
<dependency>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
@@ -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) <sub>*(基于JDK11构建的完整可执行示例)*</sub>
|
||||
|
||||
99
examples/face_offline.md
Normal file
99
examples/face_offline.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# SmartJavaAI离线下载模型案例
|
||||
|
||||
**SmartJavaAI**如果未指定模型地址,系统将自动下载模型至本地。因此,无论模型是否通过离线方式下载,SmartJavaAI 最终都会在离线环境下运行模型。
|
||||
|
||||
### 1. 安装人脸算法依赖
|
||||
|
||||
在 Maven 项目的 `pom.xml` 中添加 SmartJavaAI的人脸算法依赖:
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
### 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) <sub>*(基于JDK11构建的完整可执行示例)*</sub>
|
||||
@@ -13,7 +13,7 @@
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<smartjavaai.version>1.0.0-SNAPSHOT</smartjavaai.version>
|
||||
<exec.mainClass>ai.djl.examples.inference.cv.ObjectDetection</exec.mainClass>
|
||||
<exec.mainClass>smartai.examples.face.FaceDemo</exec.mainClass>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<dependency>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -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");
|
||||
//提取身份证人脸特征(从图片流获取)
|
||||
|
||||
16
pom.xml
16
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
<packaging>pom</packaging>
|
||||
<description>SmartJavaAI</description>
|
||||
<modules>
|
||||
@@ -34,13 +34,13 @@
|
||||
<dependency>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-common</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -127,13 +127,19 @@
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>1.9.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<name>MIT License</name>
|
||||
<url>https://opensource.org/licenses/MIT</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>smartjavaai-common</artifactId>
|
||||
@@ -14,8 +14,8 @@
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<name>MIT License</name>
|
||||
<url>https://opensource.org/licenses/MIT</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
<parent>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>smartjavaai-face</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.0.2</version>
|
||||
<name>smartjavaai-face</name>
|
||||
<description>SmartJavaAI</description>
|
||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<name>MIT License</name>
|
||||
<url>https://opensource.org/licenses/MIT</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
|
||||
@@ -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("默认不支持该功能");
|
||||
|
||||
@@ -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 图片路径
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Image, float[]> faceFeatureCriteria;
|
||||
|
||||
public static final List<Float> 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<Image, float[]> model = faceFeatureCriteria.loadModel()) {
|
||||
Predictor<Image, float[]> 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<Image, float[]> model = faceFeatureCriteria.loadModel()) {
|
||||
Predictor<Image, float[]> 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 {
|
||||
|
||||
}*/
|
||||
}
|
||||
@@ -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<Image, DetectedObjects> criteria;
|
||||
|
||||
private Criteria<Image, float[]> 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<Float> 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<Image, float[]> 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<Image, float[]> model = criteria.loadModel()) {
|
||||
Predictor<Image, float[]> 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<Float> 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<Image, float[]> 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<Image, float[]> model = criteria.loadModel()) {
|
||||
Predictor<Image, float[]> 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 {
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -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<Float> 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<Image, float[]> 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<Image, float[]> model = criteria.loadModel()) {
|
||||
Predictor<Image, float[]> 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<Float> 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<Image, float[]> 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<Image, float[]> model = criteria.loadModel()) {
|
||||
Predictor<Image, float[]> 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 {
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user