在邦購登陸時,選擇了人工檢驗驗證碼,這次用機器檢測試試。
先說基本邏輯:載入圖像,轉(zhuǎn)灰度,二值化,連通域檢測,去除連通域小的,根據(jù)各連通域的范圍切割圖像。
先下載圖片。
def getcaptcha():
for i in range(1,10):
with open('test1-{}.png'.format(i),'wb') as f:
url ='https://passport.banggo.com/CASServer//custom/loginCode.do'
res = requests.get(url,stream =True)
f.write(res.content)
得到的驗證碼長這樣:

可以看到驗證碼有干擾線,好在干擾線比較細(xì),而且驗證碼之間沒有粘連??纯磩e家怎么弄得,在python驗證碼識別
寫到了一些驗證碼識別的方法,但是幾乎都是針對像素級別,不過提供了一個思路叫連通域,就是一個筆畫里含有的像素較多,刪除那些小的聯(lián)通域就能去除干擾線了。
我看到的大多是自己寫的像素級別的函數(shù),既然這是降噪領(lǐng)域的主要思想,就會有現(xiàn)成的輪子啊,直到我發(fā)現(xiàn)了scikit-image,有自己的文檔,這篇文章進(jìn)行了少量總結(jié)api總結(jié),搬運如下

其中有專門針對連通域的api,
from skimage import measure
labels = measure.label(二值圖像,connectivity=None)
通過label_att = measure.regionprops(labels)得到各連通域的屬性如下。label_att是一個list,有幾個連通域就有幾項。

更好的是skimage還提供了剔除較小連通域的函數(shù),
from skimage import morphology
img1 = morphology.remove_small_objects(ar, min_size=要刪除的連通域大小閾值, connectivity=1,in_place=False)
怎么知道連通域的大小呢,就是label屬性里的area,第二個bbox用來切割圖像簡直不要太方便。
二者聯(lián)合起來就是現(xiàn)成的去掉干擾線+切割圖像的方法。
from skimage import io,filters,measure,morphology
import warnings
from matplotlib import pyplot as plt
def skim():
img = io.imread('1.png',as_gray=True) #變成灰度圖
thresh = filters.threshold_otsu(img) #自動確定二值化的閾值
bwimg =(img<=thresh) #留下小于閾值的部分,及黑的部分
b = morphology.remove_small_objects(bwimg, 40) #去掉小于40的連通域,可以先全局看看連通域的大小和位置后決定去掉的閾值
labels = measure.label(b)
label_att = measure.regionprops(labels)
arr =[]
for la in label_att:
(x,y,w,h) =la.bbox
#print((x,y,w,h))
bei = round((h-y)/15)
if bei >1: #寬度超過30像素的,說明有粘連,從中切開
for i in range(bei):
arr.append((x, y+round((h-y)/2)*i, w, y+round((h-y)/2)*(i+1)))
elif (y>10) and (h<100) :
arr.append((x, y, w, h))
b =b*img
b[np.where(b != 0)] = 1
with warnings.catch_warnings():
warnings.simplefilter("ignore")
fig = plt.figure()
for id, (x, y, w, h) in enumerate(arr):
roi = b[x:w,y:h]
thr = roi.copy()
# io.imsave('seg1-{}.jpg',thr)
ax = fig.add_subplot(1,4,id+1)
ax.imshow(thr)
plt.show()
反思:
1.這個驗證碼相對規(guī)整,可以發(fā)現(xiàn)他家的驗證碼多是驗證碼本身是有色彩的,而干擾線一直是黑色的,可以直接將小于15的像素變成255,效果也不錯,但是出現(xiàn)的問題是會把驗證碼本身連通域打斷,需要再次填充。
def ty():
# for i in range(1,10):
img = io.imread('{}.png'.format(4))
img[np.where(img>235)] =255
img[np.where(img < 15)] = 255
imgray = color.rgb2gray(img)
thresh = filters.threshold_otsu(imgray)
# bwimg =(imgray <= thresh)
bwimg = morphology.closing(imgray <= thresh, morphology.square(3))
b = morphology.remove_small_objects(bwimg, 20)
labels = measure.label(b)
label_att = measure.regionprops(labels)
arr =[]
for la in label_att:
(x, y, w, h) = la.bbox
bei = round((h - y) / 15)
if bei > 1:
for i in range(bei):
arr.append((x, y + round((h - y) / 2) * i, w, y + round((h - y) / 2) * (i + 1)))
elif (y > 10) and (h < 100) :
arr.append((x, y, w, h))
if (len(arr)>1) and (h-y)<12 and (arr[-2][3]-arr[-2][1] <12): #干擾線去狠了導(dǎo)致斷開驗證碼斷開了要拼接一下
arr[-1] = ((x, arr[-2][1], w, h))
del arr[-2]
b = b * imgray
b[np.where(b != 0)] = 1
with warnings.catch_warnings():
warnings.simplefilter("ignore")
fig = plt.figure()
for id, (x, y, w, h) in enumerate(arr):
roi = b[x:w, y:h]
thr = roi.copy()
# io.imsave('new{}-{}.png'.format(1,id+1), thr)
ax = fig.add_subplot(1, len(arr), id + 1)
ax.imshow(thr)
plt.show()
2.驗證碼相對規(guī)整,而且放置位置也相對固定,可以直接用經(jīng)驗值切割,但是有些比較胖就比較麻煩了。
3.在使用skimage時,確是遇到二值圖直接轉(zhuǎn)rgb會變成的黑圖或者很灰的圖,像這樣。
b = b * imgray
b[np.where(b != 0)] = 1
就是把圖調(diào)亮,因為float類型,[0,1]0是黑色,1是白色,這樣存的圖就是黑白的了。
另在float轉(zhuǎn)rgb的時候會引發(fā)warnings說,向下保存會引起精度下降,用with就好了,官方文檔推薦。
4.驗證碼需觀察其特點對癥下藥比較快。