前端页面拍照采集页面
<template>
<view class="takePhotoView">
<video ref="video" style="object-fit:fill" :show-center-play-btn="false" muted preload :width="videoWidth"
:height="videoHeight"></video>
<div :device-position="device" flash="off" @error="error"
style="width: 100%; height: 100vh;position: fixed; top: 0;left: 0;">
<cover-view
style="position: relative; width: 100vw;height: 100vh; display: flex;flex-direction: column; align-items: center;justify-content: center;">
<cover-view
style="position: absolute;width: 100%;height: 100%;z-index: -9;display: flex;flex-direction: row;justify-content: center;align-items: center;">
<cover-image class="coverImage" style="width: 100vw;height:177.8vw;"
src="../../static/images/allrlk.png"></cover-image>
</cover-view>
<cover-view
style="flex: 1;width: 100vw;display: flex;align-items: center;center;background:rgba(0,0,0,1);">
<cover-image class="coverImage" @click="close"
style="z-index: 1001;width: 60rpx;height: 60rpx;margin-left: 50rpx;"
src="../../static/images/gb.png"></cover-image>
</cover-view>
<cover-view :style="'width: 100vw; height:'+scale*100+'vw;'"></cover-view>
<cover-view
style="flex: 1;width: 100vw;display: flex;justify-content: space-around;align-items: center;center;background:rgba(0,0,0,1);">
<cover-image class="coverImage" @click="deviceQH"
:style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasSwitch?'':'opacity: 0;')"
src="../../static/images/qh.png"></cover-image>
<cover-image class="coverImage" @click="photoShoot"
style="width: 100rpx;height: 100rpx;z-index: 1001;"
src="../../static/images/pz.png"></cover-image>
<cover-image class="coverImage" @click="chooseImage"
:style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasAlbum?'':'opacity: 0;')"
src="../../static/images/tk.png"></cover-image>
</cover-view>
<cover-view
style="position: absolute;z-index: 1001;width: 100%;bottom:250rpx;font-size: 66upx;text-align: center; color: #000000;font-weight: bold;">
避免头发口罩遮挡
</cover-view>
</cover-view>
</div >
</view>
</template>
摄像头初始化设置
import config from '@/config'
const baseUrl = config.baseUrl
const constraints = {
audio: false,
video: {
width: 600,
height: 400,
facingMode: "user"
}
}
初始化采集页面和画布
this.video = this.$refs.video;
uni.getSystemInfo({
success: function (res) {
this.videoWidth=Math.max(res.windowWidth, res.windowHeight) - 120
constraints.video.width=this.videoWidth
this.videoHeight=Math.min(res.windowWidth, res.windowHeight)
constraints.video.height=this.videoHeight
},
});
this.useCamera()
调用方法
/**
* 使用摄像头
*/
useCamera(){
let that = this;
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
// 如果不是通过loacalhost或者通过https访问会将报错捕获并提示
try{
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
let promise =navigator.mediaDevices.getUserMedia(constraints);
promise.then((stream) => {
window.stream=stream;
// 返回参数
const video = document.querySelector("video");
if ("srcObject" in video) {
video.srcObject = stream;
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
video.src = window.URL.createObjectURL(stream);
}
video.onloadedmetadata = function (e) {
video.play();
};
}).catch((error) => {
console.log(error);
});
}catch(err){
this.httpsAlert = `您现在在使用非Https访问,
请先在chrome://flags/#unsafely-treat-insecure-origin-as-secure中修改配置,
添将当前链接${window.location.href}添加到列表,
并且将Insecure origins treated as secure修改为enabled,
修改完成后请重启浏览器后再次访问!`
}
},
// 关闭摄像头
closeCamera () {
if (!this.$refs['video'].srcObject) return
let stream = this.$refs['video'].srcObject
let tracks = stream.getTracks()
tracks.forEach(track => {
track.stop()
})
this.$refs['video'].srcObject = null
},
业务功能
/**
* 拍照上传
*/
photoShoot(){
// 拿到图片的base64
this.canvas = document.createElement("canvas");
let context = this.canvas.getContext("2d");
const video = document.querySelector("video");
this.canvas.width = Math.min(video.videoWidth, video.videoHeight);
this.canvas.height = Math.max(video.videoWidth, video.videoHeight);
context.drawImage(video, 0, 0, this.canvas.width,this.canvas.height);
let canvas = this.canvas.toDataURL("image/png");
// 拍照以后将video隐藏
// 停止摄像头成像
video.srcObject.getTracks()[0].stop()
video.pause()
if(canvas) {
// 拍照将base64转为file流文件
let blob = this.dataURLtoBlob(canvas);
let file = this.blobToFile(blob, "imgName");
let image = window.URL.createObjectURL(file)
this.uploadFilePromise(image);
} else {
console.log('canvas生成失败')
}
},
/**
* 将图片转为blob格式
* dataurl 拿到的base64的数据
*/
dataURLtoBlob(dataurl) {
let 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 Blob([u8arr], {
type: mime
});
},
/**
* 生成文件信息
* theBlob 文件
* fileName 文件名字
*/
blobToFile(theBlob, fileName) {
theBlob.lastModifiedDate = new Date().toLocaleDateString();
theBlob.name = fileName;
return theBlob;
},
deviceQH() {
this.useFrontCamera = !this.useFrontCamera;
if (window.stream) {
window.stream.getTracks().forEach((track) => {
track.stop();
});
}
constraints.video.facingMode = this.useFrontCamera ? 'user' : { exact: 'environment' };
let promise = navigator.mediaDevices.getUserMedia(constraints); // 调用将询问用户是否允许访问摄像机。如果用户拒绝,它将引发异常并且不返回流。因此,必须在 try/catch 块内处理这种情况,它返回一个Promise,必须使用 async/await 或 then 块
promise.then((stream) => {
window.stream=stream;
// 返回参数
const video = document.querySelector("video");
if ("srcObject" in video) {
video.srcObject = stream;
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
video.src = window.URL.createObjectURL(stream);
}
video.onloadedmetadata = function (e) {
video.play();
};
}).catch((error) => {
console.log(error);
});
},
close() {
this.$emit("close")
},
chooseImage() {
let _this = this;
if (!this.hasAlbum) {
return
}
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
uni.chooseImage({
count: 1, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: (res) => {
_this.uploadFilePromise(res.tempFilePaths[0]);
}
});
},
error(e) {
console.log(e);
},
uploadFilePromise(url) {
//弹出照片核验的效果
let _self=this;
return new Promise((resolve, reject) => {
let a = uni.uploadFile({
url: baseUrl +'/file/uploadMinio',
filePath: url,
name: 'file',
success: (res) => {
//弹出验证情况
_self.$modal.loading("验证中,请耐心等待...")
let result = JSON.parse(res.data)
console.log(result)
if(result.code==''||result.code==null){
_self.$emit('takePhoto',null)
return
}
if(result.code!=200){
this.$tab.reLaunch('/pages/face/faceValidate?code='+result.code+"&msg="+result.msg)
return
}
setTimeout(() => {
let param={};
param.url=result.data.url
param.feature=result.data.feature
_self.$emit('takePhotoTwo',param)
}, 500)
}
});
})
},
}
}
以上是前端H5人脸识别的方法,下面是全代码
<template>
<view class="takePhotoView">
<video ref="video" style="object-fit:fill" :show-center-play-btn="false" muted preload :width="videoWidth"
:height="videoHeight"></video>
<div :device-position="device" flash="off" @error="error"
style="width: 100%; height: 100vh;position: fixed; top: 0;left: 0;">
<cover-view
style="position: relative; width: 100vw;height: 100vh; display: flex;flex-direction: column; align-items: center;justify-content: center;">
<cover-view
style="position: absolute;width: 100%;height: 100%;z-index: -9;display: flex;flex-direction: row;justify-content: center;align-items: center;">
<cover-image class="coverImage" style="width: 100vw;height:177.8vw;"
src="../../static/images/allrlk.png"></cover-image>
</cover-view>
<cover-view
style="flex: 1;width: 100vw;display: flex;align-items: center;center;background:rgba(0,0,0,1);">
<cover-image class="coverImage" @click="close"
style="z-index: 1001;width: 60rpx;height: 60rpx;margin-left: 50rpx;"
src="../../static/images/gb.png"></cover-image>
</cover-view>
<cover-view :style="'width: 100vw; height:'+scale*100+'vw;'"></cover-view>
<cover-view
style="flex: 1;width: 100vw;display: flex;justify-content: space-around;align-items: center;center;background:rgba(0,0,0,1);">
<cover-image class="coverImage" @click="deviceQH"
:style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasSwitch?'':'opacity: 0;')"
src="../../static/images/qh.png"></cover-image>
<cover-image class="coverImage" @click="photoShoot"
style="width: 100rpx;height: 100rpx;z-index: 1001;"
src="../../static/images/pz.png"></cover-image>
<cover-image class="coverImage" @click="chooseImage"
:style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasAlbum?'':'opacity: 0;')"
src="../../static/images/tk.png"></cover-image>
</cover-view>
<cover-view
style="position: absolute;z-index: 1001;width: 100%;bottom:250rpx;font-size: 66upx;text-align: center; color: #000000;font-weight: bold;">
避免头发口罩遮挡
</cover-view>
</cover-view>
</div >
</view>
</template><script>
import config from '@/config'
const baseUrl = config.baseUrl
const constraints = {
audio: false,
video: {
width: 600,
height: 400,
facingMode: "user"
}
}
export default {
data() {
return {
hasSwitch:true,
hasAlbum:true,
initScale:1.277,
original:true,
canvasW: 0,
canvasH: 0,
device: 'front',
videoShow: false,
pictureShow: false,
// 图片地址
picture: '',
// 用于视频识别的节点
canvas: null,
video: null,
image: null,
timeout: 0,
// 模型识别的条件
options: '',
// 提示控制
noOne: '',
moreThanOne: '',
// 不是通过Https访问提示
httpsAlert: '',
videoWidth:600,
videoHeight:400,
useFrontCamera: true,
}
},
mounted(){
this.video = this.$refs.video;
uni.getSystemInfo({
success: function (res) {
this.videoWidth=Math.max(res.windowWidth, res.windowHeight) - 120
constraints.video.width=this.videoWidth
this.videoHeight=Math.min(res.windowWidth, res.windowHeight)
constraints.video.height=this.videoHeight
},
});
this.useCamera()
},
computed: {
scale: function() {
if (this.initScale > 1.77) return 1.77;
if (this.initScale < 1.27) return 1.27;
return this.initScale;
}
},
methods: {
/**
* 使用摄像头
*/
useCamera(){
let that = this;
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
// 如果不是通过loacalhost或者通过https访问会将报错捕获并提示
try{
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
let promise =navigator.mediaDevices.getUserMedia(constraints);
promise.then((stream) => {
window.stream=stream;
// 返回参数
const video = document.querySelector("video");
if ("srcObject" in video) {
video.srcObject = stream;
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
video.src = window.URL.createObjectURL(stream);
}
video.onloadedmetadata = function (e) {
video.play();
};
}).catch((error) => {
console.log(error);
});
}catch(err){
this.httpsAlert = `您现在在使用非Https访问,
请先在chrome://flags/#unsafely-treat-insecure-origin-as-secure中修改配置,
添将当前链接${window.location.href}添加到列表,
并且将Insecure origins treated as secure修改为enabled,
修改完成后请重启浏览器后再次访问!`
}
},
// 关闭摄像头
closeCamera () {
if (!this.$refs['video'].srcObject) return
let stream = this.$refs['video'].srcObject
let tracks = stream.getTracks()
tracks.forEach(track => {
track.stop()
})
this.$refs['video'].srcObject = null
},/**
* 人脸识别方法
* 通过canvas节点识别
* 节点对象执行递归识别绘制
*/
/* async recognizeFace(){
if (this.video.paused) return clearTimeout(this.timeout);
this.canvas.getContext('2d', { willReadFrequently: true }).drawImage(this.video, 0, 0, 400, 400);
const results = await faceApi.detectAllFaces(this.canvas, this.options).withFaceLandmarks();
if(results.length === 0){
if(this.moreThanOne !== ''){
this.moreThanOne.close()
this.moreThanOne = ''
}
if(this.noOne === ''){
this.noOne = this.$message({
message: '未识别到人脸',
type: 'warning',
duration: 0
});
}
}else if(results.length > 1){
if(this.noOne !== ''){
this.noOne.close()
this.noOne = ''
}
if(this.moreThanOne === ''){
this.moreThanOne = this.$message({
message: '检测到镜头中有多个人',
type: 'warning',
duration: 0
});
}
}else{
if(this.noOne !== ''){
this.noOne.close()
this.noOne = ''
}
if(this.moreThanOne !== ''){
this.moreThanOne.close()
this.moreThanOne = ''
}
}
// 通过canvas显示video信息
this.timeout = setTimeout(() => {
return this.recognizeFace()
});
}, */
/**
* 拍照上传
*/
photoShoot(){
// 拿到图片的base64
this.canvas = document.createElement("canvas");
let context = this.canvas.getContext("2d");
const video = document.querySelector("video");
this.canvas.width = Math.min(video.videoWidth, video.videoHeight);
this.canvas.height = Math.max(video.videoWidth, video.videoHeight);
context.drawImage(video, 0, 0, this.canvas.width,this.canvas.height);
let canvas = this.canvas.toDataURL("image/png");
// 拍照以后将video隐藏
// 停止摄像头成像
video.srcObject.getTracks()[0].stop()
video.pause()
if(canvas) {
// 拍照将base64转为file流文件
let blob = this.dataURLtoBlob(canvas);
let file = this.blobToFile(blob, "imgName");
let image = window.URL.createObjectURL(file)
this.uploadFilePromise(image);
} else {
console.log('canvas生成失败')
}
},
/**
* 将图片转为blob格式
* dataurl 拿到的base64的数据
*/
dataURLtoBlob(dataurl) {
let 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 Blob([u8arr], {
type: mime
});
},
/**
* 生成文件信息
* theBlob 文件
* fileName 文件名字
*/
blobToFile(theBlob, fileName) {
theBlob.lastModifiedDate = new Date().toLocaleDateString();
theBlob.name = fileName;
return theBlob;
},
deviceQH() {
this.useFrontCamera = !this.useFrontCamera;
if (window.stream) {
window.stream.getTracks().forEach((track) => {
track.stop();
});
}
constraints.video.facingMode = this.useFrontCamera ? 'user' : { exact: 'environment' };
let promise = navigator.mediaDevices.getUserMedia(constraints); // 调用将询问用户是否允许访问摄像机。如果用户拒绝,它将引发异常并且不返回流。因此,必须在 try/catch 块内处理这种情况,它返回一个Promise,必须使用 async/await 或 then 块
promise.then((stream) => {
window.stream=stream;
// 返回参数
const video = document.querySelector("video");
if ("srcObject" in video) {
video.srcObject = stream;
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
video.src = window.URL.createObjectURL(stream);
}
video.onloadedmetadata = function (e) {
video.play();
};
}).catch((error) => {
console.log(error);
});
},
close() {
this.$emit("close")
},
chooseImage() {
let _this = this;
if (!this.hasAlbum) {
return
}
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
uni.chooseImage({
count: 1, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: (res) => {
_this.uploadFilePromise(res.tempFilePaths[0]);
}
});
},
error(e) {
console.log(e);
},
uploadFilePromise(url) {
//弹出照片核验的效果
let _self=this;
return new Promise((resolve, reject) => {
let a = uni.uploadFile({
url: baseUrl +'/file/uploadMinio',
filePath: url,
name: 'file',
success: (res) => {
//弹出验证情况
_self.$modal.loading("验证中,请耐心等待...")
let result = JSON.parse(res.data)
console.log(result)
if(result.code==''||result.code==null){
_self.$emit('takePhoto',null)
return
}
if(result.code!=200){
this.$tab.reLaunch('/pages/face/faceValidate?code='+result.code+"&msg="+result.msg)
return
}
setTimeout(() => {
let param={};
param.url=result.data.url
param.feature=result.data.feature
_self.$emit('takePhotoTwo',param)
}, 500)
}
});
})
},
}
}
</script><style scoped>
.takePhotoView {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 9999;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}.canvas {
opacity: 0;
position: fixed;
top: 100vh;
z-index: -999;
}.coverImage {
z-index: 999;
}
uni-video {
width: 100%;
height: 100%;
}
</style>allrlk.png
gb.png
pz.png
qh.png
tk.png