从 Google 的无人驾驶汽车到能够识别假钞的自动售卖机,机器视觉一直都是一个应用普遍且具备深远的影响和雄伟的愿景的领域。html
咱们将重点介绍机器视觉的一个分支:文字识别,介绍如何用一些Python库来识别和使用在线图片中的文字。python
咱们能够很轻松的阅读图片里的文字,可是机器阅读这些图片就会很是困难,利用这种人类用户能够正常读取可是大多数机器人都无法读取的图片,验证码 (CAPTCHA)就出现了。验证码读取的难易程度也大不相同,有些验证码比其余的更加难读。git
将图像翻译成文字通常被称为光学文字识别(Optical Character Recognition, OCR)。能够实现OCR的底层库并很少,目前不少库都是使用共同的几个底层 OCR 库,或者是在上面进行定制。github
在读取和处理图像、图像相关的机器学习以及建立图像等任务中,Python一直都是很是出色的语言。虽然有不少库能够进行图像处理,但在这里咱们只重点介绍:Tesseractweb
Tesseract 是一个OCR库,目前由Google赞助(Google也是一家以OCR和机器学习技术闻名于世的公司)。Tesseract是目前公认最优秀、最精确的开源OCR系统。除了极高的精确度,Tesseract也具备很高的灵活性。它能够经过训练识别出任何字体,也能够识别出任何 Unicode字符。数据库
Windows系统
下载可执行安装文件 tesseract-ocr 安装。浏览器
Linux系统
能够经过 apt-get 安装: $sudo apt-get tesseract-ocr服务器
Mac OS系统
用 Homebrew等第三方库能够很方便地安装 : brew install tesseract网络
要使用 Tesseract 的功能,好比后面的示例中训练程序识别字母,要先在系统中设置一 个新的环境变量 $TESSDATA_PREFIX,让 Tesseract 知道训练的数据文件存储在哪里,而后搞一份tessdata数据文件,放到Tesseract目录下。session
在多数 Linux 系统和 Mac OS X 系统上,能够这么设置: $export TESSDATA_PREFIX=/usr/local/share/Tesseract
在 Windows 系统上,你能够经过下面这行命令设置环境变量: #setx TESSDATA_PREFIX C:\Program Files\Tesseract OCR\Tesseract
Tesseract 是一个Python的命令行工具,不是经过import语句导入的库。安装以后,要用 tesseract 命令在 Python 的外面运行,但咱们能够经过 pip3 安装支持Python版本的 Tesseract库:
pip install pytesseract
你要处理的大多数文字都是比较干净、格式规范的。格式规范的文字一般能够知足一些需求,不过究竟什么是“格式混乱”,什么算“格式规范”,确实因人而异。 一般,格式规范的文字具备如下特色:
文字的一些格式问题在图片预处理时能够进行解决。例如,能够把图片转换成灰度图,调整亮度和对比度,还能够根据须要进行裁剪和旋转(详情请关注图像与信号处理),可是,这些作法在进行更具扩展性的训练时会遇到一些限制。
格式规范文字的理想示例:
经过下面的命令运行 Tesseract,读取文件并把结果写到一个文本文件中:
tesseract test.jpg text
识别结果很准确,不过符号^和*分别被表示成了双引号和单引号。大致上可让你很舒服地阅读。
import pytesseract from PIL import Image image = Image.open('test.jpg') text = pytesseract.image_to_string(image) print(text)
不少时候咱们在网上会看到这样的图片:
随着背景色从左到右不断加深,文字变得愈来愈难以识别,Tesseract识别出的每一行的最后几个字符都是错的。
遇到这类问题,能够先用 Python 脚本对图片进行清理。利用Pillow库,咱们能够建立一个阈值过滤器来去掉渐变的背景色,只把文字留下来,从而让图片更加清晰,便于Tesseract读取:
from PIL import Image import subprocess def cleanFile(filePath, newFilePath): image = Image.open(filePath) # 对图片进行阈值过滤,而后保存 image = image.point(lambda x: 0 if x<143 else 255) image.save(newFilePath) # 调用系统的tesseract命令对图片进行OCR识别 subprocess.call(["tesseract", newFilePath, "output"]) # 打开文件读取结果 file = open("output.txt", 'r') print(file.read()) file.close() cleanFile("text.jpg", "textclean.png")
经过一个阈值对前面的“模糊”图片进行过滤的结果:
用Tesseract读取硬盘里图片上的文字,可能不怎么使人兴奋,但咱们把它和网络爬虫组合使用时,就能成为一个强大的工具。
网站上的图片可能并非故意把文字作得很花哨(就像餐馆菜单的JPG图片上的艺术字),但它们上面的文字对网络爬虫来讲就是隐藏起来了,举个例子:
虽然亚马逊的robots.txt文件容许抓取网站的产品页面,可是图书的预览页一般不让网络机器人采集。
图书的预览页是经过用户触发Ajax脚本进行加载的,预览图片隐藏在div节点下面;其实,普通的访问者会以为它们看起来更像是一个 Flash 动画,而不是一个图片文件。固然,即便咱们能得到图片,要把它们读成文字也没那么简单。
下面的程序就解决了这个问题:首先导航到托尔斯泰的《战争与和平》的大字号印刷版,打开阅读器,收集图片的 URL 连接,而后下载图片,识别图片,最后打印每一个图片的文字。:
import time from urllib.request import urlretrieve import subprocess from selenium import webdriver #建立新的Selenium driver driver = webdriver.PhantomJS() # 用Selenium试试Firefox浏览器: # driver = webdriver.Firefox() driver.get("http://www.amazon.com/War-Peace-Leo-Nikolayevich-Tolstoy/dp/1427030200") # 单击图书预览按钮 driver.find_element_by_id("sitbLogoImg").click() imageList = set() # 等待页面加载完成 time.sleep(5) # 当向右箭头能够点击时,开始翻页 while "pointer" in driver.find_element_by_id("sitbReaderRightPageTurner").get_attribute("style"): driver.find_element_by_id("sitbReaderRightPageTurner").click() time.sleep(2) # 获取已加载的新页面(一次能够加载多个页面,可是重复的页面不能加载到集合中) pages = driver.find_elements_by_xpath("//div[@class='pageImage']/div/img") for page in pages: image = page.get_attribute("src") imageList.add(image) driver.quit() # 用Tesseract处理咱们收集的图片URL连接 for image in sorted(imageList): # 保存图片 urlretrieve(image, "page.jpg") p = subprocess.Popen(["tesseract", "page.jpg", "page"], stdout=subprocess.PIPE,stderr=subprocess.PIPE) f = open("page.txt", "r") p.wait() print(f.read())
经过给 Tesseract 提供大量已知的文字与图片映射集,通过训练 Tesseract 就能够“学会”识别同一种字体,并且能够达到极高的精确率和准确率,甚至能够忽略图片中文字的背景色和相对位置等问题。
许多流行的内容管理系统即便加了验证码模块,其众所周知的注册页面也常常会遭到网络机器人的垃圾注册。
那么,这些网络机器人到底是怎么作的呢?既然咱们已经能够成功地识别出保存在电脑上的验证码了,那么如何才能实现一个全能的网络机器人呢?
大多数网站生成的验证码图片都具备如下属性。
- 它们是服务器端的程序动态生成的图片。验证码图片的 src 属性可能和普通图片不太一 样,好比<img src="WebForm.aspx?id=8AP85CQKE9TJ">,可是能够和其余图片同样进行下载和处理。
- 图片的答案存储在服务器端的数据库里。
- 不少验证码都有时间限制,若是你太长时间没解决就会失效。
- 经常使用的处理方法就是,首先把验证码图片下载到硬盘里,清理干净,而后用Tesseract处理图片,最后返回符合网站要求的识别结果。
#!/usr/bin/env python # -*- coding:utf-8 -*- import requests import time import pytesseract from PIL import Image from bs4 import BeautifulSoup def captcha(data): with open('captcha.jpg','wb') as fp: fp.write(data) time.sleep(1) image = Image.open("captcha.jpg") text = pytesseract.image_to_string(image) print "机器识别后的验证码为:" + text command = raw_input("请输入Y表示赞成使用,按其余键自行从新输入:") if (command == "Y" or command == "y"): return text else: return raw_input('输入验证码:') def zhihuLogin(username,password): # 构建一个保存Cookie值的session对象 sessiona = requests.Session() headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0'} # 先获取页面信息,找到须要POST的数据(而且已记录当前页面的Cookie) html = sessiona.get('https://www.zhihu.com/#signin', headers=headers).content # 找到 name 属性值为 _xsrf 的input标签,取出value里的值 _xsrf = BeautifulSoup(html ,'lxml').find('input', attrs={'name':'_xsrf'}).get('value') # 取出验证码,r后面的值是Unix时间戳,time.time() captcha_url = 'https://www.zhihu.com/captcha.gif?r=%d&type=login' % (time.time() * 1000) response = sessiona.get(captcha_url, headers = headers) data = { "_xsrf":_xsrf, "email":username, "password":password, "remember_me":True, "captcha": captcha(response.content) } response = sessiona.post('https://www.zhihu.com/login/email', data = data, headers=headers) print response.text response = sessiona.get('https://www.zhihu.com/people/maozhaojun/activities', headers=headers) print response.text if __name__ == "__main__": #username = raw_input("username") #password = raw_input("password") zhihuLogin('xxxx@qq.com','ALAxxxxIME')
值得注意的是,有两种异常状况会致使这个程序运行失败。第一种状况是,若是 Tesseract 从验证码图片中识别的结果不是四个字符(由于训练样本中验证码的全部有效答案都必须是四个字符),结果不会被提交,程序失败。第二种状况是虽然识别的结果是四个字符, 被提交到了表单,可是服务器对结果不承认,程序仍然失败。
在实际运行过程当中,第一种状况发生的可能性大约为50%,发生时程序不会向表单提交,程序直接结束并提示验证码识别错误。第二种异常状况发生的几率约为20%,四个字符都对的几率约是 30%(每一个字母的识别正确率大约是80%,若是是五个字符都识别,正确的总几率是32.8%)。
大多数其余的验证码都是比较简单的。例如,流行的PHP内容管理系统Drupal有一个著名的验证码模块:https://www.drupal.org/project/captcha 能够生成不一样难度的验证码。
那么与其余验证码相比,到底是什么让这个验证码更容易被人类和机器读懂呢?
- 字母没有相互叠加在一块儿,在水平方向上也没有彼此交叉。也就是说,能够在每个字母外面画一个方框,而不会重叠在一块儿。
- 图片没有背景色、线条或其余对OCR程序产生干扰的噪点。
- 虽然不能因一个图片下定论,可是这个验证码用的字体种类不多,并且用的是sans-serif 字体(像“4”和“M”)和一种手写形式的字体(像“m”“C”和“3”)。
- 白色背景色与深色字母之间的对比度很高。
这个验证码只作了一点点改变,就让 OCR 程序很难识别。
- 字母和数据都使用了,这会增长待搜索字符的数量。
- 字母随机的倾斜程度会迷惑 OCR 软件,可是人类仍是很容易识别的。
- 那个比较陌生的手写字体颇有挑战性,在“C”和“3”里面还有额外的线条。另外这个很是小的小写“m”,计算机须要进行额外的训练才能识别。
用下面的代码运行Tesseract识别图片:
tesseract captchaExample.png output
咱们获得的结果 output.txt 是: 4N,,,C<3.
要训练Tesseract识别一种文字,不管是晦涩难懂的字体仍是验证码,你都须要向Tesseract提供每一个字符不一样形式的样本。
作这个枯燥的工做可能要花好几个小时的时间,你可能更想用这点儿时间找个好看的视频或电影看看。首先要把大量的验证码样本下载到一个文件夹里。
下载的样本数量由验证码的复杂程度决定;我在训练集里一共放了100个样本(一共500个字符,平均每一个字符8个样本;a~z大小写字母加0~9 数字,一共62个字符),应该足够训练的了。
提示:建议使用验证码的真实结果给每一个样本文件命名(即4MmC3.jpg)。 这样能够帮你一次性对大量的文件进行快速检查——你能够先把图片调成缩略图模式,而后经过文件名对比不一样的图片。这样在后面的步骤中进行训练效果的检查也会很方便。
第二步是准确地告诉Tesseract一张图片中的每一个字符是什么,以及每一个字符的具体位置。 这里须要建立一些矩形定位文件(box file),一个验证码图片生成一个矩形定位文件。一个图片的矩形定位文件以下所示:
4 15 26 33 55 0 M 38 13 67 45 0 m 79 15 101 26 0 C 111 33 136 60 0 3 147 17 176 45 0
第一列符号是图片中的每一个字符,后面的4个数字分别是包围这个字符的最小矩形的坐标(图片左下角是原点 (0,0),4个数字分别对应每一个字符的左下角 x 坐标、左下角 y 坐标、右上角 x 坐标和右上角 y 坐标),最后一个数字“0”表示图片样本的编号。
显然,手工建立这些图片矩形定位文件很无聊,不过有一些工具能够帮你完成。我很喜欢在线工具 Tesseract OCR Chopper,由于它不须要安装,也没有其余依赖,只要有浏览器就能够运行,并且用法很简单:上传图片,若是要增长新矩形就单击“add”按钮,还能够根据须要调整矩形的尺寸,最后把新生成的矩形定位文件复制到一个新文件里就能够了。
矩形定位文件必须保存在一个 .box后缀的文本文件中。和图片文件同样,文本文件也是用验证码的实际结果命名(例如,4MmC3.box)。另外,这样便于检查 .box文件的内容和文件的名称,并且按文件名对目录中的文件排序以后,就可让 .box文件与对应的图片文件的实际结果进行对比。
你还须要建立大约 100 个 .box文件来保证你有足够的训练数据。由于Tesseract会忽略那些不能读取的文件,因此建议你尽可能多作一些矩形定位文件,以保证训练足够充分。若是你以为训练的 OCR 结果没有达到你的目标,或者Tesseract识别某些字符时老是出错,多建立一些训练数据而后从新训练将是一个不错的改进方法。
建立完满载 .box 文件和图片文件的数据文件夹以后,在作进一步分析以前最好备份一下这个文件夹。虽然在数据上运行训练程序不太可能删除任何数据,可是建立 .box 文件用了你好几个小时的时间,来之不易,稳妥一点儿总没错。此外,可以抓取一个尽是编译数据的 混乱目录,而后再尝试一次,老是好的。
前面的内容只是对Tesseract库强大的字体训练和识别能力的一个简略概述。若是你对 Tesseract 的其余训练方法感兴趣,甚至打算创建本身的验证码训练文件库,或者想和全世界的 Tesseract 爱好者分享本身对一种新字体的识别成果,推荐阅读 Tesseract 的文档:https://github.com/tesseract-ocr/tesseract/wiki