手写字符识别: - 使用Qt/C++/Linux实现手写字符(主要是界面) - 使用MNIST手写字符集作为训练源 - 使用OpenCV/SVM/KNN训练MNIST数据集
MNIST字符集读取与训练
MNIST介绍:SVM+MNIST
将代码简单修改就是本文使用的训练测试源码,在这里就不赘述了,具体可看源码。
手写字符界面
所谓手写其实是模拟手写,毕竟一般开发的笔记本和PC没有手写功能,就是用鼠标画图。
先定义两个点lastpoint,endpoint。
鼠标拖动的时候更新两个点数据
void Drawing::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
lastPoint = event->pos();
}
endPoint = lastPoint;
}
而Qt会自动调用paintEvent绘图,我们使用lastpoint,endpoint划线,同时两个点数据一直在更新,这样就模拟出手写的效果。
void Drawing::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
//qDebug()<<this->size();
QPainter pp(&pix);
pp.setPen(pen);
pp.setFont(font);
pp.drawLine(lastPoint,endPoint);
pp.setRenderHint(QPainter::HighQualityAntialiasing,true);
lastPoint = endPoint;
QPainter painter(this);
painter.drawPixmap(0,0,pix);
}
这里为了效果使用缓冲技术,先将线画在一张图片上,在将图片绘制在界面上。
手写字符处理
鼠标模拟手写结束之后,需要将我们绘制的图片处理一下。我们绘制的图片显示是黑白的,但是实际上他是彩色的。而且训练的数据是1* 784的向量,我们的图片也要转换成这个尺寸,否则会报错。
处理流程为:
保存界面图片
图片缩放至28*28(这是字符集的尺寸)
灰度化图片
二值化图片
将28*28转换为1*784(28*28)
将图片的数据转换为CV_32F
得到结果
源码为:
QImage drawImg = ui->wgtDrawing->getImage();
QImage scaleImg = drawImg.scaled(28,28);
cv::Mat img = toMat(scaleImg);
//cv::imshow("drawImg",img);//correct
cv::Mat gray = getGrayImg(img);
//cv::imshow("gray",gray);//correct
cv::Mat bin = getBinImg(gray);
//cv::imshow("bin",bin);//correct
cv::Mat temp(1,28* 28, CV_8UC1);
for(int i=0;i<bin.rows;i++){
for(int j=0;j<bin.cols;j++){
uchar a=bin.at<uchar>(i,j);
temp.at<uchar>(0,i*28+j)=a;
}
}
temp.convertTo(temp,CV_32F);
然后就可以是这个图片进行识别了
结果预测
预测流程是:
检查xml文件是否存在
加载xml文件
预测
结果显示
代码为:
std::cout << "svm预测开始" << std::endl;
file.open("svm.xml");
if (!file.is_open())
{
std::cout << "->SVM训练结果文件svm.xml不存在" << std::endl;
}
std::cout << "->开始加载svm模型" << std::endl;
cv::Ptr<cv::ml::SVM> svm = cv::Algorithm::load<cv::ml::SVM>("svm.xml");
std::cout << "->svm模型加载完毕" << std::endl;
predicted = svm->predict(temp);
result = static_cast<int>(predicted);
std::cout << "svm预测结束" << std::endl;
源码使用
本文源码地址为:HandWriting
源码有三个文件夹:
data
src
tools
data是已经解压的mnist数据集,src是QT的手写字符识别软件,tools里面是SVM/KNN/OpenCV训练测试MNIST工具。
先到tools文件夹下,可以看到:
将data文件夹中的数据集复制到此文件夹下:
然后编译:
cmake .
make
knntt
和svmtt
就是训练和测试工具(svmtraintest)。
训练SVM,执行:
./svmtt
输出为:
svm.xml就是训练结果文件,将其复制到手写字符软件编译可执行文件文件夹下。
同理可得knn结果文件:
编译运行手写字符软件:
使用鼠标绘制字符(相当于手写),点击Type下拉框选择SVM/KNN模型(暂时只有这两个)点击GO就会从软件文件夹加载之前训练的*.xml文件然后预测结果:
到此结束,源码注释都有,可自行优化。
SVM和KNN的原理在这,准确率不会太高。可能使用pytorch或者TensorFlow会更好。