原文出處 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, 75, 200)
確認取得邊緣
找出圖形輪廓並將所有輪廓排序,只保留前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)
T = threshold_local(wraped, 11, offset=10, method="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使其成為黑白狀態
沒有留言:
張貼留言