opencv bank card number recognition project based on template matching

Recently, in mending opencv, I started the actual combat of this small project under the condition of less serious learning in the early stage, and identified the bank card number based on template matching.

Overall idea:

First of all, for this project, we need to consider how to make the computer successfully identify the card number of the bank card and correctly identify the number corresponding to each card number. Then we provide an idea here. First, we recognize the contour of the bank card image, then get the contour part of the corresponding card number we need, then further recognize the contour in the contour, get the contour of each number, and finally draw the circumscribed rectangle of each number, Then, according to the external rectangular contour of the template identified in advance, the template matching can successfully identify the correct number.

preparation:

1. Convert the image to a grayscale image
2. Binary image processing
3. Draw the contour, get the contour part we need according to the different contour ratio (here we use to compare the length and width of the contour, then judge the length and width ratio of each contour, and then get the correct length and width ratio part through limited conditions), and filter out other image information on the bank card at the same time
4. Finally, do some morphological operations, such as opening and closing operations, to make the digital image information more obvious and accurate
5. After training the template, it should be noted that for the bank card number, we need to find a digital template similar to the digital style of the bank card number, otherwise the subsequent matching results will be affected;

Matching result:


Test drawing and template drawing


Process:

Template image processing

The whole preprocessing process is gray-scale image, binarization, contour search, contour drawing, resize() contour size, and all contours are sorted in order from 0 to 9, so as to facilitate the subsequent digital acquisition after matching;

Template drawing after completion of appeal operation:

Related code

#1. Read template
img=cv2.imread('number.png')
cv_show('number',img)
#2. Convert template to grayscale image
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)
#3. Change to binary image
ref,thre=cv2.threshold(gray,10,255,cv2.THRESH_BINARY_INV)
cv_show('ref',thre)
#4. Calculate the profile
refCnts,hierarchy=cv2.findContours(thre.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),3)#Draw the outline. Only the outer outline is drawn here. There are 10 outlines 0 ~ 9 in total
cv_show('imgLK',img)
refCnts=sort(refCnts,"left-to-right")[0]
digits={}

#Traverse each contour:
for(i,c) in enumerate(refCnts):#Calculate the circumscribed rectangle and resize to the appropriate size
    (x,y,w,h)=cv2.boundingRect(c)
    roi=thre[y:y+h,x:x+w]
    roi=cv2.resize(roi,(57,88))

    #Each number has a template
    digits[i]=roi#Corresponding digital template of 0 ~ 9

Original image processing

Compared with the template, the primary operation of the original image processing is the same, that is, gray processing first, and then binarization. Most of them are some morphological operations. The main purpose is to remove the noise, and then strengthen the contour and some important features. After completing these, we get some contour information we need through the aspect ratio of the contour, After getting it, continue the contour processing, draw a rectangle outside, compare, and finally get the result.
After gray and binarization of the original image, we do a top hat operation;
Because the result of the open operation is to enlarge the crack or local low brightness area, the effect image obtained by subtracting the opened image from the original image highlights the area brighter than the area around the contour of the original image, and this operation is related to the size of the selected core.
design sketch:

Then find the gradient in the X direction and get the highlighted part in the above figure:

Then do a close operation to make the highlighted area appear in blocks:

Then do a binarization to highlight the blocking part, and remove other miscellaneous information in the image:

Then do a closing operation to fill in the blank part of the area;

After completing the above operations, you can draw the contour, then filter the contour, and finally get the required contour, then carry out the above operations to get the data information in each contour, and then carry out template matching;

Complete code snippet:

import cv2
import numpy as np
from imutils import contours
from matplotlib import pyplot as plt
#Bank card practical small project

def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def sort(cnts,method="left-to-right"):
    reverse=False
    i=0
    if method=="right-to-left" or method=="bottom-to-top":
        reverse=True
    if method=="top-to-bottom" or method=="bottom-to-top":
        i=1
    boundingBoxes=[cv2.boundingRect(c) for c in cnts]#Use a smallest rectangle to wrap the found shapes, and then sort the smallest coordinates
    (cnts,boundingBoxes) = zip(*sorted(zip(cnts,boundingBoxes),key=lambda b:b[1][i],reverse=reverse))
    return cnts,boundingBoxes

#1. Read template
img=cv2.imread('number.png')
cv_show('number',img)
#2. Convert template to grayscale image
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)
#3. Change to binary image
ref,thre=cv2.threshold(gray,10,255,cv2.THRESH_BINARY_INV)
cv_show('ref',thre)
#4. Calculate the profile
refCnts,hierarchy=cv2.findContours(thre.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),3)#Draw the outline. Only the outer outline is drawn here. There are 10 outlines 0 ~ 9 in total
cv_show('imgLK',img)
refCnts=sort(refCnts,"left-to-right")[0]
digits={}#It is used to store the number corresponding to the template number

#Traverse each contour:
for(i,c) in enumerate(refCnts):#Calculate the circumscribed rectangle and resize to the appropriate size
    (x,y,w,h)=cv2.boundingRect(c)
    roi=thre[y:y+h,x:x+w]
    roi=cv2.resize(roi,(57,88))

    #Each number has a template
    digits[i]=roi#Corresponding digital template of 0 ~ 9

#Initialize the convolution kernel and do morphological processing. The size of the kernel can be defined according to the actual situation
rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

#Read the original image and preprocess
image=cv2.imread('bank_testI.png')
cv_show('card',image)
image=cv2.resize(image,(300,200))
#Gray processing
image_gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('gray',image_gray)
#Top hat operation, highlighting bright areas
tophat=cv2.morphologyEx(image_gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)
#Calculate the gradient of the boundary
gradX=cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=-1)

gradX=np.absolute(gradX)#Take absolute value
(minVal,maxVal)=(np.min(gradX),np.max(gradX))
gradX=(255*((gradX-minVal)/(maxVal-minVal))) #normalization
gradX=gradX.astype("uint8")
cv_show('gradX',gradX)

#Execute the closing operation to make the image information appear in blocks
gradX=cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
cv_show('gradX',gradX)

thresh=cv2.threshold(gradX,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#The threshold is set to 0 because the automatic judgment threshold is set in the function, which is generally applicable to bimodal cases
cv_show('gradX_t',thresh)

#Perform another closing operation to make the image information appear in groups and fill in the blank part
thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
conts,hiera=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cont=conts
curImag=image.copy()
tmp=cv2.drawContours(curImag,cont,-1,(0,0,255),3)#Draw the outline in the image after a series of processing
locs=[]

for(i,c) in enumerate(cont):
    (x,y,w,h)=cv2.boundingRect(c) #Draw the circumscribed rectangle of each area, and then filter the required parts according to the length width ratio of the circumscribed rectangle
    ar=w/float(h)
    if ar>2.5 and ar<4.0 :
        if (w>40 and w<55) and (h>10 and h<20): #Select the box that meets the conditions
            locs.append((x,y,w,h))
locs=sorted(locs,key=lambda x:x[0])#Sort the filtered contour data
output=[]
for(i,(gx,gy,gw,gh)) in enumerate(locs):
    groupOuput=[]
    group=image_gray[gy-5:gy+gh+5,gx-5:gx+gw+5]#Obtain the contour and its surrounding data. The function of plus five and minus five is to offset the obtained coordinate positions up, down, left and right a little, so as to facilitate matching
    cv_show('group',group)
    group=cv2.threshold(group,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#Again, the data in each large frame is binarized and the boundary is measured
    digitsCont,hieraD=cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)#Then carry out contour detection
    digitsCont=sort(digitsCont,method="left-to-right")[0]
    for c in digitsCont:#Calculate the value of each small box
        (x,y,w,h)=cv2.boundingRect(c)#Do the same, draw the circumscribed rectangle, and then pattern match
        roi=group[y:y+h,x:x+w]
        roi=cv2.resize(roi,(57,88))
        scores=[]
        for (digit,digitROI) in digits.items():
            result=cv2.matchTemplate(roi,digitROI,cv2.TM_CCOEFF)#Match and return the highest value
            (_,score,_,_)=cv2.minMaxLoc(result)#Make 10 matches and take the maximum value
            scores.append(score)
        groupOuput.append(str(np.argmax(scores)))
    cv2.rectangle(image,(gx-5,gy-5),(gx+gw+5,gy+gh+5),(0,0,255),1)
    cv2.putText(image,"".join(groupOuput),(gx,gy-15),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
    output.extend(groupOuput)
print("The Card's number is :{}",format("".join(output)))
cv_show('image_result', image)

Summary:

The whole practical project is not very difficult, but it is troublesome for me as a novice. After all, it is the first practical battle, so the whole process is still in an imitation stage. The whole process does not have its own independent thinking, but more is the personal understanding and learning of the whole process. Therefore, after the follow-up in-depth study, Further improvements can be made to the project. If you are lucky to see my article, if you have better learning suggestions and opinions, please leave a message in the message area ~ or if you don't understand, you can also leave a message in the message area to discuss learning together!!

reference resources

  1. Refer to the article of the boss
  2. Station B actual combat video

Keywords: Python OpenCV Computer Vision image processing

Added by oocuz on Fri, 26 Nov 2021 14:46:29 +0200