在好例子网,分享、交流、成长!
您当前所在位置:首页C/C++ 开发实例常规C/C++编程 → qt 身份证识别 源码

qt 身份证识别 源码

常规C/C++编程

下载此实例
  • 开发语言:C/C++
  • 实例大小:0.01M
  • 下载次数:42
  • 浏览次数:286
  • 发布时间:2020-06-02
  • 实例类别:常规C/C++编程
  • 发 布 人:lllcc
  • 文件格式:.zip
  • 所需积分:3
 相关标签: 身份证 识别

实例介绍

【实例简介】
【实例截图】

【核心代码】

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QTimer>
#include <QString>
#include <QFileDialog>
#include <QMessageBox>
#include <string>
#include <QDebug>
#include <stdlib.h>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked(){
    QString s=QFileDialog::getOpenFileName(this,"打开图片文件","/",tr("Images(*.png *.xpm *.jpg)"));
    std::string str=s.toStdString();
    const char* ch=str.c_str();  //路径不能包含中文,否则用imread打不开
    if(s.isEmpty()){
        return;
    }else{
        QImage* img=new QImage;
        if(!(img->load(s))){//加载图像

            QMessageBox::information(this,tr("打开图像失败"),tr("打开图像失败!"));
            delete img;
            return;
        }
        QImage* imgScaled=new QImage;//更改图片分辨率
        *imgScaled=img->scaled(480,360,Qt::KeepAspectRatio);//换为Qt::IgnoreAspectRatio,则一律变更
        ui->label_display->resize(imgScaled->size());
        ui->label_display->setPixmap(QPixmap::fromImage(*imgScaled));
        imgSrc=imread(ch,1);//使用打开的文件对话框获得的路径,由imread读出
    }
}


void MainWindow::on_pushButton_2_clicked(){
    Mat imgRplane=getRplane(imgSrc);//获得原始图像R分量

    vector<RotatedRect> rects;
    posDetect(imgRplane,rects);//获得身份证号码区域

    Mat outputMat;
    normalPosArea(imgRplane,rects[0],outputMat);//获得身份证号码字符矩阵

    vector<Mat> char_mat;//获得切割得的字符矩阵
    char_segment(outputMat,char_mat);

    //getAnnXML();//该函数只需执行一次,获得训练矩阵和标签矩阵,保存于xml文件中
    CvANN_MLP ann;
    ann_train(ann,10,24);//10为输出层结点,也等于输出的类别,24为隐藏层结点

    vector<int> char_result;
    classify(ann,char_mat,char_result);

    getParityBit(char_result); //最后一位易出错,直接由前17位计算最后一位

    //设置界面信息
    if(char_result[16]%2==1) //根据倒数第2位区分男女
    {
        ui->radioButton->setChecked(true);
    }else{
        ui->radioButton_2->setChecked(true);
    }

    QString id="";
    for(int i=0;i<int(char_result.size());  i){
        if(char_result[i]==10)
            id ="X";
        else
            id =QString::number(char_result[i],10);//将int转换为Qstring
    }

    ui->lineEdit_2->setText(id);

    QString birthday;
    for(int i=6;i<14;  i)
        birthday =id[i];
    ui->lineEdit->setText(birthday);//计算生日
}


Mat MainWindow::getRplane(const Mat &in){
    vector<Mat> splitBGR(in.channels());//容器大小为通道数3
    split(in,splitBGR);

    if(in.cols>700||in.cols>600){
        Mat resizeR(450,600,CV_8UC1);
        cv::resize(splitBGR[2],resizeR,resizeR.size());

        return resizeR;
    }
    else
        return splitBGR[2];
}


void MainWindow::OstuBeresenThreshold(const Mat &in,Mat &out){
    double ostu_T=threshold(in,out,0,255,CV_THRESH_OTSU);//otsu获得全局阈值

    double min;
    double max;
    minMaxIdx(in,&min,&max);
    const double CI=0.12;
    double beta=CI*(max-min 1)/128;
    double beta_lowT=(1-beta)*ostu_T;
    double beta_highT=(1 beta)*ostu_T;

    Mat doubleMatIn;
    in.copyTo((doubleMatIn));
    int rows=doubleMatIn.rows;
    int cols=doubleMatIn.cols;
    double Tbn;
    for(int i=0;i<rows;  i){
        //获取第 i行首像素指针
        uchar *p=doubleMatIn.ptr<uchar>(i);
        uchar *outPtr=out.ptr<uchar>(i);

        //对第i 行的每个像素(byte)操作
        for(int j=0;j<cols;  j){
            if(i<2 || i>rows-3||j<2||j>rows-3){
                if(p[j]<=beta_lowT)
                    outPtr[j]=0;
                else
                    outPtr[j]=255;
            }
            else{
                Tbn=sum(doubleMatIn(Rect(i-2,j-2,5,5)))[0]/25;//窗口大小25*25
                if(p[j]<beta_lowT||(p[j]<Tbn&&(beta_lowT<=p[j]&&p[j]>=beta_highT)))
                    outPtr[j]=0;
                if(p[j]>beta_highT||(p[j]>=Tbn&&(beta_lowT<=p[j]&&p[j]>=beta_highT)))
                    outPtr[j]=255;
            }
        }
    }
}

void MainWindow::posDetect(const Mat &in,vector<RotatedRect> &rects){
    Mat threshold_R;
    OstuBeresenThreshold(in,threshold_R);//二值化

    Mat imgInv(in.size(),in.type(),cv::Scalar(255));
    Mat threshold_Inv=imgInv-threshold_R;//黑白色反转,即背景为黑色

    Mat element=getStructuringElement(MORPH_RECT,Size(15,3));//闭形态学的结构元素
    morphologyEx(threshold_Inv,threshold_Inv,CV_MOP_CLOSE,element);

    vector<vector<Point>> contours;
    findContours(threshold_Inv,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);//只检测外轮廓

    //对候选的轮廓进行进一步筛选
    vector<vector<Point>>::iterator itc=contours.begin();
    while(itc!=contours.end()){
        RotatedRect mr=minAreaRect(Mat(*itc));//返回每个轮廓的最小有界矩形区域
        if(!isEligible(mr)){//判断矩形轮廓是否符合要求
            itc=contours.erase(itc);
        }
        else{
            rects.push_back(mr);
              itc;
        }
    }

}


bool MainWindow::isEligible(const RotatedRect &candidate){
    float error=0.2;
    const float aspect=4.5/0.3;//长宽比
    int min=10*aspect*10;//最小区域
    int max=50*aspect*50;//最大区域
    float rmin=aspect-aspect*error;//考虑误差后的最小长宽比
    float rmax=aspect aspect*error; //考虑误差后的最大长宽比

    int area=candidate.size.height*candidate.size.width;
    float r=(float)candidate.size.width/(float)candidate.size.height;
    if(r<1)
        r=1/r;

    if(area<min||area>max||(r<rmin||r>rmax))//满足该条件才认为该candidate为车牌区域
        return false;
    else
        return true;

}


void MainWindow::normalPosArea(const Mat &inputImg,RotatedRect &rects_optimal,Mat& output_area){
    float r,angle;
    angle=rects_optimal.angle;
    r=(float)rects_optimal.size.width/ (float) (float)rects_optimal.size.height;
    if(r<1)
        angle=90 angle;
    Mat rotmat=getRotationMatrix2D(rects_optimal.center,angle,1);//获得变形矩阵对象
    Mat img_rotated;
    warpAffine(inputImg,img_rotated,rotmat,inputImg.size(),CV_INTER_CUBIC);

    //裁剪图像
    Size rect_size=rects_optimal.size;

    if(r<1)
        std::swap(rect_size.width,rect_size.height);
    Mat img_crop;
    getRectSubPix(img_rotated,rect_size,rects_optimal.center,img_crop);

    //用光照直方图调整所有裁剪得到的图像,使具有相同宽度和高度,适用于训练和分类
    Mat resultResized;
    resultResized.create(20,300,CV_8UC1);
    cv::resize(img_crop,resultResized,resultResized.size(),0,0,INTER_CUBIC);

    resultResized.copyTo(output_area);
}


void MainWindow::char_segment(const Mat &inputImg,vector<Mat> &dst_mat){
    Mat img_threshold;

    Mat whiteImg(inputImg.size(),inputImg.type(),cv::Scalar(255));
    Mat in_Inv=whiteImg-inputImg;
    threshold(in_Inv,img_threshold,0,255,CV_THRESH_OTSU);//大津法二值化

    int x_char[19]={0};
    short counter=1;
    short num=0;
    bool flag[img_threshold.cols];

    for(int j=0;j<img_threshold.cols;  j){
        flag[j]=true;
        for(int i=0;i<img_threshold.rows;  i){
            if(img_threshold.at<uchar>(i,j)!=0){
                flag[j]=false;
                break;
            }
        }
    }

    for(int i=0;i<img_threshold.cols-2;  i){
        if(flag[i]==true){
            x_char[counter] =i;
            num  ;
            if(flag[i 1]==false&&flag[i 2]==false){
                x_char[counter]=x_char[counter]/num;
                num=0;
                counter  ;
            }
        }
    }

    x_char[18]=img_threshold.cols;
    for(int i=0;i<18;i  ){
        dst_mat.push_back(Mat(in_Inv,Rect(x_char[i],0,x_char[i 1]-x_char[i],img_threshold.rows)));
    }
}

void MainWindow::getAnnXML(){//此函数仅需运行一次,目的是获得训练矩阵和标签矩阵,保存于ann_xml.xml中
    FileStorage fs("D:\\qtchenxu\\idcard\\ann_xml.xml",FileStorage::WRITE);
    if(!fs.isOpened()){
        qDebug()<<"failed to open"<<"/n";

    }

    Mat trainData;
    Mat classes=Mat::zeros(1,500,CV_8UC1);//1700*48维,也即每个样本有48个特征值
    char path[60];
    Mat img_read;
    for(int i=0;i<10;i  ){//第i类
        for(int j=1;j<51;  j){//i类中第j个,即总共有11类字符,每类有50个样本
            sprintf(path,"D:\\qtchenxu\\idcard\\number\\%d\\%d(%d).png",i,i,j);
            img_read=imread(path,0);
            Mat dst_feature;
            calcGradientFeat(img_read,dst_feature);//计算每个样本的特征矢量
            trainData.push_back(dst_feature);

            classes.at<uchar>(i*50 j-1)=i;
        }
    }
    fs<<"TrainingData"<<trainData;
    fs<<"classes"<<classes;
    fs.release();
}


void MainWindow::calcGradientFeat(const Mat &imgSrc,Mat &out){
    vector<float> feat;
    Mat image;

    cv::resize(imgSrc,image,Size(8,16));

    // 计算x方向和y方向上的滤波
    float mask[3][3]={{1,2,1},{0,0,0},{-1,-2,-1}};

    Mat y_mask=Mat(3,3,CV_32F,mask)/8;
    Mat x_mask=y_mask.t();// 转置
    Mat sobelX,sobelY;

    filter2D(image,sobelX,CV_32F,x_mask);
    filter2D(image,sobelY,CV_32F,y_mask);

    sobelX=abs(sobelX);
    sobelY=abs(sobelY);

    float totleValueX=sumMatValue(sobelX);
    float totleValueY=sumMatValue(sobelY);

    // 将图像划分为4*2共8个格子,计算每个格子里灰度值总和的百分比
    for(int i=0;i<image.rows;i=i 4){
        for(int j=0;j<image.cols;j=j 4){
            Mat subImageX=sobelX(Rect(j,i,4,4));
            feat.push_back(sumMatValue(subImageX)/totleValueX);
            Mat subImageY=sobelY(Rect(j,i,4,4));
            feat.push_back(sumMatValue(subImageY)/totleValueY);

        }
    }

     //计算第2个特征
    Mat imageGray;
    cv::resize(imgSrc,imageGray,Size(4,8));
    Mat p=imageGray.reshape(1,1);
    p.convertTo(p,CV_32FC1);
    for(int i=0;i<p.cols;i  ){
        feat.push_back(p.at<float>(i));
    }

    //增加水平直方图和垂直直方图
    Mat vhist=projectHistogram(imgSrc,1);//水平直方图
    Mat hhist=projectHistogram(imgSrc,0);//垂直直方图
    for(int i=0;i<vhist.cols;i  ){
        feat.push_back(vhist.at<float>(i));
    }
    for(int i=0;i<hhist.cols;i  ){
        feat.push_back(hhist.at<float>(i));
    }


    out=Mat::zeros(1,feat.size(),CV_32F);
    for(int i=0;i<int(feat.size());i  ){
        out.at<float>(i)=feat[i];
    }
}


float MainWindow::sumMatValue(const Mat &image){
    float sumValue=0;
    int r=image.rows;
    int c=image.cols;
    if(image.isContinuous()){
        c=r*c;
        r=1;
    }
    for(int i=0;i<r;i  ){
        const uchar* linePtr=image.ptr<uchar>(i);
        for(int j=0;j<c;j  ){
            sumValue =linePtr[j];
        }
    }
    return sumValue;
}


Mat MainWindow::projectHistogram(const Mat &img,int t){
    Mat lowData;
    cv::resize(img,lowData,Size(8,16));//缩放到8*16

    int sz=(t)?lowData.rows:lowData.cols;
    Mat mhist=Mat::zeros(1,sz,CV_32F);

    for(int j=0;j<sz;j  ){
        Mat data=(t)?lowData.row(j):lowData.col(j);
        mhist.at<float>(j)=countNonZero(data);
    }

    double min,max;
    minMaxLoc(mhist,&min,&max);

    if(max>0)
        mhist.convertTo(mhist,-1,1.0f/max,0);

    return mhist;
}


void MainWindow::ann_train(CvANN_MLP &ann,int numCharacters,int nlayers){
    Mat trainData,classes;
    FileStorage fs;
    fs.open("D:\\qtchenxu\\Id_recognition\\ann_xml.xml",FileStorage::READ);

    fs["TrainingData"]>>trainData;
    fs["classes"]>>classes;

    Mat layerSizes(1,3,CV_32SC1); //3层神经网络
    layerSizes.at<int>(0)=trainData.cols;//输入层的神经元节点数,设置为24
    layerSizes.at<int>(1)=nlayers;//1个隐藏层的神经元结点数,设置为24
    layerSizes.at<int>(2)=numCharacters;//输出层的神经元结点数为:10
    ann.create(layerSizes,CvANN_MLP::SIGMOID_SYM,1,1);//初始化ann
    CvANN_MLP_TrainParams param;
    param.term_crit=cvTermCriteria(CV_TERMCRIT_ITER CV_TERMCRIT_EPS,5000,0.01);


    Mat trainClasses;
    trainClasses.create(trainData.rows,numCharacters,CV_32FC1);
    for(int i=0;i<trainData.rows;i  ){
        for(int k=0;k<trainClasses.cols;k  ){
            if(k==(int)classes.at<uchar>(i)){
                trainClasses.at<float>(i,k)=1;
            }
            else
                trainClasses.at<float>(i,k)=0;
        }
    }

    ann.train(trainData,trainClasses,Mat(),Mat(),param);
}


void MainWindow::classify(CvANN_MLP &ann,vector<Mat> &char_Mat,vector<int> &char_result){
    char_result.resize(char_Mat.size());
    for(int i=0;i<int(char_Mat.size());  i){
        Mat output(1,10,CV_32FC1);//1*10矩阵

        Mat char_feature;
        calcGradientFeat(char_Mat[i],char_feature);
        ann.predict(char_feature,output);
        Point maxLoc;
        double maxVal;
        minMaxLoc(output,0,&maxVal,0,&maxLoc);

        char_result[i]=maxLoc.x;
    }
}


void MainWindow::getParityBit(vector<int>&char_result){
    int mod=0;
    int wights[17]={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
    for(int i=0;i<17;  i)
        mod =char_result[i]*wights[i];//乘相应系数求和

    mod=mod%11;//对11求余

    int value[11]={1,0,10,9,8,7,6,5,4,3,2};
    char_result[17]=value[mod];

}

标签: 身份证 识别

实例下载地址

qt 身份证识别 源码

不能下载?内容有错? 点击这里报错 + 投诉 + 提问

好例子网口号:伸出你的我的手 — 分享

网友评论

发表评论

(您的评论需要经过审核才能显示)

查看所有0条评论>>

小贴士

感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。

  • 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
  • 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
  • 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
  • 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。

关于好例子网

本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明

;
报警