人脸识别日志
This commit is contained in:
parent
64504fdf3b
commit
473e7080a1
@ -2,4 +2,7 @@ package com.ycwl.basic.constant;
|
||||
|
||||
public class FaceConstant {
|
||||
public static final String FACE_DB_NAME_PFX="face:db:";
|
||||
public static final String USER_FACE_DB_NAME="userFace";
|
||||
public static final String FACE_SAMPLE_URL_PFX="face:sample:url:";
|
||||
public static final String FACE_USER_URL_PFX="face:user:url:";
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ AppFaceController {
|
||||
@PostMapping("/faceUPload")
|
||||
public ApiResponse faceUPload(@RequestParam("file")MultipartFile file, @RequestParam("scenicId") Long scenicId) {
|
||||
|
||||
return faceService.faceUPload(file,scenicId);
|
||||
return faceService.faceUpload(file,scenicId);
|
||||
}
|
||||
|
||||
@ApiOperation("查询人脸照片信息")
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.ycwl.basic.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ycwl.basic.model.pc.faceDetectLog.entity.FaceDetectLog;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface FaceDetectLogMapper extends BaseMapper<FaceDetectLog> {
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.ycwl.basic.model.pc.faceDetectLog.entity;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
|
||||
import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ycwl.basic.model.pc.faceDetectLog.resp.MatchLocalRecord;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@TableName("face_detect_log")
|
||||
public class FaceDetectLog {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private String reason;
|
||||
|
||||
private String faceUrl;
|
||||
|
||||
private String dbName;
|
||||
|
||||
private String matchRawResult;
|
||||
|
||||
private String matchLocalRecord;
|
||||
|
||||
public static FaceDetectLog quickCreate(String reason) {
|
||||
FaceDetectLog log = new FaceDetectLog();
|
||||
log.reason = reason;
|
||||
return log;
|
||||
}
|
||||
public static FaceDetectLog quickCreate(String reason, SearchFaceRequest request) {
|
||||
FaceDetectLog log = new FaceDetectLog();
|
||||
log.reason = reason;
|
||||
return log.fillRequest(request);
|
||||
}
|
||||
|
||||
public FaceDetectLog fillRequest(SearchFaceRequest request) {
|
||||
this.dbName = request.getDbName();
|
||||
this.faceUrl = request.getImageUrl();
|
||||
this.createTime = new Date();
|
||||
return this;
|
||||
}
|
||||
|
||||
public FaceDetectLog fillResponse(SearchFaceResponse response) {
|
||||
this.matchRawResult = JSONObject.toJSONString(response.getData());
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<MatchLocalRecord> matchLocalRecord() {
|
||||
if (matchLocalRecord == null) {
|
||||
return null;
|
||||
}
|
||||
return JSONArray.parseArray(matchLocalRecord, MatchLocalRecord.class);
|
||||
}
|
||||
|
||||
public void matchLocalRecord(List<MatchLocalRecord> matchLocalRecord) {
|
||||
if (matchLocalRecord == null) {
|
||||
this.matchLocalRecord = null;
|
||||
} else {
|
||||
this.matchLocalRecord = JSONArray.toJSONString(matchLocalRecord);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.ycwl.basic.model.pc.faceDetectLog.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MatchLocalRecord {
|
||||
private Long faceSampleId;
|
||||
private String faceUrl;
|
||||
private Float confidence;
|
||||
private String idStr;
|
||||
}
|
@ -33,6 +33,8 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.ycwl.basic.constant.FaceConstant.USER_FACE_DB_NAME;
|
||||
|
||||
/**
|
||||
* @Author:longbinbin
|
||||
* @Date:2024/12/2 16:39
|
||||
@ -51,6 +53,7 @@ public class FaceServiceImpl implements FaceService {
|
||||
|
||||
@Value("${face.score}")
|
||||
private float faceScore;
|
||||
private float strictScore = 90F;
|
||||
@Autowired
|
||||
private TaskService taskTaskService;
|
||||
@Autowired
|
||||
@ -113,7 +116,7 @@ public class FaceServiceImpl implements FaceService {
|
||||
|
||||
@Override
|
||||
// @Transactional(rollbackFor = Exception.class)
|
||||
public ApiResponse faceUPload(MultipartFile file,Long scenicId) {
|
||||
public ApiResponse faceUpload(MultipartFile file, Long scenicId) {
|
||||
//获取用户id
|
||||
JwtInfo worker = JwtTokenUtil.getWorker();
|
||||
Long userId = worker.getUserId();
|
||||
@ -121,56 +124,58 @@ public class FaceServiceImpl implements FaceService {
|
||||
|
||||
//1、上传人脸照片
|
||||
String faceUrl = uploadFileALiOss(file, userId);
|
||||
SearchFaceRespVo searchFaceRespVo = faceService.searchFace(scenicId, faceUrl);
|
||||
if (searchFaceRespVo == null) {
|
||||
SearchFaceRespVo scenicDbSearchResult = faceService.searchFace(scenicId, faceUrl);
|
||||
if (scenicDbSearchResult == null) {
|
||||
ossUtil.deleteFileByUrl(faceUrl);
|
||||
throw new BaseException("人脸照片校验失败,请重新上传");
|
||||
}
|
||||
float score = searchFaceRespVo.getScore();
|
||||
float score = scenicDbSearchResult.getScore();
|
||||
if (score<faceScore) {
|
||||
//校验失败,删除,提示重新上传
|
||||
ossUtil.deleteFileByUrl(faceUrl);
|
||||
|
||||
throw new BaseException("人脸照片校验失败,请重新上传");
|
||||
}
|
||||
// 2、查看人脸是否已上传
|
||||
FaceRespVO faceRespVO=faceMapper.getByMemberId(userId, scenicId);
|
||||
|
||||
// 2、通过人脸查找用户库
|
||||
SearchFaceRespVo userDbSearchResult = faceService.searchFace(USER_FACE_DB_NAME, faceUrl);
|
||||
Long newFaceId = SnowFlakeUtil.getLongId();
|
||||
FaceEntity faceEntity = new FaceEntity();
|
||||
faceEntity.setScore(searchFaceRespVo.getScore());
|
||||
faceEntity.setMatchResult(searchFaceRespVo.getSearchResultJson());
|
||||
if (searchFaceRespVo.getFirstMatchRate() != null) {
|
||||
faceEntity.setFirstMatchRate(BigDecimal.valueOf(searchFaceRespVo.getFirstMatchRate()));
|
||||
faceEntity.setScore(scenicDbSearchResult.getScore());
|
||||
faceEntity.setMatchResult(scenicDbSearchResult.getSearchResultJson());
|
||||
if (userDbSearchResult == null) {
|
||||
// 都是null了,那得是新的
|
||||
faceService.addFaceSample(USER_FACE_DB_NAME, newFaceId.toString(), faceUrl, newFaceId.toString());
|
||||
} else if (userDbSearchResult.getSampleListIds() == null || userDbSearchResult.getSampleListIds().isEmpty()) {
|
||||
// 没有匹配到过,也得是新的
|
||||
faceService.addFaceSample(USER_FACE_DB_NAME, newFaceId.toString(), faceUrl, newFaceId.toString());
|
||||
} else if (userDbSearchResult.getFirstMatchRate() < strictScore) {
|
||||
// 有匹配结果,但是不匹配旧的
|
||||
faceService.addFaceSample(USER_FACE_DB_NAME, newFaceId.toString(), faceUrl, newFaceId.toString());
|
||||
} else {
|
||||
// 有匹配结果,且能匹配旧的数据
|
||||
Long oldFaceId = userDbSearchResult.getSampleListIds().get(0);
|
||||
faceEntity.setId(oldFaceId);
|
||||
}
|
||||
if (searchFaceRespVo.getSampleListIds() != null) {
|
||||
faceEntity.setMatchSampleIds(searchFaceRespVo.getSampleListIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
if (scenicDbSearchResult.getFirstMatchRate() != null) {
|
||||
faceEntity.setFirstMatchRate(BigDecimal.valueOf(scenicDbSearchResult.getFirstMatchRate()));
|
||||
}
|
||||
if (scenicDbSearchResult.getSampleListIds() != null) {
|
||||
faceEntity.setMatchSampleIds(scenicDbSearchResult.getSampleListIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
}
|
||||
faceEntity.setCreateAt(new Date());
|
||||
faceEntity.setScenicId(scenicId);
|
||||
faceEntity.setMemberId(userId);
|
||||
faceEntity.setFaceUrl(faceUrl);
|
||||
if (faceRespVO==null) {
|
||||
if (faceEntity.getId()==null) {
|
||||
//新增人脸
|
||||
faceEntity.setId(SnowFlakeUtil.getLongId());
|
||||
faceEntity.setId(newFaceId);
|
||||
faceMapper.add(faceEntity);
|
||||
taskTaskService.autoCreateTaskByFaceId(faceEntity.getId());
|
||||
} else if (StringUtils.isBlank(faceRespVO.getMatchSampleIds())) {
|
||||
// 如果之前的是没有匹配到的
|
||||
// 也去新增
|
||||
faceEntity.setId(faceRespVO.getId());
|
||||
faceMapper.update(faceEntity);
|
||||
taskTaskService.autoCreateTaskByFaceId(faceEntity.getId());
|
||||
} else {
|
||||
//2、更新人脸
|
||||
faceEntity.setId(faceRespVO.getId());
|
||||
faceMapper.update(faceEntity);
|
||||
if (!Objects.equals(
|
||||
faceRespVO.getMatchSampleIds(),
|
||||
searchFaceRespVo.getSampleListIds().stream().map(String::valueOf).collect(Collectors.joining(","))
|
||||
)) {
|
||||
taskTaskService.autoCreateTaskByFaceId(faceEntity.getId());
|
||||
}
|
||||
}
|
||||
StatisticsRecordAddReq statisticsRecordAddReq = new StatisticsRecordAddReq();
|
||||
statisticsRecordAddReq.setMemberId(userId);
|
||||
statisticsRecordAddReq.setType(StatisticEnum.UPLOAD_FACE.code);
|
||||
|
@ -18,11 +18,14 @@ import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse;
|
||||
import com.ycwl.basic.config.FaceDetectConfig;
|
||||
import com.ycwl.basic.constant.FaceConstant;
|
||||
import com.ycwl.basic.exception.BaseException;
|
||||
import com.ycwl.basic.mapper.FaceDetectLogMapper;
|
||||
import com.ycwl.basic.mapper.FaceMapper;
|
||||
import com.ycwl.basic.mapper.FaceSampleMapper;
|
||||
import com.ycwl.basic.mapper.ScenicMapper;
|
||||
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
||||
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
|
||||
import com.ycwl.basic.model.pc.faceDetectLog.entity.FaceDetectLog;
|
||||
import com.ycwl.basic.model.pc.faceDetectLog.resp.MatchLocalRecord;
|
||||
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
|
||||
import com.ycwl.basic.model.pc.faceSample.req.FaceSampleReqQuery;
|
||||
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
|
||||
@ -41,8 +44,10 @@ import com.aliyuncs.profile.DefaultProfile;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@ -58,6 +63,8 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
private FaceDetectConfig faceDetectConfig;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private FaceDetectLogMapper logMapper;
|
||||
|
||||
private IAcsClient getClient() {
|
||||
DefaultProfile profile = DefaultProfile.getProfile(
|
||||
@ -69,7 +76,7 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
@Override
|
||||
public SearchFaceRespVo searchFace(Long faceId) {
|
||||
FaceRespVO faceRespVO = faceMapper.getById(faceId);
|
||||
SearchFaceRespVo respVo = searchFace(faceRespVO.getScenicId(), faceRespVO.getFaceUrl());
|
||||
SearchFaceRespVo respVo = searchFace(faceRespVO.getScenicId().toString(), faceRespVO.getFaceUrl());
|
||||
if (respVo != null) {
|
||||
FaceEntity faceEntity = new FaceEntity();
|
||||
faceEntity.setId(faceId);
|
||||
@ -96,8 +103,10 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
request.setImageUrl(faceUrl);
|
||||
request.setLimit(100);
|
||||
request.setQualityScoreThreshold(80F);
|
||||
FaceDetectLog log = FaceDetectLog.quickCreate("预留字段", request);
|
||||
try {
|
||||
SearchFaceResponse response = client.getAcsResponse(request);
|
||||
log.fillResponse(response);
|
||||
List<SearchFaceResponse.Data.MatchListItem> matchList = response.getData().getMatchList();
|
||||
if (matchList.isEmpty()) {
|
||||
return null;
|
||||
@ -110,16 +119,33 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
}
|
||||
List<SearchFaceResponse.Data.MatchListItem.FaceItemsItem> faceItems = matchList.get(0).getFaceItems().stream()
|
||||
.filter(faceItemsItem -> faceItemsItem.getConfidence() > 50).collect(Collectors.toList());
|
||||
List<Long> faceSampleIds = faceItems.stream()
|
||||
.map(SearchFaceResponse.Data.MatchListItem.FaceItemsItem::getExtraData)
|
||||
.map(Long::parseLong)
|
||||
List<MatchLocalRecord> records = faceItems.stream()
|
||||
.map(item -> {
|
||||
MatchLocalRecord record = new MatchLocalRecord();
|
||||
record.setIdStr(item.getExtraData());
|
||||
record.setFaceSampleId(Long.parseLong(item.getExtraData()));
|
||||
if (StringUtils.isNumeric(item.getDbName())) {
|
||||
record.setFaceUrl(getFaceSampleUrl(record.getFaceSampleId()));
|
||||
} else {
|
||||
record.setFaceUrl(getFaceUrl(record.getFaceSampleId()));
|
||||
}
|
||||
record.setConfidence(item.getConfidence());
|
||||
return record;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
log.matchLocalRecord(records);
|
||||
List<Long> faceSampleIds = records.stream()
|
||||
.map(MatchLocalRecord::getFaceSampleId)
|
||||
.collect(Collectors.toList());
|
||||
respVo.setFirstMatchRate(matchList.get(0).getFaceItems().get(0).getConfidence());
|
||||
respVo.setSampleListIds(faceSampleIds);
|
||||
return respVo;
|
||||
} catch (Exception e) {
|
||||
log.error("人脸搜索失败:{}", e.getMessage());
|
||||
log.setMatchRawResult("识别错误,错误为:["+e.getLocalizedMessage()+"]");
|
||||
e.printStackTrace();
|
||||
throw new BaseException(e.getMessage());
|
||||
} finally {
|
||||
logMapper.insert(log);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +159,7 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
faceSampleEntity.setScore(respVo.getScore());
|
||||
faceSampleEntity.setUpdateAt(new Date());
|
||||
faceSampleMapper.update(faceSampleEntity);
|
||||
addFaceSampleUrlCache(faceSampleId, faceSampleRespVO.getFaceUrl());
|
||||
return respVo;
|
||||
}
|
||||
|
||||
@ -250,16 +277,31 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ListFaceDbsRequest request = new ListFaceDbsRequest();
|
||||
request.setLimit(Long.MAX_VALUE);
|
||||
long offset = 0;
|
||||
List<ListFaceDbsResponse.Data.DbListItem> dbList = new ArrayList<>();
|
||||
IAcsClient client = getClient();
|
||||
while (true) {
|
||||
ListFaceDbsRequest request = new ListFaceDbsRequest();
|
||||
request.setLimit(200L);
|
||||
request.setOffset(offset);
|
||||
ListFaceDbsResponse response = client.getAcsResponse(request);
|
||||
if (response.getData().getDbList() == null) {
|
||||
return;
|
||||
List<ListFaceDbsResponse.Data.DbListItem> list = response.getData().getDbList();
|
||||
if (list == null || list.isEmpty()) {
|
||||
break;
|
||||
} else {
|
||||
dbList.addAll(list);
|
||||
offset += list.size();
|
||||
}
|
||||
boolean mismatch = response.getData().getDbList().stream().peek(item -> {
|
||||
}
|
||||
boolean mismatch;
|
||||
if (dbList.isEmpty()) {
|
||||
clearFaceDBCache();
|
||||
mismatch = true;
|
||||
} else {
|
||||
mismatch = dbList.stream().peek(item -> {
|
||||
redisTemplate.opsForValue().set(FaceConstant.FACE_DB_NAME_PFX + dbName, "1");
|
||||
}).noneMatch(db -> db.getName().equals(dbName));
|
||||
}
|
||||
if (mismatch) {
|
||||
createFaceDB(dbName);
|
||||
}
|
||||
@ -293,4 +335,57 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
||||
String entityId = entity.getDeviceId().toString() + "_" + sdf.format(entity.getCreateAt());
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public String getFaceUrl(Long faceId) {
|
||||
if (faceId == null) {
|
||||
return null;
|
||||
}
|
||||
if (redisTemplate.hasKey(FaceConstant.FACE_USER_URL_PFX + faceId)) {
|
||||
return redisTemplate.opsForValue().get(FaceConstant.FACE_USER_URL_PFX + faceId);
|
||||
}
|
||||
FaceRespVO faceRespVO = faceMapper.getById(faceId);
|
||||
if (faceRespVO == null) {
|
||||
return null;
|
||||
}
|
||||
String faceUrl = faceRespVO.getFaceUrl();
|
||||
if (StringUtils.isNotBlank(faceUrl)) {
|
||||
addFaceUrlCache(faceId, faceUrl);
|
||||
}
|
||||
return faceUrl;
|
||||
}
|
||||
public void addFaceUrlCache(Long faceId, String faceUrl) {
|
||||
if (faceId == null) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(faceUrl)) {
|
||||
return;
|
||||
}
|
||||
redisTemplate.opsForValue().set(FaceConstant.FACE_USER_URL_PFX + faceId, faceUrl, 3, TimeUnit.DAYS);
|
||||
}
|
||||
public String getFaceSampleUrl(Long faceSampleId) {
|
||||
if (faceSampleId == null) {
|
||||
return null;
|
||||
}
|
||||
if (redisTemplate.hasKey(FaceConstant.FACE_SAMPLE_URL_PFX + faceSampleId)) {
|
||||
return redisTemplate.opsForValue().get(FaceConstant.FACE_SAMPLE_URL_PFX + faceSampleId);
|
||||
}
|
||||
FaceSampleRespVO faceSampleRespVO = faceSampleMapper.getById(faceSampleId);
|
||||
if (faceSampleRespVO == null) {
|
||||
return null;
|
||||
}
|
||||
String faceUrl = faceSampleRespVO.getFaceUrl();
|
||||
if (StringUtils.isNotBlank(faceUrl)) {
|
||||
addFaceSampleUrlCache(faceSampleId, faceUrl);
|
||||
}
|
||||
return faceUrl;
|
||||
}
|
||||
public void addFaceSampleUrlCache(Long faceSampleId, String faceUrl) {
|
||||
if (faceSampleId == null) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(faceUrl)) {
|
||||
return;
|
||||
}
|
||||
redisTemplate.opsForValue().set(FaceConstant.FACE_SAMPLE_URL_PFX + faceSampleId, faceUrl, 3, TimeUnit.DAYS);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public interface FaceService {
|
||||
ApiResponse<Integer> deleteByIds(List<Long> ids);
|
||||
ApiResponse<Integer> update(FaceEntity face);
|
||||
|
||||
ApiResponse faceUPload(MultipartFile file,Long scrnicId);
|
||||
ApiResponse faceUpload(MultipartFile file, Long scrnicId);
|
||||
|
||||
ApiResponse<FaceRespVO> getFaceByMemberId(Long memberId);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ public interface TaskFaceService {
|
||||
SearchFaceRespVo searchFace(Long faceId);
|
||||
|
||||
SearchFaceRespVo searchFace(Long scenicId, String faceUrl);
|
||||
|
||||
SearchFaceRespVo searchFace(String dbName, String faceUrl);
|
||||
|
||||
AddFaceSampleRespVo addFaceSample(Long faceSampleId);
|
||||
|
Loading…
x
Reference in New Issue
Block a user