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)

在這裏插入圖片描述

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息