基本信息
源码名称:qt 身份证识别 源码
源码大小:0.01M
文件格式:.zip
开发语言:C/C++
更新时间:2020-06-02
   友情提示:(无需注册或充值,赞助后即可获取资源下载链接)

     嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):813200300

本次赞助数额为: 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];

}