腾讯云ocr识别

腾讯云ocr识别笔记

安装扩展包

laravel使用

1
composer require godruoyi/laravel-ocr

原始方式

1
2
3
4
# >= 8.1
composer require "godruoyi/ocr:^3.0"
# >= 7.2
composer require "godruoyi/ocr:^2.1"

配置文件(laravel举例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php

/*
* This file is part of the godruoyi/ocr.
*
* (c) Godruoyi <gmail@godruoyi.com>
*
* This source file is subject to the MIT license that is bundled.
*/

return [

/*
|--------------------------------------------------------------------------
| Default client
|--------------------------------------------------------------------------
|
| 指定一个默认的 client 名称,其值需要在下列的 drivers 数组中配置。
|
*/
'default' => env('OCR_DEFAULT_CLIENT', 'tencent'),

/*
|--------------------------------------------------------------------------
| Client 配置
|--------------------------------------------------------------------------
|
| Client 配置信息,包括基本密钥等;注意目前 aliyun 暂只支持 appcode 方式。
|
*/
'drivers' => [
'aliyun' => [
'appcode' => '',
'secret_id' => '',
'secret_key' => '',
],

'baidu' => [
'access_key' => '',
'secret_key' => '',
],

'tencent' => [
'secret_id' => env('SECRET_ID', ''),
'secret_key' => env('SECRET_KEY', ''),
],
],

/*
|--------------------------------------------------------------------------
| Cache 配置
|--------------------------------------------------------------------------
|
| Baidu OCR 需要缓存来保存 AccessToken,默认我们使用 Symfony Cache 组件的 FilesystemAdapter
| 你可以在这里设置为使用 Laravel 的缓存组件。
|
*/
'laravel_cache' => true,

];

其中在.env中增加 SECRET_ID = xxxxxxxxxxxxxxSECRET_KEY = xxxxxxxxxxxxxx数据,这个id和key来源于腾讯云,试用次数1000次机会,足够使用了。

表格识别

调用初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @param $filePath [图片地址]
* @throws BusinessException
*/
public function handle($filePath, $type = 1)
{
$options = [
'Region' => 'ap-shanghai'
];

$response = OCR::tencent()->recognizeTable($filePath, $options);
$res = $response->toArray();
$res = $res['Response'];
if (isset($res['Error'])) {
Log::channel('ocr')->error('识别图片:' . $filePath . ' 错误结果:', $res['Error']);
throw new BusinessException(CodeResponse::OCR_ERR);
}
// dd($res);
$data = $res['TableDetections'][0];
Log::channel('ocr')->info('识别图片:' . $filePath . ' 识别结果:', $data);
return $this->format($data);
}

通用解析表格数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* 通用识别处理
* @throws BusinessException
*/
public function format($data)
{
$table = [];
$res = [];
$items = $data['Cells'];
foreach ($items as $item) {
$table[$item['RowTl']][$item['ColTl']] = $item['Text'];
}
$data = [
'sn' => ['序号', 'ID'],
'code' => ['编码', '商品编码', '零件编码', '零件号', '配件编码', '零件编号'],
'name' => ['零件名称', '名称', '零件中文名', '配件名称'],
'num' => ['出库数', '数量'],
'quality' => ['产地', '配件品质', '品质'],
'unit' => ['单位'],
'price' => ['售价', '单价'],
'total_price' => ['金额', '价格', '销售价', '配件价格'],
];
$init = 0;//表格数据(除了表头)第一条row索引
if (!isset($table[0])) {
throw new BusinessException(CodeResponse::OCR_ERR);
} else {
$arr = array_values($table[0]);
$intersection = array_intersect($arr, $data['name']);
$user = Auth::guard('supplier')->user();
if (!empty($intersection)) {
$header = $table[0];
// 遍历表格数据(除了表头)
$init = 1;
$keys = array_keys($table[1]);
$user->table_headers = json_encode($header);
$user->save();
} else {
//获取存储的表头
$header = json_decode($user->table_headers);
if (!$header) {
Log::channel('ocr')->error('表头缺失');
throw new BusinessException(CodeResponse::OCR_ERR);
}
// 遍历表格数据(除了表头)
$keys = array_keys($table[0]);
}
}
$initData = array_fill_keys(array_keys($data), ''); // 初始化空的行数据
for ($i = $init; $i < count($table); $i++) {
$row = $table[$i];
$rowData = [];
// 遍历当前行的每个格子
foreach ($keys as $key) {
if (isset($header[$key])) {
$cellData = $row[$key] ?? '';
// 检查当前表头文字是否在 $row 数组中的值中
foreach ($data as $k => $v) {
$header_k = str_replace(' ', '', $header[$key]);
if (in_array($header_k, $v)) {
$rowData[$k] = $cellData;
break;
}
}
}
}
$rowData = array_merge($initData, $rowData);
if ($rowData['name']) {
//数据格式化
$rowData['sn'] = empty($rowData['sn']) ? count($res) + 1 : $rowData['sn'];
$rowData["code"] = str_replace(' ', '', $rowData["code"]);
$rowData["total_price"] = preg_replace("/[^0-9.]+/", "", $rowData["total_price"]);
$res[] = $rowData;
}
}
return $res;
}

Tips:

  1. 上传解析的图片需要有表格线,且单元格没有合并的情况
  2. 当表头说明文字叫法不同,但是都有一一对应的字段,可以通过$data数组进行配置
  3. 当上传第一张图片(有表头)解析成功后,会存储表头到此用户表的table_headers字段,后续没有表头只有数据也会同步使用此表头进行解析
  4. 数据格式化可针对行数据是否有name字段来判断是否需要此条数据,并二次加工数据

图片示例

特别技巧:

  • 在前端上传图片后可以进行多张图片数据通过指定key进行合并数据
1
2
3
4
5
6
7
8
9
10
11
12
//更新表格数据
let oldArr = dataSource.value;
let appendArr = res.data
appendArr.forEach((item) => {
const index = oldArr.findIndex((oldItem) => oldItem.sn.toString() === item.sn.toString());
if (index !== -1) {
oldArr[index] = item;
} else {
oldArr.push(item);
}
});
dataSource.value = oldArr;