使用Python+OpenCV進(jìn)行卡類型及16位卡號數(shù)字的OCR功能
這篇博客將介紹如何通過OpenCV和Python使用模板匹配執(zhí)行光學(xué)字符識(shí)別(OCR)。具體來說,將使用Python+OpenCV實(shí)現(xiàn)模板匹配算法,以自動(dòng)識(shí)別卡的類型和以及16位卡號數(shù)字。
在比較數(shù)字時(shí),模板匹配是一種非??焖俚姆椒?。
為此將圖像處理管道分為4個(gè)步驟:
- 通過各種圖像處理技術(shù)檢測信用卡上四組四個(gè)數(shù)字,包括形態(tài)學(xué)操作、閾值和輪廓提取。
- 從四個(gè)分組中提取每個(gè)單獨(dú)的數(shù)字,得到16個(gè)需要分類的數(shù)字。
- 將模板匹配應(yīng)用于每個(gè)數(shù)字,將其與OCR-A字體進(jìn)行比較,以獲得數(shù)字分類。
- 檢查信用卡號的第一位數(shù)字以確定發(fā)卡公司。
在對信用卡OCR系統(tǒng)進(jìn)行評估后,發(fā)現(xiàn)如果發(fā)卡信用卡公司使用OCR-A字體作為數(shù)字,該系統(tǒng)的準(zhǔn)確率為100%。 優(yōu)化可以考慮在野外采集信用卡的真實(shí)圖像,并訓(xùn)練機(jī)器學(xué)習(xí)模型(通過標(biāo)準(zhǔn)特征提取或訓(xùn)練或卷積神經(jīng)網(wǎng)絡(luò)),以進(jìn)一步提高此系統(tǒng)的準(zhǔn)確性。
1. 效果圖
首先了解一下卡的組成:
OCR-A 參考字體識(shí)別如下:原始圖 VS 灰度圖 VS 閾值化圖 VS 輪廓每個(gè)數(shù)字提取圖:
灰度圖:忽略顏色對輪廓提取的影響
閾值化圖:使得輪廓在前景白色,背景黑色便于輪廓提取。
輪廓提取圖:提取每個(gè)數(shù)字ROI并記錄,方便后續(xù)對比卡片中的區(qū)域以識(shí)別出對應(yīng)的數(shù)字。
以下卡號均是演示卡,
正確的識(shí)別卡的類型和卡號,效果圖1:
識(shí)別過程1——原圖 VS 灰度圖 VS 白帽圖 VS 梯度圖如下:
灰度圖:忽略色彩影響
白帽圖:從較暗的背景中提取較亮的區(qū)域
梯度圖:計(jì)算Schaar梯度圖,便于了解圖像的色彩分配及提?。?/p>
識(shí)別過程2——形態(tài)學(xué)閉合圖 VS 二值化圖1 VS 閾值化圖2 如下:
形態(tài)學(xué)閉合圖:矩形框形態(tài)學(xué)閉合操作,以幫助閉合信用卡數(shù)字之間的小的縫隙
二值化圖:以便于提取
閾值化圖:方形框形態(tài)學(xué)閉合操作,以二次幫助閉合信用卡數(shù)字區(qū)域之間的縫隙
識(shí)別過程3——輪廓過濾圖 VS 提取最終效果圖 如下:
輪廓過濾圖:根據(jù)面積及縱橫比,只保留卡片中的卡號區(qū)
最終效果圖:提取4組4數(shù)字每一個(gè)組,然后對每一個(gè)組中的4個(gè)數(shù)字進(jìn)行截取ROI并識(shí)別,并與之前存儲(chǔ)的數(shù)字ROI進(jìn)行模板匹配,選取匹配值最高的作為最終結(jié)果。
2. 原理
2.1 OCR-A字體
OCR-A字體,是一種專門用于輔助光學(xué)字符識(shí)別算法的字體。
主要分為:
檢測圖像中信用卡的位置;本地化信用卡上的四組四位數(shù)字;應(yīng)用OCR識(shí)別信用卡上的16位數(shù)字;識(shí)別信用卡的類型。
Tesseract庫在某些情況無法正確識(shí)別數(shù)字(這可能是因?yàn)門esseract未接受信用卡示例字體培訓(xùn))。
2.2 檢測過程步驟
在字典中存儲(chǔ)卡類型映射關(guān)系(卡號的第一位數(shù)字代表卡類型)。獲取參考圖像并提取數(shù)字。將數(shù)字模板存儲(chǔ)在字典中。本地化四個(gè)信用卡號組,每個(gè)組有四位數(shù)字(總共16位)。提取要“匹配”的數(shù)字。對每個(gè)數(shù)字執(zhí)行模板匹配,將每個(gè)單獨(dú)的ROI與每個(gè)數(shù)字模板0-9進(jìn)行比較,同時(shí)存儲(chǔ)每個(gè)嘗試匹配的分?jǐn)?shù)。查找每個(gè)候選數(shù)字的最高分?jǐn)?shù),并構(gòu)建一個(gè)名為“輸出”的列表。其中包含信用卡號。將信用卡號和信用卡類型輸出到終端,并將輸出圖像顯示到屏幕上。
2.3 優(yōu)化
使用OpenCV和Python匹配OCR腳本的模板在100%的時(shí)間內(nèi)正確識(shí)別了16位數(shù)字中的每一位。然而在將OCR圖像應(yīng)用于真實(shí)的信用卡圖像時(shí),考慮到照明條件、視角和其他一般噪音的變化,可能需要采取更面向機(jī)器學(xué)習(xí)的方法。
3. 源代碼
# 信用卡類型及卡號OCR系統(tǒng) # USAGE # python ocr_template_match.py --reference images/ocr_a_reference.png --image images/credit_card_05.png import argparse import cv2 import imutils import numpy as np # 導(dǎo)入必要的包 from imutils import contours # 構(gòu)建命令行參數(shù)及解析 # --image 必須 要進(jìn)行OCR的輸入圖像 # --reference 必須 參考OCR-A圖像 ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="path to input image") ap.add_argument("-r", "--reference", required=True, help="path to reference OCR-A image") args = vars(ap.parse_args()) # 定義一個(gè)字典(映射信用卡第一位數(shù)字和信用卡類型的編號) FIRST_NUMBER = { "3": "American Express", "4": "Visa", "5": "MasterCard", "6": "Discover Card" } # 從磁盤加載參考OCR-A圖像,轉(zhuǎn)換為灰度圖,閾值化圖像以顯示為白色前景和黑色背景 # 并反轉(zhuǎn)圖像 # and invert it, such that the digits appear as *white* on a *black* ref_origin = cv2.imread(args["reference"]) cv2.imshow("ref_origin", ref_origin) ref = ref_origin.copy() ref = cv2.cvtColor(ref, cv2.COLOR_BGR2GRAY) cv2.imshow("ref_gray", ref) ref = cv2.threshold(ref, 180, 255, cv2.THRESH_BINARY)[1] cv2.imshow("ref_threshhold", ref) cv2.waitKey(0) # 尋找OCR-A圖像中的輪廓(數(shù)字的外輪廓線) # 并從左到右排序輪廓,初始化一個(gè)字典來存儲(chǔ)數(shù)字ROI refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) print('findContours: ', len(refCnts)) refCnts = imutils.grab_contours(refCnts) refCnts = contours.sort_contours(refCnts, method="left-to-right")[0] digits = {} # 遍歷OCR-A輪廓 for (i, c) in enumerate(refCnts): # 計(jì)算數(shù)字的邊界框,提取它,縮放到固定的大小 (x, y, w, h) = cv2.boundingRect(c) cv2.rectangle(ref_origin, (x, y), (x + w, y + h), (0, 255, 0), 2) roi = ref[y:y + h, x:x + w] roi = cv2.resize(roi, (57, 88)) # 更新數(shù)字字典,數(shù)字匹配ROI digits[i] = roi cv2.imshow("ref and digits", ref_origin) cv2.waitKey(0) # 初始化矩形和方形結(jié)構(gòu)內(nèi)核 # 在圖像上滑動(dòng)它來進(jìn)行(卷積)操作,如模糊、銳化、邊緣檢測或其他圖像處理操作。 # 使用矩形函數(shù)作為Top-hat形態(tài)學(xué)運(yùn)算符,使用方形函數(shù)作為閉合運(yùn)算。 rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 準(zhǔn)備進(jìn)行OCR的輸入圖像 # 加載輸入圖像,保持縱橫比縮放圖像寬度為300,轉(zhuǎn)換為灰度圖 origin = cv2.imread(args["image"]) origin = imutils.resize(origin, width=300) image = origin.copy() cv2.imshow("origin", origin) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv2.imshow("gray", gray) # 執(zhí)行形態(tài)學(xué)操作 # 應(yīng)用tophat(白帽)形態(tài)學(xué)操作以在暗的背景中提取出亮的區(qū)域(信用卡上的數(shù)字卡號) # Top hat操作在深色背景(即信用卡號)下顯示淺色區(qū)域 tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) cv2.imshow("tophat", tophat) # 計(jì)算Scharr梯度,計(jì)算梯度值 # 在白色禮帽上,計(jì)算x方向的Scharr梯度,然后縮放到范圍[0, 255] gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) gradX = np.absolute(gradX) (minVal, maxVal) = (np.min(gradX), np.max(gradX)) # 最小/最大歸一化, 由float轉(zhuǎn)換gradX到uint8范圍[0-255] gradX = (255 * ((gradX - minVal) / (maxVal - minVal))) gradX = gradX.astype("uint8") cv2.imshow("gradient", gradX) # 使用矩形框應(yīng)用閉合操作以幫助閉合信用卡數(shù)字之間的小的縫隙 # 應(yīng)用Otsu's閾值方法二值化圖像 gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) cv2.imshow("morphologyEx", gradX) thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv2.imshow("thresh1", thresh) # 在二值化圖像上,應(yīng)用二次閉合操作 # 再一次方形框形態(tài)學(xué)操作,幫助閉合信用卡數(shù)字區(qū)域之間的縫隙 thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) cv2.imshow("thresh2", thresh) # 閾值圖像中查找輪廓,然后初始化數(shù)字位置列表 cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) locs = [] # 遍歷輪廓 for (i, c) in enumerate(cnts): # 計(jì)算輪廓的邊界框,并計(jì)算縱橫比 (x, y, w, h) = cv2.boundingRect(c) ar = w / float(h) # 由于信用卡有固定的4組4數(shù)字,可以根據(jù)縱橫比來尋找潛在的輪廓 if ar > 2.5 and ar < 4.0: # 輪廓可以在最小/最大寬度上進(jìn)一步修剪 if (w > 40 and w < 55) and (h > 10 and h < 20): # 添加數(shù)字組輪廓的編輯框輪廓到位置list locs.append((x, y, w, h)) cv2.rectangle(origin, (x, y), (x + w, y + h), (255, 0, 0), -1) cv2.imshow("contours filter", origin) # 突出顯示信用卡上四組四位數(shù)字(總共十六位)。 # 從左到右排序輪廓,并初始化list來存儲(chǔ)信用卡數(shù)字列表 locs = sorted(locs, key=lambda x: x[0]) output = [] # 遍歷四組四位數(shù)字 for (i, (gX, gY, gW, gH)) in enumerate(locs): # 初始化存放每組數(shù)字的list groupOutput = [] # 提取每組4位數(shù)字的灰度圖ROI # 應(yīng)用閾值方法從背景信用卡中分割數(shù)字 group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5] group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 檢測組中每個(gè)單獨(dú)數(shù)字的輪廓 # 從左到右排序輪廓 digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) digitCnts = imutils.grab_contours(digitCnts) digitCnts = contours.sort_contours(digitCnts, method="left-to-right")[0] # 遍歷數(shù)字輪廓 for c in digitCnts: # 計(jì)算每個(gè)單獨(dú)數(shù)字的邊界框 # 提取數(shù)字,縮放以擁有和參考OCR-A字體模板圖像相同的大小 (x, y, w, h) = cv2.boundingRect(c) roi = group[y:y + h, x:x + w] roi = cv2.resize(roi, (57, 88)) # 初始化模板匹配分?jǐn)?shù)list scores = [] # 遍歷參考數(shù)字名和數(shù)字ROI for (digit, digitROI) in digits.items(): # 應(yīng)用基于相關(guān)性的模板匹配,計(jì)算分?jǐn)?shù),更新分?jǐn)?shù)list # apply correlation-based template matching, take the # score, and update the scores list result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF) (_, score, _, _) = cv2.minMaxLoc(result) scores.append(score) # 數(shù)字ROI的分類將取 模板匹配分?jǐn)?shù)中分?jǐn)?shù)最大的參考數(shù)字 # the classification for the digit ROI will be the reference # digit name with the *largest* template matching score groupOutput.append(str(np.argmax(scores))) # 圍繞每組畫一個(gè)矩形,并以紅色文本標(biāo)識(shí)圖像上的信用卡號 # 繪制每組的數(shù)字識(shí)別分類結(jié)果 cv2.rectangle(image, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 2) cv2.putText(image, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2) # 更新輸出數(shù)字分組列表 # Pythonic的方法是使用extend函數(shù),它將iterable對象的每個(gè)元素(本例中為列表)追加到列表的末尾 output.extend(groupOutput) # 顯示檢測到的信用卡類型和卡號到屏幕上 print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]])) print("Credit Card #: {}".format("".join(output))) cv2.imshow("Image", image) cv2.waitKey(0)
參考 https://www.pyimagesearch.com/2017/07/17/credit-card-ocr-with-opencv-and-python/
到此這篇關(guān)于使用Pyhton+OpenCV進(jìn)行卡類型及16位卡號數(shù)字的OCR功能的文章就介紹到這了,更多相關(guān)Pyhton+OpenCV卡號數(shù)字識(shí)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python爬蟲_城市公交、地鐵站點(diǎn)和線路數(shù)據(jù)采集實(shí)例
下面小編就為大家分享一篇Python爬蟲_城市公交、地鐵站點(diǎn)和線路數(shù)據(jù)采集實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01Python爬蟲框架Scrapy實(shí)戰(zhàn)之批量抓取招聘信息
網(wǎng)絡(luò)爬蟲又被稱為網(wǎng)頁蜘蛛,網(wǎng)絡(luò)機(jī)器人,在FOAF社區(qū)中間,更經(jīng)常的稱為網(wǎng)頁追逐者,是按照一定的規(guī)則,自動(dòng)抓取萬維網(wǎng)信息的程序或者腳本。這篇文章主要介紹Python爬蟲框架Scrapy實(shí)戰(zhàn)之批量抓取招聘信息,有需要的朋友可以參考下2015-08-08Python數(shù)據(jù)處理之pd.Series()函數(shù)的基本使用
Series是帶標(biāo)簽的一維數(shù)組,可存儲(chǔ)整數(shù)、浮點(diǎn)數(shù)、字符串、Python 對象等類型的數(shù)據(jù),軸標(biāo)簽統(tǒng)稱為索引,下面這篇文章主要給大家介紹了關(guān)于Python數(shù)據(jù)處理之pd.Series()函數(shù)的基本使用,需要的朋友可以參考下2022-06-06PyQt5主窗口動(dòng)態(tài)加載Widget實(shí)例代碼
這篇文章主要介紹了PyQt5主窗口動(dòng)態(tài)加載Widget實(shí)例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02Python3使用正則表達(dá)式爬取內(nèi)涵段子示例
這篇文章主要介紹了Python3使用正則表達(dá)式爬取內(nèi)涵段子,涉及Python正則匹配與文件讀寫相關(guān)操作技巧,需要的朋友可以參考下2018-04-04深入理解?Python?中的?pip?虛擬環(huán)境(最佳實(shí)踐)
本文深入講解了Python中pip虛擬環(huán)境的概念及其重要性,并詳細(xì)介紹了如何創(chuàng)建、激活和管理虛擬環(huán)境,以及如何使用requirements.txt文件記錄和管理項(xiàng)目依賴,文章指出,使用虛擬環(huán)境可以有效避免依賴沖突,為每個(gè)項(xiàng)目提供一個(gè)干凈的開發(fā)環(huán)境,使得項(xiàng)目更易于維護(hù)和部署2024-10-10MxNet預(yù)訓(xùn)練模型到Pytorch模型的轉(zhuǎn)換方式
這篇文章主要介紹了MxNet預(yù)訓(xùn)練模型到Pytorch模型的轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05基于python實(shí)現(xiàn)復(fù)制文件并重命名
這篇文章主要介紹了基于python實(shí)現(xiàn)復(fù)制文件并重命名,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09