diff --git a/README.md b/README.md
index 33d899f..4a65229 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,15 @@
2、[PCN](https://github.com/Rock-100/FaceKit/tree/master/PCN)
+### 版本2.1.0更新
+
+* 1、InsightScrfdFaceDetection升级模型,使检测更加稳定,同时添加了人脸角度检测。
+* 2、InsightScrfdFaceDetection正对不能正常检出人脸的图片增加了补边操作,防止因为人脸过大导致不能检测到人脸。
+* 3、添加SeetaFaceOpenRecognition的人脸特征提取器,目前人脸特征提取器支持InsightArcFaceRecognition与SeetaFaceOpenRecognition。
+* 4、修复由于人脸过小,导致对齐异常的BUG。
+* 5、程序添加了SeetaFace6的人脸关键点遮挡模型。
+* 6、升级opencv、opensearch、onnxruntime的maven依赖版本。
+
### 版本2.0.1更新
* 1、修复PCN模型存在的潜在内存泄露问题
@@ -41,7 +50,7 @@
### 项目文档
-* 在线文档:[文档-2.0.1](scripts/docs/2.0.0.md)
+* 在线文档:[文档-2.1.0](scripts/docs/2.1.0.md)
* swagger文档:启动项目且开启swagger,访问:host:port/doc.html, 如 http://127.0.0.1:8080/doc.html
@@ -52,12 +61,12 @@
com.visual.face.search
face-search-client
- 2.0.1
+ 2.1.0
```
* 其他语言依赖
- 使用restful接口:[文档-2.0.1](scripts/docs/2.0.0.md)
+ 使用restful接口:[文档-2.1.0](scripts/docs/2.1.0.md)
### 项目部署
@@ -89,41 +98,41 @@
* 部署参数
-| 参数 | 描述 | 默认值 | 可选值|
-| -------- | -----: | :----: |--------|
-| VISUAL_SWAGGER_ENABLE | 是否开启swagger | true | |
-| SPRING_DATASOURCE_URL | 数据库地址 | | |
-| SPRING_DATASOURCE_USERNAME | 数据库用户名 | root | |
-| SPRING_DATASOURCE_PASSWORD | 数据库密码 | root | |
-| VISUAL_ENGINE_OPENSEARCH_HOST | OPENSEARCH地址 | | |
-| VISUAL_ENGINE_OPENSEARCH_PORT | OPENSEARCH端口 | 9200 | |
-| VISUAL_ENGINE_OPENSEARCH_SCHEME | OPENSEARCH协议 | https | |
-| VISUAL_ENGINE_OPENSEARCH_USERNAME | OPENSEARCH用户名 | admin | |
-| VISUAL_ENGINE_OPENSEARCH_PASSWORD | OPENSEARCH密码 | admin | |
-| VISUAL_MODEL_FACEDETECTION_NAME | 人脸检测模型名称 | PcnNetworkFaceDetection |PcnNetworkFaceDetection,InsightScrfdFaceDetection|
-| VISUAL_MODEL_FACEDETECTION_BACKUP_NAME | 备用人脸检测模型名称 | InsightScrfdFaceDetection |PcnNetworkFaceDetection,InsightScrfdFaceDetection|
-| VISUAL_MODEL_FACEKEYPOINT_NAME | 人脸关键点模型名称 | InsightCoordFaceKeyPoint |InsightCoordFaceKeyPoint|
-| VISUAL_MODEL_FACEALIGNMENT_NAME | 人脸对齐模型名称 | Simple106pFaceAlignment |Simple106pFaceAlignment,Simple005pFaceAlignment|
-| VISUAL_MODEL_FACERECOGNITION_NAME | 人脸特征提取模型名称 | InsightArcFaceRecognition |InsightArcFaceRecognition|
+| 参数 | 描述 | 默认值 | 可选值 |
+| -------- | -----: | :----: |---------------------------------------------------|
+| VISUAL_SWAGGER_ENABLE | 是否开启swagger | true | |
+| SPRING_DATASOURCE_URL | 数据库地址 | | |
+| SPRING_DATASOURCE_USERNAME | 数据库用户名 | root | |
+| SPRING_DATASOURCE_PASSWORD | 数据库密码 | root | |
+| VISUAL_ENGINE_OPENSEARCH_HOST | OPENSEARCH地址 | | |
+| VISUAL_ENGINE_OPENSEARCH_PORT | OPENSEARCH端口 | 9200 | |
+| VISUAL_ENGINE_OPENSEARCH_SCHEME | OPENSEARCH协议 | https | |
+| VISUAL_ENGINE_OPENSEARCH_USERNAME | OPENSEARCH用户名 | admin | |
+| VISUAL_ENGINE_OPENSEARCH_PASSWORD | OPENSEARCH密码 | admin | |
+| VISUAL_MODEL_FACEDETECTION_NAME | 人脸检测模型名称 | InsightScrfdFaceDetection | PcnNetworkFaceDetection,InsightScrfdFaceDetection |
+| VISUAL_MODEL_FACEDETECTION_BACKUP_NAME | 备用人脸检测模型名称 | PcnNetworkFaceDetection | PcnNetworkFaceDetection,InsightScrfdFaceDetection |
+| VISUAL_MODEL_FACEKEYPOINT_NAME | 人脸关键点模型名称 | InsightCoordFaceKeyPoint | InsightCoordFaceKeyPoint |
+| VISUAL_MODEL_FACEALIGNMENT_NAME | 人脸对齐模型名称 | Simple106pFaceAlignment | Simple106pFaceAlignment,Simple005pFaceAlignment |
+| VISUAL_MODEL_FACERECOGNITION_NAME | 人脸特征提取模型名称 | InsightArcFaceRecognition | InsightArcFaceRecognition,SeetaFaceOpenRecognition |
### 性能优化
-* 项目中为了提高人脸的检出率,使用了主要和次要的人脸检测模型,目前实现了两种人脸检测模型insightface和PCN,在docker的服务中,默认主服务为PCN,备用服务为insightface。insightface的效率高,但针对于旋转了大角度的人脸检出率不高,而pcn则可以识别大角度旋转的图片,但效率低一些。若图像均为正脸的图像,建议使用insightface为主模型,pcn为备用模型,如何切换,请查看部署参数。
+* 项目中为了提高人脸的检出率,使用了主要和次要的人脸检测模型,目前实现了两种人脸检测模型Insightface和PCN,在docker的服务中,默认主服务为Insightface,备用服务为PCN。insightface的效率高,但针对于旋转了大角度的人脸检出率不高,而pcn则可以识别大角度旋转的图片,但效率低一些。若图像均为正脸的图像,建议使用insightface为主模型,pcn为备用模型,如何切换,请查看部署参数。
### 项目演示
-* 2.0.0 测试用例(做了优化,增强了搜索结果的区分度):face-search-test[测试用例-FaceSearchExample](https://gitee.com/open-visual/face-search/blob/master/face-search-test/src/main/java/com/visual/face/search/valid/exps/FaceSearchExample.java)
+* 2.1.0 测试用例:face-search-test[测试用例-FaceSearchExample](https://gitee.com/open-visual/face-search/blob/master/face-search-test/src/main/java/com/visual/face/search/valid/exps/FaceSearchExample.java)
* 
### 演员识别(手机打开体验更好)
-* [http://actor-search.diven.nat300.top](http://actor-search.diven.nat300.top)
+* [http://actor-search.divenswu.com](http://actor-search.divenswu.com)
* 
### 交流群
-* 钉钉交流群
+* 钉钉交流群(已解散)
关注微信公众号回复:钉钉群
diff --git a/face-search-client/pom.xml b/face-search-client/pom.xml
index bd11692..6e00b59 100644
--- a/face-search-client/pom.xml
+++ b/face-search-client/pom.xml
@@ -6,7 +6,7 @@
com.visual.face.search
face-search-client
- 2.0.1
+ 2.1.0
1.8
@@ -18,7 +18,7 @@
com.alibaba
fastjson
- 1.2.58
+ 1.2.83
org.apache.httpcomponents
diff --git a/face-search-core/pom.xml b/face-search-core/pom.xml
index 27031a4..5704684 100644
--- a/face-search-core/pom.xml
+++ b/face-search-core/pom.xml
@@ -5,7 +5,7 @@
face-search
com.visual.face.search
- 2.0.1
+ 2.1.0
4.0.0
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/base/FaceMaskPoint.java b/face-search-core/src/main/java/com/visual/face/search/core/base/FaceMaskPoint.java
new file mode 100755
index 0000000..35e0a57
--- /dev/null
+++ b/face-search-core/src/main/java/com/visual/face/search/core/base/FaceMaskPoint.java
@@ -0,0 +1,21 @@
+package com.visual.face.search.core.base;
+
+import com.visual.face.search.core.domain.ImageMat;
+import com.visual.face.search.core.domain.QualityInfo;
+
+import java.util.Map;
+
+/**
+ * 人脸关键点检测
+ */
+public interface FaceMaskPoint {
+
+ /**
+ * 人脸关键点检测
+ * @param imageMat 图像数据
+ * @param params 参数信息
+ * @return
+ */
+ QualityInfo.MaskPoints inference(ImageMat imageMat, Map params);
+
+}
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/domain/FaceInfo.java b/face-search-core/src/main/java/com/visual/face/search/core/domain/FaceInfo.java
index 58b9174..c8cc44b 100755
--- a/face-search-core/src/main/java/com/visual/face/search/core/domain/FaceInfo.java
+++ b/face-search-core/src/main/java/com/visual/face/search/core/domain/FaceInfo.java
@@ -137,6 +137,18 @@ public class FaceInfo implements Comparable, Serializable {
public float distance(Point that){
return (float) Math.sqrt(Math.pow((this.x-that.x), 2)+Math.pow((this.y-that.y), 2));
}
+
+ /**
+ * 将点进行平移
+ * @param top 向上移动的像素点数
+ * @param bottom 向下移动的像素点数
+ * @param left 向左移动的像素点数
+ * @param right 向右移动的像素点数
+ * @return 平移后的点
+ */
+ public Point move(int left, int right, int top, int bottom){
+ return new Point(x - left + right, y - top + bottom);
+ }
}
/**
@@ -246,6 +258,22 @@ public class FaceInfo implements Comparable, Serializable {
}
return points;
}
+
+ /**
+ * 将点进行平移
+ * @param top 向上移动的像素点数
+ * @param bottom 向下移动的像素点数
+ * @param left 向左移动的像素点数
+ * @param right 向右移动的像素点数
+ * @return 平移后的点
+ */
+ public Points move(int left, int right, int top, int bottom){
+ Points points = build();
+ for(Point item : this){
+ points.add(item.move(left, right, top, bottom));
+ }
+ return points;
+ }
}
/**
@@ -419,6 +447,23 @@ public class FaceInfo implements Comparable, Serializable {
new Point(leftBottom.x + change_x_p2_p4, leftBottom.y + change_y_p2_p4)
);
}
+
+ /**
+ * 将框进行平移
+ * @param top 向上移动的像素点数
+ * @param bottom 向下移动的像素点数
+ * @param left 向左移动的像素点数
+ * @param right 向右移动的像素点数
+ * @return 平移后的框
+ */
+ public FaceBox move(int left, int right, int top, int bottom){
+ return new FaceBox(
+ new Point(leftTop.x - left + right, leftTop.y - top + bottom),
+ new Point(rightTop.x - left + right, rightTop.y - top + bottom),
+ new Point(rightBottom.x - left + right, rightBottom.y - top + bottom),
+ new Point(leftBottom.x - left + right, leftBottom.y - top + bottom)
+ );
+ }
}
/**
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/domain/ImageMat.java b/face-search-core/src/main/java/com/visual/face/search/core/domain/ImageMat.java
index c755dc1..7b7e002 100755
--- a/face-search-core/src/main/java/com/visual/face/search/core/domain/ImageMat.java
+++ b/face-search-core/src/main/java/com/visual/face/search/core/domain/ImageMat.java
@@ -252,6 +252,55 @@ public class ImageMat implements Serializable {
}
}
+ /**
+ * 对图像进行补边操作,不释放原始的图片
+ * @param top 向上扩展的高度
+ * @param bottom 向下扩展的高度
+ * @param left 向左扩展的宽度
+ * @param right 向右扩展的宽度
+ * @param borderType 补边的类型
+ * @return 补边后的图像
+ */
+ public ImageMat copyMakeBorderAndNotReleaseMat(int top, int bottom, int left, int right, int borderType){
+ return this.copyMakeBorder(top, bottom, left, right, borderType, false);
+ }
+
+ /**
+ * 对图像进行补边操作,并且释放原始的图片
+ * @param top 向上扩展的高度
+ * @param bottom 向下扩展的高度
+ * @param left 向左扩展的宽度
+ * @param right 向右扩展的宽度
+ * @param borderType 补边的类型
+ * @return 补边后的图像
+ */
+ public ImageMat copyMakeBorderAndDoReleaseMat(int top, int bottom, int left, int right, int borderType){
+ return this.copyMakeBorder(top, bottom, left, right, borderType, true);
+ }
+
+ /**
+ * 对图像进行补边操作
+ * @param top 向上扩展的高度
+ * @param bottom 向下扩展的高度
+ * @param left 向左扩展的宽度
+ * @param right 向右扩展的宽度
+ * @param borderType 补边的类型
+ * @param release 是否释放原始的图片
+ * @return 补边后的图像
+ */
+ private ImageMat copyMakeBorder(int top, int bottom, int left, int right, int borderType, boolean release){
+ try {
+ Mat tempMat = new Mat();
+ Core.copyMakeBorder(mat, tempMat, top, bottom, left, right, borderType);
+ return new ImageMat(tempMat);
+ }finally {
+ if(release){
+ this.release();
+ }
+ }
+ }
+
+
/**
* 对图像进行预处理,不释放原始图片数据
* @param scale 图像各通道数值的缩放比例
@@ -264,7 +313,7 @@ public class ImageMat implements Serializable {
}
/**
- * 对图像进行预处理,并释放原始图片数据
+ * 对图像进行预处理,并释放原始图片数据:(先交换RB通道(swapRB),再减法(mean),最后缩放(scale))
* @param scale 图像各通道数值的缩放比例
* @param mean 用于各通道减去的值,以降低光照的影响
* @param swapRB 交换RB通道,默认为False.
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/domain/QualityInfo.java b/face-search-core/src/main/java/com/visual/face/search/core/domain/QualityInfo.java
new file mode 100644
index 0000000..1738f08
--- /dev/null
+++ b/face-search-core/src/main/java/com/visual/face/search/core/domain/QualityInfo.java
@@ -0,0 +1,129 @@
+package com.visual.face.search.core.domain;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class QualityInfo {
+
+ public MaskPoints maskPoints;
+
+ private QualityInfo(MaskPoints maskPoints) {
+ this.maskPoints = maskPoints;
+ }
+
+ public static QualityInfo build(MaskPoints maskPoints){
+ return new QualityInfo(maskPoints);
+ }
+
+ public MaskPoints getMaskPoints() {
+ return maskPoints;
+ }
+
+
+ public boolean isMask(){
+ return null != this.maskPoints && this.maskPoints.isMask();
+ }
+
+
+ /**
+ * 遮挡类
+ */
+ public static class Mask implements Serializable {
+ /**遮挡分数*/
+ public float score;
+
+ public static Mask build(float score){
+ return new QualityInfo.Mask(score);
+ }
+
+ private Mask(float score) {
+ this.score = score;
+ }
+
+ public float getScore() {
+ return score;
+ }
+
+ public boolean isMask(){
+ return this.score >= 0.5;
+ }
+
+ @Override
+ public String toString() {
+ return "Mask{" + "score=" + score + '}';
+ }
+ }
+
+ /**
+ * 点遮挡类
+ */
+ public static class MaskPoint extends Mask{
+ /**坐标X的值**/
+ public float x;
+ /**坐标Y的值**/
+ public float y;
+
+ public static MaskPoint build(float x, float y, float score){
+ return new MaskPoint(x, y, score);
+ }
+
+ private MaskPoint(float x, float y, float score) {
+ super(score);
+ this.x = x;
+ this.y = y;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ @Override
+ public String toString() {
+ return "MaskPoint{" + "x=" + x + ", y=" + y + ", score=" + score + '}';
+ }
+ }
+
+ /**
+ * 点遮挡类集合
+ */
+ public static class MaskPoints extends ArrayList {
+
+ private MaskPoints(){}
+
+ /**
+ * 构建一个集合
+ * @return
+ */
+ public static MaskPoints build(){
+ return new MaskPoints();
+ }
+
+ /**
+ * 添加点
+ * @param point
+ * @return
+ */
+ public MaskPoints add(MaskPoint...point){
+ super.addAll(Arrays.asList(point));
+ return this;
+ }
+
+ /**
+ * 判定是否存在遮挡
+ * @return
+ */
+ public boolean isMask(){
+ for(MaskPoint point : this){
+ if(point.isMask()){
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/models/InsightScrfdFaceDetection.java b/face-search-core/src/main/java/com/visual/face/search/core/models/InsightScrfdFaceDetection.java
index 6697e6b..a4ef168 100755
--- a/face-search-core/src/main/java/com/visual/face/search/core/models/InsightScrfdFaceDetection.java
+++ b/face-search-core/src/main/java/com/visual/face/search/core/models/InsightScrfdFaceDetection.java
@@ -7,6 +7,9 @@ import com.visual.face.search.core.base.BaseOnnxInfer;
import com.visual.face.search.core.base.FaceDetection;
import com.visual.face.search.core.domain.FaceInfo;
import com.visual.face.search.core.domain.ImageMat;
+import com.visual.face.search.core.utils.ReleaseUtil;
+import org.opencv.core.Core;
+import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import java.util.*;
@@ -24,6 +27,18 @@ public class InsightScrfdFaceDetection extends BaseOnnxInfer implements FaceDete
public final static float defScoreTh = 0.5f;
//人脸重叠iou阈值
public final static float defIouTh = 0.7f;
+ //给人脸框一个默认的缩放
+ public final static float defBoxScale = 1.0f;
+ //人脸框缩放参数KEY
+ public final static String scrfdFaceboxScaleParamKey = "scrfdFaceboxScale";
+ //人脸框默认需要进行角度检测
+ public final static boolean defNeedCheckFaceAngle = true;
+ //是否需要进行角度检测的参数KEY
+ public final static String scrfdFaceNeedCheckFaceAngleParamKey = "scrfdFaceNeedCheckFaceAngle";
+ //人脸框默认需要进行角度检测
+ public final static boolean defNoFaceImageNeedMakeBorder = true;
+ //是否需要进行角度检测的参数KEY
+ public final static String scrfdNoFaceImageNeedMakeBorderParamKey = "scrfdNoFaceImageNeedMakeBorder";
/**
* 构造函数
@@ -43,11 +58,46 @@ public class InsightScrfdFaceDetection extends BaseOnnxInfer implements FaceDete
*/
@Override
public List inference(ImageMat image, float scoreTh, float iouTh, Map params) {
+ List faceInfos = this.modelInference(image, scoreTh,iouTh, params);
+ //对图像进行补边操作,进行二次识别
+ if(this.getNoFaceImageNeedMakeBorder(params) && faceInfos.isEmpty()){
+ //防止由于人脸占用大,导致检测模型识别失败
+ int t = Double.valueOf(image.toCvMat().height() * 0.2).intValue();
+ int b = Double.valueOf(image.toCvMat().height() * 0.2).intValue();
+ int l = Double.valueOf(image.toCvMat().width() * 0.2).intValue();
+ int r = Double.valueOf(image.toCvMat().width() * 0.2).intValue();
+ ImageMat tempMat = null;
+ try {
+ //补边识别
+ tempMat=image.copyMakeBorderAndNotReleaseMat(t, b, l, r, Core.BORDER_CONSTANT);
+ faceInfos = this.modelInference(tempMat, scoreTh,iouTh, params);
+ for(FaceInfo faceInfo : faceInfos){
+ //还原原始的坐标
+ faceInfo.box = faceInfo.box.move(l, 0, t, 0);
+ faceInfo.points = faceInfo.points.move(l, 0, t, 0);
+ }
+ }finally {
+ ReleaseUtil.release(tempMat);
+ }
+ }
+ return faceInfos;
+ }
+
+
+ /**
+ * 模型推理,获取人脸信息
+ * @param image 图像信息
+ * @param scoreTh 人脸人数阈值
+ * @param iouTh 人脸iou阈值
+ * @return 人脸模型
+ */
+ public List modelInference(ImageMat image, float scoreTh, float iouTh, Map params) {
OnnxTensor tensor = null;
OrtSession.Result output = null;
ImageMat imageMat = image.clone();
try {
float imgScale = 1.0f;
+ float boxScale = getBoxScale(params);
iouTh = iouTh <= 0 ? defIouTh : iouTh;
scoreTh = scoreTh <= 0 ? defScoreTh : scoreTh;
int imageWidth = imageMat.getWidth(), imageHeight = imageMat.getHeight();
@@ -68,7 +118,10 @@ public class InsightScrfdFaceDetection extends BaseOnnxInfer implements FaceDete
.blobFromImageAndDoReleaseMat(1.0/128, new Scalar(127.5, 127.5, 127.5), true)
.to4dFloatOnnxTensorAndDoReleaseMat(true);
output = getSession().run(Collections.singletonMap(getInputName(), tensor));
- return fitterBoxes(output, scoreTh, iouTh, tensor.getInfo().getShape()[3], imgScale);
+ //获取人脸信息
+ List faceInfos = fitterBoxes(output, scoreTh, iouTh, tensor.getInfo().getShape()[3], imgScale, boxScale);
+ //对人脸进行角度检查
+ return this.checkFaceAngle(faceInfos, this.getNeedCheckFaceAngle(params));
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
@@ -94,7 +147,7 @@ public class InsightScrfdFaceDetection extends BaseOnnxInfer implements FaceDete
* @return
* @throws OrtException
*/
- private List fitterBoxes(OrtSession.Result output, float scoreTh, float iouTh, long tensorWidth, float imgScale) throws OrtException {
+ private List fitterBoxes(OrtSession.Result output, float scoreTh, float iouTh, long tensorWidth, float imgScale, float boxScale) throws OrtException {
//分数过滤及计算正确的人脸框值
List faceInfos = new ArrayList<>();
for(int index=0; index< 3; index++) {
@@ -122,7 +175,7 @@ public class InsightScrfdFaceDetection extends BaseOnnxInfer implements FaceDete
float pointY = (point[2*pointIndex+1] * strides[index] + anchorY) * imgScale;
keyPoints.add(FaceInfo.Point.build(pointX, pointY));
}
- faceInfos.add(FaceInfo.build(scores[i][0], 0, FaceInfo.FaceBox.build(x1,y1,x2,y2), keyPoints));
+ faceInfos.add(FaceInfo.build(scores[i][0], 0, FaceInfo.FaceBox.build(x1,y1,x2,y2).scaling(boxScale), keyPoints));
}
}
}
@@ -147,4 +200,117 @@ public class InsightScrfdFaceDetection extends BaseOnnxInfer implements FaceDete
return faces;
}
+ /**
+ * 对人脸进行角度检测,这里通过5个关键点来确定当前人脸的角度
+ * @param faceInfos 人脸信息
+ * @param needCheckFaceAngle 是否启用检测
+ * @return
+ */
+ private List checkFaceAngle(List faceInfos, boolean needCheckFaceAngle){
+ if(!needCheckFaceAngle || null == faceInfos || faceInfos.isEmpty()){
+ return faceInfos;
+ }
+ for(FaceInfo faceInfo : faceInfos){
+ //计算当前人脸的角度数据
+ float ax1 = faceInfo.points.get(1).x;
+ float ay1 = faceInfo.points.get(1).y;
+ float ax2 = faceInfo.points.get(0).x;
+ float ay2 = faceInfo.points.get(0).y;
+ int atan = Double.valueOf(Math.atan2((ay2-ay1), (ax2-ax1)) / Math.PI * 180).intValue();
+ int angle = (180 - atan + 360) % 360;
+ int ki = (angle + 45) % 360 / 90;
+ int rotate = angle - (90 * ki); //
+ float scaling = 1 + Double.valueOf(Math.abs(Math.sin(Math.toRadians(rotate)))).floatValue() / 3;
+ faceInfo.angle = angle;
+ //重组坐标点, 旋转及缩放
+ if(ki == 0){
+ FaceInfo.Point leftTop = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y1());
+ FaceInfo.Point rightTop = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y1());
+ FaceInfo.Point rightBottom = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y2());
+ FaceInfo.Point leftBottom = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y2());
+ faceInfo.box = new FaceInfo.FaceBox(leftTop, rightTop, rightBottom, leftBottom);
+ faceInfo.box = faceInfo.box.rotate(rotate).scaling(scaling).rotate(-angle);
+ }else if(ki == 1){
+ FaceInfo.Point leftTop = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y2());
+ FaceInfo.Point rightTop = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y1());
+ FaceInfo.Point rightBottom = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y1());
+ FaceInfo.Point leftBottom = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y2());
+ faceInfo.box = new FaceInfo.FaceBox(leftTop, rightTop, rightBottom, leftBottom);
+ faceInfo.box = faceInfo.box.rotate(rotate).scaling(scaling).rotate(-angle);
+ }else if(ki == 2){
+ FaceInfo.Point leftTop = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y2());
+ FaceInfo.Point rightTop = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y2());
+ FaceInfo.Point rightBottom = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y1());
+ FaceInfo.Point leftBottom = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y1());
+ faceInfo.box = new FaceInfo.FaceBox(leftTop, rightTop, rightBottom, leftBottom);
+ faceInfo.box = faceInfo.box.rotate(rotate).scaling(scaling).rotate(-angle);
+ }else if(ki == 3){
+ FaceInfo.Point leftTop = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y1());
+ FaceInfo.Point rightTop = FaceInfo.Point.build(faceInfo.box.x2(),faceInfo.box.y2());
+ FaceInfo.Point rightBottom = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y2());
+ FaceInfo.Point leftBottom = FaceInfo.Point.build(faceInfo.box.x1(),faceInfo.box.y1());
+ faceInfo.box = new FaceInfo.FaceBox(leftTop, rightTop, rightBottom, leftBottom);
+ faceInfo.box = faceInfo.box.rotate(rotate).scaling(scaling).rotate(-angle);
+ }
+ }
+ return faceInfos;
+ }
+
+ /**人脸框的默认缩放比例**/
+ private float getBoxScale(Map params){
+ float boxScale = 0;
+ try {
+ if(null != params && params.containsKey(scrfdFaceboxScaleParamKey)){
+ Object value = params.get(scrfdFaceboxScaleParamKey);
+ if(null != value){
+ if (value instanceof Number){
+ boxScale = ((Number) value).floatValue();
+ }else{
+ boxScale = Float.parseFloat(value.toString());
+ }
+ }
+ }
+ }catch (Exception e){}
+ return boxScale > 0 ? boxScale : defBoxScale;
+ }
+
+ /**获取是否需要进行角度探测**/
+ private boolean getNeedCheckFaceAngle(Map params){
+ boolean needCheckFaceAngle = defNeedCheckFaceAngle;
+ try {
+ if(null != params && params.containsKey(scrfdFaceNeedCheckFaceAngleParamKey)){
+ Object value = params.get(scrfdFaceNeedCheckFaceAngleParamKey);
+ if(null != value){
+ if (value instanceof Boolean){
+ needCheckFaceAngle = (boolean) value;
+ }else{
+ needCheckFaceAngle = Boolean.parseBoolean(value.toString());
+ }
+ }
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return needCheckFaceAngle;
+ }
+
+ /**获取是否需要对没有检测到人脸的图像进行补边二次识别**/
+ private boolean getNoFaceImageNeedMakeBorder(Map params){
+ boolean noFaceImageNeedMakeBorder = defNoFaceImageNeedMakeBorder;
+ try {
+ if(null != params && params.containsKey(scrfdNoFaceImageNeedMakeBorderParamKey)){
+ Object value = params.get(scrfdNoFaceImageNeedMakeBorderParamKey);
+ if(null != value){
+ if (value instanceof Boolean){
+ noFaceImageNeedMakeBorder = (boolean) value;
+ }else{
+ noFaceImageNeedMakeBorder = Boolean.parseBoolean(value.toString());
+ }
+ }
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return noFaceImageNeedMakeBorder;
+ }
}
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/models/SeetaFaceOpenRecognition.java b/face-search-core/src/main/java/com/visual/face/search/core/models/SeetaFaceOpenRecognition.java
new file mode 100755
index 0000000..e3a59e2
--- /dev/null
+++ b/face-search-core/src/main/java/com/visual/face/search/core/models/SeetaFaceOpenRecognition.java
@@ -0,0 +1,59 @@
+package com.visual.face.search.core.models;
+
+import ai.onnxruntime.OnnxTensor;
+import ai.onnxruntime.OrtSession;
+import com.visual.face.search.core.base.BaseOnnxInfer;
+import com.visual.face.search.core.base.FaceRecognition;
+import com.visual.face.search.core.domain.FaceInfo.Embedding;
+import com.visual.face.search.core.domain.ImageMat;
+import com.visual.face.search.core.utils.ArrayUtil;
+import org.opencv.core.Scalar;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * 人脸识别-人脸特征提取
+ * git:https://github.com/SeetaFace6Open/index
+ */
+public class SeetaFaceOpenRecognition extends BaseOnnxInfer implements FaceRecognition {
+
+ /**
+ * 构造函数
+ * @param modelPath 模型路径
+ * @param threads 线程数
+ */
+ public SeetaFaceOpenRecognition(String modelPath, int threads) {
+ super(modelPath, threads);
+ }
+
+ /**
+ * 人脸识别,人脸特征向量
+ * @param image 图像信息
+ * @return
+ */
+ @Override
+ public Embedding inference(ImageMat image, Map params) {
+ OnnxTensor tensor = null;
+ OrtSession.Result output = null;
+ try {
+ tensor = image.resizeAndNoReleaseMat(112,112)
+ .blobFromImageAndDoReleaseMat(1.0/255, new Scalar(0, 0, 0), false)
+ .to4dFloatOnnxTensorAndDoReleaseMat(true);
+ output = getSession().run(Collections.singletonMap(getInputName(), tensor));
+ float[] embeds = ((float[][]) output.get(0).getValue())[0];
+ double normValue = ArrayUtil.matrixNorm(embeds);
+ float[] embedding = ArrayUtil.division(embeds, Double.valueOf(normValue).floatValue());
+ return Embedding.build(image.toBase64AndNoReleaseMat(), embedding);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }finally {
+ if(null != tensor){
+ tensor.close();
+ }
+ if(null != output){
+ output.close();
+ }
+ }
+ }
+
+}
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/models/SeetaMaskFaceKeyPoint.java b/face-search-core/src/main/java/com/visual/face/search/core/models/SeetaMaskFaceKeyPoint.java
new file mode 100755
index 0000000..e3983f4
--- /dev/null
+++ b/face-search-core/src/main/java/com/visual/face/search/core/models/SeetaMaskFaceKeyPoint.java
@@ -0,0 +1,94 @@
+package com.visual.face.search.core.models;
+
+import ai.onnxruntime.OnnxTensor;
+import ai.onnxruntime.OrtSession;
+import com.visual.face.search.core.base.BaseOnnxInfer;
+import com.visual.face.search.core.base.FaceMaskPoint;
+import com.visual.face.search.core.domain.ImageMat;
+import com.visual.face.search.core.domain.QualityInfo;
+import com.visual.face.search.core.utils.SoftMaxUtil;
+import org.opencv.core.*;
+import org.opencv.imgproc.Imgproc;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class SeetaMaskFaceKeyPoint extends BaseOnnxInfer implements FaceMaskPoint {
+
+ private static final int stride = 8;
+ private static final int shape = 128;
+
+ /**
+ * 构造函数
+ * @param modelPath 模型路径
+ * @param threads 线程数
+ */
+ public SeetaMaskFaceKeyPoint(String modelPath, int threads) {
+ super(modelPath, threads);
+ }
+
+ /**
+ * 人脸关键点检测
+ *
+ * @param imageMat 图像数据
+ * @param params 参数信息
+ * @return
+ */
+ @Override
+ public QualityInfo.MaskPoints inference(ImageMat imageMat, Map params) {
+ Mat borderMat = null;
+ Mat resizeMat = null;
+ OnnxTensor tensor = null;
+ OrtSession.Result output = null;
+ try {
+ Mat image = imageMat.toCvMat();
+ //将图片转换为正方形
+ int w = imageMat.getWidth();
+ int h = imageMat.getHeight();
+ int new_w = Math.max(h, w);
+ int new_h = Math.max(h, w);
+ if (Math.max(h, w) % stride != 0){
+ new_w = new_w + (stride - Math.max(h, w) % stride);
+ new_h = new_h + (stride - Math.max(h, w) % stride);
+ }
+ int ow = (new_w - w) / 2;
+ int oh = (new_h - h) / 2;
+ borderMat = new Mat();
+ Core.copyMakeBorder(image, borderMat, oh, oh, ow, ow, Core.BORDER_CONSTANT, new Scalar(114, 114, 114));
+ //对图片进行resize
+ float ratio = 1.0f * shape / new_h;
+ resizeMat = new Mat();
+ Imgproc.resize(borderMat, resizeMat, new Size(shape, shape));
+ //模型推理
+ tensor = ImageMat.fromCVMat(resizeMat)
+ .blobFromImageAndDoReleaseMat(1.0/32, new Scalar(104, 117, 123), false)
+ .to4dFloatOnnxTensorAndDoReleaseMat(true);
+ output = this.getSession().run(Collections.singletonMap(this.getInputName(), tensor));
+ float[] value = ((float[][]) output.get(0).getValue())[0];
+ //转换为标准的坐标点
+ QualityInfo.MaskPoints pointList = QualityInfo.MaskPoints.build();
+ for(int i=0; i<5; i++){
+ float x = value[i * 4 + 0] / ratio * 128 - ow;
+ float y = value[i * 4 + 1] / ratio * 128 - oh;
+ double[] softMax = SoftMaxUtil.softMax(new double[]{value[i * 4 + 2], value[i * 4 + 3]});
+ pointList.add(QualityInfo.MaskPoint.build(x, y, Double.valueOf(softMax[1]).floatValue()));
+ }
+ return pointList;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }finally {
+ if(null != tensor){
+ tensor.close();
+ }
+ if(null != output){
+ output.close();
+ }
+ if(null != borderMat){
+ borderMat.release();
+ }
+ if(null != resizeMat){
+ resizeMat.release();
+ }
+ }
+ }
+}
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/models/Simple005pFaceAlignment.java b/face-search-core/src/main/java/com/visual/face/search/core/models/Simple005pFaceAlignment.java
index 7cc6c5a..773f177 100755
--- a/face-search-core/src/main/java/com/visual/face/search/core/models/Simple005pFaceAlignment.java
+++ b/face-search-core/src/main/java/com/visual/face/search/core/models/Simple005pFaceAlignment.java
@@ -12,6 +12,8 @@ import java.util.Map;
* 五点对齐法
*/
public class Simple005pFaceAlignment implements FaceAlignment {
+ /**最小边的长度**/
+ private final static float minEdgeLength = 128;
/**对齐矩阵**/
private final static double[][] dst_points = new double[][]{
@@ -31,16 +33,33 @@ public class Simple005pFaceAlignment implements FaceAlignment {
*/
@Override
public ImageMat inference(ImageMat imageMat, FaceInfo.Points imagePoint, Map params) {
- double [][] image_points;
- if(imagePoint.size() == 5){
- image_points = imagePoint.toDoubleArray();
- }else if(imagePoint.size() == 106){
- image_points = imagePoint.select(38, 88, 80, 52, 61).toDoubleArray();
- }else{
- throw new RuntimeException("need 5 point, but get "+ imagePoint.size());
+ ImageMat alignmentImageMat = null;
+ try {
+ FaceInfo.Points alignmentPoints = imagePoint;
+ if(imageMat.getWidth() < minEdgeLength || imageMat.getHeight() < minEdgeLength){
+ float scale = minEdgeLength / Math.min(imageMat.getWidth(), imageMat.getHeight());
+ int newWidth = Float.valueOf(imageMat.getWidth() * scale).intValue();
+ int newHeight = Float.valueOf(imageMat.getHeight() * scale).intValue();
+ alignmentImageMat = imageMat.resizeAndNoReleaseMat(newWidth, newHeight);
+ alignmentPoints = imagePoint.operateMultiply(scale);
+ }else{
+ alignmentImageMat = imageMat.clone();
+ }
+ double [][] image_points;
+ if(alignmentPoints.size() == 5){
+ image_points = alignmentPoints.toDoubleArray();
+ }else if(alignmentPoints.size() == 106){
+ image_points = alignmentPoints.select(38, 88, 80, 52, 61).toDoubleArray();
+ }else{
+ throw new RuntimeException("need 5 point, but get "+ imagePoint.size());
+ }
+ Mat alignMat = AlignUtil.alignedImage(alignmentImageMat.toCvMat(), image_points, 112, 112, dst_points);
+ return ImageMat.fromCVMat(alignMat);
+ }finally {
+ if(null != alignmentImageMat){
+ alignmentImageMat.release();
+ }
}
- Mat alignMat = AlignUtil.alignedImage(imageMat.toCvMat(), image_points, 112, 112, dst_points);
- return ImageMat.fromCVMat(alignMat);
}
}
diff --git a/face-search-core/src/main/java/com/visual/face/search/core/models/Simple106pFaceAlignment.java b/face-search-core/src/main/java/com/visual/face/search/core/models/Simple106pFaceAlignment.java
index d5d557c..b0d5e01 100755
--- a/face-search-core/src/main/java/com/visual/face/search/core/models/Simple106pFaceAlignment.java
+++ b/face-search-core/src/main/java/com/visual/face/search/core/models/Simple106pFaceAlignment.java
@@ -10,6 +10,9 @@ import java.util.Map;
public class Simple106pFaceAlignment implements FaceAlignment {
+ /**最小边的长度**/
+ private final static float minEdgeLength = 128;
+
/**矫正的偏移**/
private final static double x_offset = 0;
private final static double y_offset = -8;
@@ -125,14 +128,31 @@ public class Simple106pFaceAlignment implements FaceAlignment {
@Override
public ImageMat inference(ImageMat imageMat, FaceInfo.Points imagePoint, Map params) {
- double [][] image_points;
- if(imagePoint.size() == 106){
- image_points = imagePoint.toDoubleArray();
- }else{
- throw new RuntimeException("need 106 point, but get "+ imagePoint.size());
+ ImageMat alignmentImageMat = null;
+ try {
+ FaceInfo.Points alignmentPoints = imagePoint;
+ if(imageMat.getWidth() < minEdgeLength || imageMat.getHeight() < minEdgeLength){
+ float scale = minEdgeLength / Math.min(imageMat.getWidth(), imageMat.getHeight());
+ int newWidth = Float.valueOf(imageMat.getWidth() * scale).intValue();
+ int newHeight = Float.valueOf(imageMat.getHeight() * scale).intValue();
+ alignmentImageMat = imageMat.resizeAndNoReleaseMat(newWidth, newHeight);
+ alignmentPoints = imagePoint.operateMultiply(scale);
+ }else{
+ alignmentImageMat = imageMat.clone();
+ }
+ double [][] image_points;
+ if(alignmentPoints.size() == 106){
+ image_points = alignmentPoints.toDoubleArray();
+ }else{
+ throw new RuntimeException("need 106 point, but get "+ alignmentPoints.size());
+ }
+ Mat alignMat = AlignUtil.alignedImage(alignmentImageMat.toCvMat(), image_points, 112, 112, dst_points);
+ return ImageMat.fromCVMat(alignMat);
+ }finally {
+ if(null != alignmentImageMat){
+ alignmentImageMat.release();
+ }
}
- Mat alignMat = AlignUtil.alignedImage(imageMat.toCvMat(), image_points, 112, 112, dst_points);
- return ImageMat.fromCVMat(alignMat);
}
}
diff --git a/face-search-core/src/main/resources/model/onnx/detection_face_scrfd/scrfd_500m_bnkps.onnx b/face-search-core/src/main/resources/model/onnx/detection_face_scrfd/scrfd_500m_bnkps.onnx
old mode 100755
new mode 100644
index 8594bd5..88b1496
Binary files a/face-search-core/src/main/resources/model/onnx/detection_face_scrfd/scrfd_500m_bnkps.onnx and b/face-search-core/src/main/resources/model/onnx/detection_face_scrfd/scrfd_500m_bnkps.onnx differ
diff --git a/face-search-core/src/main/resources/model/onnx/keypoint_seeta_mask/landmarker_005_mask_pts5.onnx b/face-search-core/src/main/resources/model/onnx/keypoint_seeta_mask/landmarker_005_mask_pts5.onnx
new file mode 100644
index 0000000..eeb184f
Binary files /dev/null and b/face-search-core/src/main/resources/model/onnx/keypoint_seeta_mask/landmarker_005_mask_pts5.onnx differ
diff --git a/face-search-core/src/main/resources/model/onnx/recognition_face_seeta/face_recognizer_512.onnx b/face-search-core/src/main/resources/model/onnx/recognition_face_seeta/face_recognizer_512.onnx
new file mode 100644
index 0000000..20c98f9
Binary files /dev/null and b/face-search-core/src/main/resources/model/onnx/recognition_face_seeta/face_recognizer_512.onnx differ
diff --git a/face-search-core/src/test/java/com/visual/face/search/core/test/extract/FaceCompareTest.java b/face-search-core/src/test/java/com/visual/face/search/core/test/extract/FaceCompareTest.java
new file mode 100755
index 0000000..9214ccf
--- /dev/null
+++ b/face-search-core/src/test/java/com/visual/face/search/core/test/extract/FaceCompareTest.java
@@ -0,0 +1,80 @@
+package com.visual.face.search.core.test.extract;
+
+import com.visual.face.search.core.base.*;
+import com.visual.face.search.core.domain.ExtParam;
+import com.visual.face.search.core.domain.FaceImage;
+import com.visual.face.search.core.domain.FaceInfo;
+import com.visual.face.search.core.domain.ImageMat;
+import com.visual.face.search.core.extract.FaceFeatureExtractor;
+import com.visual.face.search.core.extract.FaceFeatureExtractorImpl;
+import com.visual.face.search.core.models.*;
+import com.visual.face.search.core.test.base.BaseTest;
+import com.visual.face.search.core.utils.Similarity;
+import org.opencv.core.Mat;
+import org.opencv.imgcodecs.Imgcodecs;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class FaceCompareTest extends BaseTest {
+
+ private static String modelPcn1Path = "face-search-core/src/main/resources/model/onnx/detection_face_pcn/pcn1_sd.onnx";
+ private static String modelPcn2Path = "face-search-core/src/main/resources/model/onnx/detection_face_pcn/pcn2_sd.onnx";
+ private static String modelPcn3Path = "face-search-core/src/main/resources/model/onnx/detection_face_pcn/pcn3_sd.onnx";
+ private static String modelScrfdPath = "face-search-core/src/main/resources/model/onnx/detection_face_scrfd/scrfd_500m_bnkps.onnx";
+ private static String modelCoordPath = "face-search-core/src/main/resources/model/onnx/keypoint_coordinate/coordinate_106_mobilenet_05.onnx";
+ private static String modelArcPath = "face-search-core/src/main/resources/model/onnx/recognition_face_arc/glint360k_cosface_r18_fp16_0.1.onnx";
+ private static String modelSeetaPath = "face-search-core/src/main/resources/model/onnx/recognition_face_seeta/face_recognizer_512.onnx";
+ private static String modelArrPath = "face-search-core/src/main/resources/model/onnx/attribute_gender_age/insight_gender_age.onnx";
+
+ private static String imagePath = "face-search-test/src/main/resources/image/validate/index/马化腾/";
+ private static String imagePath3 = "face-search-test/src/main/resources/image/validate/index/雷军/";
+// private static String imagePath1 = "face-search-core/src/test/resources/images/faces/debug/debug_0001.jpg";
+// private static String imagePath2 = "face-search-core/src/test/resources/images/faces/debug/debug_0001.jpg";
+// private static String imagePath1 = "face-search-core/src/test/resources/images/faces/compare/1682052661610.jpg";
+// private static String imagePath2 = "face-search-core/src/test/resources/images/faces/compare/1682052669004.jpg";
+// private static String imagePath2 = "face-search-core/src/test/resources/images/faces/compare/1682053163961.jpg";
+// private static String imagePath1 = "face-search-test/src/main/resources/image/validate/index/张一鸣/1c7abcaf2dabdd2bc08e90c224d4c381.jpeg";
+ private static String imagePath1 = "face-search-core/src/test/resources/images/faces/small/1.png";
+ private static String imagePath2 = "face-search-core/src/test/resources/images/faces/small/2.png";
+ public static void main(String[] args) {
+ //口罩模型0.48,light模型0.52,normal模型0.62
+ Map map1 = getImagePathMap(imagePath1);
+ Map map2 = getImagePathMap(imagePath2);
+ FaceDetection insightScrfdFaceDetection = new InsightScrfdFaceDetection(modelScrfdPath, 1);
+ FaceKeyPoint insightCoordFaceKeyPoint = new InsightCoordFaceKeyPoint(modelCoordPath, 1);
+ FaceRecognition insightArcFaceRecognition = new InsightArcFaceRecognition(modelArcPath, 1);
+ FaceRecognition insightSeetaFaceRecognition = new SeetaFaceOpenRecognition(modelSeetaPath, 1);
+ FaceAlignment simple005pFaceAlignment = new Simple005pFaceAlignment();
+ FaceAlignment simple106pFaceAlignment = new Simple106pFaceAlignment();
+ FaceDetection pcnNetworkFaceDetection = new PcnNetworkFaceDetection(new String[]{modelPcn1Path, modelPcn2Path, modelPcn3Path}, 1);
+ FaceAttribute insightFaceAttribute = new InsightAttributeDetection(modelArrPath, 1);
+
+ FaceFeatureExtractor extractor = new FaceFeatureExtractorImpl(
+ insightScrfdFaceDetection, pcnNetworkFaceDetection, insightCoordFaceKeyPoint,
+ simple005pFaceAlignment, insightSeetaFaceRecognition, insightFaceAttribute);
+
+ for(String file1 : map1.keySet()){
+ for(String file2 : map2.keySet()){
+ Mat image1 = Imgcodecs.imread(map1.get(file1));
+ long s = System.currentTimeMillis();
+ ExtParam extParam = ExtParam.build().setMask(false).setTopK(20).setScoreTh(0).setIouTh(0);
+ FaceImage faceImage1 = extractor.extract(ImageMat.fromCVMat(image1), extParam, null);
+ List faceInfos1 = faceImage1.faceInfos();
+ long e = System.currentTimeMillis();
+ System.out.println("image1 extract cost:"+(e-s)+"ms");;
+
+ Mat image2 = Imgcodecs.imread(map2.get(file2));
+ s = System.currentTimeMillis();
+ FaceImage faceImage2 = extractor.extract(ImageMat.fromCVMat(image2), extParam, null);
+ List faceInfos2 = faceImage2.faceInfos();
+ e = System.currentTimeMillis();
+ System.out.println("image2 extract cost:"+(e-s)+"ms");
+ float similarity = Similarity.cosineSimilarityNorm(faceInfos1.get(0).embedding.embeds, faceInfos2.get(0).embedding.embeds);
+ System.out.println(file1 + ","+ file2 + ",face similarity="+similarity);
+ }
+ }
+
+ }
+}
diff --git a/face-search-core/src/test/java/com/visual/face/search/core/test/extract/FaceFeatureExtractTest.java b/face-search-core/src/test/java/com/visual/face/search/core/test/extract/FaceFeatureExtractTest.java
index 59b6f70..bdff7b4 100755
--- a/face-search-core/src/test/java/com/visual/face/search/core/test/extract/FaceFeatureExtractTest.java
+++ b/face-search-core/src/test/java/com/visual/face/search/core/test/extract/FaceFeatureExtractTest.java
@@ -1,5 +1,6 @@
package com.visual.face.search.core.test.extract;
+import com.alibaba.fastjson.JSONObject;
import com.visual.face.search.core.base.*;
import com.visual.face.search.core.domain.ExtParam;
import com.visual.face.search.core.domain.FaceImage;
@@ -29,8 +30,9 @@ public class FaceFeatureExtractTest extends BaseTest {
private static String modelArcPath = "face-search-core/src/main/resources/model/onnx/recognition_face_arc/glint360k_cosface_r18_fp16_0.1.onnx";
private static String modelArrPath = "face-search-core/src/main/resources/model/onnx/attribute_gender_age/insight_gender_age.onnx";
-// private static String imagePath = "face-search-core/src/test/resources/images/faces";
- private static String imagePath = "face-search-core/src/test/resources/images/faces/debug/debug_0001.jpg";
+ private static String imagePath = "face-search-core/src/test/resources/images/faces";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces/debug/debug_0001.jpg";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces/rotate/rotate_0002.jpg";
public static void main(String[] args) {
@@ -44,7 +46,7 @@ public class FaceFeatureExtractTest extends BaseTest {
FaceAttribute insightFaceAttribute = new InsightAttributeDetection(modelArrPath, 1);
FaceFeatureExtractor extractor = new FaceFeatureExtractorImpl(
- pcnNetworkFaceDetection, insightScrfdFaceDetection, insightCoordFaceKeyPoint,
+ insightScrfdFaceDetection, pcnNetworkFaceDetection, insightCoordFaceKeyPoint,
simple005pFaceAlignment, insightArcFaceRecognition, insightFaceAttribute);
for(String fileName : map.keySet()){
String imageFilePath = map.get(fileName);
@@ -56,7 +58,8 @@ public class FaceFeatureExtractTest extends BaseTest {
.setTopK(20)
.setScoreTh(0)
.setIouTh(0);
- FaceImage faceImage = extractor.extract(ImageMat.fromCVMat(image), extParam, null);
+ Map params = new JSONObject().fluentPut(InsightScrfdFaceDetection.scrfdFaceNeedCheckFaceAngleParamKey, true);
+ FaceImage faceImage = extractor.extract(ImageMat.fromCVMat(image), extParam, params);
List faceInfos = faceImage.faceInfos();
long e = System.currentTimeMillis();
System.out.println("fileName="+fileName+",\tcost="+(e-s)+",\t"+faceInfos);
@@ -67,7 +70,7 @@ public class FaceFeatureExtractTest extends BaseTest {
Imgproc.line(image, new Point(box.rightTop.x, box.rightTop.y), new Point(box.rightBottom.x, box.rightBottom.y), new Scalar(255,0,0), 1);
Imgproc.line(image, new Point(box.rightBottom.x, box.rightBottom.y), new Point(box.leftBottom.x, box.leftBottom.y), new Scalar(255,0,0), 1);
Imgproc.line(image, new Point(box.leftBottom.x, box.leftBottom.y), new Point(box.leftTop.x, box.leftTop.y), new Scalar(255,0,0), 1);
- Imgproc.putText(image, String.valueOf(faceInfo.angle), new Point(box.leftTop.x, box.leftTop.y), Imgproc.FONT_HERSHEY_PLAIN, 1, new Scalar(0,0,255));
+ Imgproc.putText(image, String.valueOf(faceInfo.angle), new Point(box.leftTop.x, box.leftTop.y+15), Imgproc.FONT_HERSHEY_PLAIN, 1, new Scalar(0,0,255));
// Imgproc.rectangle(image, new Point(faceInfo.box.x1(), faceInfo.box.y1()), new Point(faceInfo.box.x2(), faceInfo.box.y2()), new Scalar(255,0,255));
FaceInfo.FaceBox box1 = faceInfo.rotateFaceBox();
diff --git a/face-search-core/src/test/java/com/visual/face/search/core/test/models/InsightScrfdFaceDetectionTest.java b/face-search-core/src/test/java/com/visual/face/search/core/test/models/InsightScrfdFaceDetectionTest.java
index 60afa42..18bbbd5 100755
--- a/face-search-core/src/test/java/com/visual/face/search/core/test/models/InsightScrfdFaceDetectionTest.java
+++ b/face-search-core/src/test/java/com/visual/face/search/core/test/models/InsightScrfdFaceDetectionTest.java
@@ -1,5 +1,6 @@
package com.visual.face.search.core.test.models;
+import com.alibaba.fastjson.JSONObject;
import com.visual.face.search.core.domain.FaceInfo;
import com.visual.face.search.core.domain.ImageMat;
import com.visual.face.search.core.models.InsightScrfdFaceDetection;
@@ -11,15 +12,17 @@ import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class InsightScrfdFaceDetectionTest extends BaseTest {
private static String modelPath = "face-search-core/src/main/resources/model/onnx/detection_face_scrfd/scrfd_500m_bnkps.onnx";
- private static String imagePath = "face-search-core/src/test/resources/images/faces";
-// private static String imagePath = "face-search-core/src/test/resources/images/faces/rotate";
-// private static String imagePath = "face-search-core/src/test/resources/images/faces/debug";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces/rotate/rotate_0001.jpg";
+ private static String imagePath = "face-search-core/src/test/resources/images/faces/rotate";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces/big/big_002.jpg";
public static void main(String[] args) {
@@ -31,7 +34,9 @@ public class InsightScrfdFaceDetectionTest extends BaseTest {
System.out.println(imageFilePath);
Mat image = Imgcodecs.imread(imageFilePath);
long s = System.currentTimeMillis();
- List faceInfos = infer.inference(ImageMat.fromCVMat(image), 0.5f, 0.7f, null);
+ Map params = new JSONObject().fluentPut(InsightScrfdFaceDetection.scrfdFaceNeedCheckFaceAngleParamKey, true);
+
+ List faceInfos = infer.inference(ImageMat.fromCVMat(image), 0.48f, 0.7f, params);
long e = System.currentTimeMillis();
if(faceInfos.size() > 0){
System.out.println("fileName="+fileName+",\tcost="+(e-s)+",\t"+faceInfos.get(0).score);
@@ -39,10 +44,20 @@ public class InsightScrfdFaceDetectionTest extends BaseTest {
System.out.println("fileName="+fileName+",\tcost="+(e-s)+",\t"+faceInfos);
}
+ //对坐标进行调整
for(FaceInfo faceInfo : faceInfos){
- Imgproc.rectangle(image, new Point(faceInfo.box.x1(), faceInfo.box.y1()), new Point(faceInfo.box.x2(), faceInfo.box.y2()), new Scalar(0,0,255));
+ FaceInfo.FaceBox box = faceInfo.rotateFaceBox();
+ Imgproc.circle(image, new Point(box.leftTop.x, box.leftTop.y), 3, new Scalar(0,0,255), -1);
+ Imgproc.circle(image, new Point(box.rightBottom.x, box.rightBottom.y), 3, new Scalar(0,0,255), -1);
+ Imgproc.line(image, new Point(box.leftTop.x, box.leftTop.y), new Point(box.rightTop.x, box.rightTop.y), new Scalar(0,0,255), 1);
+ Imgproc.line(image, new Point(box.rightTop.x, box.rightTop.y), new Point(box.rightBottom.x, box.rightBottom.y), new Scalar(255,0,0), 1);
+ Imgproc.line(image, new Point(box.rightBottom.x, box.rightBottom.y), new Point(box.leftBottom.x, box.leftBottom.y), new Scalar(255,0,0), 1);
+ Imgproc.line(image, new Point(box.leftBottom.x, box.leftBottom.y), new Point(box.leftTop.x, box.leftTop.y), new Scalar(255,0,0), 1);
+ Imgproc.putText(image, String.valueOf(faceInfo.angle), new Point(box.leftTop.x, box.leftTop.y), Imgproc.FONT_HERSHEY_PLAIN, 1, new Scalar(0,0,255));
+
+ FaceInfo.Points points = faceInfo.points;
int pointNum = 1;
- for(FaceInfo.Point keyPoint : faceInfo.points){
+ for(FaceInfo.Point keyPoint : points){
Imgproc.circle(image, new Point(keyPoint.x, keyPoint.y), 3, new Scalar(0,0,255), -1);
Imgproc.putText(image, String.valueOf(pointNum), new Point(keyPoint.x+1, keyPoint.y), Imgproc.FONT_HERSHEY_PLAIN, 1, new Scalar(255,0,0));
pointNum ++ ;
diff --git a/face-search-core/src/test/java/com/visual/face/search/core/test/models/PcnNetworkFaceDetectionTest.java b/face-search-core/src/test/java/com/visual/face/search/core/test/models/PcnNetworkFaceDetectionTest.java
index 43bbc6e..c99b172 100755
--- a/face-search-core/src/test/java/com/visual/face/search/core/test/models/PcnNetworkFaceDetectionTest.java
+++ b/face-search-core/src/test/java/com/visual/face/search/core/test/models/PcnNetworkFaceDetectionTest.java
@@ -21,7 +21,9 @@ public class PcnNetworkFaceDetectionTest extends BaseTest {
private static String model2Path = "face-search-core/src/main/resources/model/onnx/detection_face_pcn/pcn2_sd.onnx";
private static String model3Path = "face-search-core/src/main/resources/model/onnx/detection_face_pcn/pcn3_sd.onnx";
- private static String imagePath = "face-search-core/src/test/resources/images/faces";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces";
+ private static String imagePath = "face-search-core/src/test/resources/images/faces/rotate/rotate_0001.jpg";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces/big/big_002.jpg";
// private static String imagePath = "face-search-core/src/test/resources/images/faces/rotate";
// private static String imagePath = "face-search-core/src/test/resources/images/faces/debug";
diff --git a/face-search-core/src/test/java/com/visual/face/search/core/test/models/SeetaFaceOpenRecognitionTest.java b/face-search-core/src/test/java/com/visual/face/search/core/test/models/SeetaFaceOpenRecognitionTest.java
new file mode 100644
index 0000000..9298e52
--- /dev/null
+++ b/face-search-core/src/test/java/com/visual/face/search/core/test/models/SeetaFaceOpenRecognitionTest.java
@@ -0,0 +1,53 @@
+package com.visual.face.search.core.test.models;
+
+import com.visual.face.search.core.base.FaceAlignment;
+import com.visual.face.search.core.base.FaceKeyPoint;
+import com.visual.face.search.core.base.FaceRecognition;
+import com.visual.face.search.core.domain.FaceInfo;
+import com.visual.face.search.core.domain.ImageMat;
+import com.visual.face.search.core.models.InsightCoordFaceKeyPoint;
+import com.visual.face.search.core.models.SeetaFaceOpenRecognition;
+import com.visual.face.search.core.models.Simple005pFaceAlignment;
+import com.visual.face.search.core.test.base.BaseTest;
+import com.visual.face.search.core.utils.CropUtil;
+import com.visual.face.search.core.utils.Similarity;
+import org.opencv.core.Mat;
+import org.opencv.imgcodecs.Imgcodecs;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SeetaFaceOpenRecognitionTest extends BaseTest {
+ private static String modelCoordPath = "face-search-core/src/main/resources/model/onnx/keypoint_coordinate/coordinate_106_mobilenet_05.onnx";
+ private static String modelSeetaPath = "face-search-core/src/main/resources/model/onnx/recognition_fcae_seeta/face_recognizer_512.onnx";
+// private static String modelSeetaPath = "face-search-core/src/main/resources/model/onnx/recognition_fcae_seeta/face_recognizer_1024.onnx";
+
+ private static String imagePath = "face-search-core/src/test/resources/images/faces";
+// private static String imagePath1 = "face-search-core/src/test/resources/images/faces/debug/debug_0001.jpg";
+// private static String imagePath2 = "face-search-core/src/test/resources/images/faces/debug/debug_0004.jpeg";
+ private static String imagePath1 = "face-search-core/src/test/resources/images/faces/compare/1682052661610.jpg";
+ private static String imagePath2 = "face-search-core/src/test/resources/images/faces/compare/1682052669004.jpg";
+// private static String imagePath2 = "face-search-core/src/test/resources/images/faces/compare/1682053163961.jpg";
+
+ public static void main(String[] args) {
+ FaceAlignment simple005pFaceAlignment = new Simple005pFaceAlignment();
+ FaceKeyPoint insightCoordFaceKeyPoint = new InsightCoordFaceKeyPoint(modelCoordPath, 1);
+ FaceRecognition insightSeetaFaceRecognition = new SeetaFaceOpenRecognition(modelSeetaPath, 1);
+
+ Mat image1 = Imgcodecs.imread(imagePath1);
+ Mat image2 = Imgcodecs.imread(imagePath2);
+// image1 = CropUtil.crop(image1, FaceInfo.FaceBox.build(54,27,310,380));
+// image2 = CropUtil.crop(image2, FaceInfo.FaceBox.build(48,13,292,333));
+// image2 = CropUtil.crop(image2, FaceInfo.FaceBox.build(52,9,235,263));
+
+// simple005pFaceAlignment.inference()
+
+ FaceInfo.Embedding embedding1 = insightSeetaFaceRecognition.inference(ImageMat.fromCVMat(image1), null);
+ FaceInfo.Embedding embedding2 = insightSeetaFaceRecognition.inference(ImageMat.fromCVMat(image2), null);
+ float similarity = Similarity.cosineSimilarity(embedding1.embeds, embedding2.embeds);
+ System.out.println(similarity);
+// System.out.println(Arrays.toString(embedding1.embeds));
+// System.out.println(Arrays.toString(embedding2.embeds));
+ }
+}
diff --git a/face-search-core/src/test/java/com/visual/face/search/core/test/models/SeetaMaskFaceKeyPointTest.java b/face-search-core/src/test/java/com/visual/face/search/core/test/models/SeetaMaskFaceKeyPointTest.java
new file mode 100644
index 0000000..364f2ad
--- /dev/null
+++ b/face-search-core/src/test/java/com/visual/face/search/core/test/models/SeetaMaskFaceKeyPointTest.java
@@ -0,0 +1,57 @@
+package com.visual.face.search.core.test.models;
+
+import com.visual.face.search.core.domain.FaceInfo;
+import com.visual.face.search.core.domain.ImageMat;
+import com.visual.face.search.core.domain.QualityInfo;
+import com.visual.face.search.core.models.InsightCoordFaceKeyPoint;
+import com.visual.face.search.core.models.InsightScrfdFaceDetection;
+import com.visual.face.search.core.models.SeetaMaskFaceKeyPoint;
+import com.visual.face.search.core.test.base.BaseTest;
+import com.visual.face.search.core.utils.CropUtil;
+import org.opencv.core.Mat;
+import org.opencv.core.Point;
+import org.opencv.core.Scalar;
+import org.opencv.highgui.HighGui;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.opencv.imgproc.Imgproc;
+
+import java.util.List;
+import java.util.Map;
+
+public class SeetaMaskFaceKeyPointTest extends BaseTest {
+ private static String modelDetectionPath = "face-search-core/src/main/resources/model/onnx/detection_face_scrfd/scrfd_500m_bnkps.onnx";
+ private static String modelKeypointPath = "face-search-core/src/main/resources/model/onnx/keypoint_seeta_mask/landmarker_005_mask_pts5.onnx";
+ private static String imagePath = "face-search-core/src/test/resources/images/faces";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces/compare";
+// private static String imagePath = "face-search-core/src/test/resources/images/faces/compare/1694353163955.jpg";
+
+ public static void main(String[] args) {
+ Map map = getImagePathMap(imagePath);
+ InsightScrfdFaceDetection detectionInfer = new InsightScrfdFaceDetection(modelDetectionPath, 1);
+ SeetaMaskFaceKeyPoint keyPointInfer = new SeetaMaskFaceKeyPoint(modelKeypointPath, 1);
+ for(String fileName : map.keySet()) {
+ System.out.println(fileName);
+ String imageFilePath = map.get(fileName);
+ Mat image = Imgcodecs.imread(imageFilePath);
+ List faceInfos = detectionInfer.inference(ImageMat.fromCVMat(image), 0.5f, 0.7f, null);
+ for(FaceInfo faceInfo : faceInfos){
+ FaceInfo.FaceBox rotateFaceBox = faceInfo.rotateFaceBox();
+ Mat cropFace = CropUtil.crop(image, rotateFaceBox.scaling(1.0f));
+ ImageMat cropImageMat = ImageMat.fromCVMat(cropFace);
+ QualityInfo.MaskPoints maskPoints = keyPointInfer.inference(cropImageMat, null);
+ System.out.println(maskPoints);
+ for(QualityInfo.MaskPoint maskPoint : maskPoints){
+ if(maskPoint.isMask()){
+ Imgproc.circle(cropFace, new Point(maskPoint.x, maskPoint.y), 3, new Scalar(0, 0, 255), -1);
+ }else{
+ Imgproc.circle(cropFace, new Point(maskPoint.x, maskPoint.y), 3, new Scalar(255, 0, 0), -1);
+ }
+ }
+ HighGui.imshow(fileName, cropFace);
+ HighGui.waitKey();
+ }
+ }
+ System.exit(1);
+ }
+
+}
diff --git a/face-search-core/src/test/resources/images/faces/big/big_001.jpg b/face-search-core/src/test/resources/images/faces/big/big_001.jpg
new file mode 100644
index 0000000..c5d81b5
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/big/big_001.jpg differ
diff --git a/face-search-core/src/test/resources/images/faces/big/big_002.jpg b/face-search-core/src/test/resources/images/faces/big/big_002.jpg
new file mode 100644
index 0000000..3d13d03
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/big/big_002.jpg differ
diff --git a/face-search-core/src/test/resources/images/faces/compare/1682052661610.jpg b/face-search-core/src/test/resources/images/faces/compare/1682052661610.jpg
new file mode 100644
index 0000000..e487826
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/compare/1682052661610.jpg differ
diff --git a/face-search-core/src/test/resources/images/faces/compare/1682052669004.jpg b/face-search-core/src/test/resources/images/faces/compare/1682052669004.jpg
new file mode 100644
index 0000000..4d4ffd7
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/compare/1682052669004.jpg differ
diff --git a/face-search-core/src/test/resources/images/faces/compare/1682053163961.jpg b/face-search-core/src/test/resources/images/faces/compare/1682053163961.jpg
new file mode 100644
index 0000000..380155e
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/compare/1682053163961.jpg differ
diff --git a/face-search-core/src/test/resources/images/faces/compare/1694353163955.jpg b/face-search-core/src/test/resources/images/faces/compare/1694353163955.jpg
new file mode 100644
index 0000000..525a4da
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/compare/1694353163955.jpg differ
diff --git a/face-search-core/src/test/resources/images/faces/rotate/rotate_0003.jpg b/face-search-core/src/test/resources/images/faces/rotate/rotate_0003.jpg
new file mode 100644
index 0000000..9f943dc
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/rotate/rotate_0003.jpg differ
diff --git a/face-search-core/src/test/resources/images/faces/small/1.png b/face-search-core/src/test/resources/images/faces/small/1.png
new file mode 100644
index 0000000..7e1fa08
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/small/1.png differ
diff --git a/face-search-core/src/test/resources/images/faces/small/2.png b/face-search-core/src/test/resources/images/faces/small/2.png
new file mode 100644
index 0000000..738c566
Binary files /dev/null and b/face-search-core/src/test/resources/images/faces/small/2.png differ
diff --git a/face-search-engine/pom.xml b/face-search-engine/pom.xml
index f3b4706..eb87e8a 100644
--- a/face-search-engine/pom.xml
+++ b/face-search-engine/pom.xml
@@ -5,7 +5,7 @@
face-search
com.visual.face.search
- 2.0.1
+ 2.1.0
4.0.0
diff --git a/face-search-server/pom.xml b/face-search-server/pom.xml
index 332edd0..073be62 100644
--- a/face-search-server/pom.xml
+++ b/face-search-server/pom.xml
@@ -5,7 +5,7 @@
face-search
com.visual.face.search
- 2.0.1
+ 2.1.0
4.0.0
diff --git a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/Knife4jConfig.java b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/Knife4jConfig.java
index 89a3ee6..ffc3e1e 100644
--- a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/Knife4jConfig.java
+++ b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/Knife4jConfig.java
@@ -26,9 +26,9 @@ public class Knife4jConfig {
.apiInfo(new ApiInfoBuilder()
.title("人脸搜索服务API")
.description("人脸搜索服务API")
- .version("2.0.0")
+ .version("2.1.0")
.build())
- .groupName("2.0.0")
+ .groupName("2.1.0")
.select()
.apis(RequestHandlerSelectors.basePackage("com.visual.face.search.server.controller.server"))
.paths(PathSelectors.any())
diff --git a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/ModelConfig.java b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/ModelConfig.java
index 32f63e9..bdf8951 100755
--- a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/ModelConfig.java
+++ b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/ModelConfig.java
@@ -4,6 +4,7 @@ import com.visual.face.search.core.base.*;
import com.visual.face.search.core.extract.FaceFeatureExtractor;
import com.visual.face.search.core.extract.FaceFeatureExtractorImpl;
import com.visual.face.search.core.models.*;
+import com.visual.face.search.server.utils.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -12,8 +13,8 @@ import org.springframework.context.annotation.Configuration;
@Configuration("visualModelConfig")
public class ModelConfig {
- @Value("${spring.profiles.active}")
- private String profile;
+ @Value("${visual.model.baseModelPath}")
+ private String baseModelPath;
@Value("${visual.model.faceDetection.name}")
private String faceDetectionName;
@@ -121,6 +122,8 @@ public class ModelConfig {
public FaceRecognition getFaceRecognition(){
if(faceRecognitionName.equalsIgnoreCase("InsightArcFaceRecognition")){
return new InsightArcFaceRecognition(getModelPath(faceRecognitionName, faceRecognitionNameModel)[0], faceRecognitionNameThread);
+ }else if(faceRecognitionName.equalsIgnoreCase("SeetaFaceOpenRecognition")){
+ return new SeetaFaceOpenRecognition(getModelPath(faceRecognitionName, faceRecognitionNameModel)[0], faceRecognitionNameThread);
}else{
return new InsightArcFaceRecognition(getModelPath(faceRecognitionName, faceRecognitionNameModel)[0], faceRecognitionNameThread);
}
@@ -174,10 +177,9 @@ public class ModelConfig {
* @return
*/
private String[] getModelPath(String modelName, String modelPath[]){
-
String basePath = "face-search-core/src/main/resources/";
- if("docker".equalsIgnoreCase(profile)){
- basePath = "/app/face-search/";
+ if(StringUtils.isNotEmpty(this.baseModelPath)){
+ basePath = this.baseModelPath.endsWith("/") ? this.baseModelPath : this.baseModelPath +"/";
}
if((null == modelPath || modelPath.length != 3) && "PcnNetworkFaceDetection".equalsIgnoreCase(modelName)){
@@ -200,10 +202,18 @@ public class ModelConfig {
return new String[]{basePath + "model/onnx/recognition_face_arc/glint360k_cosface_r18_fp16_0.1.onnx"};
}
+ if((null == modelPath || modelPath.length != 1) && "SeetaFaceOpenRecognition".equalsIgnoreCase(modelName)){
+ return new String[]{basePath + "model/onnx/recognition_face_seeta/face_recognizer_512.onnx"};
+ }
+
if((null == modelPath || modelPath.length != 1) && "InsightAttributeDetection".equalsIgnoreCase(modelName)){
return new String[]{basePath + "model/onnx/attribute_gender_age/insight_gender_age.onnx"};
}
+ if((null == modelPath || modelPath.length != 1) && "SeetaMaskFaceKeyPoint".equalsIgnoreCase(modelName)){
+ return new String[]{basePath + "model/onnx/keypoint_seeta_mask/landmarker_005_mask_pts5.onnx"};
+ }
+
return modelPath;
}
}
diff --git a/face-search-server/src/main/resources/application-dev.yml b/face-search-server/src/main/resources/application-dev.yml
index ffbff35..8988d75 100755
--- a/face-search-server/src/main/resources/application-dev.yml
+++ b/face-search-server/src/main/resources/application-dev.yml
@@ -22,6 +22,7 @@ logging:
# 模型配置
visual:
model:
+ baseModelPath: 'face-search-core/src/main/resources/'
faceDetection:
name: InsightScrfdFaceDetection
modelPath:
@@ -83,8 +84,8 @@ spring:
# 主库数据源
master:
url: jdbc:mysql://visual-face-search-mysql:3306/visual_face_search?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
- username: visual
- password: visual
+ username: root
+ password: root
slave:
# 从数据源开关/默认关闭
enabled: false
diff --git a/face-search-server/src/main/resources/application-docker.yml b/face-search-server/src/main/resources/application-docker.yml
index 1f5380a..466d23a 100755
--- a/face-search-server/src/main/resources/application-docker.yml
+++ b/face-search-server/src/main/resources/application-docker.yml
@@ -22,6 +22,7 @@ logging:
# 模型配置
visual:
model:
+ baseModelPath: ${VISUAL_MODEL_BASE_MODEL_PATH:'/app/face-search/'}
faceDetection:
name: ${VISUAL_MODEL_FACEDETECTION_NAME:InsightScrfdFaceDetection}
modelPath: ${VISUAL_MODEL_FACEDETECTION_PATH:}
diff --git a/face-search-server/src/main/resources/application-local.yml b/face-search-server/src/main/resources/application-local.yml
index e450a4e..17e3790 100755
--- a/face-search-server/src/main/resources/application-local.yml
+++ b/face-search-server/src/main/resources/application-local.yml
@@ -22,6 +22,7 @@ logging:
# 模型配置
visual:
model:
+ baseModelPath: 'face-search-core/src/main/resources/'
faceDetection:
name: InsightScrfdFaceDetection
modelPath:
@@ -48,7 +49,7 @@ visual:
thread: 1
engine:
open-search:
- host: 172.16.36.229
+ host: visual-face-search-opensearch
port: 9200
scheme: https
username: admin
@@ -82,9 +83,9 @@ spring:
druid:
# 主库数据源
master:
- url: jdbc:mysql://172.16.36.228:3306/visual_face_search?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
- username: visual
- password: visual
+ url: jdbc:mysql://visual-face-search-mysql:3306/visual_face_search?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
+ username: root
+ password: root
slave:
# 从数据源开关/默认关闭
enabled: false
diff --git a/face-search-test/pom.xml b/face-search-test/pom.xml
index ef9e582..7c1389d 100644
--- a/face-search-test/pom.xml
+++ b/face-search-test/pom.xml
@@ -6,7 +6,7 @@
com.visual.face.search
face-search-test
- 2.0.1
+ 2.1.0
1.8
@@ -24,9 +24,28 @@
org.openpnp
opencv
- 4.6.0-0
+ 4.7.0-0
+
+
+ public
+ aliyun nexus
+ https://maven.aliyun.com/repository/public
+
+ true
+
+
+
+ central
+ aliyun nexus
+ https://maven.aliyun.com/repository/central
+
+ true
+
+
+
+
\ No newline at end of file
diff --git a/face-search-test/src/main/java/com/visual/face/search/valid/exps/FaceSearchExample.java b/face-search-test/src/main/java/com/visual/face/search/valid/exps/FaceSearchExample.java
index 7a123cf..1c6b5ec 100644
--- a/face-search-test/src/main/java/com/visual/face/search/valid/exps/FaceSearchExample.java
+++ b/face-search-test/src/main/java/com/visual/face/search/valid/exps/FaceSearchExample.java
@@ -19,9 +19,9 @@ public class FaceSearchExample {
//本地开发模式
public static String serverHost = "http://127.0.0.1:8080";
//docker部署模式
- //public static String serverHost = "http://127.0.0.1:56789";
+// public static String serverHost = "http://172.16.24.124:56789";
//远程测试服务
- //public static String serverHost = "http://face-search.diven.nat300.top";
+// public static String serverHost = "http://face-search.divenswu.com";
public static String namespace = "namespace_1";
public static String collectionName = "collect_20211201_v11";
public static FaceSearch faceSearch = FaceSearch.build(serverHost, namespace, collectionName);
@@ -35,7 +35,16 @@ public class FaceSearchExample {
List faceColumns = new ArrayList<>();
faceColumns.add(FiledColumn.build().setName("label").setDataType(FiledDataType.STRING).setComment("标签1"));
//待创建的人脸库信息
- Collect collect = Collect.build().setCollectionComment("人脸库").setSampleColumns(sampleColumns).setFaceColumns(faceColumns);
+ Collect collect = Collect.build()
+ .setCollectionComment("人脸库")
+ //样本属性字段
+ .setSampleColumns(sampleColumns)
+ //人脸属性字段
+ .setFaceColumns(faceColumns)
+ //是否保存人脸及图片数据信息
+ .setStorageFaceInfo(true)
+ //目前只实现了数据库存储,对其他类型存储实现StorageImageService接口即可
+ .setStorageEngine(StorageEngine.CURR_DB);
//删除集合
Response deleteCollect = faceSearch.collect().deleteCollect();
System.out.println(deleteCollect);
@@ -66,9 +75,10 @@ public class FaceSearchExample {
KeyValues faceData = KeyValues.build();
faceData.add(KeyValue.build("label", "标签-" + name));
String imageBase64 = Base64Util.encode(image.getAbsolutePath());
- Face face = Face.build(sampleId).setFaceData(faceData).setImageBase64(imageBase64)
- .setMinConfidenceThresholdWithThisSample(50f)
- .setMaxConfidenceThresholdWithOtherSample(50f);
+ Face face = Face.build(sampleId).setFaceData(faceData)
+ .setMinConfidenceThresholdWithThisSample(0f)
+ .setMaxConfidenceThresholdWithOtherSample(50f)
+ .setImageBase64(imageBase64);
Response createFace = faceSearch.face().createFace(face);
System.out.println("createFace:" + createFace);
}
diff --git a/face-search-test/src/main/resources/image/validate/index/董明珠/1b11132d2ba62f058065c4eb80516624.jpeg b/face-search-test/src/main/resources/image/validate/index/董明珠/1b11132d2ba62f058065c4eb80516624.jpeg
deleted file mode 100644
index 3a5418b..0000000
Binary files a/face-search-test/src/main/resources/image/validate/index/董明珠/1b11132d2ba62f058065c4eb80516624.jpeg and /dev/null differ
diff --git a/pom.xml b/pom.xml
index 33927bf..ea36544 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
com.visual.face.search
face-search
pom
- 2.0.1
+ 2.1.0
@@ -27,10 +27,11 @@
1.1.22
- 4.6.0-0
- 2.4.0
- 1.13.1
+ 4.7.0-0
+ 2.8.0
+ 1.15.1
1.2.83
+ 2.15.0
6.0.13.Final
3.6.1
4.1
@@ -146,6 +147,55 @@
knife4j-spring-boot-starter
${knife4j-ui.version}
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-cbor
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-smile
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+ ${jackson.version}
+
diff --git a/scripts/docker-compose-opensearch.yml b/scripts/docker-compose-opensearch.yml
index 88b5209..9606280 100644
--- a/scripts/docker-compose-opensearch.yml
+++ b/scripts/docker-compose-opensearch.yml
@@ -21,7 +21,7 @@ services:
visual-opensearch:
container_name: face-search-opensearch-standalone
- image: opensearchproject/opensearch:2.4.0
+ image: opensearchproject/opensearch:2.8.0
environment:
discovery.type: single-node
expose:
@@ -37,7 +37,7 @@ services:
visual-opensearch-dashboards:
container_name: face-search-opensearch-dashboards
- image: opensearchproject/opensearch-dashboards:2.4.0
+ image: opensearchproject/opensearch-dashboards:2.8.0
environment:
OPENSEARCH_HOSTS: '["https://visual-opensearch:9200"]'
ports:
@@ -48,7 +48,7 @@ services:
visual-facesearch:
container_name: face-search-server-standalone
- image: divenswu/face-search:2.0.1
+ image: divenswu/face-search:2.1.0
environment:
SPRING_DATASOURCE_URL: 'jdbc:mysql://visual-mysql:3306/visual_face_search?useUnicode=true&characterEncoding=utf8'
SPRING_DATASOURCE_USERNAME: root
diff --git a/scripts/docker_build.sh b/scripts/docker_build.sh
index 068b12d..a67fab0 100644
--- a/scripts/docker_build.sh
+++ b/scripts/docker_build.sh
@@ -1,4 +1,4 @@
-version='2.0.1'
+version='2.1.0'
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
cd ${SHELL_FOLDER}
diff --git a/scripts/docs/2.0.0.md b/scripts/docs/2.1.0.md
similarity index 100%
rename from scripts/docs/2.0.0.md
rename to scripts/docs/2.1.0.md