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 98e62f9..7ce5b45 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 @@ -29,6 +29,25 @@ public class Similarity { } return (float) cosineSimilarity; } - + + + /** + * 两个向量可以为任意维度,但必须保持维度相同,表示n维度中的两点 + * 欧式距离 + * @param vector1 + * @param vector2 + * @return 两点间距离 + */ + public static float euclideanDistance(float[] vector1, float[] vector2) { + double distance = 0; + if (vector1.length == vector2.length) { + for (int i = 0; i < vector1.length; i++) { + double temp = Math.pow((vector1[i] - vector2[i]), 2); + distance += temp; + } + distance = Math.sqrt(distance); + } + return (float) distance; + } } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/controller/server/impl/FaceCompareControllerImpl.java b/face-search-server/src/main/java/com/visual/face/search/server/controller/server/impl/FaceCompareControllerImpl.java index 710d95b..49f4b03 100644 --- a/face-search-server/src/main/java/com/visual/face/search/server/controller/server/impl/FaceCompareControllerImpl.java +++ b/face-search-server/src/main/java/com/visual/face/search/server/controller/server/impl/FaceCompareControllerImpl.java @@ -1,15 +1,28 @@ package com.visual.face.search.server.controller.server.impl; +import javax.annotation.Resource; +import com.visual.face.search.server.controller.base.BaseController; import com.visual.face.search.server.controller.server.api.FaceCompareControllerApi; import com.visual.face.search.server.domain.common.ResponseInfo; import com.visual.face.search.server.domain.request.FaceCompareReqVo; import com.visual.face.search.server.domain.response.FaceCompareRepVo; +import com.visual.face.search.server.service.api.FaceCompareService; +import com.visual.face.search.server.utils.ResponseBuilder; -public class FaceCompareControllerImpl implements FaceCompareControllerApi { + +public class FaceCompareControllerImpl extends BaseController implements FaceCompareControllerApi { + + @Resource + private FaceCompareService faceCompareService; @Override public ResponseInfo faceCompare(FaceCompareReqVo compareReq) { - return null; + try { + return ResponseBuilder.success(faceCompareService.faceCompare(compareReq)); + }catch (Exception e){ + logger.error("do faceCompare exception:", e); + return ResponseBuilder.exception(e.getMessage(), null); + } } } diff --git a/face-search-server/src/main/java/com/visual/face/search/server/service/api/FaceCompareService.java b/face-search-server/src/main/java/com/visual/face/search/server/service/api/FaceCompareService.java new file mode 100644 index 0000000..38dadc7 --- /dev/null +++ b/face-search-server/src/main/java/com/visual/face/search/server/service/api/FaceCompareService.java @@ -0,0 +1,10 @@ +package com.visual.face.search.server.service.api; + +import com.visual.face.search.server.domain.request.FaceCompareReqVo; +import com.visual.face.search.server.domain.response.FaceCompareRepVo; + +public interface FaceCompareService { + + public FaceCompareRepVo faceCompare(FaceCompareReqVo compareReq); + +} diff --git a/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceCompareServiceImpl.java b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceCompareServiceImpl.java new file mode 100644 index 0000000..93ff67a --- /dev/null +++ b/face-search-server/src/main/java/com/visual/face/search/server/service/impl/FaceCompareServiceImpl.java @@ -0,0 +1,93 @@ +package com.visual.face.search.server.service.impl; + +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.Similarity; +import com.visual.face.search.server.domain.extend.CompareFace; +import com.visual.face.search.server.domain.extend.FaceLocation; +import com.visual.face.search.server.domain.request.FaceCompareReqVo; +import com.visual.face.search.server.domain.response.FaceCompareRepVo; +import com.visual.face.search.server.service.api.FaceCompareService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; + +@Service("visualFaceCompareServiceImpl") +public class FaceCompareServiceImpl implements FaceCompareService { + + @Resource + private FaceFeatureExtractor faceFeatureExtractor; + + + @Override + public FaceCompareRepVo faceCompare(FaceCompareReqVo compareReq) { + FaceInfo faceInfoA = getFaceInfo(compareReq.getFaceScoreThreshold(), compareReq.getImageBase64A()); + if(null == faceInfoA){ + throw new RuntimeException("image A is not face"); + } + FaceInfo faceInfoB = getFaceInfo(compareReq.getFaceScoreThreshold(), compareReq.getImageBase64B()); + if(null == faceInfoB){ + throw new RuntimeException("image B is not face"); + } + //计算余弦相似度 + float simVal = Similarity.cosineSimilarity(faceInfoA.embedding.embeds, faceInfoA.embedding.embeds); + float confidence = (float) Math.floor(simVal * 10000)/100; + //欧式距离 + float distance = Similarity.euclideanDistance(faceInfoA.embedding.embeds, faceInfoA.embedding.embeds); + //构建返回值 + FaceCompareRepVo faceCompareRep = new FaceCompareRepVo(); + faceCompareRep.setDistance(distance); + faceCompareRep.setConfidence(confidence); + if(compareReq.getNeedFaceInfo()){ + CompareFace compareFace = new CompareFace(); + compareFace.setFaceScoreA(faceInfoA.score); + compareFace.setFaceScoreB(faceInfoB.score); + FaceInfo.FaceBox boxA = faceInfoA.box; + compareFace.setLocationA(FaceLocation.build(boxA.leftTop.x, boxA.leftTop.y, boxA.width(), boxA.height())); + FaceInfo.FaceBox boxB = faceInfoB.box; + compareFace.setLocationB(FaceLocation.build(boxB.leftTop.x, boxB.leftTop.y, boxB.width(), boxB.height())); + faceCompareRep.setFaceInfo(compareFace); + } + //返回对象 + return faceCompareRep; + } + + + /** + * 图片检测并提取人脸特征 + * @param faceScoreThreshold + * @param imageBase64 + * @return + */ + private FaceInfo getFaceInfo(float faceScoreThreshold, String imageBase64){ + faceScoreThreshold = faceScoreThreshold < 0 ? 0 : faceScoreThreshold; + faceScoreThreshold = faceScoreThreshold > 100 ? 100 : faceScoreThreshold; + faceScoreThreshold = faceScoreThreshold > 1 ? faceScoreThreshold / 100 : faceScoreThreshold; + + ExtParam extParam = ExtParam.build().setMask(true).setScoreTh(faceScoreThreshold).setIouTh(0).setTopK(1); + ImageMat imageMat = null; + FaceImage faceImage = null; + try { + imageMat = ImageMat.fromBase64(imageBase64); + faceImage = faceFeatureExtractor.extract(imageMat, extParam, new HashMap<>()); + }finally { + if(null != imageMat){ + imageMat.release(); + } + } + if(null == faceImage){ + throw new RuntimeException("FeatureExtractor extract error"); + } + + List faceInfos = faceImage.faceInfos(); + if(faceInfos.size() > 0){ + return faceInfos.get(0); + } + return null; + } +}