日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

字符识别Python实现 图片验证码识别

發(fā)布時(shí)間:2025/5/22 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符识别Python实现 图片验证码识别 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

字符型圖片驗(yàn)證碼識(shí)別完整過(guò)程及Python實(shí)現(xiàn)

1???摘要

驗(yàn)證碼是目前互聯(lián)網(wǎng)上非常常見(jiàn)也是非常重要的一個(gè)事物,充當(dāng)著很多系統(tǒng)的?防火墻?功能,但是隨時(shí)OCR技術(shù)的發(fā)展,驗(yàn)證碼暴露出來(lái)的安全問(wèn)題也越來(lái)越嚴(yán)峻。本文介紹了一套字符驗(yàn)證碼識(shí)別的完整流程,對(duì)于驗(yàn)證碼安全和OCR識(shí)別技術(shù)都有一定的借鑒意義。

2???關(guān)鍵詞

關(guān)鍵詞:安全,字符圖片,驗(yàn)證碼識(shí)別,OCR,Python,SVM,PIL

3???免責(zé)聲明

本文研究所用素材來(lái)自于某舊Web框架的網(wǎng)站?完全對(duì)外公開(kāi)?的公共圖片資源。

本文只做了該網(wǎng)站對(duì)外公開(kāi)的公共圖片資源進(jìn)行了爬取,?并未越權(quán)?做任何多余操作。

本文在書(shū)寫(xiě)相關(guān)報(bào)告的時(shí)候已經(jīng)?隱去?漏洞網(wǎng)站的身份信息。

本文作者?已經(jīng)通知?網(wǎng)站相關(guān)人員此系統(tǒng)漏洞,并積極向新系統(tǒng)轉(zhuǎn)移。

本報(bào)告的主要目的也僅是用于?OCR交流學(xué)習(xí)?和引起大家對(duì)?驗(yàn)證安全的警覺(jué)?。

4???引言

關(guān)于驗(yàn)證碼的非技術(shù)部分的介紹,可以參考以前寫(xiě)的一篇科普類(lèi)的文章:

互聯(lián)網(wǎng)安全防火墻(1)--網(wǎng)絡(luò)驗(yàn)證碼的科普

http://www.cnblogs.com/beer/p/4996833.html

里面對(duì)驗(yàn)證碼的種類(lèi),使用場(chǎng)景,作用,主要的識(shí)別技術(shù)等等進(jìn)行了講解,然而并沒(méi)有涉及到任何技術(shù)內(nèi)容。本章內(nèi)容則作為它的?技術(shù)補(bǔ)充?來(lái)給出相應(yīng)的識(shí)別的解決方案,讓讀者對(duì)驗(yàn)證碼的功能及安全性問(wèn)題有更深刻的認(rèn)識(shí)。

5???基本工具

要達(dá)到本文的目的,只需要簡(jiǎn)單的編程知識(shí)即可,因?yàn)楝F(xiàn)在的機(jī)器學(xué)習(xí)領(lǐng)域的蓬勃發(fā)展,已經(jīng)有很多封裝好的開(kāi)源解決方案來(lái)進(jìn)行機(jī)器學(xué)習(xí)。普通程序員已經(jīng)不需要了解復(fù)雜的數(shù)學(xué)原理,即可以實(shí)現(xiàn)對(duì)這些工具的應(yīng)用了。

主要開(kāi)發(fā)環(huán)境:

  • python3.5

    python SDK版本

  • PIL

    圖片處理庫(kù)

  • libsvm

    開(kāi)源的svm機(jī)器學(xué)習(xí)庫(kù)

關(guān)于環(huán)境的安裝,不是本文的重點(diǎn),故略去。

6???基本流程

一般情況下,對(duì)于字符型驗(yàn)證碼的識(shí)別流程如下:

  • 準(zhǔn)備原始圖片素材
  • 圖片預(yù)處理
  • 圖片字符切割
  • 圖片尺寸歸一化
  • 圖片字符標(biāo)記
  • 字符圖片特征提取
  • 生成特征和標(biāo)記對(duì)應(yīng)的訓(xùn)練數(shù)據(jù)集
  • 訓(xùn)練特征標(biāo)記數(shù)據(jù)生成識(shí)別模型
  • 使用識(shí)別模型預(yù)測(cè)新的未知圖片集
  • 達(dá)到根據(jù)“圖片”就能返回識(shí)別正確的字符集的目標(biāo)
  • 7???素材準(zhǔn)備

    7.1???素材選擇

    由于本文是以初級(jí)的學(xué)習(xí)研究目的為主,要求?“有代表性,但又不會(huì)太難”?,所以就直接在網(wǎng)上找個(gè)比較有代表性的簡(jiǎn)單的字符型驗(yàn)證碼(感覺(jué)像在找漏洞一樣)。

    最后在一個(gè)比較舊的網(wǎng)站(估計(jì)是幾十年前的網(wǎng)站框架)找到了這個(gè)驗(yàn)證碼圖片。

    原始圖:

    放大清晰圖:

    此圖片能滿(mǎn)足要求,仔細(xì)觀察其具有如下特點(diǎn)。

    有利識(shí)別的特點(diǎn)?:

  • 由純阿拉伯?dāng)?shù)字組成
  • 字?jǐn)?shù)為4位
  • 字符排列有規(guī)律
  • 字體是用的統(tǒng)一字體
  • 以上就是本文所說(shuō)的此驗(yàn)證碼簡(jiǎn)單的重要原因,后續(xù)代碼實(shí)現(xiàn)中會(huì)用到

    不利識(shí)別的特點(diǎn)?:

  • 圖片背景有干擾噪點(diǎn)
  • 這雖然是不利特點(diǎn),但是這個(gè)干擾門(mén)檻太低,只需要簡(jiǎn)單的方法就可以除去

    7.2???素材獲取

    由于在做訓(xùn)練的時(shí)候,需要大量的素材,所以不可能用手工的方式一張張?jiān)跒g覽器中保存,故建議寫(xiě)個(gè)自動(dòng)化下載的程序。

    主要步驟如下:

  • 通過(guò)瀏覽器的抓包功能獲取隨機(jī)圖片驗(yàn)證碼生成接口
  • 批量請(qǐng)求接口以獲取圖片
  • 將圖片保存到本地磁盤(pán)目錄中
  • 這些都是一些IT基本技能,本文就不再詳細(xì)展開(kāi)了。

    關(guān)于網(wǎng)絡(luò)請(qǐng)求和文件保存的代碼,如下:

    def downloads_pic(**kwargs):pic_name = kwargs.get('pic_name', None)url = 'http://xxxx/rand_code_captcha/'res = requests.get(url, stream=True)with open(pic_path + pic_name+'.bmp', 'wb') as f:for chunk in res.iter_content(chunk_size=1024):if chunk: # filter out keep-alive new chunksf.write(chunk)f.flush()f.close()

    循環(huán)執(zhí)行N次,即可保存N張驗(yàn)證素材了。

    下面是收集的幾十張素材庫(kù)保存到本地文件的效果圖:

    8???圖片預(yù)處理

    雖然目前的機(jī)器學(xué)習(xí)算法已經(jīng)相當(dāng)先進(jìn)了,但是為了減少后面訓(xùn)練時(shí)的復(fù)雜度,同時(shí)增加識(shí)別率,很有必要對(duì)圖片進(jìn)行預(yù)處理,使其對(duì)機(jī)器識(shí)別更友好。

    針對(duì)以上原始素材的處理步驟如下:

  • 讀取原始圖片素材
  • 將彩色圖片二值化為黑白圖片
  • 去除背景噪點(diǎn)
  • 8.1???二值化圖片

    主要步驟如下:

  • 將RGB彩圖轉(zhuǎn)為灰度圖
  • 將灰度圖按照設(shè)定閾值轉(zhuǎn)化為二值圖
  • image = Image.open(img_path) imgry = image.convert('L') # 轉(zhuǎn)化為灰度圖table = get_bin_table() out = imgry.point(table, '1')

    上面引用到的二值函數(shù)的定義如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 def?get_bin_table(threshold=140): ????""" ????獲取灰度轉(zhuǎn)二值的映射table ????:param threshold: ????:return: ????""" ????table?=?[] ????for?i?in?range(256): ????????if?i < threshold: ????????????table.append(0) ????????else: ????????????table.append(1) ????return?table

    由PIL轉(zhuǎn)化后變成二值圖片:0表示黑色,1表示白色。二值化后帶噪點(diǎn)的?6937?的像素點(diǎn)輸出后如下圖:

    1111000111111000111111100001111100000011 1110111011110111011111011110111100110111 1001110011110111101011011010101101110111 1101111111110110101111110101111111101111 1101000111110111001111110011111111101111 1100111011111000001111111001011111011111 1101110001111111101011010110111111011111 1101111011111111101111011110111111011111 1101111011110111001111011110111111011100 1110000111111000011101100001110111011111

    如果你是近視眼,然后離屏幕遠(yuǎn)一點(diǎn),可以隱約看到?6937?的骨架了。

    8.2???去除噪點(diǎn)

    在轉(zhuǎn)化為二值圖片后,就需要清除噪點(diǎn)。本文選擇的素材比較簡(jiǎn)單,大部分噪點(diǎn)也是最簡(jiǎn)單的那種?孤立點(diǎn),所以可以通過(guò)檢測(cè)這些孤立點(diǎn)就能移除大量的噪點(diǎn)。

    關(guān)于如何去除更復(fù)雜的噪點(diǎn)甚至干擾線和色塊,有比較成熟的算法:?洪水填充法 Flood Fill?,后面有興趣的時(shí)間可以繼續(xù)研究一下。

    本文為了問(wèn)題簡(jiǎn)單化,干脆就用一種簡(jiǎn)單的自己想的?簡(jiǎn)單辦法?來(lái)解決掉這個(gè)問(wèn)題:

    • 對(duì)某個(gè)?黑點(diǎn)?周邊的九宮格里面的黑色點(diǎn)計(jì)數(shù)
    • 如果黑色點(diǎn)少于2個(gè)則證明此點(diǎn)為孤立點(diǎn),然后得到所有的孤立點(diǎn)
    • 對(duì)所有孤立點(diǎn)一次批量移除。

    下面將詳細(xì)介紹關(guān)于具體的算法原理。

    將所有的像素點(diǎn)如下圖分成三大類(lèi)

    • 頂點(diǎn)A
    • 非頂點(diǎn)的邊界點(diǎn)B
    • 內(nèi)部點(diǎn)C

    種類(lèi)點(diǎn)示意圖如下:

    其中:
    • A類(lèi)點(diǎn)計(jì)算周邊相鄰的3個(gè)點(diǎn)(如上圖紅框所示)
    • B類(lèi)點(diǎn)計(jì)算周邊相鄰的5個(gè)點(diǎn)(如上圖紅框所示)
    • C類(lèi)點(diǎn)計(jì)算周邊相鄰的8個(gè)點(diǎn)(如上圖紅框所示)

    當(dāng)然,由于基準(zhǔn)點(diǎn)在計(jì)算區(qū)域的方向不同,A類(lèi)點(diǎn)和B類(lèi)點(diǎn)還會(huì)有細(xì)分:

    • A類(lèi)點(diǎn)繼續(xù)細(xì)分為:左上,左下,右上,右下
    • B類(lèi)點(diǎn)繼續(xù)細(xì)分為:上,下,左,右
    • C類(lèi)點(diǎn)不用細(xì)分

    然后這些細(xì)分點(diǎn)將成為后續(xù)坐標(biāo)獲取的準(zhǔn)則。

    主要算法的python實(shí)現(xiàn)如下:

    def sum_9_region(img, x, y):"""9鄰域框,以當(dāng)前點(diǎn)為中心的田字框,黑點(diǎn)個(gè)數(shù):param x::param y::return:"""# todo 判斷圖片的長(zhǎng)寬度下限cur_pixel = img.getpixel((x, y)) # 當(dāng)前像素點(diǎn)的值width = img.widthheight = img.heightif cur_pixel == 1: # 如果當(dāng)前點(diǎn)為白色區(qū)域,則不統(tǒng)計(jì)鄰域值return 0if y == 0: # 第一行if x == 0: # 左上頂點(diǎn),4鄰域# 中心點(diǎn)旁邊3個(gè)點(diǎn)sum = cur_pixel \+ img.getpixel((x, y + 1)) \+ img.getpixel((x + 1, y)) \+ img.getpixel((x + 1, y + 1))return 4 - sumelif x == width - 1: # 右上頂點(diǎn)sum = cur_pixel \+ img.getpixel((x, y + 1)) \+ img.getpixel((x - 1, y)) \+ img.getpixel((x - 1, y + 1))return 4 - sumelse: # 最上非頂點(diǎn),6鄰域sum = img.getpixel((x - 1, y)) \+ img.getpixel((x - 1, y + 1)) \+ cur_pixel \+ img.getpixel((x, y + 1)) \+ img.getpixel((x + 1, y)) \+ img.getpixel((x + 1, y + 1))return 6 - sumelif y == height - 1: # 最下面一行if x == 0: # 左下頂點(diǎn)# 中心點(diǎn)旁邊3個(gè)點(diǎn)sum = cur_pixel \+ img.getpixel((x + 1, y)) \+ img.getpixel((x + 1, y - 1)) \+ img.getpixel((x, y - 1))return 4 - sumelif x == width - 1: # 右下頂點(diǎn)sum = cur_pixel \+ img.getpixel((x, y - 1)) \+ img.getpixel((x - 1, y)) \+ img.getpixel((x - 1, y - 1))return 4 - sumelse: # 最下非頂點(diǎn),6鄰域sum = cur_pixel \+ img.getpixel((x - 1, y)) \+ img.getpixel((x + 1, y)) \+ img.getpixel((x, y - 1)) \+ img.getpixel((x - 1, y - 1)) \+ img.getpixel((x + 1, y - 1))return 6 - sumelse: # y不在邊界if x == 0: # 左邊非頂點(diǎn)sum = img.getpixel((x, y - 1)) \+ cur_pixel \+ img.getpixel((x, y + 1)) \+ img.getpixel((x + 1, y - 1)) \+ img.getpixel((x + 1, y)) \+ img.getpixel((x + 1, y + 1))return 6 - sumelif x == width - 1: # 右邊非頂點(diǎn)# print('%s,%s' % (x, y))sum = img.getpixel((x, y - 1)) \+ cur_pixel \+ img.getpixel((x, y + 1)) \+ img.getpixel((x - 1, y - 1)) \+ img.getpixel((x - 1, y)) \+ img.getpixel((x - 1, y + 1))return 6 - sumelse: # 具備9領(lǐng)域條件的sum = img.getpixel((x - 1, y - 1)) \+ img.getpixel((x - 1, y)) \+ img.getpixel((x - 1, y + 1)) \+ img.getpixel((x, y - 1)) \+ cur_pixel \+ img.getpixel((x, y + 1)) \+ img.getpixel((x + 1, y - 1)) \+ img.getpixel((x + 1, y)) \+ img.getpixel((x + 1, y + 1))return 9 - sum

    Tips:這個(gè)地方是相當(dāng)考驗(yàn)人的細(xì)心和耐心程度了,這個(gè)地方的工作量還是蠻大的,花了半個(gè)晚上的時(shí)間才完成的。

    計(jì)算好每個(gè)像素點(diǎn)的周邊像素黑點(diǎn)(注意:PIL轉(zhuǎn)化的圖片黑點(diǎn)的值為0)個(gè)數(shù)后,只需要篩選出個(gè)數(shù)為?1或者2?的點(diǎn)的坐標(biāo)即為?孤立點(diǎn)?。這個(gè)判斷方法可能不太準(zhǔn)確,但是基本上能夠滿(mǎn)足本文的需求了。

    經(jīng)過(guò)預(yù)處理后的圖片如下所示:

    對(duì)比文章開(kāi)頭的原始圖片,那些?孤立點(diǎn)?都被移除掉,相對(duì)比較?干凈?的驗(yàn)證碼圖片已經(jīng)生成。

    9???圖片字符切割

    由于字符型?驗(yàn)證碼圖片?本質(zhì)就可以看著是由一系列的?單個(gè)字符圖片?拼接而成,為了簡(jiǎn)化研究對(duì)象,我們也可以將這些圖片分解到?原子級(jí)?,即:?只包含單個(gè)字符的圖片

    于是,我們的研究對(duì)象由?“N種字串的組合對(duì)象”?變成?“10種阿拉伯?dāng)?shù)字”?的處理,極大的簡(jiǎn)化和減少了處理對(duì)象。

    9.1???分割算法

    現(xiàn)實(shí)生活中的字符驗(yàn)證碼的產(chǎn)生千奇百怪,有各種扭曲和變形。關(guān)于字符分割的算法,也沒(méi)有很通用的方式。這個(gè)算法也是需要開(kāi)發(fā)人員仔細(xì)研究所要識(shí)別的字符圖片的特點(diǎn)來(lái)制定的。

    當(dāng)然,本文所選的研究對(duì)象盡量簡(jiǎn)化了這個(gè)步驟的難度,下文將慢慢進(jìn)行介紹。

    使用圖像編輯軟件(PhoneShop或者其它)打開(kāi)驗(yàn)證碼圖片,放大到像素級(jí)別,觀察其它一些參數(shù)特點(diǎn):

    可以得到如下參數(shù):

    • 整個(gè)圖片尺寸是 40*10
    • 單個(gè)字符尺寸是 6*10
    • 左右字符和左右邊緣相距2個(gè)像素
    • 字符上下緊挨邊緣(即相距0個(gè)像素)

    這樣就可以很容易就定位到每個(gè)字符在整個(gè)圖片中占據(jù)的像素區(qū)域,然后就可以進(jìn)行分割了,具體代碼如下:

    def get_crop_imgs(img):"""按照?qǐng)D片的特點(diǎn),進(jìn)行切割,這個(gè)要根據(jù)具體的驗(yàn)證碼來(lái)進(jìn)行工作. # 見(jiàn)原理圖:param img::return:"""child_img_list = []for i in range(4):x = 2 + i * (6 + 4) # 見(jiàn)原理圖y = 0child_img = img.crop((x, y, x + 6, y + 10))child_img_list.append(child_img)return child_img_list

    然后就能得到被切割的?原子級(jí)?的圖片元素了:

    9.2???內(nèi)容小結(jié)

    基于本部分的內(nèi)容的討論,相信大家已經(jīng)了解到了,如果驗(yàn)證碼的干擾(扭曲,噪點(diǎn),干擾色塊,干擾線……)做得不夠強(qiáng)的話,可以得到如下兩個(gè)結(jié)論:

    • 4位字符和40000位字符的驗(yàn)證碼區(qū)別不大

    • 純數(shù)字?和?數(shù)字及字母組合?的驗(yàn)證碼區(qū)別不大
      • 純數(shù)字。分類(lèi)數(shù)為10

      • 純字母
        • 不區(qū)分大小寫(xiě)。分類(lèi)數(shù)為26
        • 區(qū)分大小寫(xiě)。分類(lèi)數(shù)為52
      • 數(shù)字和區(qū)分大小寫(xiě)的字母組合。分類(lèi)數(shù)為62

    在沒(méi)有形成?指數(shù)級(jí)或者幾何級(jí)?的難度增加,而只是?線性有限級(jí)?增加計(jì)算量時(shí),意義不太大。

    10???尺寸歸一

    本文所選擇的研究對(duì)象本身尺寸就是統(tǒng)一狀態(tài):6*10的規(guī)格,所以此部分不需要額外處理。但是一些進(jìn)行了扭曲和縮放的驗(yàn)證碼,則此部分也會(huì)是一個(gè)圖像處理的難點(diǎn)。

    11???模型訓(xùn)練步驟

    在前面的環(huán)節(jié),已經(jīng)完成了對(duì)單個(gè)圖片的處理和分割了。后面就開(kāi)始進(jìn)行?識(shí)別模型?的訓(xùn)練了。

    整個(gè)訓(xùn)練過(guò)程如下:

  • 大量完成預(yù)處理并切割到原子級(jí)的圖片素材準(zhǔn)備
  • 對(duì)素材圖片進(jìn)行人為分類(lèi),即:打標(biāo)簽
  • 定義單張圖片的識(shí)別特征
  • 使用SVM訓(xùn)練模型對(duì)打了標(biāo)簽的特征文件進(jìn)行訓(xùn)練,得到模型文件
  • 12???素材準(zhǔn)備

    本文在訓(xùn)練階段重新下載了同一模式的4數(shù)字的驗(yàn)證圖片總計(jì):3000張。然后對(duì)這3000張圖片進(jìn)行處理和切割,得到12000張?jiān)蛹?jí)圖片。

    在這12000張圖片中刪除一些會(huì)影響訓(xùn)練和識(shí)別的強(qiáng)干擾的干擾素材,切割后的效果圖如下:

    13???素材標(biāo)記

    由于本文使用的這種識(shí)別方法中,機(jī)器在最開(kāi)始是不具備任何 數(shù)字的觀念的。所以需要人為的對(duì)素材進(jìn)行標(biāo)識(shí),告訴?機(jī)器什么樣的圖片的內(nèi)容是 1……。

    這個(gè)過(guò)程叫做?“標(biāo)記”

    具體打標(biāo)簽的方法是:

  • 為0~9每個(gè)數(shù)字建立一個(gè)目錄,目錄名稱(chēng)為相應(yīng)數(shù)字(相當(dāng)于標(biāo)簽)

  • 人為判定?圖片內(nèi)容,并將圖片拖到指定數(shù)字目錄中

  • 每個(gè)目錄中存放100張左右的素材

    一般情況下,標(biāo)記的素材越多,那么訓(xùn)練出的模型的分辨能力和預(yù)測(cè)能力越強(qiáng)。例如本文中,標(biāo)記素材為十多張的時(shí)候,對(duì)新的測(cè)試圖片識(shí)別率基本為零,但是到達(dá)100張時(shí),則可以達(dá)到近乎100%的識(shí)別率

  • 14???特征選擇

    對(duì)于切割后的單個(gè)字符圖片,像素級(jí)放大圖如下:

    從宏觀上看,不同的數(shù)字圖片的本質(zhì)就是將黑色按照一定規(guī)則填充在相應(yīng)的像素點(diǎn)上,所以這些特征都是最后圍繞像素點(diǎn)進(jìn)行。

    字符圖片?寬6個(gè)像素,高10個(gè)像素?,理論上可以最簡(jiǎn)單粗暴地可以定義出60個(gè)特征:60個(gè)像素點(diǎn)上面的像素值。但是顯然這樣高維度必然會(huì)造成過(guò)大的計(jì)算量,可以適當(dāng)?shù)慕稻S。

    通過(guò)查閱相應(yīng)的文獻(xiàn)?[2],給出另外一種簡(jiǎn)單粗暴的特征定義:

  • 每行上黑色像素的個(gè)數(shù),可以得到10個(gè)特征
  • 每列上黑色像素的個(gè)數(shù),可以得到6個(gè)特征
  • 最后得到16維的一組特征,實(shí)現(xiàn)代碼如下:

    def get_feature(img):"""獲取指定圖片的特征值,1. 按照每排的像素點(diǎn),高度為10,則有10個(gè)維度,然后為6列,總共16個(gè)維度:param img_path::return:一個(gè)維度為10(高度)的列表"""width, height = img.sizepixel_cnt_list = []height = 10for y in range(height):pix_cnt_x = 0for x in range(width):if img.getpixel((x, y)) == 0: # 黑色點(diǎn)pix_cnt_x += 1pixel_cnt_list.append(pix_cnt_x)for x in range(width):pix_cnt_y = 0for y in range(height):if img.getpixel((x, y)) == 0: # 黑色點(diǎn)pix_cnt_y += 1pixel_cnt_list.append(pix_cnt_y)return pixel_cnt_list

    然后就將圖片素材特征化,按照?libSVM?指定的格式生成一組帶特征值和標(biāo)記值的向量文件。內(nèi)容示例如下:


    說(shuō)明如下:

  • 第一列是標(biāo)簽列,即此圖片人為標(biāo)記值,后續(xù)還有其它數(shù)值1~9的標(biāo)記
  • 后面是16組特征值,冒號(hào)前面是索引號(hào),后面是值
  • 如果有1000張訓(xùn)練圖片,那么會(huì)產(chǎn)生1000行的記錄
  • 對(duì)此文件格式有興趣的同學(xué),可以到?libSVM?官網(wǎng)搜索更多的資料。

    15???模型訓(xùn)練

    到這個(gè)階段后,由于本文直接使用的是開(kāi)源的?libSVM?方案,屬于應(yīng)用了,所以此處內(nèi)容就比較簡(jiǎn)單的。只需要輸入特征文件,然后輸出模型文件即可。

    可以搜索到很多相關(guān)中文資料?[1]?。

    主要代碼如下:

    def train_svm_model():"""訓(xùn)練并生成model文件:return:"""y, x = svm_read_problem(svm_root + '/train_pix_feature_xy.txt')model = svm_train(y, x)svm_save_model(model_path, model)

    備注:生成的模型文件名稱(chēng)為?svm_model_file

    16???模型測(cè)試

    訓(xùn)練生成模型后,需要使用?訓(xùn)練集?之外的全新的標(biāo)記后的圖片作為?測(cè)試集?來(lái)對(duì)模型進(jìn)行測(cè)試。

    本文中的測(cè)試實(shí)驗(yàn)如下:

    • 使用一組全部標(biāo)記為8的21張圖片來(lái)進(jìn)行模型測(cè)試
    • 測(cè)試圖片生成帶標(biāo)記的特征文件名稱(chēng)為?last_test_pix_xy_new.txt

    在早期訓(xùn)練集樣本只有每字符十幾張圖的時(shí)候,雖然對(duì)訓(xùn)練集樣本有很好的區(qū)分度,但是對(duì)于新樣本測(cè)試集基本沒(méi)區(qū)分能力,識(shí)別基本是錯(cuò)誤的。逐漸增加標(biāo)記為8的訓(xùn)練集的樣本后情況有了比較好的改觀:

  • 到60張左右的時(shí)候,正確率大概80%
  • 到185張的時(shí)候,正確率基本上達(dá)到100%
  • 以數(shù)字8的這種模型強(qiáng)化方法,繼續(xù)強(qiáng)化對(duì)數(shù)字0~9中的其它數(shù)字的模型訓(xùn)練,最后可以達(dá)到對(duì)所有的數(shù)字的圖片的識(shí)別率達(dá)到近乎 100%。在本文示例中基本上每個(gè)數(shù)字的訓(xùn)練集在100張左右時(shí),就可以達(dá)到100%的識(shí)別率了。

    模型測(cè)試代碼如下

    def svm_model_test():"""使用測(cè)試集測(cè)試模型:return:"""yt, xt = svm_read_problem(svm_root + '/last_test_pix_xy_new.txt')model = svm_load_model(model_path)p_label, p_acc, p_val = svm_predict(yt, xt, model)#p_label即為識(shí)別的結(jié)果cnt = 0for item in p_label:print('%d' % item, end=',')cnt += 1if cnt % 8 == 0:print('')

    至此,驗(yàn)證的識(shí)別工作算是完滿(mǎn)結(jié)束。

    17???完整識(shí)別流程

    在前面的環(huán)節(jié),驗(yàn)證碼識(shí)別?的相關(guān)工具集都準(zhǔn)備好了。然后對(duì)指定的網(wǎng)絡(luò)上的動(dòng)態(tài)驗(yàn)證碼形成持續(xù)不斷地識(shí)別,還需要另外寫(xiě)一點(diǎn)代碼來(lái)組織這個(gè)流程,以形成穩(wěn)定的黑盒的驗(yàn)證碼識(shí)別接口。

    主要步驟如下:

  • 傳入一組驗(yàn)證碼圖片
  • 對(duì)圖片進(jìn)行預(yù)處理:去噪,二值等等
  • 切割成4張有序的單字符圖片
  • 使用模型文件分別對(duì)4張圖片進(jìn)行識(shí)別
  • 將識(shí)別結(jié)果拼接
  • 返回識(shí)別結(jié)果
  • 然后本文中,請(qǐng)求某網(wǎng)絡(luò)驗(yàn)證碼的http接口,獲得驗(yàn)證碼圖片,識(shí)別出結(jié)果,以此結(jié)果作為名稱(chēng)保存此驗(yàn)證圖片。效果如下:

    顯然,已經(jīng)達(dá)到幾乎?100%?的識(shí)別率了。

    在本算法沒(méi)有做任何優(yōu)化的情況下,在目前主流配置的PC機(jī)上運(yùn)行此程序,可以實(shí)現(xiàn)200ms識(shí)別一個(gè)(很大的耗時(shí)來(lái)自網(wǎng)絡(luò)請(qǐng)求的阻塞)。

    18???效率優(yōu)化

    后期通過(guò)優(yōu)化的方式可以達(dá)到更好的效率。

    軟件層次優(yōu)化

  • 將圖片資源的網(wǎng)絡(luò)請(qǐng)求部分做成異步非阻塞模式
  • 利用好多核CPU,多進(jìn)程并行運(yùn)行
  • 在圖片特征上認(rèn)真挑選和實(shí)驗(yàn),降低維度
  • 預(yù)計(jì)可以達(dá)到1s識(shí)別10到100個(gè)驗(yàn)證碼的樣子。

    硬件層次優(yōu)化

  • 粗暴地增加CPU性能
  • 粗暴地增加運(yùn)行機(jī)器
  • 基本上,10臺(tái)4核心機(jī)器同時(shí)請(qǐng)求,保守估計(jì)效率可以提升到1s識(shí)別1萬(wàn)個(gè)驗(yàn)證碼。

    19???互聯(lián)網(wǎng)安全警示

    如果驗(yàn)證碼被識(shí)別出來(lái)后,會(huì)有什么安全隱患呢?

    在大家通過(guò)上一小節(jié)對(duì)識(shí)別效率有了認(rèn)識(shí)之后,再提到這樣的場(chǎng)景,大家會(huì)有新的看法了吧:

    • 12306火車(chē)售票網(wǎng),春節(jié)期間早上8:00某車(chē)次放出的500張票,1s內(nèi)全部被搶光,最后發(fā)現(xiàn)正常需求的人搶不到票,但是黃牛卻大大的有票
    • 某某手機(jī)網(wǎng)站,早上10:00開(kāi)啟搶購(gòu)活動(dòng),守候了許久的無(wú)數(shù)的你都鎩羽而歸,但是同樣黃牛卻大量有貨

    暫先不管后面有沒(méi)有手續(xù)上的黑幕,在一切手續(xù)合法的情況下,只要通過(guò)技術(shù)手段識(shí)別掉了驗(yàn)證碼,再通過(guò)計(jì)算機(jī)強(qiáng)大的計(jì)算力和自動(dòng)化能力,將大量資源搶到少數(shù)黃牛手中在技術(shù)是完全可行的。

    所以今后大家搶不到票不爽的時(shí)候,可以繼續(xù)罵12306,但是不要罵它有黑幕了,而是罵他們IT技術(shù)不精吧。

    關(guān)于一個(gè)驗(yàn)證碼失效,即相當(dāng)于沒(méi)有驗(yàn)證碼的系統(tǒng),再?zèng)]有其它風(fēng)控策略的情況下,那么這個(gè)系統(tǒng)對(duì)于代碼程序來(lái)就就完全如入無(wú)人之境。

    具體請(qǐng)參考:

    Web應(yīng)用系統(tǒng)的小安全漏洞及相應(yīng)的攻擊方式

    http://www.cnblogs.com/beer/p/4814587.html

    通過(guò)上面的例子,大家可以看到:

  • 目前確實(shí)有一些web應(yīng)用系統(tǒng)連驗(yàn)證碼都沒(méi)有,只能任人宰割
  • 即使web應(yīng)用系統(tǒng)有驗(yàn)證碼但是難度不夠,也只能任人宰割
  • 所以,這一塊雖然小,但是安全問(wèn)題不能忽視。

    20???積極應(yīng)用場(chǎng)景

    本文介紹的其實(shí)是一項(xiàng)簡(jiǎn)單的OCR技術(shù)實(shí)現(xiàn)。有一些很好同時(shí)也很有積極進(jìn)步意義的應(yīng)用場(chǎng)景:

    • 銀行卡號(hào)識(shí)別
    • 身份證號(hào)識(shí)別
    • 車(chē)牌號(hào)碼識(shí)別

    這些場(chǎng)景有具有和本文所研究素材很相似的特點(diǎn):

  • 字體單一
  • 字符為簡(jiǎn)單的數(shù)字或字母組合
  • 文字的排列是標(biāo)準(zhǔn)化統(tǒng)一化的
  • 所以如果拍照時(shí)原始數(shù)據(jù)采集比較規(guī)范的情況下,識(shí)別起來(lái)應(yīng)該難度也不大。

    21???小結(jié)

    本文只是選取了一個(gè)比較典型的而且比較簡(jiǎn)單的驗(yàn)證碼的識(shí)別作為示例,但是基本上能表述出一個(gè)識(shí)別此類(lèi)驗(yàn)證碼的完整流程,可以供大家交流學(xué)習(xí)。

    由于目前全球的IT技術(shù)實(shí)力參差不齊,現(xiàn)在很多舊的IT系統(tǒng)里面都存在一些舊的頁(yè)面框架,里面使用的驗(yàn)證碼也是相當(dāng)古老,對(duì)于當(dāng)下的一些識(shí)別技術(shù)來(lái)說(shuō),完全不堪一擊。比如,我看到一些在校大學(xué)生就直接拿自己學(xué)校的?教務(wù)系統(tǒng)?的驗(yàn)證碼來(lái)?開(kāi)刀練習(xí)?的。

    最后,本文特意提出如下倡議:

  • 對(duì)于掌握OCR技術(shù)的人
    • 不要做違法的事,因?yàn)槟壳氨蛔サ摹鞍酌弊印钡男侣勔残U多的
    • 在不違法的情況下,還是可以向存在漏洞的系統(tǒng)管理員提出善意提醒
    • 以自己的專(zhuān)業(yè)知識(shí),多做一些促進(jìn)社會(huì)進(jìn)步,提升社會(huì)生產(chǎn)力的事情,如紙書(shū)電子化等等
  • 對(duì)于仍然沿用舊的落后的IT系統(tǒng)的公司或者機(jī)構(gòu)相關(guān)人員

    應(yīng)該盡快認(rèn)識(shí)到事情的嚴(yán)重性,趕緊升級(jí)自己的系統(tǒng),或者將這一塊業(yè)務(wù)交付給專(zhuān)門(mén)的安全公司

  • 22???參考資料

    [1]LibSVM for Python 使用?http://www.cnblogs.com/Finley/p/5329417.html
    [2]基于SVM的手寫(xiě)體阿拉伯?dāng)?shù)字識(shí)別.張鴿,陳書(shū)開(kāi).長(zhǎng)沙理工大學(xué)計(jì)算機(jī)通訊工程學(xué)院.2005

    23???最后題外話

    我估計(jì)這樣?長(zhǎng)文?絕大部分人是不會(huì)有興趣全部看完的。但為了它的內(nèi)容完整性,還是決定先以整篇的方式發(fā)表出來(lái)吧。

    后面有空再拆分連載吧。

    ?

    源碼開(kāi)源共享:

    https://github.com/zhengwh/captcha-svm

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/jyxbk/p/7521120.html

    總結(jié)

    以上是生活随笔為你收集整理的字符识别Python实现 图片验证码识别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。