FaceIdenService.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. package com.iden.bms.face;
  2. import cn.hutool.core.collection.CollUtil;
  3. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  4. import com.face.monitor.FaceMonitor;
  5. import com.face.monitor.model.FaceModel;
  6. import com.iden.bms.service.PersonService;
  7. import com.iden.common.cache.RedisKeyConstant;
  8. import com.iden.common.cache.RedisUtil;
  9. import com.iden.common.entity.*;
  10. import com.iden.common.enums.HandleWarningStatusEnum;
  11. import com.iden.common.enums.PersonTypeEnum;
  12. import com.iden.common.service.IdenCameraService;
  13. import com.iden.common.service.IdenCameraVideoService;
  14. import com.iden.common.service.IdenCommunityService;
  15. import com.iden.common.service.IdenFaceImageService;
  16. import com.iden.common.util.ByteUtil;
  17. import com.iden.common.util.DateUtils;
  18. import com.iden.common.util.FileUtil;
  19. import com.iden.common.util.VideoUtil;
  20. import com.iden.common.vo.FaceRetrieveResultVO;
  21. import org.apache.commons.lang3.StringUtils;
  22. import org.apache.logging.log4j.LogManager;
  23. import org.apache.logging.log4j.Logger;
  24. import org.springframework.beans.factory.annotation.Value;
  25. import org.springframework.stereotype.Service;
  26. import javax.annotation.Resource;
  27. import java.io.File;
  28. import java.math.BigDecimal;
  29. import java.util.*;
  30. /**
  31. * @Author: stw
  32. * @Date: 2021/7/14
  33. * @Desc:
  34. */
  35. @Service
  36. public class FaceIdenService {
  37. @Resource
  38. private IdenFaceImageService idenFaceImageService;
  39. @Resource
  40. private IdenCameraService idenCameraService;
  41. @Resource
  42. private IdenCommunityService idenCommunityService;
  43. @Resource
  44. private PersonService personService;
  45. @Resource
  46. private IdenCameraVideoService idenCameraVideoService;
  47. @Resource
  48. private RedisUtil redisUtil;
  49. @Value("${iden.root:#{null}}")
  50. private String idenRoot;
  51. @Value("${file.url:#{null}}")
  52. private String fileUrl;
  53. private static final Logger logger = LogManager.getLogger(FaceIdenService.class);
  54. /**
  55. * 处理各摄像头上传的图像,去除没有人脸的、不是正面的、不符合标准的、连拍的图像
  56. * @throws Exception
  57. */
  58. public void handleCameraImage() throws Exception {
  59. logger.info("FaceIdenService.handleCameraImage start...");
  60. File originImageDirFile = new File(idenRoot + "data/origin/camera/image");
  61. //使用摄像头编码做目录名,里面图像的名称上加上拍照时间,
  62. // 比如 20211222123223_76639ced-6409-11ec-b8f9-fa163e4e1e9f.jpg
  63. if (originImageDirFile.isDirectory()) {
  64. File[] cameraCodeDirs = originImageDirFile.listFiles();
  65. if(cameraCodeDirs != null && cameraCodeDirs.length > 0){
  66. //初始化引擎,加载人员图像人脸特征库
  67. String personImageDir = idenRoot + "data/final/person";
  68. //递归获取人员图像目录下的图像文件
  69. List<File> imgPersonFileList = new ArrayList<>();
  70. imgPersonFileList = FileUtil.getFiles(new File(personImageDir),imgPersonFileList);
  71. File[] imgPersonFiles = new File[imgPersonFileList.size()];
  72. imgPersonFiles = imgPersonFileList.toArray(imgPersonFiles);
  73. FaceMonitor faceMonitorPerson = FaceIdenTool.initFaceMonitor(idenRoot,imgPersonFiles);
  74. for(File cameraCodedir : cameraCodeDirs) {
  75. if(cameraCodedir.isDirectory()){
  76. File[] imgFiles = FileUtil.sortByName(cameraCodedir.listFiles());
  77. if(imgFiles != null && imgFiles.length > 0){
  78. String cameraCode = cameraCodedir.getName();
  79. QueryWrapper<IdenCamera> queryWrapper = new QueryWrapper<>();
  80. queryWrapper.lambda().eq(IdenCamera::getCode,cameraCode);
  81. IdenCamera idenCamera = idenCameraService.getOne(queryWrapper);
  82. if(idenCamera == null){
  83. continue;
  84. }
  85. //获取特征码
  86. FaceModel[] faceModels = FaceIdenTool.extractFeature(idenRoot,imgFiles);
  87. List<IdenFaceImage> idenFaceImageList = new ArrayList<>();
  88. for(int i = 0; i < imgFiles.length; i++) {
  89. File imgFile = imgFiles[i];
  90. String imgFileName = imgFile.getName();
  91. IdenFaceImage idenFaceImage = new IdenFaceImage();
  92. //识别特征码
  93. if (faceModels[i] != null) {
  94. String featPtr = ByteUtil.byte2Hex(faceModels[i].getFeatValue());
  95. //没有人脸的、不是正面的、不符合标准的,连拍的图片
  96. if(FaceIdenTool.isBad(featPtr) || isContinuation(cameraCode,imgFileName,featPtr)) {
  97. File discardDir = new File(imgFile.getParentFile().getAbsolutePath().replace("origin","discard"));
  98. if(!discardDir.exists()){
  99. discardDir.mkdirs();
  100. }
  101. File finalImgFile = new File(discardDir, imgFileName);
  102. imgFile.renameTo(finalImgFile);//移动到废弃目录
  103. continue;
  104. } else {
  105. idenFaceImage.setFeatPtr(featPtr);
  106. //存到redis
  107. redisUtil.hset(RedisKeyConstant.HANDLE_CAMERA_IMAGE_FIRST + "_" + cameraCode, imgFileName, featPtr, RedisKeyConstant.HANDLE_CAMERA_IMAGE_FIRST_TIME);
  108. }
  109. }
  110. idenFaceImage.setCameraId(idenCamera.getId());
  111. idenFaceImage.setCommunityId(idenCamera.getCommunityId());
  112. String photographPlace = idenCamera.getPlace();
  113. if (StringUtils.isEmpty(photographPlace)) {
  114. IdenCommunity idenCommunity = idenCommunityService.getById(idenCamera.getCommunityId());
  115. if(idenCommunity != null) {
  116. photographPlace = idenCommunity.getName() + "(" + idenCamera.getLongitude() + ":" + idenCamera.getLatitude() + ")";
  117. } else {
  118. photographPlace = "(" + idenCamera.getLongitude() + ":" + idenCamera.getLatitude() + ")";
  119. }
  120. }
  121. idenFaceImage.setPhotographPlace(photographPlace);
  122. idenFaceImage.setLongitude(idenCamera.getLongitude());
  123. idenFaceImage.setLatitude(idenCamera.getLatitude());
  124. String photographTime = imgFileName.substring(0, imgFileName.indexOf("_"));
  125. idenFaceImage.setPhotographTime(DateUtils.strToDate(photographTime,"yyyyMMddHHmmss"));
  126. idenFaceImage.setDataDate(photographTime.substring(0,8));
  127. File finalDir = new File(imgFile.getParentFile().getAbsolutePath().replace("origin","final"));
  128. if(!finalDir.exists()){
  129. finalDir.mkdirs();
  130. }
  131. logger.info("FaceIdenService.handleCameraImage ...finalDir == " + finalDir.getAbsolutePath());
  132. logger.info("FaceIdenService.handleCameraImage ...imgFile.getName() == " + imgFileName);
  133. File finalImgFile = new File(finalDir, imgFileName);
  134. logger.info("FaceIdenService.handleCameraImage ...finalImgFile == " + finalImgFile.getAbsolutePath());
  135. imgFile.renameTo(finalImgFile);//移动到最终目录
  136. idenFaceImage.setImage( fileUrl + "camera/image/" + cameraCode + "/" + finalImgFile.getName());
  137. //和人员图像库比对特征码,关联personId
  138. if(faceModels[i] != null){
  139. FaceRetrieveResultVO faceRetrieveResultVO = FaceIdenTool.getHitResult(faceMonitorPerson,faceModels[i]);
  140. if (faceRetrieveResultVO != null){
  141. int hitIndex = faceRetrieveResultVO.getIndex();
  142. IdenPerson idenPerson = personService.getPersonIdByImageFileName(imgPersonFiles[hitIndex].getName());
  143. if(idenPerson != null) {
  144. idenFaceImage.setPersonId(idenPerson.getId());
  145. idenFaceImage.setUid(idenPerson.getUid());
  146. idenFaceImage.setType(idenPerson.getType());
  147. idenFaceImage.setGender(idenPerson.getGender());
  148. float score = faceRetrieveResultVO.getScore();
  149. BigDecimal b = new BigDecimal(score);
  150. double similarity = b.divide(new BigDecimal(100)).setScale(4,BigDecimal.ROUND_HALF_UP).doubleValue();
  151. idenFaceImage.setSimilarity(similarity);
  152. }
  153. }
  154. }
  155. if (idenFaceImage.getPersonId() == null){ //没有关联上,是陌生人
  156. idenFaceImage.setType(PersonTypeEnum.STRANGER.getValue());
  157. //查询以前的陌生人图库,进行人脸识别,若是同一个人,得到uid, 设置为一样
  158. String uid = relevanceBeforeFace(idenFaceImage.getFeatPtr());
  159. if (StringUtils.isEmpty(uid)) {
  160. uid = UUID.randomUUID().toString();
  161. }
  162. idenFaceImage.setUid(uid);
  163. }
  164. idenFaceImage.setCreateTime(new Date());
  165. idenFaceImageList.add(idenFaceImage);
  166. }
  167. //把图像存到图库中
  168. if (CollUtil.isNotEmpty(idenFaceImageList)) {
  169. idenFaceImageService.saveBatch(idenFaceImageList);
  170. }
  171. }
  172. }
  173. }
  174. //释放人脸引擎
  175. FaceIdenTool.releaseEngine(faceMonitorPerson);
  176. }
  177. }
  178. logger.info("FaceIdenService.handleCameraImage end");
  179. }
  180. //查询以前的陌生人图库,进行人脸识别,若是同一个人,得到uid
  181. private String relevanceBeforeFace(String featPtrVisitor){
  182. QueryWrapper<IdenFaceImage> queryWrapper = new QueryWrapper<>();
  183. queryWrapper.lambda().eq(IdenFaceImage::getType, PersonTypeEnum.STRANGER.getValue())
  184. .eq(IdenFaceImage::getHandleWarningStatus,HandleWarningStatusEnum.HANDLED.getValue());
  185. //queryWrapper.last("limit 500"); //TODO 数据量大了需要分批处理 优化,以及是否区分小区
  186. List<IdenFaceImage> list = this.idenFaceImageService.list(queryWrapper);
  187. if (CollUtil.isNotEmpty(list)) {
  188. List<String> featPtrList = new ArrayList<>();
  189. for(IdenFaceImage idenFaceImage : list) {
  190. String featPtr = idenFaceImage.getFeatPtr();
  191. featPtrList.add(featPtr);
  192. }
  193. FaceRetrieveResultVO vo = FaceIdenTool.isHit(idenRoot,featPtrVisitor,featPtrList);
  194. if(vo != null) {
  195. return list.get(vo.getIndex()).getUid();
  196. }
  197. }
  198. return null;
  199. }
  200. //是否连拍
  201. private boolean isContinuation(String cameraCode,String imgFileName, String featPtr) {
  202. String photographTime = imgFileName.substring(0, imgFileName.indexOf("_"));
  203. Long photographTimeLong = DateUtils.strToDate(photographTime,"yyyyMMddHHmmss").getTime();
  204. //获取data/final/camera/image下图片
  205. File originImageDirFile = new File(idenRoot + "data/final/camera/image");
  206. File[] imgFiles = originImageDirFile.listFiles();
  207. List<String> featPtrList = new ArrayList<>();
  208. if (imgFiles != null && imgFiles.length > 0) {
  209. for(File imgFile : imgFiles) {
  210. String photographTimeTmp = imgFileName.substring(0, imgFile.getName().indexOf("_"));
  211. Long photographTimeTmpLong = DateUtils.strToDate(photographTimeTmp,"yyyyMMddHHmmss").getTime();
  212. //目标图片和以前图片拍照时间相差2分钟内
  213. if (photographTimeLong <= photographTimeTmpLong + 2 * 60 * 1000) {
  214. String featPtrTmp = (String)redisUtil.hget(RedisKeyConstant.HANDLE_CAMERA_IMAGE_FIRST + "_" + cameraCode, imgFile.getName());
  215. if(StringUtils.isNotEmpty(featPtrTmp)){
  216. featPtrList.add(featPtrTmp);
  217. }
  218. }
  219. }
  220. FaceRetrieveResultVO vo = FaceIdenTool.isHit(idenRoot,featPtr,featPtrList);
  221. return vo != null;
  222. } else {
  223. return false;
  224. }
  225. }
  226. /**
  227. * 把摄像头上传的图像解开为一帧帧图片,放到图像目录下,等待识别引擎处理
  228. * @throws Exception
  229. */
  230. public void handleCameraVideo() throws Exception {
  231. logger.info("FaceIdenService.handleCameraVideo start...");
  232. File originVideoDirFile = new File(idenRoot + "data/origin/camera/video");
  233. //使用摄像头编码做目录名,里面存放视频,名称里包含拍摄结束时间
  234. // 比如 20211217123343_76639ced-6409-11ec-b8f9-fa883e4e1e9f.mp4
  235. if (originVideoDirFile.isDirectory()) {
  236. File[] cameraCodeDirs = originVideoDirFile.listFiles();
  237. if (cameraCodeDirs != null && cameraCodeDirs.length > 0) {
  238. for(File cameraCodedir : cameraCodeDirs) {
  239. if (cameraCodedir.isDirectory()) {
  240. File[] videoFiles = FileUtil.sortByName(cameraCodedir.listFiles());
  241. if (videoFiles != null && videoFiles.length > 0) {
  242. String cameraCode = cameraCodedir.getName();
  243. QueryWrapper<IdenCamera> queryWrapper = new QueryWrapper<>();
  244. queryWrapper.lambda().eq(IdenCamera::getCode,cameraCode);
  245. IdenCamera idenCamera = idenCameraService.getOne(queryWrapper);
  246. if(idenCamera == null){
  247. continue;
  248. }
  249. for(File videoFile : videoFiles){
  250. String videoFileName = videoFile.getName();
  251. String shootEndTimeStr = videoFileName.substring(0, videoFileName.indexOf("_"));
  252. Date shootEndTime = DateUtils.strToDate(shootEndTimeStr,"yyyyMMddHHmmss");
  253. VideoUtil.fetchAllPic(videoFile,idenRoot + "data/origin/camera/image/" + cameraCode, shootEndTime);
  254. File finalDir = new File(videoFile.getParentFile().getAbsolutePath().replace("origin","final"));
  255. if(!finalDir.exists()){
  256. finalDir.mkdirs();
  257. }
  258. logger.info("FaceIdenService.handleCameraVideo ...finalDir == " + finalDir.getAbsolutePath());
  259. logger.info("FaceIdenService.handleCameraVideo ...videoFile.getName() == " + videoFileName);
  260. File finalvideoFile = new File(finalDir, videoFileName);
  261. logger.info("FaceIdenService.handleCameraVideo ...finalvideoFile == " + finalvideoFile.getAbsolutePath());
  262. videoFile.renameTo(finalvideoFile);//移动到最终目录
  263. IdenCameraVideo idenCameraVideo = new IdenCameraVideo();
  264. idenCameraVideo.setVideoUrl(fileUrl + "camera/video/" + cameraCode + "/" + videoFileName);
  265. idenCameraVideo.setCameraId(idenCamera.getId());
  266. idenCameraVideo.setCommunityId(idenCamera.getCommunityId());
  267. idenCameraVideo.setName(videoFileName.substring(0,videoFileName.lastIndexOf(".mp4")));
  268. idenCameraVideo.setPhotographTime(shootEndTime);
  269. idenCameraVideo.setCreateTime(new Date());
  270. idenCameraVideoService.save(idenCameraVideo);
  271. }
  272. }
  273. }
  274. }
  275. }
  276. }
  277. logger.info("FaceIdenService.handleCameraImage end");
  278. }
  279. }