人脸识别日志

This commit is contained in:
Jerry Yan 2024-12-26 15:32:41 +08:00
parent 64504fdf3b
commit 473e7080a1
9 changed files with 239 additions and 43 deletions

View File

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

View File

@ -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("查询人脸照片信息")

View File

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

View File

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

View File

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

View File

@ -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;
/**
* @Authorlongbinbin
* @Date2024/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);

View File

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

View File

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

View File

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