十、开发用于文本识别的分割算法
在前面的章节中,我们学习了广泛的图像处理技术,如阈值、轮廓描述符和数学形态学。 在本章中,我们将讨论您在处理扫描文档时可能遇到的常见问题,例如识别文本位置或调整其旋转。 我们还将学习如何结合前面章节中介绍的技术来解决这些问题。 在本章结束时,我们将拥有可发送到光学字符识别(OCR)库的文本分段区域。
在本章结束时,您应该能够回答以下问题:
- 存在哪些类型的 OCR 应用?
- 编写 OCR 应用时有哪些常见问题?
- 如何识别文档的区域?
- 如何处理文本中间的歪斜和其他元素等问题?
- 如何使用 Tesseract OCR 识别我的文本?
技术要求
本章要求熟悉基本的 C++ 编程语言。 本章中使用的所有代码都可以从以下 giHub 链接下载:https://github.com/PacktPublishing/Building-Computer-Vision-Projects-with-OpenCV4-and-CPlusPlus/tree/master/Chapter10GitHub。 该代码可以在任何操作系统上执行,尽管它只在 Ubuntu 上进行了测试。
请查看以下视频,了解实际操作中的代码: http://bit.ly/2KIoJFX
光学字符识别简介
识别图像中的文本是计算机视觉中非常流行的应用。 该过程通常被称为光学字符识别,其划分如下:
- 文本预处理和分割:在此步骤中,计算机必须处理图像噪声和旋转(倾斜),并识别哪些区域是候选文本。
- 文本识别:这是识别文本中每个字母的过程,将在后面的章节中介绍。
预处理和分割阶段可能会因文本来源的不同而大不相同。 让我们来看看进行预处理的常见情况:
- 带有扫描仪的生产 OCR 应用:这是一个非常可靠的文本来源。 在这种情况下,图像的背景通常为白色,文档几乎与扫描仪页边距对齐。 被扫描的内容基本上都是文本,几乎没有噪音。 这类应用依赖于简单的预处理技术,可以快速调整文本并保持快速扫描速度。 在编写生产 OCR 软件时,通常会将重要文本区域的标识委托给用户,并创建用于文本验证和索引的质量管道。
- 扫描随意拍摄的照片或视频中的文本:这是一个复杂得多的场景,因为没有指示文本可能在哪里。 此场景称为场景文本识别,OpenCV 4.0 包含一个 Conrib 库来处理它。 我们将在第 11 章,使用 Tesseract 进行文本识别中介绍这一点。 通常,预处理器将使用纹理分析技术来识别文本模式。
- 为历史文本创建制作质量的 OCR:历史文本也会被扫描,但它们有几个额外的问题,例如旧纸张颜色和墨水的使用产生的噪音。 其他常见的问题是装饰字母和特定的文本字体,以及由墨水创建的低对比度内容,这些内容会随着时间的推移而被擦除。 为手头的文档编写特定的 OCR 软件并不少见。
- 扫描地图、图表、和图表:地图、图表和图表构成了一个特别困难的场景,因为文本通常位于图像内容的任何方向和中间。 例如,城市名称通常是群集的,海洋名称通常遵循国家海岸等高线。 有些图表颜色浓重,文本以清晰和深色两种色调显示。
根据识别目标的不同,OCR 应用策略也会有所不同。 它会用于全文搜索吗? 或者,是否应该将文本分成多个逻辑字段,以便用结构化搜索的信息为数据库编制索引?
在本章中,我们将重点介绍对扫描的文本或由相机拍摄的文本进行预处理。 我们将考虑文本是图像的主要目的,例如,在一张照片纸或卡片中,例如,在这张停车罚单中:
我们将尝试删除常见的噪声,处理文本旋转(如果有的话),并裁剪可能的文本区域。 虽然大多数 OCRAPI 已经自动完成了这些工作--可能还使用了最先进的算法--但了解事情是如何在幕后发生的仍然是值得的。 这将使您更好地了解大多数 OCR API 参数,并使您更好地了解可能面临的潜在 OCR 问题。
预处理阶段
识别字母的软件通过将文本与之前记录的数据进行比较来实现这一点。 如果输入的文本清晰,如果字母处于垂直位置,并且没有其他元素(如发送到分类软件的图像),则分类结果可以大大提高。 在本节中,我们将学习如何使用预处理来调整文本。
对图像进行阈值处理
我们通常通过对图像进行阈值处理来开始预处理。 这将消除所有颜色信息。 大多数 OpenCV 函数认为信息是用白色书写的,而背景是黑色的。 因此,让我们首先创建一个阈值函数来匹配此条件:
#include opencv2/opencv.hpp;
#include vector;
using namespace std;
using namespace cv;
Mat binarize(Mat input)
{
//Uses otsu to threshold the input image
Mat binaryImage;
cvtColor(input, input, COLOR_BGR2GRAY);
threshold(input, binaryImage, 0, 255, THRESH_OTSU);
//Count the number of black and white pixels
int white = countNonZero(binaryImage);
int black = binaryImage.size().area() - white;
//If the image is mostly white (white background), invert it
return white black ? binaryImage : ~binaryImage;
}
binarize
函数应用阈值,类似于我们在第 4 章、深入研究直方图和过滤器中所做的操作。 但在这里,我们将通过在函数的第四个参数中传递THRESH_OTSU
来使用 Otsu 方法。Otsu 方法最大化类间方差。 由于阈值仅创建两个类别(黑色和白色像素),因此这与最小化类内方差相同。 此方法使用图像直方图工作。 然后,它迭代所有可能的阈值,并为阈值的每一侧(即图像的背景或前景中的像素)计算像素值的散布。 这个过程的目的是找出两个价差之和最小的阈值。
阈值设置完成后,该函数计算图像中有多少白色像素。 黑色像素就是图像区域给出的图像中的总像素数减去白色像素数。 由于文本通常是在纯背景上书写的,因此我们将验证是否存在更多的白色像素而不是黑色像素。 在本例中,我们处理的是白色背景上的黑色文本,因此我们将反转图像以进行进一步处理。
对停车罚单图像进行阈值处理的结果如下:
文本分割
下一步是找到文本所在的位置并将其提取出来。 为此,有两种常见的策略:
- 使用连通分量分析:搜索图像中的连通像素组。 这将是本章将使用的技术。
- 使用分类器搜索先前训练的字母纹理模式:对于纹理特征,如Haralick 和特征,通常使用小波变换。 另一种选择是在本任务中识别个最稳定的极值区域(MSERs)。 这种方法对于复杂背景中的文本更加健壮,将在第 11 章,使用 Tesseract进行文本识别中进行研究。 你可以在他自己的网站上读到关于哈拉里克的特写,可以在http://haralick.org/journals/TexturalFeatures.pdf上找到。
创建连接区域
如果仔细观察图像,您会注意到字母总是以块的形式排列在一起,由文本段落组成。 这就给我们留下了一个问题,我们如何检测和删除这些块?
第一步是让这些障碍更加明显。 我们可以通过使用膨胀形态运算符来实现这一点。 回想一下第 8 章、视频监控、背景建模、和形态运算,这种膨胀会使图像元素变得更厚。 让我们看一小段能做到这一点的代码片段:
auto kernel = getStructuringElement(MORPH_CROSS, Size(3,3));
Mat dilated;
dilate(input, dilated, kernel, cv::Point(-1, -1), 5);
imshow("Dilated", dilated);
在前面的代码中,我们首先创建一个将在形态学操作中使用的 3x3 交叉内核。 然后,我们以这个内核为中心,进行五次膨胀。 确切的内核大小和次数因情况而异。 只需确保这些值将同一行中的所有字母粘合在一起即可。
此操作的结果显示在以下屏幕截图中:
请注意,我们现在有了巨大的白色方块。 它们与文本的每一段精确匹配,也与其他非文本元素(如图像或边界噪声)匹配。
The ticket image that comes with the code is a low resolution image. OCR engines usually work with high resolution images (200 or 300 DPI), so it may be necessary to apply dilation more than five times.
标识段落块
下一步是执行连接分量分析,以找到与段落对应的块。 OpenCV 具有此功能,我们之前在第 5 章、自动光学检测、对象分割、和检测中使用过。 这是findContours
函数:
vector;vector;Point;contours;
findContours(dilated, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
在第一个参数中,我们传递放大的图像。 第二个参数是检测到的轮廓的向量。 然后,我们使用该选项仅检索外部轮廓并使用简单近似。 图像轮廓如下所示。 每种灰色色调代表不同的轮廓:
最后一步是确定每个轮廓的最小旋转边界矩形。 OpenCV 为此操作提供了一个名为minAreaRect
的方便函数。 此函数接收任意点的向量,并返回包含边界框的RoundedRect
。这也是丢弃不需要的矩形(即明显不是文本的矩形)的好机会。 由于我们正在制作用于 OCR 的软件,我们将假定文本包含一组字母。 在此假设下,我们将在以下情况下丢弃文本:
- 矩形宽度或大小太小,即小于 20 像素。 这将有助于丢弃边界噪声和其他小型人工制品。
- 图像的矩形的宽/高比例小于 2。 也就是说,类似于正方形的矩形(如图像图标)或更高的矩形也将被丢弃。
在第二种情况下有一个小小的警告。 由于我们处理的是旋转的边界框,因此必须测试边界框角度是否小于-45 度。 如果是,文本是垂直旋转的,所以我们必须考虑的比例是高度/宽度。
让我们通过查看以下代码来检查这一点:
//For each contour
vector;RotatedRect; areas;
for (const auto& contour : contours)
{
//Find it's rotated rect
auto box = minAreaRect(contour);
//Discard very small boxes
if (box.size.width 20 || box.size.height 20)
continue;
//Discard squares shaped boxes and boxes
//higher than larger
double proportion = box.angle -45.0 ?
box.size.height / box.size.width :
box.size.width / box.size.height;
if (proportion 2)
continue;
//Add the box
areas.push_back(box);
}
让我们看看该算法选择了哪些框:
这当然是个好结果!
我们应该注意到,在前面的代码中,步骤 2 中描述的算法也将丢弃单个字母。 这不是什么大问题,因为我们正在创建一个 OCR 预处理器,单个符号对于上下文信息通常是没有意义的;页码就是这种情况的一个例子。 在此过程中,页码将被丢弃,因为它们通常单独出现在页面底部,并且文本的大小和比例也会受到干扰。 但这不是问题,因为在文本通过 OCR 之后,您将得到大量的文本文件,根本没有分页。
我们将把所有这些代码放在一个具有以下签名的函数中:
vector RotatedRect; findTextAreas(Mat input)
文本提取和倾斜调整
现在,我们要做的就是提取文本并调整文本倾斜。 这由deskewAndCrop
函数完成,如下所示:
Mat deskewAndCrop(Mat input, const RotatedRect& box)
{
double angle = box.angle;
auto size = box.size;
//Adjust the box angle
if (angle -45.0)
{
angle += 90.0;
std::swap(size.width, size.height);
}
//Rotate the text according to the angle
auto transform = getRotationMatrix2D(box.center, angle, 1.0);
Mat rotated;
warpAffine(input, rotated, transform, input.size(), INTER_CUBIC);
//Crop the result
Mat cropped;
getRectSubPix(rotated, size, box.center, cropped);
copyMakeBorder(cropped,cropped,10,10,10,10,BORDER_CONSTANT,Scalar(0));
return cropped;
}
首先,我们从读取所需的区域角度和大小开始。 正如我们之前看到的,角度可能小于-45 度。 这意味着文本是垂直对齐的,因此我们必须将旋转角度增加 90 度,并切换宽度和高度属性。 接下来,我们需要旋转文本。 首先,我们首先创建一个描述旋转的 2D 仿射变换矩阵。 我们通过使用getRotationMatrix2D
OpenCV 函数来实现这一点。 此函数接受三个参数:
- 中心:旋转的中心位置。 旋转将围绕该中心旋转。 在我们的例子中,我们使用盒子中心。
- 角度:旋转角度。 如果角度为负,则旋转方向为顺时针。
- 比例:各向同性比例因子。 我们将使用
1.0
,因为我们希望保持长方体的原始比例不变。
旋转本身通过使用warpAffine
功能进行。 此函数接受四个必选参数:
- SRC:要转换的输入
mat
数组。 - DST:目标
mat
数组。 - M:变换矩阵。 这个矩阵是 2x3 仿射变换矩阵。 这可以是平移、缩放或旋转矩阵。 在我们的例子中,我们将只使用最近创建的矩阵。
- size:输出图像的大小。 我们将生成一个与输入图像大小相同的图像。
以下是另外三个可选参数:
- 标志:这些标志指示应该如何对图像进行插值。 我们使用
BICUBIC_INTERPOLATION
来获得更好的质量。 默认值为LINEAR_INTERPOLATION
。 - BORDER:边框模式。 我们使用默认值
BORDER_CONSTANT
。 - 边框值:边框的颜色。 我们使用默认设置,即黑色。 然后,我们使用
getRectSubPix
函数。 旋转图像后,需要裁剪边界框的矩形区域。 此函数接受四个强制参数和一个可选参数,并返回裁剪后的图像:- image:要裁剪的图像。
- size:描述要裁剪的框的宽度和高度的
cv::Size
对象。 - Center:要裁剪区域的中心像素。 请注意,因为我们绕中心旋转,所以这个点很方便地是相同的。
- 补丁:目标镜像。
- PATCH_TYPE:目标图像的深度。 我们使用默认值,表示源图像的相同深度。
最后一步由copyMakeBorder
函数完成。 此函数用于在图像周围添加边框。 这一点很重要,因为分类阶段通常要求文本周围留有边距。 函数参数非常简单:输入和输出图像、顶部、底部、左侧和右侧的边框粗细、边框类型和新边框的颜色。
对于卡片图像,将生成以下图像:
现在,是将所有功能组合在一起的时候了。 让我们来介绍执行以下操作的 Main 方法:
- 加载票证图像
- 调用我们的二值化函数
- 查找所有文本区域
- 在窗口中显示每个区域
我们将主要介绍以下方法:
int main(int argc, char* argv[])
{
//Loads the ticket image and binarize it
auto ticket = binarize(imread("ticket.png"));
auto regions = findTextAreas(ticket);
//For each region
for (const auto& region : regions) {
//Crop
auto cropped = deskewAndCrop(ticket, region);
//Show
imshow("Cropped text", cropped);
waitKey(0);
destroyWindow("Border Skew");
}
}
For the complete source code, take a look at the segment.cpp
file that comes with this book.
在操作系统上安装 Tesseract OCR
Tesseract 是一种开源的光学字符识别引擎,最初由惠普实验室布里斯托尔和惠普公司开发。它的所有代码都在阿帕奇许可下获得许可,并托管在 GitHub 的https://github.com/tesseract-ocr上。 它被认为是目前最精确的 OCR 引擎之一:它可以读取各种各样的图像格式,并可以转换用 60 多种语言书写的文本。 在本课程中,我们将教您如何在 Windows 或 Mac 上安装 Tesseract。 因为有很多 Linux 发行版,我们不会教您如何在这个操作系统上安装它。 通常,Tesseract 在您的包存储库中提供安装包,因此,在您自己编译 Tesseract 之前,只需在那里搜索它即可。
在 Windows 上安装 Tesseract
Tesseract 使用C++ 归档网络(CPPAN)作为其依赖项管理器。 要安装 Tesseract,请执行以下步骤。
建造最新的图书馆
- 从https://cppan.org/client/下载最新的 CPPAN 客户端。
- 在命令行中,运行
cppan --build pvt.cppan.demo.google.tesseract.tesseract-master
。
在 Visual Studio 中设置 Tesseract
- 在https://github.com/Microsoft/vcpkg设置 Visual C++ 包管理器
vcpkg
。 - 对于 64 位编译,请使用
vcpkg install tesseract:x64-windows
。 您还可以为主分支添加--head
。
静态链接
还可以在项目中静态链接(https://github.com/tesseract-ocr/tesseract/wiki/Compiling#static-linking)Tesseract。 这将避免将dlls
与您的可执行文件打包在一起。 要执行此操作,请像我们之前所做的那样,将vcpkg
与以下命令一起用于 32 位安装:
vcpkg install tesseract:x86-windows-static
或者,您也可以使用以下命令进行 64 位安装:
vckpg install tesseract:x64-windows-static
在 Mac 上安装 Tesseract
在 Mac 上安装 Tesseract OCR 的最简单方法是使用Homebrew。 如果您还没有安装 Homebrew,只需转到 Homebrew 的站点(Ruby),打开您的控制台,然后运行首页上的http://brew.sh/脚本。 您可能需要输入管理员密码。
安装 HomeBREW 后,只需键入以下内容:
brew install tesseract
英语已包含在此安装中。 如果要安装其他语言包,只需运行以下命令:
brew install tesseract --all-languages
这将安装所有语言包。 然后,只需转到 Tesseract 安装目录并删除任何不需要的语言。 自制软件通常在/usr/local/
目录中安装内容。
使用 Tesseract OCR 库
虽然 Tesseract OCR 已经与 OpenCV 3.0 集成,但它的 API 仍然值得研究,因为它允许对 Tesseract 参数进行更细粒度的控制。 此集成将在第 11 章,与 Tesseract 的文本识别中进行研究。
创建 OCR 功能
我们将更改前面的示例以使用 Tesseract。 首先将tesseract/baseapi.h
和fstream
添加到include
列表:
#include opencv2/opencv.hpp;
#include tesseract/baseapi.h;
#include vector;
#include fstream;
然后,我们将创建一个表示我们的 Tesseract OCR 引擎的全局TessBaseAPI
对象:
tesseract::TessBaseAPI ocr;
The ocr
engine is completely self-contained. If you want to create a multi-threaded piece of OCR software, just add a different TessBaseAPI
object in each thread, and the execution will be fairly thread-safe. You just need to guarantee that file writing is not done over the same file, otherwise you'll need to guarantee safety for this operation.
接下来,我们将创建一个名为Identify Text for(identifyText
)的函数,该函数将运行ocr
:
const char* identifyText(Mat input, const char* language = "eng")
{
ocr.Init(NULL, language, tesseract::OEM_TESSERACT_ONLY);
ocr.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
ocr.SetImage(input.data, input.cols, input.rows, 1, input.step);
const char* text = ocr.GetUTF8Text();
cout "Text:" endl;
cout text endl;
cout "Confidence: " ocr.MeanTextConf() endl;
// Get the text
return text;
}
让我们逐行解释这个函数。 在第一行中,我们从初始化tesseract
开始。 这是通过调用Init
函数来完成的。 此函数具有以下签名:
int Init(const char* datapath, const char* language,
OcrEngineMode oem)
下面我们来解释一下每个参数:
datapath
:这是指向tessdata
个文件的根目录的路径。 路径必须以反斜杠/
字符结束。tessdata
目录包含您安装的语言文件。 将NULL
传递给此参数将使tesseract
搜索其安装目录,这是此文件夹通常所在的位置。 在部署应用时,通常会将此值更改为args[0]
,并在应用路径中包含tessdata
文件夹。language
:这是语言代码的三个字母的单词(例如,英语为英语,葡萄牙语为 POR,印地语为 HIN)。 Tesseract 支持使用+
符号加载多语言代码。 因此,通过eng+por
将同时加载英语和葡萄牙语。 当然,您只能使用以前安装的语言,否则加载过程将失败。 语言配置文件可以指定必须一起加载两种或两种以上语言。 为了防止出现这种情况,您可以使用波浪号~
。 例如,您可以使用hin+~eng
来保证英语不会加载印地语,即使它被配置为这样做。OcrEngineMode
:这些是将使用的 OCR 算法。 它可以具有下列值之一:OEM_TESSERACT_ONLY
:仅使用tesseract
。 这是最快的方法,但精度也较低。OEM_CUBE_ONLY
:使用多维数据集引擎。 它更慢,但更精确。 只有当您的语言经过培训以支持此引擎模式时,这才会起作用。 要检查是否如此,请在tessdata
文件夹中查找您的语言的.cube
文件。 对英语的支持是有保证的。OEM_TESSERACT_CUBE_COMBINED
:这结合了 Tesseract 和 Cube,以实现最佳的 OCR 分类。 该引擎具有最好的精确度和最慢的执行时间。OEM_DEFAULT
:这将根据语言配置文件或命令行配置文件推断策略,如果两者都不存在,则使用OEM_TESSERACT_ONLY
。
需要强调的是,Init
函数可以多次执行。 如果提供了不同的语言或引擎模式,Tesseract 将清除以前的配置并重新启动。 如果提供了相同的参数,则 Tesseract 足够聪明,可以简单地忽略该命令。 函数init
在成功的情况下返回0
,在失败的情况下返回-1
。
然后,我们的程序将继续设置页面分割模式:
ocr.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
有几种可用的分段模式:
PSM_OSD_ONLY
:使用此模式,Tesseract 将只运行其预处理算法来检测方向和脚本检测。PSM_AUTO_OSD
:这告诉 Tesseract 使用方向和脚本检测进行自动页面分割。PSM_AUTO_ONLY
:这会进行页面分割,但会避免进行定向、脚本检测或 OCR。PSM_AUTO
:这会进行页面分割和 OCR,但会避免进行方向或脚本检测。PSM_SINGLE_COLUMN
:这假设可变大小的文本显示在单个列中。PSM_SINGLE_BLOCK_VERT_TEXT
:这会将图像视为垂直对齐的单个统一文本块。PSM_SINGLE_BLOCK
:这假定为单个文本块,并且是默认配置。 我们将使用这个标志,因为我们的预处理阶段保证了这个条件。PSM_SINGLE_LINE
:表示图像仅包含一行文本。PSM_SINGLE_WORD
:表示图像只包含一个单词。PSM_SINGLE_WORD_CIRCLE
:告诉我们图像只是一个排列在圆圈中的单词。PSM_SINGLE_CHAR
:表示图像包含单个字符。
注意,Tesseract 已经实现了去偏斜和文本分割算法,就像大多数 OCR 库一样。 但了解这些算法是很有趣的,因为您可能会为特定需求提供自己的预处理阶段。 这使您可以在许多情况下改进文本检测。 例如,如果要为旧文档创建 OCR 应用,则 Tesseract 使用的默认阈值可能会创建黑色背景。 Tesseract 也可能会被边界或严重的文本歪斜搞混。
接下来,我们使用以下签名调用SetImage
方法:
void SetImage(const unsigned char* imagedata, int width,
int height, int bytes_per_pixel, int bytes_per_line);
这些参数几乎是不言而喻的,并且大多数参数都可以直接从我们的Mat
对象中读取:
data
:包含图像数据的原始字节数组。 OpenCV 在Mat
类中包含一个名为data()
的函数,该函数提供指向数据的直接指针。width
:图像宽度。height
:图像高度。bytes_per_pixel
:每个像素的字节数。 我们使用的是1
,因为我们处理的是二进制图像。 如果希望代码更通用,还可以使用Mat::elemSize()
函数,该函数提供相同的信息。bytes_per_line
:单行中的字节数。 我们使用Mat::step
属性,因为有些图像会添加尾随字节。
然后,我们调用GetUTF8Text
来运行识别本身。 返回识别出的文本,使用 UTF8 编码,不带 BOM。 在返回它之前,我们还打印一些调试信息。
MeanTextConf
返回置信度指数,该指数可以是介于0
到100
之间的一个数字:
auto text = ocr.GetUTF8Text();
cout "Text:" endl;
cout text endl;
cout "Confidence: " ocr.MeanTextConf() endl;
将输出发送到文件
让我们更改 Main 方法,将识别的输出发送到文件。 为此,我们使用标准的ofstream
:
int main(int argc, char* argv[])
{
//Loads the ticket image and binarize it
Mat ticket = binarize(imread("ticket.png"));
auto regions = findTextAreas(ticket);
std::ofstream file;
file.open("ticket.txt", std::ios::out | std::ios::binary);
//For each region
for (const auto& region : regions) {
//Crop
auto cropped = deskewAndCrop(ticket, region);
auto text = identifyText(cropped, "por");
file.write(text, strlen(text));
file endl;
}
file.close();
}
以下行以二进制模式打开文件:
file.open("ticket.txt", std::ios::out | std::ios::binary);
这一点很重要,因为 Tesseract 返回以 UTF-8 编码的文本,并考虑了 Unicode 中提供的特殊字符。 我们还使用以下命令直接编写输出:
file.write(text, strlen(text));
在此示例中,我们使用葡萄牙语作为输入语言(这是票证编写时使用的语言)调用identify
函数。 如果你愿意,你可以用另一张照片。
The complete source file is provided in the segmentOcr.cpp
file, which comes with this book. ticket.png
is a low resolution image, since we imagined you would want to display a window with the image while studying this code. For this image, the Tesseract results are rather poor. If you want to test with a higher resolution image, the code for this book provides you with a ticketHigh.png
image. To test with this image, change the dilation repetitions to 12
and the minimum box size from 20
to 60
. You'll get a much higher confidence rate (about 87%), and the resulting text will be almost fully readable. The segmentOcrHigh.cpp
file contains these modifications.
简略的 / 概括的 / 简易判罪的 / 简易的
在本章中,我们简要介绍了 OCR 应用。 我们看到,这类系统的预处理阶段必须根据我们计划识别的文档类型进行调整。 我们了解了预处理文本文件时的常见操作,如阈值、裁剪、倾斜和文本区域分割。 最后,我们学习了如何安装和使用 Tesseract OCR 将图像转换为文本。
在下一章中,我们将使用更复杂的 OCR 技术来识别随意拍摄的图片或视频中的文本-这种情况称为场景文本识别。 这是一个复杂得多的场景,因为文本可以在任何地方,使用任何字体,并且具有不同的照明和方向。 甚至可以根本没有文字! 我们还将学习如何使用 OpenCV 3.0 文本贡献模块,该模块与 Tesseract 完全集成。