手写数字识别【QT+OpenCV】
【说明】
手写数字识别的实现方式很多。
本文尽量将其简化,以让大家能够快速了解怎样实现一个动起来的系统。
【截图】
【思路】
1.特征提取
将图像划分为5*5大小的区域,然后计算该区域内黑色(或白色)的像素点所占比例。
将需要测试的图像、用来分类的图像都进行特征提取。
2.计算当前的测试图像与用来分类的图像之间的欧氏距离。
3.找出欧式距离最小的值即为与当前测试图像最匹配的图像,即将该图像所代表的数字作为当前测试图像的结果。
4.为了处理上的方便,做了简化处理如下:
4.1仅仅选用10幅用来分类的图像。
在实际应用中,10幅图像远远是不够的。但是为了简化程序,这里仅仅选用10幅图像,即数字0~9每个数字仅仅选用了一个特征。
在实际系统内,当选用的特征图像越多时,系统的准确度越高。
4.2采用了最近邻。即选用欧式距离最小的图像作为当前测试图像的结果。
在实际系统中,往往需要采用K近邻,即选择最小的K个欧式距离,判断他们分别属于哪个类从而决定当前数字结果。
因为简化的原因,数字识别的正确率不是很高。但是基本能够满足学习的需求。
【部分代码】
该系统采用QT+OpenCV开发完成,部分代码如下:
【主类内变量及槽函数】
-
public:
-
explicit MainWindow(QWidget *parent = 0);
-
~MainWindow();
-
cv::Mat testImage,srcImage[
10],tempImage;
//testImage值要测试的数字图像,srcImage指已经有的用来实现分类的数字图像
-
QImage img;
-
float testFeature[
25];
//该数组用来存储待检测数字图像的特征值。
-
float srcFeature[
10][
25];
//用来存储原始数字图像的特征值。只有10个数字0~9的图像
-
void getFeature(cv::Mat m,float a[25]);
//这里定义一个获取图像特征的函数。
-
float ouDistance(float a[25],float b[25]);
-
float oDistance(float a[25],float b[25]);
-
-
-
private slots:
-
void on_openLenaJpg_triggered();
-
-
void on_exitSystem_triggered();
-
-
void on_openCustomeFile_triggered();
-
-
void on_restoreFile_triggered();
-
-
void on_copyright_triggered();
-
-
void on_about_triggered();
-
-
void on_showImage_triggered();
-
-
void on_showMessage_triggered();
-
-
void on_ImageAndMessage_triggered();
【打开自定义路径待测图像】
-
void MainWindow::on_openCustomeFile_triggered()
-
{
-
QString filename = QFileDialog::getOpenFileName(
this,tr(
"Open Image"),
"",tr(
"Image File(*.bmp *.jpg *.jpeg *.png)"));
-
QTextCodec *code = QTextCodec::codecForName(
"gb18030");
-
std::
string name = code->fromUnicode(filename).data();
-
testImage = cv::imread(name);
-
if(!testImage.data)
-
{
-
QMessageBox msgBox;
-
msgBox.setText(tr(
"未找到数据"));
-
msgBox.exec();
-
}
-
else
-
{
-
cv::cvtColor(testImage,testImage,CV_BGR2RGB);
-
img = QImage((
const
unsigned
char*)(testImage.data),testImage.cols,testImage.rows, testImage.cols*testImage.channels(), QImage::Format_RGB888);
-
ui->label1->clear();
-
img= img.scaled(ui->label1->width(), ui->label1->height());
-
ui->label1->setPixmap(QPixmap::fromImage(img));
-
//ui->processPushButton->setEnabled(true);
-
// ui->label1->resize(ui->label1->pixmap()->size());//设置当前标签为图像大小
-
// ui->label1->resize(img.width(),img.height());
-
-
//this->setWidget(label1);
-
}
-
}
【图像特征提取:完成图像5*5=25个特征,每个特征表示该子区域内白像素个数】
-
void MainWindow::getFeature(cv::Mat m,
float a[
25])
-
{
-
int M,N;
//用来存储图像m的宽高
-
int i,j;
-
M=m.cols;
-
N=m.rows;
-
for(i=
0;i<
25;i++)
-
a[i]=
0;
-
// QMessageBox::information(NULL, "Title", QString::number(m.at<uchar>(188,88)), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
for(i=
0;i<M;i++)
-
for(j=
0;j<N;j++)
-
if(m.at<uchar>(i,j)==
255)
-
{
-
// a[i/5*5+j/5]++; //这里计算错误,不能放入对应的特征值内
-
// QMessageBox::information(NULL, "Title", QString::number(5), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
-
// a[M/i*5+N/j]++;
-
//a[M/(i+1)*5+N/(j+1)]++;
-
a[i/(M/
5)*
5+j/(N/
5)]++;
-
// QMessageBox::information(NULL, "Title","add", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
}
-
for(i=
0;i<
25;i++)
-
{
-
// QMessageBox::information(NULL, "Title", QString::number(a[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
a[i]=a[i]/((M/
5)*(N/
5));
-
// QMessageBox::information(NULL, "Title", QString::number(a[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
// QMessageBox::information(NULL, "Title", QString::number(5), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
-
-
}
-
// QMessageBox::information(NULL, "Title", QString::number(a[5]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
-
}
【欧氏距离计算,第二个函数是测试用的】
-
float MainWindow::ouDistance(
float a[
25],
float b[
25])
//这个函数居然忘记写MainWindow的类关系,调试好久,瀑布汗!
-
{
-
int i;
-
float distance=
0;
//开始忘记置零,出错呀!!!
-
for(i=
0;i<
25;i++)
-
distance+=(a[i]-b[i])*(a[i]-b[i]);
-
distance=
sqrt(distance);
-
return distance;
-
}
-
float MainWindow::oDistance(
float a[
25],
float b[
25])
//这个函数是ouDistance出问题时测试的,并没有用
-
{
-
int i;
-
float distance=
0;
//开始忘记置零,出错呀!!!
-
//,为了测试ouDistance函数,重写了oDistance发现问题,结果再次出现问题一直却一直在此函数修改。而调用函数用的还是ouDistance
-
-
for(i=
0;i<
25;i++)
-
distance+=(a[i]-b[i])*(a[i]-b[i]);
-
distance=
sqrt(distance);
-
return distance;
-
}
【说明】
-
void MainWindow::on_copyright_triggered()
-
{
-
-
}
-
-
void MainWindow::on_about_triggered()
-
{
-
QMessageBox::information(
this,
"关于",tr(
"本软件当前版本为1.0,由李立宗等人开发。如果有问题,欢迎联系:lilizong#gmail"));
-
return;
-
}
-
void MainWindow::on_ImageAndMessage_triggered()
-
{
-
int i;
-
float min;
//用来存储最小的欧式距离
-
int mini;
//用来存储最小的欧氏距离的数字号。
-
getFeature(testImage,testFeature);
//获取测试图像的特征值,并将其放到testFeature数组内。
-
// QMessageBox::information(NULL, "Title", QString::number(testFeature[6]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
//测试当前的testFeature是否正常
-
/*
-
for(i=0;i<25;i++)
-
QMessageBox::information(NULL, "Title", QString::number(testFeature[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
*/
-
// QMessageBox::information(NULL, "Title", QString::number(testImage.rows), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
for(i=
0;i<
10;i++)
-
{
-
QString filePath,fileName,allName;
-
filePath=
"image\\stand\\";
//当前图像目录
-
fileName=
".bmp";
//当前图像的扩展名
-
allName=filePath+
"\\"+QString::number(i)+fileName;
//i是文件名,使用QString::number(i)完成将其转换为QString类型,当前为数值型
-
String s=allName.toStdString();
//转换为标准的字符串型,imread不识别QString类型
-
srcImage[i] = cv::imread(s);
-
}
-
-
//以下部分用于测试上述代码是否能够将srcImage的值获取到。
-
/*
-
cv::cvtColor(srcImage[3],srcImage[3],CV_BGR2RGB);
-
img = QImage((const unsigned char*)(srcImage[3].data),srcImage[1].cols,srcImage[1].rows, srcImage[1].cols*srcImage[1].channels(), QImage::Format_RGB888);
-
ui->label1->clear();
-
img= img.scaled(ui->label1->width(), ui->label1->height());
-
ui->label1->setPixmap(QPixmap::fromImage(img));
-
*/
-
-
// 获取原始数字图像的特征值。
-
for(i=
0;i<
10;i++)
-
getFeature(srcImage[i],srcFeature[i]);
-
/*
-
for(i=0;i<25;i++)
-
QMessageBox::information(NULL, "Title", QString::number(srcFeature[0][i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
*/
-
float ouDistanceValue[
10]={
0};
//存储当前测试图像与已知的十个数字图像之间的欧氏距离
-
for(i=
0;i<
10;i++)
-
{
-
ouDistanceValue[i]=ouDistance(testFeature,srcFeature[i]);
-
// ouDistanceValue[i]=i;
-
}
-
-
//总是不能得到结果,测试下ouDistance有没有问题。
-
/*
-
for(i=0;i<10;i++)
-
QMessageBox::information(NULL, "Title", QString::number(ouDistanceValue[i]), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
*/
-
mini=
0;
-
min=ouDistanceValue[
0];
//给min赋个初始值,假设与数字0的距离最小。
-
for(i=
0;i<
10;i++)
-
{
-
if(min>ouDistanceValue[i])
-
{
-
min=ouDistanceValue[i];
-
mini=i;
-
}
-
}
-
// QMessageBox::information(NULL, "Title", QString::number(mini), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
//上述语句测试一下mini是否能够取得正确的值
-
//将与当前测试图像匹配的图像显示在label2内
-
cv::cvtColor(srcImage[mini],srcImage[mini],CV_BGR2RGB);
-
img = QImage((
const
unsigned
char*)(srcImage[mini].data),srcImage[mini].cols,srcImage[mini].rows, srcImage[mini].cols*srcImage[mini].channels(), QImage::Format_RGB888);
-
ui->label2->clear();
-
img= img.scaled(ui->label2->width(), ui->label2->height());
-
ui->label2->setPixmap(QPixmap::fromImage(img));
-
//将当前图像的匹配结果显示在一个消息框内
-
QMessageBox::information(
NULL,
"测试结果",
"当前测试图像的识别结果为数字:"+QString::number(mini), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
-
}
转载地址
https://blog.csdn.net/superdont/article/details/46926235