如何用OpenCV和Tesseract正确检测图像文字,解决非文本区域误检问题
Tesseract OCR误检优化方案
你当前的问题核心是未做图像预处理、未配置Tesseract过滤参数,直接传入原图识别导致误检,可按以下步骤优化:
1. 图像预处理优化
Tesseract对输入图像质量要求较高,直接传入彩图会严重提升误检率,需增加以下处理步骤:
- 灰度转换:将OpenCV读取的BGR三通道彩图转为单通道灰度图,减少无关颜色信息干扰
- 自适应二值化:处理光照不均、阴影问题,将图像转为只有黑白两色的高对比度图,推荐使用反二值化保证文字为前景色
- 降噪处理:通过高斯模糊、中值模糊过滤图像噪点,避免细小噪点被判定为文字
- 形态学操作:用开运算去除小面积噪点块,闭运算补全文字断裂缺口,进一步突出文字特征
2. Tesseract参数配置优化
默认配置的Tesseract灵敏度极高,容易识别无意义区域,需增加参数限制:
- 设置合理的页分割模式(PSM):根据你的识别场景选择对应模式,比如识别单页稀疏文字可以用
PSM_SPARSE_TEXT,识别整块文字用PSM_SINGLE_BLOCK,避免全局无差别检测 - 增加置信度过滤:Tesseract返回每个识别结果的置信度评分,通常低于60分的结果都是误检,直接过滤即可
- 设置字符白名单:如果只需要识别英文、数字等指定字符,通过白名单配置排除特殊符号、乱码的识别结果
3. 识别结果后处理过滤
对返回的检测框做规则过滤:
- 过滤尺寸异常的框:根据你的业务场景预设文字的合理宽高范围,宽、高、面积不在范围内的框直接丢弃
- 过滤宽高比异常的框:正常文字的宽高比不会过大或过小,比如宽高比超过20的细长条、接近正方形的大块区域大概率不是文字
- 重叠框合并:通过非极大值抑制(NMS)合并重复识别的重叠框
优化后代码示例
void Application::Application::OpenAndProcessImageFile(void) { OPENFILENAMEA ofn; ZeroMemory(&ofn, sizeof(OPENFILENAMEA)); char szFile[260] = { 0 }; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = mWindow->getHandle(); ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = "JPG\0*.JPG\0PNG\0*.PNG\0"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if (GetOpenFileNameA(&ofn) == TRUE) { std::string filePath = ofn.lpstrFile; mImage = cv::imread(filePath.c_str()); // ========== 新增图像预处理 ========== cv::Mat grayImg, binImg; // 转灰度 cv::cvtColor(mImage, grayImg, cv::COLOR_BGR2GRAY); // 自适应二值化 cv::adaptiveThreshold(grayImg, binImg, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, 11, 2); // 降噪 cv::GaussianBlur(binImg, binImg, cv::Size(3,3), 0); // 形态学开运算去噪点 cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2,2)); cv::morphologyEx(binImg, binImg, cv::MORPH_OPEN, kernel); // ========== Tesseract参数优化 ========== tesseract::TessBaseAPI ocr = tesseract::TessBaseAPI(); ocr.Init(NULL, "eng"); // 设置页分割模式为稀疏文本模式 ocr.SetPageSegMode(tesseract::PSM_SPARSE_TEXT); // 设置字符白名单,可根据需求调整 ocr.SetVariable("tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.,"); // 传入预处理后的二值图 ocr.SetImage(binImg.data, binImg.cols, binImg.rows, 1, binImg.step); Boxa* bounds = ocr.GetWords(NULL); for (int i = 0; i < bounds->n; ++i) { Box* b = bounds->box[i]; // ========== 新增置信度和尺寸过滤 ========== ocr.SetRectangle(b->x, b->y, b->w, b->h); int conf = ocr.MeanTextConf(); // 置信度高于60且尺寸符合要求才绘制 if (conf > 60 && b->h > 8 && b->h < 120 && (float)b->w / b->h < 15) { cv::rectangle(mImage, { b->x,b->y,b->w,b->h }, { 0, 255, 0 }, 2); } } ocr.End(); cv::destroyAllWindows(); cv::imshow("İşlenmiş Resim", mImage); } }
内容的提问来源于stack exchange,提问作者Caner Kurt
相关产品推荐
相关产品推荐

