利用pytesser識別圖形驗證碼
簡單識別
1.一般思路
驗證碼識別的一般思路為:
- 圖片降噪
- 圖片切割
- 影象文字輸出
1.1 圖片降噪
所謂降噪就是把不需要的資訊通通去除,比如背景,干擾線,干擾畫素等等,只剩下需要識別的文字,讓圖片變成2進位制點陣最好。
對於彩色背景的驗證碼:每個畫素都可以放在一個5維的空間裡,這5個維度分別是,X,Y,R,G,B,也就是畫素的座標和顏色,在計算機圖形學中,有很多種色彩空間,最常用的比如RGB,印刷用的CYMK,還有比較少見的HSL或者HSV,每種色彩空間的維度都不一樣,但是可以通過公式互相轉換。在RGB空間中不好區分顏色,可以把色彩空間轉換為HSV或HSL。色彩空間參見
驗證碼圖片7039.jpg
:
1、匯入Image包,開啟圖片:
from PIL import Image
im = Image.open('7039.jpg')
2、把彩色影象轉化為灰度影象。RBG轉化到HSI彩色空間,採用I分量:
imgry = im.convert('L')
imgry.show()
灰度看起來是這樣的:
3、二值化處理
二值化是影象分割的一種常用方法。在二值化圖象的時候把大於某個臨界灰度值的畫素灰度設為灰度極大值,把小於這個值的畫素灰度設為灰度極小值,從而實現二值化(一般設定為0-1)。根據閾值選取的不同,二值化的演算法分為固定閾值和自適應閾值,這裡選用比較簡單的固定閾值。
把畫素點大於閾值的設定,1,小於閾值的設定為0。生成一張查詢表,再呼叫point()進行對映。
threshold = 140
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
out = imgry.point(table, '1')
out.show()
或者用匿名函式
from PIL import Image
im = Image.open("7364.jpg")
im_gary = im.point(lambda x: 0 if x<143 else 255) #二值化處理
im_gary.show()
處理結果看起來是這樣的:
2. 圖片切割
識別驗證碼的重點和難點就在於能否成功分割字元,對於顏色相同又完全粘連的字元,比如google的驗證碼,目前是沒法做到5%以上的識別率的。不過google的驗證碼基本上人類也只有30%的識別率。本文使用的驗證碼例子比較容易識別。可以不用切割,有關圖片切割的方法參見這篇部落格:http://www.cnblogs.com/apexchu/p/4231041.html
3.利用pytesser模組實現識別
pytesser是谷歌OCR開源專案的一個模組,在python中匯入這個模組即可將圖片中的文字轉換成文字。
連結:https://code.google.com/p/pytesser/
pytesser 呼叫了 tesseract。在python中呼叫pytesser模組,pytesser又用tesseract識別圖片中的文字。
3.1 pytesser安裝
簡單識別安裝
- 把下載下來的
pytesser
包解壓到python目錄的Lib/site_packages
裡面,名字取為pytesser
, - 然後再在這個目錄下面新建一個
pytesser.pth
檔案,內容為pytesser
或者新建一個__init__.py
空白檔案, - 然後修改
pytesser.py
,把第一句的import Image
修改為from PIL import Image
,這一步的原因是這裡我們用的是pillow而不是用的原生PIL。 - pytesser裡包含了
tesseract.exe
和英語的資料包(預設只識別英文),還有一些示例圖片,所以解壓縮後即可使用。
這樣做好以後記得把pytesser
這個目錄放入到系統環境變數,因為程式會呼叫這個目錄裡面的tesseract.exe
,如果不放到環境變數會因為找不到這個檔案而丟擲異常。
簡單識別程式碼
# -*- coding:utf-8 -*-
from PIL import Image
from pytesser import pytesser
im = Image.open(r'C:\Users\Administrator\Desktop\1.png')
imgry = im.convert('L')
# imgry.show()
threshold = 140
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
out = imgry.point(table, '1')
out.show()
text = pytesser.image_to_string(out) # 將圖片轉成字串
print text.replace(' ', '').replace('\n', '') #這裡因為識別出來的文字可能會有空格和回車
複雜識別安裝
- 如果沒有安裝PIL,請到這裡下載安裝:http://www.pythonware.com/products/pil/
- 安裝pytesser,下載地址:http://code.google.com/p/pytesser/ ,下載後直接將其解壓到專案程式碼下,或者解壓到python安裝目錄的Libsite-packages
下,並將其新增到path
環境變數中,不然在匯入模組時會出錯。
- 下載Tesseract OCR engine:http://code.google.com/p/tesseract-ocr/ ,下載後解壓,找到tessdata資料夾,用其替換掉pytesser解壓後的tessdata資料夾即可。
另外如果現在都是從PIL庫中運入Image,沒有使用Image模組,所以需要把pytesser.py
中的import Image
改為from PIL import Image
, 其次還需要在pytesser
資料夾中新建一個__init__.py
的空檔案。
3.2 呼叫pytesser識別
pytesser提供了兩種識別圖片方法,通過image物件和圖片地址,程式碼判斷如下:
from PIL import Image
from pytesser import pytesser
image = Image.open('7039.jpg')
#通過開啟的檔案識別
print pytesser.image_to_string(image)
#通過檔案路徑直接識別
print pytesser.image_file_to_string('7039.jpg')
3.3解決識別率低的問題
可以增強圖片的顯示效果,或者將其轉換為黑白的,這樣可以使其識別率提升不少:
from PIL import ImageEnhance
image = Image.open(r'C:\Users\Administrator\Desktop\1.png')
enhancer = ImageEnhance.Contrast(image)
image2 = enhancer.enhance(4)
可以再對image2呼叫image_to_string
識別
3.4識別其他語言
tesseract
是一個命令列下執行的程式,引數如下:
tesseract imagename outbase [-l lang] [-psm N] [configfile...]
imagename
是輸入的image
的名字
outbase
是輸出的文字的名字,預設為outbase.txt
-l lang
是定義要識別的的語言,預設為英文
- 通過以下步驟可以識別其他語言:
"""OCR in Python using the Tesseract engine from Google
http://code.google.com/p/pytesser/
by Michael J.T. O'Kelly
V 0.0.1, 3/10/07"""
from PIL import Image
import subprocess
import util
import errors
tesseract_exe_name = 'D:\\Python2.7\\Lib\\site-packges\\pytesser\\tesseract' # Name of executable to be called at command line
scratch_image_name = "temp.bmp" # This file must be .bmp or other Tesseract-compatible format
scratch_text_name_root = "temp" # Leave out the .txt extension
cleanup_scratch_flag = True # Temporary files cleaned up after OCR operation
def call_tesseract(input_filename, output_filename, language):
"""Calls external tesseract.exe on input file (restrictions on types),
outputting output_filename+'txt'"""
args = [tesseract_exe_name, input_filename, output_filename, "-l", language]
proc = subprocess.Popen(args)
retcode = proc.wait()
if retcode!=0:
errors.check_for_errors()
def image_to_string(im, cleanup = cleanup_scratch_flag, language = "eng"):
"""Converts im to file, applies tesseract, and fetches resulting text.
If cleanup=True, delete scratch files after operation."""
try:
util.image_to_scratch(im, scratch_image_name)
call_tesseract(scratch_image_name, scratch_text_name_root,language)
text = util.retrieve_text(scratch_text_name_root)
finally:
if cleanup:
util.perform_cleanup(scratch_image_name, scratch_text_name_root)
return text
def image_file_to_string(filename, cleanup = cleanup_scratch_flag, graceful_errors=True, language = "eng"):
"""Applies tesseract to filename; or, if image is incompatible and graceful_errors=True,
converts to compatible format and then applies tesseract. Fetches resulting text.
If cleanup=True, delete scratch files after operation."""
try:
try:
call_tesseract(filename, scratch_text_name_root, language)
text = util.retrieve_text(scratch_text_name_root)
except errors.Tesser_General_Exception:
if graceful_errors:
im = Image.open(filename)
text = image_to_string(im, cleanup)
else:
raise
finally:
if cleanup:
util.perform_cleanup(scratch_image_name, scratch_text_name_root)
return text
if __name__=='__main__':
im = Image.open('phototest.tif')
text = image_to_string(im)
print text
try:
text = image_file_to_string('fnord.tif', graceful_errors=False)
except errors.Tesser_General_Exception, value:
print "fnord.tif is incompatible filetype. Try graceful_errors=True"
print value
text = image_file_to_string('fnord.tif', graceful_errors=True)
print "fnord.tif contents:", text
text = image_file_to_string('fonts_test.png', graceful_errors=True)
print text
在呼叫image_to_string
函式時,只要加上相應的language
引數就可以了,如簡體中文最後一個引數即為 chi_sim
, 繁體中文chi_tra
,
也就是下載的語言包的XXX.traineddata
檔案的名字XXX
,如下載的中文包是chi_sim.traineddata
, 引數就是chi_sim
:
text = image_to_string(self.im, language = 'chi_sim')
至此,圖片識別就完成了。
額外附加一句:有可能中文識別出來了,但是亂碼,需要相應地將text轉換為你所用的中文編碼方式,如:
text.decode("utf8")
就可以了