配置:WIN7下Qt5.8+VS2013+Opencv3.0
基于Qt5.8的简单手写数字识别界面是在 Opencv3.0 手写数字识别(Hog特征+SVM分类器)的基础上利用Qt5.8编写的一个简单界面。
在该界面中可以通过鼠标手写输入数字进行识别,该界面直接使用 Opencv3.0 手写数字识别(Hog特征+SVM分类器)训练得到的Xml文件进行手写数字识别,由于训练样本的量较少,识别效果不是很理想,但可以作为编写手写数字识别界面的参考。具体过程如下:
直接新建一个Qt Application项目,并利用Qt Designer进行界面设计,最终效果如下:
各Label及按钮的名称如下:
QPushButton *ClearButton;
QPushButton *TestButton;
QPushButton *SaveButton;
QLabel *testResultlabel;
QLabel *loadImagelabel;
QLabel *loadXmllabel;
头文件主要进行了相关数据成员、成员函数的声明,程序如下。
#ifndef DOODLE_WIDGET_H
#define DOODLE_WIDGET_H
#include
#include "ui_doodle_widget.h"
#include
#include
#include
class Doodle_widget : public QWidget
{
Q_OBJECT
public:
Doodle_widget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *);//重绘事件(重点:由update()函数触发)
void mousePressEvent(QMouseEvent *);//鼠标按下事件
void mouseMoveEvent(QMouseEvent *);//鼠标移动事件(重点理解)
void mouseReleaseEvent(QMouseEvent *);//鼠标释放事件
void paint(QImage &theImage);//画图工具
private slots:
void savetheImage();
void test();
void clearPaint();
private:
Ui::Doodle_widgetClass ui;
QImage image;//一块画布
QRgb backColor;//画布背景色
QPoint lastPoint;//前一个点,因为线是由无数点组成的
QPoint endPoint;//后一个点(结束点)
bool isDrawing;//判断是否在绘图也就是判断鼠标是否操作
};
#endif // DOODLE_WIDGET_H
#include "doodle_widget.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::ml;
Doodle_widget::Doodle_widget(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
isDrawing = false;
image = QImage(128, 128, QImage::Format_RGB32);
backColor = qRgb(0, 0, 0);
image.fill(backColor);
ui.loadXmllabel->setText("Waitting...");
ui.loadImagelabel->setText("Waitting...");
ui.testResultlabel->setText("Waitting...");
connect(ui.SaveButton, SIGNAL(clicked()), this, SLOT(savetheImage()));
connect(ui.TestButton, SIGNAL(clicked()), this, SLOT(test()));
connect(ui.ClearButton, SIGNAL(clicked()), this, SLOT(clearPaint()));
}
void Doodle_widget::paintEvent(QPaintEvent *)
{//可理解为是一个绘图终端函数,在本程序中只通过update()触发,调用结束后
//这也是一个状态函数,只要没关闭mainwindow,一直待机等待update()触发
QPainter painter(this);//QPainter是绘图操作,父亲是paintwidget类而paintwidget的父亲又是mainwindow
//既关闭mainwindow就关闭paintwidget就关闭了painter
painter.drawImage(0, 0, image);//把图画在image上
}
void Doodle_widget::mousePressEvent(QMouseEvent *event){
if (event->button() == Qt::LeftButton){//-------------------------鼠标按下且为左键
lastPoint = event->pos();//----------------------------------设置起点为鼠标按下的点
endPoint = event->pos();//-----------------------------------设置终点为鼠标按下的点
isDrawing = true;//------------------------------------------开始绘图了
}
}
void Doodle_widget::mouseMoveEvent(QMouseEvent *event){ //重点理解部分
if (event->buttons() & Qt::LeftButton){//-------------------------鼠标按下左键并移动
endPoint = event->pos();//-----------------------------------鼠标每移动一次都刷新终点
paint(image); //--------------理解清楚这个函数--------仔细看看void paint (QImage &theImage)函数
//---------------------------------------最后会通过update()函数调用void paintEvent(QPaintEvent *)重绘函数
//---------------------------------------再仔细看看void paintEvent(QPaintEvent *)重绘函数会把图画在image画布上
//---------------------------------------我觉得理解了这个就理解得差不多了
}
}
void Doodle_widget::mouseReleaseEvent(QMouseEvent *event){
isDrawing = false;//--------------------------------------------绘图完毕
paint(image);//--------------------------------------------------把最后一点画在image画布上,可参考上面注释
}
void Doodle_widget::paint(QImage &theImage){//------------------(画图函数)调用这个函数就是调用重绘函数,把图画在image上
QPainter p(&theImage); //------------------------------------把图画在theImage(theImage是painterDevice类型参数,由于是引用,其实就是画在image上)
QPen apen;
apen.setWidth(5);//------------------------------------------画笔线条宽度设置为5
apen.setColor(Qt::white);
p.setPen(apen);//--------------------------------------------设置画笔线条宽度,也可以不设置,既把这两句注释掉,线条默认宽度为1
p.drawLine(lastPoint, endPoint);//----------------------------画线,由于鼠标移动事件会调用此函数,因此lastPoint和endPoint相距近似为0
//---------------------------因此可近似看成画点,点连起来就是画笔的痕迹了
lastPoint = endPoint;//--------------------------------------把终点复制给起点
update();//--------------------------------------------------刷新
}
void Doodle_widget::savetheImage()
{
QString path = QString("%1/demo.png").arg(QApplication::applicationDirPath());
image.save(path);
ui.loadXmllabel->setText("Save done...");
//if (!(image.save(path)))
//{
// QMessageBox::warning(this, tr("Warning"), tr("Save failed !"));
//}
//else
//{
// QMessageBox::warning(this, tr("Notice"), tr("Save OK !"));
//}
//ui.SaveButton->setText("success");
}
void Doodle_widget::test()
{
//creat SVM classfier
Ptr svm = SVM::create();
//load train file
svm = SVM::load("SVM_HOG.xml");
if (!svm)
{
ui.loadXmllabel->setText("load file faile...");
}
Mat test;
test = imread("Win32/Debug/demo.png");
//imshow("image", test);
ui.loadImagelabel->setText("succcess");
//winsize(64,128),blocksize(16,16),blockstep(8,8),cellsize(8,8),bins9
//检测窗口(64,128),块尺寸(16,16),块步长(8,8),cell尺寸(8,8),直方图bin个数9
HOGDescriptor hog(Size(128, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9);
vector descriptors;//HOG描述子向量
hog.compute(test, descriptors, Size(8, 8));//计算HOG描述子,检测窗口移动步长(8,8)
int r = svm->predict(descriptors); //对所有行进行预测
ui.testResultlabel->setText("The number is " + QString::number(r) + ".");
//ui.TestButton->setText("success");
}
void Doodle_widget::clearPaint()
{
image = QImage(128, 128, QImage::Format_RGB32);
backColor = qRgb(0, 0, 0);
image.fill(backColor);
ui.loadXmllabel->setText("Waitting...");
ui.loadImagelabel->setText("Waitting...");
ui.testResultlabel->setText("Waitting...");
update();
}
main.cpp文件默认就行,程序如下。
#include "doodle_widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Doodle_widget w;
w.show();
return a.exec();
}