研究手寫識別的時候看到了zinnia,就進行了測試,存在問題是zinnia書寫依賴筆畫順序,而且沒有找到更多的可以用於訓練的數據。
zinnia介紹
zinnia是一個基於svm的開源的手寫識別庫。zinnia簡單地接收用戶筆劃作爲一系列座標數據,並輸出按SVM置信度排序的n個最佳字符。爲了保持可移植性,Zinnia沒有任何渲染功能。此外,Zinnia還提供訓練模塊,使我們能夠以低成本創建任何手寫識別系統。
特點
支持向量機的實用精度
便攜緊湊的設計 - POSIX / Windows(取決於C ++ / STL)
線程安全的C / C ++ / Perl / Ruby / Python庫
實用識別速度(50-100字符 /秒)
快速訓練
參考 zinnia的介紹:http://taku910.github.io/zinnia/
zinnia屬於聯機文字識別,包含預處理、特徵提取和分類識別過程。
https://blog.csdn.net/stellar0/article/details/8741772
zinnia的使用
編譯zinnia
從git上下載源碼https://github.com/taku910/zinnia
打開VS2017命令行工具,進入zinnia源碼文件目錄
編譯,使用指令”nmake -f Makefile.msvc”
生成 zinnia.lib,zinnia.dll文件。
使用zinnia:
1.創建zinnia::Recognizer,加載model文件
2.創建zinnia::Character,把書寫的點座標傳進來
3.zinnia::Recognizer進行識別,得到一組包含識別的字和對應相似度的結果
注意點:
1.書寫順序會影響識別的結果,導致結果特別不準確
2.zinnia::Character需要設置字符的高寬,當只在一個小的區域書寫,設置高寬很大時,相當於拿這個小的區域去和一個字的一部分去比較,取出最相近的。所以代碼裏面有根據點來確定一個區域,根據此區域設置zinnia::Character的高寬。
代碼
.h文件:
#ifndef PAINTWIDGET_H
#define PAINTWIDGET_H
#include <QWidget>
#include <QMap>
class PaintWidget : public QWidget
{
Q_OBJECT
public:
explicit PaintWidget(QWidget *parent = nullptr);
void clear();
void init();
void recignize();
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent * e);
void mouseReleaseEvent(QMouseEvent * e);
bool event(QEvent *event);
private:
void resizeRect(QPoint p);
void adjustPoints();
signals:
void sigResult(QMap<QString, float> result);
public slots:
private:
QImage *m_pCanvas = nullptr;
QPainter *m_pPainter = nullptr;//臨時畫布的painter
QPoint m_posLast;
QMap<QString, float> m_result;
QRect m_rect;
std::vector<QPoint> m_vecPoints; //一個筆畫所含的點,目前是把
std::vector<std::vector<QPoint>> m_vecPointsList; // 裏面的vector爲一個筆畫,外面一層爲所有筆畫集合
};
#endif // PAINTWIDGET_H
.cpp文件:
#include "paintwidget.h"
#include <QPainter>
#include <QDebug>
#include <QImage>
#include <QMouseEvent>
#include <QApplication>
#include "zinnia/zinnia.h"
PaintWidget::PaintWidget(QWidget *parent) : QWidget(parent)
{
}
void PaintWidget::init()
{
m_pCanvas = new QImage(this->size(),QImage::Format_ARGB32_Premultiplied);
m_pCanvas->fill(Qt::transparent);
qInfo()<<this->size();
m_pPainter = new QPainter(m_pCanvas);
// m_pPainter->setPen(Qt::black);
m_pPainter->setRenderHint(QPainter::Antialiasing, true);
m_pPainter->setCompositionMode(QPainter::CompositionMode_Source);
m_pPainter->setPen(QPen(Qt::red,3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
}
void PaintWidget::clear()
{
m_vecPointsList.clear();
m_pCanvas->fill(Qt::transparent);
update();
}
void PaintWidget::recignize()
{
// 漢字識別
zinnia::Recognizer *recognizer = zinnia::Recognizer::create();
QString strPath = QApplication::applicationDirPath()+"/handwriting-zh_CN.model";
qInfo()<<__FUNCTION__<<strPath;
/*初始化漢字庫*/
if (!recognizer->open( strPath.toStdString().data() )){
qInfo()<<__FUNCTION__<<"open model fail";
return;
}
zinnia::Character *character = zinnia::Character::create();
if(m_vecPointsList.size()<=0){
qInfo()<<__FUNCTION__<<"no points";
return;
}
character->clear();
// character->set_width(this->width());
// character->set_height(this->height());
qInfo()<<m_rect;
character->set_width(m_rect.width());
character->set_height(m_rect.height());
adjustPoints();
for ( unsigned int i = 0 ; i < m_vecPointsList.size() ; i++ )
{
std::vector<QPoint> vecTmp = m_vecPointsList[i];
for ( unsigned int j = 0 ; j < vecTmp.size() ; j++ )
{
character->add( i , vecTmp[j].x() , vecTmp[j].y() );
}
}
zinnia::Result *result = recognizer->classify( *character , m_vecPointsList.size()/*m_vecPoints.size() / 2 +1*/ );
if ( !result ) return;
m_result.clear();
for ( size_t i = 0 ; i < result->size() ; ++i )
{
std::string str1 = result->value(i);
float score = result->score(i);
QString qs = str1.c_str();
qInfo()<<qs<<score;
m_result.insert(qs, score);
}
emit sigResult(m_result);
delete result;
delete character;
delete recognizer;
m_vecPoints.clear();
m_vecPointsList.clear();
}
void PaintWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawImage(0,0,*m_pCanvas);
}
void PaintWidget::mousePressEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
qInfo()<<"mouse press"<<pos;
m_posLast = pos;
m_pPainter->drawPoint(pos);
resizeRect(pos);
m_vecPoints.push_back(pos);
}
void PaintWidget::mouseMoveEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
m_pPainter->drawLine(m_posLast, pos);
m_vecPoints.push_back(pos);
m_posLast = pos;
resizeRect(pos);
update();
}
void PaintWidget::mouseReleaseEvent(QMouseEvent *event)
{
qInfo()<<__FUNCTION__<<event->pos();
m_vecPointsList.push_back( m_vecPoints );
m_vecPoints.clear();
}
bool PaintWidget::event(QEvent *event)
{
switch(event->type()){
case QEvent::TouchBegin:
mousePressEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::TouchUpdate:
mouseMoveEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::TouchEnd:
mouseReleaseEvent(static_cast<QMouseEvent*>(event));
break;
}
return QWidget::event(event);
}
void PaintWidget::resizeRect(QPoint p)
{
if(m_vecPointsList.size() == 0 && m_vecPoints.size()==0){
m_rect.setTopLeft(p);
m_rect.setBottomRight(p);
qInfo()<<__FUNCTION__<<"first point";
}
else{
int x = p.x();
int y = p.y();
if(x<m_rect.left()){
m_rect.setLeft(x);
}
else if(x>m_rect.right()){
m_rect.setRight(x);
}
if(y<m_rect.top()){
m_rect.setTop(y);
}
else if(y>m_rect.bottom()){
m_rect.setBottom(y);
}
}
}
void PaintWidget::adjustPoints()
{
int x = m_rect.left();
int y = m_rect.top();
for (unsigned int i = 0 ; i < m_vecPointsList.size() ; i++)
{
std::vector<QPoint> vecTmp = m_vecPointsList[i];
for (unsigned int j = 0 ; j < vecTmp.size() ; j++)
{
QPoint p = m_vecPointsList[i][j];
m_vecPointsList[i][j] = QPoint(p.x()-x, p.y()-y);
}
}
}