AI智慧社区--人脸识别
前端
人脸的采集按钮:
首先对于选中未认证的居民记录,进行人脸采集
前端的按钮
<el-form-item>
<el-button v-has="'sys:person:info'" type="info" icon="el-icon-camera" :disabled="ids.length <= 0" @click="cameraHandle()">人脸采集</el-button>
</el-form-item>
对应的采集方法:
cameraHandle() {
//选择多条数据的情况下
if (this.ids.length > 1) {
this.$message.error('人脸采集只能选择单条数据')
return
}
//已经认证的情况下
if (this.ids[0].state === 2) {
this.$message.error('人脸识别已通过,不需要重复识别')
return
}
this.faceVisible = true
this.$nextTick(() => {
this.$refs.face.init(this.ids[0].personId)
})
},
跳转到人脸采集的弹窗
弹窗代码代码
<template>
<el-dialog
:title="'采集人脸相片'"
:close-on-click-modal="false"
:visible.sync="visible"
width="1100px"
>
<el-form ref="dataForm" :model="dataForm" label-width="100px" style="width:95%;">
<el-row>
<el-col :span="9">
<el-form-item label="" style="margin: 0;">
<video v-if="openSuccess" id="videoCamera" autoplay width="260" height="200" style="border: none;" />
</el-form-item>
</el-col>
<el-col :span="3" style="height: 200px;display: flex; justify-content: center;align-items: center;flex-wrap: wrap;">
<el-button v-if="openSuccess" size="small" type="primary" style="width: 80px; text-align: center;" @click="setImage()">拍照</el-button>
<el-upload
class="upload-demo"
action="#"
:show-file-list="false"
:http-request="fileUpload"
>
<el-button size="small" type="primary">上传人脸</el-button>
</el-upload>
</el-col>
<el-col :span="8" style="display: flex; justify-content: center; align-items: center;">
<canvas id="canvasCamera" style="display:none;" width="640" height="480" />
<img v-if="imgSrc" :src="imgSrc" alt height="200">
</el-col>
</el-row>
<hr>
<el-row>
<el-col :span="12">
<el-card>
<div class="text item">
<b><font color="red">常见问题:</font></b><br><br>
1、不支持IE浏览器,建议使用火狐、谷歌、Edge浏览器进行考试<br><br>
2、若浏览器弹出是否允许调用摄像头,请点击允许<br><br>
3、<font color="red">必须使用https加密协议,摄像头方能正常使用</font><br><br>
4、如果问题还存在,请按<a
href="https://examai.cn/pc/zhinan.pdf"
target="_blank"
><font color="red"><b>启用摄像头操作指南</b></font></a>进行操作><br><br>
5、本模块仅供测试接口,具体使用请以实际情况为准
</div>
</el-card>
</el-col>
<el-col :span="12">
<img src="@/assets/images/cm.png" width="100%">
</el-col>
</el-row>
</el-form>
</el-dialog>
</template>
<script>
import { addPerson } from '@/api/sys/person'
export default {
data() {
return {
visible: false,
dataForm: {
personId: '',
extName: '',
fileBase64: ''
},
imgSrc: '',
video: null,
context: null,
canvas: null,
openSuccess: true
}
},
watch: {
visible(val) {
if (val) {
this.imgSrc = ''
setTimeout(() => {
this.getCamera()
}, 1000)
} else {
if (this.openSuccess) {
this.closeCamera()
}
}
}
},
created() {
setTimeout(() => {
this.getCamera()
}, 1000)
this.init()
},
methods: {
init(personId) {
this.visible = true
this.dataForm.personId = personId
},
// 表单提交
dataFormSubmit() {
const _this = this
this.$refs['dataForm'].validate((valid) => {
if (valid) {
const param = {
//人员的ID
'personId': this.dataForm.personId,
//文件的后缀
'extName': this.dataForm.extName,
//文件的Base64编码
'fileBase64': this.dataForm.fileBase64
}
addPerson(param).then(res => {
if (res.code === 200) {
_this.visible = false
_this.$emit('refreshDataList')
_this.$message.success(res.msg)
} else {
_this.$message.error(res.msg)
}
})
}
})
},
// 打开摄像头
getCamera() {
this.video = document.getElementById('videoCamera')
this.canvas = document.getElementById('canvasCamera')
this.context = this.canvas.getContext('2d')
const errocb = () => {
this.$message.error('摄像头打开失败!')
this.openSuccess = false
}
if (navigator.webkitGetUserMedia) {
navigator.webkitGetUserMedia({ audio: false, video: true }, (stream) => {
// video.src=window.URL.createObjectURL(stream);
this.video.srcObject = stream
this.video.play()
}, errocb)
} else if (navigator.mediaDevices.getUserMedia) {
// var constraints = { audio: true, video: { width: 1280, height: 720 } };
const constraints = { audio: false, video: true }
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
// var video = document.querySelector('video');
this.video.srcObject = stream
this.video.onloadedmetadata = (e) => {
this.video.play()
}
})
} else if (navigator.getUserMedia) {
navigator.getUserMedia({ audio: false, video: true }, (stream) => {
this.video.src = window.webkitURL.createObjectURL(stream)
this.video.srcObject = stream
this.video.play()
}, errocb)
} else {
alert('你的浏览器不支持打开摄像头')
}
},
// 绘制图片(拍照功能)
setImage() {
this.context.drawImage(
this.video,
0,
0,
this.video.videoWidth,
this.video.videoHeight
)
this.imgSrc = this.canvas.toDataURL('image/png')
this.dataForm.extName = this.imgSrc.substring(this.imgSrc.indexOf('/') + 1, this.imgSrc.indexOf(';'))
this.dataForm.fileBase64 = this.imgSrc.substring(22)
this.dataFormSubmit()
},
// 关闭摄像头
closeCamera() {
this.video.srcObject.getTracks()[0].stop()
},
// 关闭弹出层
closeDialog() {
this.visible = false
},
// 重写文件上传方法
fileUpload(file) {
this.fileToBase64(file.file).then(res => {
// console.log(res)
this.imgSrc = res
this.dataForm.extName = res.substring(res.indexOf('/') + 1, res.indexOf(';'))
const len = 19 + this.dataForm.extName.length
this.dataForm.fileBase64 = res.substring(len)
// console.log(this.dataForm.fileBase64)
this.dataFormSubmit()
}).catch(err => {
console.log(err)
this.$message.error(err)
})
},
// 转base64
fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
let fileResult = ''
if (file.size > 1024 * 1024) {
reject('文件大小不能超过1M')
}
reader.readAsDataURL(file)
// 开始转
reader.onload = () => {
fileResult = reader.result
// data:image/jpeg;base64,
}
// 转 失败
reader.onerror = function(error) {
reject(error)
}
// 转 结束 咱就 resolve 出去
reader.onloadend = function() {
resolve(fileResult)
}
})
}
}
}
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
调用后端接口
export function addPerson(data) {
return request({
//人脸录入
url: '/sys/person/addPerson',
method: 'post',
data
})
}
后端
后端代码
@LogAnnotation("人脸采集")
@PostMapping("/addPerson")
public Result addPerson(@RequestBody PersonFaceForm personFaceForm){
//通过传入的表单信息中的ID 查询居民信息
Person person = this.personService.getById(personFaceForm.getPersonId());
//如果没有查询到相应的居民信息 则说明居民不存在
if(person == null){
return Result.error("居民不存在");
}
//数据库表中的state字段说明 人脸是否已经录入
if(person.getState() == 2){
return Result.error("人脸识别已通过,不需要重复识别");
}
//文件的Base64位编码是否已经存在 如果为空说明没有人脸图片
if(personFaceForm.getFileBase64() == null || personFaceForm.getFileBase64().equals("")){
return Result.error("请上传Base64编码的图片");
}
//如果相关的关于人脸上传的配置信息已经配置,则进行下面的内容
if(apiConfiguration.isUsed()){
// 方式二:调用腾讯API人脸识别
//1.人脸认证账号创建
//获取人脸识别的ID
String faceId = newPerson(personFaceForm,person.getUserName());
if(faceId == null){
return Result.error("人脸识别失败");
}
if (faceId != null) {
//生成头像访问路径
String filename = faceId + "." + personFaceForm.getExtName();
String faceUrl = urlPrefix + "community/upload/face/" + filename;
person.setFaceUrl(faceUrl);
person.setState(2);
//更新人脸识别状态及图片地址
this.personService.updateById(person);
return Result.ok();
}
//方式一:
//模拟人脸识别
//调用随机数的工具类 随机生成模拟id
// String faceId = RandomUtil.getBitRandom();
// //获取Base64编码的前60位
// String faceBase = personFaceForm.getFileBase64().substring(0, 60);
// //如果不是头像
// if(faceBase.equals("iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAAXNSR0IArs4c")) {
// return Result.error("人脸识别失败");
// }
// //存储头像
// //生成文件名 随机数+前端传入的文件首字母
// String filename = faceId + "." + personFaceForm.getExtName();
// //本地存储路径:D:/community/upload/face/+生成的文件名
// String savePath = face + filename;
// try {
// //将 Base64 编码的字符串解码,并将解码后的二进制数据写入到指定路径(savePath)的文件中
// Base64Util.decoderBase64File(personFaceForm.getFileBase64(), savePath);
// } catch (Exception e) {
// e.printStackTrace();
// }
// //生成头像访问路径 http://localhost:8181/community/upload/face/+生成的文件名
// String faceUrl = urlPrefix + "community/upload/face/" + filename;//文件协议
// //将更新的信息封装到实体类,从而更新数据库
// person.setFaceUrl(faceUrl);
// person.setState(2);
// person.setFaceBase(faceBase);
// //更新人脸识别状态及图片地址
// this.personService.updateById(person);
// return Result.ok();
}
return Result.error("未开启人脸识别");
}
两种人脸识别的方式:
第一种:模拟人脸识别
基本思路:
1.通过传入的表单信息中的ID 查询居民信息
- 没有查询到相应的居民信息 则说明居民不存在
- 检查居民是否已经完成人脸认证
- 文件的Base64位编码是否已经存在 如果为空说明没有人脸图片
2.进行人脸认证
- 生成模拟的faceid 和 文件的Base64位编码
- 将人脸信息存储在本地
- 更新数据库中的信息
实现代码
package com.southwind.form;
import lombok.Data;
@Data
public class PersonFaceForm {
//居民ID
private Integer personId;
//人脸图片的后缀
private String extName;
//文件的Base64编码
private String fileBase64;
}
@LogAnnotation("人脸采集")
@PostMapping("/addPerson")
public Result addPerson(@RequestBody PersonFaceForm personFaceForm){
//通过传入的表单信息中的ID 查询居民信息
Person person = this.personService.getById(personFaceForm.getPersonId());
//如果没有查询到相应的居民信息 则说明居民不存在
if(person == null){
return Result.error("居民不存在");
}
//数据库表中的state字段说明 人脸是否已经录入
if(person.getState() == 2){
return Result.error("人脸识别已通过,不需要重复识别");
}
//文件的Base64位编码是否已经存在 如果为空说明没有人脸图片
if(personFaceForm.getFileBase64() == null || personFaceForm.getFileBase64().equals("")){
return Result.error("请上传Base64编码的图片");
}
//如果相关的关于人脸上传的配置信息已经配置,则进行下面的内容
if(apiConfiguration.isUsed()){
//方式一:
//模拟人脸识别
//调用随机数的工具类 随机生成模拟id
String faceId = RandomUtil.getBitRandom();
//获取Base64编码的前60位
String faceBase = personFaceForm.getFileBase64().substring(0, 60);
//如果不是头像
if(faceBase.equals("iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAAXNSR0IArs4c")) {
return Result.error("人脸识别失败");
}
//存储头像
//生成文件名 随机数+前端传入的文件首字母
String filename = faceId + "." + personFaceForm.getExtName();
//本地存储路径:D:/community/upload/face/+生成的文件名
String savePath = face + filename;
try {
//将 Base64 编码的字符串解码,并将解码后的二进制数据写入到指定路径(savePath)的文件中
Base64Util.decoderBase64File(personFaceForm.getFileBase64(), savePath);
} catch (Exception e) {
e.printStackTrace();
}
//生成头像访问路径 http://localhost:8181/community/upload/face/+生成的文件名
String faceUrl = urlPrefix + "community/upload/face/" + filename;//文件协议
//将更新的信息封装到实体类,从而更新数据库
person.setFaceUrl(faceUrl);
person.setState(2);
person.setFaceBase(faceBase);
//更新人脸识别状态及图片地址
this.personService.updateById(person);
return Result.ok();
}
return Result.error("未开启人脸识别");
}
第二种:利用腾讯云的人脸识别
人员库的创建
1.创建密钥
在控制台中,进入访问管理
API密钥管理---新建密钥 切记复制密钥
在项目的配置文件当中,配置密钥的信息
2.创建人员库
搜索人脸认证,创建人员库
配置相关的人员库信息
serverIp 和 area 不需要修改used 表示启用passPercent 表示识别率,比如 80 表示图片相似度 80即认为通过
基本思路
腾讯云的人脸识别:
- 创建人脸认证账号
- 将人脸信息保存到腾讯云
- 将人脸信息保存到本地
实现代码:
@LogAnnotation("人脸采集")
@PostMapping("/addPerson")
public Result addPerson(@RequestBody PersonFaceForm personFaceForm){
//通过传入的表单信息中的ID 查询居民信息
Person person = this.personService.getById(personFaceForm.getPersonId());
//如果没有查询到相应的居民信息 则说明居民不存在
if(person == null){
return Result.error("居民不存在");
}
//数据库表中的state字段说明 人脸是否已经录入
if(person.getState() == 2){
return Result.error("人脸识别已通过,不需要重复识别");
}
//文件的Base64位编码是否已经存在 如果为空说明没有人脸图片
if(personFaceForm.getFileBase64() == null || personFaceForm.getFileBase64().equals("")){
return Result.error("请上传Base64编码的图片");
}
//如果相关的关于人脸上传的配置信息已经配置,则进行下面的内容
if(apiConfiguration.isUsed()){
// 方式二:调用腾讯API人脸识别
// 1.人脸认证账号创建
// 获取人脸识别的ID
String faceId = newPerson(personFaceForm,person.getUserName());
if(faceId == null){
return Result.error("人脸识别失败");
}
if (faceId != null) {
//生成头像访问路径
String filename = faceId + "." + personFaceForm.getExtName();
String faceUrl = urlPrefix + "community/upload/face/" + filename;
person.setFaceUrl(faceUrl);
person.setState(2);
//更新人脸识别状态及图片地址
this.personService.updateById(person);
return Result.ok();
}
}
return Result.error("未开启人脸识别");
}
private String newPerson(PersonFaceForm vo,String personName) {
String faceId = null;
//获取PersonFaceForm中的属性值
String faceBase64 = vo.getFileBase64();
String extname = vo.getExtName();
String personId = vo.getPersonId()+"";
//保存路径为 D:/community/upload/face/
String savePath = face;
//
if (faceBase64!=null && !faceBase64.equals("")) {
FaceApi faceApi = new FaceApi();
RootResp resp = faceApi.newperson(apiConfiguration, personId, personName, faceBase64);
if(resp.getRet()==0) {
JSONObject data = JSON.parseObject(resp.getData().toString());
faceId = data.getString("FaceId");
if(faceId!=null) {
String filename = faceId + "." + extname;
savePath += filename;
try {
Base64Util.decoderBase64File(faceBase64, savePath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
else {
return faceId;
}
}
return faceId;
}
调用的faceApi的newperson
public RootResp newperson(ApiConfiguration config, String personId, String personName, String image) {
RootResp result = new RootResp();
try{
//使用配置对象中的 SecretId 和 SecretKey 创建一个 Credential 对象,用于身份验证
Credential cred = new Credential(config.getSecretId(), config.getSecretKey());
//用于配置HTTP请求
HttpProfile httpProfile = new HttpProfile();
//设置请求发送的目标服务器地址
httpProfile.setEndpoint(config.getServerIp());
//配置客户端的相关信息
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
//与腾讯云真正进行交互的对象
IaiClient client = new IaiClient(cred, config.getArea(), clientProfile);
//创建一个 JSONObject 对象,用于存储请求参数
JSONObject paramObj = new JSONObject();
paramObj.put("GroupId", config.getGroupId());
paramObj.put("PersonId", config.getPersonIdPre() + personId);
paramObj.put("PersonName", personName);
paramObj.put("Image", image);
CreatePersonRequest req = CreatePersonRequest.fromJsonString(paramObj.toJSONString(), CreatePersonRequest.class);
CreatePersonResponse resp = client.CreatePerson(req);
result.setData(CreatePersonResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
result.setRet(-1);
result.setMsg(e.toString());
logger.error(e.toString());
}
logger.info(result);
return result;
}