实例介绍
【实例简介】
【实例截图】
【实例截图】
【核心代码】
#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]; }
好例子网口号:伸出你的我的手 — 分享!
相关软件
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
网友评论
我要评论