├── credit_card_01.png ├── credit_card_02.png ├── credit_card_03.png ├── credit_card_04.png ├── credit_card_05.png ├── ocr_a_reference.png ├── README.md └── ocr_template_match.py /credit_card_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmathur5k10/Credit-Card-Recognition/HEAD/credit_card_01.png -------------------------------------------------------------------------------- /credit_card_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmathur5k10/Credit-Card-Recognition/HEAD/credit_card_02.png -------------------------------------------------------------------------------- /credit_card_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmathur5k10/Credit-Card-Recognition/HEAD/credit_card_03.png -------------------------------------------------------------------------------- /credit_card_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmathur5k10/Credit-Card-Recognition/HEAD/credit_card_04.png -------------------------------------------------------------------------------- /credit_card_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmathur5k10/Credit-Card-Recognition/HEAD/credit_card_05.png -------------------------------------------------------------------------------- /ocr_a_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmathur5k10/Credit-Card-Recognition/HEAD/ocr_a_reference.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Credit-Card-Recognition 2 | A python program to extract the vital information from credit card image such as type of card, card number, card holder's name and validity date using OCR-A font recognition in openCV 3 | -------------------------------------------------------------------------------- /ocr_template_match.py: -------------------------------------------------------------------------------- 1 | # USAGE 2 | # python ocr_template_match.py --image images/credit_card_01.png --reference ocr_a_reference.png 3 | 4 | # import the necessary packages 5 | from imutils import contours 6 | import numpy as np 7 | import argparse 8 | import imutils 9 | import cv2 10 | 11 | # construct the argument parse and parse the arguments 12 | ap = argparse.ArgumentParser() 13 | ap.add_argument("-i", "--image", required=True, 14 | help="path to input image") 15 | ap.add_argument("-r", "--reference", required=True, 16 | help="path to reference OCR-A image") 17 | args = vars(ap.parse_args()) 18 | 19 | # define a dictionary that maps the first digit of a credit card 20 | # number to the credit card type 21 | FIRST_NUMBER = { 22 | "3": "American Express", 23 | "4": "Visa", 24 | "5": "MasterCard", 25 | "6": "Discover Card" 26 | } 27 | 28 | # load the reference OCR-A image from disk, convert it to grayscale, 29 | # and threshold it, such that the digits appear as *white* on a 30 | # *black* background 31 | # and invert it, such that the digits appear as *white* on a *black* 32 | ref = cv2.imread(args["reference"]) 33 | ref = cv2.cvtColor(ref, cv2.COLOR_BGR2GRAY) 34 | ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1] 35 | 36 | # find contours in the OCR-A image (i.e,. the outlines of the digits) 37 | # sort them from left to right, and initialize a dictionary to map 38 | # digit name to the ROI 39 | refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, 40 | cv2.CHAIN_APPROX_SIMPLE) 41 | refCnts = refCnts[0] if imutils.is_cv2() else refCnts[1] 42 | refCnts = contours.sort_contours(refCnts, method="left-to-right")[0] 43 | digits = {} 44 | 45 | # loop over the OCR-A reference contours 46 | for (i, c) in enumerate(refCnts): 47 | # compute the bounding box for the digit, extract it, and resize 48 | # it to a fixed size 49 | (x, y, w, h) = cv2.boundingRect(c) 50 | roi = ref[y:y + h, x:x + w] 51 | roi = cv2.resize(roi, (57, 88)) 52 | 53 | # update the digits dictionary, mapping the digit name to the ROI 54 | digits[i] = roi 55 | 56 | # initialize a rectangular (wider than it is tall) and square 57 | # structuring kernel 58 | rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) 59 | sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) 60 | 61 | # load the input image, resize it, and convert it to grayscale 62 | image = cv2.imread(args["image"]) 63 | image = imutils.resize(image, width=300) 64 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 65 | 66 | # apply a tophat (whitehat) morphological operator to find light 67 | # regions against a dark background (i.e., the credit card numbers) 68 | tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 69 | 70 | # compute the Scharr gradient of the tophat image, then scale 71 | # the rest back into the range [0, 255] 72 | gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, 73 | ksize=-1) 74 | gradX = np.absolute(gradX) 75 | (minVal, maxVal) = (np.min(gradX), np.max(gradX)) 76 | gradX = (255 * ((gradX - minVal) / (maxVal - minVal))) 77 | gradX = gradX.astype("uint8") 78 | 79 | # apply a closing operation using the rectangular kernel to help 80 | # cloes gaps in between credit card number digits, then apply 81 | # Otsu's thresholding method to binarize the image 82 | gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 83 | thresh = cv2.threshold(gradX, 0, 255, 84 | cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 85 | 86 | # apply a second closing operation to the binary image, again 87 | # to help close gaps between credit card number regions 88 | thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) 89 | 90 | # find contours in the thresholded image, then initialize the 91 | # list of digit locations 92 | cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, 93 | cv2.CHAIN_APPROX_SIMPLE) 94 | cnts = cnts[0] if imutils.is_cv2() else cnts[1] 95 | locs = [] 96 | 97 | # loop over the contours 98 | for (i, c) in enumerate(cnts): 99 | # compute the bounding box of the contour, then use the 100 | # bounding box coordinates to derive the aspect ratio 101 | (x, y, w, h) = cv2.boundingRect(c) 102 | ar = w / float(h) 103 | 104 | # since credit cards used a fixed size fonts with 4 groups 105 | # of 4 digits, we can prune potential contours based on the 106 | # aspect ratio 107 | if ar > 2.5 and ar < 4.0: 108 | # contours can further be pruned on minimum/maximum width 109 | # and height 110 | if (w > 40 and w < 55) and (h > 10 and h < 20): 111 | # append the bounding box region of the digits group 112 | # to our locations list 113 | locs.append((x, y, w, h)) 114 | 115 | # sort the digit locations from left-to-right, then initialize the 116 | # list of classified digits 117 | locs = sorted(locs, key=lambda x:x[0]) 118 | output = [] 119 | 120 | # loop over the 4 groupings of 4 digits 121 | for (i, (gX, gY, gW, gH)) in enumerate(locs): 122 | # initialize the list of group digits 123 | groupOutput = [] 124 | 125 | # extract the group ROI of 4 digits from the grayscale image, 126 | # then apply thresholding to segment the digits from the 127 | # background of the credit card 128 | group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5] 129 | group = cv2.threshold(group, 0, 255, 130 | cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 131 | 132 | # detect the contours of each individual digit in the group, 133 | # then sort the digit contours from left to right 134 | digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, 135 | cv2.CHAIN_APPROX_SIMPLE) 136 | digitCnts = digitCnts[0] if imutils.is_cv2() else digitCnts[1] 137 | digitCnts = contours.sort_contours(digitCnts, 138 | method="left-to-right")[0] 139 | 140 | # loop over the digit contours 141 | for c in digitCnts: 142 | # compute the bounding box of the individual digit, extract 143 | # the digit, and resize it to have the same fixed size as 144 | # the reference OCR-A images 145 | (x, y, w, h) = cv2.boundingRect(c) 146 | roi = group[y:y + h, x:x + w] 147 | roi = cv2.resize(roi, (57, 88)) 148 | 149 | # initialize a list of template matching scores 150 | scores = [] 151 | 152 | # loop over the reference digit name and digit ROI 153 | for (digit, digitROI) in digits.items(): 154 | # apply correlation-based template matching, take the 155 | # score, and update the scores list 156 | result = cv2.matchTemplate(roi, digitROI, 157 | cv2.TM_CCOEFF) 158 | (_, score, _, _) = cv2.minMaxLoc(result) 159 | scores.append(score) 160 | 161 | # the classification for the digit ROI will be the reference 162 | # digit name with the *largest* template matching score 163 | groupOutput.append(str(np.argmax(scores))) 164 | 165 | # draw the digit classifications around the group 166 | cv2.rectangle(image, (gX - 5, gY - 5), 167 | (gX + gW + 5, gY + gH + 5), (0, 0, 255), 2) 168 | cv2.putText(image, "".join(groupOutput), (gX, gY - 15), 169 | cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2) 170 | 171 | # update the output digits list 172 | output.extend(groupOutput) 173 | 174 | # display the output credit card information to the screen 175 | print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]])) 176 | print("Credit Card #: {}".format("".join(output))) 177 | cv2.imshow("Image", image) 178 | cv2.waitKey(0) --------------------------------------------------------------------------------