前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >智能结构化OCR实现个人小账本demo(基于NodeJS)

智能结构化OCR实现个人小账本demo(基于NodeJS)

原创
作者头像
治电小白菜
修改2024-12-27 09:14:26
修改2024-12-27 09:14:26
1690
举报
文章被收录于专栏:技术综合技术综合

操作场景

智能结构化(Smart Structure Optical Character Recognition )融合了业界领先的深度学习技术、图像检测技术以及 OCR 大模型能力,能够实现不限版式的结构化信息抽取。本文以NodeJS为例,实现一个基于智能结构化OCR的个人小账本demo。

示例软件版本

本文示例的软件版本及说明如下:

  • NodeJS:编程语言,本文以NodeJS v20.11.1为例。

操作步骤

步骤1:创建账户的api密钥

前往访问密钥页面,新建密钥,记录下生成的secretIdsecretKey

步骤2:创建对象存储桶

智能结构化OCR的sdk支持传入图片链接的方式和使用图片base64的方式。这里使用的是传入图片链接的方式,通过先上传图片到腾讯云对象存储,再将链接传入智能结构化OCR识别。

前往对象存储存储桶列表创建存储桶,选择公有读写,方便测试。

记录下存储桶名称和所属地域,所属地域使用英文,例如ap-chengdu。

步骤3:创建项目

1. 新建文件夹,并安装依赖
代码语言:bash
复制
mkdir tencentOcr
cd tencentOcr
npm init
npm install -S express body-parser qcloud-cos-sts tencentcloud-sdk-nodejs

依赖解读:

  • express:Express 是一种保持最低程度规模的灵活 NodeJS Web 应用程序框架。
  • body-parser:NodeJS正文解析中间件,这里用来处理post请求的json参数。
  • qcloud-cos-sts:是腾讯云对象存储的sdk。
  • tencentcloud-sdk-nodejs:腾讯云开发者工具套件,用其中的OCR功能。

项目目录如下,

  • public文件夹为前端部分
  • .env为环境变量用来存放SECRET_IDSECRET_KEYBUCKETREGION
  • index.js为项目后端,提供相关接口。

步骤4:编写后端代码

1. 首先填写好环境变量

SECRET_IDSECRET_KEYBUCKETREGION填入到.env中

代码语言:txt
复制
SECRET_ID=腾讯云secretId
SECRET_KEY=腾讯云secretKey
BUCKET=对象存储存储桶名称
REGION=对象存储地域
2. 编写代码

1)先引入依赖,并初始化express对象,配置前端页面的文件夹,以及设定启动端口。

代码语言:js
复制
import express from 'express'
import bodyParser from 'body-parser'
import TencentCloud from 'tencentcloud-sdk-nodejs'
import STS from 'qcloud-cos-sts'

const app = express()
const jsonParser = bodyParser.json()

// 静态资源
app.use(express.static('public'))

// api 根路由
const ROOT_ROUTE = '/api'

const formatResponse = (data, code = 0, message = 'success') => {
  return {
    code,
    message,
    data
  }
}

app.listen(8898, () => {
  console.log('Server is running on port 8898')
})

2)配置ocr,并编写调用ocr的接口

sdk调用SmartStructuralPro的方法描述可以在这里查看

代码中我们使用ImageUrl来传入需要识别的图片,如果是传base64,需要使用ImageBase64。可以根据具体需要获取的字段来指定ItemNames,防止获取其他无意义的数据,增加筛选成本。具体如何提升获取效果,可以前往OCR Demo中通过添加自定义字段尝试效果。

代码语言:js
复制
// ocr客户端初始化
const OcrClient = TencentCloud.ocr.v20181119.Client
const ocrClient = new OcrClient({
  credential: {
    secretId: process.env.SECRET_ID,
    secretKey: process.env.SECRET_KEY
  }
})

// ocr
app.post(`${ROOT_ROUTE}/ocr`, jsonParser, async (req, res) => {
  const { image } = req.body
  const response = await ocrClient.SmartStructuralPro({
    ImageUrl: image,
    ItemNames: ['支付时间', '金额']
  })
  res.json(formatResponse(response, 0, 'ocr成功'))
})

3)配置oss,用来给前端上传时获取存储桶和地域,以及临时授权token。

代码语言:js
复制
// 配置oss sts
const stsConfig = {
  secretId: process.env.SECRET_ID,
  secretKey: process.env.SECRET_KEY,
  durationSeconds: 1800,
  bucket: process.env.BUCKET,
  region: process.env.REGION,
  allowPrefix: 'ocr/*',
}

const stsAppId = stsConfig.bucket.substring(stsConfig.bucket.lastIndexOf('-') + 1)
const stsPolicy = {
  'version': '2.0',
  'statement': [{
    'action': [
      // 简单上传
      'name/cos:PutObject',
      'name/cos:PostObject',
      // 分片上传
      'name/cos:InitiateMultipartUpload',
      'name/cos:ListMultipartUploads',
      'name/cos:ListParts',
      'name/cos:UploadPart',
      'name/cos:CompleteMultipartUpload',
    ],
    'effect': 'allow',
    'principal': { 'qcs': ['*'] },
    'resource': [
      // cos相关授权,按需使用
      'qcs::cos:' + stsConfig.region + ':uid/' + stsAppId + ':' + stsConfig.bucket + '/' + stsConfig.allowPrefix,
      // ci相关授权,按需使用
      'qcs::ci:' + stsConfig.region + ':uid/' + stsAppId + ':bucket/' + stsConfig.bucket + '/*',
    ],
  }],
}

// 获取oss的bucket和region
app.get(`${ROOT_ROUTE}/bucket`, async (req, res) => {
  res.json(formatResponse({
    bucket: process.env.BUCKET,
    region: process.env.REGION,
  }, 0, '获取bucket和region成功'))
})

// 获取sts凭证
app.get(`${ROOT_ROUTE}/sts`, async (req, res) => {
  STS.getCredential({
    secretId: stsConfig.secretId,
    secretKey: stsConfig.secretKey,
    proxy: stsConfig.proxy,
    durationSeconds: stsConfig.durationSeconds,
    region: stsConfig.region,
    endpoint: stsConfig.endpoint,
    policy: stsPolicy,
  }, function (err, credential) {
    if (err) {
      res.json(formatResponse(err, 1, '获取sts凭证失败'))
    } else {
      res.json(formatResponse(credential, 0, '获取sts凭证成功'))
    }
  })
})

步骤5:编写前端代码

1. 编写前端oss操作代码

先下载前端的oss sdk,放入public文件夹中。

编写代码public/oss.js

代码语言:js
复制
let bucket = ''
let region = ''
// 创建COS对象
const cos = new COS({
    // 使用临时授权,则需要使用这个属性
  getAuthorization: async function (options, callback) {
    // 调用接口 获取临时授权token
    const res = await fetch('/api/sts').then(res => res.json())
    const data = res.data
    console.log(data)
    if (!data || !data.credentials) {
      return console.error('credentials invalid:\n', data)
    }
    const { credentials } = data
    callback({
      TmpSecretId: credentials.tmpSecretId,
      TmpSecretKey: credentials.tmpSecretKey,
      SecurityToken: credentials.sessionToken,
      // 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
      StartTime: data.startTime, // 时间戳,单位秒,如:1580000000
      ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000
      ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
    })
  }
})

// 生成guid,用于文件名生成,防止重复文件冲突
const guid = () => {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8)
    return v.toString(16)
  })
}

let taskId;
// 上传文件,file为选择的文件
async function upload(file, onProgress, onSuccess, onError) {
  // 获取文件后缀
  const ext = file.name.split('.').pop()
  try {
    const bucketRes = await fetch('/api/bucket').then(res => res.json())
    const { bucket, region } = bucketRes.data
    const data = await cos.uploadFile({
      Bucket: bucket, // 填写自己的 bucket,必须字段
      Region: region,     // 存储桶所在地域,必须字段
      Key: 'ocr/' + guid() + '.' + ext,            // 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段
      Body: file, // 上传文件对象
      SliceSize: 1024 * 1024 * 5,  // 触发分块上传的阈值,超过5MB 使用分块上传,小于5MB使用简单上传。可自行设置,非必须
      onProgress: function(progressData) {
        onProgress && onProgress(progressData)
      },
      onTaskReady: function(id) { // 非必须
        taskId = id;
      },
    })
    onSuccess && onSuccess(data)
  } catch (e) {
    onError && onError(e)
  }
}
2. 编写页面逻辑代码

public/index.html代码如下,

代码语言:html
复制
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./cos-js-sdk-v5.min.js"></script>
  <script src="./cos.js"></script>
  <style>
    .table {
      width: 100%;
      border-collapse: collapse;
    }
    .table th, .table td {
      border: 1px solid #000;
      padding: 8px;
    }
  </style>
</head>
<body>
  <div>
    <input type="file" id="file" accept="image/*">
    <button id="upload">识别</button>
  </div>
  <table class="table">
    <thead>
      <tr>
        <th>时间</th>
        <th>金额</th>
      </tr>
    </thead>
    <tbody id="tbody">
    </tbody>
  </table>
  <script>
    const file = document.getElementById('file')
    const uploadBtn = document.getElementById('upload')
    // 上传按钮逻辑
    uploadBtn.addEventListener('click', () => {
      // 进行文件上传
      upload(file.files[0], (progressData) => {
        console.log('上传进度:', progressData)
      }, async (data) => {
        console.log('上传成功:', data)
        // 上传成功后,将图片链接传给ocr识别
        const result = await fetch('/api/ocr', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            image: 'https://' + data.Location
          })
        }).then(res => res.json())
        // 解析返回的数据
        const extractedData = result.data.StructuralList.flatMap(group => 
          group.Groups.flatMap(groupItem => 
            groupItem.Lines.map(line => ({
              AutoName: line.Key.AutoName,
              AutoContent: line.Value.AutoContent
            }))
          )
        )
        // 渲染到表格中
        const tbody = document.getElementById('tbody')
        // 需要两个autoceontent在一行
        const rows = extractedData.map(item => `<td>${item.AutoContent}</td>`)
        tbody.innerHTML = tbody.innerHTML + `<tr>${rows.join('')}</tr>`
        console.log('识别结果:', extractedData)
      }, (e) => {
        console.log('上传失败:', e)
      })
    })
  </script>
</body>
</html>
3. 页面结果展示

上传的示例图片

相关操作

可以后续将识别的数据存入CloudBase 云数据库或者腾讯云数据库服务或者自己搭建的数据库服务中。

相关问题

如果您在使用智能结构化OCR或者对象存储的过程中遇到问题,可参考以下文档并结合实际情况分析并解决问题:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 操作场景
  • 示例软件版本
  • 操作步骤
    • 步骤1:创建账户的api密钥
    • 步骤2:创建对象存储桶
    • 步骤3:创建项目
      • 1. 新建文件夹,并安装依赖
    • 步骤4:编写后端代码
      • 1. 首先填写好环境变量
      • 2. 编写代码
    • 步骤5:编写前端代码
      • 1. 编写前端oss操作代码
      • 2. 编写页面逻辑代码
      • 3. 页面结果展示
  • 相关操作
  • 相关问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档