微信小程序实现OCR扫描识别

Posted 三个木马人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信小程序实现OCR扫描识别相关的知识,希望对你有一定的参考价值。

在小程序还发过程中,经常会遇到对证件(身份证、驾驶证、营业执照)的扫描识别认证功能;这里我根据自己的经历借鉴总结一下相关的方法;

文章目录

一、第三方插件:OCR支持

1、添加第三方插件

第一步需要在微信小程序后台添加第三方插件,设置-第三方设置-插件管理,输入**ocr支持**搜索添加就可以了;

2、购买识别次数

OCR支持插件添加成功之后需要到开放社区购买识别次数,可以根据自己公司的业务需求量购买;(个人觉得有点贵)

3、使用

在 app.json 中声明引入插件,version 使用最新版本,provider 是OCR支持的 AppID;

 "plugins": 
    "ocr-plugin": 
      "version": "3.1.1",
      "provider": "wx4418e3e031e551be"
    
  

在使用OCR支持的页面 json 中引入组件


  "usingComponents": 
    "ocr-navigator": "plugin://ocr-plugin/ocr-navigator"
  

页面使用

//wxml
<ocr-navigator bind:onSuccess="success" certificateType="idCard" opposite="false">
  <button type="primary">身份证正面识别</button>
</ocr-navigator>
<ocr-navigator bind:onSuccess="success" certificateType="idCard" opposite="true">
  <button type="primary">身份证反面识别</button>
</ocr-navigator>
//js
Page(
	data:
	    name:'',
	    id:''
	,
	success(e)
		console.log(e.detail)
	    this.setData(
	      name:e.detail.name.text,
	      id:e.detail.id.text
	    )
	
)


注意:certificateType 类型不同属性也不同可以参考:OCR支持开发文档

二、百度OCR

1、申请百度AI开放平台账号

申请地址:http://ai.baidu.com/?track=cp:aipinzhuan|pf:pc|pp:AIpingtai|pu:title|ci:|kw:10005792

2、创建应用获取密钥

开放能力-文字识别 ,这里有身份证、银行卡、营业执照、护照、出生证明等识别服务;


按照个人需要填写信息,创建应用之后,在应用列表可以查看对应的 API Key 、Secret Key、AppID 等信息;

3、使用

1、配置合法域名

在使用之前,我们需要将百度OCR接口域名配置在微信小程序后台的合法域名中(https://aip.baidubce.com);

2、接口分析(身份证)

接口、入参参考:技术文档

3、调用

获取身份证图片

// 点击扫证,可拍照、可选择本机图片
getImage()
 let that = this
 wx.showActionSheet(
   itemList: ['拍照','相册'],
   success(res)
     let index = res.tabIndex;
     if(index == 0)
      that.gotophoto();
     else
      that.getAlbum();
     
   
 )
,
//从相册选择图片
getAlbum()
	let that = this;
	wx.chooseImage(
		count: 1,
		sourceType: ['album'],
		success:function(res)
			let path = res.tempFilePaths[0];
			that.getBase64Path(path).then(base64=>
				that.ocrCard(base64).then(res=>
					//这里就是扫描的结果
					console.log(res);
				)
			);
		
	)
,
//本地转化为base64编码
getBase64Path(path)
	return new Prmise((resolve,reject)=>
		wx.getFileSystemManager().readFile(
	      filePath: path,
	      encoding: 'base64',
	      success: function (res) 
	        resolve(res.data);
	      ,
	    )
	)

调用身份证OCR识别之前需要先获取 access_token

//获取access_token
getToken()
	 return new Promise((resolve,reject)=>
      let appKey = 'xxxxx';
      let secretKey  = 'xxxxx';
      let url = `https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=$appKey&client_secret=$secretKey`;
      wx.request(
        url: url,
        method:'POST',
        dataType:'json',
        header:
          'content-type': 'application/json; charset-UTF-8'
        ,
        success(res)
          resolve(res);
        ,
        fail(err)
          reject(err);
        
      )
    )
,

识别身份证

//识别图片
ocrCard(data)
    return new Promise((resolve,reject)=>
      this.getToken().then(res=>
        let token = res.data.access_token
        let url = `https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=$token`;
        wx.request(
          url: url,
          method:'POST',
          header:
            'Content-Type': 'application/x-www-form-urlencoded'
          ,
          data:
            image:data,
            id_card_side:'front'
          ,
          success(res)
            resolve(res);
          ,
          fail(err)
            reject(err);
          
        )
      )
    )
  

上面就是照片选择到身份证OCR识别的整个过程;关于 access_token 也可以在页面加载的时候获取一次,然后将 access_token 缓存在本地,这样每次获取的时候需要先判断 access_token 是否过期;

三、拍照、照片裁剪

对于拍照的照片,有些时候我们可能需要对其进行裁剪;
1、跳转到拍照页面

gotophoto()
  wx.navigateTo(
    url: '/pages/photo/photo'
  )

2、拍照页面

<camera type="2d" device-position="back" resolution="high" style="width: 100%; height: 100vh;">
  <cover-view>
  <!-- 拍照后的回显图片,必须在裁剪蒙层上面,不然不显示 -->
    <cover-image wx:if="showPic" src="image"></cover-image>
    <!-- 拍照蒙层,按照这个裁剪 -->
    <cover-image src="http://print.jiaynet.cn/icons/zhezhao.png"></cover-image>
  </cover-view>
  <!-- 拍照按钮 -->
  <cover-view style="color: #fff; width: 100%; position: fixed; left:0; bottom: 40rpx;">
    <cover-view bindtap="takePhotoAction" style="width: 100%; text-align: center;">拍照</cover-view>
  </cover-view>
</camera>
<!-- 这里的宽高一定不要忘记设置 -->
<canvas style='width:widthpx; height:heightpx; opacity: 0;' canvas-id="mycanvas"></canvas>

3、拍照

data: 
  width:0,//设备宽度
  height:0,//设备高度
  image:'',
  showPic:false
,
takePhotoAction()
   let that = this;
   let ctx = wx.createCameraContext();
   ctx.takePhoto(
     quality:'high',
     success(res)
       that.loadTempImagePath(res.tempImagePath)
     
   )
 ,
 //剪切图片
  loadTempImagePath(path)
    let that = this;
    //获取设备宽高信息
    wx.getSystemInfo(
      success: (res) => 
        that.setData(
          width:res.screenWidth,
          height:res.screenHeight
        )
        let imgX = 0.1*that.data.width;
        let imgY = 0.25*that.data.height;
        let imgWidth = 0.8*that.data.width;
        let imgHeight = 0.25*that.data.height;
        //获取图片信息
        wx.getImageInfo(
          src: path,
          success(res)
            const canvas = wx.createCanvasContext('mycanvas', that);
            //将图片放到画布上
            canvas.drawImage(path,0,0,that.data.width,that.data.height);
            //截取图片
            canvas.draw(false,setTimeout(()=>
              wx.canvasToTempFilePath(
                canvasId: 'mycanvas',
                x: 50, //画布x轴起点
                y: 200, //画布y轴起点
                width: imgWidth, //画布宽度
                height: imgHeight, //画布高度
                destWidth: imgWidth, //输出图片宽度
                destHeight: imgHeight, //输出图片高度
                success(res)
                  that.setData(
                    image:res.tempFilePath,
                    showPic:true
                  )
                
              )
            ),1000)
          
        )
      ,
    )
  

这里的重点是对截图框位置的计算,可以根据自己的需求来设计宽高;

4、由于 createCanvasContext 在 2.9.0 开始停止维护,官网推荐使用 Canvas 代替

//wxml
<camera type="2d" device-position="back" resolution="high" style="width: 100%; height: 100vh;">
  <cover-view>
  <!-- 拍照后的回显图片,必须在上面,不然不显示 -->
    <cover-image style="position: absolute; top: 0; left: 0; width: 348px; height: auto;" wx:if="showPic" src="image"></cover-image>
    <!-- 拍照蒙层,按照这个裁剪 -->
    <cover-image src="http://print.jiaynet.cn/icons/zhezhao.png"></cover-image>
  </cover-view>
  <!-- 拍照按钮 -->
  <cover-view style="color: #fff; width: 100%; position: fixed; left:0; bottom: 40rpx;">
    <cover-view bindtap="takePhotoAction" style="width: 100%; text-align: center;">拍照</cover-view>
  </cover-view>
</camera>
<!--  -->
<canvas type="2d" id="mycanvas"></canvas>
//js
 takePhotoAction()
    let that = this;
    let ctx = wx.createCameraContext();
    ctx.takePhoto(
      quality:'high',
      success(res)
        that.setData(
          image:res.tempImagePath
        )
        wx.createSelectorQuery()
          .select('#mycanvas')
          .fields(
            node: true,
            size: true
          , (res) => 
            const canvas = res.node
            const ctx2 = canvas.getContext('2d');
            that.init(ctx2, canvas)
          )
          .exec()
      
    )
  ,
  init(ctx, canvas) 
    let img = canvas.createImage()
    img.src = this.data.image;
    img.onload = (e) => 
      let c_x = img.width * 0.05
      let c_w = img.width * 0.89
      let c_y = img.height * 0.365
      let c_h = img.height * 0.12
      //截取图片指定部分并绘制到canvas
      ctx.drawImage(img, c_x, c_y, c_w, c_h, 0, 0, 300, 300 * (c_h / c_w))//width固定为300,按比例计算出height
      //将canvas内容保存为图片
      wx.canvasToTempFilePath(
        canvas: canvas,
        // x: 50, //画布x轴起点
        // y: 280, //画布y轴起点
        width:  335, //画布宽度
        height: 216, //画布高度
        // destWidth: 335, //输出图片宽度
        // destHeight: 216, //输出图片高度
        fileType: 'png',
        success: (res) => 
          this.setData(
            image:res.tempFilePath,
            showPic:true
          )
           //将图片保存到本地相册
          wx.saveImageToPhotosAlbum(
            filePath: res.tempFilePath,
          )
        ,
        fail: (res) => 
          console.log(res)
        
      )
    
    img.onerror = (e) => 
      console.error('err:', e)
    
  

由于使用 canvas 时的 drawImage 参数比较难理解,请参考:canvas的drawImage方法参数详解

微信小程序接入百度OCR(身份证识别)

微信小程序接入百度OCR(身份证识别)

1.接口描述

支持对二代居民身份证正反面所有8个字段进行结构化识别,包括姓名、性别、民族、出生日期、住址、身份证号、签发机关、有效期限,识别准确率超过99%;同时支持身份证正面头像检测,并返回头像切片的base64编码及位置信息。

同时,支持对用户上传的身份证图片进行图像风险和质量检测,可识别图片是否为复印件或临时身份证,是否被翻拍或编辑,是否存在正反颠倒、模糊、欠曝、过曝等质量问题。

请求示例

HTTP 方法:POST

请求URL: https://aip.baidubce.com/rest/2.0/ocr/v1/idcard

URL参数:

参数
access_token 通过API Key和Secret Key获取的access_token,参考“Access Token获取

Header如下:

参数
Content-Type application/x-www-form-urlencoded

Body中放置请求参数,参数详情如下:

请求参数

参数 是否必选 类型 可选值范围 说明
image string - 图像数据,base64编码后进行urlencode,要求base64编码和urlencode后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/jpeg/png/bmp格式
id_card_side string front/back front:身份证含照片的一面;back:身份证带国徽的一面
detect_direction string true/false 是否检测图像旋转角度,默认检测,即:true。朝向是指输入图像是正常方向、逆时针旋转90/180/270度。可选值包括: - true:检测旋转角度; - false:不检测旋转角度。
detect_risk string true/false 是否开启身份证风险类型(身份证复印件、临时身份证、身份证翻拍、修改过的身份证)功能,默认不开启,即:false。可选值:true-开启;false-不开启
detect_photo string true/false 是否检测头像内容,默认不检测。可选值:true-检测头像并返回头像的 base64 编码及位置信息
detect_rectify string true/false 是否进行完整性校验,默认为true,需上传各字段内容完善的图片方可识别;如果设置为false,则对于身份证切片(如仅身份证号区域)也可识别

2.小程序端调用

需要参数access_token,存放在云数据库中,并定时刷新,不明白如何获取并定时刷新的参考文章:

[小程序开发技巧总结(三)-- 云开发时效数据刷新和存储 (access_token等)]

tips:核心部分是在本地完成base64 编码

2.1 自定义文件 profunc.js,实现函数并封装

function OcrIdCard(access_token){
  return new Promise(function(resolve,reject){
    var that = this;
    //识别身份证
    wx.chooseImage({
      count: 1,
      sizeType: [\'compressed\'],
      sourceType: [\'album\', \'camera\'],
      success: function (res) {
        console.log(res.tempFilePaths)
          //核心代码
        wx.getFileSystemManager().readFile({
          filePath: res.tempFilePaths[0],
          encoding: \'base64\', //编码格式
          success(ans) {
            // console.log(ans.data)
            wx.showLoading({ title: \'识别中\' })
            wx.request({
              url: \'https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token=\' + access_token,
              method: \'POST\',
              header: {
                \'Content-Type\': \'application/x-www-form-urlencoded\'
              },
              data: {
                image: ans.data,
                id_card_side: \'front\'
              },
              success(_res) {
                wx.hideLoading();
                resolve(_res)
                
              }, fail(_res) {
                wx.hideLoading();
                wx.showToast({
                  title: \'请求出错\',
                })
                reject(_res)
              }
            })
          }
        })
      }
    })
  })
}

module.exports = {
  OcrIdCard: OcrIdCard
}

2.2 在小程序页面引用,需要传入access_token

const cwx = require(\'profunc.js\'); //在小程序页面引入该js 文件
...
ocridcard(){
      var that = this;
      cwx.OcrIdCard(that.data.access_token).then(function(_res){
        var trdata = _res.data.words_result;
        console.log(trdata)
        that.setData({
          name: trdata[\'姓名\'].words,
          idcard: trdata[\'公民身份号码\'].words,
          userloc: trdata[\'住址\'].words
        })
    })      
}

3.效果展示

接口返回数据如下

小程序 Android Web 等开发欢迎联系 QQ 1025584691

以上是关于微信小程序实现OCR扫描识别的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序实现身份证识别-ocr

微信小程序接入百度OCR(身份证识别)

微信小程序 腾讯云ocr 身份证识别

微信小程序识别二维码跳转到应用商店

微信小程序怎么将文字变成图片

微信小程序登陆不上去怎么回事?