QT案例实战1 - 从零开始编写一个OCR工具软件 (6) 关于QThread线程的使用

一、为什么用线程

        在这里将ocr识别的这个耗时的操作放在新线程内,这样是为了主线程/UI界面不会卡在那里没有响应。如果说不在乎用户体验,ocr识别也可以放在主线程,卡住就卡住,就是会被骂。

        不过关于线程的使用有的时候也要看平台框架的要求,比如android,网络请求就要在新线程内,否则就会报错。

        QT内的线程类叫做QThread,看看qthread.h内的部分方法,熟悉Java线程的大概在就会知道用法了(因为挺像的),我们这里采用创建一个新的继承自QThread类的方法使用线程。

QThread 的头文件的部分截图

 二、创建继承自Thread的类

1、创建头文件

        起了个名字,有点长,MyThreadForTextRecognition.h。

        其中public方法内主要是自定义的init方法,用来传递给线程一些ocr识别的参数,比如语言、用哪个引擎等。

        其中private是一些线程内的参数,以及对应两个ocr引擎的识别方法。

        其中protected就是我们要覆写的run方法。

        其中signals就是我们用来和主线程通信的方法。关于信号和槽可以查看官网说明。其中getRecognitionText方法是用来将识别结果传递给主线程以显示在界面上。其中recognitionFinish方法是告诉主线程识别完了,可以关闭loading了。

#ifndef MYTHREADFORTEXTRECOGNITION_H
#define MYTHREADFORTEXTRECOGNITION_H

// windows sdk api
#define _SILENCE_CLANG_COROUTINE_MESSAGE
#include "winrt/Windows.Globalization.h"
#include "winrt/windows.media.ocr.h"
#include "winrt/windows.graphics.imaging.h"
#include "winrt/Windows.Storage.h"
#include "winrt/Windows.Storage.Streams.h"
#include "winrt/Windows.Foundation.Collections.h"

// QT
#include <QObject>
#include <QThread>
#include "qdebug.h"

// 自定义
#include "mytools.h"

// tesseract
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>


class MyThreadForTextRecognition : public QThread
{
    Q_OBJECT
public:
    MyThreadForTextRecognition();
    void init(std::string filepath, std::string newFilepath, int language_index, bool preprocessing, int ocrengine);

private:
    char *outText;
    std::string _filepath;
    int _language_index;
    std::string _newFilepath;
    bool _preprocessing = false;
    std::string _ocrengine;
    void _cognizeByWinOCR();
    void _cognizeByTesseract();

protected:
    void run() override;

signals:
    void minNumToamin(int min);
    void getRecognitionText(std::string outText);
    void recognitionFinish();

};

#endif // MYTHREADFORTEXTRECOGNITION_H

2、创建cpp文件

        参考代码如下

#include <MyThreadForTextRecognition.h>
#include <string>
#include <iostream>
#include <locale>
#include <codecvt>

//#pragma execution_character_set("utf-8");

//comment(lib, "windowsapp")

using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Streams;
using namespace winrt::Windows::Globalization;
using namespace winrt::Windows::Media::Ocr;
using namespace winrt::Windows::Graphics::Imaging;

MyThreadForTextRecognition::MyThreadForTextRecognition()
{

}

/**
 * @brief MyThreadForTextRecognition::init 接收一些参数
 * @param filepath
 * @param newFilepath
 * @param language
 * @param preprocessing
 */
void MyThreadForTextRecognition::init(std::string filepath, std::string newFilepath, int language_index, bool preprocessing, int ocrengine)
{
    _filepath = filepath;
    _language_index = language_index;
    _newFilepath = newFilepath;
    _preprocessing = preprocessing;
    _ocrengine = (ocrengine==0) ? "micocr" : "tesseract";
}

/**
 * @brief MyThreadForTextRecognition::_cognizeByWinOCR
 * 基于windows的ocr
 */
void MyThreadForTextRecognition::_cognizeByWinOCR()
{
    //获取图片路径
    std::wstring uriImage = MyTools::ConvertUtf8ToWide(_newFilepath);
    //获取SoftwareBitmap
    RandomAccessStreamReference streamRef = RandomAccessStreamReference::CreateFromFile(StorageFile::GetFileFromPathAsync(uriImage).get());
    IAsyncOperation<IRandomAccessStreamWithContentType> stream = streamRef.OpenReadAsync();
    Buffer buffer = Buffer((uint)stream.get().Size());
    auto decoder = BitmapDecoder::CreateAsync(stream.get().CloneStream());
    IAsyncOperation<SoftwareBitmap> softwareBitmap = decoder.get().GetSoftwareBitmapAsync();

    //进行识别
    Language language(winrt::to_hstring(MyTools::get_Choise_Language(_language_index, _ocrengine)));
    //如果不支持的语言
    if (OcrEngine::IsLanguageSupported(language)) {
        OcrEngine engine = OcrEngine::TryCreateFromLanguage(language);
        Collections::IVectorView<OcrLine> lines = engine.RecognizeAsync(softwareBitmap.get()).get().Lines();
        if(lines.Size()>0)
        {
            for (auto p : lines)
            {
                std::string text = "";
                for (OcrWord word : p.Words()) {
                    text += to_string(word.Text()) + ((_language_index>0)?" ":"");
                }

                emit getRecognitionText(text/*const_cast<char *>(text.c_str())*/);
                qDebug() << "识别结果:" << text.c_str();
            }
        }
        else
        {
            emit getRecognitionText("");
        }
    }
    else
    {
        qDebug() << "不支持的语言:" << QString::fromStdString(MyTools::get_Choise_Language(_language_index, _ocrengine));
        emit getRecognitionText("");
    }
}

/**
 * @brief MyThreadForTextRecognition::_cognizeByTesseract
 * 基于Tesseract的ocr
 */
void MyThreadForTextRecognition::_cognizeByTesseract()
{
    // 初始化api
    tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();

    // 初始化ocr识别,使用中文并且指定tessdata路径
    if (api->Init("lib/tesseract/tessdata/", MyTools::get_Choise_Language(_language_index, _ocrengine).c_str(), tesseract::OcrEngineMode::OEM_LSTM_ONLY)) {
        emit getRecognitionText("");
    }
    else
    {
        // 使用leptonica库打开图像
        Pix *image = pixRead(_newFilepath.c_str());
        api->SetImage(image);

        // 【最简单的api】
        //outText = api->GetUTF8Text();
        //qDebug()<<"outText:"<<outText;
        //getRecognitionText(outText);

        // 【获取组件图像示例】
        Boxa* boxes = api->GetComponentImages(tesseract::RIL_PARA, true, NULL, NULL);
        for (int i = 0; i < boxes->n; i++) {
            BOX* box = boxaGetBox(boxes, i, L_CLONE);
            api->SetRectangle(box->x, box->y, box->w, box->h);
            char* ocrResult = api->GetUTF8Text();
            int meanConf = api->MeanTextConf();
            int *confs = api->AllWordConfidences();

            qDebug()<<"meanConf:"<<meanConf;
            emit getRecognitionText(ocrResult);
            //fprintf(stdout, "Box[%d]: x=%d, y=%d, w=%d, h=%d, confidence: %d, text: %s", i, box->x, box->y, box->w, box->h, conf, ocrResult);
            boxDestroy(&box);
        }

        emit recognitionFinish();

        // 销毁使用过的对象并释放内存
        api->End();
        delete api;
        //delete[] outText;
        pixDestroy(&image);
    }
}

/**
 * @brief MyThreadForTextRecognition::run 运行线程
 */
void MyThreadForTextRecognition::run()
{
    try
    {
        //图像预处理,可以没有
        if(_preprocessing)
        {
            MyTools::BinarythresoldByOpenCV(_filepath.c_str(), _newFilepath.c_str());
        }
        else
        {
            _newFilepath = _filepath;
        }

        if(_ocrengine == "micocr")
        {
            _cognizeByWinOCR();
        }
        else if(_ocrengine == "tesseract")
        {
            _cognizeByTesseract();
        }
    }
    catch(_exception e)
    {
        emit getRecognitionText("");
        qDebug()<<"ocr识别异常:"<<0;
    }
    emit recognitionFinish();
}

三、使用线程

        首先实例化线程类,然后调用init传递参数,然后绑定两个信号/槽,最后调用start方法启动线程。

//启动线程
m_thread  =  new MyThreadForTextRecognition;
m_thread->init(filePath.toStdString(), QString("%1\\screen_action.jpg").arg(qApp->applicationDirPath().replace("/", "\\")).toStdString(),
                   ui->comboBox->currentIndex(), ui->checkBox_3->isChecked(), ui->comboBox_2->currentIndex());
connect(m_thread, &MyThreadForTextRecognition::getRecognitionText,this,&TextRecognition::getRecognitionText);
connect(m_thread, &MyThreadForTextRecognition::recognitionFinish,this,&TextRecognition::recognitionFinish);
m_thread->start();

猜你喜欢

转载自blog.csdn.net/bashendixie5/article/details/127131507