Python:文本识别抛弃pytesser,直接使用Tesseract
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太过重量级这里就不介绍了,感兴趣的话可以看参考链接。
Python:文本识别抛弃pytesser,直接使用Tesseract - Penguin
[url=http://www.g390gpim935p9b5i5533g91blzjovs30s.org/]upzxdvcrcnw[/url]
pzxdvcrcnw http://www.g390gpim935p9b5i5533g91blzjovs30s.org/
apzxdvcrcnw
Python:文本识别抛弃pytesser,直接使用Tesseract - Penguin
amlxppsyyh
[url=http://www.gkr8o1f8ri958j53bk5ncs33w04122mjs.org/]umlxppsyyh[/url]
mlxppsyyh http://www.gkr8o1f8ri958j53bk5ncs33w04122mjs.org/
Python:文本识别抛弃pytesser,直接使用Tesseract - Penguin
[url=http://www.gl0983e11r07ozabxwl3s214b5wp989ss.org/]uofjjvcrmey[/url]
ofjjvcrmey http://www.gl0983e11r07ozabxwl3s214b5wp989ss.org/
aofjjvcrmey
Python:文本识别抛弃pytesser,直接使用Tesseract - Penguin
aeiqzriwonp
[url=http://www.g0j3u61knrsl1sf2e79980p86iy62o0ss.org/]ueiqzriwonp[/url]
eiqzriwonp http://www.g0j3u61knrsl1sf2e79980p86iy62o0ss.org/
Python:文本识别抛弃pytesser,直接使用Tesseract - Penguin
[url=http://www.gog93fp2nfx27b3fa74171eu95x4j96cs.org/]ulefepvzdz[/url]
lefepvzdz http://www.gog93fp2nfx27b3fa74171eu95x4j96cs.org/
alefepvzdz
Python:文本识别抛弃pytesser,直接使用Tesseract - Penguin
alnxryjow
[url=http://www.g4x6p2k3723io5w058vr16sgg4t8dsi6s.org/]ulnxryjow[/url]
lnxryjow http://www.g4x6p2k3723io5w058vr16sgg4t8dsi6s.org/
博主大大,您好!能否给我一份 64 位的 dll 文件呢?3.05 版本的也行,万分感谢!!!
'utf-8' codec can't decode byte 0xb2 in position 12: invalid start byte出现这个报错怎么办,大神
你使用的是Python3么?Python2可能有编码问题
多谢回复,我已经解决了,我用的python3,把pytesseract卸载重装就好了
收到大神发的DLL了,可是在LoadLibrary的时候出现了错误:SError: [WinError 126] The specified module could not be found,大神遇到过吗?
import ctypes
DLL_PATH = r'E:dlltesseract305.dll'
lib = ctypes.cdll.LoadLibrary(DLL_PATH)
以上走到第三行就有126 error了
多半是环境变量的问题,可以参考另一篇文章https://www.polarxiong.com/archives/Tesseract-3-05%E5%8F%8A%E4%B9%8B%E5%90%8E%E7%89%88%E6%9C%AC%E7%BC%96%E8%AF%91%E7%94%9F%E6%88%90%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93DLL.html下的评论哟
请问你们有在xp系统上搞的吗?如何让他支持xp系统呢?
没有在XP上测试过呢~不太确定XP是否支持
楼主,想问一下tesseract4.0的接口怎么写啊?有参考资料也可以!非常感谢!
你好,因为4.0还处在beta版,暂时没有更新文章的计划呢。
你可以看源码C API(https://github.com/tesseract-ocr/tesseract/blob/master/api/capi.h),然后配合文档(http://tesseract-ocr.github.io/4.x/index.html)来看怎么用,我估计套路和3.x会差不多,所以对照一下看看哪些细节变了就好了。
非常感谢,楼主的网站是用django写的吗?
用的Typecho+自己写的主题~
请忽略我上一个提问,粗心大意的错误。
我竟然遇到了同样的问题,能问一下怎么解决的吗?
Traceback (most recent call last):
rc = tesseract.TessBaseAPIInit3(ctypes.c_uint16(api), TESSDATA_PREFIX, lang)File "C:UsersliuxiaoxiangPycharmProjectsuntitled1hellowordDLL.py", line 10, in <module>
WindowsError: exception: access violation reading 0x0000BEA4
研究了一天,实在找不到哪错了,请大神帮帮忙
楼上的这个问题解决了吗?我是项目在ide里边运行可以,但是经过打包exe就会出现WindowsError: exception: access violation reading 0x0000004D1F888488 这个错误。rc = tesseract.TessBaseAPIInit2(api, TESSDATA_PREFIX, lang,None)在代码的这一行出错的,我试过不管调用那个APInit()方法都是一样的。
我也遇到这个问题了。用动态加载的方法运行,然后打包成EXE后,提示找不到语言数据包。
Running from container, but no tessdata (C:Users用户名AppDataLocalTemp_MEI67682data) found !
Traceback (most recent call last):
File "site-packagespyocrlibtesseract__init__.py", line 91, in image_to_string
File "site-packagespyocrlibtesseracttesseract_raw.py", line 344, in init
AssertionError
[5644] Failed to execute script MainWindow
跟踪进去,结果到了
handle = g_libtesseract.TessBaseAPICreate()
这里就没发再跟踪了,调用DLL的方法,没发调试了。
根本不知道他是怎么确定语言数据包路径的。
希望有大神来解惑。
还是我~,问题解决了。哈哈。
determine if application is a script file or frozen exe具体原因就是环境变量的设置造成的错误。python程序中。打包成exe的脚本和直接地运行脚本在获取路径上稍微有点不同。
if getattr(sys, 'frozen', False):
application_path = os.path.dirname(sys.executable)elif __file__:
application_path = os.path.dirname(__file__)上面的错误就可以解决了。
只要把TESSDATA_PREFIX加入环境变量,使用application_path 这个路径就可以了。然后复制语言数据包和DLL到当前EXE文件下,完美运行。已经测试成功。
牛叉的大佬啊。我是用pyocr的库解决的。你也可以试试。这个可以支持xp系统
wow~谢谢你提供的解决方法,不太确定把TESSDATA_PREFIX作为参数交给init()和把TESSDATA_PREFIX设置为环境变量的区别,有空仔细测试一下~
运行时出错
text=pytesseract.image_to_string(r'E:\temp.jpg')Traceback (most recent call last):
File "C:/Users/ecovacs/PycharmProjects/untitled/python_getpic.py", line 30, in <module>
File "C:Python27libsite-packagespytesseractpytesseract.py", line 156, in image_to_string
image.save(input_file_name)AttributeError: 'str' object has no attribute 'save'
你好,抱歉回复晚了,似乎我的代码里没有save()这个方法,还请贴出你的代码?
推荐使用tesseract OCR 的Python API,安装方法见: https://pypi.python.org/pypi/tesserocr
比使用subprocesses更加灵活。亲测识别结果与tesseract的shell版本一致。
请问楼主您这个示例是在哪里看的,windows下可以使用吗?这个依赖到项目里边如何去训练啊
我已经不记得了,当时是在linux下用的。依赖什么的我没注意,只是按照安装方法照做的,我只是用来测试了一下,放进项目里可能比较复杂,你自己看看吧,但它这个识别的效果挺差的…未必能达到你的项目需求。
似乎这个需要tesseract的依赖,就是得提前
apt-get install tesseract-ocr libtesseract-dev libleptonica-dev
看看原理的话就是第二种动态链接库的方法,不过用着确实比shell好太多。
但是mac下不能用吧?
mac下可以的,用brew安装就好了,注意包名不一样。
您好,我的是这个错误:subprocess.CalledProcessError: Command 'tesseractvery.jpg very.jpg ' returned non-zero exit status 1
返回非零啥的,请问是什么原因呢...
我是python2.7.11,tesseract版本是3.05.00
哈哈,典型的新手粗心犯的错误,'tesseract '最后面少了一个空格。
感谢您的耐心回复!
刚加上了,可还是这个错误,我感觉我的tesseract装的不对,请问怎么添加进python呢?
使用shell的方法实际和Python关系不是非常紧密,还是这个错误一般是tesseract没有加入到环境变量,具体怎么加入就自己去搜去咯;你可以先在命令行里输入tesseract试试,命令行不报错Python里一般也不会的。
刚换成tesseract3.02版本,现在命令行里使用倒没问题,但python里运行还是这个错误,头大...
兄台请问解决了吗,我也遇到这个问题,好纠结
Traceback (most recent call last):
print(image_to_string('F:/1.tif'))File "C:Usersadminimage_to_string.py", line 13, in <module>
File "C:Usersadminimage_to_string.py", line 8, in image_to_string
text = f.read().strip()UnicodeDecodeError: 'gbk' codec can't decode byte 0x9c in position 2: illegal multibyte sequence
套用博主的方法,遇到编码问题,囧
这个错误是字符编码问题,可以尝试open()中增加encoding参数指定编码为utf-8解决。我在Windows下用Python3是没有遇到这个问题的。
如果还是不能解决麻烦提供测试图片,所用Python版本,运行环境和操作系统等信息我来测试!
虫数据支持Python识别,可以试试看
child = subprocess.Popen(r'C:Program FilesTesseract-OCRtesseract.exe' + img + ' ' + img_name + ' ' + plus)
不然会儿会报错:FileNotFoundError: [WinError 2] 系统找不到指定的文件。
system path在tesseract安装的时候,已经加入,不知为啥还会儿报错
谢谢反馈,参见之前的评论,是os.popen()不等待命令执行完导致的。
你写的image_to_string函数有问题,我是在python3.5环境下跑的,还没等os.popen()执行完就直接输出了所以报错找不到txt文件,建议用subprocess模块的wait()方法,等命令行执行完毕后再执行下一条语句。
import os,subprocess def image_to_string(img, cleanup=True, plus=''): # cleanup为True则识别完成后删除生成的文本文件 # plus参数为给tesseract的附加高级参数 img_name = img.split('.')[0] child = subprocess.Popen('tesseract ' + img + ' ' + img_name + ' ' + plus) #生成同名txt文件 child.wait() text = open(img_name + '.txt').read().strip() if cleanup: os.remove(img_name + '.txt') return text print(image_to_string('test.png',False))谢谢你的反馈,稍后有空就改过来!
似乎subprocess.check_output()也是一个不错的选择:)