C++ OpenCV实现银行卡号识别功能

目录
  • 前言
  • 一、获取模板图像
    • 1.1 功能效果
    • 1.2 功能源码
  • 二、银行卡号定位
    • 2.1 将银行卡号切割成四块
    • 2.2 字符切割
  • 三、字符识别
    • 3.1.读取文件
    • 3.2.字符匹配
    • 3.3.功能源码
  • 四、效果显示
    • 4.1 功能源码
    • 4.2 效果显示
  • 五、源码
    • 5.1 hpp文件
    • 5.2 cpp文件
    • 5.3 main文件
  • 总结

前言

本文将使用OpenCV C++ 进行银行卡号识别。主要步骤可以细分为:

1、 获取模板图像

2、银行卡号区域定位

3、字符切割

4、模板匹配

5、效果显示

接下来就具体看看是如何一步步实现的吧。

一、获取模板图像

如图所示,这是我们的模板图像。我们需要将上面的字符一一切割出来保存,以便进行后续的字符匹配环节。先进行图像灰度、阈值等操作进行轮廓提取,这里就不再细说。这里我想说的是,由于经过轮廓检索,提取出来的字符并不是按(0、1、2…7、8、9)顺序排列,所以,在这里我自定义了一个Card结构体,用于图像排序。具体请看源码。

1.1 功能效果

如图为顺序切割出来的模板字符。

1.2 功能源码

bool Get_Template(Mat temp, vector<Card>&Card_Temp)
{
    //图像预处理
    Mat gray;
    cvtColor(temp, gray, COLOR_BGR2GRAY);

    Mat thresh;
    threshold(gray, thresh, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);

    //轮廓检测
    vector <vector<Point>> 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;
}

二、银行卡号定位

如图所示,这是本案例需要识别的银行卡。从图中可以看出,我们需要将银行卡号切割出来首先得将卡号分为4个小块切割,之后再需要将每一小块上的字符切割。接下来一步步看是如何操作的。

2.1 将银行卡号切割成四块

首先第一步得先进行图像预处理,通过灰度、二值化、形态学等操作提取出卡号轮廓。这里的图像预处理需要根据图像特征自行确定,并不是所有的步骤都是必须的,我们最终的目的是为了定位银行卡号所在轮廓位置。这里我使用的是二值化、以及形态学闭操作。

 //形态学操作、以便找到银行卡号区域轮廓
    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);

经过灰度、阈值、形态学操作后的图像如下图所示。我们已经将银行卡号分为四个小矩形块,接下来只需通过轮廓查找、筛选就可以扣出这四个ROI区域了。

  vector<vector<Point>>contours;
    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 功能效果

2.1.2 功能源码

bool Cut_Block(Mat src, vector<Card>&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);

    vector<vector<Point>>contours;
    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 <vector<Point>> 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 功能效果

如图为顺序切割出来的字符

2.2.2 功能源码

bool Cut_Slice(vector<Card>&Block_ROI,vector<Card>&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 <vector<Point>> 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<int>&label)
{
    fstream fin;
    fin.open(filename, ios::in);
    if (!fin.is_open())
    {
        cout << "can not open the file!" << endl;
        return false;
    }

    int data[10] = { 0 };
    for (int i = 0; i < 10; i++)
    {
        fin >> 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>&Card_Temp,
    vector<Card>&Block_ROI, vector<Card>&Slice_ROI,
    vector<int>&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; //匹配结果
            }
        }

        result_index.push_back(maxIndex);//将匹配结果进行保存
    }

    if (result_index.size() != 16)return false;

    return true;
}

四、效果显示

4.1 功能源码

bool Show_Result(Mat src, 
    vector<Card>&Block_ROI,
    vector<Card>&Slice_ROI, 
    vector<int>&result_index)
{
    //读取label标签
    vector<int>label;
    if (!ReadData("label.txt", label))return false;

    //将匹配结果进行显示
    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 << label[result_index[i]] << " ";
        putText(src, to_string(label[result_index[i]]), Point(Slice_ROI[i].rect.tl()), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
    }

    imshow("Demo", src);
    waitKey(0);
    destroyAllWindows();

    return true;
}

4.2 效果显示

如图所示,为本案例最终的效果展示。

五、源码

5.1 hpp文件

#pragma once
#include<opencv2/opencv.hpp>
#include<iostream>

struct Card
{
	cv::Mat mat;
	cv::Rect rect;
};

//获取模板图像
bool Get_Template(cv::Mat temp, std::vector<Card>&Card_Temp);

//将银行卡卡号部分切成四块
bool Cut_Block(cv::Mat src, std::vector<Card>&Block_ROI);

//将每一块数字区域切分出单独数字
bool Cut_Slice(std::vector<Card>&Block_ROI, std::vector<Card>&Slice_ROI);

//将数字与模板进行模板匹配
bool Template_Matching(std::vector<Card>&Card_Temp,
	std::vector<Card>&Block_ROI,
	std::vector<Card>&Slice_ROI,
	std::vector<int>&result_index);

//显示最终结果
bool Show_Result(cv::Mat src,
	std::vector<Card>&Block_ROI,
	std::vector<Card>&Slice_ROI,
	std::vector<int>&result_index);

5.2 cpp文件

#include<iostream>
#include"CardDectection.h"
#include<fstream>
using namespace std;
using namespace cv;

bool Get_Template(Mat temp, vector<Card>&Card_Temp)
{
	//图像预处理
	Mat gray;
	cvtColor(temp, gray, COLOR_BGR2GRAY);

	Mat thresh;
	threshold(gray, thresh, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);

	//轮廓检测
	vector <vector<Point>> 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;
			}
		}
	}

	//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<Card>&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);

	vector<vector<Point>>contours;
	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<Card>&Block_ROI,vector<Card>&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 <vector<Point>> 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<int>&label)
{
	fstream fin;
	fin.open(filename, ios::in);
	if (!fin.is_open())
	{
		cout << "can not open the file!" << endl;
		return false;
	}

	int data[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		fin >> data[i];
	}
	fin.close();

	for (int i = 0; i < 10; i++)
	{
		label.push_back(data[i]);
	}
	return true;
}

bool Template_Matching(vector<Card>&Card_Temp,
	vector<Card>&Block_ROI, vector<Card>&Slice_ROI,
	vector<int>&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; //匹配结果
			}
		}

		result_index.push_back(maxIndex);//将匹配结果进行保存
	}

	if (result_index.size() != 16)return false;

	return true;
}

bool Show_Result(Mat src,
	vector<Card>&Block_ROI,
	vector<Card>&Slice_ROI,
	vector<int>&result_index)
{
	//读取label标签
	vector<int>label;
	if (!ReadData("label.txt", label))return false;

	//将匹配结果进行显示
	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 << label[result_index[i]] << " ";
		putText(src, to_string(label[result_index[i]]), Point(Slice_ROI[i].rect.tl()), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
	}

	imshow("Demo", src);
	waitKey(0);
	destroyAllWindows();

	return true;
}

5.3 main文件

#include<iostream>
#include"CardDectection.h"
using namespace std;
using namespace cv;

int main()
{

	Mat src = imread("card.png");   //源图像 银行卡
	Mat temp = imread("number.png"); //模板图像

	if (src.empty() || temp.empty())
	{
		cout << "no image data !" << endl;
		system("pause");
		return -1;
	}

	vector<Card>Card_Temp;
	if (!Get_Template(temp, Card_Temp))
	{
		cout << "模板切割失败!" << endl;
		system("pause");
		return -1;
	}

	vector<Card>Block_ROI;
	if (Cut_Block(src, Block_ROI))
	{
		vector<Card>Slice_ROI;
		if (Cut_Slice(Block_ROI, Slice_ROI))
		{
			vector<int>result_index;
			if (Template_Matching(Card_Temp, Block_ROI, Slice_ROI, result_index))
			{
				Show_Result(src, Block_ROI, Slice_ROI, result_index);
			}
			else
			{
				cout << "识别失败!" << endl;
				system("pause");
				return -1;
			}
		}
		else
		{
			cout << "切片失败!" << endl;
			system("pause");
			return -1;
		}
	}
	else
	{
		cout << "切块失败!" << endl;
		system("pause");
		return -1;
	}

	system("pause");
	return 0;
}

总结

本文使用OpenCV C++进行银行卡号识别,关键步骤有以下几点。

1、银行卡号定位。根据本案例中的银行卡图像特征,我们先将银行卡号所在位置定位。根据图像特征,我们可以将银行卡号分为四个小方块进行定位切割。

2、字符分割。根据前面得到的银行卡号四个小方块,我们需要将它们顺序切割出每一个字符。

3、字符识别。我们将得到的字符与我们准备好的模板一一进行匹配。这里使用的匹配算法是图像模板匹配。

以上就是C++ OpenCV实现银行卡号识别功能的详细内容,更多关于C++ OpenCV银行卡号识别的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python+OpenCV实现基于颜色的目标识别

    目录 任务 主要代码 效果展示 学习了一点opencv的知识于是找了个小项目来实践一下.这里先说明一下,我的实现方法不见得是最好的(因为这只是一个用于练习的项目)仅作参考,也欢迎各位大佬指正. 任务 让摄像头识别到视野范围内的气球并返回每个气球的中心点坐标. 因为场地固定,背景单一,所以省下来很多操作和处理.于是就有两种解决思路:第一种是基于气球形状做轮廓提取,只要是闭合椭圆或圆形形就认为是目标物体:第二种是基于气球颜色,只要符合目标物体的颜色就认为是目标物体. 因为摄像头是装在四足机器人(它的

  • Python OpenCV招商银行信用卡卡号识别的方法

    学在前面 从本篇博客起,我们将实际完成几个小案例,第一个就是银行卡号识别,预计本案例将写 5 篇左右的博客才可以完成,一起加油吧. 本文的目标是最终获取一套招商银行卡,0~9 数字的图,对于下图的数字,我们需要提取出来,便于后续模板匹配使用.不过下图中找到的数字不完整,需要找到尽量多的卡片,然后补齐这些数字. 提取卡片相关数字 先对上文中卡片中的数字进行相关提取操作,加载图片的灰度图,获取目标区域.在画板中模拟一下坐标区域,为了便于进行后续的操作. 具体代码如下: import cv2 as c

  • Python如何识别银行卡卡号?

    一.现有资源梳理 目前有一张卡号模板图片 N张测试银行卡图片,其一如下 操作环境 win10-64位 代码语言 Python 3.6 二.实现方案规划 对模板操作,将十个模板和对应的数字一一对应起来 图片中通过查找轮廓,然后绘制轮廓外界矩形的方式,将每一和数字分割出来,并和对应的数字相对应.以字典的形式保存 每一个模板都是这样的形式存储. array([[ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255], [ 0, 0, 0, 0, 0, 0

  • C++ OpenCV技术实战之身份证离线识别

    目录 总体思路 图像的预处理 主要代码 实现效果 OpenCV身份证离线识别技术的主要技术就是通过OpenCV找到身份证号码区域,然后通过OCR进行数字识别该区域的截图即可得到身份证号码.本地ORC使用tess-two来完成,Tesseract是C++实现的OCR引擎,在Android中使用不是很方便,需要封装JavaAPI才能在Android平台中进行调用,然而tess-two已经帮我们做好了这些事情,通过集成tess-two就可以很方便的完成文字识别. 总体思路 图像的预处理 1.无损压缩

  • OpenCV利用手势识别实现虚拟拖放效果

    目录 第一步 第二步 第三步 完整代码 本文将实现一些通过手拖放一些框,我们可以使用这个技术实现一些游戏,控制机械臂等很多有趣的事情. 第一步 通过opencv设置显示框和调用摄像头显示当前画面 import cv2 cap = cv2.VideoCapture(0) cap.set(3,1280) cap.set(4,720) while True:     succes, img = cap.read()     cv2.imshow("Image", img)     cv2.w

  • Opencv创建车牌图片识别系统方法详解

    目录 前言 包含功能 软件版本 软件架构 参考文档 效果图展示 车牌检测过程 图片车牌文字识别过程 部分核心代码 前言 这是一个基于spring boot + maven + opencv 实现的图像识别及训练的Demo项目 包含车牌识别.人脸识别等功能,贯穿样本处理.模型训练.图像处理.对象检测.对象识别等技术点 java语言的深度学习项目,在整个开源社区来说都相对较少: 拥有完整的训练过程.检测.识别过程的开源项目更是少之又少!! 包含功能 蓝.绿.黄车牌检测及车牌号码识别 网上常见的轮廓提

  • C++ OpenCV实现银行卡号识别功能

    目录 前言 一.获取模板图像 1.1 功能效果 1.2 功能源码 二.银行卡号定位 2.1 将银行卡号切割成四块 2.2 字符切割 三.字符识别 3.1.读取文件 3.2.字符匹配 3.3.功能源码 四.效果显示 4.1 功能源码 4.2 效果显示 五.源码 5.1 hpp文件 5.2 cpp文件 5.3 main文件 总结 前言 本文将使用OpenCV C++ 进行银行卡号识别.主要步骤可以细分为: 1. 获取模板图像 2.银行卡号区域定位 3.字符切割 4.模板匹配 5.效果显示 接下来就具

  • python利用Opencv实现人脸识别功能

    本文实例为大家分享了python利用Opencv实现人脸识别功能的具体代码,供大家参考,具体内容如下 首先:需要在在自己本地安装opencv具体步骤可以问度娘 如果从事于开发中的话建议用第三方的人脸识别(推荐阿里) 1.视频流中进行人脸识别 # -*- coding: utf-8 -*- import cv2 import sys from PIL import Image def CatchUsbVideo(window_name, camera_idx): cv2.namedWindow(w

  • springboot集成opencv实现人脸识别功能的详细步骤

    前言 项目中检测人脸图片是否合法的功能,之前用的是百度的人脸识别接口,由于成本高昂不得不寻求替代方案. 什么是opencv? OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux.Windows.Android和Mac OS操作系统上.轻量级而且高效--由一系列 C 函数和少量 C++ 类构成,同时提供了Python.Java.MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法. 项目集成步骤 由于项目是放在Linux系统中跑的

  • 手把手教你利用opencv实现人脸识别功能(附源码+文档)

    目录 一.环境 二.使用Haar级联进行人脸检测 三.Haar级联结合摄像头 四.使用SSD的人脸检测 五. SSD结合摄像头人脸检测 六.结语 一.环境 pip install opencv-python python3.9 pycharm2020 人狠话不多,直接上代码,注释在代码里面,不说废话. 二.使用Haar级联进行人脸检测 测试案例: 代码:(记得自己到下载地址下载对应的xml) # coding=gbk """ 作者:川川 @时间 : 2021/9/5 16:3

  • python调用OpenCV实现人脸识别功能

    Python调用OpenCV实现人脸识别,供大家参考,具体内容如下 硬件环境: Win10 64位 软件环境: Python版本:2.7.3 IDE:JetBrains PyCharm 2016.3.2 Python库: 1.1) opencv-python(3.2.0.6) 搭建过程: OpenCV Python库: 1. PyCharm的插件源中选择opencv-python(3.2.0.6)库安装 题外话:Python入门Tips PS1:如何安装whl文件 1.先安装PIP 2.CMD命

  • java+opencv实现人脸识别功能

    背景:最近需要用到人脸识别,但又不花钱使用现有的第三方人脸识别接口,为此使用opencv结合java进行人脸识别(ps:opencv是开源的,使用它来做人脸识别存在一定的误差,效果一般). 1.安装opencv 官网地址:https://opencv.org/ , 由于官网下载速度是真的慢 百度网盘: 链接: https://pan.baidu.com/s/1RpsP-I7v8pP2dkqALDw7FQ 提取码: pq7v 如果是官网下载,就无脑安装就行了,安装完毕后. 将图一的两个文件复制到图

  • Python基于OpenCV库Adaboost实现人脸识别功能详解

    本文实例讲述了Python基于OpenCV库Adaboost实现人脸识别功能.分享给大家供大家参考,具体如下: 以前用Matlab写神经网络的面部眼镜识别算法,研究算法逻辑,采集大量训练数据,迭代,计算各感知器的系数...相当之麻烦~而现在运用调用pythonOpenCV库Adaboost算法,无需知道算法逻辑,无需进行模型训练,人脸识别变得相当之简单了. 需要用到的库是opencv(open source computer vision),下载安装方式如下: 使用pip install num

  • iOS开发之银行卡号识别

    iOS open cv 大体有个思路 1. 捕捉银行卡图像,截取到字符区域 2. 最捕捉到的图像进行处理,去噪比,二值化, 3. 将银行卡号码区域进行切割划分,然后对比字符 opencv 安装失败,直接下载拖进 xcode 的话,会报以下错误 这里是 详细解决链接地址 Undefined symbols for architecture x86_64: "_jpeg_free_large", referenced from: _free_pool in opencv2(jmemmgr.

  • Python实现基于KNN算法的笔迹识别功能详解

    本文实例讲述了Python实现基于KNN算法的笔迹识别功能.分享给大家供大家参考,具体如下: 需要用到: Numpy库 Pandas库 手写识别数据 点击此处本站下载. 数据说明: 数据共有785列,第一列为label,剩下的784列数据存储的是灰度图像(0~255)的像素值 28*28=784 KNN(K近邻算法): 从训练集中找到和新数据最接近的K条记录,根据他们的主要分类来决定新数据的类型. 这里的主要分类,可以有不同的判别依据,比如"最多","最近邻",或者

  • Python下应用opencv 实现人脸检测功能

    使用OpenCV's Haar cascades作为人脸检测,因为他做好了库,我们只管使用. 代码简单,除去注释,总共有效代码只有10多行. 所谓库就是一个检测人脸的xml 文件,可以网上查找,下面是一个地址: https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml 如何构造这个库,学习完本文后可以参考: http://note.sonots.com/Sc

随机推荐