OCR文本扫描 轮廓检测 透视变换-唐宇迪笔记
OCR文本掃描 輪廓檢測 透視變換
本項目和源代碼來自唐宇迪opencv項目實戰(zhàn)
OCR文本識別
什么是OCR,百度里的定義是:
OCR (Optical Character Recognition,光學(xué)字符識別)是指電子設(shè)備(例如掃描儀或數(shù)碼相機)檢查紙上打印的字符,通過檢測暗、亮的模式確定其形狀,然后用字符識別方法將形狀翻譯成計算機文字的過程;即,針對印刷體字符,采用光學(xué)的方式將紙質(zhì)文檔中的文字轉(zhuǎn)換成為黑白點陣的圖像文件,并通過識別軟件將圖像中的文字轉(zhuǎn)換成文本格式,供文字處理軟件進一步編輯加工的技術(shù)。
輸入一張圖,然后掃描出其中文字
算法步驟
1、導(dǎo)入圖片
image = cv2.imread('receipt.jpg')2、圖片與處理
resize 操作
resize函數(shù)的作用是按照原圖像相同長寬比,當給定長(height)或者寬(width)時將原圖resize成與原圖像同比例的大小。
至于這一步為什么要進行resize操作,我分析
1 實驗使用的圖像多為手機拍攝的圖片,圖片大小至少為3500*4000,在imshow(),在屏幕顯示并不能像是完整的圖像,不利于觀察。
該函數(shù)的返回值是resize后的圖片;參數(shù)是原圖像和指定的變換后的width或height值。
接著對圖像進一步操作
Canny邊緣檢測步驟
1、canny邊緣檢測
1、高斯濾波器:通過高斯分布去除噪點
2、計算Gx和Gy
3、非極大值抑制
4、雙閾值檢測
對比圖:
Canny邊緣檢測之前需要先降噪----高斯濾波
GaussianBlur(src,ksize,sigmaX [,dst [,sigmaY [,borderType]]])-> dst
第一個參數(shù)是輸入圖像,可以是Mat類型,圖像深度為CV_8U、CV_16U、CV_16S、CV_32F、CV_64F。
第二個參數(shù)是輸出圖像,與輸入圖像有相同的類型和尺寸。
Size ksize: 高斯內(nèi)核大小,這個尺寸與前面兩個濾波kernel尺寸不同,ksize.width和ksize.height可以不相同但是這兩個值必須為正奇數(shù),如果這兩個值為0,他們的值將由sigma計算。
double sigmaX: 高斯核函數(shù)在X方向上的標準偏差
double sigmaY: 高斯核函數(shù)在Y方向上的標準偏差,如果sigmaY是0,則函數(shù)會自動將sigmaY的值設(shè)置為與sigmaX相同的值,如果sigmaX和sigmaY都是0,這兩個值將由ksize.width和ksize.height計算而來。
Canny邊緣檢測函數(shù)
canny = cv2.Canny(gauss, 75, 200)
第一個參數(shù)時輸入的圖像,第二個參數(shù)是MinVal,第三個參數(shù)是MaxVal。
如果該點的梯度大于MaxVal, 則將該點處理為邊界。如果該點的梯度大于MinVal且小于MaxVal,則抗癌電視都與邊界相連,如果相連則將該點處理為邊界,否則不是邊界。如果該點小于MinVal則該點不是邊界。
輪廓檢測
查找輪廓,輪廓檢測使用的模型是RETR_LIST,檢測所有輪廓, 并將其保存在一條鏈表中
cv2.findContours(img, mode, methord)
下表列出了輪廓檢測的模型mode
| RETR_EXTERNAL | 只檢測最外面的輪廓 |
| RETR_LIST | 檢索所有輪廓,并將其保存到一條鏈表中 |
| RETR_CCOMP | 檢索所有輪廓,并將他們組織為兩層 ,頂層是各部分的外界邊界,第二層是空洞的邊界 |
| RETR_TREE | 檢測所有輪廓,并重構(gòu)嵌套輪廓的整個層次 |
檢測到所有輪廓后根據(jù)輪廓面積大小對輪廓進行排序,并保留前五個,畫出這按照面積排序的前五個輪廓(在繪制輪廓之前別忘了復(fù)制原圖像,否則顯示原圖形也會被更改。
輪廓近似
定義一個循環(huán),遍歷輪廓,完成輪廓近似的操作。
peri = cv2.arcLength(c, True)
先計算出輪廓的周長,然后以輪廓周長乘以一個百分比作為輪廓近似的精度。
approx = cv2.approxPolyDP(c, 0.02*peri, True)
True表示輪廓是封閉的
而輪廓近似的返回值是能夠包含圖像的點的集合。既然是逐個點確定,又包含了整個圖像,那一定是從最大的輪廓開始。當返回值的長度為4,即返回點的個數(shù)為4時,說明確定的就是能將最外面的最大輪廓包圍的四邊形的四個頂點。
透視變換
透視變換(Perspective Transformation)是將成像投影到一個新的視平面(Viewing Plane),也稱作投影映射(Projective Mapping)。如下圖,通過透視變換ABC變換到A’B’C’。
對于視角變換,我們需要一個 3x3 變換矩陣。在變換前后直線還是直線。
要構(gòu)建這個變換矩陣,你需要在輸入圖像上找 4 個點,以及他們在輸出圖像上對應(yīng)的位置。這四個點中的任意三個都不能共線。這個變換矩陣可以有函數(shù)cv2.getPerspectiveTransform() 構(gòu)建。然后把這個矩陣傳給函數(shù)cv2.warpPerspective。
1、cv2.getPerspectiveTransform(src, dst)
參數(shù)說明:
src:源圖像中待測矩形的四點坐標 sdt:目標圖像中矩形的四點坐標返回由源圖像中矩形到目標圖像矩形變換的矩陣
2、cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]])
參數(shù)為:
src:輸入圖像
M:變換矩陣
dsize:目標圖像shape
flags:插值方式,interpolation方法INTER_LINEAR或INTER_NEAREST
borderMode:邊界補償方式,BORDER_CONSTANT or BORDER_REPLICATE
borderValue:邊界補償大小,常值,默認為0
3、cv2.perspectiveTransform(src, m[, dst])
參數(shù):
src:輸入的2通道或者3通道的圖片
m:變換矩陣
返回的是相同size的圖片
4、區(qū)別
warpPerspective適用于圖像。perspectiveTransform適用于一組點。
透視變換有很多有趣的應(yīng)用,比如可以用來做圖像傾斜校正
自定義函數(shù) order_points()
def order_points(pts):# 一共4個坐標點rect = np.zeros((4, 2), dtype = "float32")# 按順序找到對應(yīng)坐標0123分別是 左上,右上,右下,左下# 計算左上,右下s = pts.sum(axis = 1)rect[0] = pts[np.argmin(s)]rect[2] = pts[np.argmax(s)]# 計算右上和左下diff = np.diff(pts, axis = 1)rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]return rect程序中定義了兩個函數(shù),order_point函數(shù)用一種方法來分辨這四個定位點分別對應(yīng)于四邊形的那個頂點,簡單說就是給四個點起名字。
左下(bl), 右下(br), 右上(tr), 左上(tl),并將四個點按順勢者或逆時針依次存放。從那個點開始存放不重要,關(guān)鍵是要通過這四個點的坐標關(guān)系確定每一個點分別對應(yīng)(四邊形)的哪一個頂點。簡單說來就是給每一個點起了一個代號,方便使用它,后面有用。
第二個函數(shù) four_point_transform(image, pts)進行透視變換了。首先調(diào)用函數(shù)order_point,使用這四個起好名字的點。根據(jù)幾個關(guān)系利用公式 s = ((x2-x1)^2 + (y2-y1))2)1/2 。因為四個點確定的近似輪廓不一定是矩形,所以分別取長和寬最大長度,
自定義函數(shù) def four_point_transform()
def four_point_transform(image, pts):# 獲取輸入坐標點rect = order_points(pts)(tl, tr, br, bl) = rect# 計算輸入的w和h值widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2)) #使用的公式s = ((x2-x1)^2 + (y2-y1))^2)^1/2 算出兩邊寬度widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))maxWidth = max(int(widthA), int(widthB))heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA), int(heightB))# 變換后對應(yīng)坐標位置dst = np.array([[0, 0],[maxWidth - 1, 0],[maxWidth - 1, maxHeight - 1],[0, maxHeight - 1]], dtype = "float32")# 計算變換矩陣M = cv2.getPerspectiveTransform(rect, dst)warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))# 返回變換后結(jié)果return warpeddst對應(yīng)的是與原圖像中輪廓大小相同,只是進行了坐標變換的圖像。左上角坐標點為(0,0),圖像的長、寬分別為四邊形輪廓長、寬最大值的四個定位點。將原圖像中卡片的輪廓摳出來,變換了坐標。
透視變換函數(shù)包含在自定義函數(shù)four_point_transform()中
透視變換就是將原始的四個定位點,變換后定位點分別對應(yīng)dst(左上角的定位點是(0, 0))中的四個定位點坐標。
返回時M是由原坐標透視變換到目標坐標點的變換矩陣。第二個返回圖像的透視變換函數(shù)cv2.warpPerspective(image, M, (maxWidth, maxHeight))第一個參數(shù)是原始圖像,第二個參數(shù)是變換矩陣(原圖片中的小卡片是一個輪廓,變換后圖像中小卡片充滿了整張圖像), 第三個參數(shù)是變換后圖像的長和寬(場合寬是前面計算出的輪廓長寬取最大值的結(jié)果)。
最后該函數(shù)返回的結(jié)果是透視變換后的圖像。
接下來就是灰度化、二值化一條龍服務(wù)。
終于把這張小卡片從圖像中單獨扣出來了!
結(jié)果如圖所示:
調(diào)用four_point_transform函數(shù)
warped = four_point_transform(orig, screenCnt.reshape(4, 2)*ratio)
說一下為什么要乘這個ratio
因為之前做的一系列圖像處理操作(最后得到了近似輪廓的四個定位點)都是在resize后的圖像上進行的。還記得resize函數(shù)是怎么做的嗎,參數(shù)中給定width或者height的值,按照與原圖像相同的比例對圖像進行縮放。那么圖像中每一個點的坐標都發(fā)生了相應(yīng)的變化(不是面積大小,而是點的坐標發(fā)生了變化)
用plt顯示圖片驗證一下
所以我們在程序一開始就把原圖坐標和變化后圖像的坐標的比例ratio記錄下來。
我們剛剛說是圖片上的每個像素點坐標發(fā)生了相應(yīng)的變化,那么我們輪廓近似的四個定位點當然不例外就在其中。
所以相當于是吧原圖像上的卡片四角的點坐標透視變換到與原圖像相同大小的平面上,而four_point_transform函數(shù)的第二個參數(shù)scrrnCnt
中存儲的坐標點應(yīng)該與第一個參數(shù)中圖像大小保持一致(都是原圖或者都是resize變換后的,一致即可)。那我為什么要從一開始就做resize這一步呢?(因為圖太大電腦屏幕放不下, 我要是有個巨大的屏幕是不是會省去很多麻煩)。
對于 M = cv2.getPerspectiveTransform(rect, dst)
以下是原理:實際上就是先把歪的二維原圖轉(zhuǎn)化為三維,再轉(zhuǎn)化為二維,在這其中,先把二維的(x,y)轉(zhuǎn)化為(x,y,1),然后乘一個33的矩陣,轉(zhuǎn)化為(x,y,z),最后轉(zhuǎn)化為二維(x/z,y/z)。
因為33的矩陣中,有8個未知數(shù),就有8個方程,最后一位是1,一組坐標可以確定2個方程,因此需要4組坐標,即一個矩形的四個角的坐標。這個函數(shù)就是幫我們算矩陣m
3 調(diào)用pytesseract進行OCR文本識別
接著在test.py中 import pytesseract 進行OCR識別
# https://digi.bib.uni-mannheim.de/tesseract/ # 配置環(huán)境變量如E:\Program Files (x86)\Tesseract-OCR # tesseract -v進行測試 # tesseract XXX.png 得到結(jié)果 # pip install pytesseract # anaconda lib site-packges pytesseract pytesseract.py # tesseract_cmd 修改為絕對路徑即可 from PIL import Image import pytesseract import cv2 import ospreprocess = 'blur' #threshimage = cv2.imread('scan.jpg') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)if preprocess == "thresh":gray = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]if preprocess == "blur":gray = cv2.medianBlur(gray, 3)filename = "{}.png".format(os.getpid()) cv2.imwrite(filename, gray)text = pytesseract.image_to_string(Image.open(filename)) print(text) os.remove(filename)cv2.imshow("Image", image) cv2.imshow("Output", gray) cv2.waitKey(0)首先對圖像進行灰度化處理, 然后進行均值濾波。
gray = cv2.medianBlur(gray, 3)
調(diào)用pytesseract.image_to_string()函數(shù)
pytesseract.image_to_string(Image.open(filename))
感謝https://blog.csdn.net/weixin_43227526/article/details/104692787
總結(jié)
以上是生活随笔為你收集整理的OCR文本扫描 轮廓检测 透视变换-唐宇迪笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宏杉科技摆“擂台”,遍寻天下存储技术高手
- 下一篇: android 监听webView滑动距