Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize the Bank Card number recognition function of C++ OpenCV

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces how C++ OpenCV realizes the bank card number recognition function, which has certain reference value. Interested friends can refer to it. I hope you will gain a lot after reading this article. Let Xiaobian take you to understand it together.

1. Obtain template image

As shown, this is our template image. We need to cut out the characters above and save them one by one for subsequent character matching. First, image gray, threshold and other operations for contour extraction, here will not be detailed. What I want to say here is that since the extracted characters are not arranged in the order of (0,1,2...7,8,9) after contour retrieval, I have defined a Card structure here for image sorting. See the source code for details.

1.1 functional effect

The figure shows the template characters cut out sequentially.

1.2 bool Get_Template(temp, vector&Card_Temp){ //image preprocessing Mat gray; cvtColor(temp, gray, COLOR_BGR2GRAY); Mat thresh; threshold(gray, thresh, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); //Contour detection vector contours; findContours(thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int i = 0; i

< contours.size(); i++) { Rect rect = boundingRect(contours[i]); double ratio = double(rect.width) / double(rect.height); //筛选出字符轮廓 if (ratio >

0.5 && ratio

< 1) { /*rectangle(temp, rect, Scalar(0, 255, 0));*/ Mat roi = temp(rect); //将字符扣出,放入Card_Temp容器备用 Card_Temp.push_back({ roi ,rect }); } } if (Card_Temp.empty())return false; //进行字符排序,使其按(0、1、2...7、8、9)顺序排序 for (int i = 0; i < Card_Temp.size()-1; i++) { for (int j = 0; j < Card_Temp.size() - 1 - i; j++) { if (Card_Temp[j].rect.x >

Card_Temp[j + 1].rect.x) { Card temp = Card_Temp[j]; Card_Temp[j] = Card_Temp[j + 1]; Card_Temp[j + 1] = temp; } } } return true;} 2. Location of bank card number

As shown in the figure, this is the bank card that needs to be identified in this case. As can be seen from the figure, we need to cut out the bank card number. First, we have to cut the card number into 4 small pieces, and then we need to cut the characters on each small piece. Next step is to see how it works.

2.1 Cut the bank card number into four pieces

The first step is to preprocess the image and extract the outline of card number by gray scale, binarization and morphology. The image preprocessing here needs to be determined according to the image characteristics, not all steps are necessary, and our ultimate goal is to locate the outline position of the bank card number. Here I'm using binarization and morphological closure.

//Morphological operations to find the outline of the bank card number area Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); Mat gaussian; GaussianBlur(gray, gaussian, Size(3, 3), 0); Mat thresh; threshold(gaussian, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU); Mat close; Mat kernel2 = getStructuringElement(MORPH_RECT, Size(15, 5)); morphologyEx(thresh, close, MORPH_CLOSE, kernel2);

After gray, threshold, morphology operation of the image as shown in the figure below. We have divided the bank card number into four small rectangular blocks, and then we can deduct these four ROI regions by contour search and screening.

vectorcontours; findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int i = 0; i

< contours.size(); i++) { //通过面积、长宽比筛选出银行卡号区域 double area = contourArea(contours[i]); if (area >

800 && area

< 1400) { Rect rect = boundingRect(contours[i]); float ratio = double(rect.width) / double(rect.height); if (ratio >

2.8 && ratio

< 3.1) { Mat ROI = src(rect); Block_ROI.push_back({ ROI ,rect }); } } } 同理,我们需要将切割下来的小块按照它原来的顺序存储。 for (int i = 0; i < Block_ROI.size()-1; i++) { for (int j = 0; j < Block_ROI.size() - 1 - i; j++) { if (Block_ROI[j].rect.x >

Block_ROI[j + 1].rect.x) { Card temp = Block_ROI[j]; Block_ROI[j] = Block_ROI[j + 1]; Block_ROI[j + 1] = temp; } } }

2.1.1 Functional effects

2.1.2 Functional source code

bool Cut_Block(Mat src, vector&Block_ROI){ //Morphological operations to find the outline of the bank card number area Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); Mat gaussian; GaussianBlur(gray, gaussian, Size(3, 3), 0); Mat thresh; threshold(gaussian, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU); Mat close; Mat kernel2 = getStructuringElement(MORPH_RECT, Size(15, 5)); morphologyEx(thresh, close, MORPH_CLOSE, kernel2); vectorcontours; findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int i = 0; i

< contours.size(); i++) { //通过面积、长宽比筛选出银行卡号区域 double area = contourArea(contours[i]); if (area >

800 && area

< 1400) { Rect rect = boundingRect(contours[i]); float ratio = double(rect.width) / double(rect.height); if (ratio >

2.8 && ratio

< 3.1) { //rectangle(src, rect, Scalar(0, 255, 0), 2); Mat ROI = src(rect); Block_ROI.push_back({ ROI ,rect }); } } } if (Block_ROI.size()!=4)return false; for (int i = 0; i < Block_ROI.size()-1; i++) { for (int j = 0; j < Block_ROI.size() - 1 - i; j++) { if (Block_ROI[j].rect.x >

Block_ROI[j + 1].rect.x) { Card temp = Block_ROI[j]; Block_ROI[j] = Block_ROI[j + 1]; Block_ROI[j + 1] = temp; } } } //for (int i = 0; i

< Block_ROI.size(); i++) //{ // imshow(to_string(i), Block_ROI[i].mat); // waitKey(0); //} return true;}2.2 字符切割 由步骤2.1,我们已经将银行卡号定位,且顺序切割成四个小块。接下来,我们只需要将他们依次的将字符切割下来就可以了。其实切割字符跟上面的切割小方块是差不多的,这里就不再多说了。在这里我着重要说明的是,切割出来的字符相对于银行卡所在位置。 由步骤2.1,我们顺序切割出来四个小方块。以其中一个小方块为例,当时我们存储了rect变量,它表示该小方块相对于图像起点(X,Y),宽W,高H。而步骤2.2我们需要做的就是将这个小方块的字符切割出来,那么每一个字符相对于小方块所在位置为起点(x,y),宽w,高h。所以,这些字符相当于银行卡所在位置就是起点(X+x,Y+y),宽 (w),高(h)。具体请细看源码。也比较简单容易理解。 //循环上面切割出来的四个小块,将上面的字符一一切割出来。 for (int i = 0; i < Block_ROI.size(); i++) { Mat roi_gray; cvtColor(Block_ROI[i].mat, roi_gray, COLOR_BGR2GRAY); Mat roi_thresh; threshold(roi_gray, roi_thresh, 0, 255, THRESH_BINARY|THRESH_OTSU); vector contours; findContours(roi_thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int j = 0; j < contours.size(); j++) { Rect rect = boundingRect(contours[j]); //字符相对于银行卡所在的位置 Rect roi_rect(rect.x + Block_ROI[i].rect.x, rect.y + Block_ROI[i].rect.y, rect.width, rect.height); Mat r_roi = Block_ROI[i].mat(rect); Slice_ROI.push_back({ r_roi ,roi_rect }); } } 同样,在这里我们也需要将切割出来的字符顺序排序。即银行卡上的号码是怎样排序的,我们就需要怎样排序保存 for (int i = 0; i < Slice_ROI.size() - 1; i++) { for (int j = 0; j < Slice_ROI.size() - 1 - i; j++) { if (Slice_ROI[j].rect.x >

Slice_ROI[j + 1].rect.x) { Card temp = Slice_ROI[j]; Slice_ROI[j] = Slice_ROI[j + 1]; Slice_ROI[j + 1] = temp; } } }

2.2.1 Functional effects

The figure shows the characters cut out in sequence

2.2.2 Functional source code

bool Cut_Slice(vector&Block_ROI,vector&Slice_ROI){ //Loop the four small blocks cut out above and cut out the characters one by one. for (int i = 0; i

< Block_ROI.size(); i++) { Mat roi_gray; cvtColor(Block_ROI[i].mat, roi_gray, COLOR_BGR2GRAY); Mat roi_thresh; threshold(roi_gray, roi_thresh, 0, 255, THRESH_BINARY|THRESH_OTSU); vector contours; findContours(roi_thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int j = 0; j < contours.size(); j++) { Rect rect = boundingRect(contours[j]); //字符相对于银行卡所在的位置 Rect roi_rect(rect.x + Block_ROI[i].rect.x, rect.y + Block_ROI[i].rect.y, rect.width, rect.height); Mat r_roi = Block_ROI[i].mat(rect); Slice_ROI.push_back({ r_roi ,roi_rect }); } } if (Slice_ROI.size() != 16) return false; for (int i = 0; i < Slice_ROI.size() - 1; i++) { for (int j = 0; j < Slice_ROI.size() - 1 - i; j++) { if (Slice_ROI[j].rect.x >

Slice_ROI[j + 1].rect.x) { Card temp = Slice_ROI[j]; Slice_ROI[j] = Slice_ROI[j + 1]; Slice_ROI[j + 1] = temp; } } } //for (int i = 0; i

< Slice_ROI.size(); i++) //{ // imshow(to_string(i), Slice_ROI[i].mat); // waitKey(0); //} return true;}三、字符识别3.1.读取文件 如图所示,为模板图像对应的label。我们需要读取文件,进行匹配。 bool ReadData(string filename, vector&label){ fstream fin; fin.open(filename, ios::in); if (!fin.is_open()) { cout data[i]; } fin.close(); for (int i = 0; i < 10; i++) { label.push_back(data[i]); } return true;}3.2.字符匹配 在这里,我的思路是:使用一个for循环,将我们切割出来的字符与现有的模板一一进行匹配。使用的算法是图像模板匹配matchTemplate。具体用法请大家自行查找相关资料。具体请看源码 3.3.功能源码bool Template_Matching(vector&Card_Temp, vector&Block_ROI, vector&Slice_ROI, vector&result_index){ for (int i = 0; i < Slice_ROI.size(); i++) { //将字符resize成合适大小,利于识别 resize(Slice_ROI[i].mat, Slice_ROI[i].mat, Size(60, 80), 1, 1, INTER_LINEAR); Mat gray; cvtColor(Slice_ROI[i].mat, gray, COLOR_BGR2GRAY); int maxIndex = 0; double Max = 0.0; for (int j = 0; j < Card_Temp.size(); j++) { resize(Card_Temp[j].mat, Card_Temp[j].mat, Size(60, 80), 1, 1, INTER_LINEAR); Mat temp_gray; cvtColor(Card_Temp[j].mat, temp_gray, COLOR_BGR2GRAY); //进行模板匹配,识别数字 Mat result; matchTemplate(gray, temp_gray, result, TM_SQDIFF_NORMED); double minVal, maxVal; Point minLoc, maxLoc; minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc); //得分最大的视为匹配结果 if (maxVal >

Max) { Max = maxVal; maxIndex = j; //matching results } } result_index.push_back(maxIndex);//Save matching results } if (result_index.size() != 16)return false; bool Show_Result(Mat src, vector&Block_ROI, vector&Slice_ROI, vector&result_index){ //read label label vectorlabel; if (! ReadData("label.txt", label))return false; //Display matching results for (int i = 0; i

< Block_ROI.size(); i++) { rectangle(src, Rect(Block_ROI[i].rect.tl(), Block_ROI[i].rect.br()), Scalar(0, 255, 0), 2); } for (int i = 0; i < Slice_ROI.size(); i++) { cout Card_Temp[j + 1].rect.x) { Card temp = Card_Temp[j]; Card_Temp[j] = Card_Temp[j + 1]; Card_Temp[j + 1] = temp; } } } //for (int i = 0; i < Card_Temp.size(); i++) //{ // imshow(to_string(i), Card_Temp[i].mat); // waitKey(0); //} return true;}bool Cut_Block(Mat src, vector&Block_ROI){ //形态学操作、以便找到银行卡号区域轮廓 Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); Mat gaussian; GaussianBlur(gray, gaussian, Size(3, 3), 0); Mat thresh; threshold(gaussian, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU); Mat close; Mat kernel2 = getStructuringElement(MORPH_RECT, Size(15, 5)); morphologyEx(thresh, close, MORPH_CLOSE, kernel2); vectorcontours; findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int i = 0; i < contours.size(); i++) { //通过面积、长宽比筛选出银行卡号区域 double area = contourArea(contours[i]); if (area >

800 && area

< 1400) { Rect rect = boundingRect(contours[i]); float ratio = double(rect.width) / double(rect.height); if (ratio >

2.8 && ratio

< 3.1) { //rectangle(src, rect, Scalar(0, 255, 0), 2); Mat ROI = src(rect); Block_ROI.push_back({ ROI ,rect }); } } } if (Block_ROI.size()!=4)return false; for (int i = 0; i < Block_ROI.size()-1; i++) { for (int j = 0; j < Block_ROI.size() - 1 - i; j++) { if (Block_ROI[j].rect.x >

Block_ROI[j + 1].rect.x) { Card temp = Block_ROI[j]; Block_ROI[j] = Block_ROI[j + 1]; Block_ROI[j + 1] = temp; } } } //for (int i = 0; i

< Block_ROI.size(); i++) //{ // imshow(to_string(i), Block_ROI[i].mat); // waitKey(0); //} return true;}bool Cut_Slice(vector&Block_ROI,vector&Slice_ROI){ //循环上面切割出来的四个小块,将上面的字符一一切割出来。 for (int i = 0; i < Block_ROI.size(); i++) { Mat roi_gray; cvtColor(Block_ROI[i].mat, roi_gray, COLOR_BGR2GRAY); Mat roi_thresh; threshold(roi_gray, roi_thresh, 0, 255, THRESH_BINARY|THRESH_OTSU); vector contours; findContours(roi_thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int j = 0; j < contours.size(); j++) { Rect rect = boundingRect(contours[j]); //字符相对于银行卡所在的位置 Rect roi_rect(rect.x + Block_ROI[i].rect.x, rect.y + Block_ROI[i].rect.y, rect.width, rect.height); Mat r_roi = Block_ROI[i].mat(rect); Slice_ROI.push_back({ r_roi ,roi_rect }); } } if (Slice_ROI.size() != 16) return false; for (int i = 0; i < Slice_ROI.size() - 1; i++) { for (int j = 0; j < Slice_ROI.size() - 1 - i; j++) { if (Slice_ROI[j].rect.x >

Slice_ROI[j + 1].rect.x) { Card temp = Slice_ROI[j]; Slice_ROI[j] = Slice_ROI[j + 1]; Slice_ROI[j + 1] = temp; } } } //for (int i = 0; i

< Slice_ROI.size(); i++) //{ // imshow(to_string(i), Slice_ROI[i].mat); // waitKey(0); //} return true;}bool ReadData(string filename, vector&label){ fstream fin; fin.open(filename, ios::in); if (!fin.is_open()) { cout data[i]; } fin.close(); for (int i = 0; i < 10; i++) { label.push_back(data[i]); } return true;}bool Template_Matching(vector&Card_Temp, vector&Block_ROI, vector&Slice_ROI, vector&result_index){ for (int i = 0; i < Slice_ROI.size(); i++) { //将字符resize成合适大小,利于识别 resize(Slice_ROI[i].mat, Slice_ROI[i].mat, Size(60, 80), 1, 1, INTER_LINEAR); Mat gray; cvtColor(Slice_ROI[i].mat, gray, COLOR_BGR2GRAY); int maxIndex = 0; double Max = 0.0; for (int j = 0; j < Card_Temp.size(); j++) { resize(Card_Temp[j].mat, Card_Temp[j].mat, Size(60, 80), 1, 1, INTER_LINEAR); Mat temp_gray; cvtColor(Card_Temp[j].mat, temp_gray, COLOR_BGR2GRAY); //进行模板匹配,识别数字 Mat result; matchTemplate(gray, temp_gray, result, TM_SQDIFF_NORMED); double minVal, maxVal; Point minLoc, maxLoc; minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc); //得分最大的视为匹配结果 if (maxVal >

Max) { Max = maxVal; maxIndex = j; //matching results } } result_index.push_back(maxIndex);//Save matching results } if (result_index.size() != 16)return false; return true;}bool Show_Result(Mat src, vector&Block_ROI, vector&Slice_ROI, vector&result_index){ //read label label vectorlabel; if (! ReadData("label.txt", label))return false; //Display matching results for (int i = 0; i < Block_ROI.size(); i++) { rectangle(src, Rect(Block_ROI[i].rect.tl(), Block_ROI[i].rect.br()), Scalar(0, 255, 0), 2); } for (int i = 0; i < Slice_ROI.size(); i++) { cout

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report