[Python] 圖片降噪

圖片驗證碼識別的可以分為幾個步驟,一般用 Pillow 庫或 OpenCV 來實現,取得圖片驗證碼文字的處理流程:

  • 1.灰度處理&二值化
  • 2.降噪
    所謂降噪就是把不需要的信息通通去除,比如背景,干擾線,干擾像素等等,只留下需要識別的字符,讓圖片變成2進制點陣,方便代入模型訓練。
  • 3.字符分割
  • 4.標準化
  • 5.識別 (OCR)

參考了這部教學影片:
[爬蟲實戰] 如何破解高鐵驗證碼 (1) – 去除圖片噪音點?
https://www.youtube.com/watch?v=6HGbKdB4kVY


python 用pip安装 cv2:

pip install opencv-python

說明:macOS 預設 python2 裡 numpy 的版本 numpy-1.8.0rc1,但 opencv 需要 1.11.1 以上,所以會安裝失敗,顯示:

ERROR: Cannot uninstall 'numpy'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.

解法:

sudo python -m pip install numpy –ignore-installed numpy
sudo python -m pip install opencv-python

python 用pip安装 matplotlib:

pip install matplotlib

python 用pip安装 pytesseract:

pip install pytesseract

Tesseract OCR 安裝方法:
https://github.com/tesseract-ocr/tesseract/wiki


透過 selenium 的 element.screenshot() 即可取得驗證碼圖片:
https://selenium-python-zh.readthedocs.io/en/latest/api.html?highlight=screenshot#selenium.webdriver.remote.webelement.WebElement.screenshot

screenshot(filename)Saves a screenshot of the current element to a PNG image file. ReturnsFalse if there is any IOError, else returns True. Use full paths in your filename.

Args:filename: The full path you wish to save your screenshot to. This should end with a .png extension.
Usage:element.screenshot(‘/Screenshots/foo.png’)

Max取得到 screenshot:

透過下面的python程式:

from PIL import Image,ImageDraw
image = Image.open("captcha.png").convert("L")

取得convert 為灰階後影像檔:

透過下面程式,用來2分化影像:

def binarizing(img,threshold): #input: gray image
     pixdata = img.load()
     w, h = img.size
     for y in range(h):
       for x in range(w):
           if pixdata[x, y] < threshold:
               pixdata[x, y] = 0
           else:
               pixdata[x, y] = 255
     return img

降噪方案1:(不好用)

def depoint(img):   #input: gray image
     pixdata = img.load()
     w,h = img.size
     for y in range(1,h-1):
         for x in range(1,w-1):
             count = 0
             if pixdata[x,y-1] > 245:
                 count = count + 1
             if pixdata[x,y+1] > 245:
                 count = count + 1
             if pixdata[x-1,y] > 245:
                 count = count + 1
             if pixdata[x+1,y] > 245:
                 count = count + 1
             if count > 2:
                 pixdata[x,y] = 255
     return img

似乎只有使用 上/下/左/右 4個方向 shift 1點來判斷,如果遇到較大的噪會無效,處理結果:

效果似乎接近無效,因為黑點都略大。

改服用下面的程式碼:

#二值判断,如果确认是噪声,用改点的上面一个点的灰度进行替换
#该函数也可以改成RGB判断的,具体看需求如何
def getPixel(image,x,y,G,N):
    L = image.getpixel((x,y))
    if L > G:
        L = True
    else:
        L = False
 
    nearDots = 0
    if L == (image.getpixel((x - 1,y - 1)) > G):
        nearDots += 1
    if L == (image.getpixel((x - 1,y)) > G):
        nearDots += 1
    if L == (image.getpixel((x - 1,y + 1)) > G):
        nearDots += 1
    if L == (image.getpixel((x,y - 1)) > G):
        nearDots += 1
    if L == (image.getpixel((x,y + 1)) > G):
        nearDots += 1
    if L == (image.getpixel((x + 1,y - 1)) > G):
        nearDots += 1
    if L == (image.getpixel((x + 1,y)) > G):
        nearDots += 1
    if L == (image.getpixel((x + 1,y + 1)) > G):
        nearDots += 1
 
    if nearDots < N:
        return image.getpixel((x,y-1))
    else:
        return None
 
# 降噪 
# 根据一个点A的RGB值,与周围的8个点的RBG值比较,设定一个值N(0 <N <8),当A的RGB值与周围8个点的RGB相等数小于N时,此点为噪点 
# G: Integer 图像二值化阀值 
# N: Integer 降噪率 0 <N <8 
# Z: Integer 降噪次数 
# 输出 
#  0:降噪成功 
#  1:降噪失败 
def clearNoise(image,G,N,Z):
    draw = ImageDraw.Draw(image)
 
    for i in range(0,Z):
        for x in range(1,image.size[0] - 1):
            for y in range(1,image.size[1] - 1):
                color = getPixel(image,x,y,G,N)
                if color != None:
                    draw.point((x,y),color)

    image.save("test4_clearNoise.jpg")


image = Image.open("captcha.png").convert("L")
image_binary = binarizing(image, 180)
clearNoise(image_binary,50,4,6)

結果是有比較好:

但上面的圖片,還是「無法」使用pytesseract 進行OCR 辨視成功:

code = pytesseract.image_to_string(image_binary, config='digits')
print("code:", code)

改用OpenCV 看看,OpenCV提供了這種技術的四種去噪方法:
https://docs.opencv.org/3.0-beta/modules/photo/doc/denoising.html

  • cv2.fastNlMeansDenoising() – 使用單個灰度影象
  • cv2.fastNlMeansDenoisingColored() – 使用彩色影象。
  • cv2.fastNlMeansDenoisingMulti() – 用於在短時間內捕獲的影象序列(灰度影象)
  • cv2.fastNlMeansDenoisingColoredMulti() – 與上面相同,但用於彩色影象。

基本的 opencv 使用範例:

import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('die.png')
dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
plt.subplot(121),plt.imshow(img)
plt.subplot(122),plt.imshow(dst)
plt.show()

總結

Tesseract-ORC 對於這種弱驗證碼識別率還是可以,大部分字符能夠正確識別出來。只不過有時候會將數字 8 識別為 0。如果圖片驗證碼稍微變得復雜點,識別率大大降低,會經常識別不出來的情況。

如果想要做到識別率較高,那麽需要使用 CNN (Convolutional Neural Network,卷積神經網絡)或者 RNN (Recurrent Neural Network,循環神經網絡)訓練出自己的識別庫。正好機器學習很流行,學習一下也無妨。


卷積神經網路(Convolutional Neural Network, CNN)

CNN 也是模仿人類大腦的認知方式,譬如我們辨識一個圖像,會先注意到顏色鮮明的點、線、面,之後將它們構成一個個不同的形狀(眼睛、鼻子、嘴巴…),這種抽象化的過程就是CNN演算法建立模型的方式。卷積層(Convolution Layer) 就是由點的比對轉成局部的比對,透過一塊塊的特徵研判,逐步堆疊綜合比對結果,就可以得到比較好的辨識結果,過程如下圖。

卷積層(Convolution Layer)

如何從點轉成面呢? 就是以圖像的每一點為中心,取周遭 N x N 格的點構成一個面(N 稱為 Kernel Size,N x N 的矩陣權重稱為『卷積核』),每一格給予不同的權重,計算加權總和,當作這一點的 output,再移動至下一點以相同方式處理,至圖像的最後一點為止,這就是 CNN 的卷積層(Convolution Layer)。

相關文章:CS231n: Convolutional Neural Networks for Visual Recognition 
http://cs231n.github.io/convolutional-networks/
上面文章的「Convolution Demo」段落,它以動畫的方式說明取樣的方式,有助於更容易了解其原理。

卷積層處理方式與影像處理方法類似,採用滑動視窗(Sliding Window)運算,藉由給予『卷積核』不同的權重組合,就可以偵測形狀的邊、角,也有去除噪音(Noise)及銳化(Sharpen)的效果,萃取這些特徵當作辨識的依據,這也克服了迴歸(Regression)會受『異常點』(Outliers)嚴重影響推測結果的缺點。

資料來源:
Day 06:處理影像的利器 — 卷積神經網路(Convolutional Neural Network)
https://ithelp.ithome.com.tw/articles/10191820


循環神經網路(Recurrent Neural Network, RNN)

語言通常要考慮前言後語,以免斷章取義,也就是說,建立語言的相關模型,如果能額外考慮上下文的關係,準確率就會顯著提高,因此,學者提出『循環神經網路』(Recurrent Neural Network, RNN)演算法,它是『自然語言處理』領域最常使用的 Neural Network 模型。


相關文章:

速記AI課程-深度學習入門(二)
https://medium.com/@baubibi/%E9%80%9F%E8%A8%98ai%E8%AA%B2%E7%A8%8B-%E6%B7%B1%E5%BA%A6%E5%AD%B8%E7%BF%92%E5%85%A5%E9%96%80-%E4%BA%8C-954b0e473d7f

我發現,網路上很多AI相關的「入門」等級的教學文章,還滿容易懂和實作的。


相關影片:

1:[爬蟲實戰] 如何使用Selenium 抓取驗證碼?
https://www.youtube.com/watch?v=hF-dJj559ug

2:[爬蟲實戰] 如何破解高鐵驗證碼 (1) – 去除圖片噪音點?
https://www.youtube.com/watch?v=6HGbKdB4kVY

3:[爬蟲實戰] 如何破解高鐵驗證碼 (2) – 使用迴歸方法去除多餘弧線?
https://www.youtube.com/watch?v=4DHcOPSfC4c

上面影片的原始碼:
實作基於CNN的台鐵訂票驗證碼辨識以及透過模仿及資料增強的訓練集產生器 (Simple captcha solver based on CNN and a training set generator by imitating the style of captcha and data augmentation)
https://github.com/JasonLiTW/simple-railway-captcha-solver

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *