名片、身份证、银行卡定位矫正算法:基于LSD直线检测,角点确定,透视变换

这里写图片描述

lsd直线提取程序说明
函数模型:ntuple_list lsd(image_double image);
直线提取程序是C语言,若使用C++,需要在头文件说明:

extern "C"
{
#include "lsd.h"
};

该程序处理的数据类型是作者自己定义的image_double类型,所以无论编程者将图像存在何种格式下,必须进行类型转化。image_double类型定义如下:

typedef struct image_double_s
{
  double * data;
  unsigned int xsize,ysize;
} * image_double;

以为图像的坐标原点在左上角,x轴表示宽度,水平向右,y轴表示高度,竖直向下,因此,图像中一点(x,y)的值为image->data[ x + y * image->xsize ]。
因为C++将图像存储为IplImage格式,而C语言的程序中无法使用C++的头文件,因此IplImage与image_double类型的转换必须在函数外进行,无法整合在lsd函数中,因此,在使用该函数前需要一小段代码:

IplImage* im_gray = cvCreateImage(cvSize(m_im->width,m_im->height),IPL_DEPTH_8U,1);
    cvCvtColor(m_im,im_gray,CV_RGB2GRAY);
    image_double image = new_image_double(im_gray->width, im_gray->height);
    BYTE* im_src = (BYTE*)im_gray->imageData;
    int xsize = image->xsize;//宽度
    int ysize = image->ysize;//高度
    int y,x;

    for (y = 0;y < ysize;y++)
    {
        for (x = 0;x < xsize;x++)  //x是横坐标,y是纵坐标
        {
            image->data[y*xsize + x] = im_src[y*im_gray->widthStep + x];
        }
    }
    cvReleaseImage(&im_gray);
上述代码中,m_im是输入的彩色图像,为IplImage* 格式,因为sld函数处理的是灰度图像,所以上述代码中,将输入的彩色图像转化为灰度图像。在这段代码后,image_double格式的image就可以直接送入lsd函数:
detected_lines = lsd(image);

detected_lines中存储着直线检测的结果,类型是作者自己定义的ntuple_list型,该类型的结构定义如下:

typedef struct ntuple_list_s
{
  unsigned int size;
  unsigned int max_size;
  unsigned int dim;
  double * values;
} * ntuple_list;

detected_lines中存储的是所有线段的信息,每一条线段都可以看做是一个子单元:detected_lines->size是指子单元的个数,即直线段的条数;
detected_lines->max_size是指最大可以存储的子单元个数(一般不会用到,程序内部自动分配空间,该项数字只是记录作用);
detected_lines->dim是指每一个子单元中数据的维数,一般是5,即线段首端点的x、y坐标,线段尾端点的x、y坐标,线段的宽度(这个指标一般不为1,但是没有用过,一般只是将线段首尾点相连作为提取到的线段);
这些指标具体的值存在value中,detected_lines->values[j*dim+0]和detected_lines->values[j*dim+1]分别是线段一个端点的x、y坐标,detected_lines->values[j*dim+2]和detected_lines->values[j*dim+3]分别是线段另一个端点的x、y坐标,detected_lines->values[j*dim+4]为线段的宽度。其中,j为某一条线段的索引号。

  1. 主调用例子
#include "IDCardRec.h"


char * configFile = "config.txt";
char* trainSetPosPath = (char *)malloc(200*sizeof(char));
void readConfig(char* configFile, char* trainSetPosPath){
    fstream f;
    char cstring[1000];
    int readS=0;
    f.open(configFile, fstream::in);
    char param1[200]; strcpy(param1,"");
    char param2[200]; strcpy(param2,"");
    char param3[200]; strcpy(param3,"");

    f.getline(cstring, sizeof(cstring));
    readS=sscanf (cstring, "%s %s %s", param1,param2, param3);
    strcpy(trainSetPosPath,param3);
}


vector<string> imgNames;
int labelTemp = 0;

void dfsFolder(string folderPath){
    _finddata_t FileInfo;
    string strfind = folderPath + "\\*";
    long Handle = _findfirst(strfind.c_str(), &FileInfo);
    if (Handle == -1L){
        cerr << "can not match the folder path" << endl;
        exit(-1);
    }
    do{
        if (FileInfo.attrib & _A_SUBDIR)        {
            if( (strcmp(FileInfo.name,".") != 0 ) &&(strcmp(FileInfo.name,"..") != 0))          {
                string newPath = folderPath + "\\" + FileInfo.name;
                labelTemp = atoi(FileInfo.name);
                dfsFolder(newPath);
            }
        }else  {
            string finalName = folderPath + "\\" + FileInfo.name;
            imgNames.push_back(finalName);

        }
    }while (_findnext(Handle, &FileInfo) == 0);
    _findclose(Handle);

}

void initTrainImage(){
    readConfig(configFile, trainSetPosPath);

    string folderPath = trainSetPosPath;
    dfsFolder(folderPath);

}

void processingTotal(){
    initTrainImage();

    int imgNum = imgNames.size();
    for(int iNum=0;iNum<imgNum;iNum++){

        cout<<endl<<iNum<<endl;
        cout<<imgNames[iNum].c_str()<<endl;
        IplImage * src=cvLoadImage(imgNames[iNum].c_str(),1);  
        if(!src) continue;
        if(1){
            cvNamedWindow("image",1);
            cvShowImage("image",src);
        }

        processingOne(src);


    }
}



extern IplImage* res_im;
void processingOne(IplImage * src){
    cvNamedWindow("dst",1);
//  IplImage * dst = jiaoZheng(src);
    char *vif = vifLine(src);
//  cout<<vif<<endl;
    IplImage * dst = jiaozheng2(res_im);
    cvShowImage("dst",dst);

    cvWaitKey(0);
}

void main()
{
    processingTotal();
}
  1. 矫正部分判断筛选:
#include "jiaoZheng.h"

//===================判断点在矩形内===================,这里把贴边的去掉
bool InRectYesOrNo(CvPoint pt,CvRect rect){
    if( ( (pt.x >rect.x)&&(pt.x <(rect.x+rect.width)) )&&((pt.y >rect.y)&&(pt.y <(rect.y+rect.height))) )
        return true;
    else
        return false;
}

//=============计算两点之间的距离
float lenghtOf2P(CvPoint pt1,CvPoint pt2){
     return sqrt((float)(pt1.x - pt2.x)*(pt1.x - pt2.x)+(pt1.y - pt2.y)*(pt1.y - pt2.y));
}

//===============根据顶部或者底部线段,筛选出同角度下总长度最长的线段====================/
vector<cv::Vec4i> chooseLine1(IplImage* src,vector<linePtAngle> lineTop){
    //第一步:确定所有线段都是首小于尾部(以X轴为参考计算)
    for(unsigned int i = 0; i < lineTop.size() ; i++){
        if( lineTop[i].startPt.x >lineTop[i].endPt.x ){
            CvPoint temp;
            temp.x =  lineTop[i].startPt.x;     temp.y =  lineTop[i].startPt.y; 
            lineTop[i].startPt.x = lineTop[i].endPt.x;      lineTop[i].startPt.y = lineTop[i].endPt.y;
            lineTop[i].endPt.x = temp.x;        lineTop[i].endPt.y = temp.y;
        }
    }

    //显示首尾ok后的线
    if(showSteps){
        for(unsigned int i = 0; i < lineTop.size() ; i++){
            cvLine(src,lineTop[i].startPt,lineTop[i].endPt,CV_RGB(0,255,255),6,CV_AA);
            printf("======================== %d,%d\n",lineTop[i].startPt.x,lineTop[i].endPt.x);
        }
        cvShowImage("src",src);
        cvWaitKey(0);
    }

    //找出堆在一起的平行线,这个一般是银联的标志
    int a[20];
    for(int n=0;n<20;n++) a[n] = -1;
    int n = 0;
    //将平行,且两条线段中的一条首与一条尾相距很小时将这两天先合并
    for(unsigned int i = 0; i < lineTop.size()-1 ; i++){
        for(unsigned int j = i+1; j < lineTop.size() ; j++){
            if( (abs(lineTop[i].angle - lineTop[j].angle)<=3 )&&( abs(lineTop[i].startPt.x - lineTop[j].startPt.x )<=10 )&&(abs(lineTop[i].endPt.x - lineTop[j].endPt.x )<=30)&&(i!=j) ){
                a[n++] = i;
                a[n++] = j;
            }
        }
    }

    //显示去掉银联标志后的正确的图
    if(showSteps){
        for(unsigned int j = 0; j < lineTop.size() ; j++){  
            if( (j!=a[0])&&(j!=a[1])&&(j!=a[2])&&(j!=a[3])&&(j!=a[4])&&(j!=a[5])&&(j!=a[6])&&(j!=a[7])&&(j!=a[8])
                &&(j!=a[9])&&(j!=a[10])&&(j!=a[11])&&(j!=a[12])&&(j!=a[13])&&(j!=a[14])&&(j!=a[15])&&(j!=a[16])&&(j!=a[17])
                &&(j!=a[18])&&(j!=a[19]) )
                cvLine(src,lineTop[j].startPt,lineTop[j].endPt,CV_RGB(0,0,255),6,CV_AA);

        }
        cvShowImage("src",src);
        cvWaitKey(0);
    }

    //将堆在一起的平行线,角度赋大值,去掉他们。
    for(int n=0;n<20;n++){
        for(unsigned int j = 0; j < lineTop.size() ; j++){  
            if(j == a[n]){
                lineTop[j].angle = 45;
            }
        }
    }
    /*cvShowImage("src",src);
    cvWaitKey(0);*/

    将数条平行在一起的线去掉
    //vector<linePtAngle>::iterator it;
    //int flagNum = 0;
    //for(it=lineTop.begin();it!=lineTop.end();++it){   
        flagNum ++;
    //  if( flagNum==a[0] ){
   
    //      it=lineTop.erase(it);
    //      break;
    //  }else
    //      ++it;   
    //}

    //for(int n=0;n<20;n++){
   
    //  int flagNum = 0;                
    //  for(vector<linePtAngle>::iterator it=lineTop.begin(); it!=lineTop.end(); ){
   
    //      flagNum ++;
    //      if(flagNum==a[n]){
   
    //          it = lineTop.erase(it); //不能写成arr.erase(it);
    //      }else{
   
    //          ++it;
    //      }           
    //  }
    //}
    //
    //for(unsigned int i = 0; i < lineTop.size() ; i++){
   
    //  cvLine(src,lineTop[i].startPt,lineTop[i].endPt,CV_RGB(255,0,0),6,CV_AA);
    //}

    //cvShowImage("src",src);
    //cvWaitKey(0);


    //将平行,且两条线段中的一条首与一条尾相距很小时将这两天先合并
    for(unsigned int i = 0; i < lineTop.size()-1 ; i++){
        for(unsigned int j = i+1; j < lineTop.size() ; j++){
            if( (abs(lineTop[i].angle - lineTop[j].angle)<=3 )&&( abs(lineTop[i].endPt.x - lineTop[j].startPt.x )<=5 )&&(lineTop[i].startPt.x < lineTop[j].startPt.x) ){
                lineTop[i].startPt.x = lineTop[i].startPt.x;
                lineTop[i].startPt.y = lineTop[i].startPt.y;

                lineTop[i].endPt.x = lineTop[j].endPt.x;
                lineTop[i].endPt.y = lineTop[j].endPt.y;
            }
        }
    }


    float *angleTop;
    angleTop = (float *)malloc(lineTop.size()*sizeof(float));

    int sumTop[40] = {
  0};


    for(int jAngle = -20;jAngle<20;jAngle ++){
        for(unsigned int i = 0; i < lineTop.size() ; i++){
            if( (lineTop[i].angle == jAngle) )
                sumTop[jAngle+20] += lineTop[i].lineLength;
        } 
    }



    if(showSteps){
        for(int jAngle = -20;jAngle<20;jAngle ++)
            cout<<sumTop[jAngle+20]<<" ";
    }


    int maxSumTop = sumTop[0];  
    for(int jAngle = -20;jAngle<20;jAngle ++){
        if (maxSumTop<sumTop[jAngle+20])  
        {  
            maxSumTop = sumTop[jAngle+20];  
        } 
    }

    if(showSteps)
        cout<<maxSumTop<<endl;

    vector<cv::Vec4i> lines;
    for(int jAngle = -20;jAngle<20;jAngle ++){
        if(maxSumTop == sumTop[jAngle+20]){
            for(int j = 0; j<lineTop.size();j++){
                if((jAngle == lineTop[j].angle)||((jAngle+1) == lineTop[j].angle)||((jAngle-1) == lineTop[j].angle)) {
                    if(showSteps)
                        cvLine(src,lineTop[j].startPt,lineTop[j].endPt,CV_RGB(255,255,255),6,CV_AA);
                    Vec4i v;
                    v[0] = lineTop[j].startPt.x; v[1] = lineTop[j].startPt.y;
                    v[2] = lineTop[j].endPt.x; v[3] = lineTop[j].endPt.y;

                    lines.push_back(v);
                }
            }
        }
    }
    return lines;
}

//===============根据顶部或者底部线段,筛选出同角度下总长度最长的线段=================
  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值