// 在调试OCR时,崩溃了这里:error_code ORT_INVALID_ARGUMENT (2) OrtErrorCode
D:\wzl\project\wzl_qt_work\third_party\onnxruntime-win-x64-1.22.0\include\onnxruntime_cxx_inline.h
auto outs = cls_session_->Run(Ort::RunOptions{ nullptr }, &inName, &tensor, 1, &outName, 1);
namespace detail {
inline void ThrowStatus(const Status& st) {
std::string error_message = st.GetErrorMessage();
OrtErrorCode error_code = st.GetErrorCode();
ORT_CXX_API_THROW(std::move(error_message), error_code);
}
} // namespace detail
堆栈:
3 Ort::detail::ThrowStatus onnxruntime_cxx_inline.h 39 0x7ff7d0958e31
4 Ort::ThrowOnError onnxruntime_cxx_inline.h 46 0x7ff7d0958d72
5 Ort::detail::SessionImpl<OrtSession>::Run onnxruntime_cxx_inline.h 1433 0x7ff7d0958c37
6 Ort::detail::SessionImpl<OrtSession>::Run onnxruntime_cxx_inline.h 1424 0x7ff7d0958b05
7 OcrService::needRotate OcrService.cpp 219 0x7ff7d0946e7b
8 OcrService::performOcr OcrService.cpp 58 0x7ff7d0944f34
9 Test::Test Test.cpp 45 0x7ff7d093fef4
10 main main.cpp 101 0x7ff7d093278f
11 qtEntryPoint qtentrypoint_win.cpp 45 0x7ff7d097d49a
12 WinMain qtentrypoint_win.cpp 64 0x7ff7d097d34e
控制台打印信息:
det output shape: 1 1 640 640
probMap min/max: 0 1
contours found: 39
// D:\wzl\project\wzl_qt_work\src\cpp\ocr\OcrService.cpp
#include "OcrService.h"
#include <stdexcept>
#include <filesystem>
OcrService::OcrService(QString& model_dir_str)
: env_(ORT_LOGGING_LEVEL_WARNING, "OcrService") {
try {
session_options_.SetIntraOpNumThreads(1);
session_options_.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
const std::filesystem::path model_dir(model_dir_str.toStdWString());
const auto det_path = model_dir / "det/det.onnx";
const auto cls_path = model_dir / "cls/cls.onnx";
const auto rec_path = model_dir / "rec/rec.onnx";
// 使用智能指针管理 Session 生命周期
det_session_ = std::make_unique<Ort::Session>(env_, det_path.c_str(), session_options_);
cls_session_ = std::make_unique<Ort::Session>(env_, cls_path.c_str(), session_options_);
rec_session_ = std::make_unique<Ort::Session>(env_, rec_path.c_str(), session_options_);
is_initialized_ = true;
} catch (const Ort::Exception& e) {
// 加载失败时抛出异常,方便上层捕获和处理
throw std::runtime_error("Failed to initialize OCR service: " + std::string(e.what()));
}
}
// ===============================================
// 外部统一入口
// ===============================================
QVector<QString> OcrService::performOcr(const cv::Mat& image) {
QVector<QString> results;
if (!is_initialized_ || image.empty()) return results;
// 1. 检测文本框
QList<TextBox> boxes = detectTextBoxes(image);
// 2. 逐框识别
for (const TextBox& box : boxes) {
// 裁剪并透视矫正
cv::Mat M, warped;
std::vector<cv::Point2f> dstPts = {
{ 0.f, 0.f },
{ float(box.pts[1].x() - box.pts[0].x()), 0.f },
{ float(box.pts[1].x() - box.pts[0].x()), float(box.pts[2].y() - box.pts[1].y()) },
{ 0.f, float(box.pts[2].y() - box.pts[1].y()) }
};
cv::warpPerspective(image, warped,
cv::getPerspectiveTransform(reinterpret_cast<const cv::Point2f*>(box.pts.data()),
reinterpret_cast<const cv::Point2f*>(dstPts.data())),
cv::Size(int(dstPts[1].x), int(dstPts[2].y)),
cv::INTER_LINEAR, cv::BORDER_REPLICATE);
// 3. 方向判断
if (needRotate(warped)) cv::rotate(warped, warped, cv::ROTATE_180);
// 4. 识别
results.append(recognizeText(warped));
}
return results;
}
// ===============================================
// 1. DBNet 后处理
// ===============================================
static QVector<std::array<int64_t, 4>> makeShape4(int n, int c, int h, int w) {
return { { static_cast<int64_t>(n), static_cast<int64_t>(c),
static_cast<int64_t>(h), static_cast<int64_t>(w) } };
}
QList<TextBox> OcrService::detectTextBoxes(const cv::Mat& image,
float det_thresh /* =0.1 */,
float box_thresh /* =0.3 */,
float unclip_ratio /* =2.0 */) {
QList<TextBox> finalBoxes;
// ---------- 1. 前处理:resize(640x640) + RGB + float32 ----------
const int detH = 640, detW = 640;
cv::Mat rgb, resized;
cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB);
cv::resize(rgb, resized, cv::Size(detW, detH));
resized.convertTo(resized, CV_32FC3, 1.0 / 255.0f); // 0‑1 归一化
// ---------- (新) 按 mean/std 归一化 + HWC→CHW ----------
static const float mean_vals[3] = { 0.485f, 0.456f, 0.406f };
static const float std_vals[3] = { 0.229f, 0.224f, 0.225f };
QVector<float> inputData(3 * detH * detW);
for (int y = 0; y < detH; ++y)
for (int x = 0; x < detW; ++x) {
cv::Vec3f pix = resized.at<cv::Vec3f>(y, x); // RGB
int idx = y * detW + x;
inputData[0 * detH * detW + idx] = (pix[0] - mean_vals[0]) / std_vals[0]; // R
inputData[1 * detH * detW + idx] = (pix[1] - mean_vals[1]) / std_vals[1]; // G
inputData[2 * detH * detW + idx] = (pix[2] - mean_vals[2]) / std_vals[2]; // B
}
// ---------- 2. 构建 ONNX Runtime 输入 ----------
Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
auto inputTensor = Ort::Value::CreateTensor<float>(
memInfo, inputData.data(), inputData.size(),
makeShape4(1, 3, detH, detW)[0].data(), 4);
// ---------- 3. 推理 ----------
Ort::AllocatorWithDefaultOptions allocator;
const char* inName = det_session_->GetInputNameAllocated(0, allocator).get();
const char* outName = det_session_->GetOutputNameAllocated(0, allocator).get();
auto outs = det_session_->Run(Ort::RunOptions{ nullptr },
&inName, &inputTensor, 1,
&outName, 1);
// ---------- 4. 输出处理:阈值化→膨胀→找轮廓 ----------
Ort::Value& pred = outs[0];
auto shape = pred.GetTensorTypeAndShapeInfo().GetShape(); // 期望 [1,1,H,W]
qDebug() << "det output shape:" << shape[0] << shape[1] << shape[2] << shape[3];
const int outH = static_cast<int>(shape[2]);
const int outW = static_cast<int>(shape[3]);
QVector<float> prob(outH * outW);
std::memcpy(prob.data(),
pred.GetTensorMutableData<float>(),
prob.size() * sizeof(float));
cv::Mat probMap(outH, outW, CV_32F, prob.data());
#if 1
// 调试代码,如果发现 cv::threshold() 前 probMap 的最大值 maxVal = 2.97e-05,说明 模型输出几乎是全零,
// 所以经过 cv::threshold(probMap, binary, det_thresh, 255, ...) 后 binary 几乎全是黑图(值为0),
// cv::findContours() 找不到任何轮廓,contours 自然为空。
// det 模型输出的 probMap 全接近 0,这通常是 预处理不对或模型有问题。
double minVal, maxVal;
cv::minMaxLoc(probMap, &minVal, &maxVal);
qDebug() << "probMap min/max:" << minVal << maxVal;
#endif
cv::Mat binary;
cv::threshold(probMap, binary, det_thresh, 255, cv::THRESH_BINARY);
binary.convertTo(binary, CV_8U);
cv::Mat dilated;
cv::dilate(binary, dilated,
cv::getStructuringElement(cv::MORPH_RECT, { 3, 3 }), // 小核膨胀一次
cv::Point(-1, -1), 1);
std::vector<std::vector<cv::Point>> contours;
cv::findContours(dilated, contours,
cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
qDebug() << "contours found:" << contours.size();
// ---------- 5. 轮廓转为 TextBox ----------
for (const auto& contour : contours) {
if (contour.size() < 4) continue;
cv::Mat mask = cv::Mat::zeros(probMap.size(), CV_8U);
cv::drawContours(mask, std::vector<std::vector<cv::Point>>{ contour }, 0, cv::Scalar(255), -1);
float score = static_cast<float>(cv::mean(probMap, mask)[0]);
if (score < box_thresh) continue;
cv::RotatedRect rect = cv::minAreaRect(contour);
// 「unclip」扩张
float area = rect.size.area();
float perimeter = 2.f * (rect.size.width + rect.size.height);
float distance = area * unclip_ratio / std::max(perimeter, 1.f);
cv::Mat offset;
cv::Mat(contour).convertTo(offset, CV_32F);
std::vector<cv::Point2f> unclipPts;
cv::convexHull(offset + (offset * (distance / cv::norm(offset))), unclipPts);
cv::RotatedRect unclipRect = cv::minAreaRect(unclipPts);
// 坐标缩放回原图
float scaleX = static_cast<float>(image.cols) / detW;
float scaleY = static_cast<float>(image.rows) / detH;
cv::Point2f pts[4];
unclipRect.points(pts);
TextBox box;
for (int i = 0; i < 4; ++i)
box.pts.append(QPointF(pts[i].x * scaleX,
pts[i].y * scaleY));
box.score = score;
finalBoxes.append(std::move(box));
}
// ---------- 6. 从上到下排序 ----------
std::sort(finalBoxes.begin(), finalBoxes.end(),
[](const TextBox& a, const TextBox& b) { return a.pts[0].y() < b.pts[0].y(); });
return finalBoxes;
}
// ===============================================
// 2. 文字方向分类
// ===============================================
bool OcrService::needRotate(const cv::Mat& roi) {
const int clsH = 48, clsW = 192;
cv::Mat rgb, resized;
cv::cvtColor(roi, rgb, cv::COLOR_BGR2RGB);
cv::resize(rgb, resized, cv::Size(clsW, clsH));
resized.convertTo(resized, CV_32FC3, 1.0 / 255);
QVector<float> data(clsH * clsW * 3);
std::memcpy(data.data(), resized.data, data.size() * sizeof(float));
Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
auto tensor = Ort::Value::CreateTensor<float>(memInfo, data.data(), data.size(),
makeShape4(1, 3, clsH, clsW)[0].data(), 4);
const char* inName = cls_session_->GetInputNameAllocated(0, Ort::AllocatorWithDefaultOptions()).get();
const char* outName = cls_session_->GetOutputNameAllocated(0, Ort::AllocatorWithDefaultOptions()).get();
auto outs = cls_session_->Run(Ort::RunOptions{ nullptr }, &inName, &tensor, 1, &outName, 1);
float* outPtr = outs[0].GetTensorMutableData<float>();
// PP-OCR v5 CLS 输出 shape=[1,2],索引 1 代表 180°
return outPtr[1] > outPtr[0];
}
// ===============================================
// 3. 识别模块
// ===============================================
QString OcrService::recognizeText(const cv::Mat& roi) {
// -- 0. 载入一次字典 --
if (dict_.isEmpty()) {
QFile f(":/models/ocr/onnx_ocr/ppocrv5/ppocrv5_dict.txt");
if (f.open(QFile::ReadOnly | QFile::Text))
while (!f.atEnd()) dict_.append(QString::fromUtf8(f.readLine().trimmed()));
}
const int recH = 48, recW = 320;
cv::Mat rgb, resized;
cv::cvtColor(roi, rgb, cv::COLOR_BGR2RGB);
cv::resize(rgb, resized, cv::Size(recW, recH));
resized.convertTo(resized, CV_32FC3, 1.0 / 255);
QVector<float> data(recH * recW * 3);
std::memcpy(data.data(), resized.data, data.size() * sizeof(float));
Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
auto tensor = Ort::Value::CreateTensor<float>(memInfo, data.data(), data.size(),
makeShape4(1, 3, recH, recW)[0].data(), 4);
const char* inName = rec_session_->GetInputNameAllocated(0, Ort::AllocatorWithDefaultOptions()).get();
const char* outName = rec_session_->GetOutputNameAllocated(0, Ort::AllocatorWithDefaultOptions()).get();
auto outs = rec_session_->Run(Ort::RunOptions{ nullptr }, &inName, &tensor, 1, &outName, 1);
// -- 后处理 CTC 解码 (argmax) --
Ort::Value& output = outs[0];
auto shape = output.GetTensorTypeAndShapeInfo().GetShape(); // [1, T, C]
int T = int(shape[1]), C = int(shape[2]);
float* ptr = output.GetTensorMutableData<float>();
QString result;
int lastIdx = -1;
for (int t = 0; t < T; ++t) {
int maxIdx = 0;
float maxVal = ptr[t * C];
for (int c = 1; c < C; ++c)
if (ptr[t * C + c] > maxVal) {
maxVal = ptr[t * C + c];
maxIdx = c;
}
// CTC:去重 / 去 blank(0)
if (maxIdx != lastIdx && maxIdx > 0 && maxIdx < dict_.size())
result.append(dict_[maxIdx]);
lastIdx = maxIdx;
}
return result;
}
// D:\wzl\project\wzl_qt_work\src\cpp\ocr\OcrService.h
#pragma once
#include <memory>
#include <opencv2/opencv.hpp>
#include <onnxruntime_cxx_api.h>
#include <QString>
#include <QVector>
// 文本框结构体,四点顺时针
struct TextBox {
QVector<QPointF> pts;
float score = 0.0f;
};
class OcrService {
public:
explicit OcrService(QString& model_dir);
QVector<QString> performOcr(const cv::Mat& image);
private:
// ==== 模块化接口 ====
QList<TextBox> detectTextBoxes(const cv::Mat& image, float det_thresh = 0.3f,
float box_thresh = 0.6f, float unclip_ratio = 2.0f);
bool needRotate(const cv::Mat& roi);
QString recognizeText(const cv::Mat& roi);
// ==== ONNX Runtime ====
Ort::Env env_;
Ort::SessionOptions session_options_;
std::unique_ptr<Ort::Session> det_session_;
std::unique_ptr<Ort::Session> cls_session_;
std::unique_ptr<Ort::Session> rec_session_;
bool is_initialized_ = false;
// ==== 字典缓存 ====
QStringList dict_;
};
// D:\wzl\project\wzl_qt_work\src\cpp\test\Test.cpp
#include "Test.h"
#include "WindowManager.h"
extern WindowManager* g_windowManager;
Test::Test() {
QTextStream qout(stdout);
QTextStream qerr(stderr);
// =======================【请修改这里】=======================
// 设置你的 ONNX 模型所在的文件夹路径
QString model_directory = "D:/wzl/project/wzl_qt_work/assets/models/ocr/onnx_ocr/ppocrv5/";
// 设置你要测试的图片完整路径
QString image_path = "D:/mhxy.jpg"; // <--- 必须修改为一张有效图片路径!
// ==========================================================
std::unique_ptr<OcrService> ocr_service;
// --- 步骤 1: 初始化 OCR 服务 ---
// 使用 try-catch 结构来安全地处理初始化过程中可能抛出的异常
try {
qout << "[INFO] Initializing OCR service from: " << model_directory << "\n";
ocr_service = std::make_unique<OcrService>(model_directory);
qout << "[SUCCESS] OCR service initialized successfully." << "\n";
} catch (const std::runtime_error& e) {
// 如果构造函数抛出异常 (如模型文件找不到), 在这里捕获并打印错误信息
std::cerr << "[ERROR] OCR service initialization failed: " << e.what() << "\n";
return; // 初始化失败, 程序退出
}
// --- 步骤 2: 加载测试图片 ---
qout << "\n[INFO] Loading image from: " << image_path << "\n";
cv::Mat image = cv::imread(image_path.toStdString());
if (image.empty()) {
qerr << "[ERROR] Could not load image. Please check the path: " << image_path << "\n";
return;
}
qout << "[SUCCESS] Image loaded." << "\n";
// --- 步骤 3: 执行 OCR 并获取结果 ---
qout << "\n[INFO] Performing OCR on the image..." << "\n";
QVector<QString> ocr_results = ocr_service->performOcr(image);
// --- 步骤 4: 打印识别结果 ---
qout << "\n========== OCR Results ==========" << "\n";
if (ocr_results.empty()) {
qout << "No text was detected in the image." << "\n";
} else {
int line_num = 1;
for (const auto& line : ocr_results) {
qout << "Line " << line_num++ << ": " << line << Qt::endl;
}
}
qout << "===============================" << "\n";
}
#### 这是我windows Qt 6.9 Quick CMake 项目的目录结构:
"D:\wzl\project\wzl_qt_work\CMakeLists.txt"
"D:\wzl\project\wzl_qt_work\src\resources.qrc"
"D:\wzl\project\wzl_qt_work\src\qml\"
"D:\wzl\project\wzl_qt_work\src\qml\Main.qml"
"D:\wzl\project\wzl_qt_work\src\qml\WzlText.qml"
"D:\wzl\project\wzl_qt_work\src\qml\SettingsWindow.qml"
"D:\wzl\project\wzl_qt_work\src\cpp\"
"D:\wzl\project\wzl_qt_work\src\cpp\pch.h"
"D:\wzl\project\wzl_qt_work\src\cpp\CMakeLists.txt"
"D:\wzl\project\wzl_qt_work\src\cpp\main.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\WindowManager.h"
"D:\wzl\project\wzl_qt_work\src\cpp\WindowManager.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\BackendController.h"
"D:\wzl\project\wzl_qt_work\src\cpp\BackendController.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\DataModel.h"
"D:\wzl\project\wzl_qt_work\src\cpp\DataModel.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\system_info\wzl_regedit.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\documentProcessing\pdf\wzl_pdf.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\documentProcessing\wzl_file.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\ocr\wzl_ocr.cpp"
"D:\wzl\project\wzl_qt_work\src\cpp\ocr\OcrService.h"
"D:\wzl\project\wzl_qt_work\src\cpp\ocr\OcrService.cpp"
"D:\wzl\project\wzl_qt_work\out\build\debug\python\ocr\ocr_test.py"
"D:\wzl\project\wzl_qt_work\assets\images\"
"D:\wzl\project\wzl_qt_work\assets\images\icons\main.ico"
"D:\wzl\project\wzl_qt_work\assets\models\ocr\onnx_ocr\ppocrv5\ppocrv5_dict.txt"
"D:\wzl\project\wzl_qt_work\assets\models\ocr\onnx_ocr\ppocrv5\cls\cls.onnx"
"D:\wzl\project\wzl_qt_work\assets\models\ocr\onnx_ocr\ppocrv5\det\det.onnx"
"D:\wzl\project\wzl_qt_work\assets\models\ocr\onnx_ocr\ppocrv5\rec\rec.onnx"
"D:\wzl\project\wzl_qt_work\third_party\onnxruntime-win-x64-1.22.0\include\"
"D:\wzl\project\wzl_qt_work\third_party\onnxruntime-win-x64-1.22.0\lib\"
"D:\vcpkg\packages\opencv4_x64-windows\include\"
"D:\vcpkg\packages\opencv4_x64-windows\lib"
# D:\wzl\project\wzl_qt_work\CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(wzl_qt_work VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# === 启用预编译头和Qt特性 ===
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
# 启用 `AUTOMOC/AUTORCC/AUTOUIC` 自动处理Qt特性
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# 指定Qt安装路径(根据实际情况调整)
if(MSVC)
# 当使用 Visual Studio 时,使用 MSVC 版本的 Qt
# 检查是否使用 Microsoft Visual Studio 的编译器 (`MSVC`)
set(Qt6_DIR "E:/Qt/6.10.0/msvc2022_64/lib/cmake/Qt6")
else()
# 其他环境(例如在 Qt Creator 中使用 MinGW 工具链)
# set(Qt6_DIR "E:/Qt/6.10.0/llvm-mingw_64/lib/cmake/Qt6")
set(Qt6_DIR "E:/Qt/6.10.0/msvc2022_64/lib/cmake/Qt6")
endif()
# === Qt 模块 ===
find_package(Qt6 REQUIRED COMPONENTS Quick Core Qml Network Gui Widgets QuickControls2 QuickLayouts)
qt_standard_project_setup(REQUIRES 6.8)
# windows 下,用 Visual Studio 2022 打开 Qt Quick CMake 项目,编译报错:
# error 指令: "Qt requires a C++17 compiler, and a suitable value for __cplusplus. On MSVC, you must pass the /Zc:__cplusplus option to the compiler."
# error: "Qt requires a C++17 compiler, and a suitable value for __cplusplus. On MSVC, you must pass the /Zc:__cplusplus option to the compiler."
# windows 下,用Visual Studio 2022 打开 Qt Quick CMake 项目,编译报错:
# E:\Qt\6.10.0\llvm-mingw_64\include\QtCore\qcompilerdetection.h(1327): error C2338: static_assert failed: 'On MSVC you must pass the /permissive- option to the compiler.'
# C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\type_traits(323): error C2139: “QString”: 未定义的类不允许作为编译器内部类型特征“__is_convertible_to”的参数
if(MSVC)
add_compile_options(
# 报告 C++ 标准
/Zc:__cplusplus
# 启用严格标准符合模式
/permissive-
# 强制源码使用 UTF-8 编码,解决中文路径问题
/utf-8
)
# 启用 ASLR,专门针对 Windows 平台和 MSVC 编译器的重要安全设置,传递给 MSVC 链接器 (link.exe) 的选项,
# - **地址空间布局随机化 (ASLR - Address Space Layout Randomization)**
# - 每次程序运行时,操作系统会将程序加载到**随机的内存地址**
# - 防止攻击者预测关键函数/数据的内存位置
add_link_options(/DYNAMICBASE)
endif()
# === 预编译头 ===
# 将 `define_url.h` 添加到 `PRECOMPILE_HEADERS` 变量中,确保 CMake 能正确识别其为预编译依赖
# 显式添加 define_url.h 到源文件列表(确保预编译依赖正确)
set(PRECOMPILE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/cpp/pch.h
${CMAKE_CURRENT_SOURCE_DIR}/src/cpp/define_url.h
)
# === 添加可执行目标 ===
# Qt Creator 会解析 CMake 目标中的文件列表
# 所有添加到可执行目标 (`qt_add_executable`) 的文件都会显示在项目树中
qt_add_executable(appwzl_qt_work
src/cpp/main.cpp
# 添加 shortcut_key.h 仅用于在 IDE 中显示;这样会将文件添加到 Qt Creator 的项目树中,但不会尝试编译它(因为它不是源文件)
src/cpp/shortcut_key.h
# 将头文件加入可执行文件依赖
${PRECOMPILE_HEADERS}
src/cpp/BackendController.cpp
src/cpp/BackendController.h
src/cpp/WindowManager.cpp
src/cpp/WindowManager.h
src/cpp/DataModel.cpp
src/cpp/DataModel.h
src/cpp/wzl_dll.cpp
src/cpp/test/Test.h
src/cpp/test/Test.cpp
src/cpp/test/company.cpp
src/cpp/ocr/wzl_ocr.cpp
src/cpp/ocr/OcrService.h
src/cpp/ocr/OcrService.cpp
src/cpp/system_info/wzl_regedit.cpp
src/cpp/documentProcessing/wzl_file.cpp
src/cpp/documentProcessing/pdf/wzl_pdf.cpp
qt_faq.md
README.md
)
# === Qt 资源文件 ===
qt_add_resources(appwzl_qt_work "app_resources"
PREFIX "/"
FILES
src/resources.qrc
assets/images/icons/main.ico
)
# === 预编译头设置 ===
#target_precompile_headers(appwzl_qt_work PRIVATE
# "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/cpp/pch.h>"
#)
# 配置预编译头(更新为使用列表)
target_precompile_headers(appwzl_qt_work PRIVATE ${PRECOMPILE_HEADERS})
# === 包含头文件目录 ===
target_include_directories(appwzl_qt_work PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/cpp
)
# === 添加 QML 模块 ===
qt_add_qml_module(appwzl_qt_work
URI "wzl_qt_work"
VERSION "1.0"
RESOURCE_PREFIX "/"
QML_FILES
src/qml/Main.qml
src/qml/SettingsWindow.qml
src/qml/wzl_text/WzlText.qml
)
# 设置目标属性(包括 Windows 和 macOS 平台)
set_target_properties(appwzl_qt_work PROPERTIES
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
MACOSX_BUNDLE TRUE
# 打开:会显示控制台;注释:隐藏控制台
WIN32_EXECUTABLE TRUE
)
# === 链接 Qt 模块 ===
target_link_libraries(appwzl_qt_work PRIVATE
Qt6::Quick
Qt6::Core
Qt6::Qml
Qt6::Network
Qt6::Gui
Qt6::Widgets
Qt6::QuickControls2
Qt6::QuickLayouts
)
# === 新增 OpenCV 支持 ===
find_package(OpenCV REQUIRED)
target_include_directories(appwzl_qt_work PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(appwzl_qt_work PRIVATE ${OpenCV_LIBS})
# === 新增 ONNX Runtime 支持 ===
set(ONNXRUNTIME_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/onnxruntime-win-x64-1.22.0")
target_include_directories(appwzl_qt_work PRIVATE ${ONNXRUNTIME_DIR}/include)
target_link_directories(appwzl_qt_work PRIVATE ${ONNXRUNTIME_DIR}/lib)
target_link_libraries(appwzl_qt_work PRIVATE onnxruntime)
# === 安装规则 ===
include(GNUInstallDirs)
install(TARGETS appwzl_qt_work
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
最新发布