基本信息
源码名称:qt 身份证识别 源码
源码大小:0.01M
文件格式:.zip
开发语言:C/C++
更新时间:2020-06-02
友情提示:(无需注册或充值,赞助后即可获取资源下载链接)
嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):813200300
本次赞助数额为: 3 元×
微信扫码支付:3 元
×
请留下您的邮箱,我们将在2小时内将文件发到您的邮箱
源码介绍
#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];
}