前言
近期在进行OpenCV的学习,在查询相关资料的过程中发现了一个很好的项目,是关于Qt集成OpenCV使用PaddleOCR做文本检测与识别的,然后也是下载了大佬的开源项目来进行学习,并结合大佬写的博客进行配置,在其项目基础上进行了一些修改,优化了相关代码,详细内容见下文,大家可以参考学习,如有错误之处,欢迎大家批评指正。
项目效果
提示:以下是本篇文章正文内容,下面案例可供参考
一、下载Paddle_inference Windows C++预测库
从官网下载新的预测库:下载安装Windows预测库
解压后内容如下:
二、下载Demo程序
从大佬的github主页下载程序:QT-OCR-Demo
我编译的是其中的paddleOCR项目,内容如下:
三、编译Demo程序
我目前使用的是OpenCV 4.5.2,预测库也与大佬使用的不一样,一开始按照其博客内容进行修改,始终无法成功编译运行,于是自己重新整理了一下依赖项,在源码路径新建了一个SDK文件夹包含了OpenCV和预测库文件以及程序运行时需要的dll,最后也是成功运行,该SDK文件夹可在下文百度网盘链接中获取。(OpenCV也是需要配置的,可以查看我之前写的这篇文章进行参考:Window下Qt5.14_MinGW_64bit+CMake_3.24+OpenCV_4.5.1环境搭建)
我的构建套件为MSVC_64bit Release,生成exe后需要在同级目录添加上述SDK的Dll文件夹内的dll,这样便运行成功了,修改程序的pro内容如下:
paddleOCRDemo.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
#定义编译选项
DEFINES += QT_DEPRECATED_WARNINGS
#生成程序名
TARGET = paddleOCRDemo
#指定生成的exe输出目录
DESTDIR = $$OUT_PWD
#设置字符
contains( CONFIG,"msvc" ):QMAKE_CXXFLAGS += /source-charset:utf-8 /execution-charset:utf-8
contains( CONFIG,"msvc" ):QMAKE_CFLAGS +=/source-charset:utf-8 /execution-charset:utf-8
#/MT:这个选项表示使用多线程静态链接库(Multithreaded)运行时库
CONFIG(debug, debug|release) {
QMAKE_CXXFLAGS_DEBUG += /MTd
}
CONFIG(release, debug|release) {
QMAKE_CXXFLAGS_RELEASE += /MT
}
#编译前执行复制操作
QMAKE_PRE_LINK += $(COPY_DIR) $$shell_path($$PWD/inference) $$shell_path($$OUT_PWD/inference) \
&& $(COPY) $$shell_path($$PWD/config.txt) $$shell_path($$OUT_PWD/config.txt) \
&& $(COPY) $$shell_path($$PWD/ppocr_keys_v1.txt) $$shell_path($$OUT_PWD/ppocr_keys_v1.txt)
#引入对应外部库的头文件及库文件
#OpenCV
INCLUDEPATH += $$PWD/SDK/OpenCV/Includes
DEPENDPATH += $$PWD/SDK/OpenCV/Includes
LIBS += -L$$PWD/SDK/OpenCV/Lib/ -lopencv_world452
#PaddleOCR
INCLUDEPATH += $$PWD/SDK/PaddleOCR/paddle/include
DEPENDPATH += $$PWD/SDK/PaddleOCR/paddle/include
LIBS += -L$$PWD/SDK/PaddleOCR/paddle/lib/ -lpaddle_inference
#PADDLE_PATH
PADDLE_PATH = $$PWD/SDK/PaddleOCR/third_party/install
INCLUDEPATH += $$PADDLE_PATH/cryptopp/include
INCLUDEPATH += $$PADDLE_PATH/gflags/include
INCLUDEPATH += $$PADDLE_PATH/glog/include
INCLUDEPATH += $$PADDLE_PATH/mkldnn/include
INCLUDEPATH += $$PADDLE_PATH/mklml/include
INCLUDEPATH += $$PADDLE_PATH/onnxruntime/include
INCLUDEPATH += $$PADDLE_PATH/paddle2onnx/include
INCLUDEPATH += $$PADDLE_PATH/protobuf/include
INCLUDEPATH += $$PADDLE_PATH/utf8proc/include
INCLUDEPATH += $$PADDLE_PATH/xxhash/include
LIBS += -L$$PADDLE_PATH/glog/lib -lglog
LIBS += -L$$PADDLE_PATH/mkldnn/lib -lmkldnn
LIBS += -L$$PADDLE_PATH/mklml/lib -lmklml -llibiomp5md
LIBS += -L$$PADDLE_PATH/onnxruntime/lib -lonnxruntime
LIBS += -L$$PADDLE_PATH/paddle2onnx/lib -lpaddle2onnx
LIBS += -L$$PADDLE_PATH/protobuf/lib -llibprotobuf
LIBS += -L$$PADDLE_PATH/xxhash/lib -lxxhash
SOURCES += \
main.cpp \
mainwindow.cpp \
paddle/clipper.cpp \
paddle/config.cpp \
paddle/ocr_cls.cpp \
paddle/ocr_det.cpp \
paddle/ocr_rec.cpp \
paddle/postprocess_op.cpp \
paddle/preprocess_op.cpp \
paddle/utility.cpp \
HEADERS += \
mainwindow.h \
paddle/clipper.h \
paddle/config.h \
paddle/ocr_cls.h \
paddle/ocr_det.h \
paddle/ocr_rec.h \
paddle/postprocess_op.h \
paddle/preprocess_op.h \
paddle/utility.h \
FORMS += \
mainwindow.ui
四、打印乱码问题
在运行示例时,发现控制台输出的文本为乱码,对此进行了修改,在h文件中添加相应的头文件,使用qDebug()代替原来的cout进行打印:
...
#include <QString>
#include <QDebug>
...
std::string tmp;
for (int i = 0; i < str_res.size(); i++)
{
//std::cout << str_res[i];
tmp.append(str_res[i]);
}
result.push_back(tmp);
//std::cout << "\tscore: " << score << std::endl;
qDebug()<<QString::fromStdString(tmp)<<" 得分:"<<score;
...
五、我的示例代码
我重新修改了主界面的布局,相应的代码如下:
1.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>
#include <string>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include "paddle/config.h"
#include "paddle/ocr_det.h"
#include "paddle/ocr_rec.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void initWidget();
QPixmap cvMatToPixmap(const cv::Mat imageMat);
private slots:
void on_pb_selectImage_clicked();
void on_pb_getText_clicked();
private:
Ui::MainWindow *ui;
cv::Mat m_showMat; //界面显示图像
PaddleOCR::DBDetector *m_det; //文本检测模块
PaddleOCR::CRNNRecognizer *m_rec; //文本识别模块
};
#endif // MAINWINDOW_H
2.mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->initWidget();
}
MainWindow::~MainWindow()
{
delete ui;
delete m_det;
delete m_rec;
}
//初始化界面
void MainWindow::initWidget()
{
m_showMat = cv::Mat();
this->setFixedSize(650,520);
ui->cb_showRect->setChecked(false);
//初始化PaddleOCR
PaddleOCR::OCRConfig config("./config.txt");
//文本检测对象
m_det = new PaddleOCR::DBDetector(config.det_model_dir, config.use_gpu, config.gpu_id,
config.gpu_mem, config.cpu_math_library_num_threads,
config.use_mkldnn, config.max_side_len, config.det_db_thresh,
config.det_db_box_thresh, config.det_db_unclip_ratio,
config.visualize, config.use_tensorrt, config.use_fp16);
//文本识别对象
m_rec = new PaddleOCR::CRNNRecognizer(config.rec_model_dir, config.use_gpu, config.gpu_id,
config.gpu_mem, config.cpu_math_library_num_threads,
config.use_mkldnn, config.char_list_file,
config.use_tensorrt, config.use_fp16);
}
//cv::Mat转QPixmap
QPixmap MainWindow::cvMatToPixmap(const cv::Mat imageMat)
{
QImage showImage;
if(imageMat.channels() > 1)
{
showImage = QImage((const uchar*)(imageMat.data),imageMat.cols,imageMat.rows,imageMat.step,QImage::Format_RGB888).rgbSwapped(); //彩色图
}
else
{
cv::Mat rgbMat;
cv::cvtColor(imageMat,rgbMat,cv::COLOR_GRAY2BGR);
showImage = QImage((const uchar*)(rgbMat.data),rgbMat.cols,rgbMat.rows,rgbMat.step,QImage::Format_RGB888).rgbSwapped(); //灰度图
}
return QPixmap::fromImage(showImage.scaled(ui->lb_show->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
}
//选择图像
void MainWindow::on_pb_selectImage_clicked()
{
QString imageName = QFileDialog::getOpenFileName(this,"选择图像","E:/photo/OCRTest/","Image File(*.png *.jpg *.bmp)");
if(!imageName.isEmpty())
{
m_showMat = cv::imread(imageName.toLocal8Bit().toStdString(),1);
QPixmap showPixmap = cvMatToPixmap(m_showMat.clone());
if(!showPixmap.isNull())
{
ui->tb_text->clear();
ui->lb_show->setPixmap(showPixmap);
}
}
}
//识别文本
void MainWindow::on_pb_getText_clicked()
{
if(m_showMat.empty())
{
QMessageBox::information(this,"提示","请先选择图像!");
return;
}
ui->tb_text->clear();
//返回检测框
cv::Mat srcMat = m_showMat.clone();
std::vector<std::vector<std::vector<int>>> boxes;
cv::Mat dstMat = m_det->Run(srcMat,boxes);
if(ui->cb_showRect->isChecked())
{
ui->lb_show->setPixmap(cvMatToPixmap(dstMat));
}
else
{
ui->lb_show->setPixmap(cvMatToPixmap(srcMat));
}
//获取文本
std::vector<std::string> res = m_rec->Run(boxes,srcMat,nullptr);
for(auto s:res)
{
ui->tb_text->append(s.data());
}
}
3.mainwindow.ui
六、下载链接
OCR依赖项百度网盘链接:https://pan.baidu.com/s/1mgmFrCHR4JJ7ZxyYrhRkrA?pwd=xxcj
提取码:xxcj
注意:需要将官网下载的预测库解压到/SDK/PaddleOCR内,这里占内存空间太大,就没放在网盘内。
总结
在我的示例中Qt集成OpenCV使用PaddleOCR进行文本识别,可以看到识别效果也是不错的,这里十分感谢大佬的开源奉献。下面这些关于OCR文本识别的文章,其内容也很详细,大家可以学习一下。
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。
参考文章:
QT使用PaddleOCR和百度OCR进行文字识别
使用qt+PaddleOCR做一个OCR软件demo
C++ | PaddleOCR+OpenCV实现文字识别步骤与代码演示