mirror of
https://github.com/zs-yg/kortapp-z.git
synced 2025-12-07 00:20:43 +08:00
添加OCR代码
这是一个废弃代码
This commit is contained in:
27
others/C++/gcc_OCR/src/error_handler.cpp
Normal file
27
others/C++/gcc_OCR/src/error_handler.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "../include/error_handler.hpp"
|
||||
#include <iostream>
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/fl_ask.H>
|
||||
|
||||
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 "未定义的错误代码";
|
||||
}
|
||||
}
|
||||
39
others/C++/gcc_OCR/src/file_io.cpp
Normal file
39
others/C++/gcc_OCR/src/file_io.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "../include/file_io.hpp"
|
||||
#include "../include/error_handler.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
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<String> getSupportedImageFormats() {
|
||||
return {
|
||||
"*.png",
|
||||
"*.jpg",
|
||||
"*.jpeg",
|
||||
"*.bmp",
|
||||
"*.tif",
|
||||
"*.tiff"
|
||||
};
|
||||
}
|
||||
106
others/C++/gcc_OCR/src/gui_window.cpp
Normal file
106
others/C++/gcc_OCR/src/gui_window.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "../include/gui_window.hpp"
|
||||
#include "../include/file_io.hpp"
|
||||
#include "../include/ocr_engine.hpp"
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
#include <thread>
|
||||
|
||||
// 异常信息结构体
|
||||
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<ExceptionData*>(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<GUIWindow*>(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<std::pair<GUIWindow*, String>*>(data);
|
||||
ctx->first->setOCRResult(ctx->second.empty() ? "无法识别" : ctx->second);
|
||||
ctx->first->enableButtons();
|
||||
delete ctx;
|
||||
}, new std::pair<GUIWindow*, String>(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<GUIWindow*>(data);
|
||||
const char* filename = fl_file_chooser("保存识别结果", "*.txt", "");
|
||||
if (filename && win->textBuffer->length() > 0) {
|
||||
saveTextToFile(filename, win->textBuffer->text());
|
||||
}
|
||||
}
|
||||
64
others/C++/gcc_OCR/src/image_processor.cpp
Normal file
64
others/C++/gcc_OCR/src/image_processor.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "../include/image_processor.hpp"
|
||||
#include "../include/error_handler.hpp"
|
||||
#include <leptonica/allheaders.h>
|
||||
|
||||
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;
|
||||
}
|
||||
21
others/C++/gcc_OCR/src/main.cpp
Normal file
21
others/C++/gcc_OCR/src/main.cpp
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
68
others/C++/gcc_OCR/src/ocr_engine.cpp
Normal file
68
others/C++/gcc_OCR/src/ocr_engine.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "../include/ocr_engine.hpp"
|
||||
#include "../include/error_handler.hpp"
|
||||
#include <tesseract/baseapi.h>
|
||||
#include <leptonica/allheaders.h>
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user