diff --git a/README.md b/README.md index 9d91506..361ecea 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## 人脸搜索M:N -* 本项目是阿里云视觉智能开放平台的人脸1:N的开源替代,项目中使用的模型均为开源模型,项目支持milvus和proxima向量存储库,并具有较高的自定义能力。 +* 本项目是阿里云视觉智能开放平台的人脸1:N的开源替代,项目中使用的模型均为开源模型,项目支持opensearch(1.x版本支持milvus和proxima)向量存储库,并具有较高的自定义能力。 * 项目使用纯Java开发,免去使用Python带来的服务不稳定性。 @@ -22,9 +22,7 @@     2、[onnx](https://github.com/onnx/onnx) -    3、[milvus](https://github.com/milvus-io/milvus/) - -    4、[proxima](https://github.com/alibaba/proximabilin) +    3、[opensearch](https://opensearch.org/) * 深度学习模型 @@ -32,15 +30,14 @@     2、[PCN](https://github.com/Rock-100/FaceKit/tree/master/PCN) -### 版本1.1.0更新 - -* 1、修复已知BUG -* 2、添加人脸比对1:1接口,详见文档:[05、人脸比对服务](https://gitee.com/open-visual/face-search/blob/v1.2.0/scripts/docs/doc-1.1.0.md#05%E4%BA%BA%E8%84%B8%E6%AF%94%E5%AF%B9%E6%9C%8D%E5%8A%A1) +### 版本2.2.0更新 +* 1、添加对opensearch的支持,删除对proxima与milvus向量引擎的支持 +* 2、更新:删除搜索结果中的距离指标,仅保留置信度指标(余弦相似度) ### 项目文档 -* 在线文档:[文档-1.2.0](https://gitee.com/open-visual/face-search/blob/v1.1.0/scripts/docs/doc-1.1.0.md) +* 在线文档:[文档-2.0.0](scripts/docs/2.0.0.md * swagger文档:启动项目且开启swagger,访问:host:port/doc.html, 如 http://127.0.0.1:8080/doc.html @@ -51,23 +48,30 @@ com.visual.face.search face-search-client - 1.1.0 + 2.0.0 ``` * 其他语言依赖 -   使用restful接口:[文档-1.2.0](https://gitee.com/open-visual/face-search/blob/v1.2.0/scripts/docs/doc-1.1.0.md) +   使用restful接口:[文档-2.0.0](scripts/docs/2.0.0.md) ### 项目部署 * docker部署,脚本目录:face-search/scripts ``` -1、使用milvus作为向量搜索引擎 - docker-compose -f docker-compose-milvus.yml --compatibility up -d +1、配置环境变量:FACESEARCH_VOLUME_DIRECTORY,指定当前的挂载根路径,默认为当前路径 -2、使用proxima作为向量搜索引擎 - docker-compose -f docker-compose-proxima.yml --compatibility up -d +2、对opensearch的挂载目录进行赋权: + 新建目录:${FACESEARCH_VOLUME_DIRECTORY:-.}/volumes-face-search/opensearch/data + 目录赋权:chmod 777 ${FACESEARCH_VOLUME_DIRECTORY:-.}/volumes-face-search/opensearch/data + +3、使用opensearch作为向量搜索引擎 + docker-compose -f docker-compose-opensearch.yml --compatibility up -d + +4、服务访问: + opensearch自带的可视化工具:http://127.0.0.1:5601 + facesearch的swagger文档: http://127.0.0.1:56789/doc.html ``` * 项目编译 @@ -101,17 +105,11 @@ * 项目中为了提高人脸的检出率,使用了主要和次要的人脸检测模型,目前实现了两种人脸检测模型insightface和PCN,在docker的服务中,默认主服务为PCN,备用服务为insightface。insightface的效率高,但针对于旋转了大角度的人脸检出率不高,而pcn则可以识别大角度旋转的图片,但效率低一些。若图像均为正脸的图像,建议使用insightface为主模型,pcn为备用模型,如何切换,请查看部署参数。 -* 在测试过程中,针对milvus和proxima,发现proxima的速度比milvus稍快,但稳定性没有milvus好,线上服务使用时,还是建议使用milvus作为向量检索引擎。 - ### 项目演示 -* 1.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) +* 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) -* ![输入图片说明](scripts/images/validate.jpg) - -* 1.2.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) - -* ![输入图片说明](scripts/images/validate-1.2.0.jpg) +* ![输入图片说明](scripts/images/validate-2.0.0.jpg) ### 交流群 diff --git a/face-search-client/pom.xml b/face-search-client/pom.xml old mode 100755 new mode 100644 index ce948ee..b03096e --- a/face-search-client/pom.xml +++ b/face-search-client/pom.xml @@ -2,12 +2,11 @@ - 4.0.0 - face-search-client com.visual.face.search - 1.1.0 + face-search-client + 2.0.0 1.8 diff --git a/face-search-client/src/main/java/com/visual/face/search/handle/CollectHandler.java b/face-search-client/src/main/java/com/visual/face/search/handle/CollectHandler.java index 4648707..5248f9f 100755 --- a/face-search-client/src/main/java/com/visual/face/search/handle/CollectHandler.java +++ b/face-search-client/src/main/java/com/visual/face/search/handle/CollectHandler.java @@ -46,7 +46,6 @@ public class CollectHandler extends BaseHandler{ .setMaxDocsPerSegment(collect.getMaxDocsPerSegment()) .setSampleColumns(collect.getSampleColumns()) .setFaceColumns(collect.getFaceColumns()) - .setSyncBinLog(collect.isSyncBinLog()) .setShardsNum(collect.getShardsNum()) .setStorageFaceInfo(collect.getStorageFaceInfo()) .setStorageEngine(collect.getStorageEngine()); diff --git a/face-search-client/src/main/java/com/visual/face/search/model/Collect.java b/face-search-client/src/main/java/com/visual/face/search/model/Collect.java index d2e9f02..d08eb54 100755 --- a/face-search-client/src/main/java/com/visual/face/search/model/Collect.java +++ b/face-search-client/src/main/java/com/visual/face/search/model/Collect.java @@ -18,8 +18,6 @@ public class Collect> implements Serializab private List sampleColumns = new ArrayList<>(); /**自定义的人脸字段**/ private List faceColumns = new ArrayList<>(); - /**启用binlog同步**/ - private Boolean syncBinLog = false; /**是否保留图片及人脸信息**/ private Boolean storageFaceInfo = false; /**保留图片及人脸信息的存储组件**/ @@ -86,17 +84,6 @@ public class Collect> implements Serializab return (ExtendsVo) this; } - public boolean isSyncBinLog() { - return null == syncBinLog ? false : syncBinLog; - } - - public ExtendsVo setSyncBinLog(Boolean syncBinLog) { - if(null != syncBinLog){ - this.syncBinLog = syncBinLog; - } - return (ExtendsVo) this; - } - public boolean getStorageFaceInfo() { return null == storageFaceInfo ? false : storageFaceInfo; } diff --git a/face-search-client/src/main/java/com/visual/face/search/model/SampleFace.java b/face-search-client/src/main/java/com/visual/face/search/model/SampleFace.java index 37b4ffe..acf9881 100755 --- a/face-search-client/src/main/java/com/visual/face/search/model/SampleFace.java +++ b/face-search-client/src/main/java/com/visual/face/search/model/SampleFace.java @@ -11,8 +11,6 @@ public class SampleFace implements Comparable, Serializable { /**人脸人数质量**/ private Float faceScore; /**转换后的置信度**/ - private Float distance; - /**转换后的置信度**/ private Float confidence; /**样本扩展的额外数据**/ private KeyValues sampleData; @@ -67,14 +65,6 @@ public class SampleFace implements Comparable, Serializable { this.faceScore = faceScore; } - public Float getDistance() { - return distance; - } - - public void setDistance(Float distance) { - this.distance = distance; - } - public Float getConfidence() { return confidence; } diff --git a/face-search-core/pom.xml b/face-search-core/pom.xml old mode 100755 new mode 100644 index bfef8d5..69af6cb --- a/face-search-core/pom.xml +++ b/face-search-core/pom.xml @@ -5,8 +5,9 @@ face-search com.visual.face.search - 1.2.0 + 2.0.0 + 4.0.0 face-search-core @@ -30,6 +31,6 @@ com.alibaba fastjson - + \ No newline at end of file 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 742d285..58b9174 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 @@ -123,9 +123,9 @@ public class FaceInfo implements Comparable, Serializable { * @return 旋转后的角 */ public Point rotation(Point center, float angle){ - double k = new Float(Math.toRadians(angle)); - float nx1 = new Float((this.x-center.x)*Math.cos(k) +(this.y-center.y)*Math.sin(k)+center.x); - float ny1 = new Float(-(this.x-center.x)*Math.sin(k) + (this.y-center.y)*Math.cos(k)+center.y); + double k = Math.toRadians(angle); + float nx1 = (float) ((this.x - center.x) * Math.cos(k) + (this.y - center.y) * Math.sin(k) + center.x); + float ny1 = (float) (-(this.x - center.x) * Math.sin(k) + (this.y - center.y) * Math.cos(k) + center.y); return new Point(nx1, ny1); } diff --git a/face-search-core/src/main/java/com/visual/face/search/core/models/InsightArcFaceRecognition.java b/face-search-core/src/main/java/com/visual/face/search/core/models/InsightArcFaceRecognition.java index 1e12caa..38fb9de 100755 --- a/face-search-core/src/main/java/com/visual/face/search/core/models/InsightArcFaceRecognition.java +++ b/face-search-core/src/main/java/com/visual/face/search/core/models/InsightArcFaceRecognition.java @@ -42,7 +42,7 @@ public class InsightArcFaceRecognition extends BaseOnnxInfer implements FaceRec .to4dFloatOnnxTensorAndDoReleaseMat(true); output = getSession().run(Collections.singletonMap(getInputName(), tensor)); float[][] embeds = (float[][]) output.get(0).getValue(); - return FaceInfo.Embedding.build(image.toBase64AndNoReleaseMat(), embeds[0]); + return Embedding.build(image.toBase64AndNoReleaseMat(), embeds[0]); } catch (Exception e) { throw new RuntimeException(e); }finally { diff --git a/face-search-core/src/main/java/com/visual/face/search/core/models/PcnNetworkFaceDetection.java b/face-search-core/src/main/java/com/visual/face/search/core/models/PcnNetworkFaceDetection.java index 3af30f0..9b6c328 100755 --- a/face-search-core/src/main/java/com/visual/face/search/core/models/PcnNetworkFaceDetection.java +++ b/face-search-core/src/main/java/com/visual/face/search/core/models/PcnNetworkFaceDetection.java @@ -66,7 +66,7 @@ public class PcnNetworkFaceDetection extends BaseOnnxInfer implements FaceDetect imgPad = pad_img_not_release_mat(mat); float[] iouThs = iouTh <= 0 ? defIouThs : new float[]{iouTh, iouTh, 0.3f}; float[] scoreThs = scoreTh <= 0 ? defScoreThs : new float[]{0.375f * scoreTh, 0.5f * scoreTh, scoreTh}; - List willis = detect(this.getSessions(), mat, imgPad, scoreThs, iouThs); + List willis = detect(this.getSessions(), mat, imgPad, scoreThs, iouThs); return trans_window(mat, imgPad, willis); } catch (Exception e) { throw new RuntimeException(e); @@ -573,7 +573,7 @@ public class PcnNetworkFaceDetection extends BaseOnnxInfer implements FaceDetect * @throws OrtException * @throws IOException */ - private static List detect(OrtSession[] sessions, Mat img, Mat imgPad, float[] scoreThs, float iouThs[]) throws OrtException, IOException { + private static List detect(OrtSession[] sessions, Mat img, Mat imgPad, float[] scoreThs, float iouThs[]) throws OrtException, IOException { Mat img180 = new Mat(); Core.flip(imgPad, img180, 0); @@ -583,7 +583,7 @@ public class PcnNetworkFaceDetection extends BaseOnnxInfer implements FaceDetect Mat imgNeg90 = new Mat(); Core.flip(img90, imgNeg90, 0); - List winlist = stage1(img, imgPad, sessions[0], scoreThs[0]); + List winlist = stage1(img, imgPad, sessions[0], scoreThs[0]); winlist = NMS(winlist, true, iouThs[0]); winlist = stage2(imgPad, img180, sessions[1], scoreThs[1], 24, winlist); @@ -604,7 +604,7 @@ public class PcnNetworkFaceDetection extends BaseOnnxInfer implements FaceDetect /** * 临时的人脸框 */ - private static class Window2 implements Comparable{ + private static class Window2 implements Comparable{ public int x; public int y; public int w; @@ -624,7 +624,7 @@ public class PcnNetworkFaceDetection extends BaseOnnxInfer implements FaceDetect } @Override - public int compareTo(PcnNetworkFaceDetection.Window2 o) { + public int compareTo(Window2 o) { if(o.conf == this.conf){ return new Integer(this.y).compareTo(o.y); }else{ diff --git a/face-search-core/src/main/java/com/visual/face/search/core/utils/Similarity.java b/face-search-core/src/main/java/com/visual/face/search/core/utils/Similarity.java index 98dcbc7..0952be9 100644 --- a/face-search-core/src/main/java/com/visual/face/search/core/utils/Similarity.java +++ b/face-search-core/src/main/java/com/visual/face/search/core/utils/Similarity.java @@ -76,4 +76,20 @@ public class Similarity { return Double.valueOf(sim).floatValue(); } + /** + * 对cos的原始值进行进行增强 + * @param cos + * @return + */ + public static float cosEnhance(float cos){ + double sim = cos; + if(cos >= 0.5){ + sim = cos + 2 * (cos - 0.5) * (1 - cos); + }else if(cos >= 0){ + sim = cos - 2 * (cos - 0.5) * (0 - cos); + } + return Double.valueOf(sim).floatValue(); + } + + } diff --git a/face-search-engine/libs/milvus-java-sdk-2.0.0.jar b/face-search-engine/libs/milvus-java-sdk-2.0.0.jar deleted file mode 100755 index b763a76..0000000 Binary files a/face-search-engine/libs/milvus-java-sdk-2.0.0.jar and /dev/null differ diff --git a/face-search-engine/libs/milvus-java-sdk-2.0.4.jar b/face-search-engine/libs/milvus-java-sdk-2.0.4.jar deleted file mode 100755 index fbc2259..0000000 Binary files a/face-search-engine/libs/milvus-java-sdk-2.0.4.jar and /dev/null differ diff --git a/face-search-engine/libs/proxima-be-java-sdk-0.2.0.jar b/face-search-engine/libs/proxima-be-java-sdk-0.2.0.jar deleted file mode 100755 index 4c8f7fc..0000000 Binary files a/face-search-engine/libs/proxima-be-java-sdk-0.2.0.jar and /dev/null differ diff --git a/face-search-engine/pom.xml b/face-search-engine/pom.xml old mode 100755 new mode 100644 index d6cd44d..385c937 --- a/face-search-engine/pom.xml +++ b/face-search-engine/pom.xml @@ -5,118 +5,22 @@ face-search com.visual.face.search - 1.2.0 + 2.0.0 4.0.0 + face-search-engine - - UTF-8 - 1.36.0 - 3.12.0 - 3.12.0 - 4.3 - 1.8 - 1.8 - - - - - - io.grpc - grpc-bom - ${grpc.version} - pom - import - - - - - - io.grpc - grpc-netty-shaded - 1.36.0 - - - io.grpc - grpc-stub - 1.36.0 - - - io.grpc - grpc-protobuf - 1.36.0 - - - com.google.guava - guava - 21.0 - - - org.projectlombok - lombok - 1.16.16 - - - com.google.protobuf - protobuf-java - 3.14.0 - - - org.slf4j - slf4j-api - 1.7.30 - - - org.apache.logging.log4j - log4j-slf4j-impl - 2.12.1 - - - io.milvus - milvus-java-sdk - 2.0.4 - system - ${project.basedir}/libs/milvus-java-sdk-2.0.4.jar - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - com.google.protobuf - protobuf-java-util - ${protobuf.version} - - - org.apache.commons - commons-text - 1.6 - org.apache.commons commons-collections4 - ${commons-collections4.version} - - - org.json - json - 20190722 - com.alibaba.proxima - proxima-be-java-sdk - 0.2.0 - system - ${project.basedir}/libs/proxima-be-java-sdk-0.2.0.jar + org.opensearch.client + opensearch-rest-high-level-client - - \ No newline at end of file diff --git a/face-search-engine/src/main/java/com/visual/face/search/engine/api/SearchEngine.java b/face-search-engine/src/main/java/com/visual/face/search/engine/api/SearchEngine.java new file mode 100755 index 0000000..c2f234d --- /dev/null +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/api/SearchEngine.java @@ -0,0 +1,30 @@ +package com.visual.face.search.engine.api; + + +import com.visual.face.search.engine.model.MapParam; +import com.visual.face.search.engine.model.SearchResponse; + +import java.util.List; + +public interface SearchEngine { + + public Object getEngine(); + + public boolean exist(String collectionName); + + public boolean dropCollection(String collectionName); + + public boolean createCollection(String collectionName, MapParam param); + + public boolean insertVector(String collectionName, String sampleId, String faceId, float[] vectors); + + public boolean deleteVectorByKey(String collectionName, String faceId); + + public boolean deleteVectorByKey(String collectionName, List faceIds); + + public SearchResponse search(String collectionName, float[][] features, String algorithm, int topK); + + public float searchMinScoreBySampleId(String collectionName, String sampleId,float[] feature, String algorithm); + + public float searchMaxScoreBySampleId(String collectionName, String sampleId,float[] feature, String algorithm); +} diff --git a/face-search-engine/src/main/java/com/visual/face/search/engine/conf/Constant.java b/face-search-engine/src/main/java/com/visual/face/search/engine/conf/Constant.java new file mode 100755 index 0000000..9144894 --- /dev/null +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/conf/Constant.java @@ -0,0 +1,13 @@ +package com.visual.face.search.engine.conf; + +public class Constant { + + public final static String IndexShardsNum = "shardsNum"; + public final static String IndexReplicasNum = "replicasNum"; + + public final static String ColumnNameFaceId = "face_id"; + public final static String ColumnNameSampleId = "sample_id"; + public final static String ColumnNameFaceVector = "face_vector"; + public final static String ColumnNameFaceScore = "face_score"; + +} diff --git a/face-search-engine/src/main/java/com/visual/face/search/engine/exps/SearchEngineException.java b/face-search-engine/src/main/java/com/visual/face/search/engine/exps/SearchEngineException.java new file mode 100644 index 0000000..8d7b3a8 --- /dev/null +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/exps/SearchEngineException.java @@ -0,0 +1,25 @@ +package com.visual.face.search.engine.exps; + + +public class SearchEngineException extends RuntimeException{ + + public SearchEngineException() { + } + + public SearchEngineException(String message) { + super(message); + } + + public SearchEngineException(String message, Throwable cause) { + super(message, cause); + } + + public SearchEngineException(Throwable cause) { + super(cause); + } + + public SearchEngineException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/face-search-engine/src/main/java/com/visual/face/search/engine/impl/OpenSearchEngine.java b/face-search-engine/src/main/java/com/visual/face/search/engine/impl/OpenSearchEngine.java new file mode 100644 index 0000000..aefe96a --- /dev/null +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/impl/OpenSearchEngine.java @@ -0,0 +1,247 @@ +package com.visual.face.search.engine.impl; + +import com.visual.face.search.engine.api.SearchEngine; +import com.visual.face.search.engine.conf.Constant; +import com.visual.face.search.engine.exps.SearchEngineException; +import com.visual.face.search.engine.model.*; +import org.apache.commons.collections4.MapUtils; +import org.opensearch.action.DocWriteResponse; +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; +import org.opensearch.action.delete.DeleteRequest; +import org.opensearch.action.delete.DeleteResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.search.MultiSearchRequest; +import org.opensearch.action.search.MultiSearchResponse; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.client.indices.CreateIndexRequest; +import org.opensearch.client.indices.CreateIndexResponse; +import org.opensearch.client.indices.GetIndexRequest; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.query.*; +import org.opensearch.index.query.functionscore.ScriptScoreQueryBuilder; +import org.opensearch.index.reindex.BulkByScrollResponse; +import org.opensearch.index.reindex.DeleteByQueryRequest; +import org.opensearch.rest.RestStatus; +import org.opensearch.script.Script; +import org.opensearch.search.SearchHit; +import org.opensearch.search.builder.SearchSourceBuilder; +import java.io.IOException; +import java.util.*; + +public class OpenSearchEngine implements SearchEngine { + + private RestHighLevelClient client; + private MapParam params = new MapParam(); + + public OpenSearchEngine(RestHighLevelClient client){ + this(client, null); + } + + public OpenSearchEngine(RestHighLevelClient client, MapParam params){ + this.client = client; + if(null != params) { this.params = params; } + } + + @Override + public Object getEngine() { + return this.client; + } + + @Override + public boolean exist(String collectionName) { + try { + GetIndexRequest request = new GetIndexRequest(collectionName); + return this.client.indices().exists(request, RequestOptions.DEFAULT); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean dropCollection(String collectionName) { + try { + DeleteIndexRequest request = new DeleteIndexRequest(collectionName); + return this.client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean createCollection(String collectionName, MapParam param) { + try { + //构建请求 + CreateIndexRequest createIndexRequest = new CreateIndexRequest(collectionName); + createIndexRequest.settings(Settings.builder() + .put("index.number_of_shards", param.getIndexShardsNum()) + .put("index.number_of_replicas", param.getIndexReplicasNum()) + ); + HashMap properties = new HashMap<>(); + properties.put(Constant.ColumnNameSampleId, Map.of("type", "keyword")); + properties.put(Constant.ColumnNameFaceVector, Map.of("type", "knn_vector", "dimension", "512")); + createIndexRequest.mapping(Map.of("properties", properties)); + //创建集合 + CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT); + return createIndexResponse.isAcknowledged(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean insertVector(String collectionName, String sampleId, String faceId, float[] vectors) { + try { + //构建请求 + IndexRequest request = new IndexRequest(collectionName) + .id(faceId) + .source(Map.of( + Constant.ColumnNameSampleId, sampleId, + Constant.ColumnNameFaceVector, vectors + )); + //插入数据 + IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); + DocWriteResponse.Result result = indexResponse.getResult(); + return DocWriteResponse.Result.CREATED == result || DocWriteResponse.Result.UPDATED == result; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean deleteVectorByKey(String collectionName, String faceId) { + try { + DeleteRequest deleteDocumentRequest = new DeleteRequest(collectionName, faceId); + DeleteResponse deleteResponse = client.delete(deleteDocumentRequest, RequestOptions.DEFAULT); + return DocWriteResponse.Result.DELETED == deleteResponse.getResult(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean deleteVectorByKey(String collectionName, List keyIds) { + try { + String[] idArray = new String[keyIds.size()]; idArray = keyIds.toArray(idArray); + QueryBuilder queryBuilder = new BoolQueryBuilder().must(QueryBuilders.idsQuery().addIds(idArray)); + DeleteByQueryRequest request = new DeleteByQueryRequest(collectionName).setQuery(queryBuilder); + BulkByScrollResponse response = client.deleteByQuery(request, RequestOptions.DEFAULT); + return response.getBulkFailures() != null && response.getBulkFailures().size() == 0; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public SearchResponse search(String collectionName, float[][] features, String algorithm, int topK) { + try { + //构建搜索请求 + MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); + for(float[] feature : features){ + QueryBuilder queryBuilder = new MatchAllQueryBuilder(); + Map params = new HashMap<>(); + params.put("field", Constant.ColumnNameFaceVector); + params.put("space_type", algorithm); + params.put("query_value", feature); + Script script = new Script(Script.DEFAULT_SCRIPT_TYPE, "knn", "knn_score", params); + ScriptScoreQueryBuilder scriptScoreQueryBuilder = new ScriptScoreQueryBuilder(queryBuilder, script); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder() + .query(scriptScoreQueryBuilder).size(topK) + .fetchSource(null, Constant.ColumnNameFaceVector); //是否需要向量字段 + SearchRequest searchRequest = new SearchRequest(collectionName).source(searchSourceBuilder); + multiSearchRequest.add(searchRequest); + } + //查询索引 + MultiSearchResponse response = this.client.msearch(multiSearchRequest, RequestOptions.DEFAULT); + MultiSearchResponse.Item[] responses = response.getResponses(); + if(features.length != responses.length){ + throw new SearchEngineException("features.length != responses.length"); + } + //解析数据 + List result = new ArrayList<>(); + for(MultiSearchResponse.Item item : response.getResponses()){ + List documents = new ArrayList<>(); + SearchHit[] searchHits = item.getResponse().getHits().getHits(); + if(searchHits != null){ + for(SearchHit searchHit : searchHits){ + String faceId = searchHit.getId(); + float score = searchHit.getScore()-1; + Map sourceMap = searchHit.getSourceAsMap(); + String sampleId = MapUtils.getString(sourceMap, Constant.ColumnNameSampleId); + Object faceVector = MapUtils.getObject(sourceMap, Constant.ColumnNameFaceVector); + SearchDocument document = SearchDocument.build(sampleId, faceId, score).setVectors(faceVector); + documents.add(document); + } + } + result.add(SearchResult.build(documents)); + } + //返回结果信息 + return SearchResponse.build(SearchStatus.build(0, "success"), result); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public float searchMinScoreBySampleId(String collectionName, String sampleId,float[] feature, String algorithm) { + try { + //构建请求 + QueryBuilder queryBuilder = new MatchQueryBuilder(Constant.ColumnNameSampleId, sampleId); + Map params = new HashMap<>(); + params.put("field", Constant.ColumnNameFaceVector); + params.put("space_type", algorithm); + params.put("query_value", feature); + Script script = new Script(Script.DEFAULT_SCRIPT_TYPE, "knn", "knn_score", params); + ScriptScoreQueryBuilder scriptScoreQueryBuilder = new ScriptScoreQueryBuilder(queryBuilder, script); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder() + .query(scriptScoreQueryBuilder) + .fetchSource(false).size(10000); //是否需要索引字段 + SearchRequest searchRequest = new SearchRequest(collectionName).source(searchSourceBuilder); + //搜索请求 + org.opensearch.action.search.SearchResponse response = this.client.search(searchRequest, RequestOptions.DEFAULT); + if(RestStatus.OK == response.status()){ + SearchHit[] searchHits = response.getHits().getHits(); + Double minScore = Arrays.stream(searchHits).mapToDouble(SearchHit::getScore).min().orElse(2f); + return minScore.floatValue()-1; + }else{ + throw new RuntimeException("get score error!"); + } + } catch (Exception e) { + throw new SearchEngineException(e); + } + } + + @Override + public float searchMaxScoreBySampleId(String collectionName, String sampleId,float[] feature, String algorithm) { + try { + //构建请求 + QueryBuilder queryBuilder = new BoolQueryBuilder() + .mustNot(new MatchQueryBuilder(Constant.ColumnNameSampleId, sampleId)); + Map params = new HashMap<>(); + params.put("field", Constant.ColumnNameFaceVector); + params.put("space_type", algorithm); + params.put("query_value", feature); + Script script = new Script(Script.DEFAULT_SCRIPT_TYPE, "knn", "knn_score", params); + ScriptScoreQueryBuilder scriptScoreQueryBuilder = new ScriptScoreQueryBuilder(queryBuilder, script); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder() + .query(scriptScoreQueryBuilder) + .fetchSource(false).size(1); //是否需要索引字段 + SearchRequest searchRequest = new SearchRequest(collectionName).source(searchSourceBuilder); + //搜索请求 + org.opensearch.action.search.SearchResponse response = this.client.search(searchRequest, RequestOptions.DEFAULT); + if(RestStatus.OK == response.status()){ + SearchHit[] searchHits = response.getHits().getHits(); + Double maxScore = Arrays.stream(searchHits).mapToDouble(SearchHit::getScore).max().orElse(1f); + return maxScore.floatValue()-1; + }else{ + throw new RuntimeException("get score error!"); + } + } catch (Exception e) { + throw new SearchEngineException(e); + } + } + + +} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/MapParam.java b/face-search-engine/src/main/java/com/visual/face/search/engine/model/MapParam.java similarity index 80% rename from face-search-server/src/main/java/com/visual/face/search/server/engine/model/MapParam.java rename to face-search-engine/src/main/java/com/visual/face/search/engine/model/MapParam.java index 2eead7e..595adfa 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/MapParam.java +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/model/MapParam.java @@ -1,6 +1,6 @@ -package com.visual.face.search.server.engine.model; +package com.visual.face.search.engine.model; -import com.visual.face.search.server.engine.conf.Constant; +import com.visual.face.search.engine.conf.Constant; import org.apache.commons.collections4.MapUtils; import java.util.concurrent.ConcurrentHashMap; @@ -11,7 +11,6 @@ public class MapParam extends ConcurrentHashMap { return new MapParam(); } - public MapParam put(String key, Object value){ if(null != key && null != value){ super.put(key, value); @@ -60,15 +59,15 @@ public class MapParam extends ConcurrentHashMap { } /******************************************************************************************************************/ - public Long getMaxDocsPerSegment(){ - Long maxDocsPerSegment = this.getLong(Constant.ParamKeyMaxDocsPerSegment, 0L); + public Long getIndexReplicasNum(){ + Long maxDocsPerSegment = this.getLong(Constant.IndexReplicasNum, 1L); maxDocsPerSegment = (null == maxDocsPerSegment || maxDocsPerSegment < 0) ? 0 : maxDocsPerSegment; return maxDocsPerSegment; } - public Integer getShardsNum(){ - Integer shardsNum = this.getInteger(Constant.ParamKeyShardsNum, 0); - shardsNum = (null == shardsNum || shardsNum <= 0) ? 2 : shardsNum; + public Integer getIndexShardsNum(){ + Integer shardsNum = this.getInteger(Constant.IndexShardsNum, 4); + shardsNum = (null == shardsNum || shardsNum <= 0) ? 4 : shardsNum; return shardsNum; } diff --git a/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchDocument.java b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchDocument.java new file mode 100755 index 0000000..601b3b4 --- /dev/null +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchDocument.java @@ -0,0 +1,72 @@ +package com.visual.face.search.engine.model; + +import java.io.Serializable; +import com.visual.face.search.engine.utils.NumberUtils; + +public class SearchDocument implements Serializable { + private float score; + private String faceId; + private String sampleId; + private Float[] vectors; + + public SearchDocument(){} + + public SearchDocument(String sampleId, String faceId, float score) { + this(sampleId, faceId, score, null); + } + + public SearchDocument(String sampleId, String faceId, float score, Float[] vectors) { + this.score = score; + this.faceId = faceId; + this.sampleId = sampleId; + this.vectors = vectors; + } + + public static SearchDocument build(String sampleId, String faceId, float score){ + return build(sampleId, faceId, score, null); + } + + public static SearchDocument build(String sampleId, String faceId, float score, Float[] vectors){ + return new SearchDocument(sampleId, faceId, score, vectors); + } + + public float getScore() { + return score; + } + + public SearchDocument setScore(float score) { + this.score = score; + return this; + } + + public String getFaceId() { + return faceId; + } + + public SearchDocument setFaceId(String faceId) { + this.faceId = faceId; + return this; + } + + public String getSampleId() { + return sampleId; + } + + public SearchDocument setSampleId(String sampleId) { + this.sampleId = sampleId; + return this; + } + + public Float[] getVectors() { + return vectors; + } + + public SearchDocument setVectors(Object vectors) { + if(vectors == null){ + return this; + }else{ + this.vectors = NumberUtils.getFloatArray(vectors); + } + return this; + } +} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchResponse.java b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchResponse.java similarity index 94% rename from face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchResponse.java rename to face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchResponse.java index b0fd1fd..1fc99eb 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchResponse.java +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchResponse.java @@ -1,4 +1,4 @@ -package com.visual.face.search.server.engine.model; +package com.visual.face.search.engine.model; import java.util.ArrayList; import java.util.List; diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchResult.java b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchResult.java similarity index 92% rename from face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchResult.java rename to face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchResult.java index ee9265b..25b18d8 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchResult.java +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchResult.java @@ -1,4 +1,4 @@ -package com.visual.face.search.server.engine.model; +package com.visual.face.search.engine.model; import java.util.ArrayList; diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchStatus.java b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchStatus.java similarity index 88% rename from face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchStatus.java rename to face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchStatus.java index 9d29b67..d8aa1a0 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchStatus.java +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/model/SearchStatus.java @@ -1,8 +1,8 @@ -package com.visual.face.search.server.engine.model; +package com.visual.face.search.engine.model; public class SearchStatus { private int code; - private String reason; + private String reason; public SearchStatus(){} diff --git a/face-search-engine/src/main/java/com/visual/face/search/engine/utils/NumberUtils.java b/face-search-engine/src/main/java/com/visual/face/search/engine/utils/NumberUtils.java new file mode 100644 index 0000000..a77ea65 --- /dev/null +++ b/face-search-engine/src/main/java/com/visual/face/search/engine/utils/NumberUtils.java @@ -0,0 +1,74 @@ +package com.visual.face.search.engine.utils; + +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.List; + +public class NumberUtils { + + public static Number getNumber(Object value) { + if (value != null) { + if (value instanceof Number) { + return (Number)value; + } + if (value instanceof String) { + try { + String text = (String)value; + return NumberFormat.getInstance().parse(text); + } catch (ParseException var4) { + + } + } + } + return null; + } + + public static Float getFloat(Object value) { + Number answer = getNumber(value); + if (answer == null) { + return null; + } else { + return answer instanceof Float ? (Float)answer : answer.floatValue(); + } + } + + + public static Float[] getFloatArray(Object values) { + if(null != values){ + if(values.getClass().isArray()){ + return getFloatArray((Object[]) values); + }else if(values instanceof List){ + return getFloatArray((List)values); + }else{ + throw new RuntimeException("type error for:"+values.getClass()); + } + }else{ + return null; + } + } + + public static Float[] getFloatArray(List values) { + if(null != values){ + Float[] floats = new Float[values.size()]; + for(int i=0; i httpClientBuilder + .setDefaultCredentialsProvider(credentialsProvider) + .setSSLHostnameVerifier((hostname, session) -> true) + .setSSLContext(sslContext) + .setMaxConnTotal(10) + .setMaxConnPerRoute(10) + ); + //构建client + return new RestHighLevelClient(builder); + } + + + public static void main(String[] args) throws InterruptedException { +// String index_name = "app-opensearch-index-001"; +// String index_name = "app-opensearch-index-002"; +// String index_name = "python-test-index2"; + String index_name = "visual_search_namespace_1_collect_20211201_v05_ohr6_vector"; + + OpenSearchEngine engine = new OpenSearchEngine(getOpenSearchClientImpl()); + + boolean exist = engine.exist(index_name); + System.out.println(exist); + if(exist){ +// boolean drop = engine.dropCollection(index_name); +// System.out.println(drop); + } + +// boolean create = engine.createCollection(index_name, MapParam.build()); +// System.out.println(create); +// +// for(int i=0; i<10; i++){ +// float[] vectors = new float[512]; +// vectors[i] = 0.23333333f; +// boolean insert = engine.insertVector(index_name, "simple-0001", String.valueOf(i), vectors); +// System.out.println("insert="+insert); +// } +// +// Thread.sleep(2000); + +// boolean delete = engine.deleteVectorByKey(index_name, "0"); +// System.out.println(delete); + +// boolean delete1 = engine.deleteVectorByKey(index_name, Arrays.asList("1", "5", "9")); +// System.out.println(delete1); + + +// float[][] a = new float[2][]; +// + float[] vectors = new float[512]; + vectors[0] = 0.768888f; + vectors[1] = 20000.768888f; + vectors[162] = 33333f; +// a[0] = vectors; +// +// float[] vectors1 = new float[512]; +// vectors1[2] = 10000.54444f; +// a[1] = vectors1; +// SearchResponse searchResponse = engine.search(index_name, a, "cosinesimil",1); +// System.out.println(searchResponse); + +// engine.searchCount(index_name, vectors, "", 1); + float minScore = engine.searchMinScoreBySampleId(index_name, "d4395b36984926a1934a0f9b916b32d21", vectors, "cosinesimil"); + float maxScore = engine.searchMaxScoreBySampleId(index_name, "d4395b36984926a1934a0f9b916b32d21", vectors, "cosinesimil"); + + System.out.println(minScore); + System.out.println(maxScore); + System.exit(1); + +// Object y = vectors; +// Object[] y1 = (Object[]) y; +// System.out.println(y1.length); +// +// System.out.println(y.getClass().getComponentType()); +// System.out.println(y.getClass().isArray()); +// System.out.println(cc instanceof Float); +// System.exit(1); + } +} diff --git a/face-search-server/pom.xml b/face-search-server/pom.xml old mode 100755 new mode 100644 index fcc1730..65af582 --- a/face-search-server/pom.xml +++ b/face-search-server/pom.xml @@ -5,7 +5,7 @@ face-search com.visual.face.search - 1.2.0 + 2.0.0 4.0.0 @@ -17,6 +17,22 @@ spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-validation + + + org.hibernate.validator + hibernate-validator + + + + + + org.hibernate.validator + hibernate-validator + + com.visual.face.search face-search-core @@ -76,11 +92,11 @@ io.springfox - springfox-swagger2 + springfox-boot-starter com.github.xiaoymin - swagger-bootstrap-ui + knife4j-spring-boot-starter @@ -90,9 +106,7 @@ org.springframework.boot spring-boot-maven-plugin - - true - + 2.6.0 diff --git a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/EngineConfig.java b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/EngineConfig.java index ec475ae..93357ff 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/EngineConfig.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/EngineConfig.java @@ -1,49 +1,74 @@ package com.visual.face.search.server.bootstrap.conf; -import com.alibaba.proxima.be.client.ConnectParam; -import com.alibaba.proxima.be.client.ProximaGrpcSearchClient; -import com.alibaba.proxima.be.client.ProximaSearchClient; -import com.visual.face.search.server.engine.api.SearchEngine; -import com.visual.face.search.server.engine.impl.MilvusSearchEngine; -import com.visual.face.search.server.engine.impl.ProximaSearchEngine; -import io.milvus.client.MilvusServiceClient; +import com.visual.face.search.engine.api.SearchEngine; +import com.visual.face.search.engine.impl.OpenSearchEngine; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestClientBuilder; +import org.opensearch.client.RestHighLevelClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; + @Configuration("visualEngineConfig") public class EngineConfig { //日志 public Logger logger = LoggerFactory.getLogger(getClass()); - @Value("${visual.engine.selected:proxima}") - private String selected; - - @Value("${visual.engine.proxima.host}") - private String proximaHost; - @Value("${visual.engine.proxima.port:16000}") - private Integer proximaPort; - - @Value("${visual.engine.milvus.host}") - private String milvusHost; - @Value("${visual.engine.milvus.port:19530}") - private Integer milvusPort; + @Value("${visual.engine.open-search.host:localhost}") + private String openSearchHost; + @Value("${visual.engine.open-search.port:9200}") + private Integer openSearchPort; + @Value("${visual.engine.open-search.scheme:https}") + private String openSearchScheme; + @Value("${visual.engine.open-search.username:admin}") + private String openSearchUserName; + @Value("${visual.engine.open-search.password:admin}") + private String openSearchPassword; @Bean(name = "visualSearchEngine") - public SearchEngine getSearchEngine(){ - if(selected.equalsIgnoreCase("milvus")){ - logger.info("current vector engine is milvus"); - io.milvus.param.ConnectParam connectParam = io.milvus.param.ConnectParam.newBuilder().withHost(milvusHost).withPort(milvusPort).build(); - MilvusServiceClient client = new MilvusServiceClient(connectParam); - return new MilvusSearchEngine(client); - }else{ - logger.info("current vector engine is proxima"); - ConnectParam connectParam = ConnectParam.newBuilder().withHost(proximaHost).withPort(proximaPort).build(); - ProximaSearchClient client = new ProximaGrpcSearchClient(connectParam); - return new ProximaSearchEngine(client); + public SearchEngine simpleSearchEngine(){ + //认证参数 + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(openSearchUserName, openSearchPassword)); + //ssl设置 + final SSLContext sslContext; + try { + sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, new TrustManager[] { new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { return null; } + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + public void checkServerTrusted(X509Certificate[] certs, String authType) {} + }}, new SecureRandom()); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + logger.error("create SearchEngine error:", e); + throw new RuntimeException(e); } + //构建请求 + RestClientBuilder builder = RestClient.builder(new HttpHost(openSearchHost, openSearchPort, openSearchScheme)) + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder + .setDefaultCredentialsProvider(credentialsProvider) + .setSSLHostnameVerifier((hostname, session) -> true) + .setSSLContext(sslContext) + .setMaxConnTotal(10) + .setMaxConnPerRoute(10) + ); + //构建client + return new OpenSearchEngine(new RestHighLevelClient(builder)); } } 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 new file mode 100644 index 0000000..89a3ee6 --- /dev/null +++ b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/Knife4jConfig.java @@ -0,0 +1,39 @@ +package com.visual.face.search.server.bootstrap.conf; + +import org.springframework.context.annotation.Bean; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.spring.web.plugins.Docket; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.oas.annotations.EnableOpenApi; +import springfox.documentation.builders.RequestHandlerSelectors; +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; + +@Configuration +@EnableOpenApi +@EnableKnife4j +public class Knife4jConfig { + + @Value("${visual.swagger.enable:true}") + private Boolean enable; + + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.OAS_30) + .enable(enable) + .apiInfo(new ApiInfoBuilder() + .title("人脸搜索服务API") + .description("人脸搜索服务API") + .version("2.0.0") + .build()) + .groupName("2.0.0") + .select() + .apis(RequestHandlerSelectors.basePackage("com.visual.face.search.server.controller.server")) + .paths(PathSelectors.any()) + .build(); + } +} + + diff --git a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/SwaggerConfig.java b/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/SwaggerConfig.java deleted file mode 100755 index a26bc0f..0000000 --- a/face-search-server/src/main/java/com/visual/face/search/server/bootstrap/conf/SwaggerConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.visual.face.search.server.bootstrap.conf; - -import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; -import io.swagger.annotations.Api; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -@Configuration -@EnableSwagger2 -@EnableSwaggerBootstrapUI -public class SwaggerConfig implements WebMvcConfigurer { - - @Value("${visual.swagger.enable:true}") - private Boolean enable; - - @Bean - public Docket ProductApi() { - return new Docket(DocumentationType.SWAGGER_2) - .enable(enable) - .useDefaultResponseMessages(false) - .forCodeGeneration(false) - .pathMapping("/") - .apiInfo(apiInfo()) - .select() - .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) - .build(); - } - - private ApiInfo apiInfo() { - return new ApiInfoBuilder() - .title("人脸搜索服务API") - .description("人脸搜索服务API") - .version("1.1.0") - .build(); - } - -} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/config/DruidConfig.java b/face-search-server/src/main/java/com/visual/face/search/server/config/DruidConfig.java index fba6945..1ac620d 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/config/DruidConfig.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/config/DruidConfig.java @@ -89,7 +89,7 @@ public class DruidConfig Filter filter = new Filter() { @Override - public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + public void init(FilterConfig filterConfig) throws ServletException { } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/domain/base/CollectVo.java b/face-search-server/src/main/java/com/visual/face/search/server/domain/base/CollectVo.java index f5cd99b..874640d 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/domain/base/CollectVo.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/domain/base/CollectVo.java @@ -28,7 +28,7 @@ public class CollectVo> extends BaseVo { /**数据分片中最大的文件个数**/ @Min(value = 0, message = "maxDocsPerSegment must greater than or equal to 0") @ApiModelProperty(value="数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效", position = 3,required = false) - private Long maxDocsPerSegment; + private Long replicasNum; /**数据分片中最大的文件个数**/ @Min(value = 0, message = "shardsNum must greater than or equal to 0") @ApiModelProperty(value="要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效", position = 4,required = false) @@ -39,9 +39,6 @@ public class CollectVo> extends BaseVo { /**自定义的人脸字段**/ @ApiModelProperty(value="自定义的人脸属性字段", position = 6,required = false) private List faceColumns = new ArrayList<>(); - /**启用binlog同步**/ - @ApiModelProperty(value="启用binlog同步。扩展字段,暂不支持该功能", position = 7,required = false) - private Boolean syncBinLog; /**是否保留图片及人脸信息**/ @ApiModelProperty(value="是否保留图片及人脸信息", position = 8,required = false) private Boolean storageFaceInfo; @@ -86,12 +83,12 @@ public class CollectVo> extends BaseVo { return (ExtendsVo) this; } - public Long getMaxDocsPerSegment() { - return maxDocsPerSegment; + public Long getReplicasNum() { + return replicasNum; } - public ExtendsVo setMaxDocsPerSegment(Long maxDocsPerSegment) { - this.maxDocsPerSegment = maxDocsPerSegment; + public ExtendsVo setReplicasNum(Long replicasNum) { + this.replicasNum = replicasNum; return (ExtendsVo) this; } @@ -126,15 +123,6 @@ public class CollectVo> extends BaseVo { return (ExtendsVo) this; } - public boolean isSyncBinLog() { - return null == syncBinLog ? false : syncBinLog; - } - - public ExtendsVo setSyncBinLog(Boolean syncBinLog) { - this.syncBinLog = syncBinLog; - return (ExtendsVo) this; - } - public boolean getStorageFaceInfo() { return null == storageFaceInfo ? false : storageFaceInfo; } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/domain/extend/SampleFaceVo.java b/face-search-server/src/main/java/com/visual/face/search/server/domain/extend/SampleFaceVo.java index c21babf..5a7812c 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/domain/extend/SampleFaceVo.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/domain/extend/SampleFaceVo.java @@ -16,16 +16,13 @@ public class SampleFaceVo implements Comparable, Serializable { @ApiModelProperty(value="人脸分数:[0,100]", position = 3, required = true) private Float faceScore; /**转换后的置信度**/ - @ApiModelProperty(value="向量距离:>=0", position = 4, required = true) - private Float distance; - /**转换后的置信度**/ - @ApiModelProperty(value="转换后的置信度:[-100,100],值越大,相似度越高。", position = 5, required = true) + @ApiModelProperty(value="转换后的置信度:[-100,100],值越大,相似度越高。", position = 4, required = true) private Float confidence; /**样本扩展的额外数据**/ - @ApiModelProperty(value="样本扩展的额外数据", position = 6, required = false) + @ApiModelProperty(value="样本扩展的额外数据", position = 5, required = false) private FieldKeyValues sampleData; /**人脸扩展的额外数据**/ - @ApiModelProperty(value="人脸扩展的额外数据", position = 7, required = false) + @ApiModelProperty(value="人脸扩展的额外数据", position = 6, required = false) private FieldKeyValues faceData; /** @@ -76,14 +73,6 @@ public class SampleFaceVo implements Comparable, Serializable { this.faceScore = faceScore; } - public Float getDistance() { - return distance; - } - - public void setDistance(Float distance) { - this.distance = distance; - } - public Float getConfidence() { return confidence; } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/domain/request/FaceSearchReqVo.java b/face-search-server/src/main/java/com/visual/face/search/server/domain/request/FaceSearchReqVo.java index 90e8670..7d0a917 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/domain/request/FaceSearchReqVo.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/domain/request/FaceSearchReqVo.java @@ -30,13 +30,16 @@ public class FaceSearchReqVo extends BaseVo { @Range(min = -100, max = 100, message = "faceScoreThreshold is not in the range") @ApiModelProperty(value="人脸匹配分数阈值,范围:[-100,100]:默认0", position = 4, required = false) private Float confidenceThreshold = 0f; + /**选择搜索评分的算法,默认余弦相似度(COSINESIMIL),可选参数:L1、L2、LINF、COSINESIMIL、INNERPRODUCT、HAMMINGBIT**/ + @ApiModelProperty(hidden = true, value="选择搜索评分的算法,默认是余弦相似度(COSINESIMIL),可选参数:L1、L2、LINF、COSINESIMIL、INNERPRODUCT、HAMMINGBIT", position = 5, required = false) + private String algorithm = SearchAlgorithm.COSINESIMIL.name(); /**搜索条数:默认10**/ @Min(value = 0, message = "limit must greater than or equal to 0") - @ApiModelProperty(value="最大搜索条数:默认5", position = 5, required = false) + @ApiModelProperty(value="最大搜索条数:默认5", position = 6, required = false) private Integer limit; /**对输入图像中多少个人脸进行检索比对**/ @Min(value = 0, message = "maxFaceNum must greater than or equal to 0") - @ApiModelProperty(value="对输入图像中多少个人脸进行检索比对:默认5", position = 6, required = false) + @ApiModelProperty(value="对输入图像中多少个人脸进行检索比对:默认5", position = 7, required = false) private Integer maxFaceNum; /** @@ -95,6 +98,21 @@ public class FaceSearchReqVo extends BaseVo { return this; } + public SearchAlgorithm getAlgorithm() { + if(null != algorithm && !algorithm.isEmpty()){ + return SearchAlgorithm.valueOf(this.algorithm); + }else{ + return SearchAlgorithm.COSINESIMIL; + } + } + + public FaceSearchReqVo setAlgorithm(String algorithm) { + if(null != algorithm){ + this.algorithm = algorithm; + } + return this; + } + public Integer getLimit() { return limit; } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/domain/request/SearchAlgorithm.java b/face-search-server/src/main/java/com/visual/face/search/server/domain/request/SearchAlgorithm.java new file mode 100644 index 0000000..151ce69 --- /dev/null +++ b/face-search-server/src/main/java/com/visual/face/search/server/domain/request/SearchAlgorithm.java @@ -0,0 +1,22 @@ +package com.visual.face.search.server.domain.request; + +public enum SearchAlgorithm { + L1("l1"), + L2("l2"), + LINF("linf"), + HAMMINGBIT("innerproduct"), + INNERPRODUCT("hammingbit"), + COSINESIMIL("cosinesimil"); + + + private String algorithm; + + SearchAlgorithm(String algorithm){ + this.algorithm = algorithm; + } + + public String algorithm(){ + return this.algorithm; + } + +} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/api/SearchEngine.java b/face-search-server/src/main/java/com/visual/face/search/server/engine/api/SearchEngine.java deleted file mode 100755 index 9674681..0000000 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/api/SearchEngine.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.visual.face.search.server.engine.api; - -import com.visual.face.search.server.engine.model.MapParam; -import com.visual.face.search.server.engine.model.SearchResponse; - -import java.util.List; - -public interface SearchEngine { - - public Object getEngine(); - - public boolean exist(String collectionName); - - public boolean dropCollection(String collectionName); - - public boolean createCollection(String collectionName, MapParam param); - - public boolean insertVector(String collectionName, Long keyId, String faceID, float[] vectors); - - public boolean deleteVectorByKey(String collectionName, Long keyId); - - public boolean deleteVectorByKey(String collectionName, List keyIds); - - public SearchResponse search(String collectionName, float[][] features, int topK); - -} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/conf/Constant.java b/face-search-server/src/main/java/com/visual/face/search/server/engine/conf/Constant.java deleted file mode 100755 index 9745c37..0000000 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/conf/Constant.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.visual.face.search.server.engine.conf; - -public class Constant { - - public final static String ParamKeyShardsNum = "shardsNum"; - public final static String ParamKeyMaxDocsPerSegment = "maxDocsPerSegment"; - - public final static String ColumnPrimaryKey = "id"; - public final static String ColumnNameFaceId = "face_id"; - public final static String ColumnNameFaceScore = "face_score"; - public final static String ColumnNameFaceIndex = "face_index"; - public final static String ColumnNameFaceVector = "face_vector"; - public final static String ColumnNameSampleId = "sample_id"; - -} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/impl/MilvusSearchEngine.java b/face-search-server/src/main/java/com/visual/face/search/server/engine/impl/MilvusSearchEngine.java deleted file mode 100755 index fb43aff..0000000 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/impl/MilvusSearchEngine.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.visual.face.search.server.engine.impl; - -import com.visual.face.search.server.engine.api.SearchEngine; -import com.visual.face.search.server.engine.conf.Constant; -import com.visual.face.search.server.engine.model.*; -import com.visual.face.search.server.engine.utils.VectorUtils; -import io.milvus.response.SearchResultsWrapper; -import io.milvus.client.MilvusServiceClient; -import io.milvus.grpc.*; -import io.milvus.param.*; -import io.milvus.param.collection.*; -import io.milvus.param.dml.DeleteParam; -import io.milvus.param.dml.InsertParam; -import io.milvus.param.dml.SearchParam; -import io.milvus.param.index.CreateIndexParam; - -import java.util.*; - -public class MilvusSearchEngine implements SearchEngine { - - private static final Integer SUCCESS_STATUE = 0; - - private MilvusServiceClient client; - - public MilvusSearchEngine(MilvusServiceClient client) { - this.client = client; - } - - @Override - public Object getEngine(){ - return this.client; - } - - @Override - public boolean exist(String collectionName) { - HasCollectionParam param = HasCollectionParam.newBuilder().withCollectionName(collectionName).build(); - R response = this.client.hasCollection(param); - if(SUCCESS_STATUE.equals(response.getStatus())){ - return response.getData(); - }else{ - throw new RuntimeException(response.getMessage(), response.getException()); - } - } - - @Override - public boolean dropCollection(String collectionName) { - DropCollectionParam param = DropCollectionParam.newBuilder().withCollectionName(collectionName).build(); - R response = this.client.dropCollection(param); - if(SUCCESS_STATUE.equals(response.getStatus())){ - return true; - }else{ - throw new RuntimeException(response.getMessage(), response.getException()); - } - } - - @Override - public boolean createCollection(String collectionName, MapParam param) { - FieldType keyFieldType = FieldType.newBuilder() - .withName(Constant.ColumnPrimaryKey) - .withDescription("id") - .withDataType(DataType.Int64) - .withPrimaryKey(true) - .withAutoID(false) - .build(); - - FieldType indexFieldType = FieldType.newBuilder() - .withName(Constant.ColumnNameFaceIndex) - .withDescription("face vector") - .withDataType(DataType.FloatVector) - .withDimension(512) - .build(); - - CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder() - .withCollectionName(collectionName) - .withDescription(collectionName) - .addFieldType(keyFieldType) - .addFieldType(indexFieldType) - .withShardsNum(param.getShardsNum()) - .build(); - - CreateIndexParam indexParam = CreateIndexParam.newBuilder() - .withCollectionName(collectionName) - .withFieldName(Constant.ColumnNameFaceIndex) - .withIndexType(IndexType.IVF_FLAT) - .withMetricType(MetricType.L2) - .withExtraParam("{\"nlist\":128}") - .withSyncMode(Boolean.TRUE) - .build(); - - LoadCollectionParam loadParam = LoadCollectionParam.newBuilder() - .withCollectionName(collectionName) - .build(); - - R response = this.client.createCollection(createCollectionReq); - if(SUCCESS_STATUE.equals(response.getStatus())){ - R indexResponse = this.client.createIndex(indexParam); - if(SUCCESS_STATUE.equals(indexResponse.getStatus())){ - R loadResponse = this.client.loadCollection(loadParam); - if(SUCCESS_STATUE.equals(loadResponse.getStatus())){ - return true; - }else{ - this.dropCollection(collectionName); - return false; - } - }else{ - this.dropCollection(collectionName); - return false; - } - }else{ - throw new RuntimeException(response.getMessage(), response.getException()); - } - } - - @Override - public boolean insertVector(String collectionName, Long keyId, String faceID, float[] vectors) { - List fields = new ArrayList<>(); - fields.add(new InsertParam.Field(Constant.ColumnPrimaryKey, DataType.Int64, Collections.singletonList(keyId))); - fields.add(new InsertParam.Field(Constant.ColumnNameFaceIndex, DataType.FloatVector, Collections.singletonList(VectorUtils.convertVector(vectors)))); - - InsertParam insertParam = InsertParam.newBuilder() - .withCollectionName(collectionName) - .withFields(fields) - .build(); - - R response = this.client.insert(insertParam); - if(SUCCESS_STATUE.equals(response.getStatus())){ - return true; - }else{ - throw new RuntimeException(response.getMessage(), response.getException()); - } - } - - @Override - public boolean deleteVectorByKey(String collectionName, Long keyId) { - String deleteExpr = Constant.ColumnPrimaryKey + " in " + "[" + keyId + "]"; - DeleteParam build = DeleteParam.newBuilder() - .withCollectionName(collectionName) - .withExpr(deleteExpr) - .build(); - - R response = this.client.delete(build); - if(SUCCESS_STATUE.equals(response.getStatus())){ - return true; - }else{ - throw new RuntimeException(response.getMessage(), response.getException()); - } - } - - @Override - public boolean deleteVectorByKey(String collectionName, List keyIds) { - String deleteExpr = Constant.ColumnPrimaryKey + " in " + keyIds.toString(); - DeleteParam build = DeleteParam.newBuilder() - .withCollectionName(collectionName) - .withExpr(deleteExpr) - .build(); - - R response = this.client.delete(build); - if(SUCCESS_STATUE.equals(response.getStatus())){ - return true; - }else{ - throw new RuntimeException(response.getMessage(), response.getException()); - } - } - - @Override - public SearchResponse search(String collectionName, float[][] features, int topK) { - SearchParam searchParam = SearchParam.newBuilder() - .withCollectionName(collectionName) - .withMetricType(MetricType.L2) - .withParams("{\"nprobe\": 128}") - .withOutFields(Collections.singletonList(Constant.ColumnPrimaryKey)) - .withTopK(topK) - .withVectors(VectorUtils.convertVector(features)) - .withVectorFieldName(Constant.ColumnNameFaceIndex) - .build(); - R response = this.client.search(searchParam); - if(SUCCESS_STATUE.equals(response.getStatus())){ - SearchStatus status = SearchStatus.build(0, "success"); - List result = new ArrayList<>(); - if(response.getData().getResults().hasIds()){ - SearchResultsWrapper wrapper = new SearchResultsWrapper(response.getData().getResults()); - for (int i = 0; i < features.length; ++i) { - List documents = new ArrayList<>(); - List scores = wrapper.getIDScore(i); - for(SearchResultsWrapper.IDScore scoreId : scores){ - long primaryKey = scoreId.getLongID(); - float score = scoreId.getScore(); - documents.add(SearchDocument.build(primaryKey, score, null)); - } - result.add(SearchResult.build(documents)); - } - } - return SearchResponse.build(status, result); - }else{ - throw new RuntimeException(response.getMessage(), response.getException()); - } - } - -} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/impl/ProximaSearchEngine.java b/face-search-server/src/main/java/com/visual/face/search/server/engine/impl/ProximaSearchEngine.java deleted file mode 100755 index c71de68..0000000 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/impl/ProximaSearchEngine.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.visual.face.search.server.engine.impl; - -import com.alibaba.proxima.be.client.*; -import com.visual.face.search.server.engine.api.SearchEngine; -import com.visual.face.search.server.engine.conf.Constant; -import com.visual.face.search.server.engine.model.*; - -import java.util.*; - -public class ProximaSearchEngine implements SearchEngine { - - private ProximaSearchClient client; - - /** - * 构造获取连接对象 - * @param client - */ - public ProximaSearchEngine(ProximaSearchClient client){ - this.client = client; - } - - @Override - public Object getEngine(){ - return this.client; - } - - @Override - public boolean exist(String collectionName) { - DescribeCollectionResponse response = client.describeCollection(collectionName); - return response.ok(); - } - - @Override - public boolean dropCollection(String collectionName) { - if(exist(collectionName)){ - Status status = client.dropCollection(collectionName); - return status.ok(); - } - return true; - } - - @Override - public boolean createCollection(String collectionName, MapParam param) { - Long maxDocsPerSegment = param.getMaxDocsPerSegment(); - CollectionConfig config = CollectionConfig.newBuilder() - .withCollectionName(collectionName) - .withMaxDocsPerSegment(maxDocsPerSegment) - .withForwardColumnNames(Arrays.asList(Constant.ColumnNameFaceId)) - .addIndexColumnParam(Constant.ColumnNameFaceIndex, DataType.VECTOR_FP32, 512).build(); - Status status = client.createCollection(config); - if(status.ok()){ - return true; - }else{ - throw new RuntimeException(status.getReason()); - } - } - - @Override - public boolean insertVector(String collectionName, Long keyId, String faceID, float[] vectors) { - WriteRequest.Row insertRow = WriteRequest.Row.newBuilder() - .withPrimaryKey(keyId) - .addIndexValue(vectors) - .addForwardValue(faceID) - .withOperationType(WriteRequest.OperationType.INSERT) - .build(); - WriteRequest writeRequest = WriteRequest.newBuilder() - .withCollectionName(collectionName) - .withForwardColumnList(Collections.singletonList(Constant.ColumnNameFaceId)) - .addIndexColumnMeta(Constant.ColumnNameFaceIndex, DataType.VECTOR_FP32, 512) - .addRow(insertRow) - .build(); - Status status = client.write(writeRequest); - if(status.ok()){ - return true; - }else{ - throw new RuntimeException(status.getReason()); - } - } - - @Override - public boolean deleteVectorByKey(String collectionName, Long keyId) { - WriteRequest.Row deleteRow = WriteRequest.Row.newBuilder() - .withPrimaryKey(keyId) - .withOperationType(WriteRequest.OperationType.DELETE) - .build(); - WriteRequest writeRequest = WriteRequest.newBuilder() - .withCollectionName(collectionName) - .withForwardColumnList(Collections.singletonList(Constant.ColumnNameFaceId)) - .addIndexColumnMeta(Constant.ColumnNameFaceIndex, DataType.VECTOR_FP32, 512) - .addRow(deleteRow) - .build(); - Status status = client.write(writeRequest); - if(status.ok()){ - return true; - }else{ - throw new RuntimeException(status.getReason()); - } - } - - @Override - public boolean deleteVectorByKey(String collectionName, List keyIds) { - if(null == keyIds || keyIds.isEmpty()){ - return false; - } - List deleteIds = new ArrayList<>(); - for(Long keyId : keyIds){ - GetDocumentRequest r = GetDocumentRequest.newBuilder().withCollectionName(collectionName).withPrimaryKey(keyId).build(); - GetDocumentResponse p = client.getDocumentByKey(r); - if(p.ok() && p.getDocument().getPrimaryKey() == keyId){ - deleteIds.add(keyId); - } - } - if(deleteIds.isEmpty()){ - return true; - } - WriteRequest.Builder builder = WriteRequest.newBuilder().withCollectionName(collectionName); - for(Long keyId : deleteIds){ - WriteRequest.Row deleteRow = WriteRequest.Row.newBuilder().withPrimaryKey(keyId).withOperationType(WriteRequest.OperationType.DELETE).build(); - builder.addRow(deleteRow); - } - Status status = client.write(builder.build()); - if(status.ok()){ - return true; - }else{ - throw new RuntimeException(status.getReason()); - } - } - - @Override - public SearchResponse search(String collectionName, float[][] features, int topK) { - QueryRequest queryRequest = QueryRequest.newBuilder() - .withCollectionName(collectionName) - .withKnnQueryParam( - QueryRequest.KnnQueryParam.newBuilder() - .withColumnName(Constant.ColumnNameFaceIndex) - .withTopk(topK) - .withFeatures(features) - .build()) - .build(); - //搜索向量 - QueryResponse queryResponse; - try { - queryResponse = client.query(queryRequest); - } catch (Exception e) { - return SearchResponse.build(SearchStatus.build(1, e.getMessage()), null); - } - //搜索失败 - if(!queryResponse.ok()){ - return SearchResponse.build(SearchStatus.build(1, "response status is error"), null); - } - //转换对象 - List result = new ArrayList<>(); - SearchStatus status = SearchStatus.build(0, "success"); - for (int i = 0; i < queryResponse.getQueryResultCount(); ++i) { - List documents = new ArrayList<>(); - QueryResult queryResult = queryResponse.getQueryResult(i); - for (int d = 0; d < queryResult.getDocumentCount(); ++d) { - Document document = queryResult.getDocument(d); - long primaryKey = document.getPrimaryKey(); - float score = document.getScore(); - Set forwardKeys = document.getForwardKeySet(); - String faceId = null; - if(forwardKeys.contains(Constant.ColumnNameFaceId)){ - faceId = document.getForwardValue(Constant.ColumnNameFaceId).getStringValue(); - } - documents.add(SearchDocument.build(primaryKey, score, faceId)); - } - result.add(SearchResult.build(documents)); - } - //返回 - return SearchResponse.build(status, result); - } - -} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchDocument.java b/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchDocument.java deleted file mode 100755 index 2b594b8..0000000 --- a/face-search-server/src/main/java/com/visual/face/search/server/engine/model/SearchDocument.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.visual.face.search.server.engine.model; - -public class SearchDocument { - private long primaryKey; - private float score; - private String faceId; - - public SearchDocument(){} - - public SearchDocument(long primaryKey, float score, String faceId) { - this.primaryKey = primaryKey; - this.score = score; - this.faceId = faceId; - } - - public static SearchDocument build(long primaryKey, float score, String faceId){ - return new SearchDocument(primaryKey, score, faceId); - } - - public long getPrimaryKey() { - return primaryKey; - } - - public void setPrimaryKey(long primaryKey) { - this.primaryKey = primaryKey; - } - - public float getScore() { - return score; - } - - public void setScore(float score) { - this.score = score; - } - - public String getFaceId() { - return faceId; - } - - public void setFaceId(String faceId) { - this.faceId = faceId; - } -} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/mapper/CollectMapper.java b/face-search-server/src/main/java/com/visual/face/search/server/mapper/CollectMapper.java index 9feaa7c..1a32968 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/mapper/CollectMapper.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/mapper/CollectMapper.java @@ -44,7 +44,7 @@ public interface CollectMapper { "and namespace = #{namespace,jdbcType=VARCHAR} ", "and collection = #{collection,jdbcType=VARCHAR}" }) - int deleteByName(@Param("namespace")String namespace, @Param("collection")String collection); + int deleteByName(@Param("namespace") String namespace, @Param("collection") String collection); @Select({ "select", @@ -72,7 +72,7 @@ public interface CollectMapper { @Result(column="update_time", property="updateTime", jdbcType=JdbcType.TIMESTAMP), @Result(column="deleted", property="deleted", jdbcType=JdbcType.CHAR) }) - Collection selectByName(@Param("namespace")String namespace, @Param("collection")String collection); + Collection selectByName(@Param("namespace") String namespace, @Param("collection") String collection); @Select({ "select", @@ -98,5 +98,5 @@ public interface CollectMapper { @Result(column="update_time", property="updateTime", jdbcType=JdbcType.TIMESTAMP), @Result(column="deleted", property="deleted", jdbcType=JdbcType.CHAR) }) - List selectByNamespace(@Param("namespace")String namespace); + List selectByNamespace(@Param("namespace") String namespace); } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/mapper/FaceDataMapper.java b/face-search-server/src/main/java/com/visual/face/search/server/mapper/FaceDataMapper.java index baa2c1e..404b3a9 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/mapper/FaceDataMapper.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/mapper/FaceDataMapper.java @@ -61,25 +61,25 @@ public interface FaceDataMapper { int deleteById(@Param("table") String table, @Param("id") Long id); @Delete({"delete from ${table} where sample_id = #{sampleId,jdbcType=VARCHAR}"}) - int deleteBySampleId(@Param("table") String table, @Param("sampleId")String sampleId); + int deleteBySampleId(@Param("table") String table, @Param("sampleId") String sampleId); @Delete({"delete from ${table} where sample_id = #{sampleId,jdbcType=VARCHAR} and face_id=#{faceId,jdbcType=VARCHAR}"}) - int deleteByFaceId(@Param("table") String table, @Param("sampleId")String sampleId, @Param("faceId")String faceId); + int deleteByFaceId(@Param("table") String table, @Param("sampleId") String sampleId, @Param("faceId") String faceId); @Select({"select count(1) from ${table} where sample_id = '${sampleId}'"}) - long countBySampleId(@Param("table") String table, @Param("sampleId")String sampleId); + long countBySampleId(@Param("table") String table, @Param("sampleId") String sampleId); @Select({"select count(1) from ${table} where sample_id = '${sampleId}' and face_id=#{faceId,jdbcType=VARCHAR}"}) - long count(@Param("table") String table, @Param("sampleId")String sampleId, @Param("faceId")String faceId); + long count(@Param("table") String table, @Param("sampleId") String sampleId, @Param("faceId") String faceId); @Select({"select id from ${table} where sample_id = '${sampleId}' and face_id=#{faceId,jdbcType=VARCHAR}"}) - Long getIdByFaceId(@Param("table") String table, @Param("sampleId")String sampleId, @Param("faceId")String faceId); + Long getIdByFaceId(@Param("table") String table, @Param("sampleId") String sampleId, @Param("faceId") String faceId); - @Select({"select id from ${table} where sample_id=#{sampleId,jdbcType=VARCHAR}"}) - List getIdBySampleId(@Param("table") String table, @Param("sampleId")String sampleId); + @Select({"select face_id from ${table} where sample_id=#{sampleId,jdbcType=VARCHAR}"}) + List getFaceIdBySampleId(@Param("table") String table, @Param("sampleId") String sampleId); @Select({"select * from ${table} where sample_id = #{sampleId,jdbcType=VARCHAR}"}) - List> getBySampleId(@Param("table") String table, @Param("sampleId")String sampleId); + List> getBySampleId(@Param("table") String table, @Param("sampleId") String sampleId); @Select({ "" }) - List> getBySampleIds(@Param("table") String table, @Param("sampleIds")List sampleIds); + List> getBySampleIds(@Param("table") String table, @Param("sampleIds") List sampleIds); @Select({"select * from ${table} where sample_id = #{sampleId,jdbcType=VARCHAR} and face_id=#{faceId,jdbcType=VARCHAR}"}) - Map getByFaceId(@Param("table") String table, @Param("sampleId")String sampleId, @Param("faceId")String faceId); + Map getByFaceId(@Param("table") String table, @Param("sampleId") String sampleId, @Param("faceId") String faceId); @Select({ "" }) - List> getByFaceIds(@Param("table") String table, @Param("faceIds")List faceIds); + List> getByFaceIds(@Param("table") String table, @Param("columns") List columns, @Param("faceIds") List faceIds); @Select({ "" }) - List> getByPrimaryIds(@Param("table") String table, @Param("keyIds")List keyIds); + List> getByPrimaryIds(@Param("table") String table, @Param("keyIds") List keyIds); } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/mapper/ImageDataMapper.java b/face-search-server/src/main/java/com/visual/face/search/server/mapper/ImageDataMapper.java index a0d15e5..3372537 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/mapper/ImageDataMapper.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/mapper/ImageDataMapper.java @@ -18,6 +18,6 @@ public interface ImageDataMapper { "#{image.createTime,jdbcType=TIMESTAMP}, #{image.modifyTime,jdbcType=TIMESTAMP})" }) @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="id", before=false, resultType=Long.class) - int insert(@Param("table")String table, @Param("image")ImageData record); + int insert(@Param("table") String table, @Param("image") ImageData record); } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/mapper/OperateTableMapper.java b/face-search-server/src/main/java/com/visual/face/search/server/mapper/OperateTableMapper.java index 277c2d7..fe29400 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/mapper/OperateTableMapper.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/mapper/OperateTableMapper.java @@ -12,10 +12,10 @@ import com.visual.face.search.server.model.TableColumn; public interface OperateTableMapper { @Select({"SHOW TABLES LIKE '${table}'"}) - String showTable(@Param("table")String table); + String showTable(@Param("table") String table); @Update({ "DROP TABLE IF EXISTS ${table}"}) - int dropTable(@Param("table")String table); + int dropTable(@Param("table") String table); @Update({ "" }) - int update(@Param("table") String table, @Param("sampleId")String sampleId, @Param("columnValues") List columnValues); + int update(@Param("table") String table, @Param("sampleId") String sampleId, @Param("columnValues") List columnValues); @Delete({"delete from ${table} where sample_id = #{sampleId,jdbcType=VARCHAR}",}) - int delete(@Param("table") String table, @Param("sampleId")String sampleId); + int delete(@Param("table") String table, @Param("sampleId") String sampleId); @Select({"select * from ${table} where sample_id = #{sampleId,jdbcType=VARCHAR}"}) - Map getBySampleId(@Param("table") String table, @Param("sampleId")String sampleId); + Map getBySampleId(@Param("table") String table, @Param("sampleId") String sampleId); @Select({"select * from ${table} order by id ${order} limit ${offset}, ${limit}"}) - List> getBySampleList(@Param("table") String table, @Param("offset")Integer offset, @Param("limit")Integer limit, @Param("order")String order); + List> getBySampleList(@Param("table") String table, @Param("offset") Integer offset, @Param("limit") Integer limit, @Param("order") String order); @Select({ "" }) - List> getBySampleIds(@Param("table") String table, @Param("sampleIds")List sampleIds); + List> getBySampleIds(@Param("table") String table, @Param("sampleIds") List sampleIds); } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/scheduler/FlushScheduler.java b/face-search-server/src/main/java/com/visual/face/search/server/scheduler/FlushScheduler.java index 8e5ff48..13b5957 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/scheduler/FlushScheduler.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/scheduler/FlushScheduler.java @@ -1,79 +1,79 @@ -package com.visual.face.search.server.scheduler; - -import io.milvus.param.R; -import io.milvus.grpc.FlushResponse; -import io.milvus.client.MilvusServiceClient; -import io.milvus.param.collection.FlushParam; -import io.milvus.param.collection.HasCollectionParam; -import com.visual.face.search.server.utils.VTableCache; -import com.alibaba.proxima.be.client.ProximaSearchClient; -import com.visual.face.search.server.engine.api.SearchEngine; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import javax.annotation.Resource; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.scheduling.annotation.Scheduled; - -/** - * 用于刷新数据,Milvus不刷新数据可能导致数据丢失 - */ -@Component -public class FlushScheduler { - - private static final Integer SUCCESS_STATUE = 0; - public Logger logger = LoggerFactory.getLogger(getClass()); - - @Resource - private SearchEngine searchEngine; - @Value("${visual.scheduler.flush.enable:true}") - private boolean enable; - - @Scheduled(fixedDelayString = "${visual.scheduler.flush.interval:300000}") - public void flush(){ - if(enable){ - Object client = searchEngine.getEngine(); - if(client instanceof MilvusServiceClient){ - this.flushMilvus((MilvusServiceClient) client); - }else if(client instanceof ProximaSearchClient){ - this.flushProxima((ProximaSearchClient) client); - } - } - } - - /** - * 刷新数据到数据库中 - * @param client - */ - private void flushMilvus(MilvusServiceClient client){ - VTableCache.getVectorTables().forEach((vectorTable) -> { - try { - HasCollectionParam requestParam = HasCollectionParam.newBuilder().withCollectionName(vectorTable).build(); - boolean exist = client.hasCollection(requestParam).getData(); - if(exist){ - FlushParam flushParam = FlushParam.newBuilder().addCollectionName(vectorTable).build(); - R response = client.flush(flushParam); - if(SUCCESS_STATUE.equals(response.getStatus())){ - VTableCache.remove(vectorTable); - logger.info("flushMilvus success: table is {}", vectorTable); - }else{ - throw new RuntimeException("FlushResponse Error"); - } - } - }catch (Exception e){ - logger.error("flushMilvus error:", e); - } - }); - } - - /** - * 刷新数据到数据库中 - * @param client - */ - private void flushProxima(ProximaSearchClient client){ - - } - -} +//package com.visual.face.search.server.scheduler; +// +//import io.milvus.param.R; +//import io.milvus.grpc.FlushResponse; +//import io.milvus.client.MilvusServiceClient; +//import io.milvus.param.collection.FlushParam; +//import io.milvus.param.collection.HasCollectionParam; +//import com.visual.face.search.server.utils.VTableCache; +//import com.alibaba.proxima.be.client.ProximaSearchClient; +//import com.visual.face.search.server.engine.api.SearchEngine; +// +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import javax.annotation.Resource; +// +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.stereotype.Component; +//import org.springframework.scheduling.annotation.Scheduled; +// +///** +// * 用于刷新数据,Milvus不刷新数据可能导致数据丢失 +// */ +//@Component +//public class FlushScheduler { +// +// private static final Integer SUCCESS_STATUE = 0; +// public Logger logger = LoggerFactory.getLogger(getClass()); +// +// @Resource +// private SearchEngine searchEngine; +// @Value("${visual.scheduler.flush.enable:true}") +// private boolean enable; +// +// @Scheduled(fixedDelayString = "${visual.scheduler.flush.interval:300000}") +// public void flush(){ +// if(enable){ +// Object client = searchEngine.getEngine(); +// if(client instanceof MilvusServiceClient){ +// this.flushMilvus((MilvusServiceClient) client); +// }else if(client instanceof ProximaSearchClient){ +// this.flushProxima((ProximaSearchClient) client); +// } +// } +// } +// +// /** +// * 刷新数据到数据库中 +// * @param client +// */ +// private void flushMilvus(MilvusServiceClient client){ +// VTableCache.getVectorTables().forEach((vectorTable) -> { +// try { +// HasCollectionParam requestParam = HasCollectionParam.newBuilder().withCollectionName(vectorTable).build(); +// boolean exist = client.hasCollection(requestParam).getData(); +// if(exist){ +// FlushParam flushParam = FlushParam.newBuilder().addCollectionName(vectorTable).build(); +// R response = client.flush(flushParam); +// if(SUCCESS_STATUE.equals(response.getStatus())){ +// VTableCache.remove(vectorTable); +// logger.info("flushMilvus success: table is {}", vectorTable); +// }else{ +// throw new RuntimeException("FlushResponse Error"); +// } +// } +// }catch (Exception e){ +// logger.error("flushMilvus error:", e); +// } +// }); +// } +// +// /** +// * 刷新数据到数据库中 +// * @param client +// */ +// private void flushProxima(ProximaSearchClient client){ +// +// } +// +//} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/CollectServiceImpl.java b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/CollectServiceImpl.java index 45b4283..7c5f6fd 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/CollectServiceImpl.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/CollectServiceImpl.java @@ -5,10 +5,9 @@ import com.visual.face.search.core.utils.JsonUtil; import com.visual.face.search.core.utils.ThreadUtil; import com.visual.face.search.server.domain.request.CollectReqVo; import com.visual.face.search.server.domain.response.CollectRepVo; -import com.visual.face.search.server.engine.api.SearchEngine; -import com.visual.face.search.server.engine.conf.Constant; -import com.visual.face.search.server.engine.model.MapParam; -//import com.visual.face.search.server.mapper.CollectMapper; +import com.visual.face.search.engine.api.SearchEngine; +import com.visual.face.search.engine.conf.Constant; +import com.visual.face.search.engine.model.MapParam; import com.visual.face.search.server.mapper.CollectMapper; import com.visual.face.search.server.model.Collection; import com.visual.face.search.server.service.api.CollectService; @@ -21,12 +20,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; -import org.springframework.util.DigestUtils; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; -import java.util.UUID; @Service("visualCollectService") @@ -91,8 +88,8 @@ public class CollectServiceImpl extends BaseService implements CollectService { } //创建人脸向量库 MapParam param = MapParam.build() - .put(Constant.ParamKeyShardsNum, collect.getShardsNum()) - .put(Constant.ParamKeyMaxDocsPerSegment, collect.getMaxDocsPerSegment()); + .put(Constant.IndexShardsNum, collect.getShardsNum()) + .put(Constant.IndexReplicasNum, collect.getReplicasNum()); boolean createVectorFlag = searchEngine.createCollection(vectorTableName, param); if(!createVectorFlag){ throw new RuntimeException("create vector table error"); diff --git a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceDataServiceImpl.java b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceDataServiceImpl.java index 0fc53af..01a23c2 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceDataServiceImpl.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceDataServiceImpl.java @@ -1,12 +1,13 @@ package com.visual.face.search.server.service.impl; +import com.visual.face.search.engine.api.SearchEngine; 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.utils.JsonUtil; -import com.visual.face.search.core.utils.Similarity; +import com.visual.face.search.server.domain.request.SearchAlgorithm; import com.visual.face.search.server.domain.storage.StorageDataInfo; import com.visual.face.search.server.domain.storage.StorageImageInfo; import com.visual.face.search.server.domain.storage.StorageInfo; @@ -14,11 +15,6 @@ import com.visual.face.search.server.domain.extend.FieldKeyValue; import com.visual.face.search.server.domain.extend.FieldKeyValues; import com.visual.face.search.server.domain.request.FaceDataReqVo; import com.visual.face.search.server.domain.response.FaceDataRepVo; -import com.visual.face.search.server.engine.api.SearchEngine; -import com.visual.face.search.server.engine.conf.Constant; -import com.visual.face.search.server.engine.model.SearchDocument; -import com.visual.face.search.server.engine.model.SearchResponse; -import com.visual.face.search.server.engine.model.SearchResult; import com.visual.face.search.server.mapper.CollectMapper; import com.visual.face.search.server.mapper.FaceDataMapper; import com.visual.face.search.server.mapper.SampleDataMapper; @@ -33,8 +29,6 @@ import com.visual.face.search.server.service.base.BaseService; import com.visual.face.search.server.utils.CollectionUtil; import com.visual.face.search.server.utils.TableUtils; import com.visual.face.search.server.utils.VTableCache; -import com.visual.face.search.server.utils.ValueUtil; -import org.apache.commons.collections4.MapUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -101,45 +95,18 @@ public class FaceDataServiceImpl extends BaseService implements FaceDataService float[] embeds = faceInfo.embedding.embeds; //当前样本的人脸相似度的最小阈值 if(null != face.getMinConfidenceThresholdWithThisSample() && face.getMinConfidenceThresholdWithThisSample() > 0){ - List> faces = faceDataMapper.getBySampleId(collection.getFaceTable(), face.getSampleId()); - for(Map item : faces){ - String faceVectorStr = MapUtils.getString(item, Constant.ColumnNameFaceVector); - float[] faceVector = ValueUtil.convertVector(faceVectorStr); - float simVal = Similarity.cosineSimilarityNorm(embeds, faceVector); - float confidence = (float) Math.floor(simVal * 10000)/100; - if(confidence < face.getMinConfidenceThresholdWithThisSample()){ - throw new RuntimeException("this face confidence is less than minConfidenceThresholdWithThisSample,confidence="+confidence+",threshold="+face.getMinConfidenceThresholdWithThisSample()); - } + float minScore = this.searchEngine.searchMinScoreBySampleId(collection.getVectorTable(), face.getSampleId(), embeds, SearchAlgorithm.COSINESIMIL.algorithm()); + float confidence = (float) Math.floor(minScore * 10000)/100; + if(confidence < face.getMinConfidenceThresholdWithThisSample()){ + throw new RuntimeException("this face confidence is less than minConfidenceThresholdWithThisSample,confidence="+confidence+",threshold="+face.getMinConfidenceThresholdWithThisSample()); } } //当前样本与其他样本的人脸相似度的最大阈值 if(null != face.getMaxConfidenceThresholdWithOtherSample() && face.getMaxConfidenceThresholdWithOtherSample() > 0){ - //查询 - List otherFaceIds = new ArrayList<>(); - List faceIds = faceDataMapper.getIdBySampleId(collection.getFaceTable(), face.getSampleId()); - int topK = faceIds.size() + 2; - float [][] vectors = new float[1][]; vectors[0] = embeds; - SearchResponse response = searchEngine.search(collection.getVectorTable(), vectors, topK); - if(response.getStatus().ok()){ - for(SearchResult result : response.getResult()){ - for(SearchDocument document : result.getDocuments()){ - if(!faceIds.contains(document.getPrimaryKey())){ - otherFaceIds.add(document.getPrimaryKey()); - } - } - } - } - if(!otherFaceIds.isEmpty()){ - List> faces = faceDataMapper.getByPrimaryIds(collection.getFaceTable(), otherFaceIds); - for(Map item : faces){ - String faceVectorStr = MapUtils.getString(item, Constant.ColumnNameFaceVector); - float[] faceVector = ValueUtil.convertVector(faceVectorStr); - float simVal = Similarity.cosineSimilarityNorm(embeds, faceVector); - float confidence = (float) Math.floor(simVal * 10000)/100; - if(confidence > face.getMaxConfidenceThresholdWithOtherSample()){ - throw new RuntimeException("this face confidence is gather than maxConfidenceThresholdWithOtherSample,confidence="+confidence+",threshold="+face.getMaxConfidenceThresholdWithOtherSample()); - } - } + float minScore = this.searchEngine.searchMaxScoreBySampleId(collection.getVectorTable(), face.getSampleId(), embeds, SearchAlgorithm.COSINESIMIL.algorithm()); + float confidence = (float) Math.floor(minScore * 10000)/100; + if(confidence > face.getMaxConfidenceThresholdWithOtherSample()){ + throw new RuntimeException("this face confidence is gather than maxConfidenceThresholdWithOtherSample,confidence="+confidence+",threshold="+face.getMaxConfidenceThresholdWithOtherSample()); } } //保存图片信息并获取图片存储 @@ -188,7 +155,7 @@ public class FaceDataServiceImpl extends BaseService implements FaceDataService throw new RuntimeException("create face error"); } //写入数据到人脸向量库 - boolean flag1 = searchEngine.insertVector(collection.getVectorTable(), facePo.getId(), faceId, embeds); + boolean flag1 = searchEngine.insertVector(collection.getVectorTable(), face.getSampleId(), faceId, embeds); if(!flag1){ throw new RuntimeException("create face vector error"); } @@ -218,7 +185,7 @@ public class FaceDataServiceImpl extends BaseService implements FaceDataService throw new RuntimeException("face id is not exist"); } //删除向量 - boolean delete = searchEngine.deleteVectorByKey(collection.getVectorTable(), keyId); + boolean delete = searchEngine.deleteVectorByKey(collection.getVectorTable(), faceId); if(!delete){ throw new RuntimeException("delete face vector error"); } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceSearchServiceImpl.java b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceSearchServiceImpl.java index aaca9d8..a6fcdd8 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceSearchServiceImpl.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceSearchServiceImpl.java @@ -12,12 +12,13 @@ import com.visual.face.search.core.utils.Similarity; import com.visual.face.search.server.domain.extend.FaceLocation; import com.visual.face.search.server.domain.extend.SampleFaceVo; import com.visual.face.search.server.domain.request.FaceSearchReqVo; +import com.visual.face.search.server.domain.request.SearchAlgorithm; import com.visual.face.search.server.domain.response.FaceSearchRepVo; -import com.visual.face.search.server.engine.api.SearchEngine; -import com.visual.face.search.server.engine.conf.Constant; -import com.visual.face.search.server.engine.model.SearchDocument; -import com.visual.face.search.server.engine.model.SearchResponse; -import com.visual.face.search.server.engine.model.SearchResult; +import com.visual.face.search.engine.api.SearchEngine; +import com.visual.face.search.engine.conf.Constant; +import com.visual.face.search.engine.model.SearchDocument; +import com.visual.face.search.engine.model.SearchResponse; +import com.visual.face.search.engine.model.SearchResult; import com.visual.face.search.server.mapper.CollectMapper; import com.visual.face.search.server.mapper.FaceDataMapper; import com.visual.face.search.server.mapper.SampleDataMapper; @@ -82,7 +83,7 @@ public class FaceSearchServiceImpl extends BaseService implements FaceSearchServ } //特征搜索 int topK = (null == search.getLimit() || search.getLimit() <= 0) ? 5 : search.getLimit(); - SearchResponse searchResponse =searchEngine.search(collection.getVectorTable(), vectors, topK); + SearchResponse searchResponse =searchEngine.search(collection.getVectorTable(), vectors, search.getAlgorithm().algorithm(), topK); if(!searchResponse.getStatus().ok()){ throw new RuntimeException(searchResponse.getStatus().getReason()); } @@ -106,33 +107,19 @@ public class FaceSearchServiceImpl extends BaseService implements FaceSearchServ return vos; } //获取关联数据ID - boolean needFixFaceId = false; - Set faceIds = new HashSet<>(); + Set faceIds = new HashSet<>(); for(SearchResult searchResult : result){ List documents = searchResult.getDocuments(); for(SearchDocument document : documents){ - faceIds.add(document.getPrimaryKey()); - if(null == document.getFaceId() || document.getFaceId().isEmpty()){ - needFixFaceId = true; - } + faceIds.add(document.getFaceId()); } } //查询数据 - List> faceList = faceDataMapper.getByPrimaryIds(collection.getFaceTable(), new ArrayList<>(faceIds)); + List> faceList = faceDataMapper.getByFaceIds(collection.getFaceTable(), ValueUtil.getAllFaceColumnNames(collection), new ArrayList<>(faceIds)); Set sampleIds = faceList.stream().map(item -> MapUtils.getString(item, Constant.ColumnNameSampleId)).collect(Collectors.toSet()); List> sampleList = sampleDataMapper.getBySampleIds(collection.getSampleTable(), new ArrayList<>(sampleIds)); Map> faceMapping = ValueUtil.mapping(faceList, Constant.ColumnNameFaceId); Map> sampleMapping = ValueUtil.mapping(sampleList, Constant.ColumnNameSampleId); - //补全结果数据中的FaceId。由于milvus不支持字符串结构,只会返回人脸数据的主键ID - if(needFixFaceId){ - Map mapping = ValueUtil.mapping(faceList, Constant.ColumnPrimaryKey, Constant.ColumnNameFaceId); - for(SearchResult searchResult : result){ - List documents = searchResult.getDocuments(); - for(SearchDocument document : documents){ - document.setFaceId(mapping.get(document.getPrimaryKey())); - } - } - } //构造返回结果 List vos = new ArrayList<>(); for(int i=0; i= search.getConfidenceThreshold()){ Map sample = sampleMapping.get(sampleId); SampleFaceVo faceVo = SampleFaceVo.build(); @@ -159,7 +148,6 @@ public class FaceSearchServiceImpl extends BaseService implements FaceSearchServ faceVo.setFaceId(document.getFaceId()); faceVo.setFaceScore(faceScore); faceVo.setConfidence(confidence); - faceVo.setDistance((float) Math.floor(document.getScore() * 10000) / 10000); faceVo.setFaceData(ValueUtil.getFieldKeyValues(face, ValueUtil.getFaceColumns(collection))); faceVo.setSampleData(ValueUtil.getFieldKeyValues(sample, ValueUtil.getSampleColumns(collection))); match.add(faceVo); diff --git a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/SampleDataServiceImpl.java b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/SampleDataServiceImpl.java index 3cce817..1b724fc 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/SampleDataServiceImpl.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/SampleDataServiceImpl.java @@ -1,12 +1,12 @@ package com.visual.face.search.server.service.impl; +import com.visual.face.search.engine.conf.Constant; +import com.visual.face.search.engine.api.SearchEngine; import com.visual.face.search.server.domain.extend.FieldKeyValue; import com.visual.face.search.server.domain.extend.FieldKeyValues; import com.visual.face.search.server.domain.extend.SimpleFaceVo; import com.visual.face.search.server.domain.request.SampleDataReqVo; import com.visual.face.search.server.domain.response.SampleDataRepVo; -import com.visual.face.search.server.engine.api.SearchEngine; -import com.visual.face.search.server.engine.conf.Constant; import com.visual.face.search.server.mapper.CollectMapper; import com.visual.face.search.server.mapper.FaceDataMapper; import com.visual.face.search.server.mapper.SampleDataMapper; @@ -21,7 +21,6 @@ import org.apache.commons.collections4.MapUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; - import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; @@ -128,7 +127,7 @@ public class SampleDataServiceImpl extends BaseService implements SampleDataServ throw new RuntimeException("sample_id is not exist"); } //删除向量数据 - List faceIds = faceDataMapper.getIdBySampleId(collection.getFaceTable(), sampleId); + List faceIds = faceDataMapper.getFaceIdBySampleId(collection.getFaceTable(), sampleId); searchEngine.deleteVectorByKey(collection.getVectorTable(), faceIds); //删除人脸数据 faceDataMapper.deleteBySampleId(collection.getFaceTable(), sampleId); diff --git a/face-search-server/src/main/java/com/visual/face/search/server/utils/SpringUtils.java b/face-search-server/src/main/java/com/visual/face/search/server/utils/SpringUtils.java index 8f0321f..b274edd 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/utils/SpringUtils.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/utils/SpringUtils.java @@ -39,7 +39,7 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC * * @param name * @return Object 一个以所给名字注册的bean的实例 - * @throws org.springframework.beans.BeansException + * @throws BeansException * */ @SuppressWarnings("unchecked") @@ -53,7 +53,7 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC * * @param clz * @return - * @throws org.springframework.beans.BeansException + * @throws BeansException * */ public static T getBean(Class clz) throws BeansException @@ -78,7 +78,7 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC * * @param name * @return boolean - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * @throws NoSuchBeanDefinitionException * */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException @@ -89,7 +89,7 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC /** * @param name * @return Class 注册对象的类型 - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * @throws NoSuchBeanDefinitionException * */ public static Class getType(String name) throws NoSuchBeanDefinitionException @@ -102,7 +102,7 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC * * @param name * @return - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * @throws NoSuchBeanDefinitionException * */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException diff --git a/face-search-server/src/main/java/com/visual/face/search/server/utils/ValueUtil.java b/face-search-server/src/main/java/com/visual/face/search/server/utils/ValueUtil.java index 07568bc..b842ef7 100755 --- a/face-search-server/src/main/java/com/visual/face/search/server/utils/ValueUtil.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/utils/ValueUtil.java @@ -1,9 +1,11 @@ package com.visual.face.search.server.utils; import com.visual.face.search.core.utils.JsonUtil; +import com.visual.face.search.engine.conf.Constant; import com.visual.face.search.server.domain.extend.FieldKeyValue; import com.visual.face.search.server.domain.extend.FieldKeyValues; import com.visual.face.search.server.domain.extend.FiledColumn; +import com.visual.face.search.server.domain.extend.FiledDataType; import com.visual.face.search.server.domain.response.CollectRepVo; import com.visual.face.search.server.model.Collection; import org.apache.commons.collections4.MapUtils; @@ -12,6 +14,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public class ValueUtil { @@ -25,6 +28,7 @@ public class ValueUtil { return new ArrayList<>(); } + public static List getSampleColumns(Collection collection){ if(null != collection.getSchemaInfo() && !collection.getSchemaInfo().isEmpty()){ CollectRepVo collectVo = JsonUtil.toEntity(collection.getSchemaInfo(), CollectRepVo.class); @@ -35,6 +39,19 @@ public class ValueUtil { return new ArrayList<>(); } + public static List getAllFaceColumnNames(Collection collection){ + List columns = getAllFaceColumns(collection); + return columns.stream().map(FiledColumn::getName).collect(Collectors.toList()); + } + + public static List getAllFaceColumns(Collection collection){ + List columns = getFaceColumns(collection); + columns.add(FiledColumn.build().setName(Constant.ColumnNameSampleId).setComment("样本ID").setDataType(FiledDataType.STRING)); + columns.add(FiledColumn.build().setName(Constant.ColumnNameFaceId).setComment("人脸ID").setDataType(FiledDataType.STRING)); + columns.add(FiledColumn.build().setName(Constant.ColumnNameFaceScore).setComment("人脸分数").setDataType(FiledDataType.FLOAT)); + return columns; + } + public static FieldKeyValues getFieldKeyValues(Map map , List columns){ columns = null != columns ? columns : new ArrayList<>(); Map keyMap = new HashMap<>(); diff --git a/face-search-server/src/main/resources/application-dev.yml b/face-search-server/src/main/resources/application-dev.yml index 9e472bc..ffbff35 100755 --- a/face-search-server/src/main/resources/application-dev.yml +++ b/face-search-server/src/main/resources/application-dev.yml @@ -47,13 +47,12 @@ visual: modelPath: thread: 1 engine: - selected: milvus - proxima: - host: visual-face-search-proxima - port: 16000 - milvus: - host: visual-face-search-milvus - port: 19530 + open-search: + host: visual-face-search-opensearch + port: 9200 + scheme: https + username: admin + password: admin scheduler: flush: enable: true diff --git a/face-search-server/src/main/resources/application-docker.yml b/face-search-server/src/main/resources/application-docker.yml index 79e2e8b..1f5380a 100755 --- a/face-search-server/src/main/resources/application-docker.yml +++ b/face-search-server/src/main/resources/application-docker.yml @@ -23,37 +23,36 @@ logging: visual: model: faceDetection: - name: InsightScrfdFaceDetection - modelPath: - thread: 4 + name: ${VISUAL_MODEL_FACEDETECTION_NAME:InsightScrfdFaceDetection} + modelPath: ${VISUAL_MODEL_FACEDETECTION_PATH:} + thread: ${VISUAL_MODEL_FACEDETECTION_THREAD:4} backup: - name: PcnNetworkFaceDetection - modelPath: - thread: 4 + name: ${VISUAL_MODEL_FACEDETECTION_BACKUP_NAME:PcnNetworkFaceDetection} + modelPath: ${VISUAL_MODEL_FACEDETECTION_BACKUP_PATH:} + thread: ${VISUAL_MODEL_FACEDETECTION_BACKUP_THREAD:4} faceKeyPoint: - name: InsightCoordFaceKeyPoint - modelPath: - thread: 4 + name: ${VISUAL_MODEL_FACEKEYPOINT_NAME:InsightCoordFaceKeyPoint} + modelPath: ${VISUAL_MODEL_FACEKEYPOINT_PATH:} + thread: ${VISUAL_MODEL_FACEKEYPOINT_THREAD:4} faceAlignment: - name: Simple005pFaceAlignment - modelPath: - thread: 4 + name: ${VISUAL_MODEL_FACEALIGNMENT_NAME:Simple005pFaceAlignment} + modelPath: ${VISUAL_MODEL_FACEALIGNMENT_PATH:} + thread: ${VISUAL_MODEL_FACEALIGNMENT_THREAD:4} faceRecognition: - name: InsightArcFaceRecognition - modelPath: - thread: 4 + name: ${VISUAL_MODEL_FACERECOGNITION_NAME:InsightArcFaceRecognition} + modelPath: ${VISUAL_MODEL_FACERECOGNITION_PATH:} + thread: ${VISUAL_MODEL_FACERECOGNITION_THREAD:4} faceAttribute: - name: InsightAttributeDetection - modelPath: - thread: 4 + name: ${VISUAL_MODEL_FACEATTRIBUTE_NAME:InsightAttributeDetection} + modelPath: ${VISUAL_MODEL_FACEATTRIBUTE_PATH:} + thread: ${VISUAL_MODEL_FACEATTRIBUTE_THREAD:4} engine: - selected: proxima - proxima: - host: - port: 16000 - milvus: - host: - port: 19530 + open-search: + host: ${VISUAL_ENGINE_OPENSEARCH_HOST} + port: ${VISUAL_ENGINE_OPENSEARCH_PORT:9200} + scheme: ${VISUAL_ENGINE_OPENSEARCH_SCHEME:https} + username: ${VISUAL_ENGINE_OPENSEARCH_USERNAME:admin} + password: ${VISUAL_ENGINE_OPENSEARCH_PASSWORD:admin} scheduler: flush: enable: true @@ -62,7 +61,7 @@ visual: face-search: false face-compare: false swagger: - enable: true + enable: ${VISUAL_SWAGGER_ENABLE:true} # Spring配置 spring: @@ -83,9 +82,9 @@ spring: druid: # 主库数据源 master: - url: - username: - password: + url: ${SPRING_DATASOURCE_URL} + username: ${SPRING_DATASOURCE_USERNAME:root} + password: ${SPRING_DATASOURCE_PASSWORD:root} slave: # 从数据源开关/默认关闭 enabled: false diff --git a/face-search-server/src/main/resources/application-local.yml b/face-search-server/src/main/resources/application-local.yml new file mode 100755 index 0000000..e450a4e --- /dev/null +++ b/face-search-server/src/main/resources/application-local.yml @@ -0,0 +1,132 @@ +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为80 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # tomcat最大线程数,默认为200 + max-threads: 10 + # Tomcat启动初始化的线程数,默认值25 + min-spare-threads: 5 + +# 日志配置 +logging: + level: + com.visual.face.search: info + org.springframework: warn + +# 模型配置 +visual: + model: + faceDetection: + name: InsightScrfdFaceDetection + modelPath: + thread: 1 + backup: + name: PcnNetworkFaceDetection + modelPath: + thread: 1 + faceKeyPoint: + name: InsightCoordFaceKeyPoint + modelPath: + thread: 1 + faceAlignment: + name: Simple005pFaceAlignment + modelPath: + thread: 1 + faceRecognition: + name: InsightArcFaceRecognition + modelPath: + thread: 1 + faceAttribute: + name: InsightAttributeDetection + modelPath: + thread: 1 + engine: + open-search: + host: 172.16.36.229 + port: 9200 + scheme: https + username: admin + password: admin + scheduler: + flush: + enable: true + interval: 60000 + face-mask: + face-search: false + face-compare: false + swagger: + enable: true + +# Spring配置 +spring: + jackson: + time-zone: GMT+8 + date-format: yyyy-MM-dd HH:mm:ss + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + #数据源 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + 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 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: + login-password: + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/face-search-server/src/main/resources/application.yml b/face-search-server/src/main/resources/application.yml index 615dec4..3b41dea 100755 --- a/face-search-server/src/main/resources/application.yml +++ b/face-search-server/src/main/resources/application.yml @@ -1,5 +1,8 @@ spring: application: name: open-face-search + mvc: + pathmatch: + matching-strategy: ant_path_matcher profiles: - active: dev + active: local diff --git a/face-search-test/pom.xml b/face-search-test/pom.xml index c53eda1..aa151f9 100644 --- a/face-search-test/pom.xml +++ b/face-search-test/pom.xml @@ -4,9 +4,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - face-search-test com.visual.face.search - 1.2.0 + face-search-test + 2.0.0 1.8 @@ -18,7 +18,7 @@ com.visual.face.search face-search-client - 1.1.0 + ${project.version} 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 362379b..7a11447 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 @@ -23,7 +23,7 @@ public class FaceSearchExample { //远程测试服务 //public static String serverHost = "http://face-search.diven.nat300.top"; public static String namespace = "namespace_1"; - public static String collectionName = "collect_20211201_v05"; + public static String collectionName = "collect_20211201_v10"; public static FaceSearch faceSearch = FaceSearch.build(serverHost, namespace, collectionName); /**集合创建*/ @@ -67,8 +67,8 @@ public class FaceSearchExample { faceData.add(KeyValue.build("label", "标签-" + name)); String imageBase64 = Base64Util.encode(image.getAbsolutePath()); Face face = Face.build(sampleId).setFaceData(faceData).setImageBase64(imageBase64) - .setMinConfidenceThresholdWithThisSample(0f) - .setMaxConfidenceThresholdWithOtherSample(0f); + .setMinConfidenceThresholdWithThisSample(50f) + .setMaxConfidenceThresholdWithOtherSample(50f); Response createFace = faceSearch.face().createFace(face); System.out.println("createFace:" + createFace); } @@ -82,8 +82,13 @@ public class FaceSearchExample { String searchPath = "face-search-test/src/main/resources/image/validate/search"; for(File image : Objects.requireNonNull(new File(searchPath).listFiles())){ String imageBase64 = Base64Util.encode(image.getAbsolutePath()); + System.out.println(imageBase64); Long s = System.currentTimeMillis(); - Response> listResponse = faceSearch.search().search(Search.build(imageBase64).setMaxFaceNum(10).setLimit(1)); + Response> listResponse = faceSearch.search() + .search(Search.build(imageBase64) + .setConfidenceThreshold(50f) //最小置信分:50 + .setMaxFaceNum(10).setLimit(1) + ); Long e = System.currentTimeMillis(); System.out.println("search cost:" + (e-s)+"ms"); System.out.println(JsonUtil.toString(listResponse, true, false)); @@ -95,13 +100,15 @@ public class FaceSearchExample { FaceLocation location = rep.getLocation(); drawImage.drawRect(new DrawImage.Rect(location.getX(), location.getY(), location.getW(), location.getH()), 2, Color.RED); List faces = rep.getMatch(); - for(SampleFace face : faces){ - int distance = face.getDistance().intValue(); - int confidence = face.getConfidence().intValue(); - String name = face.getSampleData().getString("name"); - drawImage.drawText("姓名:" + name, new DrawImage.Point(location.getX() + 5, location.getY()+5), 14, Color.RED); - drawImage.drawText("分数:" + confidence, new DrawImage.Point(location.getX()+5, location.getY()+25), 14, Color.RED); - drawImage.drawText("距离:" + distance, new DrawImage.Point(location.getX()+5, location.getY()+50), 14, Color.RED); + if(faces.size() > 0){ + for(SampleFace face : faces){ + int confidence = face.getConfidence().intValue(); + String name = face.getSampleData().getString("name"); + drawImage.drawText("姓名:" + name, new DrawImage.Point(location.getX() + 5, location.getY()+5), 14, Color.RED); + drawImage.drawText("分数:" + confidence, new DrawImage.Point(location.getX()+5, location.getY()+25), 14, Color.RED); + } + }else{ + drawImage.drawText("未识别", new DrawImage.Point(location.getX() + 5, location.getY()+5), 14, Color.GREEN); } } } @@ -115,8 +122,8 @@ public class FaceSearchExample { /**main**/ public static void main(String[] args) { -// collect(); -// index(); + collect(); + index(); search(); } diff --git a/pom.xml b/pom.xml index e5c71b3..d0d0576 100644 --- a/pom.xml +++ b/pom.xml @@ -3,39 +3,44 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + org.springframework.boot spring-boot-starter-parent - 2.1.1.RELEASE + 2.7.5 + com.visual.face.search face-search - 1.2.0 pom + 2.0.0 + face-search-core - face-search-server face-search-client face-search-engine + face-search-server face-search-test 1.1.22 4.5.1-2 - 1.9.0 + 2.4.0 + 1.13.1 1.2.58 + 6.0.13.Final 3.6.1 4.1 3.4.6 1.3.1 5.1.4 - 1.2.3 - 2.9.2 - 1.9.6 - 1.8 + 1.4.5 + 3.0.0 + 3.0.3 + 11 2.6 @@ -71,6 +76,12 @@ ${onnxruntime.version} + + org.opensearch.client + opensearch-rest-high-level-client + ${opensearch.version} + + com.alibaba fastjson @@ -96,6 +107,12 @@ ${druid.version} + + org.hibernate.validator + hibernate-validator + ${hibernate.version} + + org.mybatis mybatis @@ -121,15 +138,14 @@ io.springfox - springfox-swagger2 + springfox-boot-starter ${swagger.version} com.github.xiaoymin - swagger-bootstrap-ui - ${swagger-bootstrap-ui.version} + knife4j-spring-boot-starter + ${knife4j-ui.version} - @@ -144,6 +160,7 @@ model/** application-dev.yml + application-local.yml @@ -160,6 +177,14 @@ true + + central + aliyun nexus + https://maven.aliyun.com/repository/central + + true + + @@ -170,4 +195,17 @@ + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + \ No newline at end of file diff --git a/scripts/docker-compose-milvus.yml b/scripts/docker-compose-milvus.yml deleted file mode 100644 index 0432cbd..0000000 --- a/scripts/docker-compose-milvus.yml +++ /dev/null @@ -1,96 +0,0 @@ -version: '3.5' - -services: - visual-mysql: - container_name: face-search-server-mysql - image: mysql/mysql-server:5.7 - environment: - MYSQL_DATABASE: visual_face_search - MYSQL_ROOT_PASSWORD: root - MYSQL_ROOT_HOST: '%' - TZ: Asia/Shanghai - expose: - - "3306" - command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/mysql:/var/lib/mysql - ports: - - "43306:3306" - restart: always - - visual-etcd: - container_name: face-search-milvus-etcd - image: quay.io/coreos/etcd:v3.5.0 - environment: - - ETCD_AUTO_COMPACTION_MODE=revision - - ETCD_AUTO_COMPACTION_RETENTION=1000 - - ETCD_QUOTA_BACKEND_BYTES=4294967296 - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/etcd:/etcd - command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd - restart: always - - visual-minio: - container_name: face-search-milvus-minio - image: minio/minio:RELEASE.2020-12-03T00-03-10Z - environment: - MINIO_ACCESS_KEY: minioadmin - MINIO_SECRET_KEY: minioadmin - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/minio:/minio_data - command: minio server /minio_data - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] - interval: 30s - timeout: 20s - retries: 3 - restart: always - - visual-milvus: - container_name: face-search-milvus-standalone - image: milvusdb/milvus:v2.0.2 - command: ["milvus", "run", "standalone"] - environment: - ETCD_ENDPOINTS: visual-etcd:2379 - MINIO_ADDRESS: visual-minio:9000 - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/milvus:/var/lib/milvus - ports: - - "19530:19530" - depends_on: - - "visual-etcd" - - "visual-minio" - restart: always - - visual-facesearch: - container_name: face-search-server-standalone - image: divenswu/face-search:1.2.0 - environment: - SPRING_DATASOURCE_URL: 'jdbc:mysql://visual-mysql:3306/visual_face_search?useUnicode=true&characterEncoding=utf8' - SPRING_DATASOURCE_USERNAME: root - SPRING_DATASOURCE_PASSWORD: root - VISUAL_ENGINE_SELECTED: milvus - VISUAL_ENGINE_MILVUS_HOST: visual-milvus - VISUAL_ENGINE_MILVUS_PORT: 19530 - VISUAL_SWAGGER_ENABLE: 'true' - JAVA_OPTS: '-XX:MaxDirectMemorySize=400M' - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/search/logs:/app/face-search/logs - ports: - - "56789:8080" - deploy: - resources: - limits: - cpus: '2' - memory: 1G - reservations: - cpus: '1' - memory: 500M - depends_on: - - "visual-mysql" - - "visual-milvus" - restart: always - -networks: - default: - name: facesearch-milvus \ No newline at end of file diff --git a/scripts/docker-compose-opensearch.yml b/scripts/docker-compose-opensearch.yml new file mode 100644 index 0000000..0ac5c4b --- /dev/null +++ b/scripts/docker-compose-opensearch.yml @@ -0,0 +1,64 @@ +version: '3.5' + +services: + visual-mysql: + container_name: face-search-server-mysql + image: mysql/mysql-server:5.7 + environment: + MYSQL_DATABASE: visual_face_search + MYSQL_ROOT_PASSWORD: root + MYSQL_ROOT_HOST: '%' + TZ: Asia/Shanghai + expose: + - "3306" + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + volumes: + - ${FACESEARCH_VOLUME_DIRECTORY:-.}/volumes-face-search/mysql:/var/lib/mysql + restart: always + + visual-opensearch: + container_name: face-search-opensearch-standalone + image: opensearchproject/opensearch:2.4.0 + environment: + discovery.type: single-node + expose: + - "9200" + - "9600" + volumes: + - ${FACESEARCH_VOLUME_DIRECTORY:-.}/volumes-face-search/opensearch/data:/usr/share/opensearch/data + restart: always + + visual-opensearch-dashboards: + container_name: face-search-opensearch-dashboards + image: opensearchproject/opensearch-dashboards:2.4.0 + environment: + OPENSEARCH_HOSTS: '["https://visual-opensearch:9200"]' + ports: + - "5601:5601" + depends_on: + - "visual-opensearch" + restart: always + + visual-facesearch: + container_name: face-search-server-standalone + image: divenswu/face-search:2.0.0 + environment: + SPRING_DATASOURCE_URL: 'jdbc:mysql://visual-mysql:3306/visual_face_search?useUnicode=true&characterEncoding=utf8' + SPRING_DATASOURCE_USERNAME: root + SPRING_DATASOURCE_PASSWORD: root + VISUAL_ENGINE_OPENSEARCH_HOST: visual-opensearch + VISUAL_ENGINE_OPENSEARCH_PORT: 9200 + VISUAL_SWAGGER_ENABLE: 'true' + JAVA_OPTS: '-XX:MaxDirectMemorySize=400M' + volumes: + - ${FACESEARCH_VOLUME_DIRECTORY:-.}/volumes-face-search/search/logs:/app/face-search/logs + ports: + - "56789:8080" + depends_on: + - "visual-mysql" + - "visual-opensearch" + restart: always + +networks: + default: + name: facesearch-opensearch \ No newline at end of file diff --git a/scripts/docker-compose-proxima.yml b/scripts/docker-compose-proxima.yml deleted file mode 100644 index 156a8e7..0000000 --- a/scripts/docker-compose-proxima.yml +++ /dev/null @@ -1,62 +0,0 @@ -version: '3.5' - -services: - visual-mysql: - container_name: face-search-server-mysql - image: mysql/mysql-server:5.7 - environment: - MYSQL_DATABASE: visual_face_search - MYSQL_ROOT_PASSWORD: root - MYSQL_ROOT_HOST: '%' - TZ: Asia/Shanghai - expose: - - "3306" - command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/mysql:/var/lib/mysql - restart: always - - visual-proxima: - container_name: face-search-proxima-standalone - image: divenswu/proxima-be:0.2.0-official -# image: ghcr.io/proximabilin/proxima-be:0.2.0 # so slowly - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/proxima/data:/var/lib/proxima-be/data - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/proxima/log:/var/lib/proxima-be/log -# ports: -# - "16000:16000" #grpc -# - "16001:16001" #http - restart: always - - visual-facesearch: - container_name: face-search-server-standalone - image: divenswu/face-search:1.2.0 - environment: - SPRING_DATASOURCE_URL: 'jdbc:mysql://visual-mysql:3306/visual_face_search?useUnicode=true&characterEncoding=utf8' - SPRING_DATASOURCE_USERNAME: root - SPRING_DATASOURCE_PASSWORD: root - VISUAL_ENGINE_SELECTED: proxima - VISUAL_ENGINE_PROXIMA_HOST: visual-proxima - VISUAL_ENGINE_PROXIMA_PORT: 16000 - VISUAL_SWAGGER_ENABLE: 'true' - JAVA_OPTS: '-XX:MaxDirectMemorySize=400M' - volumes: - - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes-face-search/search/logs:/app/face-search/logs - ports: - - "56789:8080" - deploy: - resources: - limits: - cpus: '2' - memory: 1G - reservations: - cpus: '1' - memory: 500M - depends_on: - - "visual-mysql" - - "visual-proxima" - restart: always - -networks: - default: - name: facesearch-proxima \ No newline at end of file diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile index e537712..b4e367c 100644 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -1,9 +1,9 @@ -FROM frolvlad/alpine-oraclejre8 +FROM openjdk:11.0.11-jre-slim MAINTAINER face-search WORKDIR /app/face-search -RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ -apk update && apk add libssl1.0 libx11 libxext libxrender libstdc++ freetype fontconfig +#RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \ +#apk update && apk add libssl1.0 libx11 libxext libxrender libstdc++ freetype fontconfig COPY scripts/docker/entrypoint.sh /app/face-search RUN chmod +x /app/face-search/entrypoint.sh diff --git a/scripts/docker/entrypoint.sh b/scripts/docker/entrypoint.sh index 9ec624e..84f2bc0 100644 --- a/scripts/docker/entrypoint.sh +++ b/scripts/docker/entrypoint.sh @@ -11,100 +11,4 @@ else fi ################################################## active config end ################################################### - -################################################## swagger config start ################################################ -if [ "${VISUAL_SWAGGER_ENABLE}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.swagger.enable='$VISUAL_SWAGGER_ENABLE'" -fi -################################################## swagger config end ################################################## - - -################################################## datasource config start ############################################# -if [ "${SPRING_DATASOURCE_URL}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dspring.datasource.druid.master.url='$SPRING_DATASOURCE_URL'" -fi -if [ "${SPRING_DATASOURCE_USERNAME}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dspring.datasource.druid.master.username='$SPRING_DATASOURCE_USERNAME'" -fi -if [ "${SPRING_DATASOURCE_PASSWORD}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dspring.datasource.druid.master.password='$SPRING_DATASOURCE_PASSWORD'" -fi -################################################## datasource config end ############################################### - - -###################################################### engine config start ############################################# -if [ "${VISUAL_ENGINE_SELECTED}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.engine.selected='$VISUAL_ENGINE_SELECTED'" -fi - -if [ "${VISUAL_ENGINE_PROXIMA_HOST}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.engine.proxima.host='$VISUAL_ENGINE_PROXIMA_HOST'" -fi -if [ "${VISUAL_ENGINE_PROXIMA_PORT}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.engine.proxima.port='$VISUAL_ENGINE_PROXIMA_PORT'" -fi - -if [ "${VISUAL_ENGINE_MILVUS_HOST}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.engine.milvus.host='$VISUAL_ENGINE_MILVUS_HOST'" -fi -if [ "${VISUAL_ENGINE_MILVUS_PORT}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.engine.milvus.port='$VISUAL_ENGINE_MILVUS_PORT'" -fi -###################################################### engine config end ############################################### - - -###################################################### model config start ############################################## - -if [ "${VISUAL_MODEL_FACEDETECTION_NAME}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceDetection.name='$VISUAL_MODEL_FACEDETECTION_NAME'" -fi -if [ "${VISUAL_MODEL_FACEDETECTION_PATH}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceDetection.modelPath='$VISUAL_MODEL_FACEDETECTION_PATH'" -fi -if [ "${VISUAL_MODEL_FACEDETECTION_THREAD}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceDetection.thread='$VISUAL_MODEL_FACEDETECTION_THREAD'" -fi - -if [ "${VISUAL_MODEL_FACEDETECTION_BACKUP_NAME}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceDetection.backup.name='$VISUAL_MODEL_FACEDETECTION_BACKUP_NAME'" -fi -if [ "${VISUAL_MODEL_FACEDETECTION_BACKUP_PATH}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceDetection.backup.modelPath='$VISUAL_MODEL_FACEDETECTION_BACKUP_PATH'" -fi -if [ "${VISUAL_MODEL_FACEDETECTION_BACKUP_THREAD}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceDetection.backup.thread='$VISUAL_MODEL_FACEDETECTION_BACKUP_THREAD'" -fi - -if [ "${VISUAL_MODEL_FACEKEYPOINT_NAME}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceKeyPoint.name='$VISUAL_MODEL_FACEKEYPOINT_NAME'" -fi -if [ "${VISUAL_MODEL_FACEKEYPOINT_PATH}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceKeyPoint.modelPath='$VISUAL_MODEL_FACEKEYPOINT_PATH'" -fi -if [ "${VISUAL_MODEL_FACEKEYPOINT_THREAD}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceKeyPoint.thread='$VISUAL_MODEL_FACEKEYPOINT_THREAD'" -fi - -if [ "${VISUAL_MODEL_FACEALIGNMENT_NAME}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceAlignment.name='$VISUAL_MODEL_FACEALIGNMENT_NAME'" -fi -if [ "${VISUAL_MODEL_FACEALIGNMENT_PATH}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceAlignment.modelPath='$VISUAL_MODEL_FACEALIGNMENT_PATH'" -fi -if [ "${VISUAL_MODEL_FACEALIGNMENT_THREAD}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceAlignment.thread='$VISUAL_MODEL_FACEALIGNMENT_THREAD'" -fi - -if [ "${VISUAL_MODEL_FACERECOGNITION_NAME}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceRecognition.name='$VISUAL_MODEL_FACERECOGNITION_NAME'" -fi -if [ "${VISUAL_MODEL_FACERECOGNITION_PATH}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceRecognition.modelPath='$VISUAL_MODEL_FACERECOGNITION_PATH'" -fi -if [ "${VISUAL_MODEL_FACERECOGNITION_THREAD}" ];then - SPRING_PROFILE_CONFIG="${SPRING_PROFILE_CONFIG} -Dvisual.model.faceRecognition.thread='$VISUAL_MODEL_FACERECOGNITION_THREAD'" -fi -###################################################### model config end ############################################### - - sh -c "java -server ${SPRING_PROFILE_CONFIG} ${SPRING_OPTS} ${JAVA_OPTS} -jar /app/face-search/face-search-server.jar" diff --git a/scripts/docker_build.sh b/scripts/docker_build.sh index d204971..6bd8e49 100644 --- a/scripts/docker_build.sh +++ b/scripts/docker_build.sh @@ -1,4 +1,4 @@ -version='1.2.0' +version='2.0.0' SHELL_FOLDER=$(cd "$(dirname "$0")";pwd) cd ${SHELL_FOLDER} diff --git a/scripts/docs/2.0.0.md b/scripts/docs/2.0.0.md new file mode 100644 index 0000000..e483b89 --- /dev/null +++ b/scripts/docs/2.0.0.md @@ -0,0 +1,1210 @@ +# 人脸搜索服务API + + +**简介**:人脸搜索服务API + + +**HOST**:http://127.0.0.1:8080 + + +**联系人**: + + +**Version**:2.0.0 + + +**接口路径**:/v3/api-docs?group=2.0.0 + + +[TOC] + + + + + + +# 01、集合(数据库)管理 + + +## 1、创建一个集合(数据库) + + +**接口地址**:`/visual/collect/create` + + +**请求方式**:`POST` + + +**请求数据类型**:`application/x-www-form-urlencoded,application/json` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求示例**: + + +```javascript +{ + "collectionComment": "", + "collectionName": "", + "faceColumns": [ + { + "comment": "", + "dataType": "", + "name": "" + } + ], + "namespace": "", + "replicasNum": 0, + "sampleColumns": [ + { + "comment": "", + "dataType": "", + "name": "" + } + ], + "shardsNum": 0, + "storageEngine": "", + "storageFaceInfo": false +} +``` + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|collectReqVo|集合信息|body|true|CollectReqVo|CollectReqVo| +|  collectionComment|集合描述:最大128个字符||false|string|| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合||true|string|| +|  faceColumns|自定义的人脸属性字段||false|array|FiledColumn| +|    comment|字段描述,最大64个字符||false|string|| +|    dataType|字段类型,不能为UNDEFINED类型,可用值:BOOL,DOUBLE,FLOAT,INT,STRING,UNDEFINED||true|string|| +|    name|字段名称,支持小写字母、数字和下划线的组合,最大32个字符||true|string|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合||true|string|| +|  replicasNum|数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效||false|integer(int64)|| +|  sampleColumns|自定义的样本属性字段||false|array|FiledColumn| +|    comment|字段描述,最大64个字符||false|string|| +|    dataType|字段类型,不能为UNDEFINED类型,可用值:BOOL,DOUBLE,FLOAT,INT,STRING,UNDEFINED||true|string|| +|    name|字段名称,支持小写字母、数字和下划线的组合,最大32个字符||true|string|| +|  shardsNum|要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效||false|integer(int32)|| +|  storageEngine|保留图片及人脸信息的存储组件,可用值:ALI_OSS,CURR_DB,MIN_IO,TCE_COS||false|string|| +|  storageFaceInfo|是否保留图片及人脸信息||false|boolean|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«boolean»| +|201|Created|| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": false, + "message": "" +} +``` + + +## 2、根据命名空间,集合名称删除集合 + + +**接口地址**:`/visual/collect/delete` + + +**请求方式**:`GET` + + +**请求数据类型**:`application/x-www-form-urlencoded` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|namespace|命名空间|query|true|string|| +|collectionName|集合名称|query|true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«boolean»| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": false, + "message": "" +} +``` + + +## 3、根据命名空间,集合名称查看集合信息 + + +**接口地址**:`/visual/collect/get` + + +**请求方式**:`GET` + + +**请求数据类型**:`application/x-www-form-urlencoded` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|namespace|命名空间|query|true|string|| +|collectionName|集合名称|query|true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«CollectRepVo»| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|CollectRepVo|CollectRepVo| +|  collectionComment|集合描述:最大128个字符|string|| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合|string|| +|  faceColumns|自定义的人脸属性字段|array|FiledColumn| +|    comment|字段描述,最大64个字符|string|| +|    dataType|字段类型,不能为UNDEFINED类型,可用值:BOOL,DOUBLE,FLOAT,INT,STRING,UNDEFINED|string|| +|    name|字段名称,支持小写字母、数字和下划线的组合,最大32个字符|string|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合|string|| +|  replicasNum|数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效|integer(int64)|| +|  sampleColumns|自定义的样本属性字段|array|FiledColumn| +|    comment|字段描述,最大64个字符|string|| +|    dataType|字段类型,不能为UNDEFINED类型,可用值:BOOL,DOUBLE,FLOAT,INT,STRING,UNDEFINED|string|| +|    name|字段名称,支持小写字母、数字和下划线的组合,最大32个字符|string|| +|  shardsNum|要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效|integer(int32)|| +|  storageEngine|保留图片及人脸信息的存储组件,可用值:ALI_OSS,CURR_DB,MIN_IO,TCE_COS|string|| +|  storageFaceInfo|是否保留图片及人脸信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": { + "collectionComment": "", + "collectionName": "", + "faceColumns": [ + { + "comment": "", + "dataType": "", + "name": "" + } + ], + "namespace": "", + "replicasNum": 0, + "sampleColumns": [ + { + "comment": "", + "dataType": "", + "name": "" + } + ], + "shardsNum": 0, + "storageEngine": "", + "storageFaceInfo": false + }, + "message": "" +} +``` + + +## 4、根据命名空间查看集合列表 + + +**接口地址**:`/visual/collect/list` + + +**请求方式**:`GET` + + +**请求数据类型**:`application/x-www-form-urlencoded` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|namespace|命名空间|query|true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«List«CollectRepVo»»| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|array|CollectRepVo| +|  collectionComment|集合描述:最大128个字符|string|| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合|string|| +|  faceColumns|自定义的人脸属性字段|array|FiledColumn| +|    comment|字段描述,最大64个字符|string|| +|    dataType|字段类型,不能为UNDEFINED类型,可用值:BOOL,DOUBLE,FLOAT,INT,STRING,UNDEFINED|string|| +|    name|字段名称,支持小写字母、数字和下划线的组合,最大32个字符|string|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合|string|| +|  replicasNum|数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效|integer(int64)|| +|  sampleColumns|自定义的样本属性字段|array|FiledColumn| +|    comment|字段描述,最大64个字符|string|| +|    dataType|字段类型,不能为UNDEFINED类型,可用值:BOOL,DOUBLE,FLOAT,INT,STRING,UNDEFINED|string|| +|    name|字段名称,支持小写字母、数字和下划线的组合,最大32个字符|string|| +|  shardsNum|要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效|integer(int32)|| +|  storageEngine|保留图片及人脸信息的存储组件,可用值:ALI_OSS,CURR_DB,MIN_IO,TCE_COS|string|| +|  storageFaceInfo|是否保留图片及人脸信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": [ + { + "collectionComment": "", + "collectionName": "", + "faceColumns": [ + { + "comment": "", + "dataType": "", + "name": "" + } + ], + "namespace": "", + "replicasNum": 0, + "sampleColumns": [ + { + "comment": "", + "dataType": "", + "name": "" + } + ], + "shardsNum": 0, + "storageEngine": "", + "storageFaceInfo": false + } + ], + "message": "" +} +``` + + +# 02、人脸样本管理 + + +## 1、创建一个样本 + + +**接口地址**:`/visual/sample/create` + + +**请求方式**:`POST` + + +**请求数据类型**:`application/x-www-form-urlencoded,application/json` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求示例**: + + +```javascript +{ + "collectionName": "", + "namespace": "", + "sampleData": [ + { + "key": "", + "value": {} + } + ], + "sampleId": "" +} +``` + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|sampleDataReqVo|样本信息|body|true|SampleDataReqVo|SampleDataReqVo| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合||true|string|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合||true|string|| +|  sampleData|扩展字段||false|array|FieldKeyValue| +|    key|字段名,与创建集合时给定的字段名一致||true|string|| +|    value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符||false|object|| +|  sampleId|样本ID:最大32个字符,支持小写字母、数字和下划线的组合||true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«boolean»| +|201|Created|| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": false, + "message": "" +} +``` + + +## 3、根据条件删除样本 + + +**接口地址**:`/visual/sample/delete` + + +**请求方式**:`GET` + + +**请求数据类型**:`application/x-www-form-urlencoded` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|namespace|命名空间|query|true|string|| +|collectionName|集合名称|query|true|string|| +|sampleId|样本ID|query|true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«boolean»| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": false, + "message": "" +} +``` + + +## 4、根据条件查看样本 + + +**接口地址**:`/visual/sample/get` + + +**请求方式**:`GET` + + +**请求数据类型**:`application/x-www-form-urlencoded` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|namespace|命名空间|query|true|string|| +|collectionName|集合名称|query|true|string|| +|sampleId|样本ID|query|true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«SampleDataRepVo»| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|SampleDataRepVo|SampleDataRepVo| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合|string|| +|  faces|人脸数据|array|SimpleFaceVo| +|    faceData|人脸扩展的额外数据|array|FieldKeyValue| +|      key|字段名,与创建集合时给定的字段名一致|string|| +|      value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符|object|| +|    faceId|人脸ID|string|| +|    faceScore|人脸分数|number|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合|string|| +|  sampleData|扩展字段|array|FieldKeyValue| +|    key|字段名,与创建集合时给定的字段名一致|string|| +|    value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符|object|| +|  sampleId|样本ID:最大32个字符,支持小写字母、数字和下划线的组合|string|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": { + "collectionName": "", + "faces": [ + { + "faceData": [ + { + "key": "", + "value": {} + } + ], + "faceId": "", + "faceScore": 0 + } + ], + "namespace": "", + "sampleData": [ + { + "key": "", + "value": {} + } + ], + "sampleId": "" + }, + "message": "" +} +``` + + +## 5、根据查询信息查看样本列表 + + +**接口地址**:`/visual/sample/list` + + +**请求方式**:`GET` + + +**请求数据类型**:`application/x-www-form-urlencoded` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|namespace|命名空间|query|true|string|| +|collectionName|集合名称|query|true|string|| +|offset|起始记录:默认0|query|true|integer(int32)|| +|limit|样本数目:默认10|query|true|integer(int32)|| +|order|排列方式:默认asc,包括asc(升序)和desc(降序)|query|true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«List«SampleDataRepVo»»| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|array|SampleDataRepVo| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合|string|| +|  faces|人脸数据|array|SimpleFaceVo| +|    faceData|人脸扩展的额外数据|array|FieldKeyValue| +|      key|字段名,与创建集合时给定的字段名一致|string|| +|      value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符|object|| +|    faceId|人脸ID|string|| +|    faceScore|人脸分数|number|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合|string|| +|  sampleData|扩展字段|array|FieldKeyValue| +|    key|字段名,与创建集合时给定的字段名一致|string|| +|    value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符|object|| +|  sampleId|样本ID:最大32个字符,支持小写字母、数字和下划线的组合|string|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": [ + { + "collectionName": "", + "faces": [ + { + "faceData": [ + { + "key": "", + "value": {} + } + ], + "faceId": "", + "faceScore": 0 + } + ], + "namespace": "", + "sampleData": [ + { + "key": "", + "value": {} + } + ], + "sampleId": "" + } + ], + "message": "" +} +``` + + +## 2、更新一个样本 + + +**接口地址**:`/visual/sample/update` + + +**请求方式**:`POST` + + +**请求数据类型**:`application/x-www-form-urlencoded,application/json` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求示例**: + + +```javascript +{ + "collectionName": "", + "namespace": "", + "sampleData": [ + { + "key": "", + "value": {} + } + ], + "sampleId": "" +} +``` + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|sampleDataReqVo|样本信息|body|true|SampleDataReqVo|SampleDataReqVo| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合||true|string|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合||true|string|| +|  sampleData|扩展字段||false|array|FieldKeyValue| +|    key|字段名,与创建集合时给定的字段名一致||true|string|| +|    value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符||false|object|| +|  sampleId|样本ID:最大32个字符,支持小写字母、数字和下划线的组合||true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«boolean»| +|201|Created|| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": false, + "message": "" +} +``` + + +# 03、人脸数据管理 + + +## 1、创建一个人脸数据 + + +**接口地址**:`/visual/face/create` + + +**请求方式**:`POST` + + +**请求数据类型**:`application/x-www-form-urlencoded,application/json` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求示例**: + + +```javascript +{ + "collectionName": "", + "faceData": [ + { + "key": "", + "value": {} + } + ], + "faceScoreThreshold": 0, + "imageBase64": "", + "maxConfidenceThresholdWithOtherSample": 0, + "minConfidenceThresholdWithThisSample": 0, + "namespace": "", + "sampleId": "" +} +``` + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|faceDataReqVo|FaceDataReqVo|body|true|FaceDataReqVo|FaceDataReqVo| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合||true|string|| +|  faceData|扩展字段||false|array|FieldKeyValue| +|    key|字段名,与创建集合时给定的字段名一致||true|string|| +|    value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符||false|object|| +|  faceScoreThreshold|人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式||false|number(float)|| +|  imageBase64|图像Base64编码值||true|string|| +|  maxConfidenceThresholdWithOtherSample|当前样本与其他样本的人脸相似度的最大阈值,范围:[0,100]:默认0。当设置为0时,表示不做类间相似度判断逻辑,开启后对效率有较大影响||false|number(float)|| +|  minConfidenceThresholdWithThisSample|当前样本的人脸相似度的最小阈值,范围:[0,100]:默认0。当设置为0时,表示不做类间相似度判断逻辑,开启后对效率有较大影响||false|number(float)|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合||true|string|| +|  sampleId|样本ID:最大32个字符,支持小写字母、数字和下划线的组合||true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«FaceDataRepVo»| +|201|Created|| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|FaceDataRepVo|FaceDataRepVo| +|  collectionName|集合名称:最大24个字符,支持小写字母、数字和下划线的组合|string|| +|  faceData|扩展字段|array|FieldKeyValue| +|    key|字段名,与创建集合时给定的字段名一致|string|| +|    value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符|object|| +|  faceId|人脸ID|string|| +|  faceScore|人脸人数质量|number(float)|| +|  namespace|命名空间:最大12个字符,支持小写字母、数字和下划线的组合|string|| +|  sampleId|样本ID:最大32个字符,支持小写字母、数字和下划线的组合|string|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": { + "collectionName": "", + "faceData": [ + { + "key": "", + "value": {} + } + ], + "faceId": "", + "faceScore": 0, + "namespace": "", + "sampleId": "" + }, + "message": "" +} +``` + + +## 2、根据条件删除人脸数据 + + +**接口地址**:`/visual/face/delete` + + +**请求方式**:`GET` + + +**请求数据类型**:`application/x-www-form-urlencoded` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|namespace|命名空间|query|true|string|| +|collectionName|集合名称|query|true|string|| +|sampleId|样本ID|query|true|string|| +|faceId|人脸ID|query|true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«boolean»| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|boolean|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": false, + "message": "" +} +``` + + +# 04、人脸搜索服务 + + +## 1、人脸搜索M:N + + +**接口地址**:`/visual/search/do` + + +**请求方式**:`POST` + + +**请求数据类型**:`application/x-www-form-urlencoded,application/json` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求示例**: + + +```javascript +{ + "collectionName": "", + "confidenceThreshold": 0, + "faceScoreThreshold": 0, + "imageBase64": "", + "limit": 0, + "maxFaceNum": 0, + "namespace": "" +} +``` + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|faceSearchReqVo|人脸搜索参数|body|true|FaceSearchReqVo|FaceSearchReqVo| +|  collectionName|集合名称||true|string|| +|  confidenceThreshold|人脸匹配分数阈值,范围:[-100,100]:默认0||false|number(float)|| +|  faceScoreThreshold|人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式||false|number(float)|| +|  imageBase64|图像Base64编码值||true|string|| +|  limit|最大搜索条数:默认5||false|integer(int32)|| +|  maxFaceNum|对输入图像中多少个人脸进行检索比对:默认5||false|integer(int32)|| +|  namespace|命名空间||true|string|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«List«FaceSearchRepVo»»| +|201|Created|| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|array|FaceSearchRepVo| +|  faceScore|人脸分数:[0,100]|number(float)|| +|  location|人脸位置信息|FaceLocation|FaceLocation| +|    h|人脸高度|integer|| +|    w|人脸宽度|integer|| +|    x|左上角x坐标|integer|| +|    y|左上角y坐标|integer|| +|  match|匹配的人脸列表|array|SampleFaceVo| +|    confidence|转换后的置信度:[-100,100],值越大,相似度越高。|number|| +|    faceData|人脸扩展的额外数据|array|FieldKeyValue| +|      key|字段名,与创建集合时给定的字段名一致|string|| +|      value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符|object|| +|    faceId|人脸ID|string|| +|    faceScore|人脸分数:[0,100]|number|| +|    sampleData|样本扩展的额外数据|array|FieldKeyValue| +|      key|字段名,与创建集合时给定的字段名一致|string|| +|      value|字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符|object|| +|    sampleId|样本ID|string|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": [ + { + "faceScore": 0, + "location": { + "h": 0, + "w": 0, + "x": 0, + "y": 0 + }, + "match": [ + { + "confidence": 0, + "faceData": [ + { + "key": "", + "value": {} + } + ], + "faceId": "", + "faceScore": 0, + "sampleData": [ + { + "key": "", + "value": {} + } + ], + "sampleId": "" + } + ] + } + ], + "message": "" +} +``` + + +# 05、人脸比对服务 + + +## 1、人脸比对1:1 + + +**接口地址**:`/visual/compare/do` + + +**请求方式**:`POST` + + +**请求数据类型**:`application/x-www-form-urlencoded,application/json` + + +**响应数据类型**:`*/*` + + +**接口描述**: + + +**请求示例**: + + +```javascript +{ + "faceScoreThreshold": 0, + "imageBase64A": "", + "imageBase64B": "", + "needFaceInfo": false +} +``` + + +**请求参数**: + + +| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema | +| -------- | -------- | ----- | -------- | -------- | ------ | +|faceCompareReqVo|FaceCompareReqVo|body|true|FaceCompareReqVo|FaceCompareReqVo| +|  faceScoreThreshold|人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式||false|number(float)|| +|  imageBase64A|图像A的Base64编码值||true|string|| +|  imageBase64B|图像B的Base64编码值||true|string|| +|  needFaceInfo|是否需要人脸信息,默认为:true||false|boolean|| + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| -------- | -------- | ----- | +|200|OK|ResponseInfo«FaceCompareRepVo»| +|201|Created|| +|401|Unauthorized|| +|403|Forbidden|| +|404|Not Found|| + + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| -------- | -------- | ----- |----- | +|code|返回代码|integer(int32)|integer(int32)| +|data|数据信息|FaceCompareRepVo|FaceCompareRepVo| +|  confidence|余弦距离转换后的置信度:[-100,100],值越大,相似度越高。|number(float)|| +|  distance|向量欧式距离:>=0|number(float)|| +|  faceInfo|人脸信息,参数needFaceInfo=false时,值为null|CompareFace|CompareFace| +|    faceScoreA|A图片人脸分数:[0,100]|number|| +|    faceScoreB|B图片人脸分数:[0,100]|number|| +|    locationA|A图片人脸位置信息|FaceLocation|FaceLocation| +|      h|人脸高度|integer|| +|      w|人脸宽度|integer|| +|      x|左上角x坐标|integer|| +|      y|左上角y坐标|integer|| +|    locationB|B图片人脸位置信息|FaceLocation|FaceLocation| +|      h|人脸高度|integer|| +|      w|人脸宽度|integer|| +|      x|左上角x坐标|integer|| +|      y|左上角y坐标|integer|| +|message|返回信息|string|| + + +**响应示例**: +```javascript +{ + "code": 0, + "data": { + "confidence": 0, + "distance": 0, + "faceInfo": { + "faceScoreA": 0, + "faceScoreB": 0, + "locationA": { + "h": 0, + "w": 0, + "x": 0, + "y": 0 + }, + "locationB": { + "h": 0, + "w": 0, + "x": 0, + "y": 0 + } + } + }, + "message": "" +} +``` \ No newline at end of file diff --git a/scripts/docs/doc-1.0.0.md b/scripts/docs/doc-1.0.0.md deleted file mode 100644 index 8437b54..0000000 --- a/scripts/docs/doc-1.0.0.md +++ /dev/null @@ -1,1230 +0,0 @@ - -**人脸搜索服务API** - - -**简介**:

人脸搜索服务API

- - -**联系人**:divenswu@163.com - - -**Version**:1.0.0 - -**接口路径**:/v2/api-docs - - -# 01、集合(数据库)管理 - - -## 1、创建一个集合(数据库) - - -**接口描述**: - - -**接口地址**:`/visual/collect/create` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "collectionComment": "", - "maxDocsPerSegment": 0, - "shardsNum": 0, - "sampleColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "faceColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "syncBinLog": true -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collect| 集合信息 | body | true |CollectReqVo | CollectReqVo | - -**schema属性说明** - - - -**CollectReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionComment| 集合描述:最大128个字符 | body | false |string | | -|maxDocsPerSegment| 数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效 | body | false |integer(int64) | | -|shardsNum| 要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效 | body | false |integer(int32) | | -|sampleColumns| 自定义的样本属性字段 | body | false |array | FiledColumn | -|faceColumns| 自定义的人脸属性字段 | body | false |array | FiledColumn | -|syncBinLog| 启用binlog同步。扩展字段,暂不支持该功能。 | body | false |boolean | | - -**FiledColumn** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|name| 字段名称,支持小写字母、数字和下划线的组合,最大32个字符 | body | true |string | | -|comment| 字段描述,最大64个字符 | body | false |string | | -|dataType| 字段类型,不能为UNDEFINED类型,可用值:UNDEFINED,STRING,BOOL,INT,FLOAT,DOUBLE | body | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 2、根据命名空间,集合名称删除集合 - - -**接口描述**: - - -**接口地址**:`/visual/collect/delete` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 3、根据命名空间,集合名称查看集合信息 - - -**接口描述**: - - -**接口地址**:`/visual/collect/get` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": { - "namespace": "", - "collectionName": "", - "collectionComment": "", - "maxDocsPerSegment": 0, - "shardsNum": 0, - "sampleColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "faceColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "syncBinLog": true - } -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |CollectRepVo | CollectRepVo | - - - -**schema属性说明** - - - - -**CollectRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionComment | 集合描述:最大128个字符 |string | | -|maxDocsPerSegment | 数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效 |integer(int64) | | -|shardsNum | 要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效 |integer(int32) | | -|sampleColumns | 自定义的样本属性字段 |array | FiledColumn | -|faceColumns | 自定义的人脸属性字段 |array | FiledColumn | -|syncBinLog | 启用binlog同步。扩展字段,暂不支持该功能。 |boolean | | - -**FiledColumn** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|name | 字段名称,支持小写字母、数字和下划线的组合,最大32个字符 |string | | -|comment | 字段描述,最大64个字符 |string | | -|dataType | 字段类型,不能为UNDEFINED类型,可用值:UNDEFINED,STRING,BOOL,INT,FLOAT,DOUBLE |string | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«CollectRepVo»| -## 4、根据命名空间查看集合列表 - - -**接口描述**: - - -**接口地址**:`/visual/collect/list` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间 | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": [ - { - "namespace": "", - "collectionName": "", - "collectionComment": "", - "maxDocsPerSegment": 0, - "shardsNum": 0, - "sampleColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "faceColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "syncBinLog": true - } - ] -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |array | CollectRepVo | - - - -**schema属性说明** - - - - -**CollectRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionComment | 集合描述:最大128个字符 |string | | -|maxDocsPerSegment | 数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效 |integer(int64) | | -|shardsNum | 要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效 |integer(int32) | | -|sampleColumns | 自定义的样本属性字段 |array | FiledColumn | -|faceColumns | 自定义的人脸属性字段 |array | FiledColumn | -|syncBinLog | 启用binlog同步。扩展字段,暂不支持该功能。 |boolean | | - -**FiledColumn** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|name | 字段名称,支持小写字母、数字和下划线的组合,最大32个字符 |string | | -|comment | 字段描述,最大64个字符 |string | | -|dataType | 字段类型,不能为UNDEFINED类型,可用值:UNDEFINED,STRING,BOOL,INT,FLOAT,DOUBLE |string | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«List«CollectRepVo»»| -# 02、人脸样本管理 - -## 1、创建一个样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/create` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ] -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|sample| 样本信息 | body | true |SampleDataReqVo | SampleDataReqVo | - -**schema属性说明** - - - -**SampleDataReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleId| 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleData| 扩展字段 | body | false |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|key| 字段名,与创建集合时给定的字段名一致 | body | true |string | | -|value| 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 | body | false |object | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 3、根据条件删除样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/delete` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | -|sampleId| 样本ID | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 4、根据条件查看样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/get` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | -|sampleId| 样本ID | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": { - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ], - "faces": [ - { - "faceId": "", - "faceData": [ - { - "key": "", - "value": {} - } - ], - "faceScore": 0 - } - ] - } -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |SampleDataRepVo | SampleDataRepVo | - - - -**schema属性说明** - - - - -**SampleDataRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleId | 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleData | 扩展字段 |array | FieldKeyValue | -|faces | 人脸数据 |array | SimpleFaceVo | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**SimpleFaceVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|faceId | 人脸ID |string | | -|faceData | 人脸扩展的额外数据 |array | FieldKeyValue | -|faceScore | 人脸分数 |number(float) | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«SampleDataRepVo»| -## 5、根据查询信息查看样本列表 - - -**接口描述**: - - -**接口地址**:`/visual/sample/list` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|limit| 样本数目:默认10 | query | true |integer | | -|namespace| 命名空间 | query | true |string | | -|offset| 起始记录:默认0 | query | true |integer | | -|order| 排列方式:默认asc,包括asc(升序)和desc(降序) | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": [ - { - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ], - "faces": [ - { - "faceId": "", - "faceData": [ - { - "key": "", - "value": {} - } - ], - "faceScore": 0 - } - ] - } - ] -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |array | SampleDataRepVo | - - - -**schema属性说明** - - - - -**SampleDataRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleId | 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleData | 扩展字段 |array | FieldKeyValue | -|faces | 人脸数据 |array | SimpleFaceVo | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**SimpleFaceVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|faceId | 人脸ID |string | | -|faceData | 人脸扩展的额外数据 |array | FieldKeyValue | -|faceScore | 人脸分数 |number(float) | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«List«SampleDataRepVo»»| -## 2、更新一个样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/update` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ] -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|sample| 样本信息 | body | true |SampleDataReqVo | SampleDataReqVo | - -**schema属性说明** - - - -**SampleDataReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleId| 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleData| 扩展字段 | body | false |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|key| 字段名,与创建集合时给定的字段名一致 | body | true |string | | -|value| 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 | body | false |object | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -# 03、人脸数据管理 - -## 1、创建一个人脸数据 - - -**接口描述**: - - -**接口地址**:`/visual/face/create` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "sampleId": "", - "imageBase64": "", - "faceScoreThreshold": 0, - "minConfidenceThresholdWithThisSample": 0, - "maxConfidenceThresholdWithOtherSample": 0, - "faceData": [ - { - "key": "", - "value": {} - } - ] -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|face| face | body | true |FaceDataReqVo | FaceDataReqVo | - -**schema属性说明** - - - -**FaceDataReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleId| 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|imageBase64| 图像Base64编码值 | body | true |string | | -|faceScoreThreshold| 人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式 | body | false |number(float) | | -|minConfidenceThresholdWithThisSample| 当前样本的人脸相似度的最小阈值,范围:[0,100]:默认0。当设置为0时,表示不做类间相似度判断逻辑,开启后对效率有较大影响 | body | false |number(float) | | -|maxConfidenceThresholdWithOtherSample| 当前样本与其他样本的人脸相似度的最大阈值,范围:[0,100]:默认0。当设置为0时,表示不做类间相似度判断逻辑,开启后对效率有较大影响 | body | false |number(float) | | -|faceData| 扩展字段 | body | false |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|key| 字段名,与创建集合时给定的字段名一致 | body | true |string | | -|value| 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 | body | false |object | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": { - "namespace": "", - "collectionName": "", - "sampleId": "", - "faceId": "", - "faceScore": 0, - "faceData": [ - { - "key": "", - "value": {} - } - ] - } -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |FaceDataRepVo | FaceDataRepVo | - - - -**schema属性说明** - - - - -**FaceDataRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleId | 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 |string | | -|faceId | 人脸ID |string | | -|faceScore | 人脸人数质量 |number(float) | | -|faceData | 扩展字段 |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«FaceDataRepVo»| -## 2、根据条件删除人脸数据 - - -**接口描述**: - - -**接口地址**:`/visual/face/delete` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|faceId| 人脸ID | query | true |string | | -|namespace| 命名空间 | query | true |string | | -|sampleId| 样本ID | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -# 04、人脸搜索服务 - -## 1、人脸搜索1:N - - -**接口描述**: - - -**接口地址**:`/visual/search/do` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "imageBase64": "", - "faceScoreThreshold": 0, - "confidenceThreshold": 0, - "limit": 0, - "maxFaceNum": 0 -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|search| 人脸搜索参数 | body | true |FaceSearchReqVo | FaceSearchReqVo | - -**schema属性说明** - - - -**FaceSearchReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间 | body | true |string | | -|collectionName| 集合名称 | body | true |string | | -|imageBase64| 图像Base64编码值 | body | true |string | | -|faceScoreThreshold| 人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式 | body | false |number(float) | | -|confidenceThreshold| 人脸匹配分数阈值,范围:[-100,100]:默认0 | body | false |number(float) | | -|limit| 最大搜索条数:默认5 | body | false |integer(int32) | | -|maxFaceNum| 对输入图像中多少个人脸进行检索比对:默认5 | body | false |integer(int32) | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": [ - { - "location": { - "x": 0, - "y": 0, - "w": 0, - "h": 0 - }, - "faceScore": 0, - "match": [ - { - "sampleId": "", - "faceId": "", - "faceScore": 0, - "distance": 0, - "confidence": 0, - "sampleData": [ - { - "key": "", - "value": {} - } - ], - "faceData": [ - { - "key": "", - "value": {} - } - ] - } - ] - } - ] -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |array | FaceSearchRepVo | - - - -**schema属性说明** - - - - -**FaceSearchRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|location | 人脸位置信息 |FaceLocation | FaceLocation | -|faceScore | 人脸分数:[0,100] |number(float) | | -|match | 匹配的人脸列表 |array | SampleFaceVo | - -**FaceLocation** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|x | 左上角x坐标 |integer(int32) | | -|y | 左上角y坐标 |integer(int32) | | -|w | 人脸宽度 |integer(int32) | | -|h | 人脸高度 |integer(int32) | | - -**SampleFaceVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|sampleId | 样本ID |string | | -|faceId | 人脸ID |string | | -|faceScore | 人脸分数:[0,100] |number(float) | | -|distance | 向量距离:>=0 |number(float) | | -|confidence | 转换后的置信度:[-100,100],值越大,相似度越高。 |number(float) | | -|sampleData | 样本扩展的额外数据 |array | FieldKeyValue | -|faceData | 人脸扩展的额外数据 |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«List«FaceSearchRepVo»»| -# 05、公共服务-健康检测 - -## 公共-服务健康检测 - - -**接口描述**: - - -**接口地址**:`/common/health/check` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: -暂无 - - - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": "" -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |string | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«string»| diff --git a/scripts/docs/doc-1.1.0.md b/scripts/docs/doc-1.1.0.md deleted file mode 100644 index eba37c3..0000000 --- a/scripts/docs/doc-1.1.0.md +++ /dev/null @@ -1,1340 +0,0 @@ - -**人脸搜索服务API** - - -**简介**:

人脸搜索服务API

- - -**HOST**:127.0.0.1:8080 - - -**联系人**: - - -**Version**:1.1.0 - -**接口路径**:/v2/api-docs - - -# 01、集合(数据库)管理 - - -## 1、创建一个集合(数据库) - - -**接口描述**: - - -**接口地址**:`/visual/collect/create` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "collectionComment": "", - "maxDocsPerSegment": 0, - "shardsNum": 0, - "sampleColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "faceColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "syncBinLog": true -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collect| 集合信息 | body | true |CollectReqVo | CollectReqVo | - -**schema属性说明** - - - -**CollectReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionComment| 集合描述:最大128个字符 | body | false |string | | -|maxDocsPerSegment| 数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效 | body | false |integer(int64) | | -|shardsNum| 要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效 | body | false |integer(int32) | | -|sampleColumns| 自定义的样本属性字段 | body | false |array | FiledColumn | -|faceColumns| 自定义的人脸属性字段 | body | false |array | FiledColumn | -|syncBinLog| 启用binlog同步。扩展字段,暂不支持该功能。 | body | false |boolean | | - -**FiledColumn** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|name| 字段名称,支持小写字母、数字和下划线的组合,最大32个字符 | body | true |string | | -|comment| 字段描述,最大64个字符 | body | false |string | | -|dataType| 字段类型,不能为UNDEFINED类型,可用值:UNDEFINED,STRING,BOOL,INT,FLOAT,DOUBLE | body | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 2、根据命名空间,集合名称删除集合 - - -**接口描述**: - - -**接口地址**:`/visual/collect/delete` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 3、根据命名空间,集合名称查看集合信息 - - -**接口描述**: - - -**接口地址**:`/visual/collect/get` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": { - "namespace": "", - "collectionName": "", - "collectionComment": "", - "maxDocsPerSegment": 0, - "shardsNum": 0, - "sampleColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "faceColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "syncBinLog": true - } -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |CollectRepVo | CollectRepVo | - - - -**schema属性说明** - - - - -**CollectRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionComment | 集合描述:最大128个字符 |string | | -|maxDocsPerSegment | 数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效 |integer(int64) | | -|shardsNum | 要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效 |integer(int32) | | -|sampleColumns | 自定义的样本属性字段 |array | FiledColumn | -|faceColumns | 自定义的人脸属性字段 |array | FiledColumn | -|syncBinLog | 启用binlog同步。扩展字段,暂不支持该功能。 |boolean | | - -**FiledColumn** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|name | 字段名称,支持小写字母、数字和下划线的组合,最大32个字符 |string | | -|comment | 字段描述,最大64个字符 |string | | -|dataType | 字段类型,不能为UNDEFINED类型,可用值:UNDEFINED,STRING,BOOL,INT,FLOAT,DOUBLE |string | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«CollectRepVo»| -## 4、根据命名空间查看集合列表 - - -**接口描述**: - - -**接口地址**:`/visual/collect/list` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间 | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": [ - { - "namespace": "", - "collectionName": "", - "collectionComment": "", - "maxDocsPerSegment": 0, - "shardsNum": 0, - "sampleColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "faceColumns": [ - { - "name": "", - "comment": "", - "dataType": "" - } - ], - "syncBinLog": true - } - ] -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |array | CollectRepVo | - - - -**schema属性说明** - - - - -**CollectRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionComment | 集合描述:最大128个字符 |string | | -|maxDocsPerSegment | 数据分片中最大的文件个数,默认为0(不限制),仅对Proxima引擎生效 |integer(int64) | | -|shardsNum | 要创建的集合的分片数,默认为0(即系统默认),仅对Milvus引擎生效 |integer(int32) | | -|sampleColumns | 自定义的样本属性字段 |array | FiledColumn | -|faceColumns | 自定义的人脸属性字段 |array | FiledColumn | -|syncBinLog | 启用binlog同步。扩展字段,暂不支持该功能。 |boolean | | - -**FiledColumn** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|name | 字段名称,支持小写字母、数字和下划线的组合,最大32个字符 |string | | -|comment | 字段描述,最大64个字符 |string | | -|dataType | 字段类型,不能为UNDEFINED类型,可用值:UNDEFINED,STRING,BOOL,INT,FLOAT,DOUBLE |string | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«List«CollectRepVo»»| -# 02、人脸样本管理 - -## 1、创建一个样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/create` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ] -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|sample| 样本信息 | body | true |SampleDataReqVo | SampleDataReqVo | - -**schema属性说明** - - - -**SampleDataReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleId| 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleData| 扩展字段 | body | false |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|key| 字段名,与创建集合时给定的字段名一致 | body | true |string | | -|value| 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 | body | false |object | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 3、根据条件删除样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/delete` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | -|sampleId| 样本ID | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -## 4、根据条件查看样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/get` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|namespace| 命名空间 | query | true |string | | -|sampleId| 样本ID | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": { - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ], - "faces": [ - { - "faceId": "", - "faceData": [ - { - "key": "", - "value": {} - } - ], - "faceScore": 0 - } - ] - } -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |SampleDataRepVo | SampleDataRepVo | - - - -**schema属性说明** - - - - -**SampleDataRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleId | 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleData | 扩展字段 |array | FieldKeyValue | -|faces | 人脸数据 |array | SimpleFaceVo | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**SimpleFaceVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|faceId | 人脸ID |string | | -|faceData | 人脸扩展的额外数据 |array | FieldKeyValue | -|faceScore | 人脸分数 |number(float) | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«SampleDataRepVo»| -## 5、根据查询信息查看样本列表 - - -**接口描述**: - - -**接口地址**:`/visual/sample/list` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|limit| 样本数目:默认10 | query | true |integer | | -|namespace| 命名空间 | query | true |string | | -|offset| 起始记录:默认0 | query | true |integer | | -|order| 排列方式:默认asc,包括asc(升序)和desc(降序) | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": [ - { - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ], - "faces": [ - { - "faceId": "", - "faceData": [ - { - "key": "", - "value": {} - } - ], - "faceScore": 0 - } - ] - } - ] -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |array | SampleDataRepVo | - - - -**schema属性说明** - - - - -**SampleDataRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleId | 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleData | 扩展字段 |array | FieldKeyValue | -|faces | 人脸数据 |array | SimpleFaceVo | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**SimpleFaceVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|faceId | 人脸ID |string | | -|faceData | 人脸扩展的额外数据 |array | FieldKeyValue | -|faceScore | 人脸分数 |number(float) | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«List«SampleDataRepVo»»| -## 2、更新一个样本 - - -**接口描述**: - - -**接口地址**:`/visual/sample/update` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "sampleId": "", - "sampleData": [ - { - "key": "", - "value": {} - } - ] -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|sample| 样本信息 | body | true |SampleDataReqVo | SampleDataReqVo | - -**schema属性说明** - - - -**SampleDataReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleId| 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleData| 扩展字段 | body | false |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|key| 字段名,与创建集合时给定的字段名一致 | body | true |string | | -|value| 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 | body | false |object | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -# 03、人脸数据管理 - -## 1、创建一个人脸数据 - - -**接口描述**: - - -**接口地址**:`/visual/face/create` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "sampleId": "", - "imageBase64": "", - "faceScoreThreshold": 0, - "minConfidenceThresholdWithThisSample": 0, - "maxConfidenceThresholdWithOtherSample": 0, - "faceData": [ - { - "key": "", - "value": {} - } - ] -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|face| face | body | true |FaceDataReqVo | FaceDataReqVo | - -**schema属性说明** - - - -**FaceDataReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|collectionName| 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|sampleId| 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 | body | true |string | | -|imageBase64| 图像Base64编码值 | body | true |string | | -|faceScoreThreshold| 人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式 | body | false |number(float) | | -|minConfidenceThresholdWithThisSample| 当前样本的人脸相似度的最小阈值,范围:[0,100]:默认0。当设置为0时,表示不做类间相似度判断逻辑,开启后对效率有较大影响 | body | false |number(float) | | -|maxConfidenceThresholdWithOtherSample| 当前样本与其他样本的人脸相似度的最大阈值,范围:[0,100]:默认0。当设置为0时,表示不做类间相似度判断逻辑,开启后对效率有较大影响 | body | false |number(float) | | -|faceData| 扩展字段 | body | false |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|key| 字段名,与创建集合时给定的字段名一致 | body | true |string | | -|value| 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 | body | false |object | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": { - "namespace": "", - "collectionName": "", - "sampleId": "", - "faceId": "", - "faceScore": 0, - "faceData": [ - { - "key": "", - "value": {} - } - ] - } -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |FaceDataRepVo | FaceDataRepVo | - - - -**schema属性说明** - - - - -**FaceDataRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|namespace | 命名空间:最大12个字符,支持小写字母、数字和下划线的组合 |string | | -|collectionName | 集合名称:最大24个字符,支持小写字母、数字和下划线的组合 |string | | -|sampleId | 样本ID:最大32个字符,支持小写字母、数字和下划线的组合 |string | | -|faceId | 人脸ID |string | | -|faceScore | 人脸人数质量 |number(float) | | -|faceData | 扩展字段 |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«FaceDataRepVo»| -## 2、根据条件删除人脸数据 - - -**接口描述**: - - -**接口地址**:`/visual/face/delete` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|collectionName| 集合名称 | query | true |string | | -|faceId| 人脸ID | query | true |string | | -|namespace| 命名空间 | query | true |string | | -|sampleId| 样本ID | query | true |string | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": true -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |boolean | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«boolean»| -# 04、人脸搜索服务 - -## 1、人脸搜索M:N - - -**接口描述**: - - -**接口地址**:`/visual/search/do` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - -**请求示例**: -```json -{ - "namespace": "", - "collectionName": "", - "imageBase64": "", - "faceScoreThreshold": 0, - "confidenceThreshold": 0, - "limit": 0, - "maxFaceNum": 0 -} -``` - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|search| 人脸搜索参数 | body | true |FaceSearchReqVo | FaceSearchReqVo | - -**schema属性说明** - - - -**FaceSearchReqVo** - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|namespace| 命名空间 | body | true |string | | -|collectionName| 集合名称 | body | true |string | | -|imageBase64| 图像Base64编码值 | body | true |string | | -|faceScoreThreshold| 人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式 | body | false |number(float) | | -|confidenceThreshold| 人脸匹配分数阈值,范围:[-100,100]:默认0 | body | false |number(float) | | -|limit| 最大搜索条数:默认5 | body | false |integer(int32) | | -|maxFaceNum| 对输入图像中多少个人脸进行检索比对:默认5 | body | false |integer(int32) | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": [ - { - "location": { - "x": 0, - "y": 0, - "w": 0, - "h": 0 - }, - "faceScore": 0, - "match": [ - { - "sampleId": "", - "faceId": "", - "faceScore": 0, - "distance": 0, - "confidence": 0, - "sampleData": [ - { - "key": "", - "value": {} - } - ], - "faceData": [ - { - "key": "", - "value": {} - } - ] - } - ] - } - ] -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |array | FaceSearchRepVo | - - - -**schema属性说明** - - - - -**FaceSearchRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|location | 人脸位置信息 |FaceLocation | FaceLocation | -|faceScore | 人脸分数:[0,100] |number(float) | | -|match | 匹配的人脸列表 |array | SampleFaceVo | - -**FaceLocation** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|x | 左上角x坐标 |integer(int32) | | -|y | 左上角y坐标 |integer(int32) | | -|w | 人脸宽度 |integer(int32) | | -|h | 人脸高度 |integer(int32) | | - -**SampleFaceVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|sampleId | 样本ID |string | | -|faceId | 人脸ID |string | | -|faceScore | 人脸分数:[0,100] |number(float) | | -|distance | 向量距离:>=0 |number(float) | | -|confidence | 转换后的置信度:[-100,100],值越大,相似度越高。 |number(float) | | -|sampleData | 样本扩展的额外数据 |array | FieldKeyValue | -|faceData | 人脸扩展的额外数据 |array | FieldKeyValue | - -**FieldKeyValue** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|key | 字段名,与创建集合时给定的字段名一致 |string | | -|value | 字段值,与创建集合时给定的类型一致,若为字符串,最大为512个字符 |object | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«List«FaceSearchRepVo»»| -# 05、人脸比对服务 - -## 1、人脸比对1:1 - - -**接口描述**: - - -**接口地址**:`/visual/compare/do` - - -**请求方式**:`POST` - - -**consumes**:`["application/json"]` - - -**produces**:`["*/*"]` - - - -**请求参数**: - -| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | -| ------------ | -------------------------------- |-----------|--------|----|--- | -|faceScoreThreshold| 人脸质量分数阈值,范围:[0,100]:默认0。当设置为0时,会默认使用当前模型的默认值,该方法为推荐使用方式 | query | false |number | | -|imageBase64A| 图像A的Base64编码值 | query | true |string | | -|imageBase64B| 图像B的Base64编码值 | query | true |string | | -|needFaceInfo| 是否需要人脸信息,默认为:true | query | false |boolean | | - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": { - "distance": 0, - "confidence": 0, - "faceInfo": { - "faceScoreA": 0, - "faceScoreB": 0, - "locationA": { - "x": 0, - "y": 0, - "w": 0, - "h": 0 - }, - "locationB": { - "x": 0, - "y": 0, - "w": 0, - "h": 0 - } - } - } -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |FaceCompareRepVo | FaceCompareRepVo | - - - -**schema属性说明** - - - - -**FaceCompareRepVo** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|distance | 向量欧式距离:>=0 |number(float) | | -|confidence | 余弦距离转换后的置信度:[-100,100],值越大,相似度越高。 |number(float) | | -|faceInfo | 人脸信息,参数needFaceInfo=false时,值为null |CompareFace | CompareFace | - -**CompareFace** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|faceScoreA | A图片人脸分数:[0,100] |number(float) | | -|faceScoreB | B图片人脸分数:[0,100] |number(float) | | -|locationA | A图片人脸位置信息 |FaceLocation | FaceLocation | -|locationB | B图片人脸位置信息 |FaceLocation | FaceLocation | - -**FaceLocation** - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | ------------------|--------|----------- | -|x | 左上角x坐标 |integer(int32) | | -|y | 左上角y坐标 |integer(int32) | | -|w | 人脸宽度 |integer(int32) | | -|h | 人脸高度 |integer(int32) | | - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«FaceCompareRepVo»| -# 06、公共服务-健康检测 - -## 公共-服务健康检测 - - -**接口描述**: - - -**接口地址**:`/common/health/check` - - -**请求方式**:`GET` - - -**consumes**:`` - - -**produces**:`["*/*"]` - - - -**请求参数**: -暂无 - - - -**响应示例**: - -```json -{ - "code": 0, - "message": "", - "data": "" -} -``` - -**响应参数**: - - -| 参数名称 | 参数说明 | 类型 | schema | -| ------------ | -------------------|-------|----------- | -|code| 返回代码 |integer(int32) | integer(int32) | -|message| 返回信息 |string | | -|data| 数据信息 |string | | - - - - - -**响应状态**: - - -| 状态码 | 说明 | schema | -| ------------ | -------------------------------- |---------------------- | -| 200 | OK |ResponseInfo«string»| diff --git a/scripts/images/validate-1.2.0.jpg b/scripts/images/validate-1.2.0.jpg deleted file mode 100644 index fdbb5cd..0000000 Binary files a/scripts/images/validate-1.2.0.jpg and /dev/null differ diff --git a/scripts/images/validate-2.0.0.jpg b/scripts/images/validate-2.0.0.jpg new file mode 100644 index 0000000..231d423 Binary files /dev/null and b/scripts/images/validate-2.0.0.jpg differ diff --git a/scripts/images/validate.jpg b/scripts/images/validate.jpg deleted file mode 100644 index 50ff547..0000000 Binary files a/scripts/images/validate.jpg and /dev/null differ