opencv task1-------銀行卡識別
前言
一個好的深度學習算法工程師,歷來就是和opencv、pilow、Matplotlib一塊兒混合用,因爲這個代碼不是我寫的,我就不上傳代碼了。python
opencv的安裝
這裏咱們使用python版opnecv,c++版本的基礎算法也差很少。c++
pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
預備知識
1.二值化
ret, dst = cv2.threshold(src, thresh, maxval, type)
src: 輸入圖,只能輸入單通道圖像,一般來講爲灰度圖
thresh:通常取127和255
maxval: 當像素值超過了閾值(或者小於閾值,根據type來決定),所賦予的值
type:二值化操做的類型,包含如下5種類型:
1.cv2.THRESH_BINARY 超過閾值部分取maxval(最大值),不然取0
2.cv2.THRESH_BINARY_INV THRESH_BINARY的反轉
3.cv2.THRESH_TRUNC 大於閾值部分設爲閾值,不然不變
4.cv2.THRESH_TOZERO 大於閾值部分不改變,不然設爲0
5.cv2.THRESH_TOZERO_INV THRESH_TOZERO的反轉
經常使用:
THRESH_BINARY_INV
自動找到闕值:
cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
git
import cv2 import matplotlib.pyplot as plt img = cv2.imread('cat.jpg') img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret,img_bi = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY) ret,img_bi_inv = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY_INV) ret,img_tr = cv2.threshold(img_gray,127,255,cv2.THRESH_TRUNC) ret,img_zero = cv2.threshold(img_gray,127,255,cv2.THRESH_TOZERO) ret,img_zero_inv = cv2.threshold(img_gray,127,255,cv2.THRESH_TOZERO_INV) titles = ['Original','Binary','Binary_INV','TRUNC','ZERO','ZERO_INV'] images = [img,img_bi,img_bi_inv,img_tr,img_zero,img_zero_inv] for i in range(6): plt.subplot(2,3,i+1),plt.imshow(images[i],'gray'),plt.title(titles[i]) plt.xticks([]),plt.yticks([]) # 不顯示座標軸 plt.show(
2.輪廓檢測
contours,hierarchy=cv2.findContours(image, mode, method[, offset])
image:8位單通道圖像。視爲二值圖像
contours:輪廓點。列表格式,每個元素爲一個3維數組(其形狀爲(n,1,2),其中n表示輪廓點個數,2表示像素點座標),表示一個輪廓
hierarchy:輪廓間的層次關係,爲三維數組,形狀爲(1,n,4),其中n表示輪廓總個數,4指的是用4個數表示各輪廓間的相互關係。第一個數表示同級輪廓的下一個輪廓編號,第二個數表示同級輪廓的上一個輪廓的編號,第三個數表示該輪廓下一級輪廓的編號,第四個數表示該輪廓的上一級輪廓的編號
mode:輪廓檢索的方式
1.cv2.RETR_EXTERNAL:只檢索外部輪廓
2.cv2.RETR_LIST: 檢測全部輪廓且不創建層次結構
3.cv2.RETR_CCOMP: 檢測全部輪廓,創建兩級層次結構
4.cv2.RETR_TREE: 檢測全部輪廓,創建完整的層次結構
method:輪廓近似的方法
1.cv2.CHAIN_APPROX_NONE:存儲全部的輪廓點
2.cv2.CHAIN_APPROX_SIMPLE:壓縮水平,垂直和對角線段,只留下端點。
經常使用:
cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
算法
3.形態學
腐蝕與膨脹屬於形態學操做,所謂的形態學,就是改變物體的形狀,形象理解一些:腐蝕=變瘦(白色部分) 膨脹=變胖(白色部分)
主要是採用 cv2.erode() 和 cv2.dilate()
數組
主要針對二值化圖像的白色部分
腐蝕:是一種消除邊界點,使邊界向內部收縮的過程.到亮區(背景)變細,而黑色區域(字母)則變大了。
bash
通俗講法:在原圖的每個小區域裏取最小值,因爲是二值化圖像,只要有一個點爲0,則都爲0,來達到瘦身的目的
算法:用 3x3 的 kernel,掃描圖像的每個像素;用 kernel 與其覆蓋的二值圖像作 「與」 操做;若都爲1,則圖像的該像素爲1;不然爲0. 最終結果:使二值圖像減少一圈.
膨脹:是將與物體接觸的全部背景點合併到該物體中,使邊界向外部擴張的過程,能夠用來填補物體中的空洞。背景(白色)膨脹,而黑色字母縮小了。
app
算法:用 3x3 的 kernel,掃描圖像的每個像素;用 kernel 與其覆蓋的二值圖像作 「與」 操做;若都爲0,則圖像的該像素爲0;不然爲1. 最終結果:使二值圖像擴大一圈
1.先腐蝕後膨脹的過程稱爲 開運算。用來消除小物體、在纖細點處分離物體、平滑較大物體的邊界的同時並不明顯改變其面積
【cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)】
學習
2.先膨脹後腐蝕的過程稱爲 閉運算。用來填充物體內細小空洞、鏈接鄰近物體、平滑其邊界的同時並不明顯改變其面積.
【cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)】
字體
3.膨脹 減去 腐蝕的過程稱爲 梯度運算。用來計算輪廓【cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)】ui
4.頂帽:原始輸入 減去 開運算結果 【cv2.morphologyEx(img,cv.MORPH_TOPHAT,kernel)】
5.黑帽:閉運算 減去 原始輸入 【cv2.morphologyEx(img,cv.MORPH_BLACKHAT,kernel)】
3.sobel算子
cv2.Sobel(src, ddepth, dx, dy, ksize) 進行sobel算子計算
src表示當前圖片,
ddepth表示圖片深度,這裏使用cv2.CV_64F使得結果能夠是負值,
dx表示x軸方向,
dy表示y軸方向, ksize表示移動方框的大小
cv2.convertScalerAbs(src) 將像素點進行絕對值計算
src表示當前圖片
import cv2 import matplotlib.pyplot as plt img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE) sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)# 1,0: 表示只算水平的,不算豎直的 sobelxx = cv2.convertScaleAbs(sobelx)# sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3) sobelyy = cv2.convertScaleAbs(sobely) # 分別計算x和y,再求和,融合的較好 sobelxy_1 = cv2.addWeighted(sobelxx,0.5,sobelyy,0.5,0) # 不建議直接計算,融合的很差 sobelxy_2 = cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3) # cv_show('pie',img) cv2.imshow('sobelx',sobelx) cv2.imshow('sobelxx',sobelxx) cv2.imshow('sobely',sobely) cv2.imshow('sobelyy',sobelyy) cv2.imshow('sobelxy_1',sobelxy_1) cv2.imshow('sobelxy_2',sobelxy_2) cv2.waitKey(0) cv2.destroyAllWindows()
4.模版匹配
模板匹配和卷積原理很像,模板在原圖像上從原點開始滑動,計算模板與(圖像被模板覆蓋的地方)的差異程度,這個差異程度的計算方法在opencv裏有6種,而後格每次計算的結果放入一個矩陣裏,做爲結果輸出.假如原圖形是AxB大小,而模板是axb大小,則輸出結果的矩陣是(A-a+1)x(B-b+1)
簡單來講,模板匹配就是在整個圖像區域發現與給定子圖像匹配的小塊區域。
cv2.matchTemplate(image, templ, method, result=None, mask=None)
image:待搜索圖像
templ:模板圖像
method:計算匹配程度的方法
返回參數res:是一個結果矩陣,假設待匹配圖像爲 I,寬高爲(W,H),模板圖像爲 T,寬高爲(w,h)。那麼result的大小就爲(W-w+1, H-h+1)
其中method:
TM-SQDIFF:計算平方不一樣,計算出來的值越小,越相關
TM_CCORR:計算相關性,計算出來的值越大,越相關
TM_CCOEFF:計算相關係數,計算出來的值越大,越相關
TM SQDIFF-NORMED: 計算歸一化平方不一樣,計算出來的值越接近0,越相關
TM_CCORR-NORMED: 計t算歸一化相關性,計算出來的值越接近1,越相關
TM-CCOEFF-NORMED:計算歸一化相關係數,計算出來的值越接近1,越相關
項目步驟
1.準備識別模版
準備好識別模版,對它進行opencv操做,拆分爲單個圖0,1,2,3,4,5…並按從左到右排序。
2.對銀行卡操做
對銀行卡部分進行opencv的一些列操做,選取銀行卡卡號的部分,進行拆分,並排序。
3.模版匹配
利用模版匹配,計算得分最高的,銀行卡號進行識別。
具體代碼
文件配置
ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", default='./image/credit_card_01.png',required=True, help="path to input image") ap.add_argument("-t", "--template",default='./image/ocr_a_reference.png', required=True, help="path to template OCR-A image") args = vars(ap.parse_args()) # 指定信用卡類型 FIRST_NUMBER = { "3": "American Express", "4": "Visa", "5": "MasterCard", "6": "Discover Card" } # 繪圖展現 def cv_show(name,img): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows() print(args["template"])
識別模版讀取與裁剪
img=cv2.imread(args["template"]) cv_show("tem",img) #灰度圖 ref=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) cv_show("GRAY",ref) #二值化 ref=cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1] cv_show("GRAY1",ref) refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # cv版本大於3.8的,只有兩個返回值 cv2.drawContours(img,refCnts,-1,(0,0,255),3) cv_show("GRAY1",img) refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] digits = {} # 存模板的單個數字 # 2.遍歷每個輪廓,外接矩形 for (i, c) in enumerate(refCnts): # 計算外接矩形而且resize成合適大小 (x, y, w, h) = cv2.boundingRect(c) # 3.摳出模板 roi = ref[y:y + h, x:x + w] # 每一個roi對應一個數字 roi = cv2.resize(roi, (57, 88)) # 過小,調大點 cv_show("waijie",roi) # 4.每個數字對應每個模板 digits[i] = roi
代碼拆分:
img=cv2.imread(args["template"]) cv_show("tem",img)
#灰度圖 ref=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) cv_show("GRAY",ref)
#二值化 ref=cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1] cv_show("GRAY1",ref)
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # cv版本大於3.8的,只有兩個返回值 cv2.drawContours(img,refCnts,-1,(0,0,255),3) cv_show("GRAY1",img)
for (i, c) in enumerate(refCnts): # 計算外接矩形而且resize成合適大小 (x, y, w, h) = cv2.boundingRect(c) # 3.摳出模板 roi = ref[y:y + h, x:x + w] # 每一個roi對應一個數字 roi = cv2.resize(roi, (57, 88)) # 過小,調大點 cv_show("waijie",roi) # 4.每個數字對應每個模板 digits[i] = roi
#初始化卷積核的大小 ################################################# # kernel = cv2.getStructuringElement(shape, ksize, anchor) # shape: 核的形狀 # cv2.MORPH_RECT: 矩形 # cv2.MORPH_CROSS: 十字形(以矩形的錨點爲中心的十字架) # cv2.MORPH_ELLIPSE: 橢圓(矩形的內切橢圓) # # ksize: 核的大小,矩形的寬,高格式爲(width, height) # anchor: 核的錨點,默認值爲(-1, -1), 即核的中心點 ################################################# #卷積核的大小根據項目設定 rectKernel =cv2.getStructuringElement(cv2.MORPH_RECT,(9,3)) sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
銀行卡讀取與裁剪
# 2.讀取輸入圖像,預處理 image = cv2.imread(args["image"]) cv_show('Input_img',image) image = myutils.resize(image, width=300) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv_show('Input_gray',gray) # 3.禮帽操做,突出更明亮的區域 # 形態學操做,禮帽+閉操做能夠突出明亮區域,但並非非得禮帽+閉操做 tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) cv_show('tophat',tophat) # 4.x方向的Sobel算子,實驗代表,加y的效果的並很差 gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0,ksize=-1) #ksize=-1至關於用3*3的 # sobelyy = cv2.convertScaleAbs(gradX) # cv_show('gradXt',sobelyy) # x方向取絕對值 -> 歸一化 # 絕對值+歸一化 # 歸一化 x’ = (x-min)/(max-min gradX = np.absolute(gradX) # absolute: 計算絕對值 (minVal, maxVal) = (np.min(gradX), np.max(gradX)) gradX = (255 * ((gradX - minVal) / (maxVal - minVal))) gradX = gradX.astype("uint8") print (np.array(gradX).shape) cv_show('Input_Sobel_gradX',gradX) #經過閉操做(先膨脹,再腐蝕)將數字連在一塊兒.變亮 gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) cv_show('Input_CLOSE_gradX',gradX) # THRESH_OTSU會自動尋找合適的閾值,適合雙峯,需把閾值參數設置爲0 thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv_show('Input_thresh',thresh) #再來一個閉操做,填補空洞 #兩段代碼雷同 # onekernel = np.ones((9,9), np.uint8) # thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,onekernel) thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) # 填補空洞 cv_show('Input_thresh_CLOSE',thresh) #計算輪廓 threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 1.遍歷輪廓 locs = [] # 存符合條件的輪廓 for i,c in enumerate(threshCnts): # 計算矩形 x,y,w,h = cv2.boundingRect(c) ar = w / float(h) # 選擇合適的區域,根據實際任務來,這裏的基本都是四個數字一組 if ar > 2.5 and ar < 4.0: if (w > 40 and w < 55) and (h > 10 and h < 20): #符合的留下來 locs.append((x, y, w, h)) # 將符合的輪廓從左到右排序 #x:x[]字母能夠隨意修改,排序方式按照中括號[]裏面的維度,[0]按照第一維,[1]按照第二維。 locs = sorted(locs,key=lambda x:x[0])
分解代碼:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv_show('Input_gray',gray)
# 3.禮帽操做,突出更明亮的區域 # 形態學操做,禮帽+閉操做能夠突出明亮區域,但並非非得禮帽+閉操做 tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) cv_show('tophat',tophat)
# 4.x方向的Sobel算子,實驗代表,加y的效果的並很差 gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0,ksize=-1) #ksize=-1至關於用3*3的
# x方向取絕對值 -> 歸一化 # 絕對值+歸一化 # 歸一化 x’ = (x-min)/(max-min gradX = np.absolute(gradX) # absolute: 計算絕對值 (minVal, maxVal) = (np.min(gradX), np.max(gradX)) gradX = (255 * ((gradX - minVal) / (maxVal - minVal))) gradX = gradX.astype("uint8")
#經過閉操做(先膨脹,再腐蝕)將數字連在一塊兒.變亮 gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) cv_show('Input_CLOSE_gradX',gradX)
# THRESH_OTSU會自動尋找合適的閾值,適合雙峯,需把閾值參數設置爲0 thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv_show('Input_thresh',thresh)
進行模版匹配
遍歷每個輪廓中的數字 output = [] # 存正確的數字 for (i,(gx,gy,gw,gh)) in enumerate(locs): groupOutput = [] # 根據座標提取每個組(4個值) group = gray[gy-5:gy+gh+5, gx-5:gx+gw+5] # 往外擴一點 #cv_show('group_' + str(i), group) # 二值化預處理 group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 二值化的group #cv_show('group2_' + str(i), group) # 把每一個輪廓分紅小輪廓 digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] #排序 digitCnts = myutils.sort_contours(digitCnts, method="left-to-right")[0] for c in digitCnts: z = 0 (x, y, w, h) = cv2.boundingRect(c) # 外接矩形 roi = group[y:y + h, x:x + w] # 在原圖中取出小輪廓覆蓋區域,即數字 roi = cv2.resize(roi, (57, 88)) #cv_show('roi' , roi ) scores = [] #進行遍歷模版匹配 for (digit, digitROI) in digits.items(): # 進行模板匹配, res是結果矩陣 res = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF) # 此時roi是X digitROI是0 依次是1,2.. 匹配10次,看模板最高得分多少 Max_score = cv2.minMaxLoc(res)[1] # 返回4個,取第二個最大值Maxscore scores.append(Max_score) # 10個最大值 print("scores:", scores) groupOutput.append(str(np.argmax(scores))) z = z + 1 # 2畫出來 cv2.rectangle(image, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 1) # 左上角,右下角 # putText參數:圖片,添加的文字,左上角座標,字體,字體大小,顏色,字體粗細 cv2.putText(image, "".join(groupOutput), (gx, gy - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2) # 獲得結果 output.extend(groupOutput) print("groupOutput:", groupOutput) cv_show("result",image)