diff --git a/shapelight-admin/pom.xml b/shapelight-admin/pom.xml index f48540b..a21f5ba 100644 --- a/shapelight-admin/pom.xml +++ b/shapelight-admin/pom.xml @@ -226,7 +226,15 @@ 2.0.0 - + + com.arcsoft.face + arcsoft-sdk-face + 4.1.1.0 + + + + + ${project.artifactId} diff --git a/shapelight-admin/src/main/java/net/shapelight/AdminApplication.java b/shapelight-admin/src/main/java/net/shapelight/AdminApplication.java index 4754793..f08c491 100644 --- a/shapelight-admin/src/main/java/net/shapelight/AdminApplication.java +++ b/shapelight-admin/src/main/java/net/shapelight/AdminApplication.java @@ -4,8 +4,8 @@ package net.shapelight; //import net.shapelight.modules.dev.mqtt.MqttClientUtil; import lombok.extern.slf4j.Slf4j; -import net.shapelight.commons.engine.sdk.PalmSDK; -import org.mybatis.spring.annotation.MapperScan; +/*import net.shapelight.commons.engine.sdk.PalmSDK; +import org.mybatis.spring.annotation.MapperScan;*/ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @@ -29,8 +29,8 @@ public class AdminApplication { SpringApplication.run(AdminApplication.class, args); //初始化掌静脉sdk - int initCode = PalmSDK.init(); - log.debug("掌静脉sdk初始化。。。。。。。。。。:"+initCode); + //int initCode = PalmSDK.init(); + //log.debug("掌静脉sdk初始化。。。。。。。。。。:"+initCode); // //mqtt服务启动 // MqttClientUtil.createClient(); // @@ -38,8 +38,8 @@ public class AdminApplication { @Override public void run() { try { - int code = PalmSDK.release(); - log.debug("掌静脉sdk释放。。。。。。。。。。:"+code); + //int code = PalmSDK.release(); + //log.debug("掌静脉sdk释放。。。。。。。。。。:"+code); } catch (Throwable e) { e.printStackTrace(); } diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppApiController.java b/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppApiController.java index 7f8e462..b9c5bdc 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppApiController.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppApiController.java @@ -346,16 +346,14 @@ public class AppApiController { tenPerson.setStatus(2); tenPerson.setPersonType(Constant.PERSON_TYPE_GUEST); tenPerson.setTenantId(user.getTenantId()); - int res; + String res; try { res = tenPersonService.save(tenPerson); } catch (Exception e) { return R.error(e.getMessage()); } - if (res==2) { - return R.error("照片未检测到人脸"); - }else if(res == 10){ - return R.error("文件不存在"); + if (res!=null) { + return R.error(res); } return R.ok(); } @@ -566,14 +564,14 @@ public class AppApiController { tenPerson.setCreateTime(new Date()); tenPerson.setRegisterType(Constant.RESGISTER_TYPE_APP); tenPerson.setStatus(Constant.PESON_SUATUS_NOMOR); - int res = 10; + String res; try { res = tenPersonService.save(tenPerson); } catch (Exception e) { return R.error(e.getMessage()); } - if (res==2) { - return R.error("照片未检测到人脸"); + if (res!=null) { + return R.error(res); } return R.ok(); } @@ -662,14 +660,14 @@ public class AppApiController { - int res; + String res; try { res = tenPersonService.save(tenPerson); } catch (Exception e) { return R.error(e.getMessage()); } - if (res==2) { - return R.error("照片未检测到人脸"); + if (res!=null) { + return R.error(res); } return R.ok(); } @@ -975,7 +973,12 @@ public class AppApiController { .eq(TenRelation::getParentId,user.getUserId()) .eq(TenRelation::getStudentId,entity.getPersonId())); if(tenRelation!=null) { - return R.error("该学生已和您绑定"); + if(tenRelation.getStatus()==2) { + return R.error("您已提交绑定审批,请耐心等待"); + } + if(tenRelation.getStatus()==1) { + return R.error("该学生已和您绑定"); + } } TenRelation relation = new TenRelation(); relation.setParentId(user.getUserId()); diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppInfoApiController.java b/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppInfoApiController.java index 44733d0..e82082b 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppInfoApiController.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppInfoApiController.java @@ -314,10 +314,10 @@ public class AppInfoApiController { //业主 else if(scope.getRoleId() == Constant.PERSON_TYPE_PARENT){ List relationList = relationService.list(new LambdaQueryWrapper() - .eq(TenRelation::getParentId,user.getUserId())); + .eq(TenRelation::getParentId,user.getUserId()).eq(TenRelation::getStatus,1)); if(!relationList.isEmpty()) { List list = relationList.stream().map(TenRelation::getStudentId).collect(Collectors.toList()); - params.put("cellId",scope.getCellId()); + params.put("tenantId",user.getTenantId()); params.put("personIds",list); PageUtils page = tenRecordService.getByPersonIds(params); return R.ok().put("data", page); diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppLoginController.java b/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppLoginController.java index 3bb1854..784b42d 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppLoginController.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/app/controller/AppLoginController.java @@ -1,6 +1,7 @@ package net.shapelight.modules.app.controller; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import net.shapelight.common.utils.IpUtils; import net.shapelight.common.utils.R; import net.shapelight.common.utils.ServletUtils; @@ -15,6 +16,8 @@ import net.shapelight.modules.app.utils.JwtUtils; //import net.shapelight.modules.sys.service.PushService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import net.shapelight.modules.ten.entity.TenParent; +import net.shapelight.modules.ten.service.TenParentService; import net.shapelight.modules.vo.TokenVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -37,6 +40,8 @@ public class AppLoginController { private JwtUtils jwtUtils; // @Autowired // PushService pushService; + @Autowired + TenParentService parentService; /** * 登录 @@ -76,6 +81,8 @@ public class AppLoginController { String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); user.setLoginTime(new Date()); user.setLoginIp(ip); + TenParent parent = parentService.getOne(new LambdaQueryWrapper().eq(TenParent::getUserId,user.getUserId())); + user.setParent(parent); userService.saveOrUpdate(user); return R.ok().put("data", user); } diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/app/entity/AppUserEntity.java b/shapelight-admin/src/main/java/net/shapelight/modules/app/entity/AppUserEntity.java index a88cbb5..0d27d52 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/app/entity/AppUserEntity.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/app/entity/AppUserEntity.java @@ -1,5 +1,6 @@ package net.shapelight.modules.app.entity; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -10,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; +import net.shapelight.modules.ten.entity.TenParent; /** * App用户 @@ -76,5 +78,8 @@ public class AppUserEntity implements Serializable { @JsonSerialize(using = ToStringSerializer.class) private Long sysUserId; + @TableField(exist = false) + TenParent parent; + } diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/excel/listener/PersonExcelListener.java b/shapelight-admin/src/main/java/net/shapelight/modules/excel/listener/PersonExcelListener.java index 4bd4d80..4a6c4b1 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/excel/listener/PersonExcelListener.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/excel/listener/PersonExcelListener.java @@ -8,7 +8,6 @@ import io.minio.PutObjectOptions; import lombok.Getter; import net.shapelight.common.utils.R; import net.shapelight.common.utils.UUIDUtil; -import net.shapelight.commons.engine.sdk.PicSDK; import net.shapelight.modules.excel.model.PersonModel; import net.shapelight.modules.ten.entity.*; import net.shapelight.modules.ten.service.TenCellDeptService; diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/FaceEngineAutoRun.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/FaceEngineAutoRun.java new file mode 100644 index 0000000..f68757c --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/FaceEngineAutoRun.java @@ -0,0 +1,117 @@ +package net.shapelight.modules.face; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; +import net.shapelight.common.config.GlobalValue; +import net.shapelight.modules.face.entity.UserCompareInfo; +import net.shapelight.modules.face.service.FaceEngineService; +import net.shapelight.modules.face.util.Base64Util; +import net.shapelight.modules.face.util.UserInfo; +import net.shapelight.modules.face.util.UserRamGroup; +import net.shapelight.modules.sys.controller.AbstractController; +import net.shapelight.modules.ten.entity.TenCellEntity; +import net.shapelight.modules.ten.entity.TenPersonEntity; +import net.shapelight.modules.ten.service.TenCellService; +import net.shapelight.modules.ten.service.TenPersonService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@Order(1) +@Slf4j +public class FaceEngineAutoRun extends AbstractController implements ApplicationRunner { + @Autowired + private FaceEngineService faceEngineService; + @Autowired + private TenPersonService tenPersonService; + @Autowired + private TenCellService tenCellService; + @Autowired + private GlobalValue globalValue; + + @Override + public void run(ApplicationArguments args) throws Exception { + // 任务初始化 + log.debug("服务启动。。。。。初始化人脸库"); +// Map fileMap = Maps.newHashMap(); +// fileMap.put("zhao1", "赵丽颖"); +// fileMap.put("yang1", "杨紫"); +// fileMap.put("baixue", "白雪"); +// fileMap.put("chenchuang", "陈创"); +// for (String f : fileMap.keySet()) { +// ClassPathResource resource = new ClassPathResource("static/images/" + f + ".jpg"); +// InputStream inputStream = resource.getInputStream(); +// ImageInfo rgbData = ImageFactory.getRGBData(inputStream); +// List faceInfoList = faceEngineService.detectFaces(rgbData); +// if (CollectionUtil.isNotEmpty(faceInfoList)) { +// byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfoList.get(0), ExtractType.REGISTER); +// UserRamCache.UserInfo userInfo = new UserCompareInfo(); +// userInfo.setFaceId(f); +// userInfo.setName(fileMap.get(f)); +// userInfo.setFaceFeature(feature); +// //这边注册到内存缓存中,也可以根据业务,注册到数据库中 +// UserRamCache.addUser(userInfo); +// } +// } + +// int count = tenPersonService.findCount(); +// int pageSize = 1000; +// int page = count/pageSize; +// if(count%1000!=0){ +// page = page+1; +// } +// int faceCount = 0; +// for (int i = 0; i < page; i++) { +// int start = i*1000; +// List listPage = tenPersonService.listPage(start,1000); +// for(TenPersonEntity personEntity: listPage){ +// if(personEntity.getFeature()!=null && personEntity.getFeature().length()>0){ +// UserRamCache.UserInfo userInfo = new UserCompareInfo(); +// userInfo.setFaceId(personEntity.getPersonId()+""); +// userInfo.setName(personEntity.getName()); +// userInfo.setFaceFeature(Base64Util.base64ToBytes(personEntity.getFeature())); +// //这边注册到内存缓存中 +// UserRamCache.addUser(userInfo); +// faceCount++; +// } +// } +// } + + + List cellList = tenCellService.list(new QueryWrapper() + .eq("tenant_id","1298283126102949890") + .eq("delete_flag",0)); + for(TenCellEntity cellEntity: cellList){ + String cellId = cellEntity.getCellId()+""; + UserRamGroup.addCell(cellId); + int count = tenPersonService.findCellCount(Long.valueOf(cellId)); + int pageSize = 1000; + int page = count/pageSize; + if(count%1000!=0){ + page = page+1; + } + int faceCount = 0; + for (int i = 0; i < page; i++) { + int start = i*1000; + List listPage = tenPersonService.listPage(start,1000, cellId); + for(TenPersonEntity personEntity: listPage){ + if(personEntity.getFeature()!=null && personEntity.getFeature().length()>0){ + UserInfo userInfo = new UserCompareInfo(); + userInfo.setFaceId(personEntity.getPersonId()+""); + userInfo.setName(personEntity.getName()); + userInfo.setFaceFeature(Base64Util.base64ToBytes(personEntity.getFeature())); + //这边注册到内存缓存中 + UserRamGroup.addUser(userInfo,cellId); + faceCount++; + } + } + } + log.debug(cellEntity.getName()+":初始化人脸库完成,共 "+faceCount+" 人"); + } + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/config/ArcFaceAutoConfiguration.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/config/ArcFaceAutoConfiguration.java new file mode 100644 index 0000000..605c24e --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/config/ArcFaceAutoConfiguration.java @@ -0,0 +1,133 @@ +package net.shapelight.modules.face.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; +import java.util.List; + +@Slf4j +@Configuration +public class ArcFaceAutoConfiguration implements InitializingBean, DisposableBean { + + + private static final String PLATFORM; + + private static final String USER_HOME; + + public static String CACHE_LIB_FOLDER; + + @Value("${config.arcface-sdk.version}") + public String ARC_FACE_VERSION; + + static { + String jvmName = System.getProperty("java.vm.name", "").toLowerCase(); + String osName = System.getProperty("os.name", "").toLowerCase(); + String osArch = System.getProperty("os.arch", "").toLowerCase(); + String abiType = System.getProperty("sun.arch.abi", "").toLowerCase(); + String libPath = System.getProperty("sun.boot.library.path", "").toLowerCase(); + USER_HOME = System.getProperty("user.home"); + if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) { + osName = "android"; + } else if (jvmName.startsWith("robovm") && osName.startsWith("darwin")) { + osName = "ios"; + osArch = "arm"; + } else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) { + osName = "macosx"; + } else { + int spaceIndex = osName.indexOf(' '); + if (spaceIndex > 0) { + osName = osName.substring(0, spaceIndex); + } + } + if (osArch.equals("i386") || osArch.equals("i486") || osArch.equals("i586") || osArch.equals("i686")) { + osArch = "x86"; + } else if (osArch.equals("amd64") || osArch.equals("x86-64") || osArch.equals("x64")) { + osArch = "x86_64"; + } else if (osArch.startsWith("aarch64") || osArch.startsWith("armv8") || osArch.startsWith("arm64")) { + osArch = "arm64"; + } else if ((osArch.startsWith("arm")) && ((abiType.equals("gnueabihf")) || (libPath.contains("openjdk-armhf")))) { + osArch = "armhf"; + } else if (osArch.startsWith("arm")) { + osArch = "arm"; + } + PLATFORM = osName + "-" + osArch; + + } + + + @Override + public void afterPropertiesSet() throws IOException { + CACHE_LIB_FOLDER = USER_HOME + "/.arcface/cache/" + ARC_FACE_VERSION + "/" + PLATFORM + "/"; + loadLibrary(); + } + + public void loadLibrary() throws IOException { + String baseFolder = ""; + String suffix = ".dll"; + if ("windows-x86_64".equals(PLATFORM)) { + baseFolder = "WIN64"; + } else if ("windows-x86".equals(PLATFORM)) { + baseFolder = "WIN32"; + } else if ("linux-x86_64".equals(PLATFORM)) { + baseFolder = "LINUX64"; + suffix = ".so"; + } + + if ("".equals(baseFolder)) { + throw new RuntimeException("ArcFace不支持该操作系统"); + } + + + File file = new File(CACHE_LIB_FOLDER); + if (!file.exists()) { + file.mkdirs(); + } + + List libList = new LinkedList<>(); + libList.add("libarcsoft_face"); + libList.add("libarcsoft_face_engine"); + libList.add("libarcsoft_face_engine_jni"); + + for (String lib : libList) { + ClassPathResource resource = new ClassPathResource("libs/" + ARC_FACE_VERSION + "/" + baseFolder + "/" + lib + suffix); + InputStream inputStream = resource.getInputStream(); + int faceLength = inputStream.available(); + File facePath = new File(CACHE_LIB_FOLDER + lib + suffix); + if (facePath.exists()) { + if (facePath.length() == faceLength) { + continue; + } + facePath.delete(); + } + writeToLocal(CACHE_LIB_FOLDER + lib + suffix, inputStream); + } + } + + private void writeToLocal(String destination, InputStream input) + throws IOException { + int index; + byte[] bytes = new byte[1024 * 100]; + FileOutputStream fileOutputStream = new FileOutputStream(destination); + while ((index = input.read(bytes)) != -1) { + fileOutputStream.write(bytes, 0, index); + } + fileOutputStream.flush(); + fileOutputStream.close(); + input.close(); + } + + + @Override + public void destroy() throws Exception { + } + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/CompareFacesReqDTO.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/CompareFacesReqDTO.java new file mode 100644 index 0000000..c4971fb --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/CompareFacesReqDTO.java @@ -0,0 +1,14 @@ +package net.shapelight.modules.face.dto; + +import lombok.Data; + +/** + * @author shentao + * @desc + * @date 2022/3/30 + */ +@Data +public class CompareFacesReqDTO { + private String image1; + private String image2; +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceAddReqDTO.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceAddReqDTO.java new file mode 100644 index 0000000..b438dfb --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceAddReqDTO.java @@ -0,0 +1,12 @@ +package net.shapelight.modules.face.dto; + +import lombok.Data; + +@Data +public class FaceAddReqDTO { + + private String name; + + private String image; + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceDetectReqDTO.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceDetectReqDTO.java new file mode 100644 index 0000000..30bf032 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceDetectReqDTO.java @@ -0,0 +1,10 @@ +package net.shapelight.modules.face.dto; + +import lombok.Data; + +@Data +public class FaceDetectReqDTO { + + private String image; + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceDetectResDTO.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceDetectResDTO.java new file mode 100644 index 0000000..45e2ca2 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceDetectResDTO.java @@ -0,0 +1,14 @@ +package net.shapelight.modules.face.dto; + +import com.arcsoft.face.Rect; +import lombok.Data; + +@Data +public class FaceDetectResDTO { + private Rect rect; + private int orient; + private int faceId = -1; + private int age = -1; + private int gender = -1; + private int liveness = -1; +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceRecognitionReqDTO.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceRecognitionReqDTO.java new file mode 100644 index 0000000..5d4dd52 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceRecognitionReqDTO.java @@ -0,0 +1,10 @@ +package net.shapelight.modules.face.dto; + +import lombok.Data; + +@Data +public class FaceRecognitionReqDTO { + + private String image; + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceRecognitionResDTO.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceRecognitionResDTO.java new file mode 100644 index 0000000..3d77ef9 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/FaceRecognitionResDTO.java @@ -0,0 +1,15 @@ +package net.shapelight.modules.face.dto; + + +import com.arcsoft.face.Rect; +import lombok.Data; + +@Data +public class FaceRecognitionResDTO { + + private Rect rect; + private String personId; + private String name; + private float similar; + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/GetFaceListResDTO.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/GetFaceListResDTO.java new file mode 100644 index 0000000..beecd7a --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/dto/GetFaceListResDTO.java @@ -0,0 +1,14 @@ +package net.shapelight.modules.face.dto; + +import lombok.Data; + +@Data +public class GetFaceListResDTO { + + private String id; + + private String name; + + private String url; + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/entity/ProcessInfo.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/entity/ProcessInfo.java new file mode 100644 index 0000000..01e0fe9 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/entity/ProcessInfo.java @@ -0,0 +1,12 @@ +package net.shapelight.modules.face.entity; + + +import lombok.Data; + +@Data +public class ProcessInfo { + private int age; + private int gender; + private int liveness; + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/entity/UserCompareInfo.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/entity/UserCompareInfo.java new file mode 100644 index 0000000..c7bc271 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/entity/UserCompareInfo.java @@ -0,0 +1,10 @@ +package net.shapelight.modules.face.entity; + + +import lombok.Data; +import net.shapelight.modules.face.util.UserInfo; + +@Data +public class UserCompareInfo extends UserInfo { + private Float similar; +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/enums/ErrorCodeEnum.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/enums/ErrorCodeEnum.java new file mode 100644 index 0000000..50795eb --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/enums/ErrorCodeEnum.java @@ -0,0 +1,49 @@ +package net.shapelight.modules.face.enums; + + +import lombok.Getter; +import net.shapelight.modules.face.rpc.ErrorCode; + +@Getter +public enum ErrorCodeEnum implements ErrorCode { + + /** + * 成功 + */ + SUCCESS(0, "success", "成功"), + FAIL(1, "fail", "失败"), + PARAM_ERROR(2, "param error", "参数错误"), + SYSTEM_ERROR(999, "system error", "系统错误"), + + ; + private Integer code; + private String desc; + private String descCN; + + ErrorCodeEnum(Integer code, String desc) { + this.code = code; + this.desc = desc; + } + + ErrorCodeEnum(Integer code, String desc, String descCN) { + this.code = code; + this.desc = desc; + this.descCN = descCN; + } + + @Override + public Integer getCode() { + return code; + } + + @Override + public String getDesc() { + return desc; + } + + @Override + public String getDescCN() { + return descCN; + } + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/face/FaceRecognize.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/face/FaceRecognize.java new file mode 100644 index 0000000..6e9f829 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/face/FaceRecognize.java @@ -0,0 +1,292 @@ +package net.shapelight.modules.face.face; + +import com.arcsoft.face.*; +import com.arcsoft.face.enums.DetectMode; +import com.arcsoft.face.enums.ErrorInfo; +import com.arcsoft.face.enums.ExtractType; +import com.arcsoft.face.toolkit.ImageFactory; +import com.arcsoft.face.toolkit.ImageInfo; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import net.shapelight.modules.face.config.ArcFaceAutoConfiguration; +import net.shapelight.modules.face.factory.FaceEngineFactory; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +import java.io.File; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +public class FaceRecognize { + + /** + * VIDEO模式人脸检测引擎,用于预览帧人脸追踪 + */ + private FaceEngine ftEngine; + + /** + * 人脸注册引擎 + */ + private FaceEngine regEngine; + + /** + * 用于人脸识别的引擎池 + */ + private GenericObjectPool frEnginePool; + + + private volatile ConcurrentHashMap faceResultRegistry = new ConcurrentHashMap<>(); + + private ExecutorService frService = Executors.newFixedThreadPool(20); + + public ConcurrentHashMap faceFeatureRegistry = new ConcurrentHashMap<>(); + + /** + * 初始化引擎 + */ + public void initEngine(String appId, String sdkKey, String activeKey, String activeFile) { + + //引擎配置 + ftEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER); + int activeCode; + if (StringUtils.isNotEmpty(activeFile)) { + activeCode = ftEngine.activeOffline(activeFile); + } else { + activeCode = ftEngine.activeOnline(appId, sdkKey, activeKey); + } + + EngineConfiguration ftEngineCfg = new EngineConfiguration(); + ftEngineCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_VIDEO); + ftEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceDetect(true).build()); + int ftInitCode = ftEngine.init(ftEngineCfg); + + //引擎配置 + regEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER); + + EngineConfiguration regEngineCfg = new EngineConfiguration(); + regEngineCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE); + regEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceDetect(true).supportFaceRecognition(true).build()); + int regInitCode = regEngine.init(regEngineCfg); + + + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMaxIdle(5); + poolConfig.setMaxTotal(5); + poolConfig.setMinIdle(5); + poolConfig.setLifo(false); + EngineConfiguration frEngineCfg = new EngineConfiguration(); + frEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceRecognition(true).build()); + frEnginePool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey,activeFile, frEngineCfg), poolConfig);//底层库算法对象池 + + + if (!(activeCode == ErrorInfo.MOK.getValue() || activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue())) { + log.error("activeCode: " + activeCode); + throw new RuntimeException("activeCode: " + activeCode); + } + if (ftInitCode != ErrorInfo.MOK.getValue()) { + log.error("ftInitEngine: " + ftInitCode); + throw new RuntimeException("ftInitEngine: " + ftInitCode); + } + + if (regInitCode != ErrorInfo.MOK.getValue()) { + log.error("regInitEngine: " + regInitCode); + throw new RuntimeException("regInitEngine: " + regInitCode); + } + + } + + + public void registerFace(String imagePath) { + + log.info("正在注册人脸"); + + int count = 0; + if (regEngine != null) { + File file = new File(imagePath); + File[] files = file.listFiles(); + + for (File file1 : files) { + ImageInfo imageInfo = ImageFactory.getRGBData(file1); + if (imageInfo != null) { + List faceInfoList = new ArrayList<>(); + int code = regEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), + imageInfo.getImageFormat(), faceInfoList); + + if (code == 0 && faceInfoList.size() > 0) { + FaceFeature faceFeature = new FaceFeature(); + int resCode = regEngine.extractFaceFeature(imageInfo, faceInfoList.get(0), ExtractType.REGISTER, 0, faceFeature); + if (resCode == 0) { + int lastIndexOf = file1.getName().lastIndexOf("."); + String name = file1.getName().substring(0, file1.getName().length() - lastIndexOf - 1); + faceFeatureRegistry.put(name, faceFeature.getFeatureData()); + log.info("成功注册人脸:" + name); + count++; + } + } + } + } + log.info("人脸注册完成,共注册:" + count + "张人脸"); + } else { + throw new RuntimeException("注册失败,引擎未初始化或初始化失败"); + } + + + } + + public void registerFace(Map face) { + face.forEach((k, v) -> { + faceFeatureRegistry.put(k, v.clone()); + }); + } + + public void removeFace(String name) { + faceFeatureRegistry.remove(name); + } + + public void clearFace() { + faceFeatureRegistry.clear(); + } + + public FaceResult getFaceResult(FaceInfo faceInfo, ImageInfo imageInfo) { + FaceResult faceResult = faceResultRegistry.get(faceInfo.getFaceId()); + if (faceResult == null) { + faceResult = new FaceResult(); + faceResultRegistry.put(faceInfo.getFaceId(), faceResult); + frService.submit(new FaceInfoRunnable(faceInfo, imageInfo, faceResult)); + } else if (faceResult.isFlag()) { + return faceResult; + } + return null; + } + + public List detectFaces(ImageInfo imageInfo) { + if (ftEngine != null) { + List faceInfoList = new ArrayList<>(); + int code = ftEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), + imageInfo.getImageFormat(), faceInfoList); + + List previewInfoList = new LinkedList<>(); + for (FaceInfo faceInfo : faceInfoList) { + FacePreviewInfo facePreviewInfo = new FacePreviewInfo(); + facePreviewInfo.setFaceInfo(faceInfo); + previewInfoList.add(facePreviewInfo); + } + + clearFaceResultRegistry(faceInfoList); + return previewInfoList; + + } + return null; + } + + + private long lastClearTime = System.currentTimeMillis(); + + //清理过时的人脸 + private void clearFaceResultRegistry(List faceInfoList) { + if (System.currentTimeMillis() - lastClearTime > 5000) { + Iterator iterator = faceResultRegistry.keySet().iterator(); + for (; iterator.hasNext(); ) { + Integer next = iterator.next(); + boolean flag = false; + for (FaceInfo faceInfo : faceInfoList) { + if (next.equals(faceInfo.getFaceId())) { + flag = true; + } + } + if (!flag) { + iterator.remove(); + } + + } + lastClearTime = System.currentTimeMillis(); + } + + + } + + + @Data + public class FaceResult { + private boolean flag = false; + private String name; + private float score; + + + } + + @Data + public class FacePreviewInfo { + + private FaceInfo faceInfo; + private int age; + private boolean liveness; + + } + + + private class FaceInfoRunnable implements Runnable { + private FaceInfo faceInfo; + private ImageInfo imageInfo; + private FaceResult faceResult; + + public FaceInfoRunnable(FaceInfo faceInfo, ImageInfo imageInfo, FaceResult faceResult) { + this.faceInfo = faceInfo; + this.imageInfo = imageInfo; + this.faceResult = faceResult; + } + + @Override + public void run() { + FaceEngine frEngine = null; + try { + frEngine = frEnginePool.borrowObject(); + if (frEngine != null) { + FaceFeature faceFeature = new FaceFeature(); + int resCode = frEngine.extractFaceFeature(imageInfo, faceInfo, ExtractType.RECOGNIZE, 0, faceFeature); + if (resCode == 0) { + + float score = 0.0F; + Iterator> iterator = faceFeatureRegistry.entrySet().iterator(); + for (; iterator.hasNext(); ) { + Map.Entry next = iterator.next(); + FaceFeature faceFeatureTarget = new FaceFeature(); + faceFeatureTarget.setFeatureData(next.getValue()); + + FaceSimilar faceSimilar = new FaceSimilar(); + frEngine.compareFaceFeature(faceFeatureTarget, faceFeature, faceSimilar); + if (faceSimilar.getScore() > score) { + score = faceSimilar.getScore(); + faceResult.setName(next.getKey()); + } + } + + log.info("相似度:" + score); + if (score >= 0.8f) { + faceResult.setScore(score); + faceResult.setFlag(true); + faceResultRegistry.put(faceInfo.getFaceId(), faceResult); + } else { + faceResultRegistry.remove(faceInfo.getFaceId()); + } + + } + } + } catch (Exception e) { + + } finally { + if (frEngine != null) { + frEnginePool.returnObject(frEngine); + } + } + + + } + } + + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/factory/FaceEngineFactory.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/factory/FaceEngineFactory.java new file mode 100644 index 0000000..bc90db4 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/factory/FaceEngineFactory.java @@ -0,0 +1,73 @@ +package net.shapelight.modules.face.factory; + +import com.arcsoft.face.EngineConfiguration; +import com.arcsoft.face.FaceEngine; +import com.arcsoft.face.enums.ErrorInfo; +import lombok.extern.slf4j.Slf4j; +import net.shapelight.modules.face.config.ArcFaceAutoConfiguration; +import net.shapelight.modules.face.enums.ErrorCodeEnum; +import net.shapelight.modules.face.rpc.BusinessException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.pool2.BasePooledObjectFactory; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.impl.DefaultPooledObject; + +@Slf4j +public class FaceEngineFactory extends BasePooledObjectFactory { + + private String appId; + private String sdkKey; + private String activeKey; + private String activeFile; + private EngineConfiguration engineConfiguration; + + + public FaceEngineFactory(String appId, String sdkKey, String activeKey, String activeFile, EngineConfiguration engineConfiguration) { + this.appId = appId; + this.sdkKey = sdkKey; + this.activeKey = activeKey; + this.activeFile=activeFile; + this.engineConfiguration = engineConfiguration; + } + + + @Override + public FaceEngine create() { + + + FaceEngine faceEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER); + +// FaceEngine faceEngine = new FaceEngine("/home/huangyifang/gb/咸阳师范/ArcSoft_ArcFacePro_linux_java_V4.1/libs/LINUX64/"); + int activeCode; + if (StringUtils.isNotEmpty(activeFile)) { + activeCode = faceEngine.activeOffline(activeFile); + } else { + activeCode = faceEngine.activeOnline(appId, sdkKey, activeKey); + } + log.debug("引擎激活errorCode:" + activeCode); + if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) { + log.error("引擎激活失败" + activeCode); + throw new BusinessException(ErrorCodeEnum.FAIL, "引擎激活失败" + activeCode); + } + int initCode = faceEngine.init(engineConfiguration); + if (initCode != ErrorInfo.MOK.getValue()) { + log.error("引擎初始化失败" + initCode); + throw new BusinessException(ErrorCodeEnum.FAIL, "引擎初始化失败" + initCode); + } + log.debug("初始化引擎errorCode:" + initCode); + return faceEngine; + } + + @Override + public PooledObject wrap(FaceEngine faceEngine) { + return new DefaultPooledObject<>(faceEngine); + } + + + @Override + public void destroyObject(PooledObject p) throws Exception { + FaceEngine faceEngine = p.getObject(); + int result = faceEngine.unInit(); + super.destroyObject(p); + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/BusinessException.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/BusinessException.java new file mode 100644 index 0000000..4675a16 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/BusinessException.java @@ -0,0 +1,64 @@ +package net.shapelight.modules.face.rpc; + +import lombok.Data; + +/** + * @Author: st7251 + * @Date: 2018/11/23 14:18 + */ +@Data +public class BusinessException extends RuntimeException { + private ErrorCode errorCode; + private String msg; + private String msgCN; + + public BusinessException(Response response) { + this.errorCode = new ErrorCode() { + @Override + public Integer getCode() { + return response.getCode(); + } + + @Override + public String getDesc() { + return response.getMsg(); + } + + @Override + public String getDescCN() { + return response.getMsg(); + } + }; + this.msg=response.getMsg(); + this.msgCN=response.getMsg(); + } + + public BusinessException(ErrorCode errorCode) { + super(errorCode.getDesc()); + this.errorCode = errorCode; + this.msg= errorCode.getDesc(); + this.msgCN=errorCode.getDescCN(); + } + + public BusinessException(ErrorCode errorCode, String msg) { + super(errorCode.getDesc()); + this.errorCode = errorCode; + this.msg = msg; + this.msgCN=msg; + } + + public BusinessException(Throwable cause, ErrorCode errorCode) { + super(cause); + this.errorCode = errorCode; + this.msg= errorCode.getDesc(); + this.msgCN=errorCode.getDescCN(); + } + + + public BusinessException(Throwable cause, ErrorCode errorCode, String msg) { + super(cause); + this.errorCode = errorCode; + this.msg = msg; + this.msgCN=msg; + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/ErrorCode.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/ErrorCode.java new file mode 100644 index 0000000..e22ec67 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/ErrorCode.java @@ -0,0 +1,12 @@ +package net.shapelight.modules.face.rpc; + +public interface ErrorCode { + + + Integer getCode(); + + String getDesc(); + + String getDescCN(); + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/GlobalExceptionHandler.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/GlobalExceptionHandler.java new file mode 100644 index 0000000..ddd04ef --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/GlobalExceptionHandler.java @@ -0,0 +1,47 @@ +package net.shapelight.modules.face.rpc; + + +import lombok.extern.slf4j.Slf4j; +import net.shapelight.modules.face.enums.ErrorCodeEnum; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler{ + + + /** + * 自定义异常 + */ + @ExceptionHandler(BusinessException.class) + public Response businessException(BusinessException e) { + log.error(e.getMessage(), e); + Response response = new Response(); + response.setCode(e.getErrorCode().getCode()); + response.setMsg(e.getMsgCN()); + return response; + } + + @ExceptionHandler(IllegalArgumentException.class) + public Response handleIllegalArgumentException(IllegalArgumentException e) { + log.error(e.getMessage(), e); + Response response = new Response(); + response.setCode(ErrorCodeEnum.PARAM_ERROR.getCode()); + response.setMsg(e.getMessage()); + return response; + } + + @ExceptionHandler(Exception.class) + public Response handleException(Exception e) { + log.error(e.getMessage(), e); + Response response = new Response(); + response.setCode(ErrorCodeEnum.SYSTEM_ERROR.getCode()); + response.setMsg(ErrorCodeEnum.SYSTEM_ERROR.getDescCN()); + return response; + } + + + + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/Response.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/Response.java new file mode 100644 index 0000000..926d866 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/rpc/Response.java @@ -0,0 +1,39 @@ +package net.shapelight.modules.face.rpc; + +import lombok.Data; + +@Data +public class Response { + + private int code = -1; + private String msg = "success"; + private T data; + + public static Response newSuccessResponse(T data) { + return newResponse(data, 0, "success"); + } + + public static Response newFailedResponse(Integer code, String message) { + return newResponse(null, code, message); + } + + public static Response newFailedResponse(ErrorCode ErrorCode) { + return newResponse(null, ErrorCode.getCode(), ErrorCode.getDesc()); + } + + public static Response newFailedResponse(ErrorCode ErrorCode, String message) { + return newResponse(null, ErrorCode.getCode(), message); + } + + public static Response newResponse(T data, Integer code, String message) { + Response response = new Response(); + response.setCode(code); + response.setMsg(message); + if (data != null && data instanceof String && "".equals(data)) { + response.setData(null); + } else { + response.setData(data); + } + return response; + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/service/FaceEngineService.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/service/FaceEngineService.java new file mode 100644 index 0000000..04d1b02 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/service/FaceEngineService.java @@ -0,0 +1,28 @@ +package net.shapelight.modules.face.service; + + +import com.arcsoft.face.FaceInfo; +import com.arcsoft.face.enums.ExtractType; +import com.arcsoft.face.toolkit.ImageInfo; +import net.shapelight.modules.face.entity.ProcessInfo; +import net.shapelight.modules.face.entity.UserCompareInfo; +import net.shapelight.modules.face.util.UserInfo; + +import java.util.List; + + +public interface FaceEngineService { + + List detectFaces(ImageInfo imageInfo); + + Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) ; + + byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType); + + List faceRecognition(byte[] faceFeature, List userInfoList, float passRate) ; + + List process(ImageInfo imageInfo, List faceInfoList); + + + +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/service/impl/FaceEngineServiceImpl.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/service/impl/FaceEngineServiceImpl.java new file mode 100644 index 0000000..411f541 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/service/impl/FaceEngineServiceImpl.java @@ -0,0 +1,349 @@ +package net.shapelight.modules.face.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.arcsoft.face.*; +import com.arcsoft.face.enums.DetectMode; +import com.arcsoft.face.enums.DetectOrient; +import com.arcsoft.face.enums.ExtractType; +import com.arcsoft.face.toolkit.ImageInfo; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import net.shapelight.modules.face.entity.ProcessInfo; +import net.shapelight.modules.face.entity.UserCompareInfo; +import net.shapelight.modules.face.enums.ErrorCodeEnum; +import net.shapelight.modules.face.factory.FaceEngineFactory; +import net.shapelight.modules.face.rpc.BusinessException; +import net.shapelight.modules.face.service.FaceEngineService; +import net.shapelight.modules.face.util.UserInfo; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + + +@Service("faceEngineService") +@Slf4j +public class FaceEngineServiceImpl implements FaceEngineService { + + public final static Logger logger = LoggerFactory.getLogger(FaceEngineServiceImpl.class); + + @Value("${config.arcface-sdk.app-id}") + public String appId; + + @Value("${config.arcface-sdk.sdk-key}") + public String sdkKey; + + @Value("${config.arcface-sdk.active-key}") + public String activeKey; + + @Value("${config.arcface-sdk.active-file}") + public String activeFile; + + @Value("${config.arcface-sdk.detect-pool-size}") + public Integer detectPooSize; + + @Value("${config.arcface-sdk.compare-pool-size}") + public Integer comparePooSize; + + private ExecutorService compareExecutorService; + + //通用人脸识别引擎池 + private GenericObjectPool faceEngineGeneralPool; + + //人脸比对引擎池 + private GenericObjectPool faceEngineComparePool; + + @PostConstruct + public void init() { + + + GenericObjectPoolConfig detectPoolConfig = new GenericObjectPoolConfig(); + detectPoolConfig.setMaxIdle(detectPooSize); + detectPoolConfig.setMaxTotal(detectPooSize); + detectPoolConfig.setMinIdle(detectPooSize); + detectPoolConfig.setLifo(false); + EngineConfiguration detectCfg = new EngineConfiguration(); + FunctionConfiguration detectFunctionCfg = new FunctionConfiguration(); + detectFunctionCfg.setSupportFaceDetect(true);//开启人脸检测功能 + detectFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能 + detectFunctionCfg.setSupportAge(true);//开启年龄检测功能 + detectFunctionCfg.setSupportGender(true);//开启性别检测功能 + detectFunctionCfg.setSupportLiveness(true);//开启活体检测功能 + detectCfg.setFunctionConfiguration(detectFunctionCfg); + detectCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式,如果是连续帧的视频流图片,那么改成VIDEO模式 + detectCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);//人脸旋转角度 + faceEngineGeneralPool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey, activeFile, detectCfg), detectPoolConfig);//底层库算法对象池 + + + //初始化特征比较线程池 + GenericObjectPoolConfig comparePoolConfig = new GenericObjectPoolConfig(); + comparePoolConfig.setMaxIdle(comparePooSize); + comparePoolConfig.setMaxTotal(comparePooSize); + comparePoolConfig.setMinIdle(comparePooSize); + comparePoolConfig.setLifo(false); + EngineConfiguration compareCfg = new EngineConfiguration(); + FunctionConfiguration compareFunctionCfg = new FunctionConfiguration(); + compareFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能 + compareCfg.setFunctionConfiguration(compareFunctionCfg); + compareCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式,如果是连续帧的视频流图片,那么改成VIDEO模式 + compareCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);//人脸旋转角度 + faceEngineComparePool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey, activeFile, compareCfg), comparePoolConfig);//底层库算法对象池 + compareExecutorService = Executors.newFixedThreadPool(comparePooSize); + } + + + @Override + public List detectFaces(ImageInfo imageInfo) { + + FaceEngine faceEngine = null; + try { + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败"); + } + + //人脸检测得到人脸列表 + List faceInfoList = new ArrayList(); + //人脸检测 + int errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList); + if (errorCode == 0) { + return faceInfoList; + } else { + log.error("人脸检测失败,errorCode:" + errorCode); + } + + } catch (Exception e) { + log.error("", e); + } finally { + if (faceEngine != null) { + //释放引擎对象 + faceEngineGeneralPool.returnObject(faceEngine); + } + } + + return null; + + } + + @Override + public Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) { + + List faceInfoList1 = detectFaces(imageInfo1); + List faceInfoList2 = detectFaces(imageInfo2); + + if (CollectionUtil.isEmpty(faceInfoList1)) { + throw new BusinessException(ErrorCodeEnum.FAIL, "照片1未检测到人脸"); + } + if (CollectionUtil.isEmpty(faceInfoList2)) { + throw new BusinessException(ErrorCodeEnum.FAIL, "照片2未检测到人脸"); + } + + byte[] feature1 = extractFaceFeature(imageInfo1, faceInfoList1.get(0), ExtractType.REGISTER); + byte[] feature2 = extractFaceFeature(imageInfo2, faceInfoList2.get(0), ExtractType.RECOGNIZE); + + FaceEngine faceEngine = null; + try { + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败"); + } + + FaceFeature faceFeature1 = new FaceFeature(); + faceFeature1.setFeatureData(feature1); + FaceFeature faceFeature2 = new FaceFeature(); + faceFeature2.setFeatureData(feature2); + //提取人脸特征 + FaceSimilar faceSimilar = new FaceSimilar(); + int errorCode = faceEngine.compareFaceFeature(faceFeature1, faceFeature2, faceSimilar); + if (errorCode == 0) { + return faceSimilar.getScore(); + } else { + log.error("特征提取失败,errorCode:" + errorCode); + } + + } catch (Exception e) { + log.error("", e); + } finally { + if (faceEngine != null) { + //释放引擎对象 + faceEngineGeneralPool.returnObject(faceEngine); + } + } + + return null; + + } + + /** + * 人脸特征 + * + * @param imageInfo + * @return + */ + @Override + public byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType) { + + FaceEngine faceEngine = null; + try { + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败"); + } + + FaceFeature faceFeature = new FaceFeature(); + //提取人脸特征 + int errorCode = faceEngine.extractFaceFeature(imageInfo, faceInfo, extractType, 0, faceFeature); + if (errorCode == 0) { + return faceFeature.getFeatureData(); + } else { + log.error("特征提取失败,errorCode:" + errorCode); + } + + } catch (Exception e) { + log.error("", e); + } finally { + if (faceEngine != null) { + //释放引擎对象 + faceEngineGeneralPool.returnObject(faceEngine); + } + } + + return null; + + } + + @Override + public List faceRecognition(byte[] faceFeature, List userInfoList, float passRate) { + List resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表 + + FaceFeature targetFaceFeature = new FaceFeature(); + targetFaceFeature.setFeatureData(faceFeature); + + List> faceUserInfoPartList = Lists.partition(userInfoList, 1000);//分成1000一组,多线程处理 + CompletionService> completionService = new ExecutorCompletionService(compareExecutorService); + for (List part : faceUserInfoPartList) { + completionService.submit(new CompareFaceTask(part, targetFaceFeature, passRate)); + } + for (int i = 0; i < faceUserInfoPartList.size(); i++) { + List faceUserInfoList = null; + try { + faceUserInfoList = completionService.take().get(); + } catch (InterruptedException | ExecutionException e) { + } + if (CollectionUtil.isNotEmpty(userInfoList)) { + resultUserInfoList.addAll(faceUserInfoList); + } + } + + resultUserInfoList.sort((h1, h2) -> h2.getSimilar().compareTo(h1.getSimilar()));//从大到小排序 + + return resultUserInfoList; + } + + + + @Override + public List process(ImageInfo imageInfo, List faceInfoList) { + FaceEngine faceEngine = null; + try { + //获取引擎对象 + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败"); + } + + + int errorCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList, FunctionConfiguration.builder().supportAge(true).supportGender(true).supportLiveness(true).build()); + if (errorCode == 0) { + List processInfoList = Lists.newLinkedList(); + + //性别列表 + List genderInfoList = new ArrayList(); + faceEngine.getGender(genderInfoList); + + //年龄列表 + List ageInfoList = new ArrayList(); + faceEngine.getAge(ageInfoList); + //活体结果列表 + List livenessInfoList = new ArrayList(); + faceEngine.getLiveness(livenessInfoList); + + + for (int i = 0; i < genderInfoList.size(); i++) { + ProcessInfo processInfo = new ProcessInfo(); + processInfo.setGender(genderInfoList.get(i).getGender()); + processInfo.setAge(ageInfoList.get(i).getAge()); + processInfo.setLiveness(livenessInfoList.get(i).getLiveness()); + processInfoList.add(processInfo); + } + return processInfoList; + + } + + + } catch (Exception e) { + logger.error("", e); + } finally { + if (faceEngine != null) { + //释放引擎对象 + faceEngineGeneralPool.returnObject(faceEngine); + } + } + + return null; + + } + + + private class CompareFaceTask implements Callable> { + + private List userInfoList; + private FaceFeature targetFaceFeature; + private float passRate; + + + public CompareFaceTask(List userInfoList, FaceFeature targetFaceFeature, float passRate) { + this.userInfoList = userInfoList; + this.targetFaceFeature = targetFaceFeature; + this.passRate = passRate; + } + + @Override + public List call() throws Exception { + FaceEngine faceEngine = null; + List resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表 + try { + faceEngine = faceEngineComparePool.borrowObject(); + for (UserInfo userInfo : userInfoList) { + FaceFeature sourceFaceFeature = new FaceFeature(); + sourceFaceFeature.setFeatureData(userInfo.getFaceFeature()); + FaceSimilar faceSimilar = new FaceSimilar(); + faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar); + if (faceSimilar.getScore() > passRate) {//相似值大于配置预期,加入到识别到人脸的列表 + UserCompareInfo info = new UserCompareInfo(); + info.setName(userInfo.getName()); + info.setFaceId(userInfo.getFaceId()); + info.setSimilar(faceSimilar.getScore()); + resultUserInfoList.add(info); + } + } + } catch (Exception e) { + logger.error("", e); + } finally { + if (faceEngine != null) { + faceEngineComparePool.returnObject(faceEngine); + } + } + + return resultUserInfoList; + } + + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/util/Base64Util.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/Base64Util.java new file mode 100644 index 0000000..29db3c6 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/Base64Util.java @@ -0,0 +1,33 @@ +package net.shapelight.modules.face.util; + +import org.springframework.util.ObjectUtils; + +import java.util.Base64; + +public class Base64Util { + public static String base64Process(String base64Str) { + if (!ObjectUtils.isEmpty(base64Str)) { + String photoBase64 = base64Str.substring(0, 30).toLowerCase(); + int indexOf = photoBase64.indexOf("base64,"); + if (indexOf > 0) { + base64Str = base64Str.substring(indexOf + 7); + } + base64Str = base64Str.replaceAll(" ", "+"); + base64Str = base64Str.replaceAll("\r|\n", ""); + return base64Str; + } + return ""; + } + + public static byte[] base64ToBytes(String base64) { + if (ObjectUtils.isEmpty(base64)) { + return null; + } + String base64Process = base64Process(base64); + + byte[] decode = Base64.getDecoder().decode(base64Process); + return decode; + + + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/util/FaceEngineTest.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/FaceEngineTest.java new file mode 100644 index 0000000..4e4fa7d --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/FaceEngineTest.java @@ -0,0 +1,225 @@ +package net.shapelight.modules.face.util; + +import com.arcsoft.face.*; +import com.arcsoft.face.enums.*; +import com.arcsoft.face.toolkit.ImageFactory; +import com.arcsoft.face.toolkit.ImageInfo; +import com.arcsoft.face.toolkit.ImageInfoEx; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class FaceEngineTest { + + + public static void main(String[] args) { + + //激活码,从官网获取 + String appId = "DEnAZa1bWXcaAxyWUg33QZaKCmMkNmrQxuKZJQGmZsHJ"; + String sdkKey = "vWbvUyStZeartSaM6QoTzPYWFpSaj4uhfDmRifSzCd6"; + String activeKey = "82G1-11QA-713Y-8NB4"; + + System.err.println("注意,如果返回的errorCode不为0,可查看com.arcsoft.face.enums.ErrorInfo类获取相应的错误信息"); + + //人脸识别引擎库存放路径 + FaceEngine faceEngine = new FaceEngine("/home/huangyifang/gb/咸阳师范/ArcSoft_ArcFacePro_linux_java_V4.1/libs/LINUX64"); + //激活引擎 + int errorCode = faceEngine.activeOnline(appId, sdkKey, activeKey); + System.out.println("引擎激活errorCode:" + errorCode); + + ActiveDeviceInfo activeDeviceInfo = new ActiveDeviceInfo(); + //采集设备信息(可离线) + errorCode = faceEngine.getActiveDeviceInfo(activeDeviceInfo); + System.out.println("采集设备信息errorCode:" + errorCode); + System.out.println("设备信息:" + activeDeviceInfo.getDeviceInfo()); + +// faceEngine.activeOffline("d:\\ArcFacePro64.dat.offline"); + + ActiveFileInfo activeFileInfo = new ActiveFileInfo(); + errorCode = faceEngine.getActiveFileInfo(activeFileInfo); + System.out.println("获取激活文件errorCode:" + errorCode); + System.out.println("激活文件信息:" + activeFileInfo.toString()); + + //引擎配置 + EngineConfiguration engineConfiguration = new EngineConfiguration(); + engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE); + engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT); + engineConfiguration.setDetectFaceMaxNum(10); + //功能配置 + FunctionConfiguration functionConfiguration = new FunctionConfiguration(); + functionConfiguration.setSupportAge(true); + functionConfiguration.setSupportFaceDetect(true); + functionConfiguration.setSupportFaceRecognition(true); + functionConfiguration.setSupportGender(true); + functionConfiguration.setSupportLiveness(true); + functionConfiguration.setSupportIRLiveness(true); + functionConfiguration.setSupportImageQuality(true); + functionConfiguration.setSupportMaskDetect(true); + functionConfiguration.setSupportUpdateFaceData(true); + engineConfiguration.setFunctionConfiguration(functionConfiguration); + + //初始化引擎 + errorCode = faceEngine.init(engineConfiguration); + System.out.println("初始化引擎errorCode:" + errorCode); + VersionInfo version = faceEngine.getVersion(); + System.out.println(version); + + //人脸检测 + ImageInfo imageInfo = ImageFactory.getRGBData(new File("/home/huangyifang/gb/咸阳师范/10.jpg")); + List faceInfoList = new ArrayList(); + errorCode = faceEngine.detectFaces(imageInfo, faceInfoList); + System.out.println("人脸检测errorCode:" + errorCode); + System.out.println("检测到人脸数:" + faceInfoList.size()); + + ImageQuality imageQuality = new ImageQuality(); + errorCode = faceEngine.imageQualityDetect(imageInfo, faceInfoList.get(0), 0, imageQuality); + System.out.println("图像质量检测errorCode:" + errorCode); + System.out.println("图像质量分数:" + imageQuality.getFaceQuality()); + + //特征提取 + FaceFeature faceFeature = new FaceFeature(); + errorCode = faceEngine.extractFaceFeature(imageInfo, faceInfoList.get(0), ExtractType.REGISTER, 0, faceFeature); + System.out.println("特征提取errorCode:" + errorCode); + + //人脸检测2 + ImageInfo imageInfo2 = ImageFactory.getRGBData(new File("/home/huangyifang/gb/咸阳师范/10.jpg")); + List faceInfoList2 = new ArrayList(); + errorCode = faceEngine.detectFaces(imageInfo2, faceInfoList2); + System.out.println("人脸检测errorCode:" + errorCode); + System.out.println("检测到人脸数:" + faceInfoList.size()); + + //特征提取2 + FaceFeature faceFeature2 = new FaceFeature(); + errorCode = faceEngine.extractFaceFeature(imageInfo2, faceInfoList2.get(0), ExtractType.RECOGNIZE, 0, faceFeature2); + System.out.println("特征提取errorCode:" + errorCode); + + //特征比对 + FaceFeature targetFaceFeature = new FaceFeature(); + targetFaceFeature.setFeatureData(faceFeature.getFeatureData()); + FaceFeature sourceFaceFeature = new FaceFeature(); + sourceFaceFeature.setFeatureData(faceFeature2.getFeatureData()); + FaceSimilar faceSimilar = new FaceSimilar(); + + errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar); + System.out.println("特征比对errorCode:" + errorCode); + System.out.println("人脸相似度:" + faceSimilar.getScore()); + + + //人脸属性检测 + FunctionConfiguration configuration = new FunctionConfiguration(); + configuration.setSupportAge(true); + configuration.setSupportGender(true); + configuration.setSupportLiveness(true); + configuration.setSupportMaskDetect(true); + errorCode = faceEngine.process(imageInfo, faceInfoList, configuration); + System.out.println("图像属性处理errorCode:" + errorCode); + + //性别检测 + List genderInfoList = new ArrayList(); + errorCode = faceEngine.getGender(genderInfoList); + System.out.println("性别:" + genderInfoList.get(0).getGender()); + + //年龄检测 + List ageInfoList = new ArrayList(); + errorCode = faceEngine.getAge(ageInfoList); + System.out.println("年龄:" + ageInfoList.get(0).getAge()); + + //活体检测 + List livenessInfoList = new ArrayList(); + errorCode = faceEngine.getLiveness(livenessInfoList); + System.out.println("活体:" + livenessInfoList.get(0).getLiveness()); + + //口罩检测 + List maskInfoList = new ArrayList(); + errorCode = faceEngine.getMask(maskInfoList); + System.out.println("口罩:" + maskInfoList.get(0).getMask()); + + + //IR属性处理 + ImageInfo imageInfoGray = ImageFactory.getGrayData(new File("/home/huangyifang/gb/咸阳师范/10.jpg")); + List faceInfoListGray = new ArrayList(); + errorCode = faceEngine.detectFaces(imageInfoGray, faceInfoListGray); + + FunctionConfiguration configuration2 = new FunctionConfiguration(); + configuration2.setSupportIRLiveness(true); + errorCode = faceEngine.processIr(imageInfoGray, faceInfoListGray, configuration2); + //IR活体检测 + List irLivenessInfo = new ArrayList<>(); + errorCode = faceEngine.getLivenessIr(irLivenessInfo); + System.out.println("IR活体:" + irLivenessInfo.get(0).getLiveness()); + + //获取激活文件信息 + ActiveFileInfo activeFileInfo2 = new ActiveFileInfo(); + errorCode = faceEngine.getActiveFileInfo(activeFileInfo2); + + //更新人脸数据 + errorCode = faceEngine.updateFaceData(imageInfo, faceInfoList); + + //高级人脸图像处理接口 + ImageInfoEx imageInfoEx = new ImageInfoEx(); + imageInfoEx.setHeight(imageInfo.getHeight()); + imageInfoEx.setWidth(imageInfo.getWidth()); + imageInfoEx.setImageFormat(imageInfo.getImageFormat()); + imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()}); + imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3}); + List faceInfoList1 = new ArrayList<>(); + errorCode = faceEngine.detectFaces(imageInfoEx, DetectModel.ASF_DETECT_MODEL_RGB, faceInfoList1); + ImageQuality imageQuality1 = new ImageQuality(); + errorCode = faceEngine.imageQualityDetect(imageInfoEx, faceInfoList1.get(0), 0, imageQuality1); + FunctionConfiguration fun = new FunctionConfiguration(); + fun.setSupportAge(true); + errorCode = faceEngine.process(imageInfoEx, faceInfoList1, fun); + List ageInfoList1 = new ArrayList<>(); + int age = faceEngine.getAge(ageInfoList1); + FaceFeature feature = new FaceFeature(); + errorCode = faceEngine.extractFaceFeature(imageInfoEx, faceInfoList1.get(0), ExtractType.REGISTER, 0, feature); + errorCode = faceEngine.updateFaceData(imageInfoEx, faceInfoList1); + + //设置活体测试 + errorCode = faceEngine.setLivenessParam(0.5f, 0.7f, 0.3f); + System.out.println("设置活体活体阈值errorCode:" + errorCode); + + LivenessParam livenessParam=new LivenessParam(); + errorCode = faceEngine.getLivenessParam(livenessParam); + + //注册人脸信息1 + FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo(); + faceFeatureInfo.setSearchId(5); + faceFeatureInfo.setFaceTag("FeatureData1"); + faceFeatureInfo.setFeatureData(faceFeature.getFeatureData()); + errorCode = faceEngine.registerFaceFeature(faceFeatureInfo); + + //注册人脸信息2 + FaceFeatureInfo faceFeatureInfo2 = new FaceFeatureInfo(); + faceFeatureInfo2.setSearchId(6); + faceFeatureInfo2.setFaceTag("FeatureData2"); + faceFeatureInfo2.setFeatureData(faceFeature2.getFeatureData()); + errorCode = faceEngine.registerFaceFeature(faceFeatureInfo2); + + //获取注册人脸个数 + FaceSearchCount faceSearchCount = new FaceSearchCount(); + errorCode = faceEngine.getFaceCount(faceSearchCount); + System.out.println("注册人脸个数:" + faceSearchCount.getCount()); + + //搜索最相似人脸 + SearchResult searchResult = new SearchResult(); + errorCode = faceEngine.searchFaceFeature(faceFeature, CompareModel.LIFE_PHOTO, searchResult); + System.out.println("最相似人脸Id:" + searchResult.getFaceFeatureInfo().getSearchId()); + + //更新人脸信息 + FaceFeatureInfo faceFeatureInfo3 = new FaceFeatureInfo(); + faceFeatureInfo3.setSearchId(6); + faceFeatureInfo3.setFaceTag("FeatureData2Update"); + faceFeatureInfo3.setFeatureData(faceFeature2.getFeatureData()); + errorCode = faceEngine.updateFaceFeature(faceFeatureInfo3); + + //移除人脸信息 + errorCode = faceEngine.removeFaceFeature(6); + + //引擎卸载 + errorCode = faceEngine.unInit(); + + + } +} \ No newline at end of file diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserInfo.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserInfo.java new file mode 100644 index 0000000..07781f5 --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserInfo.java @@ -0,0 +1,11 @@ +package net.shapelight.modules.face.util; + + +import lombok.Data; + +@Data +public class UserInfo { + private String faceId; + private String name; + private byte[] faceFeature; +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserRamCache.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserRamCache.java new file mode 100644 index 0000000..f1ab06b --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserRamCache.java @@ -0,0 +1,66 @@ +package net.shapelight.modules.face.util; + +import com.google.common.collect.Lists; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + +public class UserRamCache { + + private final ConcurrentHashMap USER_INFO_MAP = new ConcurrentHashMap<>(); + + private final Set REGISTER = new CopyOnWriteArraySet<>(); + + public void addUser(UserInfo userInfo) { + USER_INFO_MAP.put(userInfo.getFaceId(), userInfo); + for (Listener listener : REGISTER) { + listener.onAdd(userInfo); + } + } + + public void removeUser(String faceId) { + UserInfo userInfo = USER_INFO_MAP.remove(faceId); + for (Listener listener : REGISTER) { + listener.onRemove(userInfo); + } + } + + public List getUserList() { + List userInfoList = Lists.newLinkedList(); + userInfoList.addAll(USER_INFO_MAP.values()); + return userInfoList; + } + + public void clear(){ + USER_INFO_MAP.clear(); + REGISTER.clear(); + } + + public void addListener(Listener listener) { + REGISTER.add(listener); + } + + public void removeListener(Listener listener) { + REGISTER.remove(listener); + } + +// @Data +// public class UserInfo { +// +// private String faceId; +// private String name; +// private byte[] faceFeature; +// +// } + + public interface Listener { + default void onAdd(UserInfo userInfo) { + } + + default void onRemove(UserInfo userInfo) { + + } + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserRamGroup.java b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserRamGroup.java new file mode 100644 index 0000000..a54789c --- /dev/null +++ b/shapelight-admin/src/main/java/net/shapelight/modules/face/util/UserRamGroup.java @@ -0,0 +1,34 @@ +package net.shapelight.modules.face.util; + +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public class UserRamGroup { + + private static final ConcurrentHashMap USER_RAM_GROUP_MAP = new ConcurrentHashMap<>(); + + public static void addCell(String cellId){ + UserRamCache cell = new UserRamCache(); + USER_RAM_GROUP_MAP.put(cellId,cell); + } + + public static void removeCell(String cellId){ + USER_RAM_GROUP_MAP.remove(cellId); + } + + public static void addUser(UserInfo userInfo, String cellId) { + USER_RAM_GROUP_MAP.get(cellId).addUser(userInfo); + } + + public static void removeUser(String faceId, String cellId) { + USER_RAM_GROUP_MAP.get(cellId).removeUser(faceId); + } + + public static List getUserList(String cellId) { + return USER_RAM_GROUP_MAP.get(cellId).getUserList(); + } + + public static void clear(){ + USER_RAM_GROUP_MAP.clear(); + } +} diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/job/task/EmpowerTask.java b/shapelight-admin/src/main/java/net/shapelight/modules/job/task/EmpowerTask.java index b02d96e..ea89891 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/job/task/EmpowerTask.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/job/task/EmpowerTask.java @@ -32,6 +32,8 @@ public class EmpowerTask implements ITask{ KeysEntity.passKey = json.getString("passKey"); KeysEntity.empowerText = json.getString("empowerText"); log.debug("TokenResponse:{}", json.getString("passKey")); + } else { + } } diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/ten/controller/TenPersonController.java b/shapelight-admin/src/main/java/net/shapelight/modules/ten/controller/TenPersonController.java index 3f23fb2..e41df50 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/ten/controller/TenPersonController.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/ten/controller/TenPersonController.java @@ -426,12 +426,12 @@ public class TenPersonController extends AbstractController { tenPerson.setCreateTime(new Date()); tenPerson.setRegisterType(Constant.RESGISTER_TYPE_WEB); - int res; + String res; tenPerson.setStatus(0); res = tenPersonService.save(tenPerson); - if (res==2) { - return R.error("照片未检测到人脸"); + if (res!=null) { + return R.error(res); } return R.ok(); @@ -1303,11 +1303,11 @@ public class TenPersonController extends AbstractController { //----------------------------------掌静脉特征压缩包封装-------------------------------------- - int res; + String res; res = tenPersonService.save(tenPerson); - if (res==2) { - return R.error("照片未检测到人脸"); + if (res!=null) { + return R.error(res); } return R.ok(); } @@ -1361,7 +1361,7 @@ public class TenPersonController extends AbstractController { AppUserScopeEntity userScopeEntity = appUserScopeService.getOne(new LambdaQueryWrapper() .eq(AppUserScopeEntity::getUserId, params.get("userId"))); List relationList = relationService.list(new LambdaQueryWrapper() - .eq(TenRelation::getParentId,userScopeEntity.getUserId())); + .eq(TenRelation::getParentId,userScopeEntity.getUserId()).eq(TenRelation::getStatus,1)); if(!relationList.isEmpty()) { List list = relationList.stream().map(TenRelation::getStudentId).collect(Collectors.toList()); params.put("cellId",userScopeEntity.getCellId()); diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/ten/dao/TenPersonDao.java b/shapelight-admin/src/main/java/net/shapelight/modules/ten/dao/TenPersonDao.java index bc3b380..27ccef9 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/ten/dao/TenPersonDao.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/ten/dao/TenPersonDao.java @@ -124,6 +124,8 @@ public interface TenPersonDao { IPage getByPersonIds(Page page,@Param("personIds")List personIds, @Param("cellId")Long cellId); + List listPage(@Param("start")int start, @Param("count")int count, @Param("cellId")String cellId); + } diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/ten/entity/TenPersonEntity.java b/shapelight-admin/src/main/java/net/shapelight/modules/ten/entity/TenPersonEntity.java index d1d3a52..86ce9aa 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/ten/entity/TenPersonEntity.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/ten/entity/TenPersonEntity.java @@ -347,6 +347,8 @@ public class TenPersonEntity extends BaseEntity implements Serializable { //-----------------v5http----- private String thdFeature; + //人脸特征 + private String feature; @JsonSerialize(using = ToStringSerializer.class) @ApiModelProperty("部门ID") diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/TenPersonService.java b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/TenPersonService.java index cdfaa87..29b1104 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/TenPersonService.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/TenPersonService.java @@ -27,7 +27,7 @@ public interface TenPersonService { PageUtils queryVerify(Map params); - int save(TenPersonEntity entity); + String save(TenPersonEntity entity); int save3d(TenPersonEntity entity); int update3d(TenPersonEntity entity); boolean saveOtherRoom(TenPersonEntity entity); @@ -154,5 +154,7 @@ public interface TenPersonService { TenPersonEntity getByRyId(String ryId,Long cellId,String idCard); + List listPage(int start, int count, String cellId); + } diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenParentServiceImpl.java b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenParentServiceImpl.java index b2c1d60..b7fb16d 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenParentServiceImpl.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenParentServiceImpl.java @@ -38,7 +38,7 @@ public class TenParentServiceImpl extends ServiceImpl page = parentMapper.getParentVoList(pageParam,params); page.getRecords().forEach(item -> { int count = relationService.count(new LambdaQueryWrapper() - .eq(TenRelation::getParentId,item.getUserId())); + .eq(TenRelation::getParentId,item.getUserId()).eq(TenRelation::getStatus,1)); item.setCount(count); }); return new PageUtils(page); diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenPersonServiceImpl.java b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenPersonServiceImpl.java index 349cc78..06e9da4 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenPersonServiceImpl.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenPersonServiceImpl.java @@ -1,9 +1,14 @@ package net.shapelight.modules.ten.service.impl; +import cn.hutool.core.collection.CollectionUtil; import com.alibaba.excel.EasyExcelFactory; import com.alibaba.excel.metadata.Sheet; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.arcsoft.face.FaceInfo; +import com.arcsoft.face.enums.ExtractType; +import com.arcsoft.face.toolkit.ImageFactory; +import com.arcsoft.face.toolkit.ImageInfo; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.minio.MinioClient; import io.minio.PutObjectOptions; @@ -21,6 +26,8 @@ import net.shapelight.modules.app.service.AppUserService; import net.shapelight.modules.app.service.impl.AppUserScopeServiceImpl; import net.shapelight.modules.excel.listener.PersonExcelListener; import net.shapelight.modules.excel.model.PersonModel; +import net.shapelight.modules.face.dto.FaceRecognitionResDTO; +import net.shapelight.modules.face.service.FaceEngineService; import net.shapelight.modules.job.entity.KeysEntity; import net.shapelight.modules.nettyapi.service.ServerApiService; import net.shapelight.modules.sys.entity.SysDictEntity; @@ -106,6 +113,8 @@ public class TenPersonServiceImpl implements TenPersonService { private String imagBaseUrl; @Value("${global.minio.bucketName}") private String bucketName; + @Autowired + private FaceEngineService faceEngineService; @Override @@ -253,18 +262,17 @@ public class TenPersonServiceImpl implements TenPersonService { @Override @Transactional(rollbackFor = Exception.class) @CacheEvict(value = "TenPerson", allEntries = true) - public int save(TenPersonEntity entity) { - String userFileUrl = globalValue.getImagesDir() + "/" + - entity.getCellId().toString() + "/" + - entity.getPersonId().toString() + "/"; + public String save(TenPersonEntity entity) { + String userFileUrl = globalValue.getImagesDir() + "/org/"; + //保存原始图片 + String tempOrgImageFile = entity.getOrgImageTemp(); + String tempIdFrontImage = entity.getIdFrontImage(); + String tempIdBackImage = entity.getIdBackImage(); try { - //保存原始图片 - String tempOrgImageFile = entity.getOrgImageTemp(); - String tempIdFrontImage = entity.getIdFrontImage(); - String tempIdBackImage = entity.getIdBackImage(); + if (tempOrgImageFile != null && !tempOrgImageFile.isEmpty()) { String orgImageFileName = userFileUrl + "o_" + UUIDUtil.uuid() + ".jpg"; - String faceImageFileName = userFileUrl + "s_" + UUIDUtil.uuid() + ".jpg"; +// String faceImageFileName = userFileUrl + "s_" + UUIDUtil.uuid() + ".jpg"; //MinioClient minioClient = MinioUtil.getMinioClient(); try { // 调用statObject()来判断对象是否存在。 @@ -275,47 +283,40 @@ public class TenPersonServiceImpl implements TenPersonService { //判断人脸照片是否合格 //1.保存到本地 InputStream tempInputStream = minioClient.getObject(minioConfig.getBucketName(), tempOrgImageFile); - String tempPath = globalValue.getStaticLocations() + "/";//+globalValue.getTempDir()+"/"; - String tempOrgFilePath = tempPath + tempOrgImageFile; - int index; - byte[] bytes = new byte[1024]; - File outFile = new File(tempOrgFilePath); - FileOutputStream downloadFile = new FileOutputStream(outFile); - while ((index = tempInputStream.read(bytes)) != -1) { - downloadFile.write(bytes, 0, index); - downloadFile.flush(); + //----------------算法检测---------------------------------------------- + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = tempInputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); } - downloadFile.close(); - tempInputStream.close(); - //2.测试上传的文件 - String tempFaceFileName = UUIDUtil.uuid() + ".jpg"; - String tempFaceFilePath = tempPath + tempFaceFileName; - String osName = System.getProperty("os.name");//获取指定键(即os.name)的系统属性,如:Windows 7。 - if (Pattern.matches("Windows.*", osName)) { -// if (Pattern.matches("Windows.*", osName)) { - int res = PicSDK.getFace(tempOrgFilePath, tempFaceFilePath); - if (res != 0) { + byte[] bytes = outputStream.toByteArray(); + outputStream.close(); + tempInputStream.close(); + //------------------------------------------------------------------------- + ImageInfo rgbData = ImageFactory.getRGBData(bytes); + List faceInfoList = faceEngineService.detectFaces(rgbData); + if (CollectionUtil.isNotEmpty(faceInfoList)) { + FaceInfo faceInfo = faceInfoList.get(0); + FaceRecognitionResDTO faceRecognitionResDTO = new FaceRecognitionResDTO(); + faceRecognitionResDTO.setRect(faceInfo.getRect()); + byte[] featureBytes = faceEngineService.extractFaceFeature(rgbData, faceInfo, ExtractType.REGISTER); + if (featureBytes != null) { +// UserRamCache.UserInfo userInfo = new UserCompareInfo(); +// userInfo.setFaceId(faceAddReqDTO.getName()); +// userInfo.setName(faceAddReqDTO.getName()); +// userInfo.setFaceFeature(feature); +// //这边注册到内存缓存中,也可以根据业务,注册到数据库中 +// UserRamCache.addUser(userInfo); + entity.setFeature(Base64.getEncoder().encodeToString(featureBytes)); + }else{ log.error("图片不合格,未检测到人脸"); - return 2; + return "图片不合格,未检测到人脸"; } - //保存底片文件到oss - InputStream inputStream = new FileInputStream(tempFaceFilePath); - PutObjectOptions putObjectOptions = new PutObjectOptions(inputStream.available(), -1); - putObjectOptions.setContentType("image/jpeg"); - minioClient.putObject( - minioConfig.getBucketName(), faceImageFileName, inputStream, putObjectOptions); - inputStream.close(); - } else { - minioClient.copyObject( - minioConfig.getBucketName(), - faceImageFileName, - null, - null, - minioConfig.getBucketName(), - tempOrgImageFile, - null, - null); + }else{ + log.error("图片不合格,未检测到人脸"); + return "图片不合格,未检测到人脸"; } //拷贝临时文件正式文件 minioClient.copyObject( @@ -329,16 +330,15 @@ public class TenPersonServiceImpl implements TenPersonService { null); entity.setOrgImage(orgImageFileName); - entity.setFaceImage(faceImageFileName); - //删除临时文件oss - minioClient.removeObject(minioConfig.getBucketName(), tempOrgImageFile); + entity.setFaceImage(orgImageFileName); +// entity.setFaceImage(faceImageFileName); //删除本地临时文件 - new File(tempFaceFilePath).delete(); +// new File(tempFaceFilePath).delete(); } catch (Exception e) { entity.setOrgImage(""); entity.setFaceImage(""); e.printStackTrace(); - return 10; + return "文件不存在"; } } @@ -474,7 +474,7 @@ public class TenPersonServiceImpl implements TenPersonService { appUserService.updateById(appUser); } - //发送设备通知 + /*//发送设备通知 List devList = tenDeviceService.findByCellId(entity.getCellId()); //状态是0正常,发送推送 if (entity.getStatus().intValue() == Constant.PESON_SUATUS_NOMOR) { @@ -498,41 +498,52 @@ public class TenPersonServiceImpl implements TenPersonService { list.add(vo); serverApiService.personOperation(dev.getSn(), list); } + }*/ + if(entity.getPersonType()!=Constant.PERSON_TYPE_GUEST) { + List dictList = sysDictService.queryByType("optype"); + Map optypeMap = new HashMap<>(); + dictList.forEach(dict -> { + optypeMap.put(dict.getValue(),dict.getCode()); + }); + Map personParams = new HashMap<>(); + personParams.put("operation","editUserData"); + personParams.put("accountNumber",globalValue.accountNumber); + personParams.put("passKey", KeysEntity.passKey); + personParams.put("empowerText",KeysEntity.empowerText); + JSONArray jsonArray = new JSONArray(); + Map student = new HashMap<>(); + student.put("objectUuid",String.valueOf(entity.getPersonId())); + student.put("userCode",entity.getRyid()); + student.put("userName",entity.getName()); + student.put("userType",optypeMap.get(entity.getLabelName())); + student.put("studentType","0"+entity.getLabelId()); + student.put("gender",entity.getGender()==0?"WOMAN":"MAN"); + student.put("cardNumber",""); + student.put("idCard",entity.getIdCard()); + student.put("fileUrl",imagBaseUrl+"/"+bucketName+"/"+entity.getOrgImage()); + student.put("fileName",entity.getOrgImage().substring(entity.getOrgImage().lastIndexOf("/"))); + jsonArray.add(student); + personParams.put("dataInfo",jsonArray); + JSONObject jsonObject = opFeignClient.submitData(personParams); + if(!jsonObject.getString("shrgStatus").equals("S")) { + log.error(jsonObject.toJSONString()); + return jsonObject.getString("shrgMsg"); + } + if(!jsonObject.getJSONArray("errInfo").isEmpty()) { + log.error(jsonObject.toJSONString()); + return jsonObject.getJSONArray("errInfo").toJSONString(); + } } - List dictList = sysDictService.queryByType("optype"); - Map optypeMap = new HashMap<>(); - dictList.forEach(dict -> { - optypeMap.put(dict.getValue(),dict.getCode()); - }); - Map personParams = new HashMap<>(); - personParams.put("operation","editUserData"); - personParams.put("accountNumber",globalValue.accountNumber); - personParams.put("passKey", KeysEntity.passKey); - personParams.put("empowerText",KeysEntity.empowerText); - JSONArray jsonArray = new JSONArray(); - Map student = new HashMap<>(); - student.put("objectUuid",String.valueOf(entity.getPersonId())); - student.put("userCode",entity.getRyid()); - student.put("userName",entity.getName()); - student.put("userType",optypeMap.get(entity.getLabelName())); - student.put("studentType","0"+entity.getLabelId()); - student.put("gender",entity.getGender()==0?"WOMAN":"MAN"); - student.put("cardNumber",""); - student.put("idCard",entity.getIdCard()); - student.put("fileUrl",imagBaseUrl+"/"+bucketName+"/"+entity.getOrgImage()); - student.put("fileName",entity.getOrgImage().substring(entity.getOrgImage().lastIndexOf("/"))); - jsonArray.add(student); - personParams.put("dataInfo",jsonArray); - JSONObject jsonObject = opFeignClient.submitData(personParams); - if(!jsonObject.getString("shrgStatus").equals("S")) { - log.error(jsonObject.toJSONString()); - } - if(!jsonObject.getJSONArray("errInfo").isEmpty()) { - log.error(jsonObject.toJSONString()); - } - return 0; + } - return 1; + //删除临时文件oss + try { + minioClient.removeObject(minioConfig.getBucketName(), tempOrgImageFile); + } catch (Exception e) { + return "删除失败"; + } + + return null; } @@ -2527,4 +2538,9 @@ public class TenPersonServiceImpl implements TenPersonService { IPage page = tenPersonDao.getByPersonIds(pageParam, list,cellId); return new PageUtils(page); } + + @Override + public List listPage(int start, int count, String cellId) { + return tenPersonDao.listPage(start, count, cellId); + } } diff --git a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenRecordServiceImpl.java b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenRecordServiceImpl.java index 9ff9960..a8bb0c4 100644 --- a/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenRecordServiceImpl.java +++ b/shapelight-admin/src/main/java/net/shapelight/modules/ten/service/impl/TenRecordServiceImpl.java @@ -482,8 +482,27 @@ public class TenRecordServiceImpl implements TenRecordService { pageParam.setCurrent(Long.parseLong((String) params.get("page"))); pageParam.setSize(Long.parseLong((String) params.get("limit"))); List list = (List) params.get("personIds"); - Long cellId = Long.parseLong(params.get("cellId").toString()); - IPage page = tenRecordDao.getByPersonIds(pageParam,params); + Long cellId = Long.parseLong(params.get("tenantId").toString()); + int during = Integer.parseInt((String)params.get("during")); + String recordTimeStart = null; + String recordTimeEnd = null; + if(during == 0){ //当天 + recordTimeStart = MyDateUtils.getCurrentDayStartTime(); + recordTimeEnd = MyDateUtils.getCurrentDayEndTime(); + }else if(during == 1){ //本周 + recordTimeStart = MyDateUtils.getCurrentWeekStartTime(); + recordTimeEnd = MyDateUtils.getCurrentWeekEndTime(); + }else if(during == 2){ //本月 + recordTimeStart = MyDateUtils.getCurrentMonthStartTime(); + recordTimeEnd = MyDateUtils.getCurrentMonthEndTime(); + } + params.put("recordTimeStart",recordTimeStart); + params.put("recordTimeEnd",recordTimeEnd); + IPage page = tenRecordDao.getByPersonIds(pageParam,params); + page.getRecords().forEach(item-> { + TenPersonEntity person = tenPersonService.getById(item.getPersonId(),item.getCellId()); + item.setPerson(person); + }); return new PageUtils(page); } } diff --git a/shapelight-admin/src/main/resources/application.yml b/shapelight-admin/src/main/resources/application.yml index ca7f46a..261b9c3 100644 --- a/shapelight-admin/src/main/resources/application.yml +++ b/shapelight-admin/src/main/resources/application.yml @@ -133,6 +133,19 @@ shapelight: # token有效时长,180天,单位秒 #expire: 15552000 header: token +# sdk配置--------------------------------------- +config: + arcface-sdk: + version: 4.1 + app-id: SUQLGn78W5o7StEEbm6WTTfaMgAxSsN8HwJziApVyNN + sdk-key: 7dJ9RqEhc3mPCatuUceKjgYwRR6QtyMsxLUiL7JYAkrt + active-key: 82K1-11TT-K136-FFVW + active-file: + detect-pool-size: 16 + compare-pool-size: 16 + rec-face-thd: 0.8 + rec-id-thd: 0.5 + #mybatis diff --git a/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face.dll b/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face.dll new file mode 100644 index 0000000..d0a4c97 Binary files /dev/null and b/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face.dll differ diff --git a/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine.dll b/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine.dll new file mode 100644 index 0000000..db65b00 Binary files /dev/null and b/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine.dll differ diff --git a/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine_jni.dll b/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine_jni.dll new file mode 100644 index 0000000..0266b32 Binary files /dev/null and b/shapelight-admin/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine_jni.dll differ diff --git a/shapelight-admin/src/main/resources/mapper/ten/TenPersonDao.xml b/shapelight-admin/src/main/resources/mapper/ten/TenPersonDao.xml index 01cf4a3..85d86f7 100644 --- a/shapelight-admin/src/main/resources/mapper/ten/TenPersonDao.xml +++ b/shapelight-admin/src/main/resources/mapper/ten/TenPersonDao.xml @@ -1333,5 +1333,12 @@ + + diff --git a/shapelight-admin/src/main/resources/mapper/ten/TenRecordDao.xml b/shapelight-admin/src/main/resources/mapper/ten/TenRecordDao.xml index 3c837b1..d12a8af 100644 --- a/shapelight-admin/src/main/resources/mapper/ten/TenRecordDao.xml +++ b/shapelight-admin/src/main/resources/mapper/ten/TenRecordDao.xml @@ -639,14 +639,22 @@