银行卡几何校正及卡号定位
一、首先看下效果:
图1
图2
二、几何校正:
步骤:
0.背景:纯色背景(如果卡面颜色与背景颜色相近,后续的边缘检测会有影响,这块后续可以通过更改边缘检测的阀值参数或其他方法提高)
1.边缘检测(预处理)
2.直线检测(设置检测直线的条件(最小直线长度和最大直接间隔),获取银行卡边,条件:直线数量大于等于3,小于10)
3.根据直线集获得点集(Vec4i转换成vector>)
4.根据点集获得最小外接可旋转矩形minAreaRect();
5.几何校正(getRotationMatrix2D(),warpAffine(),getRectSubPix();)
三、银行卡的上卡号定位:
方法1:
根据模板匹配,获取卡号的位置
已实现,识别率>70%;缺点,对无银联标志或其在右上角的卡,不适用
(此标志是在测试环境中获取的)
方法2:
构想 (未实现):
1.cropped.rows/5至cropped.rows*4/5之间,以间距cropped.rows/5扫描
2.检测直线,比较直线的长度和数量来确认;另一种方法,二值化图像,比较数字间的空白数。但因为背景复杂且有些卡相差很大,此种方法成功率较低。
ps:如果需要高效稳定的方法,这一块还需要继续找其他方法
四、卡号识别(下一步实现)
图3 图4 图5
难度:
1.同一张卡,背景复杂,如图3切割于图2的工商卡,有个白底e。固定化的阀值化操作已不适用于此类型卡。更离谱是有些凹凸卡号,不认真看,连人眼都分不清。
2.数字的字体多,不适用于模板匹配。可使用SVM、KNN等进行识别
3.数字的预测度设置,目前在微信上,我的两张卡都会识别错误(主要原因是背景过于复杂)
暂时先到这,后续需要找到新的方法,解决以上难点,还需要不断提高。
五、demo代码
#include "stdafx.h"
#include
#include
#include
using namespace cv;
using namespace std;
void drawDetectLines(Mat& image,const vector& lines,Scalar & color);
void on_canny( int, void* );
//void on_Threshold( int, void* );以备后面使用
void on_line( int, void* );
void vec4i2vecPoint();
bool findUPflag();
void cropBankNoByUPflag();
Mat frame;
Mat grayImg;
Mat contours;
Mat threImg;
int i_canThres=50;
int g_nThresholdValue=100;
int g_nThresholdType=1;
int minLineLength=170;
int maxLineGap=10;
vector lines;
vector co;
vector > conn;
Mat UPImg,cropped,noImg;
Point minLoc;
void main()
{
Mat imgROI;
bool stop = false;
char c;
//VideoCapture capture(0);
VideoCapture capture("D://img//bankcard//1.wmv");
namedWindow( "canny");
//namedWindow( "Threshold");
namedWindow( "Bob_BankCard_Num_Reader");
//cvtColor(frame,grayImg,CV_BGR2GRAY);
//createTrackbar( "模式","Threshold", &g_nThresholdType,4, on_Threshold );
//createTrackbar( "参数值","Threshold", &g_nThresholdValue,255, on_Threshold );
createTrackbar( "canThres","canny", &i_canThres,100, on_canny );
createTrackbar( "最小直线长度","Bob_BankCard_Num_Reader", &minLineLength,300, on_line);
createTrackbar( "最大直线间隔度","Bob_BankCard_Num_Reader", &maxLineGap,300, on_line);
Mat M, rotated;
float angle;
Size rect_size;
float ratio;
int lineSize=0;
UPImg=imread("D://img//bankcard//UPflag.jpg");
while(!stop)
{
capture>>frame;
cvtColor(frame,grayImg,CV_BGR2GRAY);
//on_Threshold(0,0);
//imshow("canny",contours);
on_canny(0,0);
imshow("canny",contours);
on_line(0,0);
lineSize=lines.size();
if(lineSize>=3&&lineSize<=10)//线条的数量,可根据亮度去调节canny的阀值
{
drawDetectLines(frame,lines,Scalar(0,255,0));
vec4i2vecPoint();
Rect rc=boundingRect(conn.at(0));
RotatedRect rect;
rect=minAreaRect(conn.at(0));
imgROI=frame(rc);
imshow("outRect",imgROI);
//后期如果需要提高卡的轮廓识别率,可以判断卡的height/width比,大约0.64(实际量过)
ratio=rect.size.height/rect.size.width;
cout<
if(ratio>0.6&&ratio<0.7)
{
angle = rect.angle;
rect_size = rect.size;
if (rect.angle < -45.)
{
angle += 90.0;
swap(rect_size.width, rect_size.height);
}
M = getRotationMatrix2D(rect.center, angle, 1.0);
warpAffine(frame, rotated, M, frame.size(), INTER_CUBIC);
getRectSubPix(rotated, rect_size, rect.center, cropped);
imshow("cropped",cropped);
if(findUPflag())
cropBankNoByUPflag();
//else
//执行第二种方法定位银行卡号的y值,方法构想
//1.cropped.rows/5至cropped.rows*4/5之间,以间距cropped.rows/5扫描
//2.检测直线,比较直线的长度和数量来确认;另一种方法,二值化图像,比较数字间的空白数。但因为背景复杂且有时相差很大,此种方法成功率较低。
//如果需要高效稳定的方法,这一块还需要继续找其他方法
}
}
co.clear();
conn.clear();
lines.clear();
imshow("Bob_BankCard_Num_Reader",frame);
c=waitKey(24);;
if(c==27)
stop=true;
}
}
void drawDetectLines(Mat& image,const vector& lines,Scalar & color)
{
vector::const_iterator it=lines.begin();
while(it!=lines.end()&&lines.size()>=3)
{
Point pt1((*it)[0],(*it)[1]);
Point pt2((*it)[2],(*it)[3]);
line(image,pt1,pt2,color,1); // 线条宽度设置为1
++it;
}
}
void vec4i2vecPoint()
{
vector::const_iterator it=lines.begin();
while(it!=lines.end())
{
Point pt1((*it)[0],(*it)[1]);
Point pt2((*it)[2],(*it)[3]);
co.push_back(pt1);
co.push_back(pt2);
++it;
}
conn.push_back(co);
}
void on_canny( int, void* )
{
Canny(grayImg,contours,i_canThres,i_canThres*3);
}
//void on_Threshold( int, void* )
//{
//threshold(contours ,contours ,g_nThresholdValue,255,g_nThresholdType);
//imshow( "Threshold", contours );
//}
void on_line( int, void* )
{
HoughLinesP(contours,lines,1,CV_PI/180,80,minLineLength,maxLineGap);//maxLineGap);
}
bool findUPflag()
{
int result_cols = cropped.cols - UPImg.cols + 1;
int result_rows = cropped.rows - UPImg.cols + 1;
Mat result = Mat( result_cols, result_rows, CV_32FC1 );
matchTemplate( cropped, UPImg, result, CV_TM_SQDIFF );
normalize(result,result,0,1,NORM_MINMAX,-1,Mat());
minMaxLoc( result, NULL, NULL, &minLoc, NULL, Mat() );
rectangle(cropped, Rect(minLoc,UPImg.size()),Scalar(255,0,0), 1, 8, 0);
cout<
if(minLoc.x
return false;
else
return true;//在右下角的情况才会返回,因为大部分卡如此
}
void cropBankNoByUPflag()
{
Rect rc;
rc.x=10;//左起
rc.width=cropped.cols-10;//右结束
rc.y=minLoc.y-60;
rc.height=60;
noImg=cropped(rc);
imshow("noImg",noImg);
}