文本识别抛弃pytesser,直接使用Tesseract(转)
文本识别抛弃pytesser,直接使用Tesseract(转)
原文地址:https://www.polarxiong.com/archives/python-3-http-cookie.html
Update
本文最初写于2015年5月,最近Tesseract推出了3.05版,加入了一些新的特性;且原文存在一些纰漏,现重新编写。
PyTesser
PyTesser在Python Package Index中的版本仍为最初的2007年的0.0.1版,怀疑是不是已经不再维护。PyTesser似乎仅仅是在Tesseract的可执行程序tesseract.exe基础上写了一个面向Python的接口,就是通过shell执行tesseract命令获取返回值。
对于Tesseract这种C++编写的库采用可执行文件方式通过shell来建立库和Python的通信似乎无可厚非,但PyTesser在这里就犯了几个致命的错误:
- 直接集成tesseract.exe,导致x64不兼容,Linux不兼容等问题;
- Tesseract版本过老且不可手动替换文件升级,其Tesseract版本为2007年之前版本;
- PyTesser提供的接口代码效率不高(当时Tesseract支持度不高的原因)。
总的来说,除非PyTesser作出升级,否则PyTesser是基本没有价值的,不推荐使用。
关于Tesseract
Tesseract是一个流行的OCR(Optical Character Recognition,光学字符识别)库,通俗来说就是文本识别。Tesseract最初由HP(就是惠普啦)在1985年开始研发,后面貌似就没啥太重大的进展了;直到2005年HP将Tesseract开源,2006年开始交给Google维护。
Tesseract在进入3.0版本后各方面功能都有了长足的发展,尤其是3.02.02版本开始提供C-API,使得通过动态链接库与其他编程语言混合开发成为了可能。
直接使用Tesseract
这里介绍两种方法,一种是类似PyTesser的通过shell与tesseract通信完成识别过程;另一种是通过动态链接库(Windows下即DLL)实现。
shell
tesseract命令的格式为
tesseract imagename outputbase [-l lang] [-psm pagesegmode] [configfile...]
其中imagename
为输入图片路径,outputbase
为输出文本文件路径,此文本文件内容为图片文本识别结果。
所以通过shell实现的简单步骤就是
- 在Python中通过shell接口执行tesseract命令,指定输入和输出路径
- 读取输出文本文件内容
- 返回识别结果
安装Tesseract
首先安装Tesseract,参考官方wiki。Linux下直接通过包管理器安装(如apt-get install tesseract
);Windows下3.02之后版本不提供安装包,但有一个3.05版的非官方安装包,点击这里直接下载,安装时记得展开“Registry settings”选项,在“Add to Path”前打钩。
安装完成后在shell中输入
tesseract -v
即可看到如下信息:
tesseract 3.05.00dev
leptonica-1.73
libgif 4.1.6(?) : libjpeg 8d (libjpeg-turbo 1.4.2) : libpng 1.6.20 : libtiff 4.0.6 : zlib 1.2.8 : libwebp 0.4.3 : libopenjp2 2.1.0
注意:安装得到的Tesseract自带英文语言包,本文仅演示英文效果;如需中文请自行下载中文语言包,并修改相关命令。
封装
现在将Tesseract封装为一个Python函数。
import os import subprocess def image_to_string(img, cleanup=True, plus=''): # cleanup为True则识别完成后删除生成的文本文件 # plus参数为给tesseract的附加高级参数 subprocess.check_output('tesseract ' + img + ' ' + img + ' ' + plus, shell=True) # 生成同名txt文件 text = '' with open(img + '.txt', 'r') as f: text = f.read().strip() if cleanup: os.remove(img + '.txt') return text
这里解决了之前代码使用os.popen()
不等待返回的bug,subprocess.check_output()
会等待tesseract命令运行完成再返回。
运行
print(image_to_string('./phototest.tif')) # 打印识别出的文本,删除txt文件 print(image_to_string('./phototest.tif', False)) # 打印识别出的文本,不删除txt文件 print(image_to_string('./phototest.tif', False, '-l eng')) # 打印识别出的文本,不删除txt文件,同时提供高级参数
上述3中调用方式得到相同的结果。
DLL
通过动态链接库相对与shell方式有几个优点:
- 无需安装Tesseract(一般由自身自带DLL文件)
- 无需生成文本文件再读取,直接调用方法返回识别得到的字符串
- 更丰富的API支持
DLL方法即通过加载DLL文件,直接利用Tesseract C-API,在Python中调用此API完成识别。
DLL文件
DLL文件通常需要编译得到,编译Tesseract参考官方wiki。Linux下得到libtesseract.so,Windows下得到libtesseract.dll。
Windows下编译得到DLL文件也可以参考我的这篇文章:Tesseract 3.05及之后版本编译生成动态链接库DLL。
封装和运行
对加载DLL和调用API作封装。
x86
import ctypes DLL_PATH = 'C:/tesseract/build/bin/Release/tesseract305.dll' TESSDATA_PREFIX = b'./tessdata' lang = b'eng' tesseract = ctypes.cdll.LoadLibrary(DLL_PATH) api = tesseract.TessBaseAPICreate() rc = tesseract.TessBaseAPIInit3(api, TESSDATA_PREFIX, lang) if rc: tesseract.TessBaseAPIDelete(api) print('Could not initialize tesseract.\n') exit(3) def from_file(path): tesseract.TessBaseAPIProcessPages(api, path, None, 0, None) text_out = tesseract.TessBaseAPIGetUTF8Text(api) return ctypes.string_at(text_out) if __name__ == '__main__': image_file_path = b'./phototest.tif' result = from_file(image_file_path) print(result)
x64
import ctypes DLL_PATH = 'C:/tesseract/build/bin/Release/tesseract305.dll' TESSDATA_PREFIX = b'./tessdata' lang = b'eng' tesseract = ctypes.cdll.LoadLibrary(DLL_PATH) tesseract.TessBaseAPICreate.restype = ctypes.c_uint64 api = tesseract.TessBaseAPICreate() rc = tesseract.TessBaseAPIInit3(ctypes.c_uint64(api), TESSDATA_PREFIX, lang) if rc: tesseract.TessBaseAPIDelete(ctypes.c_uint64(api)) print('Could not initialize tesseract.\n') exit(3) def from_file(path): tesseract.TessBaseAPIProcessPages( ctypes.c_uint64(api), path, None, 0, None) tesseract.TessBaseAPIGetUTF8Text.restype = ctypes.c_uint64 text_out = tesseract.TessBaseAPIGetUTF8Text(ctypes.c_uint64(api)) return ctypes.string_at(text_out) if __name__ == '__main__': image_file_path = b'./phototest.tif' result = from_file(image_file_path) print(result)
关于对x86和x64进行区分的原因,参见我的另一篇文章Python x64下ctypes动态链接库出现access violation的原因分析。
关于更多的API调用方法,还请自行寻找。
小结
对比来说,shell方法和DLL方法各有优劣,但总的来说DLL方法更胜一筹,由于少了一次磁盘写入和读取,在性能上也更优。不管怎样,PyTesser实在是没有继续用下去的意义了。
在介绍DLL方法的时候本来想介绍直接向API传入图片数据(不读取文件)进行识别的,但折腾PIL库没有成功,不过网上倒有通过opencv成功的例子,但opencv太过重量级这里就不介绍了,感兴趣的话可以看参考链接。