当前位置: 首页 > news >正文

用 C++ + OpenCV + Tesseract 实现英文数字验证码识别

本文展示如何用 C++ 结合 OpenCV 做图像预处理,再调用 Tesseract OCR 识别验证码。适用于希望在高性能后端或本地服务里集成 OCR 的场景。方案包含:
更多内容访问ttocr.com或联系1436423940
环境与依赖安装

图像预处理(灰度、二值化、形态学去噪、放大)

使用 Tesseract API 调用(设定白名单、PSM)

完整 C++ 示例与 CMake 构建

批量识别、并发与部署建议

评估方法与常见问题排查

1 环境准备
1.1 安装系统依赖(Ubuntu 示例)
sudo apt update
sudo apt install -y build-essential cmake pkg-config git
sudo apt install -y libopencv-dev
sudo apt install -y tesseract-ocr libtesseract-dev libleptonica-dev

macOS(Homebrew):

brew install opencv tesseract

确保 pkg-config --modversion opencv4、tesseract --version 可用。

1.2 创建工程目录
captcha_cpp_ocr/
├── CMakeLists.txt
├── src/
│ └── main.cpp
└── samples/
└── captcha.png

2 图像预处理思路(为什么要预处理)

Tesseract 对清晰、对比高、字符分离的图像效果最好。典型预处理步骤:

放缩(resize)——放大小字体提高识别率

灰度化(cv::cvtColor)

高斯模糊(可选,用于去噪)

自适应/固定阈值二值化(cv::adaptiveThreshold 或 cv::threshold)

形态学操作(开/闭运算)去除噪点或断连字符

倾斜校正(如果需要)

3 完整 C++ 示例代码(main.cpp)

把以下文件保存为 src/main.cpp。

// main.cpp
// 依赖: OpenCV, Tesseract (libtesseract)
// 编译: 使用 CMake(见项目 CMakeLists.txt)

include

include

include

include

include <opencv2/opencv.hpp>

include <tesseract/baseapi.h>

include <leptonica/allheaders.h>

namespace fs = std::filesystem;

// ---------- 图像预处理函数 ----------
cv::Mat preprocess(const cv::Mat &src) {
cv::Mat gray;
if (src.channels() == 3) {
cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
} else {
gray = src.clone();
}

// 放大 - 提高小字体识别率
const double scale = 2.0;
cv::Mat resized;
cv::resize(gray, resized, cv::Size(), scale, scale, cv::INTER_CUBIC);// 可选: 高斯去噪
cv::Mat blurred;
cv::GaussianBlur(resized, blurred, cv::Size(3, 3), 0);// 自适应阈值(二值化)
cv::Mat bw;
cv::adaptiveThreshold(blurred, bw, 255,cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY_INV, 11, 2);// 形态学处理:先开运算去小噪点,再闭运算连接字符可能断裂边缘
cv::Mat morph;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2,2));
cv::morphologyEx(bw, morph, cv::MORPH_OPEN, kernel);
cv::morphologyEx(morph, morph, cv::MORPH_CLOSE, kernel);// 可选: 再次去小区域噪点
std::vector<std::vector<cv::Point>> contours;
cv::findContours(morph.clone(), contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
for (auto &c : contours) {double area = cv::contourArea(c);if (area < 20) {cv::drawContours(morph, std::vector<std::vector<cv::Point>>{c}, -1, cv::Scalar(0), cv::FILLED);}
}// 最终返回二值图 - 转回白底黑字(Tesseract 对白底黑字通常表现更稳)
cv::Mat final;
cv::bitwise_not(morph, final); // invert to white background, black text
return final;

}

// ---------- 使用 Tesseract 识别单图 ----------
std::string recognizeWithTesseract(const cv::Mat &img, tesseract::TessBaseAPI &api) {
// 将 OpenCV Mat 转为 Pix*(Leptonica)
Pix *pix = pixCreate(img.cols, img.rows, 8);
for (int y = 0; y < img.rows; ++y) {
memcpy(pixGetData(pix) + y * pixGetWpl(pix) * 4, img.ptr(y), img.cols);
}
// 也可使用 pixReadMem or pixCreateHeader + pixSetData,以上为简化示例

api.SetImage(pix);
api.Recognize(0);
char *out = api.GetUTF8Text();
std::string result;
if (out) {result = std::string(out);delete[] out;
}
pixDestroy(&pix);
// 清理并返回
// 去掉换行与非字母数字字符
std::string cleaned;
for (char c : result) {if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))cleaned.push_back(c);
}
return cleaned;

}

int main(int argc, char** argv) {
if (argc < 2) {
std::cout << "Usage: " << argv[0] << " <image_or_folder>\n";
return 1;
}

std::string path = argv[1];
tesseract::TessBaseAPI api;
// 初始化 tesseract:NULL 表示使用默认 TESSDATA_PREFIX 环境变量或安装路径
if (api.Init(NULL, "eng", tesseract::OEM_LSTM_ONLY)) {std::cerr << "Could not initialize tesseract.\n";return -1;
}
// 白名单:只识别大写字母和数字(根据需求调整)
api.SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
// 可设置页面分割模式:单行/单词等,常用 PSM_SINGLE_LINE 或 PSM_SINGLE_BLOCK
api.SetPageSegMode(tesseract::PSM_SINGLE_LINE);// 如果是文件夹,批量识别
fs::path p(path);
std::vector<fs::path> images;
if (fs::is_directory(p)) {for (auto &entry : fs::directory_iterator(p)) {if (!entry.is_regular_file()) continue;std::string ext = entry.path().extension().string();std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp") {images.push_back(entry.path());}}
} else {images.push_back(p);
}for (auto &imgPath : images) {cv::Mat src = cv::imread(imgPath.string());if (src.empty()) {std::cerr << "Failed to open " << imgPath << "\n";continue;}cv::Mat proc = preprocess(src);// 调试可视化:// cv::imshow("proc", proc); cv::waitKey(0);std::string text = recognizeWithTesseract(proc, api);std::cout << imgPath.filename().string() << " -> " << text << std::endl;
}api.End();
return 0;

}

说明:上面把 Pix 创建做了示意(简化),在实际工程中建议使用 pixCreateHeader + pixSetData 或将 Mat 保存为临时 PNG 再 pixRead。若要避免内存复制,参考 Leptonica 文档使用 pixCreateHeader 并设置数据指针,注意行对齐(wpl)和内存生命周期。

4 CMakeLists.txt(构建脚本)

创建项目根目录 CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
project(captcha_ocr)

set(CMAKE_CXX_STANDARD 17)
find_package(PkgConfig REQUIRED)
pkg_check_modules(OPENCV4 REQUIRED opencv4)
find_package(Tesseract REQUIRED)

include_directories(${OPENCV4_INCLUDE_DIRS})
link_directories(${OPENCV4_LIBRARY_DIRS})
add_definitions(${OPENCV4_CFLAGS_OTHER})

add_executable(captcha_ocr src/main.cpp)

target_link_libraries(captcha_ocr ${OPENCV4_LIBRARIES} tesseract lept)

5 构建与运行
mkdir build && cd build
cmake ..
make -j4

单张测试

./captcha_ocr ../samples/captcha.png

批量测试

./captcha_ocr ../samples/

6 性能调优与工程化建议

避免频繁 Init/End:在服务中持久化 tesseract::TessBaseAPI 实例,避免每次请求初始化开销。

并发:Tesseract 对并发不完全线程安全,推荐创建 worker 池,每个 worker 拥有自己的 TessBaseAPI(避免共享同一实例)。

预处理批量化:在多线程中把预处理与 I/O 分离,使用队列减少阻塞。

更好的 Pix 转换:使用 Leptonica 的内存接口避免写临时文件,提高吞吐。

PSM 与 OEM 调优:尝试 PSM_SINGLE_CHAR, PSM_SINGLE_WORD, PSM_SINGLE_LINE,以及 OEM 的 OEM_LSTM_ONLY 或 OEM_DEFAULT 来找到适合样本的设置。

白名单严格化:若验证码仅数字,则白名单只设置 0123456789,能显著降低误检率。

候选后处理:固定长度验证码可做长度校验;通过字典或正则修正规则(如把小写 o 映射为 O,0 vs O 映射)。

多模型/多预处理投票:对同一图像用多种预处理参数产生多个候选结果,投票选最频繁结果,能提升稳健性。

7 评估方法(量化识别效果)

准备人工标注测试集(建议 500–2000 张真实样本)。

指标:

单字符准确率 = 正确识别的字符总数 / 总字符数

整串准确率 = 被完全正确识别的图像数量 / 总图像数

记录错误示例并统计混淆(例如 O↔0, I↔1),用来决定后处理规则。

8 常见问题与排查

输出为空或乱码:检查 Tesseract tessdata 路径及语言包是否安装;确认图片不是全白或全黑(预处理是否出错)。

CT C or internal errors:检查 pix 创建是否正确(位深、行对齐),建议先以临时 PNG 写磁盘再 pixRead 验证流程。

识别速度慢:避免每次读写磁盘、使用内存转换;批量或多线程处理。

误识别率高:增强数据(噪声、放大、不同阈值)、调整 SetPageSegMode、白名单或用训练模型(tess training)微调。

9 拓展方向(如果要更高准确率)

使用深度学习模型(CRNN、Transformer)对特定风格验证码训练专用模型,通常优于通用 OCR。

当验证码样式固定时,考虑对 Tesseract 进行自训练(生成 .traineddata)。

将前端图像分割成字符单元后逐字符识别(若字符分割可行)。

10 合法与道德提醒

本教程旨在技术学习与提升防护能力,不得用于未经授权的绕过安全措施或攻击网站。在对真实网站进行任何自动识别或抓取前,请获得授权并遵守使用条款与法律法规。

http://www.wxhsa.cn/company.asp?id=5154

相关文章:

  • java 第一节课课前提问
  • 二进制解码器、选通器和分配器
  • 2025最新版 Photoshop软件免费下载安装完整教程(PS2025)超详细安装教程
  • nac一键卸载软件脚本
  • 交叉编译openharmony版本的openssh
  • 为什么不建议在 Docker 中跑 MySQL
  • CFD
  • [MCP][05]Elicitation示例
  • Warsaw主题关闭导航条
  • Python Socket网络编程(2)
  • PS2025安装包下载及PS2025安装包安装教程详细步骤(包含安装包下载链接)
  • Nature Genetics | 本周最新文献速递
  • 关于go里切片作为函数参数时是引用传递还是值传递
  • DRAN读写循环
  • 数据结构操作相关
  • Neisbitt 不等式的证法
  • 端口转发神器Rinetd:轻量级安装与配置指南
  • C语言中递归思想的应用
  • WITH RECURSIVE 递归公用表表达式(CTE)
  • leetcode 3541. 找到频率最高的元音和辅音 便捷
  • 匿名递归与不动点组合子
  • Markdown学习Day01
  • flutter compass结构代码分析
  • 25.9.15
  • 二十八、共享内存多处理器的基本概念
  • 详细介绍:【ARMv7】系统复位上电后的程序执行过程
  • C#高级语法
  • 配置Maven
  • 那两年的回忆录
  • DDR4基本介绍