數字圖像處理——信用卡識別

在數字支付時代,信用卡處理自動化技術日益重要。本文介紹如何利用Python和OpenCV實現信用卡數字的自動識別,結合圖像處理與模式識別技術,具有顯著實用價值。

系統概述與工作原理

信用卡數字識別系統包含兩大核心模塊:模板數字預處理和信用卡圖像識別。系統先處理標準數字模板(0-9),再提取信用卡圖像中的數字區域進行匹配識別。

識別流程采用多種圖像處理技術,包括灰度轉換、閾值處理、形態學操作和輪廓檢測。通過分析數字形狀特征和空間關系,系統能精確定位并識別信用卡數字序列。

環境配置與依賴安裝

確保安裝以下Python庫:

pip install opencv-python numpy

Anaconda用戶可使用:

conda install -c conda-forge opencv

驗證模塊導入:

import cv2
import numpy as np

模板數字處理

處理標準數字模板作為識別基準:

# 讀取模板圖像
# 使用cv2.imread函數從文件路徑'number.png'讀取圖像[1,2](@ref)
# 返回值img是一個numpy數組,表示圖像的像素數據,默認以BGR格式存儲[1,2](@ref)
img = cv2.imread('number.png')# 將圖像轉換為灰度圖
# 使用cv2.cvtColor將BGR格式的彩色圖像轉換為單通道灰度圖像[2,3](@ref)
# 灰度化是許多圖像處理任務(如二值化、輪廓檢測)常見的預處理步驟,能減少計算復雜度[2,3,4](@ref)
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 對灰度圖像進行二值化處理
# 使用cv2.threshold函數進行閾值處理[4,5](@ref)
# 參數10是閾值:像素值大于10的將被設置為255(白色),小于等于10的將被設置為0(黑色)[5](@ref)
# cv2.THRESH_BINARY_INV 表示采用反向二值化模式,即通常的黑底白字變為白底黑字,便于輪廓檢測[4](@ref)
# 函數返回一個元組,我們通過[1]索引取第二個元素,即二值化后的圖像矩陣[4](@ref)
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]# 在二值圖像中查找輪廓
# 使用cv2.findContours函數檢測ref圖像中的輪廓[4](@ref)
# ref.copy() 使用圖像的副本進行操作,避免修改原始圖像
# cv2.RETR_EXTERNAL 表示只檢測最外層輪廓,忽略內部嵌套的輪廓(如數字0的內部)[4](@ref)
# cv2.CHAIN_APPROX_SIMPLE 壓縮水平、垂直和對角方向的冗余點,僅保留輪廓的端點,節省內存[4](@ref)
# 函數返回兩個值:輪廓列表(refCnts)和層次信息(此處用_忽略)[4](@ref)
refCnts, _ = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 創建一個空字典用于存儲標準化后的數字模板
digits = {}# 遍歷檢測到的所有輪廓(refCnts),i是索引,c是每個輪廓的點集
for i, c in enumerate(refCnts):# 獲取每個輪廓的外接矩形[4](@ref)# cv2.boundingRect(c) 返回一個元組 (x, y, w, h)# (x, y) 是矩形左上角的坐標,w是寬度,h是高度[4](@ref)x, y, w, h = cv2.boundingRect(c)# 從二值化圖像ref中提取出當前輪廓所包圍的區域(Region of Interest, ROI)# 使用數組切片操作,取y到y+h行,x到x+w列的區域roi = ref[y:y+h, x:x+w]# 將提取出的ROI(數字區域)縮放到統一尺寸 (57, 88)# 標準化模板尺寸對于后續的模板匹配或機器學習步驟至關重要,可以消除原始圖像中數字大小不一的影響[6](@ref)# 將縮放后的模板存儲在字典digits中,鍵為數字的索引i,值為對應的圖像矩陣digits[i] = cv2.resize(roi, (57, 88))

??關鍵處理步驟與技術要點:??

  1. 1??圖像讀取與色彩空間轉換??:OpenCV 默認使用 BGR 格式讀取圖像,這與許多其他庫(如 matplotlib 的 RGB)不同,在處理顏色時需要注意。轉換為灰度圖是簡化后續處理的關鍵步驟。
  2. ??二值化(Thresholding)??:通過設定一個閾值(此代碼中為 10),將灰度圖像轉換為僅包含純黑(0)和純白(255)像素的二值圖像。使用?THRESH_BINARY_INV進行反向二值化,使得數字本身變為白色(前景),背景變為黑色,這通常是輪廓檢測所期望的輸入。
  3. 輪廓檢測(findContours)??:在二值圖像上,輪廓檢測能夠找出所有白色區域的邊界RETR_EXTERNAL模式只檢索最外層的輪廓,這對于提取獨立的數字塊非常有效,避免了將數字內部(如08)的空洞也作為輪廓檢出。CHAIN_APPROX_SIMPLE通過壓縮冗余點來節省內存。
  4. ??提取與標準化(ROI & Resize)??:通過?boundingRect獲取每個輪廓(即每個數字)的精確位置和大小,然后從原圖中截取出該區域(ROI)。最后,將所有數字 ROI 縮放到統一的尺寸(57x88 像素),這是為了確保后續進行模板匹配或特征提取時,所有模板都具有相同的大小,從而保證比較的公平性和準確性。

信用卡圖像預處理

關鍵預處理步驟突出數字區域:

# 使用OpenCV的imread函數讀取信用卡圖像文件
# 'picture/credit1.png' 是圖像文件的路徑
# 返回值image是一個包含圖像像素數據的numpy數組,默認以BGR格式存儲[3,12](@ref)
image = cv2.imread('picture/credit1.png')# 調用自定義的resize函數調整圖像尺寸,保持寬高比
# 這里將圖像的寬度調整為300像素,高度會按比例自動計算
# 縮小圖像可以減少后續處理的計算量,提高處理速度[1,5](@ref)
image = resize(image, width=300)# 將BGR格式的彩色圖像轉換為灰度圖像
# 灰度化是許多圖像處理任務的基礎步驟,能減少計算復雜度和通道數[1,2](@ref)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 定義形態學操作中使用的結構元素(核)
# 第一個是矩形核,尺寸為(9, 3),寬度較大,適合用于連接水平方向的數字[5,6](@ref)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
# 第二個是方形核,尺寸為(5, 5),用于后續的閉操作以填充小孔洞和連接區域[5](@ref)
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 應用頂帽(禮帽)形態學操作
# 頂帽操作是原圖像與開操作結果的差,能突出比周圍更亮的細小區域
# 在這里用于增強信用卡上明亮的數字區域,同時抑制暗背景[1,5](@ref)
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)# 應用閉操作(先膨脹后腐蝕)
# 使用相同的矩形核進行閉操作,有助于連接數字中間斷開的區域(如數字4、8等)
# 使數字區域更加連貫,便于后續的輪廓檢測[2,6](@ref)
closeX = cv2.morphologyEx(tophat, cv2.MORPH_CLOSE, rectKernel)# 對圖像進行二值化處理
# 使用OTSU's方法自動計算最佳閾值,適用于處理灰度分布不均的圖像
# cv2.THRESH_BINARY | cv2.THRESH_OTSU 組合表示使用OTSU算法進行自動閾值二值化
# 函數返回一個元組,[1]獲取二值化后的圖像[7](@ref)
thresh = cv2.threshold(closeX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]# 再次應用閉操作,這次使用方形核
# 目的是進一步填充數字內部可能存在的微小孔洞,并增強數字區域的連通性
# 使數字輪廓更加完整,為后續的輪廓檢測做好準備[6](@ref)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)

代碼功能總結:??

這段代碼是信用卡數字識別流程中的??圖像預處理部分??,主要目的是從信用卡圖像中突出并增強數字區域,同時抑制背景干擾。其處理流程包括:

  1. 圖像讀取與尺寸調整??:讀取原始圖像并調整到合適尺寸,平衡處理速度與細節保留。
  2. 色彩空間轉換??:將彩色圖像轉換為灰度圖像,簡化后續處理。
  3. 形態學核定義??:定義不同形狀和尺寸的結構元素,用于后續的形態學操作。
  4. ??圖像增強??:
    • 頂帽操作??:突出明亮的數字區域。
    • ??閉操作??:連接數字中斷開的部分,增強其連通性。
  5. ??二值化??:將圖像轉換為黑白二值圖,便于輪廓檢測。
  6. ??進一步形態學處理??:使用不同核進行閉操作,優化二值化結果,填充空洞,使數字輪廓更完整。

數字區域定位與提取

篩選有效數字區域:

# 在二值圖像thresh中查找輪廓
# thresh.copy(): 使用二值圖像的副本進行操作,避免修改原始圖像
# cv2.RETR_EXTERNAL: 只檢測最外層輪廓,忽略內部嵌套的輪廓(如數字內部的孔洞)
# cv2.CHAIN_APPROX_SIMPLE: 壓縮水平、垂直和對角方向的冗余點,僅保留輪廓的端點
# 函數返回兩個值:輪廓列表(threshCnts)和層次結構信息(此處用_忽略)
threshCnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 初始化一個空列表,用于存儲符合篩選條件的數字區域位置信息
locs = []# 遍歷檢測到的所有輪廓,i是索引,c是每個輪廓的點集
for i, c in enumerate(threshCnts):# 獲取每個輪廓的外接矩形# cv2.boundingRect(c) 返回一個元組 (x, y, w, h)# (x, y) 是矩形左上角的坐標,w是寬度,h是高度x, y, w, h = cv2.boundingRect(c)# 計算輪廓的寬高比(Aspect Ratio)# 信用卡數字通常具有特定的寬高比特征[4](@ref)ar = w / float(h)# 基于寬高比和物理尺寸篩選可能是數字的輪廓# 2.5 < ar < 4.0: 典型的信用卡數字寬高比范圍(數字通常比高度寬)# 40 < w < 55: 數字寬度的合理范圍(基于圖像縮放后的尺寸)# 10 < h < 20: 數字高度的合理范圍# 這些閾值需要根據實際圖像尺寸和數字字體進行調整[16](@ref)if 2.5 < ar < 4.0 and 40 < w < 55 and 10 < h < 20:# 如果輪廓符合篩選條件,將其位置信息(坐標和尺寸)添加到locs列表中locs.append((x, y, w, h))# 對篩選出的數字區域按照x坐標(水平位置)進行排序
# 使用Python內置的sort方法,key=lambda x: x[0]表示按照元組的第一個元素(x坐標)排序
# 這確保了數字按照從左到右的正確順序排列,為后續的數字識別提供正確的順序[5,6](@ref)
locs.sort(key=lambda x: x[0])

代碼功能總結:??

這段代碼是信用卡數字識別流程中的??關鍵步驟??,主要完成以下任務:

  1. ??輪廓檢測??:在預處理后的二值圖像上查找所有可能包含數字的區域輪廓
  2. 2??輪廓篩選??:基于數字的??幾何特征??(寬高比和物理尺寸)從所有輪廓中篩選出最可能是數字的區域
    • 寬高比(Aspect Ratio)??:信用卡上的數字通常具有特定的寬高比(此代碼中為2.5到4.0之間),這是一個非常重要的篩選條件,可以有效地排除許多非數字的干擾輪廓
    • 寬度和高度??:同時限制數字的絕對尺寸(寬度40-55像素,高度10-20像素),確保篩選出的區域大小符合預期。這些具體的閾值需要根據圖像的實際分辨率和預處理時的縮放比例進行調整
  3. ??位置排序??:將篩選出的數字區域按照它們的水平位置(x坐標)進行排序,確保數字按照從左到右的正確順序排列,這對于后續的數字識別和拼接成完整的卡號至關重要

數字識別與匹配

單個數字識別過程:

# 初始化一個空列表output,用于存儲最終識別出的信用卡數字序列
output = []# 遍歷之前篩選出的四個數字組區域(locs中的每個元素代表一個數字組的坐標和尺寸:gX, gY, gW, gH)
for gX, gY, gW, gH in locs:# 從灰度圖像gray中提取當前數字組的感興趣區域(ROI)# 為了確保完整捕獲數字,在原始坐標基礎上上下左右各擴展5個像素group = gray[gY-5:gY+gH+5, gX-5:gX+gW+5]# 對提取出的數字組區域進行二值化處理# 使用OTSU's方法自動計算最佳閾值,適用于處理灰度分布不均的圖像# cv2.THRESH_BINARY | cv2.THRESH_OTSU 組合表示使用OTSU算法進行自動閾值二值化# 函數返回一個元組,[1]獲取二值化后的圖像矩陣group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]# 在二值化的數字組圖像中查找輪廓# group.copy(): 使用圖像的副本進行操作,避免修改原始圖像# cv2.RETR_EXTERNAL: 只檢測最外層輪廓,忽略內部嵌套的輪廓# cv2.CHAIN_APPROX_SIMPLE: 壓縮冗余點,僅保留輪廓端點# 返回輪廓列表(digitCnts)和層次結構信息(此處用_忽略)digitCnts, _ = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 對檢測到的數字輪廓進行排序,確保按從左到右的正確順序排列# sort_contours函數返回排序后的輪廓和邊界框,這里取[0]即排序后的輪廓列表digitCnts = sort_contours(digitCnts, method="left-to-right")[0]# 遍歷當前數字組中的每個數字輪廓for c in digitCnts:# 獲取當前數字輪廓的外接矩形# cv2.boundingRect(c) 返回一個元組 (x, y, w, h)# (x, y) 是矩形左上角在group圖像中的坐標,w是寬度,h是高度x, y, w, h = cv2.boundingRect(c)# 從數字組圖像group中提取出當前數字的區域# 使用數組切片操作,取y到y+h行,x到x+w列的區域# 然后將該數字區域縮放到與模板相同的尺寸(57, 88)像素# 標準化尺寸對于后續的模板匹配至關重要,確保公平比較[1,9,18](@ref)roi = cv2.resize(group[y:y+h, x:x+w], (57, 88))# 模板匹配:將當前數字ROI與模板字典digits中的所有數字模板進行匹配# 計算與每個模板數字的匹配得分# 使用列表推導式遍歷digits字典中的所有模板數字(d)# cv2.matchTemplate(roi, d, cv2.TM_CCOEFF): 使用相關系數法進行模板匹配# cv2.minMaxLoc(...)[1]: 獲取匹配結果中的最大值(對于TM_CCOEFF,最大值表示最佳匹配)# 最終scores列表包含當前數字ROI與0-9每個模板數字的匹配得分scores = [cv2.minMaxLoc(cv2.matchTemplate(roi, d, cv2.TM_CCOEFF))[1] for d in digits.values()]# 找出得分最高的索引,即識別出的數字# np.argmax(scores)返回scores列表中最大值的索引# 由于digits字典的鍵是0-9(按模板中的順序),索引即對應數字本身# 將識別出的數字轉換為字符串并添加到output列表中output.append(str(np.argmax(scores)))

代碼功能總結:??

這段代碼是信用卡數字識別流程中的??核心識別部分??,主要完成以下任務:

  1. ??數字組提取與預處理??:從已定位的四個數字組區域中提取ROI,并進行二值化處理,為單個數字的分割和識別做準備
  2. ??單個數字分割??:在每個數字組內部進行輪廓檢測,找到并分離出各個獨立的數字
  3. ??數字標準化??:將每個分割出的數字區域縮放到與模板相同的尺寸(57x88像素),消除大小差異對匹配的影響
  4. ??模板匹配與識別??:將待識別的數字與預先準備好的0-9模板庫進行相似度比較(使用相關系數法?TM_CCOEFF),選擇相似度最高的模板所對應的數字作為識別結果。這是一種直接利用圖像相似性進行識別的方法。
  5. 結果存儲??:將識別出的數字按順序存儲在列表?output中,最終?output列表將包含信用卡上所有識別出的數字序列。

工具函數

輔助功能實現:

import cv2def sort_contours(cnts, method='left-to-right'):"""對輪廓進行排序。該函數根據指定的方向方法對找到的輪廓進行排序,常用于OCR等需要確定輪廓順序的場景。參數:cnts (list): 待排序的輪廓列表,通常由cv2.findContours函數得到。method (str): 排序方法,可選:'left-to-right'(默認):從左到右'right-to-left':從右到左'top-to-bottom':從上到下'bottom-to-top':從下到上返回:tuple: 返回一個元組,包含兩個元素:- 排序后的輪廓列表 (cnts)- 對應的邊界框列表 (boundingBoxes),每個邊界框格式為 (x, y, w, h)"""# 初始化排序方向和索引reverse = False  # 默認升序(例如,左到右,上到下)i = 0            # 默認按x坐標排序(用于水平方向排序)# 判斷是否需要反向排序(從右到左或從下到上)if method == 'right-to-left' or method == 'bottom-to-top':reverse = True# 判斷是否按y坐標排序(用于垂直方向排序:從上到下或從下到上)if method == 'top-to-bottom' or method == 'bottom-to-top':i = 1  # 將索引改為1,表示對邊界框的y坐標進行排序# 為每個輪廓計算其外接矩形(邊界框)# cv2.boundingRect(c) 返回一個元組 (x, y, w, h),表示能包圍輪廓的最小矩形的左上角坐標(x,y)和寬高(w,h)boundingBoxes = [cv2.boundingRect(c) for c in cnts]# 將輪廓(cnts)和其對應的邊界框(boundingBoxes)打包成元組列表,然后根據邊界框的特定坐標(x或y)進行排序# key=lambda b: b[1][i]:b是 (輪廓, 邊界框) 的元組,b[1]是邊界框,b[1][i] 是邊界框的第i個坐標(i=0為x,i=1為y)# reverse 參數控制升序或降序sorted_list = sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse)# 使用zip(*...)將排序后的元組列表解壓回兩個獨立的列表(輪廓和邊界框)cnts, boundingBoxes = zip(*sorted_list)# 返回排序后的輪廓列表和邊界框列表return cnts, boundingBoxesdef resize(image, width=None, height=None, inter=cv2.INTER_AREA):"""按比例縮放圖像,保持原始圖像的寬高比。參數:image (numpy.ndarray): 輸入圖像。width (int, optional): 目標寬度。如果為None,則根據高度計算。height (int, optional): 目標高度。如果為None,則根據寬度計算。inter (int, optional): 插值方法,默認為cv2.INTER_AREA(用于縮小圖像)。返回:numpy.ndarray: 縮放后的圖像。注意:必須指定width或height中的一個(但不能同時為None)。"""# 獲取圖像原始高度(h)和寬度(w)h, w = image.shape[:2]# 如果寬高都未指定,直接返回原圖if width is None and height is None:return imagedim = None  # 初始化目標尺寸 (width, height)# 如果指定了寬度,但高度為None,則根據寬度和原始寬高比計算新高度if width is not None:r = width / float(w)  # 計算寬度縮放比例dim = (width, int(h * r))  # 新尺寸為 (指定寬度, 按比例計算的高度)# 否則,如果指定了高度,則根據高度和原始寬高比計算新寬度elif height is not None:r = height / float(h)  # 計算高度縮放比例dim = (int(w * r), height)  # 新尺寸為 (按比例計算的寬度, 指定高度)# 使用cv2.resize函數進行圖像縮放,inter參數指定插值方法resized = cv2.resize(image, dim, interpolation=inter)return resized

關鍵點說明:??

.??sort_contours函數??:

??核心思想??:通過計算每個輪廓的邊界框(cv2.boundingRect),然后根據邊界框的特定坐標(x 或 y)進行排序,從而實現輪廓的排序。
??應用場景??:在OCR(光學字符識別)項目中非常常見,例如對銀行卡號數字、印刷文本的輪廓進行排序,以確保正確的讀取順序(如從左到右)。


??zip和?sorted的妙用??:zip(cnts, boundingBoxes)將輪廓和其邊界框一一對應打包。sorted(...)根據指定的坐標排序。zip(*sorted_list)則將排序后的結果重新解包成兩個獨立的列表。


??resize函數??:
??核心思想??:在改變圖像尺寸時,始終保持圖像的原始寬高比,避免圖像變形。
??插值方法??:cv2.INTER_AREA插值方法通常用于圖像縮小,它能產生較好的結果。
結果輸出與信用卡類型識別

最終識別輸出:

CARD_TYPE = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"
}print(f"Credit Card Type: {CARD_TYPE[output[0]]}")
print(f"Credit Card #: {''.join(output)}")# 可視化結果
cv2.rectangle(image, (gX-5, gY-5), (gX+gW+5, gY+gH+5), (0, 0, 255), 1)
cv2.putText(image, "".join(output), (gX, gY-15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
cv2.imshow("Result", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

完整代碼

工具函數

myutils.py

import cv2def sort_contours(cnts, method='left-to-right'):reverse = Falsei=0if method == 'right-to-left' or method == 'bottom-to-top':reverse = Trueif method == 'top-to-bottom' or method == 'bottom-to-top':i=1boundingBoxes = [cv2.boundingRect(c) for c in cnts](cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda x: x[1][i], reverse=reverse))return (cnts, boundingBoxes)def resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resized

主代碼

import cv2
import numpy as np
import argparse
import myutils# ap = argparse.ArgumentParser()
# ap.add_argument("-i", "--image", required=True,help="path to input image")
#
# ap.add_argument("-t", "--output", 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"}# img = cv2.imread(args["templater"])
img = cv2.imread('number.png')
# cv2.imshow("templater", img)
# cv2.waitKey(0)ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("ref", ref)
cv2.waitKey(0)
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
# cv2.imshow("ref", ref)
# cv2.waitKey(0)refCnts,hierarchy = cv2.findContours(ref,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img, refCnts, -1, (0,0,255), 3)
cv2.imshow("img", img)
cv2.waitKey(0)digits = {}
for (i,c) in enumerate(refCnts):(x,y,w,h) = cv2.boundingRect(c)roi = ref[y:y+h,x:x+w]roi = cv2.resize(roi, (57,88))# cv2.imshow("ROI", roi)# cv2.waitKey(0)digits[i] = roi
print(digits)
# cv2.destroyAllWindows()'''信用卡的圖像處理'''
image = cv2.imread('picture/credit1.png')
cv2.imshow("image", image)
cv2.waitKey(0)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv2.imshow("tophat", tophat)
cv2.waitKey(0)closeX = cv2.morphologyEx(gray,cv2.MORPH_CLOSE,rectKernel)
# cv2.imshow("closeX", closeX)
# cv2.waitKey(0)
thresh = cv2.threshold(closeX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
# cv2.imshow("thresh", thresh)
# cv2.waitKey(0)
thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
# cv2.imshow("thresh1", thresh)
# cv2.waitKey(0)threshCnts,h =cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)
cv2.imshow("cur_img", cur_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
locs = []
for (i,c) in enumerate(cnts):(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))
locs = sorted(locs, key=lambda x: x[0])
output = []
for (i,(gX,gY,gW,gH)) in enumerate(locs):groupOutput = []group = gray[gY-5:gY+gH+5, gX-5:gX+gW+5]cv2.imshow("group", group)cv2.waitKey(0)group = cv2.threshold(group,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]cv2.imshow("group", group)cv2.waitKey(0)digitCnts,hierarchy = cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)digitCnts = myutils.sort_contours(digitCnts, method="left-to-right")[0]for c in digitCnts:(x,y,w,h) = cv2.boundingRect(c)roi = group[y:y+h,x:x+w]roi = cv2.resize(roi, (57,88))cv2.imshow("roi", roi)cv2.waitKey(0)score = []for (digit, digitROI) in digits.items():result = cv2.matchTemplate(roi,digitROI,cv2.TM_CCOEFF)scores = cv2.minMaxLoc(result)[1]score.append(scores)groupOutput.append(str(np.argmax(score)))cv2.rectangle(cur_img,(gX-5,gY-5),(gX+gW+5,gY+gH+5),(0,0,255),1)cv2.putText(image,"".join(groupOutput),(gX,gY-15),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2 )output.extend(groupOutput)
print(output[0])
# print("Credit Card Types:{}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #:{}".format("".join(output)))
cv2.imshow("results", image)
cv2.waitKey(0)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98424.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98424.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98424.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

嵌入式ARM64 基于RK3588原生SDK添加用戶配置選項./build lunch debian

1 背景 在我們正常拿到SDK后會有一些配置選項&#xff0c;在使用./build.sh lunch之后會輸出一些defautconfig讓我們選擇&#xff0c;瑞芯微的原廠sdk會提供一些主板的配置選項&#xff0c;但是我們的如果是一塊新的主板就需要添加自己的配置選項&#xff0c;本文就討論如何來添…

專為石油和天然氣檢測而開發的基于無人機的OGI相機

專為石油和天然氣檢測而開發的基于無人機的OGI相機基于無人機的 OGI 相機:&#xff08;Optical Gas Imaging&#xff0c;光學氣體成像&#xff09;其實是近幾年油氣、電力、化工等行業里非常熱門的應用方向。什么是 OGI 相機OGI&#xff08;Optical Gas Imaging&#xff09;&am…

iPhone17全系優缺點分析,加持遠程控制讓你的手機更好用!

知名數碼廠商蘋果&#xff0c;不久前已官宣將于北京時間9月10日凌晨1點開啟發布會&#xff0c;主打對于iPhone 17系列產品介紹&#xff0c;并且和以往不同的是&#xff0c;今年會在購物平臺上開啟線上直播&#xff0c;還是很有新意的。9.13全平臺渠道將開啟預售模式&#xff0c…

人工智能-python-深度學習-神經網絡VGG(詳解)

LeNet 系列之后 —— VGG&#xff08;詳解&#xff09;&#xff1a;從原理到 PyTorch 實現 文章目錄LeNet 系列之后 —— **VGG&#xff08;詳解&#xff09;**&#xff1a;從原理到 PyTorch 實現1. VGG 的發展歷史與意義&#xff08;一句話&#xff0b;背景&#xff09;2. VGG…

光伏運維迎來云端革命!AcrelCloud-1200如何破解分布式光伏四大痛點?

在國家“雙碳”目標推動下&#xff0c;分布式光伏正迎來爆發式增長&#x1f31e;。甘肅、吉林、云南等多地政策接連落地&#xff0c;整縣推進屋頂光伏試點如火如荼&#xff01;然而&#xff0c;快速發展的背后&#xff0c;你是否也遇到過這些“光伏運維之痛”&#xff1f;?【痛…

將 maven 集成到 idea 后出現 向項目創建模塊時出錯:null 的問題

1.出現的問題今天想將maven繼承到idea出現了一下問題&#xff1a;用生成器里面的也會報錯&#xff0c;找了找帖子并沒有哪位大佬出現類似錯誤&#xff0c;于是我解決完想分享一下&#xff0c;如果有不對&#xff0c;請指正。2.解決辦法很可能是java 的 版本 與 maven 版本有問題…

類似于 Progress Telerik Fiddler Classic 的 免費 或 開源 HTTP/HTTPS 抓包與調試工具推薦

以下是一些 類似于 Progress Telerik Fiddler Classic 的 免費 或 開源 HTTP/HTTPS 抓包與調試工具推薦&#xff1a;免費 / 開源替代工具推薦 1. Wireshark 免費且開源的網絡協議分析工具&#xff0c;支持 Windows、macOS、Linux 等平臺。可捕獲并深入分析網絡流量&#xff0c;…

7.0 熱電偶的工作原理

在工業生產過程中&#xff0c;溫度是需要測量和控制的重要參數之一。在溫度測量中&#xff0c;熱電偶的應用極為廣泛&#xff0c;它具有結構簡單、制造方便、測量范圍廣、精度高、慣性小和輸出信號便于遠傳等許多優點。另外&#xff0c;由于熱電偶是一種無源傳感器&#xff0c;…

commons-lang3

概述 提供了許多幫助程序實用程序&#xff0c;特別是字符串操作方法&#xff0c;基本數值方法&#xff0c;對象反射&#xff0c;并發&#xff0c;創建和序列化以及系統屬性。maven依賴<dependency><groupId>org.apache.commons</groupId><artifactId>c…

vue-amap組件呈現的效果圖如何截圖

我們用amap呈現了幾個圖層后&#xff0c;用戶覺得效果很好&#xff0c;想點個按鈕直接將這個畫面截圖下來。 首先我們用Canvas的toDataURL方法可以直接獲取圖像數據&#xff0c;但是實踐發現截圖后是空白的。 原因在警告中&#xff1a; 地圖的WebGL context 的preserveDrawin…

杰理燒錄ERROR: Data error after erasing, address = 0x430000

把CONFIG_BOARD_DEV_KIT關閉&#xff0c;打開CONFIG_BOARD_DEVELOP

超越自動化:為什么說供應鏈的終局是“AI + 人類專家”的混合智能?

摘要&#xff1a;當前&#xff0c;圍繞AI賦能供應鏈的討論&#xff0c;大多聚焦于“自動化”帶來的降本增效。然而&#xff0c;這僅僅是第一層。當我們的系統面對“黑天鵝”事件時&#xff0c;一個過度依賴自動化的“脆弱”系統可能會瞬間崩潰。本文旨在深入探討供應鏈演進的下…

Spine文件導入Unity流程

1、轉為Json文件導出 2、對文件進行處理 3、添加Spine的Package包 一、Spine文件導出設置 1、選擇Json文件 2、選擇導出所在路徑 3、點擊打包設置 更改圖集擴展名 二、文件導出后的設置 1、修改Json的Spine版本 這里必須是3.8 三、下載Unity支持包 1、鏈接 spine-unit…

Docker Compose healthcheck介紹(監控容器中服務的實際健康狀態)數據庫健康檢查pg_isready

文章目錄**功能概述****核心參數詳解****配置示例****1. 基礎用法****2. 使用數據庫健康檢查****3. 結合 depends_on 控制啟動順序****高級用法****1. 自定義健康檢查腳本****2. 多種健康檢查類型**- **HTTP 檢查**&#xff1a;- **TCP 端口檢查**&#xff1a;- **Redis 檢查**…

算法之雙指針

在算法設計中&#xff0c;雙指針是一種高效優化工具&#xff0c;主要用于線性數據結構&#xff08;如數組&#xff08;數組劃分和數組分塊常用&#xff09;、鏈表、字符串&#xff09;&#xff0c;通過控制兩個指針的移動軌跡&#xff0c;將原本需要 O (n) 時間復雜度的問題優化…

冪等性、順序性保障以及消息積壓

冪等性 概念 在應用程序中&#xff0c;冪等性就是指對一個系統進行重復調用&#xff08;相同參數&#xff09;&#xff0c;不論請求多少次&#xff0c;這些請求對系統的影響都是相同的效果. 比如數據庫的select操作.不同時間兩次查詢的結果可能不同&#xff0c;但是這個操作…

算法訓練營DAY58 第十一章:圖論part08

拓撲排序精講 卡碼網&#xff1a;117. 軟件構建(opens new window) 題目描述&#xff1a; 某個大型軟件項目的構建系統擁有 N 個文件&#xff0c;文件編號從 0 到 N - 1&#xff0c;在這些文件中&#xff0c;某些文件依賴于其他文件的內容&#xff0c;這意味著如果文件 A 依…

如何在Python中使用正則表達式?

在Python中使用正則表達式主要通過內置的re模塊實現。正則表達式用于匹配、查找、替換字符串中的特定模式&#xff0c;是處理文本的強大工具。以下是使用正則表達式的核心方法和示例&#xff1a; 一、基本用法步驟 導入re模塊&#xff1a;import re定義正則表達式模式&#xff…

用 Trae 玩轉 Bright Data MCP 集成

引言 在自動化與智能體浪潮中&#xff0c;Trae 以“開箱即用、所見即所得”的工具編排體驗&#xff0c;成為個人與團隊落地 AI 工作流的高效選擇。本篇將以 Trae 為主角&#xff0c;展示如何通過最少配置完成與 Bright Data MCP 的對接&#xff0c;并快速構建一個可用、可觀測…

大數據Spark(六十三):RDD-Resilient Distributed Dataset

文章目錄 RDD-Resilient Distributed Dataset 一、RDD五大特性 二、RDD創建方式 RDD-Resilient Distributed Dataset 在 Apache Spark 編程中&#xff0c;RDD&#xff08;Resilient Distributed Dataset&#xff0c;彈性分布式數據集&#xff09;是 Spark Core 中最基本的數…