從 Google 的無人駕駛汽車到能夠識別假鈔的自動售賣機,機器視覺一直都是一個應用廣 泛且具備深遠的影響和雄偉的願景的領域。html
咱們將重點介紹機器視覺的一個分支:文字識別,介紹如何用一些 Python庫來識別和使用在線圖片中的文字。python
咱們能夠很輕鬆的閱讀圖片裏的文字,可是機器閱讀這些圖片就會很是困難,利用這種人類用戶能夠正常讀取可是大多數機器人都無法讀取的圖片,驗證碼 (CAPTCHA)就出現了。驗證碼讀取的難易程度也大不相同,有些驗證碼比其餘的更加難讀。linux
將圖像翻譯成文字通常被稱爲光學文字識別(Optical Character Recognition, OCR)。能夠實現OCR的底層庫並很少,目前不少庫都是使用共同的幾個底層 OCR 庫,或者是在上面 進行定製。git
在讀取和處理圖像、圖像相關的機器學習以及建立圖像等任務中,Python 一直都是很是出色的語言。雖然有不少庫能夠進行圖像處理,但在這裏咱們只重點介紹:Tesseractgithub
Tesseract 是一個 OCR 庫,目前由 Google 贊助(Google 也是一家以 OCR 和機器學習技術聞名於世的公司)。Tesseract 是目前公認最優秀、最精確的開源 OCR 系統。 除了極高的精確度,Tesseract 也具備很高的靈活性。它能夠經過訓練識別出任何字體,也能夠識別出任何 Unicode 字符。web
下載可執行安裝文件https://code.google.com/p/tesseract-ocr/downloads/list安裝。數據庫
能夠經過 apt-get 安裝: windows
$sudo apt-get install tesseract-ocr
用 Homebrew(http://brew.sh/)等第三方庫能夠很方便地安裝 brew install tesseract
瀏覽器
$TESSDATA_PREFIX
,讓 Tesseract 知道訓練的數據文件存儲在哪裏,而後搞一份tessdata數據文件,放到Tesseract目錄下。在大多數 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 的外面運行,但咱們能夠經過 pip 安裝支持Python 版本的 Tesseract庫:
pip install pytesseract
你要處理的大多數文字都是比較乾淨、格式規範的。格式規範的文字一般能夠知足一些需求,不過究竟什麼是「格式混亂」,什麼算「格式規範」,確實因人而異。 一般,格式規範的文字具備如下特色:
文字的一些格式問題在圖片預處理時能夠進行解決。例如,能夠把圖片轉換成灰度圖,調 整亮度和對比度,還能夠根據須要進行裁剪和旋轉(詳情請關注圖像與信號處理),可是,這些作法在進行更具擴展性的 訓練時會遇到一些限制。
經過下面的命令運行 Tesseract,讀取文件並把結果寫到一個文本文件中: `tesseract test.jpg text
cat text.txt
便可顯示結果。
識別結果很準確,不過符號^
和*
分別被表示成了雙引號和單引號。大致上可讓你很舒服地閱讀。
import pytesseract
from PIL import Image
image = Image.open('test.jpg')
text = pytesseract.image_to_string(image)
print text
運行結果:
This is some text, written in Arial, that will be read by
Tesseract. Here are some symbols: !@#$%"&*()
不少時候咱們在網上會看到這樣的圖片:
Tesseract 不能完整處理這個圖片,主要是由於圖片背景色是漸變的,最終結果是這樣:
隨着背景色從左到右不斷加深,文字變得愈來愈難以識別,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("text2.jpg", "text2clean.png")
經過一個閾值對前面的「模糊」圖片進行過濾的結果
除了一些標點符號不太清晰或丟失了,大部分文字都被讀出來了。Tesseract 給出了最好的 結果:
用 Tesseract 讀取硬盤裏圖片上的文字,可能不怎麼使人興奮,但當咱們把它和網絡爬蟲組合使用時,就能成爲一個強大的工具。
網站上的圖片可能並非故意把文字作得很花哨 (就像餐館菜單的 JPG 圖片上的藝術字),但它們上面的文字對網絡爬蟲來講就是隱藏起來 了,舉個例子:
雖然亞馬遜的 robots.txt 文件容許抓取網站的產品頁面,可是圖書的預覽頁一般不讓網絡機 器人採集。
圖書的預覽頁是經過用戶觸發 Ajax 腳本進行加載的,預覽圖片隱藏在 div 節點 下面;其實,普通的訪問者會以爲它們看起來更像是一個 Flash 動畫,而不是一個圖片文 件。固然,即便咱們能得到圖片,要把它們讀成文字也沒那麼簡單。
下面的程序就解決了這個問題:首先導航到托爾斯泰的《戰爭與和平》的大字號印刷版 1, 打開閱讀器,收集圖片的 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 讀取的效果同樣,這個程序也會完美地打印書中不少長長的段 落,第六頁的預覽以下所示:
6
"A word of friendly advice, mon
cher. Be off as soon as you can,
that's all I have to tell you. Happy
he who has ears to hear. Good-by,
my dear fellow. Oh, by the by!" he
shouted through the doorway after
Pierre, "is it true that the countess
has fallen into the clutches of the
holy fathers of the Society of je-
sus?"
Pierre did not answer and left Ros-
topchin's room more sullen and an-
gry than he had ever before shown
himself.
可是,當文字出如今彩色封面上時,結果就不那麼完美了:
WEI' nrrd Peace
Len Nlkelayevldu Iolfluy
Readmg shmdd be ax
wlnvame asnossxble Wenfler
an mm m our cram: Llhvary
- Leo Tmsloy was a Russian rwovelwst
I and moval phflmopher med lur
A ms Ideas 01 nonviolenx reswslance m 5 We range 0, "and"
若是想把文字加工成普通人能夠看懂的 效果,還須要花不少時間去處理。
下一節將介紹另外一種方法來解決文字混亂的問題,尤爲是當你願意花一點兒時間訓練 Tesseract 的時候。
經過給 Tesseract 提供大量已知的文字與圖片映射集,通過訓練 Tesseract 就能夠「學會」識別同一種字體,並且能夠達到極高的精確率和準確率,甚至能夠忽略圖 片中文字的背景色和相對位置等問題。
許多流行的內容管理系統即便加了驗證碼模塊,其衆所周知的註冊頁面也常常會遭到網絡 機器人的垃圾註冊。
那麼,這些網絡機器人究,竟是怎麼作的呢?既然咱們已經,能夠成功地識別出保存在電腦上 的驗證碼了,那麼如何才能實現一個全能的網絡機器人呢?
大多數網站生成的驗證碼圖片都具備如下屬性。
<img src="WebForm.aspx?id=8AP85CQKE9TJ">
,可是能夠和其餘圖片同樣進行 下載和處理。#!/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 程序很難識別。
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(http://pp19dd.com/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,加油!