支持离线下载模型

This commit is contained in:
dengwenjie
2025-02-26 11:23:39 +08:00
parent e1aebbf37d
commit 685727e67c
15 changed files with 447 additions and 309 deletions

20
LICENSE Normal file
View 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.

View File

@@ -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
View 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>

View File

@@ -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>

View File

@@ -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
View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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("默认不支持该功能");

View File

@@ -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 图片路径

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

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

View File

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

View File

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