正方体总是缺少图片中的文本行

Posted

技术标签:

【中文标题】正方体总是缺少图片中的文本行【英文标题】:Tesseract always missing a text line in picture 【发布时间】:2020-09-13 07:05:29 【问题描述】:

我正在尝试使用 OCR 从图片中提取数据。我在 C++ 中使用 Tesseract API 来实现这一点。

原图是这样的:

现在对我来说重要的数据是这样的:

但是,无论我尝试什么,都无法识别标记的蓝线。

用tesseract分析图片的代码如下:

std::string readFromFile(const std::string& filename)

    tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
    api->SetPageSegMode(tesseract::PSM_AUTO);
    if (api->Init("folder_to_tessdata", "deu+eng")) 
        fprintf(stderr, "Could not initialize tesseract.\n");
        exit(1);
    

    // Open input image with leptonica library
    Pix *image = pixRead(filename.c_str());
    api->SetImage(image);
    // Get OCR result

    char *outText = api->GetUTF8Text();

    std::string result outText ;

    api->End();
    delete[] outText;
    pixDestroy(&image);

    return result;

我尝试像在这个问题中建议的那样预处理图像来提高准确性:image processing to improve tesseract OCR accuracy

预处理代码:

cv::Mat image;
image = cv::imread(filename, cv::IMREAD_COLOR);
cv::resize(image, image, cv::Size, 1.2, 1.2, cv::INTER_CUBIC);

cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);

auto kernel = cv::Mat(1, 1, CV_8UC1, cv::Scalar(1));
cv::dilate(image, image, kernel);
cv::erode(image, image, kernel);

cv::Mat filter;
cv::bilateralFilter(image, filter, 5, 75, 75);

cv::threshold(filter, image, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);

我错过了什么吗?我可以更多地调整 Tesseract 本身还是应该更改图像的预处理?

【问题讨论】:

就个人而言,我会删除 C++ 标签。如果它正在编译和运行并提取一些文本,则这部分正在工作,并且修复不会涉及更改 C++ 代码。相反,这似乎是一个视觉/检测问题。通过保留 C++ 标记,您可以吸引关注点与您需要的不同的评论者和回答者。 我会保留 C++ 标记,因为您很可能需要使用 C++ 通过调整或更改一些超参数和/或逻辑来回答这个问题。 【参考方案1】:

我的参考是here。

注意:您不需要处理预处理步骤,因为您似乎已经有了一个纯图像。噪音不大。

我的环境信息:

Operating system: Ubuntu 16.04

Tesseract 版本由tesseract --version 命令:

tesseract 4.1.1-rc2-21-gf4ef
 leptonica-1.78.0
  libgif 5.1.4 : libjpeg 8d (libjpeg-turbo 1.4.2) : libpng 1.2.54 : libtiff 4.0.6 : zlib 1.2.8 : libwebp 0.4.4 : libopenjp2 2.1.2
 Found AVX
 Found SSE
 Found libarchive 3.1.2

通过pkg-config --modversion opencv命令的OpenCV版本:

3.4.3

区别:当我检查你的代码时,我只看到了与这个明显的区别。您正在使用 leptonica 库而不是 opencv 再次打开图像。

这是代码和结果输出:

输入:

输出文本:

Al AQ A3 Ad AS A6 Al A8

| 2 3 4 5 6 7 8

WH GN YE GY PK Bu RD VT
K101 K102 K103 K104 K105 K107 K109 K110
Q30,0 Q30.1 Q30.2 Q30.3 Q30.4 Q30.5 Q30.6 Q30.7
=13/L.2 =13/2.2 =13/4.2 =13/6.2 =13/7.2 =13/10.2 FIBL.2 = 1312.2

AS AlO All Al2 AL3 Al4 ALS AL6

9 10 ll 12 13 14 15 16
GY /PK RD/BU WH/GN BN/GN WH/YE YE/BN WH/GY GY/BN
Kl1l K112 y114 K115 K117 K118 K124
Q31,0 Q31.1 Q31.2 Q31.3 Q31.4 Q31.5 Q31.6 Q31.7
=13/13.2 =13/14.2 =13/15.2 =13/16.2 =1B7.2 PIB. =13/21.2

Beckhoff KL 2809

代码:

#include <string>
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char* argv[])

    string outText;


    // Create Tesseract object
    tesseract::TessBaseAPI *ocr = new tesseract::TessBaseAPI();

    ocr->Init(NULL, "eng", tesseract::OEM_LSTM_ONLY);


    // Set Page segmentation mode to PSM_AUTO (3)
    ocr->SetPageSegMode(tesseract::PSM_AUTO);


    // Open input image using OpenCV
    Mat im = cv::imread("/ur/image/directory/tessatest.png", IMREAD_COLOR);


    // Set image data
    ocr->SetImage(im.data, im.cols, im.rows, 3, im.step);

    // Run Tesseract OCR on image
    outText = string(ocr->GetUTF8Text());

    // print recognized text
    cout << outText << endl;

    // Destroy used object and release memory
    ocr->End();

    return EXIT_SUCCESS;

代码编译:

g++ -O3 -std=c++11 test.cpp -o output `pkg-config --cflags --libs tesseract opencv`

【讨论】:

哇,我很惊讶你的结果有多好。我只添加了预处理的东西,因为没有它我无法得到一个好的结果(但也没有它)。我注意到您明确添加了OEM_LSTM_ONLY,但我没有。这可能是它在我的代码中效果不佳的主要原因吗?虽然我有一点点挑剔。你检测到y114,但它应该是V114 您可以在您的代码中尝试不同的变体,并找出造成这种情况的真正原因。是的,它可能是OEM_LSTM_ONLY。是的,也许它的 V 外观不好。为此可以应用一些步骤来明确。 通过添加 deu+eng 而不是 eng,我得到了更好的结果。 对于字符 v? 是的,它看起来识别度稍好一些。使用你的方法,我在输出中也得到了许多白线。所以我假设他逐行浏览图像?【参考方案2】:

Tesseract 在以下几种情况下倾向于删除行或文本片段:

有一些非文本的东西会干扰(线条、人工制品、灯光渐变) 有太多东西没有足够确定地被识别为字符 线条不均匀(凹凸)/对齐不良,还有透视等扭曲 行内空格太大 文本距离其他文本太近,尤其是字体大小也不均匀时

我不会发布现成的解决方案或代码,但可以根据我使用 Tesseract 的经验编写我想尝试的内容:

    不要对扫描的图像进行阈值处理,这通常会因为信息丢失而导致效果更差,当不扫描文本但具有光/阴影渐变等的照片时,它更有意义(在这种场景中自适应阈值或其他过滤器+ 阈值效果相对较好)。否则 - 没有理由这样做,Tesseract 会在内部进行一些二值化(这对于闪电/阴影渐变效果很差,因为它不是自适应的,但对扫描的图像却很好)。

    尝试检查它在不同 DPI/图像尺寸下的表现。如果你找到最佳的可能会更好(它更多的是关于旧版本的 Tesseract,目前它不太重要)。

编辑:在opencv中调整大小可以使用:

cv::resize(inImg, outImg, cv::Size(), 0.7, 0.7);

    删除文本周围的矩形可能会有所帮助。

    可以通过线检测或矩形检测或轮廓检测,根据相对于图像宽度的长度/大小(或绝对值,如果它始终相同)进行过滤,并在其上绘制白色以便将其移除。

    编辑:互联网上有多个矩形检测教程。大多数检测和绘制。例如alyssaq / opencv / squares.cpp on Github。您可以检测正方形,然后在 c++ 中按大小过滤它们,然后将它们绘制为白色,这样它应该在黑色上绘制白色并有效地删除它们。

    也可以通过带掩码的复制来完成,但它可能更难编写并且性能更差

    逐行处理可能会有所帮助。如果扫描始终对齐或可以对齐(例如通过测量框的角度),那么您可以按 Y(垂直)制作暗像素数的直方图,并找出线条之间的空间,剪掉这些线条,添加一些白色填充给他们每个人,并一一处理。当然,所有这些都是在删除框线之后。就性能而言,它更糟,但很少会丢失线条。

编辑:对于 Y 上的直方图和查找行之间的空格,请参阅这个问题 Find all peaks for Mat() in OpenCV C++ - 它应该类似地完成,但在其他轴上。

关于裁剪请看这个问题和答案How to crop a CvMat in OpenCV?

添加填充有一个 copyMakeBorder() 方法,请参阅文档中的Adding borders to your images。

    您也可以尝试通过其他方法查找文本在哪里,并单独处理每个字段/单词(这样效率更低,但不太可能删除文本)。然后可以重新连接成行(通过Y匹配成行并按X排序)。

    可能会侵蚀阈值图像以使字母聚集在一起,找到轮廓,过滤它们,获取特定尺寸的字母进行处理,用蒙版剪掉它们,用白色填充,处理每个字母

    编辑:为此,您可能会从此链接中找到有用的问题和答案:Extracting text OpenCV

    可以使用您可见的矩形 - 通过形状检测找到它们的位置、剪切内容、单独处理

    您也可以尝试使用 Tesseract 来获取单词或符号边界框 + 确定性,而不是不太可能丢弃某些文本部分的文本(但它仍然可以这样做)。然后可以自己将框连接成线(如果您的照片不均匀的纸张+不同的字体大小+透视,这是相当困难的问题,但如果您对正常文档进行了良好对齐的扫描,则相当容易)。您可能还需要设置一个阈值来过滤掉可能出现的伪影。

编辑:要找出单词或符号可以使用此代码:

tesseract::ResultIterator *iter = tess.GetIterator();
tesseract::PageIteratorLevel level = tesseract::RIL_WORD; // may use RIL_SYMBOL

if (iter != 0) 
  do 
    const char *word = iter->GetUTF8Text(level);
    float conf = iter->Confidence(level);
    int x1, y1, x2, y2;
    iter->BoundingBox(level, &x1, &y1, &x2, &y2);

    if (word) 
      printf("word: '%s';  \tconfidence: %.2f\t bounding box: [%d,%d,%d,%d]\n", word, conf, x1, y1, x2, y2);

      // ... use that info

      delete[] word;
    
   while (iter->Next(level));

代码未经测试,不同版本的 Tesseract 的正确代码可能会有所不同,这是针对 3.0 的。

    最后但并非最不重要的一点 - 如果不是所有图像都经过良好对齐的扫描,那么当然需要进行一些处理以使其正确对齐和歪斜,如果图像是通过照片而不是扫描仪完成的,您还需要删除渐变/阴影.尽管如此,在示例中,我发现这些扫描相对较好,因此这里不需要(我发现某些字符的打印/复印效果不佳,将很难对那个字符做任何事情)。

编辑:不会为这一点提供示例或链接,因为它是一个非常广泛的主题,并且取决于图像的质量、这些是如何完成的、文本的外观、背景是什么等。

【讨论】:

实际上图像甚至没有被扫描它是从pdf转换的。我会尝试一些过去的建议。虽然我对图像处理不是很熟悉。一些例子真的很有帮助...... 添加了更多信息 BTW 用于商业用途,与 Tesseract 相比,使用云提供商 OCR 作为 SaaS 可能会更好。更好的结果(通常)、更容易集成、更少的编码、不需要预处理,但必须付出一些代价并且失去对系统该部分内部工作的控制。 不幸的是我不能使用商业应用程序。我必须坚持开源。所以上面的这个算法会迭代单词吗? 是的,但非常类似地可能会遍历符号 - 并编写自己的逻辑如何将它们(单词或符号)连接成线,或者根据相对位置或按轴排序等找出什么是什么。跨度>

以上是关于正方体总是缺少图片中的文本行的主要内容,如果未能解决你的问题,请参考以下文章

根据javascript中的文本行数更改textarea的高度[重复]

在 C++ 中计算文本文件中的文本行数时出错

C 语言文件操作 ( 配置文件读写 | 写出或更新配置文件 | 逐行遍历文件文本数据 | 获取文件中的文本行 | 查询文本行数据 | 追加文件数据 | 使用占位符方式拼接字符串 )

如何在 C++ 程序中的 2 个特定字符之间比较 2 个文件中的文本行

win7装visio studio 2005时总是提示无法启动程序,缺少啥文件。

idea中dependencies中总是有红色波浪线(缺少dependency)问题