訂閱
糾錯(cuò)
加入自媒體

如何對(duì)圖像進(jìn)行去噪來(lái)繞過(guò)驗(yàn)證碼的方法

?通過(guò)使用廣度優(yōu)先搜索算法對(duì)圖像進(jìn)行去噪來(lái)繞過(guò)驗(yàn)證碼的新方法

驗(yàn)證碼被廣泛用作確保對(duì)系統(tǒng)執(zhí)行的操作是由人類(lèi)而不是機(jī)器人完成的一種方式。然而,這種方法并不是萬(wàn)無(wú)一失的,尤其是在OCR和計(jì)算機(jī)視覺(jué)技術(shù)如此發(fā)達(dá)的今天。讓我們從用戶(hù)的角度來(lái)看,每次想要訪問(wèn)某個(gè)網(wǎng)站時(shí)都必須解決驗(yàn)證碼是一件非常痛苦的事情,尤其是當(dāng)你每天都需要這樣做時(shí)!在本文中,將探索我自己的繞過(guò)特定類(lèi)型驗(yàn)證碼的方法。該方法利用廣度優(yōu)先搜索 (BFS) 和OpenCV在將圖像傳遞給OCR引擎 (Tesseract) 之前對(duì)圖像進(jìn)行去噪。由于有太多不同類(lèi)型的文本驗(yàn)證碼具有不同形式的失真/增強(qiáng),因此我的方法僅適用于我試圖繞過(guò)的特定驗(yàn)證碼源。但也許你會(huì)在我的文章中找到一些對(duì)你自己解決驗(yàn)證碼問(wèn)題有用的細(xì)節(jié)!驗(yàn)證碼

正如我所提到的,有許多不同類(lèi)型的文本驗(yàn)證碼。我試圖繞過(guò)的驗(yàn)證碼的變化對(duì)單詞應(yīng)用傾斜失真,同時(shí)用雪狀圖案覆蓋圖像。從根本上(并且幸運(yùn)的是),這種類(lèi)型的驗(yàn)證碼并不太難解決。這是因?yàn)轵?yàn)證碼文本本身沒(méi)有任何噪音,可以被 OCR 引擎識(shí)別。如果文本被進(jìn)一步扭曲,像 Tesseract 這樣的開(kāi)源 OCR 庫(kù)將無(wú)法讀取文本。因此,我們現(xiàn)在需要做的是一系列圖像過(guò)濾以去除所有噪聲并僅保留驗(yàn)證碼文本。方法我將要討論的方法具有概率特性,這意味著它不能保證在每次試驗(yàn)中都有效。但是,我們可以利用驗(yàn)證碼和系統(tǒng)的一些先驗(yàn)知識(shí)來(lái)確保我們最大化驗(yàn)證碼的成功概率。但首先,讓我們談?wù)勻?yàn)證碼圖像的去噪方法。該過(guò)程可以概括為以下步驟:轉(zhuǎn)換為灰度中值濾波器(內(nèi)核大小 3)圖像閾值處理島嶼去除中值濾波器(內(nèi)核大小 3)

圖像首先轉(zhuǎn)換為灰度,以將通道數(shù)減少到僅 1。然后注意到在現(xiàn)有的隨機(jī)雪花噪聲之上放置了一致的明暗像素點(diǎn)圖案。中值濾波器可以有效去除這種密集和重復(fù)的噪聲模式。盡管有輕微的模糊,但經(jīng)過(guò)中值濾波器后的圖像更加清晰。我們要做的下一步是對(duì)圖像進(jìn)行閾值處理,將所有像素強(qiáng)度推到 1 或 0。閾值通過(guò)反復(fù)試驗(yàn)進(jìn)行微調(diào),以確保保留文本的所有像素。

在此之后,我們留下了驗(yàn)證碼文本,周?chē)屈c(diǎn)狀噪聲,這是閾值化的殘留物。這些點(diǎn)分散在圖像周?chē)?但更多地集中在文本周?chē)_@就是使用 BFS 的地方,因?yàn)槲覀儗⑹褂眠@種我稱(chēng)之為“島嶼去除”的方法來(lái)去除所有的點(diǎn)狀噪聲。該方法訪問(wèn)了圖像上的所有黑色像素。在每個(gè)黑色像素上,它使用 BFS 找到所有也是黑色的鄰居。

本質(zhì)上,該函數(shù)識(shí)別圖像上的所有黑色像素簇,如果簇大小小于預(yù)定閾值,則將其移除(即轉(zhuǎn)換為白色像素)。這種“去除島嶼”方法的靈感來(lái)自于相當(dāng)流行的編程問(wèn)題Number of Islands,你應(yīng)該使用 BFS 或 DFS 來(lái)計(jì)算二維數(shù)組(即島嶼)中 1 的簇?cái)?shù)。最后一步是應(yīng)用另一個(gè)內(nèi)核大小為 3 的中值濾波器來(lái)平滑圖像的邊緣。然后,它準(zhǔn)備好傳遞到 Tesseract OCR 進(jìn)行文本提取。從上面的步驟圖可以看出,去噪過(guò)程產(chǎn)生了相當(dāng)積極的輸出。最重要的是,Tesseract 能夠識(shí)別輸出正確驗(yàn)證碼文本的單詞就足夠了。下面是去噪的代碼片段。

def bfs(visited, queue, array, node):
   # I make BFS itterative instead of recursive
   def getNeighboor(array, node):
       neighboors = []
       if node[0]+1           if array[node[0]+1,node[1]] == 0:
               neighboors.a(chǎn)ppend((node[0]+1,node[1]))
       if node[0]-1>0:
           if array[node[0]-1,node[1]] == 0:
               neighboors.a(chǎn)ppend((node[0]-1,node[1]))
       if node[1]+1           if array[node[0],node[1]+1] == 0:
               neighboors.a(chǎn)ppend((node[0],node[1]+1))
       if node[1]-1>0:
           if array[node[0],node[1]-1] == 0:
               neighboors.a(chǎn)ppend((node[0],node[1]-1))
       return neighboors
   queue.a(chǎn)ppend(node)
   visited.a(chǎn)dd(node)
   while queue:
       current_node = queue.pop(0)
       for neighboor in getNeighboor(array, current_node):
           if neighboor not in visited:
   #             print(neighboor)
               visited.a(chǎn)dd(neighboor)
               queue.a(chǎn)ppend(neighboor)
def removeIsland(img_arr, threshold):
   # !important: the black pixel is 0 and white pixel is 1
   while 0 in img_arr:
       x,y = np.where(img_arr == 0)
       point = (x[0],y[0])
       visited = set()
       queue = []
       bfs(visited, queue, img_arr, point)
       
       if len(visited) <= threshold:
           for i in visited:
               img_arr[i[0],i[1]] = 1
       else:
           # if the cluster is larger than threshold (i.e is the text),
           # we convert it to a temporary value of 2 to mark that we
           # have visited it.
           for i in visited:
               img_arr[i[0],i[1]] = 2
               
   img_arr = np.where(img_arr==2, 0, img_arr)
   return img_arr
img  = cv2.imread("temp.png")
# Convert to grayscale
c_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# Median filter
kernel = np.ones((3,3),np.uint8)
out = cv2.medianBlur(c_gray,3)
# Image thresholding
a = np.where(out>195, 1, out)
out = np.where(a!=1, 0, a)
# Islands removing with threshold = 30
out = removeIsland(out, 30)
# Median filter
out = cv2.medianBlur(out,3)
# Convert to Image type and pass it to tesseract
im = Image.fromarray(out*255)
print(pytesseract.image_to_string(im))

結(jié)果上面顯示的示例是該方法性能較好的結(jié)果之一。就像我之前說(shuō)的,方法是要看概率的。因此,對(duì)于許多輸入,它將無(wú)法輸出正確的驗(yàn)證碼。這是關(guān)于驗(yàn)證碼的先驗(yàn)知識(shí)派上用場(chǎng)的地方,因?yàn)槲覀兛梢允褂盟鼈儊?lái)創(chuàng)建過(guò)濾器檢查,以確保預(yù)測(cè)的驗(yàn)證碼文本滿足這些基本標(biāo)準(zhǔn)。

如果方法輸出未通過(guò)檢查,我們肯定知道它預(yù)測(cè)錯(cuò)誤,只需刷新即可獲得新的驗(yàn)證碼樣本。我們可以通過(guò)查看驗(yàn)證碼數(shù)據(jù)集得出的一些先驗(yàn)信息是:它包含正常和大寫(xiě)的字母數(shù)字字符。驗(yàn)證碼字符串大小為 4 或 5。因此,我們可以得出以下檢查來(lái)確保預(yù)測(cè)的驗(yàn)證碼:必須僅包含字母數(shù)字字符。長(zhǎng)度在 4 到 5 之間。盡管此過(guò)濾器檢查并不能保證預(yù)測(cè)的驗(yàn)證碼始終正確,但它有助于增加預(yù)測(cè)文本正確的可能性。

以上是此方法在輸出正確驗(yàn)證碼之前進(jìn)行的試驗(yàn)次數(shù)分布的直方圖。數(shù)據(jù)是我自己記錄的,因?yàn)槲覀(gè)人每天都使用這種方法超過(guò) 3 個(gè)月?梢钥闯,大多數(shù)試驗(yàn)次數(shù)低于 20,精確平均值為 9.02。結(jié)論本文中介紹的方法有一定的優(yōu)缺點(diǎn)。優(yōu)點(diǎn)是該方法不需要任何訓(xùn)練,因此不需要標(biāo)記數(shù)據(jù)集。計(jì)算速度快,實(shí)現(xiàn)簡(jiǎn)單。然而,缺點(diǎn)是該方法是概率性的,因此對(duì)于在多次錯(cuò)誤嘗試驗(yàn)證碼后阻止用戶(hù)的系統(tǒng),使用這種方法可能會(huì)鎖定你的帳戶(hù)。此外,該方法針對(duì)非常特定類(lèi)型的驗(yàn)證碼,需要微調(diào),甚至可能不適用于其他類(lèi)型的驗(yàn)證碼。

聲明: 本文由入駐維科號(hào)的作者撰寫(xiě),觀點(diǎn)僅代表作者本人,不代表OFweek立場(chǎng)。如有侵權(quán)或其他問(wèn)題,請(qǐng)聯(lián)系舉報(bào)。

發(fā)表評(píng)論

0條評(píng)論,0人參與

請(qǐng)輸入評(píng)論內(nèi)容...

請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字

您提交的評(píng)論過(guò)于頻繁,請(qǐng)輸入驗(yàn)證碼繼續(xù)

  • 看不清,點(diǎn)擊換一張  刷新

暫無(wú)評(píng)論

暫無(wú)評(píng)論

    掃碼關(guān)注公眾號(hào)
    OFweek人工智能網(wǎng)
    獲取更多精彩內(nèi)容
    文章糾錯(cuò)
    x
    *文字標(biāo)題:
    *糾錯(cuò)內(nèi)容:
    聯(lián)系郵箱:
    *驗(yàn) 證 碼:

    粵公網(wǎng)安備 44030502002758號(hào)