Tesseract 总是缺少图片中的文本行
Tesseract always missing a text line in picture
我正在尝试使用 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 本身还是应该更改图像的预处理?
Tesseract 在几种情况下有掉线或掉文本片段的倾向:
- 有一些非文本的东西干扰(线条、人工制品、光照渐变)
- 有太多东西没有被足够确定地识别为字符
- 线条不均匀(颠簸)/排列不当,还有透视等扭曲
- 行内空格太大
- 文字太靠近其他文字,尤其是字体大小不均匀时
我不会 post 现成的解决方案或代码,但可以根据我使用 Tesseract 的经验编写我想尝试的内容:
不要对扫描图像进行阈值处理,因为信息丢失通常会使效果更差,当没有扫描文本但具有 light/shadow 渐变等的照片时,它更有意义(在这样的情况下)场景自适应阈值或其他过滤器+阈值效果相对较好)。否则 - 没有理由这样做,Tesseract 在内部做了一些二值化(这对 lightning/shadows 渐变效果很差,因为它不是自适应的,但对扫描图像很好)。
尝试检查不同的 DPI/图像尺寸如何。如果你找到最优的可能会更好(它更多的是关于旧版本的 Tesseract,在当前它不太重要)。
编辑:要在 opencv 中调整大小可以使用:
cv::resize(inImg, outImg, cv::Size(), 0.7, 0.7);
删除文本周围的矩形可能会有所帮助。
- 它可以通过线检测或矩形检测或轮廓检测来完成,通过 length/size 相对于图像宽度(或绝对值,如果它始终相同)进行过滤并在其上绘制白色以将其删除。
编辑:互联网上有多个矩形检测教程。大多数检测和绘制。例如 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。
您也可以尝试通过其他方法查找文本的位置,然后单独处理每个 field/word(效率更低,但不太可能丢失文本)。然后可以连接回线(通过按 Y 匹配成线并按 X 排序)。
- 可能会侵蚀阈值图像以使字母聚集在一起,找到轮廓,过滤它们,取特定尺寸的进行处理,用蒙版剪掉它们,用白色填充它们,处理每个
编辑:为此,您可能会从这个 link 中找到有用的问题和答案:Extracting text OpenCV
- 可以使用您可见的矩形 - 通过形状检测找到它们的位置,剪切内容,单独处理
您也可以尝试使用 Tesseract 来获取文字或符号边界框 + 确定性而不是文本,这不太可能丢失文本的某些部分(但它仍然可以做到这一点)。然后可以自己将框连接成线(如果您的照片纸张不均匀 sheet + 不同的字体大小 + 透视,这将是一个相当困难的问题,但如果您对正常文档进行良好对齐的扫描,则相当容易)。您可能还需要设置一个阈值来过滤掉可能出现的伪影。
编辑:要找出单词或符号可以使用此代码:
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 的。
- 最后但并非最不重要 - 如果并非所有图像都经过良好对齐的扫描,那么当然需要进行一些处理以使其良好对齐和校正,如果图像是由照片而不是扫描仪。尽管如此,在示例中我看到那些扫描效果相对较好,所以这里不需要(我看到一些字符的问题不是 printed/xero-ed 好,很难对那个做任何事情)。
编辑:不会为这一点提供示例或 links,因为它是一个非常广泛的主题,取决于图像的质量、这些是如何完成的、文本的外观、背景是什么等。
我的参考是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
OpenCV Version 通过pkg-config --modversion 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`
我正在尝试使用 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 本身还是应该更改图像的预处理?
Tesseract 在几种情况下有掉线或掉文本片段的倾向:
- 有一些非文本的东西干扰(线条、人工制品、光照渐变)
- 有太多东西没有被足够确定地识别为字符
- 线条不均匀(颠簸)/排列不当,还有透视等扭曲
- 行内空格太大
- 文字太靠近其他文字,尤其是字体大小不均匀时
我不会 post 现成的解决方案或代码,但可以根据我使用 Tesseract 的经验编写我想尝试的内容:
不要对扫描图像进行阈值处理,因为信息丢失通常会使效果更差,当没有扫描文本但具有 light/shadow 渐变等的照片时,它更有意义(在这样的情况下)场景自适应阈值或其他过滤器+阈值效果相对较好)。否则 - 没有理由这样做,Tesseract 在内部做了一些二值化(这对 lightning/shadows 渐变效果很差,因为它不是自适应的,但对扫描图像很好)。
尝试检查不同的 DPI/图像尺寸如何。如果你找到最优的可能会更好(它更多的是关于旧版本的 Tesseract,在当前它不太重要)。
编辑:要在 opencv 中调整大小可以使用:
cv::resize(inImg, outImg, cv::Size(), 0.7, 0.7);
删除文本周围的矩形可能会有所帮助。
- 它可以通过线检测或矩形检测或轮廓检测来完成,通过 length/size 相对于图像宽度(或绝对值,如果它始终相同)进行过滤并在其上绘制白色以将其删除。
编辑:互联网上有多个矩形检测教程。大多数检测和绘制。例如 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。
您也可以尝试通过其他方法查找文本的位置,然后单独处理每个 field/word(效率更低,但不太可能丢失文本)。然后可以连接回线(通过按 Y 匹配成线并按 X 排序)。
- 可能会侵蚀阈值图像以使字母聚集在一起,找到轮廓,过滤它们,取特定尺寸的进行处理,用蒙版剪掉它们,用白色填充它们,处理每个
编辑:为此,您可能会从这个 link 中找到有用的问题和答案:Extracting text OpenCV
- 可以使用您可见的矩形 - 通过形状检测找到它们的位置,剪切内容,单独处理
您也可以尝试使用 Tesseract 来获取文字或符号边界框 + 确定性而不是文本,这不太可能丢失文本的某些部分(但它仍然可以做到这一点)。然后可以自己将框连接成线(如果您的照片纸张不均匀 sheet + 不同的字体大小 + 透视,这将是一个相当困难的问题,但如果您对正常文档进行良好对齐的扫描,则相当容易)。您可能还需要设置一个阈值来过滤掉可能出现的伪影。
编辑:要找出单词或符号可以使用此代码:
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 的。
- 最后但并非最不重要 - 如果并非所有图像都经过良好对齐的扫描,那么当然需要进行一些处理以使其良好对齐和校正,如果图像是由照片而不是扫描仪。尽管如此,在示例中我看到那些扫描效果相对较好,所以这里不需要(我看到一些字符的问题不是 printed/xero-ed 好,很难对那个做任何事情)。
编辑:不会为这一点提供示例或 links,因为它是一个非常广泛的主题,取决于图像的质量、这些是如何完成的、文本的外观、背景是什么等。
我的参考是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
OpenCV Version 通过pkg-config --modversion 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`