智能结构化(Smart Structure Optical Character Recognition )融合了业界领先的深度学习技术、图像检测技术以及 OCR 大模型能力,能够实现不限版式的结构化信息抽取。本文以NodeJS为例,实现一个基于智能结构化OCR的个人小账本demo。
本文示例的软件版本及说明如下:
前往访问密钥页面,新建密钥,记录下生成的secretId
和secretKey
。
智能结构化OCR的sdk支持传入图片链接的方式和使用图片base64的方式。这里使用的是传入图片链接的方式,通过先上传图片到腾讯云对象存储,再将链接传入智能结构化OCR识别。
前往对象存储的存储桶列表创建存储桶,选择公有读写,方便测试。
记录下存储桶名称和所属地域,所属地域使用英文,例如ap-chengdu。
mkdir tencentOcr
cd tencentOcr
npm init
npm install -S express body-parser qcloud-cos-sts tencentcloud-sdk-nodejs
依赖解读:
项目目录如下,
public
文件夹为前端部分.env
为环境变量用来存放SECRET_ID
,SECRET_KEY
,BUCKET
,REGION
index.js
为项目后端,提供相关接口。将
SECRET_ID
,SECRET_KEY
,BUCKET
,REGION
填入到.env中
SECRET_ID=腾讯云secretId
SECRET_KEY=腾讯云secretKey
BUCKET=对象存储存储桶名称
REGION=对象存储地域
1)先引入依赖,并初始化express对象,配置前端页面的文件夹,以及设定启动端口。
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中通过添加自定义字段尝试效果。
// 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。
// 配置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凭证成功'))
}
})
})
先下载前端的oss sdk,放入public
文件夹中。
编写代码public/oss.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)
}
}
public/index.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>
上传的示例图片
可以后续将识别的数据存入CloudBase 云数据库或者腾讯云数据库服务或者自己搭建的数据库服务中。
如果您在使用智能结构化OCR或者对象存储的过程中遇到问题,可参考以下文档并结合实际情况分析并解决问题:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。