上一節介紹瞭如何openCV的透視轉換方法的應用,並構建了一個基於給定四角點轉換鳥瞰圖的方法函數,這一節將繼續這個運用,通過查找邊緣的方法實現自動轉換的功能。
實現步驟實際上很簡單,只需要三步:
第一步:查找文檔的邊緣
第二步:通過邊緣查找文檔輪廓並找到四個角點的座標
第三步:使用透視轉換函數完成圖像轉換
下面的代碼基於openCV/python的版本:openCV2.4/3+, python2.7/3+
上一章節我們完成了transform.py模塊的構建,我們將在接下來的涉及圖像四角點處理的問題中均會使用到。打開你的python編輯器,創建一個新的文檔,並命名為scan.py。
# 導入必要的庫
#導入上一節構建的模塊和函數
from transform import four_point_transform
#記得安裝scikit-image包,threshold-local函數幫助我們處理黑白圖像
from skimage.filters import threshold_local
import numpy as np
import argparse
import cv2
#imutils是一個很實用的圖像處理庫,比如resize/cropping/rotate等圖像基本編輯
import imutils
# 構建解析器並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image to be scanned")
args = vars(ap.parse_args())
接下來就是第一步:邊緣查找:
# 加載圖像並計算新舊圖像高度的比例,並拷貝一份,修改大小。
# 為了加快圖像處理速度,同時使邊緣檢測步驟更加準確,
# 我們將掃描圖像的高度調整為500像素。
# 我們還特別注意跟蹤圖像的原始高度與新高度的比值,
# 這將允許我們對原始圖像而不是調整大小的圖像執行掃描。
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()
image = imutils.resize(image, height = 500)
# 把圖象轉化為灰度, 並加模糊處理,然後查找邊緣
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 顯示原始圖像和檢測到的邊緣圖像
print("STEP 1: Edge Detection")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
測試一下效果:
shell
python scan.py --image images/doc.jpg
上一節介紹瞭如何openCV的透視轉換方法的應用,並構建了一個基於給定四角點轉換鳥瞰圖的方法函數,這一節將繼續這個運用,通過查找邊緣的方法實現自動轉換的功能。
實現步驟實際上很簡單,只需要三步:
第一步:查找文檔的邊緣
第二步:通過邊緣查找文檔輪廓並找到四個角點的座標
第三步:使用透視轉換函數完成圖像轉換
下面的代碼基於openCV/python的版本:openCV2.4/3+, python2.7/3+
上一章節我們完成了transform.py模塊的構建,我們將在接下來的涉及圖像四角點處理的問題中均會使用到。打開你的python編輯器,創建一個新的文檔,並命名為scan.py。
# 導入必要的庫
#導入上一節構建的模塊和函數
from transform import four_point_transform
#記得安裝scikit-image包,threshold-local函數幫助我們處理黑白圖像
from skimage.filters import threshold_local
import numpy as np
import argparse
import cv2
#imutils是一個很實用的圖像處理庫,比如resize/cropping/rotate等圖像基本編輯
import imutils
# 構建解析器並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image to be scanned")
args = vars(ap.parse_args())
接下來就是第一步:邊緣查找:
# 加載圖像並計算新舊圖像高度的比例,並拷貝一份,修改大小。
# 為了加快圖像處理速度,同時使邊緣檢測步驟更加準確,
# 我們將掃描圖像的高度調整為500像素。
# 我們還特別注意跟蹤圖像的原始高度與新高度的比值,
# 這將允許我們對原始圖像而不是調整大小的圖像執行掃描。
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()
image = imutils.resize(image, height = 500)
# 把圖象轉化為灰度, 並加模糊處理,然後查找邊緣
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 顯示原始圖像和檢測到的邊緣圖像
print("STEP 1: Edge Detection")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
測試一下效果:
shell
python scan.py --image images/doc.jpg
雖然背景有點不乾淨但是文檔的邊緣還是很明顯的,接下來我們想辦法查找文檔的邊緣並生成輪廓。
第二步:尋找輪廓:
事實上,在構建文檔掃描器時,有一個非常重要的前提:掃描儀只是在一張紙上掃描。一張紙被假定為長方形,矩形有四條邊。因此,我們可以創建一個簡單的方法來幫助我們構建文檔掃描器。我們假設圖像中最大的輪廓恰好有四個點,這就是我們要掃描的那張紙。這也是一個相當安全的假設——當然,也可以人為的給定文檔輪廓。
# 在邊緣圖像的基礎上查找輪廓保留最大的一個,並在圖像中標識出來
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
# 循環處理
for c in cnts:
\t# 大致輪廓
\tperi = cv2.arcLength(c, True)
\tapprox = cv2.approxPolyDP(c, 0.02 * peri, True)
\t# 如果查找的大致輪廓有四個角點,即假設為我們需要查找的
\tif len(approx) == 4:
\t\tscreenCnt = approx
\t\tbreak
# 顯示文檔的輪廓
print("STEP 2: Find contours of paper")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
運行一下代碼測試效果:shell中輸入
python scan.py --image images/receipt.jpg
上一節介紹瞭如何openCV的透視轉換方法的應用,並構建了一個基於給定四角點轉換鳥瞰圖的方法函數,這一節將繼續這個運用,通過查找邊緣的方法實現自動轉換的功能。
實現步驟實際上很簡單,只需要三步:
第一步:查找文檔的邊緣
第二步:通過邊緣查找文檔輪廓並找到四個角點的座標
第三步:使用透視轉換函數完成圖像轉換
下面的代碼基於openCV/python的版本:openCV2.4/3+, python2.7/3+
上一章節我們完成了transform.py模塊的構建,我們將在接下來的涉及圖像四角點處理的問題中均會使用到。打開你的python編輯器,創建一個新的文檔,並命名為scan.py。
# 導入必要的庫
#導入上一節構建的模塊和函數
from transform import four_point_transform
#記得安裝scikit-image包,threshold-local函數幫助我們處理黑白圖像
from skimage.filters import threshold_local
import numpy as np
import argparse
import cv2
#imutils是一個很實用的圖像處理庫,比如resize/cropping/rotate等圖像基本編輯
import imutils
# 構建解析器並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image to be scanned")
args = vars(ap.parse_args())
接下來就是第一步:邊緣查找:
# 加載圖像並計算新舊圖像高度的比例,並拷貝一份,修改大小。
# 為了加快圖像處理速度,同時使邊緣檢測步驟更加準確,
# 我們將掃描圖像的高度調整為500像素。
# 我們還特別注意跟蹤圖像的原始高度與新高度的比值,
# 這將允許我們對原始圖像而不是調整大小的圖像執行掃描。
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()
image = imutils.resize(image, height = 500)
# 把圖象轉化為灰度, 並加模糊處理,然後查找邊緣
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 顯示原始圖像和檢測到的邊緣圖像
print("STEP 1: Edge Detection")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
測試一下效果:
shell
python scan.py --image images/doc.jpg
雖然背景有點不乾淨但是文檔的邊緣還是很明顯的,接下來我們想辦法查找文檔的邊緣並生成輪廓。
第二步:尋找輪廓:
事實上,在構建文檔掃描器時,有一個非常重要的前提:掃描儀只是在一張紙上掃描。一張紙被假定為長方形,矩形有四條邊。因此,我們可以創建一個簡單的方法來幫助我們構建文檔掃描器。我們假設圖像中最大的輪廓恰好有四個點,這就是我們要掃描的那張紙。這也是一個相當安全的假設——當然,也可以人為的給定文檔輪廓。
# 在邊緣圖像的基礎上查找輪廓保留最大的一個,並在圖像中標識出來
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
# 循環處理
for c in cnts:
\t# 大致輪廓
\tperi = cv2.arcLength(c, True)
\tapprox = cv2.approxPolyDP(c, 0.02 * peri, True)
\t# 如果查找的大致輪廓有四個角點,即假設為我們需要查找的
\tif len(approx) == 4:
\t\tscreenCnt = approx
\t\tbreak
# 顯示文檔的輪廓
print("STEP 2: Find contours of paper")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
運行一下代碼測試效果:shell中輸入
python scan.py --image images/receipt.jpg
正如您所看到的,我們已經成功地利用邊緣檢測圖像找到了文檔的輪廓(outline),我的收據周圍的綠色矩形顯示了輪廓(outline)。最後,讓我們進入步驟3,這將是用到four_point_transform函數。
第三步:轉換圖像:構建移動文檔掃描器的最後一步是取代表文檔大綱的四個點,並應用透視圖轉換來獲得自頂向下的圖像“鳥瞰圖”。
# 應用四點轉換生成鳥瞰圖
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
# 把變形的圖像轉化成灰度
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
T = threshold_local(warped, 11, offset = 10, method = "gaussian")
warped = (warped > T).astype("uint8") * 255
# 顯示轉換前後的圖像
print("STEP 3: Apply perspective transform")
cv2.imshow("Original", imutils.resize(orig, height = 650))
cv2.imshow("Scanned", imutils.resize(warped, height = 650))
cv2.waitKey(0)
我們將把兩個參數傳遞給four_point_transform:第一個參數是我們從磁盤加載的原始圖像(不是調整大小的圖像),第二個參數是表示文檔的輪廓線,乘以調整大小的比例。
你可能會想,為什麼要乘以調整後的比例? 我們乘以調整後的比例,因為我們進行了邊緣檢測,在調整後的高度=500像素的圖像上發現了輪廓。但是,我們希望對原始圖像進行掃描,而不是對調整大小的圖像進行掃描,因此我們將輪廓點乘以調整大小的比例。
為了獲得圖像的黑白感覺,我們將扭曲後的圖像轉換為灰度圖像,並應用自適應閾值。
好的,我們來運行一下效果:
shell
python scan.py --image images/receipt.jpg
上一節介紹瞭如何openCV的透視轉換方法的應用,並構建了一個基於給定四角點轉換鳥瞰圖的方法函數,這一節將繼續這個運用,通過查找邊緣的方法實現自動轉換的功能。
實現步驟實際上很簡單,只需要三步:
第一步:查找文檔的邊緣
第二步:通過邊緣查找文檔輪廓並找到四個角點的座標
第三步:使用透視轉換函數完成圖像轉換
下面的代碼基於openCV/python的版本:openCV2.4/3+, python2.7/3+
上一章節我們完成了transform.py模塊的構建,我們將在接下來的涉及圖像四角點處理的問題中均會使用到。打開你的python編輯器,創建一個新的文檔,並命名為scan.py。
# 導入必要的庫
#導入上一節構建的模塊和函數
from transform import four_point_transform
#記得安裝scikit-image包,threshold-local函數幫助我們處理黑白圖像
from skimage.filters import threshold_local
import numpy as np
import argparse
import cv2
#imutils是一個很實用的圖像處理庫,比如resize/cropping/rotate等圖像基本編輯
import imutils
# 構建解析器並解析參數
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image to be scanned")
args = vars(ap.parse_args())
接下來就是第一步:邊緣查找:
# 加載圖像並計算新舊圖像高度的比例,並拷貝一份,修改大小。
# 為了加快圖像處理速度,同時使邊緣檢測步驟更加準確,
# 我們將掃描圖像的高度調整為500像素。
# 我們還特別注意跟蹤圖像的原始高度與新高度的比值,
# 這將允許我們對原始圖像而不是調整大小的圖像執行掃描。
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()
image = imutils.resize(image, height = 500)
# 把圖象轉化為灰度, 並加模糊處理,然後查找邊緣
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 顯示原始圖像和檢測到的邊緣圖像
print("STEP 1: Edge Detection")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
測試一下效果:
shell
python scan.py --image images/doc.jpg
雖然背景有點不乾淨但是文檔的邊緣還是很明顯的,接下來我們想辦法查找文檔的邊緣並生成輪廓。
第二步:尋找輪廓:
事實上,在構建文檔掃描器時,有一個非常重要的前提:掃描儀只是在一張紙上掃描。一張紙被假定為長方形,矩形有四條邊。因此,我們可以創建一個簡單的方法來幫助我們構建文檔掃描器。我們假設圖像中最大的輪廓恰好有四個點,這就是我們要掃描的那張紙。這也是一個相當安全的假設——當然,也可以人為的給定文檔輪廓。
# 在邊緣圖像的基礎上查找輪廓保留最大的一個,並在圖像中標識出來
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
# 循環處理
for c in cnts:
\t# 大致輪廓
\tperi = cv2.arcLength(c, True)
\tapprox = cv2.approxPolyDP(c, 0.02 * peri, True)
\t# 如果查找的大致輪廓有四個角點,即假設為我們需要查找的
\tif len(approx) == 4:
\t\tscreenCnt = approx
\t\tbreak
# 顯示文檔的輪廓
print("STEP 2: Find contours of paper")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
運行一下代碼測試效果:shell中輸入
python scan.py --image images/receipt.jpg
正如您所看到的,我們已經成功地利用邊緣檢測圖像找到了文檔的輪廓(outline),我的收據周圍的綠色矩形顯示了輪廓(outline)。最後,讓我們進入步驟3,這將是用到four_point_transform函數。
第三步:轉換圖像:構建移動文檔掃描器的最後一步是取代表文檔大綱的四個點,並應用透視圖轉換來獲得自頂向下的圖像“鳥瞰圖”。
# 應用四點轉換生成鳥瞰圖
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
# 把變形的圖像轉化成灰度
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
T = threshold_local(warped, 11, offset = 10, method = "gaussian")
warped = (warped > T).astype("uint8") * 255
# 顯示轉換前後的圖像
print("STEP 3: Apply perspective transform")
cv2.imshow("Original", imutils.resize(orig, height = 650))
cv2.imshow("Scanned", imutils.resize(warped, height = 650))
cv2.waitKey(0)
我們將把兩個參數傳遞給four_point_transform:第一個參數是我們從磁盤加載的原始圖像(不是調整大小的圖像),第二個參數是表示文檔的輪廓線,乘以調整大小的比例。
你可能會想,為什麼要乘以調整後的比例? 我們乘以調整後的比例,因為我們進行了邊緣檢測,在調整後的高度=500像素的圖像上發現了輪廓。但是,我們希望對原始圖像進行掃描,而不是對調整大小的圖像進行掃描,因此我們將輪廓點乘以調整大小的比例。
為了獲得圖像的黑白感覺,我們將扭曲後的圖像轉換為灰度圖像,並應用自適應閾值。
好的,我們來運行一下效果:
shell
python scan.py --image images/receipt.jpg
好了,到目前為止,掃描圖像到文檔提取鳥瞰圖的過程實現完成了。遺留問題:
實際上這個程序還有不少地方需要你的改進,比如要求轉換的文檔本身是規則的四邊形,拍攝時儘量放在對比度明顯的桌面背景,這樣做的目的是為了避免邊緣查找時出現多於四邊的情況,多於四邊的邊緣後續輪廓查找會出現問題。也就是找不到合適的四邊輪廓來匹配。
解決方案:可以採用人工標註四個角點的方式來提取輪廓更為可靠。因為在實際應用場景往往是不規則的文檔。下一節我們來探討這個方案實現過程。