vue 基于获取摄像头权限 拍照的人脸识别技术

为什么要做这个呢?

不同用户具有不同的权限 如果管理员用户被别人知晓了怎么办? 为了解决该用户 账号密码泄露导致的误操作。

前端实现思路是什么呢?

前端思想是获取摄像头权限 通过canvas 获取base64图片 然后转为二进制文件流 通过FormData(form表单提交)和后端交互 的方式进行识别

后端的实现思路呢?

创建一个sql 里面存一些有权限的人员信息包含照片等 用上传的照片和sql里面的数据进行对比人脸识别 进而知道进入当前系统的人是否具有某些权限 当然为了界面友好还需要有权限人员库用于展示

会遇到什么问题?

中间遇到了低版本浏览器无法使用canvas的问题 所以此方法只支持最新的浏览器
上传至服务器时出现http的谷歌因为安全设置问题 导致无法获取摄像机资源 需用https解决 或通过配置chrome安全限制解决 详情可以看我的下个文章

代码在哪里啊?(基于vue的初版代码)

调用方法

		living-validation>
	data设置如下
	livingValidationResponse:{
		isShowDialog: false,
		title: "活体检测",
		callbackName:"",
		item: {}	
	},

就在下面了 哈哈

<template>
	<div>
		<add-dialog :response="response" v-if="response.isShowDialog" ref="addDialogRef" @closeDialog="closeDialog">
			<div slot="libBox" class="libBox">
				<div class="auto">
					<video id="video" width="480" height="320" autoplay>video>
					<canvas id="canvas" width="480" height="320" style="display: none;">canvas>
					<img src="/static/images/body_default.png" id="img" width="480" height="320" style="margin-left: 20px;">
					
					<div>
						<button id="capture" title="点击进行拍照" @click="takePhoto">拍照button>
					div>
					<div>
						<button id="sure" title="是否用这张图片进行验证" @click="sure">确认button>
					div>
				div>
			div>
		add-dialog>
		<loading :loadingStatus="loadingStatus">loading>
		
	div>
template>
<script>
	import loading from "src/biz_components/loading/loading.vue";
	import addDialog from "biz_components/dialog.vue";
	export default {
		name: "livingvalidation",
		props: ["response"],
		/*	authuor wenbin by 2019-03-26
			response:{
			isShowDialog: false, //是否展示
			title: "活体检测", //标题
			item: {}	需要传入数据用于子组件使用
		},*/
		components: {
			addDialog: addDialog,
			loading:loading
		},
		data: function() {
			return {
				File:"", //上传图片文件
				streamPicture:"",
				loadingStatus: {
                    isShowDialogMark: false,
                    loadingTitle: "等待"
                },
			}
		},
        created:function(){
			this.$message.success('先进行人脸识别 识别成功方可进行新增布控');
        },
       
		mounted: function() {
			this.initCamera();
		},
		methods: {
			//初始化摄像头 获取摄像头权限
			initCamera(){
				let self = this;
				navigator.getUserMedia  = navigator.getUserMedia ||navigator.webkitGetUserMedia ||navigator.mozGetUserMedia;
				if (navigator.getUserMedia) {
					//调用用户媒体设备, 访问摄像头
					navigator.getUserMedia({video : {width: 480, height: 320}},function(stream) {
							var video = document.querySelector('video');
							video.srcObject = stream;
							self.streamPicture = stream; //关闭摄像头需要用
							video.onloadedmetadata = function(e) {
								video.play();
							};
						}, function(err) {
							console.log("访问用户媒体设备失败: " + err.name);
						})
				} else {
					self.$message.error('不支持访问用户媒体');
				}
			},
			//拍照点击
			takePhoto(){
				let canvas = document.getElementById('canvas');
				let context = canvas.getContext('2d');
				context.drawImage(video, 0, 0, 480, 320);      
				// 获取图片base64链接
				var image = canvas.toDataURL('image/png');
				// 定义一个img
				var img = document.getElementById("img");
				//设置属性和src
				//img.id = "imgBoxxx";
				img.src = image;
				//将图片添加到页面中
				//document.body.appendChild(img);
				function dataURLtoFile(dataurl, filename) {
					var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
						bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
					while (n--) {
						u8arr[n] = bstr.charCodeAt(n);
					}
					return new File([u8arr], filename, {type: mime});
				}
				this.File = dataURLtoFile(image, 'aa.png');
				//console.log(dataURLtoFile(image, 'aa.png'));
				return dataURLtoFile(image, 'aa.png');
			},
			//确认图片上传 人脸识别 确定权限
			sure(){
				debugger;
				var self = this;
				//console.log(this.File);
				if(this.File == ""){ //如果不点击拍照
					self.$message.warning(`请先拍照再点击确定`);
					return;
				}
				this.loadingStatus.isShowDialogMark = true;
				var formData = new FormData();
				formData.append("file",this.File);
				$.ajax({
					type: "POST", // 数据提交类型
					url: "/gateway/disposition-service/face_verification", // 发送地址
					data: formData, //发送数据
					async: true, // 是否异步
					processData: false, //processData 默认为false,当设置为true的时候,jquery ajax 提交的时候不会序列化 data,而是直接使用data
					contentType: false,
					success:function(data){
						if(data.data){
							self.$refs.addDialogRef.closeDialog();//关闭 dialog 组件 和 摄像机流 
							self.$emit('realAddNewTask'); //触发父组件realAddNewTask方法 弹出新建布控窗口
							self.$message.success(`${data.message}`);
						}else{
							self.$message.warning(`${data.message}`);
						}
						self.loadingStatus.isShowDialogMark = false;
					},
					error:function(e){
						self.loadingStatus.isShowDialogMark = false;
						self.$message.warning(`${e}`);
						//console.log("不成功"+e);
					}
				});
			},
			closeDialog(){
				//console.log("closeDialog");
				if(this.streamPicture){
                    this.streamPicture.getTracks()[0].stop();//如果是活体检测界面 关闭摄像机
                }
			}
		},
	}
script>
<style lang="less" scoped>
	@import "../less/ixwpre.less";
		/* 样式 写的比较随意 感觉不好看 可以自行修改*/
		.libBox{
			width:100%;
			height:100%;
			background:  url("/static/images/searchIdentity.png") no-repeat; 
			#capture{
				position: absolute;
				right: 190px;
				bottom: -40px;

			}
			#video{
				position: absolute;
				right: 0;
				top: 0;
			}
			#img{
				position: absolute;
				left: 0;
				top: 0;
			}
			.auto{
				position: absolute;
				left: 50%;
				top: 50%;
				height: 320px;
				margin-top: -160px;
			}
			#sure{
				position: absolute;
				left: 210px;
				bottom: -40px;

			}
			button{
				cursor: pointer;
				margin: 0 auto;
				border: 1px solid #f0f0f0;
				background: #5CACEE;
				color: #FFF;
				width: 100px;
				height: 36px;
				line-height: 36px;
				border-radius: 8px;
				text-align: center;
				/*禁止选择*/
				-webkit-touch-callout: none; /* iOS Safari */
				-webkit-user-select: none; /* Chrome/Safari/Opera */
				-khtml-user-select: none; /* Konqueror */
				-moz-user-select: none; /* Firefox */
				-ms-user-select: none; /* Internet Explorer/Edge */
				user-select: none; /* Non-prefixed version, currently not supported by any browser */
			}
		}
		
style>

基于组件 dialog 不同的dialog不尽相同 接下来是我写的dialog

<template>
    <transition name="dialog-frame" :css="response.isUseTransition">
        <div class="model-dialog" :style="setFullScreenStyle" v-if="response.isShowDialog">
            <iframe scroll="none" src="about:blank" class="model-dialog-iframe" >iframe>
            <div class="dialog-main">
                <p class="dialog-title-box"><span class="dialog-title">{{response.title}}span>
                    <a href="javascript:void(0)" class="fr icon-dialog-close" @click="closeDialog">a>
                p>
                <div style="position:absolute;left:0;right:0; top: 30px; bottom:0; overflow-y: auto; overflow-x: hidden;" class="model-dialog-content">
                    <slot name="libBox">
                    slot>
                div>
            div>
        div>
    transition>
template>
<script>
	export default {
		name: "newDialog",
		props: ["response", "closeFn", "isFullScreen"],
		data: function() {
			return {
				modelBg: false
			}
		},
		computed: {
			setFullScreenStyle: function() {
				if(this.isFullScreen) {
					return {'top': '0'};
				} else {
					return {'top': '50px'};
				}
			}
        },
        watch:{
            response:function(data){
                console.log("transition",data.isUseTransition)
            }
        },
		methods: {
			/**
			 * 关闭弹层
			 * @return {[type]} [description]
			 */
			closeDialog: function() {
                //console.log("closeDialog11");
                $('#frequentViolationsId').css('overflow','');
                var self = this;
                //console.log(self.response);
                // if(self.response.streamPicture){
                //     self.response.streamPicture.getTracks()[0].stop();//如果是活体检测界面 关闭摄像机
                // }
                self.$emit('closeDialog');
				if(self.closeFn) self.closeFn();
				setTimeout(function() { //添加延迟 先让ocx先消失
					self.response.isShowDialog = false;
				}, 90);
			}
		}
	}
script>
<style lang="less">
    @imgPath: "../assets/images/dialog";
    .dialog-frame-enter {
        opacity: 0;
        transform: translateY(800px);
        -webkit-transform: translateY(800px);
    }
    .dialog-frame-enter-active {
        opacity: 1;
        transition: all .2s ease;
    }
    .dialog-frame-leave{
        opacity: 1;
    }
    .dialog-frame-leave-active {
        opacity: 0;
        transition: all .5s ease;
        transform: translateY(800px);
        -webkit-transform: translateY(800px);
    }

    .icon-dialog-close {
        position: absolute;
        right: 0;
        top: 0;
        width: 30px;
        height: 30px;
        background: url("@{imgPath}/close-btn.png") no-repeat;
        &:hover {
            background-position: -30px 0;
         }
    }
    .model-dialog {
        position: fixed;
        top: 50px;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 950;
    }
    .model-dialog-iframe{
        position: absolute;
        width: 100%;
        height: 100%;
    }
    .dialog-main {
        position: absolute;
        height: 100%;
        width: 100%;
        bottom: 0px;
        left: 0px;
        background: #fff;
        z-index: 900;
        overflow: hidden;
    }

    .dialog-title-box {
        position: relative;
        height: 30px;
        background:#303845;
        color: #fff;
        text-align: left;
        padding-left: 30px;
        font-size: 14px;
        line-height: 30px;
    }

    .dialog-title {
        position: relative;
    }
style>

你可能感兴趣的:(个人日记,vue,javascript)