GLM-OCR与Qt框架集成开发跨平台桌面端OCR工具最近在做一个项目需要从各种图片里提取文字比如扫描的文档、网页截图甚至是手机拍的图片。手动输入肯定不现实用在线工具又担心数据安全和网络问题。于是我就琢磨着能不能自己搞一个本地的、能跨平台运行的桌面OCR工具试了几个方案最后决定用GLM-OCR作为识别引擎搭配Qt框架来构建图形界面。GLM-OCR的识别准确率不错而且支持中英文混合识别对复杂版面的适应性也挺好。Qt就更不用说了用它写的程序在Windows、macOS和Linux上都能直接运行界面还特别漂亮。今天这篇文章我就来分享一下整个开发过程。我会从零开始带你一步步搭建一个功能完整的桌面OCR应用。这个工具将支持拖拽图片识别、截图识别、批量处理还能让你编辑和导出识别结果。更重要的是我会重点讲解如何用多线程技术让界面始终保持流畅不会因为识别任务而卡死。最后我们还会一起看看怎么把这个应用打包分享给其他人使用。1. 为什么选择GLM-OCR和Qt在动手之前我们先聊聊为什么是这两个技术组合。这就像盖房子你得先选对地基和框架。GLM-OCR是一个基于深度学习的开源OCR引擎。它的优势在于模型文件相对小巧推理速度在CPU上也能接受最关键的是它对中文的识别效果尤其是对印刷体和手写体的混合场景表现相当出色。这意味着我们不需要依赖网络API所有识别过程都在本地完成数据安全有保障离线也能用。Qt呢是一个老牌的C跨平台应用程序开发框架。用它来开发桌面应用有几个好处第一真正的“一次编写到处编译”同一套代码稍作调整就能生成三个主流操作系统的可执行文件第二它提供了极其丰富的UI控件和强大的布局管理器做出来的界面既专业又美观第三Qt的信号与槽机制让处理用户交互和后台任务变得非常清晰和方便。把它们俩结合起来GLM-OCR负责核心的“脑力劳动”——识别文字Qt则负责打造一个好用、好看的“外壳”——用户界面。这个组合既能保证功能强大又能确保用户体验流畅。2. 开发环境搭建与项目初始化工欲善其事必先利其器。我们先来把开发环境准备好。2.1 安装必要的软件和库首先你需要安装以下基础软件C编译器Windows上推荐安装Visual Studio带MSVC或MinGWmacOS上安装Xcode Command Line ToolsLinux上安装g。CMake一个跨平台的构建工具版本建议3.10以上。这是编译GLM-OCR和Qt项目所必需的。Qt去Qt官网下载在线安装程序。安装时记得勾选你目标平台的编译套件比如Windows的MinGW或MSVCmacOS的Clang。对于这个项目我们主要需要Qt Core、Qt GUI、Qt Widgets这几个模块。接下来是GLM-OCR。我们需要从它的开源仓库获取代码并编译。打开终端或命令提示符执行以下步骤# 1. 克隆GLM-OCR的代码仓库 git clone https://github.com/THUDM/GLM-OCR.git cd GLM-OCR # 2. 创建并进入一个构建目录 mkdir build cd build # 3. 使用CMake配置项目。这里假设你已经安装了OpenCV等依赖。 # 你可以根据GLM-OCR的README文档安装所有必需的依赖。 cmake .. # 4. 编译 cmake --build . --config Release编译成功后你会在build目录下找到生成的库文件如.lib,.a或.so和头文件。记下它们的路径我们稍后需要用到。2.2 创建Qt项目打开Qt Creator新建一个项目选择“Qt Widgets Application”。给项目起个名字比如OcrDesktopTool。在项目创建向导中选择你安装的Qt版本和编译套件。创建完成后你会得到一个包含main.cpp、mainwindow.cpp、mainwindow.h和mainwindow.ui文件的基础项目。现在我们需要把GLM-OCR集成进来。在Qt Creator的项目文件.pro里添加库文件和头文件的路径。打开OcrDesktopTool.pro文件在末尾添加类似下面的内容# 假设你的GLM-OCR编译输出在 D:/dev/GLM-OCR/build INCLUDEPATH D:/dev/GLM-OCR/include LIBS -LD:/dev/GLM-OCR/build -lglm_ocr # 同时确保链接了OpenCV库GLM-OCR的依赖 LIBS -lopencv_core -lopencv_imgproc -lopencv_highgui请务必将路径替换成你自己电脑上的实际路径。Windows、macOS和Linux的路径写法略有不同需要注意。3. 设计应用界面与核心功能一个工具好不好用界面设计占了一半。我们的OCR工具需要几个核心功能区。3.1 主界面布局设计打开mainwindow.ui文件我们用Qt Designer来拖拽控件。我设计的界面主要分为四个区域顶部工具栏放置“打开图片”、“截图”、“批量识别”、“导出结果”等动作按钮。左侧图片预览区用一个QLabel来显示当前加载的图片支持拖拽放入。右侧文本编辑区用一个QTextEdit来显示和编辑识别出的文字。底部状态栏显示识别状态、进度等信息。为了实现拖拽功能我们需要重写主窗口的dragEnterEvent和dropEvent函数允许它接受图片文件的拖放。在mainwindow.h中声明这些函数然后在mainwindow.cpp中实现它们用于处理用户拖拽图片文件到窗口的操作。3.2 实现图片加载与显示当用户通过按钮或拖拽选择图片后我们需要用OpenCV读取图片然后转换成Qt的QImage格式最后在QLabel中显示出来。这里要注意图片可能很大需要做适当的缩放以适应预览区。// mainwindow.cpp 中的示例代码片段 void MainWindow::loadImage(const QString filePath) { // 使用OpenCV读取图片 cv::Mat cvImage cv::imread(filePath.toStdString()); if(cvImage.empty()) { QMessageBox::warning(this, 错误, 无法加载图片); return; } // 将BGR格式的OpenCV Mat转换为RGB格式的QImage cv::cvtColor(cvImage, cvImage, cv::COLOR_BGR2RGB); QImage qtImage((uchar*)cvImage.data, cvImage.cols, cvImage.rows, cvImage.step, QImage::Format_RGB888); // 缩放图片以适应预览区保持宽高比 QPixmap scaledPixmap QPixmap::fromImage(qtImage).scaled(ui-imageLabel-size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); ui-imageLabel-setPixmap(scaledPixmap); ui-imageLabel-setAlignment(Qt::AlignCenter); // 保存当前图片路径供OCR识别使用 m_currentImagePath filePath; }3.3 集成GLM-OCR识别引擎这是最核心的一步。我们需要创建一个OCR处理类例如OcrProcessor在这个类里初始化GLM-OCR引擎并提供一个接口函数来识别图片。在OcrProcessor的头文件中我们引入GLM-OCR的头文件并声明一个识别函数// ocrprocessor.h #include string #include vector class OcrProcessor { public: OcrProcessor(); ~OcrProcessor(); bool initialize(const std::string modelPath); // 初始化模型 std::string recognizeImage(const std::string imagePath); // 识别单张图片 private: // 这里存放GLM-OCR引擎的句柄或指针 void* m_engine; };在ocrprocessor.cpp中我们实现初始化和识别逻辑。具体如何调用GLM-OCR的API需要参考其官方文档。通常流程是加载模型 - 读取图片 - 预处理 - 推理 - 后处理得到文本。// ocrprocessor.cpp 中的简化示例 std::string OcrProcessor::recognizeImage(const std::string imagePath) { if (!m_engine) { return OCR引擎未初始化; } // 1. 使用OpenCV读取图片 cv::Mat image cv::imread(imagePath); if (image.empty()) { return 无法读取图片: imagePath; } // 2. 调用GLM-OCR的识别函数此处为伪代码具体函数名参考官方文档 // std::vectorstd::string results glm_ocr_recognize(m_engine, image); // 3. 将识别出的多行文本合并成一个字符串根据实际情况调整 std::string fullText; // for (const auto line : results) { fullText line \n; } // 假设我们通过某个函数得到了识别文本 fullText 这是识别出的示例文本。\n这是第二行。; return fullText; }4. 实现多线程与保持界面流畅如果你直接在界面线程主线程中调用耗时的OCR识别函数界面就会卡住直到识别完成。这对于用户体验是灾难性的。解决方案是使用多线程。Qt提供了QThread类来方便地创建线程。我们将耗时的OCR识别任务放到一个工作线程中执行。4.1 创建OCR工作线程我们创建一个继承自QThread的类OcrWorkerThread。// ocrworkerthread.h #include QThread #include QString class OcrWorkerThread : public QThread { Q_OBJECT // 必须添加才能使用信号与槽 public: explicit OcrWorkerThread(QObject *parent nullptr); void setImagePath(const QString path); signals: void recognitionFinished(const QString text); // 识别完成信号 void recognitionError(const QString error); // 识别错误信号 protected: void run() override; // 线程入口函数 private: QString m_imagePath; OcrProcessor m_processor; };在线程的run()函数中执行识别任务// ocrworkerthread.cpp void OcrWorkerThread::run() { if (!m_processor.initialize(path/to/your/model)) { emit recognitionError(OCR引擎初始化失败); return; } std::string result m_processor.recognizeImage(m_imagePath.toStdString()); emit recognitionFinished(QString::fromStdString(result)); }4.2 在主界面中连接信号与槽在主窗口类中我们创建OcrWorkerThread的实例并将它的完成信号连接到主窗口的槽函数用于更新UI。// 在MainWindow的构造函数或某个初始化函数中 m_ocrThread new OcrWorkerThread(this); connect(m_ocrThread, OcrWorkerThread::recognitionFinished, this, MainWindow::onRecognitionFinished); connect(m_ocrThread, OcrWorkerThread::recognitionError, this, MainWindow::onRecognitionError); connect(m_ocrThread, QThread::finished, m_ocrThread, QObject::deleteLater); // 线程结束后自动清理 // 当用户点击识别按钮时 void MainWindow::onRecognizeButtonClicked() { if (m_currentImagePath.isEmpty()) return; ui-textEdit-clear(); ui-statusBar-showMessage(正在识别...); ui-recognizeButton-setEnabled(false); // 禁用按钮防止重复点击 m_ocrThread-setImagePath(m_currentImagePath); m_ocrThread-start(); // 启动线程 } // 识别完成后的槽函数 void MainWindow::onRecognitionFinished(const QString text) { ui-textEdit-setPlainText(text); ui-statusBar-showMessage(识别完成, 3000); // 显示3秒 ui-recognizeButton-setEnabled(true); // 重新启用按钮 m_ocrThread-quit(); // 请求线程退出 } // 识别错误的槽函数 void MainWindow::onRecognitionError(const QString error) { QMessageBox::critical(this, 识别错误, error); ui-statusBar-showMessage(识别失败); ui-recognizeButton-setEnabled(true); m_ocrThread-quit(); }通过这种方式识别任务在后台运行界面完全不会卡顿用户可以随时进行其他操作。状态栏的提示和按钮状态的改变也让用户清楚地知道当前应用在做什么。5. 扩展功能截图、批量处理与导出有了核心的识别功能我们可以再添加一些提升效率的实用功能。5.1 实现屏幕截图识别利用Qt的QScreen和QPixmap可以很方便地实现截图功能。思路是隐藏主窗口 - 全屏截图 - 显示一个半透明的选区窗口让用户框选区域 - 获取选区图片 - 进行识别。void MainWindow::onScreenshotButtonClicked() { this-hide(); // 隐藏主窗口 QTimer::singleShot(300, this, MainWindow::captureScreen); // 稍等片刻让窗口完全隐藏 } void MainWindow::captureScreen() { // 创建一个全屏的、透明的选区窗口 ScreenshotSelector *selector new ScreenshotSelector(); connect(selector, ScreenshotSelector::regionCaptured, this, MainWindow::onRegionCaptured); selector-showFullScreen(); } void MainWindow::onRegionCaptured(const QPixmap capturedPixmap) { this-show(); // 重新显示主窗口 // 将截图保存为临时文件或直接转换为OpenCV Mat然后调用识别 // ... }ScreenshotSelector是一个自定义的窗口类它负责绘制一个半透明的覆盖层并处理鼠标事件来框选区域。实现起来稍复杂但原理是清晰的。5.2 实现批量图片识别批量识别意味着要处理一个文件夹下的多张图片。我们可以使用QFileDialog让用户选择文件夹然后用QDir遍历文件夹内的图片文件。关键点在于如何管理多个识别任务。我们可以创建一个任务队列然后让工作线程逐个处理。或者更简单一点使用Qt Concurrent框架来并行处理如果CPU核心足够多。void MainWindow::onBatchRecognizeButtonClicked() { QString dirPath QFileDialog::getExistingDirectory(this, 选择包含图片的文件夹); if (dirPath.isEmpty()) return; QDir dir(dirPath); QStringList imageFilters {*.png, *.jpg, *.jpeg, *.bmp}; QStringList imageFiles dir.entryList(imageFilters, QDir::Files); // 创建一个进度对话框 QProgressDialog progress(正在批量识别..., 取消, 0, imageFiles.count(), this); progress.setWindowModality(Qt::WindowModal); for (int i 0; i imageFiles.count(); i) { progress.setValue(i); if (progress.wasCanceled()) break; QString filePath dir.absoluteFilePath(imageFiles[i]); // 这里可以同步调用OCR识别或者将任务提交到线程池 // std::string text m_processor.recognizeImage(filePath.toStdString()); // 将结果保存到文件或数据库中... // 为了不卡界面每次循环处理一下事件 QCoreApplication::processEvents(); } progress.setValue(imageFiles.count()); }5.3 识别结果的编辑与导出识别结果不可能100%准确所以提供一个编辑功能是必要的。我们已经在主界面放置了QTextEdit控件用户可以直接在里面修改文字。导出功能也很简单。我们可以提供几种导出方式复制到剪贴板QApplication::clipboard()-setText(ui-textEdit-toPlainText());保存为文本文件使用QFileDialog::getSaveFileName获取保存路径然后用QFile写入。保存为带格式的文件比如Word或PDF。这需要借助第三方库如用于生成PDF的QPdfWriter或者通过调用系统办公软件的命令行接口来实现。6. 跨平台打包与发布应用开发完了最后一步是把它打包成用户可以独立安装和运行的程序。6.1 使用windeployqtWindows对于WindowsQt提供了windeployqt工具它能自动将程序运行所需的Qt库文件复制到可执行文件旁边。在Qt Creator中以Release模式编译你的项目。在构建目录的release文件夹中找到生成的.exe文件例如OcrDesktopTool.exe。打开Qt自带的命令行工具如Qt 5.15.2 (MinGW 7.3.0 64-bit)。切换到exe文件所在目录执行windeployqt OcrDesktopTool.exe工具会自动将必要的DLL和资源文件拷贝过来。最后你需要手动将GLM-OCR的DLL文件如glm_ocr.dll和OpenCV的DLL文件也复制到这个目录。现在这个文件夹里的所有文件一起就可以分发给别人了。你可以用Inno Setup或NSIS等工具将它们打包成一个安装程序。6.2 使用macdeployqtmacOSmacOS的打包过程类似使用macdeployqt工具。在Qt Creator中以Release模式编译。在build目录中找到生成的.appbundle例如OcrDesktopTool.app。在终端中切换到.app所在目录的上一级执行macdeployqt OcrDesktopTool.app这个命令会将Qt库打包进.app内部并修复依赖关系。同样你需要将GLM-OCR的动态库.dylib文件手动复制到.app/Contents/Frameworks/目录下。最终生成的.app就是一个可以双击运行的独立应用了。6.3 Linux打包Linux的打包相对灵活常见的方式是提供AppImage或Flatpak包这样能更好地解决依赖问题。最简单的方式是提供一个包含所有依赖的压缩包。编译Release版本。创建一个目录比如OcrDesktopTool-linux。将可执行文件、GLM-OCR的.so库文件、以及必要的Qt库可以用ldd命令查看依赖都复制到这个目录。写一个简单的启动脚本run.sh设置好库路径LD_LIBRARY_PATH。将这个目录压缩成tar.gz包分发。对于更专业的分发可以研究一下linuxdeployqt工具或AppImage的制作方法。7. 总结与后续优化方向走完这一整套流程一个功能完备的跨平台桌面OCR工具就诞生了。从拖拽图片、截图到批量处理从本地识别到多线程流畅体验再到最后的打包发布我们覆盖了桌面应用开发的几个关键环节。用下来感觉GLM-OCR和Qt的搭配确实很高效。GLM-OCR提供了可靠的识别能力而Qt则让构建一个复杂且美观的界面变得不那么困难。多线程的设计是保证体验的关键它让耗时操作在后台默默进行用户完全感知不到卡顿。当然这个工具还有很多可以打磨的地方。比如可以增加对更多图片格式的支持优化识别结果的排版保持原文的段落、列表格式甚至集成翻译功能。在性能上如果识别大量图片可以考虑引入线程池来管理并发任务。界面方面可以增加识别历史记录、自定义快捷键等功能。如果你对C和Qt开发感兴趣这个项目是一个很好的练手机会。它不仅涉及UI设计、文件操作、多线程还涉及到与本地AI模型的集成知识面覆盖很广。你可以基于这个基础框架添加任何你想要的特色功能。希望这篇文章能为你打开一扇门让你也能打造出属于自己的智能桌面应用。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。