diff --git a/others/C++/gcc_OCR/Makefile b/others/C++/gcc_OCR/Makefile new file mode 100644 index 0000000..420059c --- /dev/null +++ b/others/C++/gcc_OCR/Makefile @@ -0,0 +1,30 @@ +# 编译器设置 +CXX = g++ +CXXFLAGS = -std=c++17 -Wall -Wextra -Iinclude +LDFLAGS = -lfltk -ltesseract -lleptonica + +# 源文件和目标文件 +SRCS = src/main.cpp src/gui_window.cpp src/ocr_engine.cpp \ + src/file_io.cpp src/error_handler.cpp src/image_processor.cpp +OBJS = $(patsubst src/%.cpp,obj/%.o,$(SRCS)) + +# 目标可执行文件 +TARGET = ocr_app.exe + +# 默认目标 +all: $(TARGET) + +# 链接规则 +$(TARGET): $(OBJS) + $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) + +# 编译规则 +obj/%.o: src/%.cpp + @mkdir -p obj + $(CXX) $(CXXFLAGS) -c -o $@ $< + +# 清理 +clean: + rm -rf obj $(TARGET) + +.PHONY: all clean diff --git a/others/C++/gcc_OCR/include/common.hpp b/others/C++/gcc_OCR/include/common.hpp new file mode 100644 index 0000000..1ca5649 --- /dev/null +++ b/others/C++/gcc_OCR/include/common.hpp @@ -0,0 +1,19 @@ +#ifndef COMMON_HPP +#define COMMON_HPP + +#include +#include +#include +#include + +// 公共宏定义 +#define APP_NAME "OCR识别器" +#define APP_VERSION "1.0.0" + +// 公共类型定义 +using String = std::string; + +// 错误处理宏 +#define THROW_EXCEPTION(msg) throw std::runtime_error(std::string(__FILE__) + ":" + std::to_string(__LINE__) + " " + msg) + +#endif // COMMON_HPP diff --git a/others/C++/gcc_OCR/include/config.hpp b/others/C++/gcc_OCR/include/config.hpp new file mode 100644 index 0000000..872ac41 --- /dev/null +++ b/others/C++/gcc_OCR/include/config.hpp @@ -0,0 +1,40 @@ +#ifndef CONFIG_HPP +#define CONFIG_HPP + +#include "../include/common.hpp" +#include + +struct AppConfig { + String language; // OCR识别语言 + String lastDir; // 最后打开的目录 + int windowWidth; // 窗口宽度 + int windowHeight; // 窗口高度 +}; + +class ConfigManager { +public: + ConfigManager(); + + // 加载配置 + bool loadConfig(const String& filePath = "config.ini"); + + // 保存配置 + bool saveConfig(const String& filePath = "config.ini"); + + // 获取配置 + AppConfig getConfig() const; + + // 更新配置 + void updateConfig(const AppConfig& newConfig); + +private: + AppConfig config; + + // 解析INI文件 + void parseIni(const String& content); + + // 生成INI文件内容 + String generateIni() const; +}; + +#endif // CONFIG_HPP diff --git a/others/C++/gcc_OCR/include/error_handler.hpp b/others/C++/gcc_OCR/include/error_handler.hpp new file mode 100644 index 0000000..d9701c5 --- /dev/null +++ b/others/C++/gcc_OCR/include/error_handler.hpp @@ -0,0 +1,28 @@ +#ifndef ERROR_HANDLER_HPP +#define ERROR_HANDLER_HPP + +#include "../include/common.hpp" +#include + +// 错误代码枚举 +enum class ErrorCode { + FILE_IO_ERROR, + OCR_INIT_ERROR, + OCR_PROCESS_ERROR, + GUI_ERROR, + UNKNOWN_ERROR +}; + +// 自定义异常类 +class OCRException : public std::runtime_error { +public: + ErrorCode code; + + OCRException(ErrorCode ec, const String& msg) + : std::runtime_error(msg), code(ec) {} +}; + +// 错误处理函数 +void handleError(const std::exception& e); + +#endif // ERROR_HANDLER_HPP diff --git a/others/C++/gcc_OCR/include/file_io.hpp b/others/C++/gcc_OCR/include/file_io.hpp new file mode 100644 index 0000000..e1f30cf --- /dev/null +++ b/others/C++/gcc_OCR/include/file_io.hpp @@ -0,0 +1,16 @@ +#ifndef FILE_IO_HPP +#define FILE_IO_HPP + +#include "../include/common.hpp" +#include + +// 保存文本到文件 +bool saveTextToFile(const String& filePath, const String& content); + +// 从文件加载文本 +String loadTextFromFile(const String& filePath); + +// 获取支持的图像格式列表 +std::vector getSupportedImageFormats(); + +#endif // FILE_IO_HPP diff --git a/others/C++/gcc_OCR/include/gui_window.hpp b/others/C++/gcc_OCR/include/gui_window.hpp new file mode 100644 index 0000000..95f3cda --- /dev/null +++ b/others/C++/gcc_OCR/include/gui_window.hpp @@ -0,0 +1,50 @@ +#ifndef GUI_WINDOW_HPP +#define GUI_WINDOW_HPP + +#include +#include +#include +#include +#include +#include +#include "../include/common.hpp" + +class GUIWindow { +public: + GUIWindow(int width, int height, const char* title); + ~GUIWindow(); + + // 设置OCR结果文本 + void setOCRResult(const String& text); + + // 按钮状态控制 + void disableButtons(); + void enableButtons(); + + // 获取当前语言设置 + String getLanguage() const; + +private: + Fl_Window* window; + Fl_Text_Display* textDisplay; + Fl_Text_Buffer* textBuffer; + Fl_Button* openButton; + Fl_Button* saveButton; + Fl_Choice* languageChoice; + + // 支持的语言列表 + static constexpr const char* LANGUAGES[3] = {"英文", "简体中文", "中英文混合"}; + static constexpr const char* LANGUAGE_CODES[3] = {"eng", "chi_sim", "eng+chi_sim"}; + + // 回调函数 + static void openCallback(Fl_Widget* w, void* data); + static void saveCallback(Fl_Widget* w, void* data); + + // 初始化UI + void initUI(); + + // 异常处理辅助方法 + static void handleException(void* data); +}; + +#endif // GUI_WINDOW_HPP diff --git a/others/C++/gcc_OCR/include/image_processor.hpp b/others/C++/gcc_OCR/include/image_processor.hpp new file mode 100644 index 0000000..39aa2b4 --- /dev/null +++ b/others/C++/gcc_OCR/include/image_processor.hpp @@ -0,0 +1,31 @@ +#ifndef IMAGE_PROCESSOR_HPP +#define IMAGE_PROCESSOR_HPP + +#include "../include/common.hpp" +#include + +class ImageProcessor { +public: + // 从文件加载图像并进行预处理 + static Pix* loadAndPreprocess(const String& filePath); + + // 图像预处理 + static Pix* preprocess(Pix* image); + + // 转换为灰度图像 + static Pix* convertToGrayscale(Pix* image); + + // 二值化处理 + static Pix* binarize(Pix* image); + + // 调整对比度 + static Pix* adjustContrast(Pix* image, float factor); + + // 调整亮度 + static Pix* adjustBrightness(Pix* image, int delta); + + // 降噪处理 + static Pix* removeNoise(Pix* image); +}; + +#endif // IMAGE_PROCESSOR_HPP diff --git a/others/C++/gcc_OCR/include/ocr_engine.hpp b/others/C++/gcc_OCR/include/ocr_engine.hpp new file mode 100644 index 0000000..e5a066b --- /dev/null +++ b/others/C++/gcc_OCR/include/ocr_engine.hpp @@ -0,0 +1,30 @@ +#ifndef OCR_ENGINE_HPP +#define OCR_ENGINE_HPP + +#include +#include +#include "../include/common.hpp" + +class OCREngine { +public: + OCREngine(); + ~OCREngine(); + + // 设置识别语言 + bool setLanguage(const String& lang); + + // 从图像文件识别文本 + String recognizeFromFile(const String& filePath); + + // 从内存图像识别文本 + String recognizeFromImage(Pix* image); + +private: + tesseract::TessBaseAPI* api; + String currentLanguage; + + // 初始化Tesseract + void initTesseract(); +}; + +#endif // OCR_ENGINE_HPP diff --git a/others/C++/gcc_OCR/include/result_display.hpp b/others/C++/gcc_OCR/include/result_display.hpp new file mode 100644 index 0000000..e9ce897 --- /dev/null +++ b/others/C++/gcc_OCR/include/result_display.hpp @@ -0,0 +1,27 @@ +#ifndef RESULT_DISPLAY_HPP +#define RESULT_DISPLAY_HPP + +#include "../include/common.hpp" +#include + +class ResultDisplay { +public: + // 格式化OCR结果 + static String formatResult(const String& rawText); + + // 校正常见OCR错误 + static String correctCommonErrors(const String& text); + + // 分割段落 + static std::vector splitParagraphs(const String& text); + + // 高亮低置信度区域 + static String highlightLowConfidence(const String& text, const std::vector& confidences); + + // 导出为不同格式 + static bool exportAsText(const String& filePath, const String& content); + static bool exportAsHtml(const String& filePath, const String& content); + static bool exportAsPdf(const String& filePath, const String& content); +}; + +#endif // RESULT_DISPLAY_HPP diff --git a/others/C++/gcc_OCR/include/utils.hpp b/others/C++/gcc_OCR/include/utils.hpp new file mode 100644 index 0000000..4a67f7d --- /dev/null +++ b/others/C++/gcc_OCR/include/utils.hpp @@ -0,0 +1,20 @@ +#ifndef UTILS_HPP +#define UTILS_HPP + +#include "../include/common.hpp" +#include + +// 字符串工具 +String trim(const String& str); +std::vector split(const String& str, char delimiter); +String join(const std::vector& strings, const String& delimiter); + +// 图像工具 +bool isImageFile(const String& filePath); +String getFileExtension(const String& filePath); + +// 系统工具 +String getCurrentDateTime(); +String getHomeDirectory(); + +#endif // UTILS_HPP diff --git a/others/C++/gcc_OCR/src/error_handler.cpp b/others/C++/gcc_OCR/src/error_handler.cpp new file mode 100644 index 0000000..467b212 --- /dev/null +++ b/others/C++/gcc_OCR/src/error_handler.cpp @@ -0,0 +1,27 @@ +#include "../include/error_handler.hpp" +#include +#include +#include + +void handleError(const std::exception& e) { + // 记录错误到控制台 + std::cerr << "错误: " << e.what() << std::endl; + + // 显示用户友好的错误消息 + if (Fl::first_window()) { + fl_alert("%s", e.what()); + } else { + std::cerr << "无法显示错误对话框: 没有可用的GUI窗口" << std::endl; + } +} + +String errorCodeToString(ErrorCode code) { + switch(code) { + case ErrorCode::FILE_IO_ERROR: return "文件IO错误"; + case ErrorCode::OCR_INIT_ERROR: return "OCR初始化错误"; + case ErrorCode::OCR_PROCESS_ERROR: return "OCR处理错误"; + case ErrorCode::GUI_ERROR: return "GUI错误"; + case ErrorCode::UNKNOWN_ERROR: return "未知错误"; + default: return "未定义的错误代码"; + } +} diff --git a/others/C++/gcc_OCR/src/file_io.cpp b/others/C++/gcc_OCR/src/file_io.cpp new file mode 100644 index 0000000..7f5ccf0 --- /dev/null +++ b/others/C++/gcc_OCR/src/file_io.cpp @@ -0,0 +1,39 @@ +#include "../include/file_io.hpp" +#include "../include/error_handler.hpp" +#include +#include + +bool saveTextToFile(const String& filePath, const String& content) { + std::ofstream outFile(filePath); + if (!outFile) { + THROW_EXCEPTION("无法打开文件进行写入: " + filePath); + return false; + } + + outFile << content; + outFile.close(); + return true; +} + +String loadTextFromFile(const String& filePath) { + std::ifstream inFile(filePath); + if (!inFile) { + THROW_EXCEPTION("无法打开文件进行读取: " + filePath); + } + + std::stringstream buffer; + buffer << inFile.rdbuf(); + inFile.close(); + return buffer.str(); +} + +std::vector getSupportedImageFormats() { + return { + "*.png", + "*.jpg", + "*.jpeg", + "*.bmp", + "*.tif", + "*.tiff" + }; +} diff --git a/others/C++/gcc_OCR/src/gui_window.cpp b/others/C++/gcc_OCR/src/gui_window.cpp new file mode 100644 index 0000000..7a24b52 --- /dev/null +++ b/others/C++/gcc_OCR/src/gui_window.cpp @@ -0,0 +1,106 @@ +#include "../include/gui_window.hpp" +#include "../include/file_io.hpp" +#include "../include/ocr_engine.hpp" +#include +#include + +// 异常信息结构体 +struct ExceptionData { + GUIWindow* window; + String errorMsg; +}; + +GUIWindow::GUIWindow(int width = 900, int height = 700, const char* title = "OCR识别工具") { + window = new Fl_Window(width, height, title); + initUI(); + window->end(); + window->show(); +} + +GUIWindow::~GUIWindow() { + delete textBuffer; + delete window; +} + +void GUIWindow::initUI() { + textBuffer = new Fl_Text_Buffer(); + textDisplay = new Fl_Text_Display(20, 20, 860, 550, "识别结果"); + textDisplay->buffer(textBuffer); + + // 添加语言选择下拉菜单 + languageChoice = new Fl_Choice(70, 580, 300, 25, "识别语言"); + for (int i = 0; i < 3; ++i) { + languageChoice->add(LANGUAGES[i]); + } + languageChoice->value(0); // 默认选择英文 + + openButton = new Fl_Button(390, 580, 150, 25, "打开图片"); + openButton->callback(openCallback, this); + + saveButton = new Fl_Button(550, 580, 150, 25, "保存结果"); + saveButton->callback(saveCallback, this); +} + +void GUIWindow::setOCRResult(const String& text) { + textBuffer->text(text.c_str()); +} + +String GUIWindow::getLanguage() const { + return LANGUAGE_CODES[languageChoice->value()]; +} + +void GUIWindow::handleException(void* data) { + ExceptionData* exData = static_cast(data); + exData->window->setOCRResult("无法识别"); + exData->window->enableButtons(); + fl_alert("识别错误: %s", exData->errorMsg.c_str()); + delete exData; +} + +void GUIWindow::openCallback(Fl_Widget* w, void* data) { + (void)w; + GUIWindow* win = static_cast(data); + const char* filename = fl_file_chooser("选择图片文件", "*.{png,jpg,bmp}", ""); + if (filename) { + win->setOCRResult("正在识别..."); + win->disableButtons(); + + std::thread([win, filename]() { + try { + OCREngine ocr; + if (!ocr.setLanguage(win->getLanguage())) { + throw std::runtime_error("无法设置识别语言"); + } + String result = ocr.recognizeFromFile(filename); + Fl::awake([](void* data) { + auto* ctx = static_cast*>(data); + ctx->first->setOCRResult(ctx->second.empty() ? "无法识别" : ctx->second); + ctx->first->enableButtons(); + delete ctx; + }, new std::pair(win, result)); + } catch (const std::exception& e) { + ExceptionData* exData = new ExceptionData{win, e.what()}; + Fl::awake(handleException, exData); + } + }).detach(); + } +} + +void GUIWindow::disableButtons() { + openButton->deactivate(); + saveButton->deactivate(); +} + +void GUIWindow::enableButtons() { + openButton->activate(); + saveButton->activate(); +} + +void GUIWindow::saveCallback(Fl_Widget* w, void* data) { + (void)w; + GUIWindow* win = static_cast(data); + const char* filename = fl_file_chooser("保存识别结果", "*.txt", ""); + if (filename && win->textBuffer->length() > 0) { + saveTextToFile(filename, win->textBuffer->text()); + } +} diff --git a/others/C++/gcc_OCR/src/image_processor.cpp b/others/C++/gcc_OCR/src/image_processor.cpp new file mode 100644 index 0000000..5506bf2 --- /dev/null +++ b/others/C++/gcc_OCR/src/image_processor.cpp @@ -0,0 +1,64 @@ +#include "../include/image_processor.hpp" +#include "../include/error_handler.hpp" +#include + +Pix* ImageProcessor::loadAndPreprocess(const String& filePath) { + Pix* image = pixRead(filePath.c_str()); + if (!image) { + THROW_EXCEPTION("无法加载图像文件: " + filePath); + } + return preprocess(image); +} + +Pix* ImageProcessor::preprocess(Pix* image) { + Pix* processed = convertToGrayscale(image); + processed = adjustContrast(processed, 1.5f); + processed = binarize(processed); + processed = removeNoise(processed); + return processed; +} + +Pix* ImageProcessor::convertToGrayscale(Pix* image) { + Pix* gray = pixConvertRGBToGray(image, 0.3f, 0.59f, 0.11f); + if (!gray) { + pixDestroy(&image); + THROW_EXCEPTION("灰度转换失败"); + } + return gray; +} + +Pix* ImageProcessor::binarize(Pix* image) { + // 使用更精细的局部二值化 + if (pixOtsuAdaptiveThreshold(image, 16, 16, 5, 5, 0.1f, nullptr, nullptr) != 0) { + pixDestroy(&image); + THROW_EXCEPTION("二值化失败"); + } + return image; +} + +Pix* ImageProcessor::adjustContrast(Pix* image, float factor) { + Pix* contrast = pixContrastTRC(nullptr, image, factor); + if (!contrast) { + pixDestroy(&image); + THROW_EXCEPTION("对比度调整失败"); + } + return contrast; +} + +Pix* ImageProcessor::adjustBrightness(Pix* image, int delta) { + if (pixMultConstantGray(image, 1.0f + delta/255.0f) != 0) { + pixDestroy(&image); + THROW_EXCEPTION("亮度调整失败"); + } + return image; +} + +Pix* ImageProcessor::removeNoise(Pix* image) { + // 增强降噪效果 + Pix* denoised = pixCleanImage(image, 2, 0, 2, 10); + if (!denoised) { + pixDestroy(&image); + THROW_EXCEPTION("降噪失败"); + } + return denoised; +} diff --git a/others/C++/gcc_OCR/src/main.cpp b/others/C++/gcc_OCR/src/main.cpp new file mode 100644 index 0000000..ec22f72 --- /dev/null +++ b/others/C++/gcc_OCR/src/main.cpp @@ -0,0 +1,21 @@ +#include "../include/common.hpp" +#include "../include/gui_window.hpp" +#include "../include/ocr_engine.hpp" + +int main(int argc, char** argv) { + (void)argc; + (void)argv; + try { + // 初始化GUI窗口 + GUIWindow window(900, 700, "OCR识别器"); + + // 初始化OCR引擎 + OCREngine ocr; + + // 运行主循环 + return Fl::run(); + } catch (const std::exception& e) { + std::cerr << "错误: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/others/C++/gcc_OCR/src/ocr_engine.cpp b/others/C++/gcc_OCR/src/ocr_engine.cpp new file mode 100644 index 0000000..dfe0113 --- /dev/null +++ b/others/C++/gcc_OCR/src/ocr_engine.cpp @@ -0,0 +1,68 @@ +#include "../include/ocr_engine.hpp" +#include "../include/error_handler.hpp" +#include +#include + +OCREngine::OCREngine() : api(new tesseract::TessBaseAPI()), currentLanguage("eng") { + initTesseract(); +} + +OCREngine::~OCREngine() { + if (api) { + api->End(); + delete api; + } +} + +bool OCREngine::setLanguage(const String& lang) { + // 支持多语言混合识别,如"eng+chi_sim" + if (api->Init("C:/msys64/ucrt64/share/tessdata/", lang.c_str())) { + // 尝试回退到英文 + if (api->Init("C:/msys64/ucrt64/share/tessdata/", "eng")) { + THROW_EXCEPTION("无法设置OCR语言: " + lang + " 且回退到英文失败"); + return false; + } + currentLanguage = "eng"; + return false; + } + currentLanguage = lang; + + // 针对中文优化识别参数 + if (lang.find("chi") != String::npos) { + api->SetVariable("tessedit_char_whitelist", + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府称太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞"); + api->SetPageSegMode(tesseract::PSM_SPARSE_TEXT_OSD); + } + return true; +} + +String OCREngine::recognizeFromFile(const String& filePath) { + Pix* image = pixRead(filePath.c_str()); + if (!image) { + THROW_EXCEPTION("无法加载图像文件: " + filePath); + } + + String result = recognizeFromImage(image); + pixDestroy(&image); + return result; +} + +String OCREngine::recognizeFromImage(Pix* image) { + api->SetImage(image); + char* text = api->GetUTF8Text(); + if (!text) { + THROW_EXCEPTION("OCR识别失败"); + } + + String result(text); + delete[] text; + return result; +} + +void OCREngine::initTesseract() { + if (api->Init("C:/msys64/ucrt64/share/tessdata/", currentLanguage.c_str())) { + THROW_EXCEPTION("无法初始化Tesseract OCR引擎"); + } + api->SetPageSegMode(tesseract::PSM_AUTO); +}