OCR(Optical Character Recognition,光学字符识别)识别认证功能

1,131 阅读9分钟

思路步骤

1. 用户界面设计

  • 摄像头访问:使用 navigator.mediaDevices.getUserMedia() 方法获取摄像头权限,展示实时视频流。
  • 指示框:在视频流上覆盖一个指示框,引导用户正确放置身份证。
  • 拍照按钮:提供一个按钮让用户在合适的时机拍摄身份证照片。

2. 拍照与图像预处理

  • 截图:当用户点击拍照按钮时,从视频流中捕获一帧图像。
  • 裁剪:根据之前的指示框位置裁剪出身份证区域。
  • 优化:对图像进行必要的优化,如灰度化、二值化等,提高识别率。

3. 图片上传与 OCR 识别

  • 上传图片:将处理好的图片上传至服务器或发送给 OCR API。
  • 调用 OCR API:使用 API 密钥调用 OCR 服务。
  • 接收结果:接收 API 返回的识别结果。

4. 展示结果

  • 解析结果:将接收到的 JSON 格式的 OCR 结果解析为可用的数据。
  • 显示信息:将身份证上的信息展示给用户查看。

5. 错误处理

  • 错误反馈:如果 OCR 识别失败,需要给出相应的提示。
  • 重试机制:提供重试机会,让用户重新拍照或手动输入信息。

代码实现

1. 用户界面设计

首先,我们需要设计一个简洁直观的用户界面,包括摄像头访问、指示框和拍照按钮。


<template>
  <div id="scanner">
    <!-- 显示摄像头画面 -->
    <video ref="video" autoplay muted playsinline></video>
    <!-- 指示框 -->
    <div class="overlay" v-if="showOverlay">
      <div class="frame"></div>
    </div>
    <!-- 拍照按钮 -->
    <button @click="capture">拍照</button>
    <!-- 识别结果 -->
    <div v-if="result">
      <h2>识别结果:</h2>
      <p>ID: {{ result.id }}</p>
      <p>Name: {{ result.name }}</p>
      <p>Address: {{ result.address }}</p>
    </div>
  </div>
</template>

2. 数据和生命周期钩子

接下来,我们定义组件的数据和生命周期钩子来初始化摄像头。

</script>
    export default {
          data() {
            return {
              // 视频流对象
              stream: null,
              // 控制指示框是否显示
              showOverlay: false,
              // 保存 OCR 识别结果
              result: null,
            };
          },、
          
          mounted() {
            // 初始化摄像头,在组件挂载后执行
            this.initCamera();
          },
          
          methods: {
            // 初始化摄像头
            async initCamera() {
              try {
                // 请求摄像头权限
                const stream = await navigator.mediaDevices.getUserMedia({ video: true });
                this.stream = stream; // 保存视频流
                // 设置视频流到 video 元素
                this.$refs.video.srcObject = stream;
                // 等待一段时间后再显示指示框
                setTimeout(() => {
                  this.showOverlay = true;
                }, 1000);
              } catch (error) {
                console.error('Error accessing camera:', error);
              }
            },
    };
    </script>

3. 拍照与图像预处理

接下来,我们需要实现截图、裁剪和图像优化的功能。


// 添加到 methods 中
    // 拍照方法
    async capture() {
          // 创建一个画布
          const canvas = document.createElement('canvas');
          canvas.width = this.$refs.video.videoWidth;
          canvas.height = this.$refs.video.videoHeight;
          const ctx = canvas.getContext('2d');

          // 把当前视频帧绘制到画布上
          ctx.drawImage(this.$refs.video, 0, 0, canvas.width, canvas.height);

          // 裁剪身份证区域
          const croppedCanvas = document.createElement('canvas');
          croppedCanvas.width = 200;
          croppedCanvas.height = 150;
          const croppedCtx = croppedCanvas.getContext('2d');

          // 裁剪并绘制到新的画布
          croppedCtx.drawImage(canvas, 100, 100, 200, 150, 0, 0, 200, 150);

          // 对图像进行灰度化和二值化处理
          const imageData = croppedCanvas.toDataURL();
          const processedImage = await this.processImage(imageData);

          // 调用 OCR API
          const result = await this.recognizeIdCard(processedImage);
          if (result) {

            // 解析并显示 OCR 识别结果
            this.parseResult(result);
          }
    },
    
    // 处理图像的方法
    async processImage(imageData) {
         // 使用 html2canvas 或其他库将图像进行灰度化和二值化处理
          const processedImage = await html2canvas(croppedCanvas).then((canvas) => canvas.toDataURL());
          return processedImage;
    },

4. 图片上传与 OCR 识别

这里我们将实现图片上传和调用 OCR API 的功能。

// 添加到 methods 中
    // 调用 OCR API
    async recognizeIdCard(imageData) {
          try {
                // 发送 POST 请求到后端 API
                const response = await axios.post('/api/ocr/idcard', { image: imageData }, {
                  headers: { 'Content-Type': 'application/json' },
            });
                return response.data;
          } catch (error) {
                console.error('Error in OCR:', error);
          }
    },

5. 展示结果

最后,我们需要解析并显示 OCR 识别的结果。


// 添加到 methods 中
     // 解析 OCR 识别结果
        parseResult(result) {
              this.result = {
                    id: result.id,
                    name: result.name,
                    address: result.address,
              };
        },

6. 错误处理

添加错误处理和重试机制。


1// 添加到 methods 中
    // 错误处理
    handleError(error) {
          alert('OCR 识别失败,请重新尝试!');
          this.result = null;
    },
    
    // 重试拍照
    retryCapture() {
          this.capture();
    },

7. 样式

最后,我们还需要定义一些基本的样式来美化用户界面。

<style scoped>
#scanner {
  /* 设置扫描区域的样式 */
  position: relative;
  width: 100%;
  height: 300px;
  margin-bottom: 20px;
}

video {
  /* 设置视频元素的样式 */
  width: 100%;
  height: 100%;
}

.overlay {
  /* 设置指示框的样式 */
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.frame {
  /* 设置指示框内部边框的样式 */
  border: 2px dashed #f00;
  width: 200px;
  height: 150px;
}

button {
  /* 设置按钮的样式 */
  display: block;
  margin: 20px auto;
}
</style>

完整的组件代码

现在我们可以将所有部分整合在一起,形成一个完整的 Vue 2 组件。

```html
<template>
  <div id="scanner">
  
    <!-- 显示摄像头画面 -->
    <video ref="video" autoplay muted playsinline></video>
    
    <!-- 指示框 -->
    <div class="overlay" v-if="showOverlay">
      <div class="frame"></div>
    </div>
    
    <!-- 拍照按钮 -->
    <button @click="capture">拍照</button>
    
    <!-- 识别结果 -->
    <div v-if="result">
    
      <h2>识别结果:</h2>
      <p>ID: {{ result.id }}</p>
      <p>Name: {{ result.name }}</p>
      <p>Address: {{ result.address }}</p>
    </div>
  </div>
</template>

<script>
import axios from 'axios';
import html2canvas from 'html2canvas'; // 用于截图和图像处理

export default {
  data() {
    return {
    
      // 视频流对象
      stream: null,
      
      // 控制指示框是否显示
      showOverlay: false,
      
      // 保存 OCR 识别结果
      result: null,
    };
  },
  mounted() {
  
    // 初始化摄像头,在组件挂载后执行
    this.initCamera();
  },
  methods: {
  
    // 初始化摄像头
    async initCamera() {
      try {
      
        // 请求摄像头权限
        const stream = await navigator.mediaDevices.getUserMedia({ video: true });
        this.stream = stream; // 保存视频流
        
        // 设置视频流到 video 元素
        this.$refs.video.srcObject = stream;
        
        // 等待一段时间后再显示指示框
        setTimeout(() => {
          this.showOverlay = true;
        }, 1000);
      } catch (error) {
        console.error('Error accessing camera:', error);
      }
    },
    // 拍照方法
    async capture() {
      // 创建一个画布
      const canvas = document.createElement('canvas');
      canvas.width = this.$refs.video.videoWidth;
      canvas.height = this.$refs.video.videoHeight;
      const ctx = canvas.getContext('2d');
      
      // 把当前视频帧绘制到画布上
      ctx.drawImage(this.$refs.video, 0, 0, canvas.width, canvas.height);

      // 裁剪身份证区域
      const croppedCanvas = document.createElement('canvas');
      croppedCanvas.width = 200;
      croppedCanvas.height = 150;
      const croppedCtx = croppedCanvas.getContext('2d');
      
      // 裁剪并绘制到新的画布
      croppedCtx.drawImage(canvas, 100, 100, 200, 150, 0, 0, 200, 150);

      // 对图像进行灰度化和二值化处理
      const imageData = croppedCanvas.toDataURL();
      const processedImage = await this.processImage(imageData);

      // 调用 OCR API
      const result = await this.recognizeIdCard(processedImage);
      if (result) {
      
        // 解析并显示 OCR 识别结果
        this.parseResult(result);
      }
    },
    
    // 处理图像的方法
    async processImage(imageData) {
      // 使用 html2canvas 或其他库将图像进行灰度化和二值化处理
      const processedImage = await html2canvas(croppedCanvas).then((canvas) => canvas.toDataURL());
      return processedImage;
    },
    
    // 调用 OCR API
    async recognizeIdCard(imageData) {
      try {
      
        // 发送 POST 请求到后端 API
        const response = await axios.post('/api/ocr/idcard', { image: imageData }, {
          headers: { 'Content-Type': 'application/json' },
        });
        return response.data;
      } catch (error) {
        console.error('Error in OCR:', error);
      }
    },
    
    // 解析 OCR 识别结果
    parseResult(result) {
      this.result = {
        id: result.id,
        name: result.name,
        address: result.address,
      };
    },
    
    // 错误处理
    handleError(error) {
      alert('OCR 识别失败,请重新尝试!');
      this.result = null;
    },
    
    // 重试拍照
    retryCapture() {
      this.capture();
    },
  },
};
</script>

<style scoped>
#scanner {

  /* 设置扫描区域的样式 */
  position: relative;
  width: 100%;
  height: 300px;
  margin-bottom: 20px;
}

video {
  /* 设置视频元素的样式 */
  width: 100%;
  height: 100%;
}

.overlay {

  /* 设置指示框的样式 */
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.frame {
  /* 设置指示框内部边框的样式 */
  border: 2px dashed #f00;
  width: 200px;
  height: 150px;
}

button {
  /* 设置按钮的样式 */
  display: block;
  margin: 20px auto;
}
</style>

代码解释

HTML (模板)

  1. 容器 (<div id="scanner">) :

    • 该 <div> 是整个组件的容器,包含所有与身份证扫描相关的元素。
    • 使用 id="scanner" 便于 CSS 选择器定位。
    • 设置 position: relative 以允许其子元素使用绝对定位。
  2. 视频 (<video ref="video" autoplay muted playsinline>) :

    • <video> 元素用于显示摄像头捕捉的画面。
    • ref="video" 通过 Vue 的 ref 属性将 <video> 元素绑定到 Vue 实例中,便于访问和操作 DOM 元素。
    • autoplay 属性使视频自动播放。
    • muted 属性设置视频静音模式,避免干扰用户。
    • playsinline 属性确保视频在移动端也能正常播放。
  3. 指示框 (<div class="overlay">) :

    • <div class="overlay"> 用于显示一个指示框,帮助用户正确放置身份证。
    • v-if="showOverlay" 控制指示框的显示与否。
    • 内部的 <div class="frame"> 作为指示框的具体样式,用来引导用户将身份证置于框内。
  4. 拍照按钮 (<button @click="capture">拍照</button>) :

    • <button> 用于触发拍照行为。
    • @click="capture" 绑定点击事件,触发 capture 方法。
  5. 识别结果 (<div v-if="result">) :

    • <div> 用于显示 OCR 识别结果。
    • v-if="result" 控制结果的显示与否。
    • <h2> 和 <p> 元素分别用于显示标题和具体的识别结果。

JavaScript (脚本)

  • 导入了 axios 和 html2canvas 库,分别用于 HTTP 请求和屏幕截图功能。
  1. 组件数据 (data()) :

    • stream: null: 用于存储视频流对象,方便控制视频的播放和停止。
    • showOverlay: false: 控制指示框的显示与隐藏。
    • result: null: 用于存储 OCR 识别的结果。
  2. 组件生命周期钩子 (mounted()) :

    • mounted 钩子在组件挂载到 DOM 后执行。
    • 在组件挂载后立即调用 initCamera() 方法初始化摄像头。
  3. 初始化摄像头 (initCamera()) :

    • 使用 navigator.mediaDevices.getUserMedia 获取用户的摄像头流。
    • 将获取的流绑定到 <video> 元素的 srcObject 属性,使视频画面可见。
    • 使用 setTimeout 延迟一秒后显示指示框,给用户时间调整姿势。
  4. 拍照 (capture()) :

    • initCamera: 初始化摄像头,获取视频流,并将其设置到 <video> 元素中。

    • 使用 navigator.mediaDevices.getUserMedia 获取用户的摄像头流。

      • 设置 srcObject 属性以显示视频流。
      • 使用 setTimeout 延迟一秒后显示指示框。
    • capture: 捕获视频帧,裁剪身份证区域,并调用 OCR API。

      • 创建 canvas 元素,并使用 drawImage 方法将视频帧绘制到画布上。
      • 创建另一个 canvas 元素,用于裁剪身份证区域。
      • 使用 toDataURL 方法将裁剪后的图像转换为 Data URL。
      • 调用 processImage 方法对图像进行预处理。
      • 调用 recognizeIdCard 方法进行 OCR 识别。
      • 如果识别成功,则调用 parseResult 方法解析结果。
  5. 图像预处理 (processImage()) :

    • 使用 html2canvas 库处理图像,通常用于灰度化、二值化等预处理步骤。

      • 使用 html2canvas 库处理图像。
      • 返回处理后的图像 Data URL。
  6. OCR 识别 (recognizeIdCard()) :

    • 使用 axios 发送 POST 请求到后端的 OCR 识别接口。
    • 设置请求头 Content-Type 为 application/json
    • 接收并返回后端返回的 OCR 识别结果。
  7. 解析识别结果 (parseResult()) :

    • 根据后端返回的 JSON 数据更新 result 数据属性。
    • 更新界面以显示识别结果。
  8. 错误处理 (handleError()) :

    • 显示错误提示信息。
    • 清除已有的识别结果。
  9. 重试拍照 (retryCapture()) :

    • 重新调用 capture 方法以允许用户再次拍照。

CSS (样式)

  1. 扫描区域样式 (#scanner) :

    • 设置扫描区域的宽度和高度。
      • position: relative: 相对定位,用于绝对定位子元素。
      • width: 100% 和 height: 300px: 设置扫描区域的宽度和高度。
      • margin-bottom: 20px: 添加底部边距。
  2. 视频样式 (video) :

    • 设置视频的宽度和高度以填充扫描区域。
      • width: 100% 和 height: 100%: 使视频填满扫描区域。
  3. 指示框样式 (.overlay) :

    • 使用绝对定位,使其覆盖整个扫描区域。
    • 使用 Flexbox 居中内部元素。
      • position: absolute: 绝对定位,相对于父元素 #scanner
      • top: 0left: 0width: 100%height: 100%: 使指示框覆盖整个扫描区域。
      • display: flexalign-items: centerjustify-content: center: 居中显示内部元素。
  4. 指示框具体样式 (.frame) :

    • 设置指示框的宽度、高度和边框样式。
      • border: 2px dashed #f00: 设置红色虚线边框。
      • width: 200px 和 height: 150px: 设置指示框的尺寸。
  5. 拍照按钮样式 (button) :

    • 设置按钮的样式,包括显示方式和边距。
      • display: block: 设置按钮为块级元素。
      • margin: 20px auto: 设置上下边距为 20px,水平居中。

注意事项

  • 确保你已经安装了 axios 和 html2canvas 这两个 npm 包。可以通过运行 npm install axios html2canvas 来安装。
  • 需要配置好后端接口 /api/ocr/idcard 以接收图像数据并返回 OCR 识别结果。