2020年7月27日 星期一

[Python+OpenCV] Build Document Scanner

原文出處  www.pyimagesearch.com

本文為紀錄心得與添加學習過程中的註解。 

How to Build a Document Scanner in Just 5 Minutes


如何將文件變成像是掃描的效果?

如果你手邊有些筆記MEMO、收據、明細等等之類的小物,有時想要整理一下變得井然有序,

作為練習OpenCV, 這是一個很不錯的練習範例。

先來看看結果,圖片前後對比:



     





要達成這個效果,必要步驟如下:

1. 偵測圖像邊緣

2. 透過邊緣去找出--要被轉成掃描效果的紙張輪廓

3. 套用透視變換取得鳥瞰圖(俯視)



偵測圖像邊緣的必要步驟

◎ 轉為灰階

◎ 運用高斯模糊

◎ Canny方法

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5,5), 0) # 這裡有關鍵細節...
# 高斯模糊: cv2.GaussianBlur(img, ksize, sigmaX, sigmaY, borderType)
# ksize: 濾波核的大小, 必須是奇數。 如果數值越大就越模糊。
# sigmaX: 是卷積核在X軸上的標準差, 通常與sigmaY都可設為0
# borderType: 邊界類型, 直接採用預設值即可。

edged = cv2.Canny(gray, 75200)


確認取得邊緣




找出圖形輪廓並將所有輪廓排序,只保留前5個最大的輪廓。 (但為什麼是5? 不是6?  不甚明白)

總之,目標是要找出矩形輪廓。

我們可以想像:一張紙是個矩形,right !? 

矩形有四個邊,所以若能找出正好有4個點的最大輪廓,那就是我們要掃描的紙張矩形。


# find Contours, 面積從大到小排序, 只保留前5個
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key =cv2.contourArea, reverse=True)[:5]  

for cnt in cnts:
    # 近似輪廓
    peri = cv2.arcLength(cnt, True)    # 取得(perimeter), True: 邊界Closed 無中斷
    # 多邊形逼近, 目標只要取4個點就好
    # 可以想成是用粗一點的線來描邊, 來忽略掉細微的毛邊或雜點
    # 實務上建議取Contour周長的1~5%作為其epsilon值
    # 值愈大,則Contour Approximation結果的點數愈少,反之則愈多。
    approx = cv2.approxPolyDP(cnt, 0.01 * peri, True)  

    if len(approx) == 4:
        screenCnt = approx

cv2.drawContours(image, [screenCnt], -1, (0,255,0), 2)






應用透視轉換 and threshold


# 對原圖進行透視轉換, 目標圖檔的輪廓乘以之前調整的比例 (因為之前的處理是縮小後的圖), 
# 但要對原圖進行處理, 所以要恢復的話就必須乘回去
wraped = four_point_transform(ori, screenCnt.reshape(4,2)* ratio)

# 換成掃描黑白效果, 先轉成灰階後 + 自適應閾值
wraped = cv2.cvtColor(wraped, cv2.COLOR_BGR2GRAY)
= threshold_local(wraped, 11offset=10method="gaussian")
wraped = (wraped > T).astype("uint8"* 255

cv2.imshow("Scanned", imutils.resize(wraped, height = 650))
cv2.imwrite("images\\recipt.png", wraped)




看起來確實是像掃描過的圖片呢

原範例中使用了作者提供的類別

from pyimagesearch.transform import four_point_transform # 進行透視轉換用
import imutils # 相當便利的圖像處理工具
from skimage.filters import threshold_local # 將圖片應用threshold使其成為黑白狀態