python驗證碼識別教程之利用投影法、連通域法分割圖片

來源:互聯網
上載者:User
這篇文章主要介紹了關於python驗證碼識別教程之利用投影法、連通域法分割圖片,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

前言

今天這篇文章主要記錄一下如何切分驗證碼,用到的主要庫就是Pillow和Linux下的影像處理工具GIMP。首先假設一個固定位置和寬度、無粘連、無幹擾的例子學習一下如何使用Pillow來切割圖片。

使用GIMP開啟圖片後,按 加號 放大圖片,然後點擊View->Show Grid來顯示網格線:


其中,每個正方形邊長為10像素,所以數字1切割座標為左20、上20、右40、下70。以此類推可以知道剩下3個數位切割位置。

代碼如下:

from PIL import Imagep = Image.open("1.png")# 注意位置順序為左、上、右、下cuts = [(20,20,40,70),(60,20,90,70),(100,10,130,60),(140,20,170,50)]for i,n in enumerate(cuts,1): temp = p.crop(n) # 調用crop函數進行切割 temp.save("cut%s.png" % i)

切割後得到4張圖片:


那麼,如果字元位置不固定怎麼辦呢?現在假設一種隨機位置寬度、無粘連、無幹擾線的情況。

第一種方法,也是最簡單的方法叫做”投影法”。原理就是將二值化後的圖片在豎直方向進行投影,根據投影后的極值來判斷分割邊界。這裡我依然使用上面的驗證碼圖片來進行示範:

def vertical(img): """傳入二值化後的圖片進行垂直投影""" pixdata = img.load() w,h = img.size ver_list = [] # 開始投影 for x in range(w): black = 0 for y in range(h):  if pixdata[x,y] == 0:  black += 1 ver_list.append(black) # 判斷邊界 l,r = 0,0 flag = False cuts = [] for i,count in enumerate(ver_list): # 閾值這裡為0 if flag is False and count > 0:  l = i  flag = True if flag and count == 0:  r = i-1  flag = False  cuts.append((l,r)) return cutsp = Image.open('1.png')b_img = binarizing(p,200)v = vertical(b_img)

通過vertical函數我們就得到了一個包含所有黑色像素在X軸上投影后左右邊界的位置。由於驗證碼沒有任何幹擾,所以我的閾值設定為0。 關於binarizing函數可以參考上一篇文章

輸出如下:

[(21, 37), (62, 89), (100, 122), (146, 164)]

可以看到,投影法給出左右邊界和我們手工查看得到很接近。對於上下邊界,偷懶的可以直接使用0和圖片的高度,也可以在水平方向進行投影,這裡有興趣的小夥伴可以自己嘗試。

但是,對於字元間有粘連的情況,投影法就會出現拆分錯誤,比如上篇文章中的:


修改閾值為5後,投影法給出的左右邊界是:

[(5, 27), (33, 53), (59, 108)]

明顯最後的6和9數字沒有切割。

修改閾值為7,結果則是:

[(5, 27), (33, 53), (60, 79), (83, 108)]

所以對於簡單粘連的情況,調整閾值也是可以解決的。

第二種方法,叫做CFS連通域分割法。原理就是假定每個字元都由一個單獨的連通域組成,換言之就是無粘連,找到一個黑色像素並開始判斷,直到所有相連的黑色像素都被遍曆標記過後即可判斷出這個字元的分割位置。演算法如下:

  • 將二值化後的圖片進行從左至右、從上到下的遍曆,如果遇到黑色像素並且這個像素沒有沒訪問過,就將這個像素入棧並標記為已經訪問。

  • 如果棧不為空白,則繼續探測周圍8個像素,並執行第2步;如果棧空,則代表探測完了一個字元塊。

  • 探測結束,這樣就確定了若干字元。

代碼如下:

import queuedef cfs(img): """傳入二值化後的圖片進行連通域分割""" pixdata = img.load() w,h = img.size visited = set() q = queue.Queue() offset = [(-1,-1),(0,-1),(1,-1),(-1,0),(1,0),(-1,1),(0,1),(1,1)] cuts = [] for x in range(w):  for y in range(h):   x_axis = []   #y_axis = []   if pixdata[x,y] == 0 and (x,y) not in visited:    q.put((x,y))    visited.add((x,y))   while not q.empty():    x_p,y_p = q.get()    for x_offset,y_offset in offset:     x_c,y_c = x_p+x_offset,y_p+y_offset     if (x_c,y_c) in visited:      continue     visited.add((x_c,y_c))     try:      if pixdata[x_c,y_c] == 0:       q.put((x_c,y_c))       x_axis.append(x_c)       #y_axis.append(y_c)     except:      pass   if x_axis:    min_x,max_x = min(x_axis),max(x_axis)    if max_x - min_x > 3:     # 寬度小於3的認為是噪點,根據需要修改     cuts.append((min_x,max_x)) return cuts

調用後輸出結果和使用投影法是一樣的。另外我看網上還有一種叫做“泛洪填充(Flood Fill)”的方法,似乎和連通域是一樣的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.