python利用百度OCR图片转文字

最近在研究图片文字识别,特别是表格的识别,印象中百度的OCR表格识别比较给力。github搜了一圈,居然没有人做开箱即用版的(貌似百度不咋受开源社区待见)。nodejs几乎不存在,python的倒是找到一些简单脚本。以下记录找到的有用信息:

  1. Bob,一款 Mac 端翻译软件,支持划词翻译、截图翻译以及手动输入翻译。github star近4k,维护积极,文档完善,如果用mac系统,Bob就是终极选项。另外从它的文档中还得知一个噩耗:百度从5月24日起免费OCR额度大幅缩水,后悔当初没有实名制啊

  2. zyb5086zyb/baidu_ocr_test 除了OCR还有一些图片前处理脚本,比如降噪之类的,有点用处

  3. chenguanyou/BaiduTextApi 总算找到一个封装好的python库,但是文档过于简单,懒得研究

  4. wadelucky/baidu_table_ocr 比较简陋,需要的库也不多,可以跑通,但异步获取结果的时间间隔居然是写死的,没有轮询,放弃

  5. uestcmee/PiecemealScripts 这个就靠谱多了,但还是有很多bug,于是动手改造,有了下面的成果

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# encoding:utf-8
# pip3 install requests pillow
import io
import os
import sys
import requests
import base64
import time

import tkinter as tk
from tkinter import filedialog

from PIL import ImageGrab

requests.adapters.DEFAULT_RETRIES = 2
basic_url = 'https://aip.baidubce.com/'
def get_token():
# client_id 为官网获取的AK, client_secret 为官网获取的SK
host = basic_url+'oauth/2.0/token?grant_type=client_credentials&client_id=xxx&client_secret=xxx'
try:
with open(sys.path[0]+"/token") as t:
return t.read()
except:
print("token不存在!准备获取..")
try:
response = requests.get(host)
aTok = response.json()['access_token']
with open(sys.path[0]+"/token", "w") as code:
code.write(aTok)
except:
input('无法获取或保存token!Ctrl+C取消重试')
return get_token()
else:
print('获取token完成!')
return aTok

def send_img():
'''
表格文字识别(异步接口)
'''
request_url = basic_url+"rest/2.0/solution/v1/form_ocr/request"
request_url = request_url + "?access_token=" + token
headers = {'content-type': 'application/x-www-form-urlencoded'}
print('发送图片中..')
try:
response = requests.post(request_url, data=imgdata, headers=headers, timeout=(5, TOUT))
print(response.text)
requestId = response.json()['result'][0]['request_id']
print('图片上传完成!')
return(requestId)
except:
print(sys.exc_info())
input('上传失败,请尝试直接从网页复制图片! Ctrl+C取消重试')
return send_img()


def fetch_result(request_id):
'''
表格文字识别(异步接口)
'''
request_url = basic_url+"rest/2.0/solution/v1/form_ocr/get_request_result"
params = {"request_id": request_id,'result_type':'excel'} # 可使用json或excel
request_url = request_url + "?access_token=" + token
headers = {'content-type': 'application/x-www-form-urlencoded'}
counts = 1
tags=['—','\\','|','/']
while (counts<=30):
try:
counts+=1
response = requests.post(request_url, data=params, headers=headers)
percent = int(response.json()['result']['percent'])
sys.stdout.write("\r识别中[%s]%3d%%|%s| %s/100" %(tags[(percent-1)%4], percent, "█"*(percent//2), percent))
sys.stdout.flush()
if percent == 100:
break
except:
pass
time.sleep(2)
print()
try:
result_data=response.json()['result']['result_data']
except:
input('无法获取识别状态!Ctrl+C取消重试')
return fetch_result(request_id)
else:
print('识别完成->\033[4m{0}\033[0m'.format(result_data))
return(result_data)

def download_xls(url):
'''
表格文字识别(异步接口)
'''
print('开始下载xls..')
try:
r = requests.get(url)
root = tk.Tk()
root.withdraw()
with filedialog.asksaveasfile(mode='wb', defaultextension=".xls", filetypes=[('Excel','.xls')]) as f:
if f:
f.write(r.content)
print('表格下载完成!')
except:
input('下载失败!Ctrl+C取消重试')
return download_xls(url)
return 0
def accurate_basic():
'''
文字精确识别(同步接口)
'''
request_url = basic_url+"rest/2.0/ocr/v1/accurate_basic"
access_token = token
request_url = request_url + "?access_token=" + access_token
headers = {'content-type': 'application/x-www-form-urlencoded'}
try:
response = requests.post(request_url, data=imgdata, headers=headers)
print('识别完成!\n==========')
retString = ''
for words in response.json()['words_result']:
retString=retString+words['words']+'\n'
return retString #response.text
except:
input('识别错误!Ctrl+C取消重试')
return accurate_basic()

def parse_img():
global imgdata
str2 = "表格" if ISTBL else "文本"
if IMG == '':
try:
clipimg = ImageGrab.grabclipboard()
img_bytes = io.BytesIO()
clipimg.save(img_bytes, format='PNG')
imgdata = {"image": base64.b64encode(img_bytes.getvalue())} #.decode('ascii')
return '剪切板', str2
except:
input('从剪切板获取失败!Ctrl+C取消重试')
return parse_img()
elif IMG == 'f':
root = tk.Tk()
root.withdraw()
try:
with filedialog.askopenfile(mode="rb", filetypes=[('image files',('.png','.jpg','.jpeg','.bmp'))]) as f:
if f:
imgdata = {"image": base64.b64encode(f.read())}
return f.name, str2
except:
input('选择图片文件失败!Ctrl+C取消重试')
return parse_img()
elif IMG.startswith('http'):
if ISTBL:
try:
resp = requests.get(IMG)
imgdata = {"image": base64.b64encode(resp.content)}
except:
input('图片下载失败!Ctrl+C取消重试')
return parse_img()
else:
imgdata = {"url": IMG}
return '\033[4m{0}\033[0m'.format(IMG), str2

TOUT=20
imgdata = {}
os.system("color")
token=get_token() # 获取token
while 1:
IMG=input('输入链接(留空=剪切板;f=文件选择;q=退出):')
if IMG == 'q':
break
ISTBL=input("表格请输入文字再回车:")
imgsrc, imgtype = parse_img()
input('从 \033[4m{0}\033[0m 识别 \033[4m{1}\033[0m,回车确认上传..'.format(imgsrc, imgtype))
if ISTBL:
request_id=send_img() # 上传图片,获取request_id
url=fetch_result(request_id) # 获取结果url
download_xls(url) # 下载结果url
else:
print(accurate_basic())

代码做了很多容错处理,并且在出错时(基本是网络原因)可以直接重试。token可以保存到本地(30天有效期),这样就节省了一次网络请求。这个小工具基本实现了精确识别和表格识别两大功能,其中精确识别可以用图片url作为输入参数。另外,超时时间也可以自定义,防止一些比较大的图片上传失败。

后记

其实还有一些别的想法,比如直接读取剪切板,批量处理等等。先挖个坑,以后如果需求很大再填上。还有一个比较关键的是图片的旋转矫正,目前没有找到比较好的解决方案(手机上的app比较多),也留个坑吧(貌似PPT可以做到,没证实)

更新

6.10 经过一天的奋战,把脚本优化了下,重新梳理了逻辑,而且现在可以读取剪切板里的图像了
6.11
刚在Mac上试了下,丝般顺滑~

参考

百度智能云通用 OCR 免费额度调整!(new) (gitee.io)

文字识别OCR (baidu.com)

zyb5086zyb/baidu_ocr_test: 采用百度接口做OCR标注 (github.com)

wadelucky/baidu_table_ocr: An example of how to use baidu table ocr. (github.com)

chenguanyou/BaiduTextApi: 百度文字识别Api封装,在之前的基础上重写了下,更易于管理与使用! (github.com)

PiecemealScripts/main.py at master · uestcmee/PiecemealScripts (github.com)