共计 9866 个字符,预计需要花费 25 分钟才能阅读完成。
by Adrian Rosebrock on August 20, 2018 in Deep Learning, Optical Character Recognition (OCR), Tutorials 点击这里下载这篇文章的源代码
在本教程中,您将学习如何使用 EAST 文本检测器使用 OpenCV 检测自然场景图像中的文本。OpenCV 的 EAST 文本检测器是一种基于新颖架构和训练模式的深度学习模型。它能够(1)在 720p 图像上以 13 FPS 接近实时运行,并且(2)获得最先进的文本检测精度。在本教程的其余部分,您将学习如何使用 OpenCV 的 EAST 检测器自动检测图像和视频流中的文本。
在本教程中,您将学习如何使用 EAST 文本检测器使用 OpenCV 检测图像中的文本。EAST 文本检测器要求我们在我们的系统上运行 OpenCV 3.4.2 或 OpenCV 4 – 如果您还没有安装 OpenCV 3.4.2 或更高版本,请参阅我的 OpenCV 安装指南并按照各自的操作系统进行操作。在今天的教程的第一部分中,我将讨论为什么在自然场景图像中检测文本会如此具有挑战性。从那里我将简要讨论 EAST 文本检测器,我们为什么使用它,以及使算法如此新颖的原因 – 我还将包含原始论文的链接,以便您可以阅读详细信息,如果您如此倾向。最后,我将提供我的 Python + OpenCV 文本检测实现,以便您可以开始在自己的应用程序中应用文本检测。
为什么自然场景文本检测如此具有挑战性
图 1:自然场景图像的示例,其中文本检测由于光照条件,图像质量和非平面物体而具有挑战性(Mancas-Thillou 和 Gosselin 的图 1)。
在受约束的受控环境中检测文本通常可以通过使用基于启发式的方法来完成,例如利用梯度信息或者文本通常被分组成段落并且字符出现在直线上的事实。在我之前关于检测护照图像中的机器可读区域的博客文章中可以看到这种基于启发式的文本检测器的示例。自然场景文本检测虽然不同 – 而且更具挑战性。由于廉价数码相机的普及,更不用说几乎每部智能手机现在都有摄像头这一事实,我们需要高度关注拍摄图像的条件 – 此外,我们能做什么和不能做什么假设。我已经包含了 Celine Mancas-Thillou 和 Bernard Gosselin 在 2017 年优秀论文“自然场景文本理解”中描述的自然场景文本检测挑战的总结版本:. 图像 / 传感器噪声:手持式摄像机的传感器噪声通常高于传统扫描仪的噪声。此外,低价相机通常会插入原始传感器的像素以产生真实的颜色。. 视角:自然场景文本自然可以具有与文本不平行的视角,使文本更难识别。. 模糊:不受控制的环境往往会模糊,特别是如果最终用户使用的智能手机没有某种形式的稳定性。. 照明条件:我们无法对自然场景图像中的照明条件做出任何假设。它可能接近黑暗,相机上的闪光灯可能会亮起,或者太阳可能会发出明亮的光线,使整个图像饱和。. 分辨率:并非所有摄像机都是相同的 – 我们可能在处理分辨率低于标准的相机。. 非纸质对象:大多数(但不是全部)纸张都不是反光的(至少在您尝试扫描的纸张环境中)。自然场景中的文字可能是反光的,包括徽标,标志等。. 非平面物体:考虑当您将文本环绕在瓶子周围时会发生什么 – 表面上的文本变得扭曲和变形。虽然人类仍然可以轻松地“检测”并阅读文本,但我们的算法会很困难。我们需要能够处理这样的用例。. 未知布局:我们不能使用任何先验信息来为我们的算法提供关于文本所在位置的“线索”。
正如我们将要了解的那样,OpenCV 的 EAST 文本检测器实现非常强大,即使文本模糊,反射或部分模糊,也能够对文本进行本地化:
图 2:OpenCV 的 EAST 场景文本检测器即使在模糊和模糊的图像中也能检测到。
EAST 深度学习文本检测器
图 3:EAST 文本检测全卷积网络的结构(Zhou 等人的图 3)。
随着 OpenCV 3.4.2 和 OpenCV 4 的发布,我们现在可以使用一种名为 EAST 的基于深度学习的文本检测器,它基于 Zhou 等人的 2017 年论文“EAST:一种高效精确的场景文本检测器”。我们将算法称为“EAST”,因为它是:高效且准确的场景文本检测管道。据作者说,EAST 管道能够在 720p 图像上以任意方向预测文本的单词和行,并且能够以 13 FPS 运行。也许最重要的是,由于深度学习模型是端到端的,因此可以回避其他文本检测器通常应用的计算上昂贵的子算法,包括候选聚合和字分区。为了构建和训练这样一种深度学习模型,EAST 方法采用了新颖,精心设计的损失函数。有关 EAST 的更多详细信息,包括架构设计和培训方法,请务必参考作者的出版物。
项目结构
首先,请访问“下载”部分,确保将源代码 + 图像抓取到今天的帖子。从那里,只需使用 tree terminal 命令查看项目结构:
$ tree –dirsfirst
.
├── images
│ ├── car_wash.png
│ ├── lebron_james.jpg
│ └── sign.jpg
├── frozen_east_text_detection.pb
├── text_detection.py
└── text_detection_video.py
1 directory, 6 files
请注意,我在 images / 目录中提供了三张示例图片。您可能希望添加自己使用智能手机收集的图像或在线查找的图像。我们今天将审查两个.py 文件:text_detection.py:检测静态图像中的文本。text_detection_video.py:通过网络摄像头或输入视频文件检测文本。
这两个脚本都使用了序列化的 EAST 模型(frozen_east_text_detection.pb),以便在“下载”中提供。
实施说明
我今天包含的文本检测实现基于 OpenCV 的官方 C ++ 示例; 但是,我必须承认在将其转换为 Python 时遇到了一些麻烦。首先,Python 中没有 Point2f 和 RotatedRect 函数,因此,我无法 100%模仿 C ++ 实现。C ++ 实现可以生成旋转的边界框,但不幸的是,我今天与你分享的那个不能。其次,NMSBoxes 函数不返回 Python 绑定的任何值(至少对于我的 OpenCV 4 预发布安装),最终导致 OpenCV 抛出错误。NMSBoxes 函数可以在 OpenCV 3.4.2 中工作,但我无法对其进行详尽的测试。我解决了这个问题,我在 imutils 中使用我自己的非最大值抑制实现,但同样,我不相信这两个是 100%可互换的,因为看起来 NMSBoxes 接受其他参数。鉴于这一切,我尽力使用我的工作功能和资源为您提供最好的 OpenCV 文本检测实现。如果您对方法有任何改进,请随时在下面的评论中分享。
使用 OpenCV 实现我们的文本检测器
在我们开始之前,我想指出您至少需要在系统上安装 OpenCV 3.4.2(或 OpenCV 4)才能使用 OpenCV 的 EAST 文本检测器,因此如果您尚未安装 OpenCV 3.4.2 或更高版本在您的系统上,请参阅我的 OpenCV 安装指南。
接下来,确保您的系统上还安装 / 升级了 imutils:pip install –upgrade imutils 此时您的系统已配置好,因此打开 text_detection.py 并插入以下代码:
首先,我们在第 2 - 6 行导入我们所需的包和模块。值得注意的是,我们从 imutils.object_detection 导入 NumPy,OpenCV 和我的 non_max_suppression 实现。
然后我们继续解析第 9 -20 行的五个命令行参数:–image:输入图像的路径。–east:EAST 场景文本检测器模型文件路径。–min-confidence:确定文本的概率阈值。可选,默认值 = 0.5。–width:调整后的图像宽度 – 必须是 32 的倍数。可选,默认值 = 320。–height:调整后的图像高度 – 必须是 32 的倍数。可选,默认值 = 320。
重要提示:EAST 文本要求输入图像尺寸为 32 的倍数,因此如果您选择调整 –width 和 –height 值,请确保它们是 32 的倍数!
从那里,让我们加载我们的图像并调整它的大小:
在第 23 和 24 行,我们加载并复制输入图像。
从那里,第 30 行和第 31 行确定原始图像尺寸与新图像尺寸的比率(基于为 –width 和 –height 提供的命令行参数)。
然后我们调整图像大小,忽略纵横比(第 34 行)。
为了使用 OpenCV 和 EAST 深度学习模型执行文本检测,我们需要提取两层的输出特征映射:
我们在 40-42 行构建一个 layerNames 列表:第一层是我们的输出 sigmoid 激活,它给出了包含文本或不包含文本的区域的概率。第二层是表示图像“几何”的输出要素图 – 我们将能够使用此几何来导出输入图像中文本的边界框坐标
让我们加载 OpenCV 的 EAST 文本检测器:
我们使用 cv2.dnn.readNet 将神经网络加载到内存中,方法是将路径传递给 EAST 检测器(包含在我们的命令行 args 字典中)作为第 46 行的参数。
然后我们通过将其转换为第 50 行和第 51 行的 blob 来准备我们的图像。要了解有关此步骤的更多信息,请参阅深度学习:OpenCV 的 blobFromImage 如何工作。
要预测文本,我们可以简单地将 blob 设置为输入并调用 net.forward(第 53 和 54 行)。这些行被抓取时间戳包围,以便我们可以在第 58 行打印经过的时间。
通过将 layerNames 作为参数提供给 net.forward,我们正在指示 OpenCV 返回我们感兴趣的两个特征映射:输出几何图用于导出输入图像中文本的边界框坐标类似地,分数图包含包含文本的给定区域的概率
我们需要逐个遍历每个值:
我们首先抓住得分量的维度(第 63 行),然后初始化两个列表:rects:存储文本区域的边界框(x,y)– 坐标置信度:存储与 rects 中每个边界框关联的概率
我们稍后将对这些区域应用非最大值抑制。
在第 68 行开始循环。
第 72-77 行提取当前行的分数和几何数据 y。
接下来,我们遍历当前所选行的每个列索引:
对于每一行,我们开始循环第 80 行的列。
我们需要通过忽略不具有足够高概率的区域来过滤掉弱文本检测(第 82 和 83 行)。
当文本通过网络时,EAST 文本检测器自然地减小了体积大小 – 我们的体积大小实际上比输入图像小 4 倍,所以我们乘以 4 使坐标回到原始图像的方向。
我已经介绍了如何在第 91-93 行提取角度数据; 但是,正如我在上一节中所提到的,我无法像在 C ++ 实现中那样构建一个旋转的边界框 – 如果你想要处理任务,从第 91 行的角度开始将是你的第一个步。
从那里,第 97-105 行导出文本区域的边界框坐标。
然后,我们分别更新我们的 rects 和 confidences 列表(第 109 和 110 行)。
我们差不多完成了!
最后一步是将非最大值抑制应用于我们的边界框以抑制弱重叠边界框,然后显示结果文本预测:
正如我在上一节中提到的,我无法在我的 OpenCV 4 安装(cv2.dnn.NMSBoxes)中使用非最大值抑制,因为 Python 绑定没有返回值,最终导致 OpenCV 错误输出。我无法完全在 OpenCV 3.4.2 中进行测试,因此它可以在 v3.4.2 中运行。
相反,我使用了 imutils 包中提供的非最大值抑制实现(第 114 行)。结果仍然很好; 但是,我无法将输出与 NMSBoxes 函数进行比较,看看它们是否相同。
第 117-126 行环绕我们的边界框,将坐标缩放回原始图像尺寸,并将输出绘制到我们的原始图像。显示原始图像,直到按下一个键(第 129 和 130 行)。
作为最后的实现说明,我想提一下,我们的两个嵌套 for 循环用于循环第 68-110 行的分数和几何体积,这将是一个很好的例子,你可以利用 Cython 来大大加速你的管道。我已经展示了 Cython 在 Fast 中的强大功能,使用 OpenCV 和 Python 优化了 ’for’ 像素循环。OpenCV 文本检测结果
你准备好对图像应用文本检测了吗?
首先抓住此博客文章的“下载”并解压缩文件。
从那里,您可以在终端中执行以下命令(记下两个命令行参数):
$ python text_detection.py –image images/lebron_james.jpg \
–east frozen_east_text_detection.pb
[INFO] loading EAST text detector…
[INFO] text detection took 0.142082 seconds
您的结果应类似于以下图像:
图 4:着名的篮球运动员,Lebron James 的球衣文字通过 OpenCV 和 EAST 文本检测成功识别。
勒布朗詹姆斯确定了三个文本区域。
现在让我们尝试检测商业标志的文字:
$ python text_detection.py –image images/car_wash.png \
–east frozen_east_text_detection.pb
[INFO] loading EAST text detector…
[INFO] text detection took 0.142295 seconds
图 5:在洗车站的这个自然场景中使用 EAST 和 Python 以及 OpenCV 可以轻松识别文本。
最后,我们将尝试一个路标:
$ python text_detection.py –image images/sign.jpg \
–east frozen_east_text_detection.pb
[INFO] loading EAST text detector…
[INFO] text detection took 0.141675 seconds
图 6:使用 Python + OpenCV 进行场景文本检测,EAST 文本检测器成功检测到此西班牙语停止标志上的文本。
此场景包含西班牙停车标志。OpenCV 和 EAST 正确检测到“ALTO”这个词。
正如您所知,EAST 非常准确且相对较快,每张图像的平均时间约为 0.14 秒。
使用 OpenCV 在视频中进行文本检测
现在我们已经看到了如何检测图像中的文本,让我们继续使用 OpenCV 检测视频中的文本。
这个解释非常简短; 有关详细信息,请参阅上一节。
打开 text_detection_video.py 并插入以下代码:
# import the necessary packages
from imutils.video import VideoStream
from imutils.video import FPS
from imutils.object_detection import non_max_suppression
import numpy as np
import argparse
import imutils
import time
import cv2
我们首先导入我们的包。我们将使用 VideoStream 访问网络摄像头和 FPS 以对此脚本的每秒帧数进行基准测试。其他所有内容与上一节中的相同。
为方便起见,让我们定义一个新函数来解码我们的预测函数 – 它将被重用于每个帧并使我们的循环更清晰
def decode_predictions(scores, geometry):
# grab the number of rows and columns from the scores volume, then
# initialize our set of bounding box rectangles and corresponding
# confidence scores
(numRows, numCols) = scores.shape[2:4]
rects = []
confidences = []
# loop over the number of rows
for y in range(0, numRows):
# extract the scores (probabilities), followed by the
# geometrical data used to derive potential bounding box
# coordinates that surround text
scoresData = scores[0, 0, y]
xData0 = geometry[0, 0, y]
xData1 = geometry[0, 1, y]
xData2 = geometry[0, 2, y]
xData3 = geometry[0, 3, y]
anglesData = geometry[0, 4, y]
# loop over the number of columns
for x in range(0, numCols):
# if our score does not have sufficient probability,
# ignore it
if scoresData[x] < args[“min_confidence”]:
continue
# compute the offset factor as our resulting feature
# maps will be 4x smaller than the input image
(offsetX, offsetY) = (x * 4.0, y * 4.0)
# extract the rotation angle for the prediction and
# then compute the sin and cosine
angle = anglesData[x]
cos = np.cos(angle)
sin = np.sin(angle)
# use the geometry volume to derive the width and height
# of the bounding box
h = xData0[x] + xData2[x]
w = xData1[x] + xData3[x]
# compute both the starting and ending (x, y)-coordinates
# for the text prediction bounding box
endX = int(offsetX + (cos * xData1[x]) + (sin * xData2[x]))
endY = int(offsetY – (sin * xData1[x]) + (cos * xData2[x]))
startX = int(endX – w)
startY = int(endY – h)
# add the bounding box coordinates and probability score
# to our respective lists
rects.append((startX, startY, endX, endY))
confidences.append(scoresData[x])
# return a tuple of the bounding boxes and associated confidences
return (rects, confidences)
我们定义了 decode_predictions 函数。此函数用于提取:文本区域的边界框坐标以及文本区域检测的概率
这个专用函数将使代码在以后的脚本中更易于阅读和管理。
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument(“-east”, “–east”, type=str, required=True,
help=”path to input EAST text detector”)
ap.add_argument(“-v”, “–video”, type=str,
help=”path to optinal input video file”)
ap.add_argument(“-c”, “–min-confidence”, type=float, default=0.5,
help=”minimum probability required to inspect a region”)
ap.add_argument(“-w”, “–width”, type=int, default=320,
help=”resized image width (should be multiple of 32)”)
ap.add_argument(“-e”, “–height”, type=int, default=320,
help=”resized image height (should be multiple of 32)”)
args = vars(ap.parse_args())
我们的命令行参数解析:–east:EAST 场景文本检测器模型文件路径。–video:输入视频的路径。可选 – 如果提供了视频路径,则不会使用网络摄像头。–min-confidence:确定文本的概率阈值。可选,默认值 = 0.5。–width:调整后的图像宽度(必须是 32 的倍数)。可选,默认值 = 320。–height:调整后的图像高度(必须是 32 的倍数)。可选,默认值 = 320。
上一节中仅使用图像的脚本(就命令行参数而言)的主要变化是我用 –video 替换了 –image 参数。
重要提示:EAST 文本要求输入图像尺寸为 32 的倍数,因此如果您选择调整 –width 和 –height 值,请确保它们是 32 的倍数!
接下来,我们将执行模仿前一个脚本的重要初始化:
第 84-86 行的高度 / 宽度和比率初始化将允许我们稍后正确地缩放我们的边界框。
定义了输出层名称,我们在第 91-97 行加载了预训练的 EAST 文本检测器。
以下块设置我们的视频流和每秒帧数计数器:
我们的视频流设置为:网络摄像头(100-103 行)或视频文件(第 106-107 行)
从那里我们初始化我们在第 110 行的每秒帧数,并开始循环传入帧:
我们开始在 113 行的视频 / 网络摄像头帧上循环。
我们的框架调整大小,保持纵横比(第 124 行)。从那里,我们抓住尺寸并计算比例比(第 129-132 行)。然后我们再次调整框架的大小(必须是 32 的倍数),这次忽略纵横比,因为我们已经存储了安全保持的比率(第 135 行)。
预测和绘图文本区域边界框发生在以下行:
在这个块中我们:使用 EAST 通过创建 blob 并将其传递通过网络来检测文本区域(第 139-142 行)解码预测并应用 NMS(第 146 和 147 行)。我们使用此脚本中先前定义的 decode_predictions 函数和我的 imutils non_max_suppression 便利函数。环绕边界框并在框架上绘制它们(第 150-159 行)。这涉及通过先前收集的比率来缩放框。
从那里我们将关闭帧处理循环以及脚本本身:
我们在循环的每次迭代中更新我们的 fps 计数器(第 162 行),以便在我们突破循环时计算并显示计时(第 173-175 行)。
我们在 165 行显示 EAST 文本检测的输出并处理按键(第 166-170 行)。如果按“q”进行“退出”,我们就会跳出循环并继续清理并释放指针。
视频文本检测结果
要使用 OpenCV 将文本检测应用于视频,请务必使用此博客文章的“下载”部分。
从那里,打开一个终端并执行以下命令(这将启动你的网络摄像头,因为我们没有通过命令行参数提供 –video):
$ python text_detection_video.py –east frozen_east_text_detection.pb
[INFO] loading EAST text detector…
[INFO] starting video stream…
[INFO] elasped time: 59.76
[INFO] approx. FPS: 8.85
我们的 OpenCV 文本检测视频脚本可实现 7 -9 FPS。
这个结果并不像作者报道的那么快(13 FPS); 但是,我们使用的是 Python 而不是 C ++。通过使用 Cython 优化 for 循环,我们应该能够提高文本检测管道的速度。
摘要
在今天的博客文章中,我们学习了如何使用 OpenCV 的新 EAST 文本检测器来自动检测自然场景图像中是否存在文本。
文本检测器不仅准确,而且能够在 720p 图像上以近似实时的速度运行,大约 13 FPS。
为了提供 OpenCV 的 EAST 文本检测器的实现,我需要转换 OpenCV 的 C ++ 示例; 然而,我遇到了许多挑战,例如:无法使用 OpenCV 的 NMSBox 进行非最大值抑制,而是必须使用 imutils 的实现。由于缺少 RotatedRect 的 Python 绑定,无法计算真正的旋转边界框。
我试图让我的实现尽可能接近 OpenCV,但请记住,我的版本与 C ++ 版本没有 100%完全相同,并且可能会有一两个小问题需要随着时间的推移而解决。
无论如何,我希望你喜欢今天的 OpenCV 文本检测教程!