要集成阿里云的人脸识别首先得开通人体人脸功能
视觉智能开放平台-控制台 (aliyun.com)
可以按需开通,首次开通有免费的5千次识别次数
开通好之后需要保存好AccessKey ID 和 AccessKey Secre代码中用的上
之后引入阿里云的人脸识别依赖
<!--人脸识别(阿里云)-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-facebody</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>viapi-utils</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-viapiutils</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>facebody20191230</artifactId>
<version>5.1.0</version>
</dependency>
我们在数据库表设计中新建一张表,来存储图片路径和用户id(用来绑定人脸图片)
我们在比对之前得先录入人脸图片
/**
* @Description:
* @Author: XIAO_FEI
* @CreateTime: 2024/01/09 15:25
* @Version 1.0
*/
@RestController
@RequestMapping("/mta/v1/face")
public class ALiYunFaceController {
@Autowired
private ALiYunFaceService aLiYunFaceService;
/**
* 录入人脸识别
*
* @param multipartFile 图片文件
* @return 录入结果
*/
@PostMapping("/inputFace")
public ResponseResult inputFace(@Param("file") MultipartFile multipartFile,
@Param("id") Integer id) {
return aLiYunFaceService.inputFace(multipartFile, id);
}
/**
* @Description:
* @Author: XIAO_FEI
* @CreateTime: 2024/01/09 15:26
* @Version 1.0
*/
public interface ALiYunFaceService {
ResponseResult inputFace(MultipartFile multipartFile, Integer id);
}
实现类里面我们使用了人体活体检测和人脸属性检测,活体检测主要检测是不是卡通人物等等....
人脸属性检测主要是为了检测人脸数量是否满足一个
/**
* @Description:
* @Author: XIAO_FEI
* @CreateTime: 2024/01/09 17:56
* @Version 1.0
*/
@Service
@Slf4j
public class ALiYunFaceServiceImpl implements ALiYunFaceService {
@Autowired
private PersonnelFacesMapper personnelFacesMapper;
@Autowired
private PersonnelMapper personnelMapper;
@Autowired
private FileUploadServiceImpl fileUploadService;
@Autowired
private MinIOFileStorageServiceImpl minIOFileStorageService;
// 这里就填写之前保存的 AccessKey ID 和 AccessKey Secret
private static final String ACCESS_KEY_ID = "****************";
private static final String ACCESS_KEY_SECRET = "***************";
private static final String ENDPOINT = "cn-shanghai"; // 访问的域名
private static final DefaultProfile PROFILE = DefaultProfile.getProfile(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
private static final IAcsClient CLIENT = new DefaultAcsClient(PROFILE);
/**
* 录入人脸照片
*
* @param multipartFile 图片文件
* @param id 人员id
* @return 录入结果
*/
@Override
public ResponseResult inputFace(MultipartFile multipartFile, Integer id) {
// 1. 判断图片是否存在
if (multipartFile.isEmpty()) {
return ResponseResult.errorResult(5000, "图片文件不能为空");
}
FileUtils fileUtils = null;
// 阿里云返回的图片路径
String imageUrlTow = "";
// minio 返回的路径
String imageUpload = "";
List<String> imagesPath = new ArrayList<>();
try {
fileUtils = FileUtils.getInstance(ACCESS_KEY_ID, ACCESS_KEY_SECRET);
// 存入 minio
// 文件路径 = 姓名+ 年月日
Personnel personnel = personnelMapper.selectById(id);
String prefix = personnel.getName();
imageUpload = fileUploadService.imageUpload(multipartFile, prefix);
imagesPath.add(imageUpload);
imageUrlTow = fileUtils.upload(imageUpload);
// 人脸活体检测
if (!isLivingFace(imageUrlTow)) {
// 删除 minio 图片
minIOFileStorageService.delete(imagesPath);
log.info("当前检测不是活物,删除图片成功");
return ResponseResult.errorResult(5003, "当前检测不是活体");
}
// 人脸属性检测
if (!RecognizeFace(imageUrlTow)) {
// 删除 minio 图片
minIOFileStorageService.delete(imagesPath);
log.info("当前检测人数不止一个或者没有,删除图片成功");
return ResponseResult.errorResult(5003, "当前检测人数不止一个或者没有");
}
// 把人脸的文件路径和人员 id 绑定在一起
Warehousing(imageUpload, id);
} catch (ClientException | IOException e) {
log.info("人脸比对出现问题", e);
// 删除 minio 图片
minIOFileStorageService.delete(imagesPath);
log.info("删除图片成功");
return ResponseResult.errorResult(5003, "人脸录入出现故障请重试,或者联系管理员");
}
return ResponseResult.okResult("人脸录入成功");
}
/**
* Warehousing 存入数据库
*
* @param imagePath 文件路径
* @param id 人员 id
*/
public void Warehousing(String imagePath, Integer id) {
// 查询数据库中是否已存在该人员的人脸数据
LambdaQueryWrapper<PersonnelFaces> lqw = new LambdaQueryWrapper<>();
lqw.eq(PersonnelFaces::getPersonnelId, id);
PersonnelFaces personnelFaces = personnelFacesMapper.selectOne(lqw);
if (!ObjectUtils.isEmpty(personnelFaces)) {
// 删除Minio中的文件
List<String> pathUrls = Collections.singletonList(personnelFaces.getImagePath());
minIOFileStorageService.delete(pathUrls);
// 更新数据库中的人脸数据
LambdaQueryWrapper<PersonnelFaces> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PersonnelFaces::getPersonnelId, id);
PersonnelFaces pf = new PersonnelFaces();
pf.setImagePath(imagePath);
pf.setUpdateTime(LocalDateTime.now());
personnelFacesMapper.update(pf, wrapper);
} else {
// 插入新的人脸数据
personnelFacesMapper.insert(
PersonnelFaces.builder()
.imagePath(imagePath)
.createTime(LocalDateTime.now())
.personnelId(id)
.build());
}
}
/**
* DetectLivingFace 人脸活体识别
*
* @param imageUrl 人脸活体识别
* @return 是否为活体
*/
public static boolean isLivingFace(String imageUrl) {
DetectLivingFaceRequest request = new DetectLivingFaceRequest();
request.setRegionId(ENDPOINT);
List<DetectLivingFaceRequest.Tasks> tasksList = new ArrayList<>();
DetectLivingFaceRequest.Tasks tasks1 = new DetectLivingFaceRequest.Tasks();
tasks1.setImageURL(imageUrl);
tasksList.add(tasks1);
request.setTaskss(tasksList);
try {
DetectLivingFaceResponse response = CLIENT.getAcsResponse(request);
log.info("人脸活体识别:{}", JSONObject.toJSONString(response));
JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(response));
jsonObject = JSONObject.parseObject(jsonObject.get("data").toString());
int resultPass = jsonObject.toString().indexOf("pass");
int resultBlock = jsonObject.toString().indexOf("block");
return resultPass != -1 && resultBlock == -1;
} catch (com.aliyuncs.exceptions.ClientException e) {
log.error("阿里云客户端错误", e);
return false;
}
}
/**
* RecognizeFace 人脸属性检测
*
* @param imageUrl 图片路径
* @return 检测结果
*/
public static boolean RecognizeFace(String imageUrl) {
RecognizeFaceRequest request = new RecognizeFaceRequest();
request.setRegionId("cn-shanghai");
request.setImageURL(imageUrl);
try {
RecognizeFaceResponse response = CLIENT.getAcsResponse(request);
System.out.println("人脸检测:" + new Gson().toJson(response));
JSONObject jsonObject = JSONObject.parseObject(new Gson().toJson(response));
jsonObject = JSONObject.parseObject((jsonObject.get("data").toString()));
String faceCounts = jsonObject.get("faceCount").toString();
if (Integer.parseInt(faceCounts) != 1) {
return false;
}
String likeDoubles = jsonObject.get("faceProbabilityList").toString();
likeDoubles = likeDoubles.substring(1, likeDoubles.length() - 1);
if ("".equals(likeDoubles)) {
return false;
}
String[] likeList = likeDoubles.split(",");
if (likeList.length > 0) {
String num_str = likeList[0].toString();
double num_double = Double.parseDouble(num_str);
// 满足条件返回 true
return num_double > 0.7;
}
return false;
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
log.info("错误码: " + e.getErrCode());
log.info("错误信息: " + e.getErrMsg());
log.info("请求id" + e.getRequestId());
}
return false;
}
以上使用的是Mybatis-Plus插入的数据库,所以没有sql
当图片入库之后就得开始人脸比对了
再写一个接口来进行人脸比对, 实现思路就是,通过传入的图片对比保存在minio的图片
以上service实现类可以共用,我们在加一个人脸比对的方法就行
/**
* VerifyFace 人脸比对
*
* @param imageUrl_login 对比人脸图片1
* @param imageUrl_register 对比人脸图片2
*/
public static boolean VerifyFace(String imageUrl_login, String imageUrl_register) {
CompareFaceRequest request = new CompareFaceRequest();
request.setRegionId("cn-shanghai");
request.setImageURLA(imageUrl_login);
request.setImageURLB(imageUrl_register);
try {
CompareFaceResponse response = CLIENT.getAcsResponse(request);
JSONObject jsonObject = JSONObject.parseObject(new Gson().toJson(response));
jsonObject = JSONObject.parseObject((jsonObject.get("data").toString()));
String confidence = jsonObject.get("confidence").toString();
Float confidenceFloat = Float.parseFloat(confidence);
if (confidenceFloat >= 0.71) {
return true;
} else {
return false;
}
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
// 错误码
log.info("人脸比对错误码" + e.getErrCode());
// 错误信息
log.info("人脸比对错误信息: " + e.getErrMsg());
// 请求 id
log.info("人脸比对请求id: " + e.getRequestId());
}
return true;
}
所有图片均存入minio,如果需要存入minio代码可以评论在下方,当然有什么不对的地方欢迎指出,大家共同讨论,点个赞再走吧!