使用zinnia庫進行中文手寫識別

        研究手寫識別的時候看到了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);
        }
    }
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章