272 lines
9.1 KiB
Vue
272 lines
9.1 KiB
Vue
<template>
|
||
<view class="_vocr">
|
||
<view>
|
||
<view v-if="workstep == 1" class="rel">
|
||
<cover-image v-if="maskpng" :src="file_stor(maskpng)" class="abs t0 l0" :style="{opacity:0.8,width: '100%', height: height + 'vw'}"></cover-image>
|
||
<camera device-position="back" flash="off" :style="{width: '100%', height: height + 'vw'}"></camera>
|
||
</view>
|
||
<view v-if="workstep == 2" class="rel">
|
||
<image :src="src" :style="{width: '100%', height: height + 'vw',display:'block'}"></image>
|
||
<view v-for="txtpo in txtpos" class='abs' @tap="boxclick(txtpo)" :style="{top:txtpo.top,left:txtpo.left,width:txtpo.width,height:txtpo.height,minWidth:'0.7em',minHeight:'0.7em',border:'1px solid ' + bordercolor,borderRadius:'3px'}"></view>
|
||
<view v-if="txtpos.length == 0" class='abs b0 r0 _noocr'>无文字</view>
|
||
</view>
|
||
<view class="flex px2" style="justify-content: space-between;height:3em;">
|
||
<view>
|
||
<ciy-svgimg v-if="workstep==0" @tap="workstep=1" :src="svg.opencamera" ciystyle="width:2em;height:2em;"></ciy-svgimg>
|
||
<ciy-svgimg v-else @tap="workstep=0" :src="svg.closecamera" ciystyle="width:2em;height:2em;"></ciy-svgimg>
|
||
</view>
|
||
<view>
|
||
<button v-if="workstep==1" class="btn lg cc" @tap="ocrnow">拍摄识别</button>
|
||
</view>
|
||
<view>
|
||
<ciy-svgimg v-if="workstep == 2" @tap="workstep=1" :src="svg.recamera" ciystyle="width:2em;height:2em;"></ciy-svgimg>
|
||
<view v-else style="width:2em;height:2em;"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
emits: ['change', 'textclick'],
|
||
props: {
|
||
maskpng: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
height: {
|
||
type: String,
|
||
default: '66'
|
||
},
|
||
bordercolor: {
|
||
type: String,
|
||
default: '#00e112'
|
||
},
|
||
mode: {
|
||
type: String,
|
||
default: 'text' //idcard身份证自动校正,none 无文字不ocr,text ocr有文字(无文字5秒超时)
|
||
},
|
||
checkidcard: {
|
||
type: String,
|
||
default: '' //front人像,back国徽
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
txtpos: [],
|
||
src: '',
|
||
svg:{
|
||
opencamera:'<svg viewBox="0 0 1024 1024" version="1" xmlns="http://www.w3.org/2000/svg"><path d="M868 288a64 64 0 0 1 101 52l3 303a64 64 0 0 1-103 51l-207-158a64 64 0 0 1 2-103l204-145z" fill="#1296db"></path><path d="M144 192h456a96 96 0 0 1 96 96v417a96 96 0 0 1-96 96H144a96 96 0 0 1-96-96V288a96 96 0 0 1 96-96z" fill="#1296db"></path></svg>',
|
||
closecamera:'<svg viewBox="0 0 1024 1024" version="1" xmlns="http://www.w3.org/2000/svg"><path d="M107 152A32 32 0 1 1 149 104l768 672a32 32 0 0 1-42 48l-768-672z" fill="#1296db"></path><path d="M733 462l-37-52 172-122a64 64 0 0 1 101 52l3 303a64 64 0 0 1-20 47l-44-47-3-303-172 122z m-35-54l36 53c-11 7-24 12-37 12a64 64 0 0 1-64-64V288a32 32 0 0 0-32-32h-206V192h206a96 96 0 0 1 96 96v121c0-0 1-1 2-1zM632 608h64v97a96 96 0 0 1-96 96H144a96 96 0 0 1-96-96V288a96 96 0 0 1 96-96h96v64h-96a32 32 0 0 0-32 32v417a32 32 0 0 0 32 32h456a32 32 0 0 0 32-32V608z" fill="#1296db"></path></svg>',
|
||
recamera:'<svg viewBox="0 0 1024 1024" version="1" xmlns="http://www.w3.org/2000/svg"><path d="M533 175v-62l-228 143 228 143v-107c114 26 200 128 200 249 0 141-116 257-257 257s-257-116-257-257c0-32-26-57-57-57-32 0-57 26-57 57 0 204 167 371 371 371s371-167 371-371c0-185-137-339-314-366z" fill="#1296db"></path></svg>'
|
||
},
|
||
workstep: 0,
|
||
};
|
||
},
|
||
watch: {},
|
||
computed: {},
|
||
mounted() {
|
||
var vkopn = {};
|
||
vkopn.version = 'v1';
|
||
vkopn.track = {};
|
||
vkopn.track.IDCard = {};
|
||
vkopn.track.IDCard.mode = 2;
|
||
vkopn.track.OCR = {};
|
||
vkopn.track.OCR.mode = 2;
|
||
vkopn.gl = this.gl;
|
||
this.session = wx.createVKSession(vkopn);
|
||
this.session.start(err => {
|
||
this.session.on('updateAnchors', this.onanchor);
|
||
});
|
||
},
|
||
unmounted() {
|
||
this.session.off('updateAnchors', this.onanchor);
|
||
this.session.stop();
|
||
this.session.destroy();
|
||
},
|
||
methods: {
|
||
boxclick(po) {
|
||
this.$emit('textclick', po);
|
||
},
|
||
onanchor(anchors) {
|
||
if (typeof(this.auchorcb) == 'function')
|
||
this.auchorcb(anchors);
|
||
},
|
||
Step(step) {
|
||
this.workstep = step;
|
||
},
|
||
//摄像头开关在下,且用图标占位。
|
||
//重新拍摄在下右。方便点击。
|
||
//拍摄识别在中。
|
||
|
||
|
||
//按拍摄后,自动识别
|
||
//无文字,在图片旁边标注即可。
|
||
//拍照完成,上面有文字。
|
||
//用户点门头,上传门头。
|
||
//用户点企业,再点文字,文字进企业框。
|
||
//用户点展位号,
|
||
//
|
||
|
||
|
||
//拍照,拍照+识别
|
||
//在摄像头正常的时候,点一下门头,拍照,上传。
|
||
//在摄像头正常的时候,点一下企业,拍照识别,点框
|
||
//在摄像头正常的时候,点一下企业,拍照识别,点框
|
||
|
||
|
||
|
||
async runvk(mode, file) {
|
||
return new Promise(async (resolve, reject) => {
|
||
var _time = setTimeout(() => {
|
||
reject('timeout');
|
||
}, 5000);
|
||
this.auchorcb = anchors => {
|
||
clearTimeout(_time);
|
||
resolve(anchors);
|
||
};
|
||
if (mode == 'idcard') {
|
||
this.session.detectIDCard({
|
||
frameBuffer: file.buffer,
|
||
width: file.width,
|
||
height: file.height,
|
||
getAffineImg: true,
|
||
});
|
||
} else {
|
||
this.session.runOCR({
|
||
frameBuffer: file.buffer,
|
||
width: file.width,
|
||
height: file.height,
|
||
});
|
||
}
|
||
}).catch(e => {
|
||
console.error('vk error', e);
|
||
return e;
|
||
});
|
||
},
|
||
ocrnow() {
|
||
const ctx = wx.createCameraContext();
|
||
ctx.takePhoto({
|
||
quality: 'high',
|
||
success: async (res) => {
|
||
this.workstep = 2;
|
||
this.txtpos = [];
|
||
this.src = res.tempImagePath;
|
||
res.buffer = await this.getimgbuffer(res);
|
||
var ret = {};
|
||
ret.tempimg = res.tempImagePath;
|
||
ret.width = res.width;
|
||
ret.height = res.height;
|
||
if (this.mode == 'idcard') {
|
||
var anchors = await this.runvk('idcard', res);
|
||
var anchor = anchors[0];
|
||
if (anchor.isComplete != 1)
|
||
return this.alert('身份证拍摄不完整');
|
||
if (this.checkidcard == 'front') {
|
||
if (anchor.label != 0)
|
||
return this.alert('请拍摄身份证人像面');
|
||
if (anchor.orientation != 0)
|
||
return this.alert('请朝上放正拍摄身份证');
|
||
}
|
||
if (this.checkidcard == 'back') {
|
||
if (anchor.label != 1)
|
||
return this.alert('请拍摄身份证国徽面');
|
||
if (anchor.orientation != 0)
|
||
return this.alert('请朝上放正拍摄身份证');
|
||
}
|
||
ret.idcardface = anchor.label; //0 照片面 / 1 国徽面
|
||
ret.orientation = anchor.orientation; // 身份证朝向 (0 朝上 1 朝下 2 朝下 3 朝左)
|
||
|
||
const canvas = wx.createOffscreenCanvas({
|
||
type: '2d',
|
||
width: anchor.affineImgWidth,
|
||
height: anchor.affineImgHeight,
|
||
});
|
||
const context = canvas.getContext('2d');
|
||
|
||
context.clearRect(0, 0, anchor.affineImgWidth, anchor.affineImgHeight);
|
||
|
||
context.setTransform(
|
||
Number(anchor.affineMat[0]), Number(anchor.affineMat[3]), Number(anchor.affineMat[1]),
|
||
Number(anchor.affineMat[4]), Number(anchor.affineMat[2]), Number(anchor.affineMat[5])
|
||
);
|
||
context.drawImage(this._img, 0, 0, res.width, res.height);
|
||
const imageData = context.getImageData(0, 0, anchor.affineImgWidth, anchor.affineImgHeight);
|
||
res.buffer = imageData.data.buffer;
|
||
res.width = anchor.affineImgWidth;
|
||
res.height = anchor.affineImgHeight;
|
||
this.src = canvas.toDataURL();
|
||
}
|
||
ret.txts = [];
|
||
ret.txtall = '';
|
||
if (this.mode != 'none') { //无文字时会有直接卡死bug
|
||
try {
|
||
var anchors = await this.runvk('ocr', res);
|
||
ret.anchors = anchors;
|
||
for (var i in anchors) {
|
||
if (anchors[i].text)
|
||
ret.txtall = anchors[i].text;
|
||
if (!anchors[i].subtext)
|
||
continue;
|
||
ret.txts.push(anchors[i].subtext);
|
||
var pos = anchors[i].box;
|
||
this.txtpos.push({
|
||
text: anchors[i].subtext,
|
||
alltext: anchors[i].text,
|
||
pos: pos,
|
||
top: (pos[0].y * 100) + '%',
|
||
left: (pos[0].x * 100) + '%',
|
||
width: ((pos[1].x - pos[0].x) * 100) + '%',
|
||
height: ((pos[2].y - pos[0].y) * 100) + '%',
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.log(e);
|
||
//this.toast('无文字,请重新拍摄');
|
||
//this.workstep = 1;
|
||
//return;
|
||
}
|
||
}
|
||
this.$emit('change', ret);
|
||
}
|
||
});
|
||
},
|
||
async getimgbuffer(file) {
|
||
const canvas = wx.createOffscreenCanvas({
|
||
type: '2d',
|
||
width: file.width,
|
||
height: file.height,
|
||
});
|
||
const context = canvas.getContext('2d');
|
||
this._img = canvas.createImage();
|
||
this._img.src = file.tempImagePath;
|
||
var err = await this.goe(this.go_load(this._img));
|
||
if (err)
|
||
return reject('Image Load Error');
|
||
|
||
context.clearRect(0, 0, file.width, file.height);
|
||
context.drawImage(this._img, 0, 0, file.width, file.height);
|
||
|
||
var imgData = context.getImageData(0, 0, file.width, file.height);
|
||
return imgData.data.buffer;
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
._vocr {
|
||
margin: 0;
|
||
padding: 0;
|
||
position: relative;
|
||
}
|
||
|
||
._noocr {
|
||
background: #00000044;
|
||
color: #000000;
|
||
text-shadow: 1px 1px 0 #ffffff88;
|
||
padding: 0.3em 0.8em;
|
||
border-radius: 0.5em 0 0 0;
|
||
}
|
||
</style> |