Python+Opencv⽂本检测的实现
⽬录
EAST 深度学习⽂本检测器
项⽬结构
实施说明
使⽤ OpenCV 实现我们的⽂本检测器
OpenCV ⽂本检测结果
视频⽂字检测结果
在本教程中,您将学习如何使⽤ OpenCV 使⽤ EAST ⽂本检测器检测图像中的⽂本。
EAST ⽂本检测器要求我们在我们的系统上运⾏ OpenCV 3.4.2 或 OpenCV 4 。
在今天教程的第⼀部分中,我将讨论为什么在⾃然场景图像中检测⽂本会如此具有挑战性。从那⾥我将简
要讨论 EAST ⽂本检测器,我们为什么使⽤它,以及是什么让算法如此新颖——我还将提供原始论⽂的链接,以便您可以阅读详细信息,如果您愿意的话。
最后,我将提供我的 Python + OpenCV ⽂本检测实现,以便您可以开始在⾃⼰的应⽤程序中应⽤⽂本检测。
为什么⾃然场景⽂本检测如此具有挑战性?
在受约束的受控环境中检测⽂本通常可以通过使⽤基于启发式的⽅法来完成,例如利⽤梯度信息或⽂本通常被分组为段落并且字符出现在⼀条直线上的事实。
然⽽,⾃然场景⽂本检测是不同的——⽽且更具挑战性。由于廉价数码相机的普及,更不⽤说现在⼏乎每部智能⼿机都配备了相机这⼀事实,我们需要⾼度关注拍摄图像的条件——此外,我们可以做出哪些假设,哪些不可⾏。我在 Celine Mancas-Thillou 和 Bernard Gosselin 的 2017 年优秀论⽂《⾃然场景⽂本理解》中描述了⾃然场景⽂本检测挑战的总结版本,如下所⽰:
图像/传感器噪声:⼿持相机的传感器噪声通常⾼于传统扫描仪的噪声。此外,低价相机通常会插⼊原始传感器的像素以产⽣真实的颜⾊。
视⾓:⾃然场景⽂本⾃然会有与⽂本不平⾏的视⾓,使⽂本更难识别。
模糊:不受控制的环境往往会模糊,特别是如果最终⽤户使⽤的智能⼿机没有某种形式的稳定性。
光照条件:我们⽆法对⾃然场景图像中的光照条件做出任何假设。可能接近⿊暗,相机上的闪光灯可能打开,或者太阳可能很耀眼,使整个图像饱和。
分辨率:并⾮所有相机都是⼀样的——我们可能会处理分辨率低于标准的相机。
⾮纸质物体:⼤多数(但不是全部)纸张不具有反射性(⾄少在您尝试扫描的纸张环境中)。⾃然场景中的⽂本可能具有反射性,包括徽标、标志等。
⾮平⾯对象:考虑将⽂本环绕在瓶⼦周围时会发⽣什么 - 表⾯上的⽂本会扭曲变形。虽然⼈类可能仍然能够轻松“检测”和阅读⽂本,但我们的算法将⾯临困难。我们需要能够处理这样的⽤例。
未知布局:我们不能使⽤任何先验信息来为我们的算法提供有关⽂本所在位置的“线索”。
EAST 深度学习⽂本检测器
随着 OpenCV 3.4.2 和 OpenCV 4 的发布,我们现在可以使⽤名为 EAST 的基于深度学习的⽂本检测器,该检测器基于 Zhou 等⼈ 2017 年的论⽂ EAST: An Efficient and Accurate Scene Text Detector。
我们称该算法为“EAST”,因为它是⼀个:⾼效且准确的场景⽂本检测管道。
这组作者说,EAST 管道能够预测 720p 图像上任意⽅向的单词和⽂本⾏,⽽且可以以 13 FPS 的速度运⾏。也许最重要的是,由于深度学习模型是端到端的,因此可以避开其他⽂本检测器通常应⽤的计算成本⾼的⼦算法,包括候选聚合和单词分区。
为了构建和训练这样⼀个深度学习模型,EAST ⽅法利⽤了新颖、精⼼设计的损失函数。有关 EAST 的更多详细信息,包括架构设计和训练⽅法,请务必参阅作者的出版物。
项⽬结构
$ tree --dirsfirst
.
├── images
│├── car_wash.png
│├── lebron_james.jpg
│└── sign.jpg
├── frozen_east_text_detection.pb
├── text_detection.py
└── text_detection_video.py
请注意,我在 images/ ⽬录中提供了三张⽰例图⽚。您可能希望添加⾃⼰的智能⼿机收集的图像或您在⽹上到的图像。我们今天将审查两个 .py ⽂件:
text_detection.py :检测静态图像中的⽂本。
text_detection_video.py :通过⽹络摄像头或输⼊视频⽂件检测⽂本。
实施说明
我今天包含的⽂本检测实现基于 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 ⽂本检测器,接下来,确保您的系统上也安装/升级了 imutils:
pip install --upgrade imutils
此时您的系统已经配置完毕,因此打开 text_detection.py 并插⼊以下代码:
# import the necessary packages
from imutils.object_detection import non_max_suppression
import numpy as np
import argparse
import time
import cv2
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", type=str,
help="path to input image")
ap.add_argument("-east", "--east", type=str,
help="path to input EAST text detector")
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())
⾸先,导⼊所需的包和模块。值得注意的是,我们从 imutils.object_detection 导⼊了 NumPy、OpenCV 和我对
non_max_suppression 的实现。然后我们继续解析五个命令⾏参数:
–image :我们输⼊图像的路径。
–east : EAST 场景⽂本检测器模型⽂件路径。
–min-confidence :确定⽂本的概率阈值。可选, default=0.5 。
–width :调整后的图像宽度 - 必须是 32 的倍数。默认值为 320 时可选。
–height :调整后的图像⾼度 - 必须是 32 的倍数。默认值为 320 时可选。
重要提⽰:EAST ⽂本要求您的输⼊图像尺⼨是 32 的倍数,因此如果您选择调整 --width 和 --height 值,请确保它们是 32 的倍数!从那⾥,让我们加载我们的图像并调整它的⼤⼩:
# load the input image and grab the image dimensions
image = cv2.imread(args["image"])
orig = py()
(H, W) = image.shape[:2]
# set the new width and height and then determine the ratio in change
# for both the width and height
(newW, newH) = (args["width"], args["height"])
rW = W / float(newW)
rH = H / float(newH)
# resize the image and grab the new image dimensions
image = size(image, (newW, newH))
(H, W) = image.shape[:2]
我们加载并复制我们的输⼊图像。确定原始图像尺⼨与新图像尺⼨的⽐率(基于为 --width 和 --height 提供的命令⾏参数)。然后我们调整图像⼤⼩,忽略纵横⽐。为了使⽤ OpenCV 和 EAST 深度学习模型进⾏⽂本检测,我们需要提取两层的输出特征图:
# define the two output layer names for the EAST detector model that
# we are interested -- the first is the output probabilities and the
# second can be used to derive the bounding box coordinates of text
layerNames = [
"feature_fusion/Conv_7/Sigmoid",
"feature_fusion/concat_3"]
我们构建了⼀个 layerNames 列表:
第⼀层是我们的输出 sigmoid 激活,它为我们提供了⼀个区域是否包含⽂本的概率。
第⼆层是输出特征图,表⽰图像的“⼏何”——我们将能够使⽤这个⼏何来推导出输⼊图像中⽂本的边界框坐标
让我们加载 OpenCV 的 EAST ⽂本检测器:
# load the pre-trained EAST text detector
print("[INFO] loading EAST ")
net = adNet(args["east"])
# construct a blob from the image and then perform a forward pass of
# the model to obtain the two output layer sets
blob = cv2.dnn.blobFromImage(image, 1.0, (W, H),
(123.68, 116.78, 103.94), swapRB=True, crop=False)
start = time.time()
net.setInput(blob)
(scores, geometry) = net.forward(layerNames)
end = time.time()
# show timing information on text prediction
print("[INFO] text detection took {:.6f} seconds".format(end - start))
我们使⽤ adNet 将神经⽹络加载到内存中,⽅法是将路径传递给 EAST 检测器。
然后,我们通过将其转换为 blob 来准备我们的图像。要阅读有关此步骤的更多信息,请参阅深度学习:OpenCV 的blobFromImage ⼯作原理。为了预测⽂本,我们可以简单地将 blob 设置为输⼊并调⽤ net.forward。这些⾏被抓取时间戳包围,以便我们可以打印经过的时间。通过将 layerNames 作为参数
提供给 net.forward,我们指⽰ OpenCV 返回我们感兴趣的两个特征图:
⽤于导出输⼊图像中⽂本的边界框坐标的输出⼏何图
同样,分数图,包含给定区域包含⽂本的概率
我们需要⼀个⼀个地循环这些值中的每⼀个:
# 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]
我们⾸先获取分数卷的维度(,然后初始化两个列表:
rects :存储⽂本区域的边界框 (x, y) 坐标
置信度:将与每个边界框关联的概率存储在 rects 中
我们稍后将对这些区域应⽤⾮极⼤值抑制。循环遍历⾏。提取当前⾏ 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])
对于每⼀⾏,我们开始遍历列。我们需要通过忽略概率不够⾼的区域来过滤掉弱⽂本检测。
当图像通过⽹络时,EAST ⽂本检测器⾃然会减⼩体积⼤⼩——我们的体积⼤⼩实际上⽐我们的输⼊图像⼩ 4 倍,因此我们乘以 4 以将坐标带回原始图像。
提取⾓度数据。然后我们分别更新我们的矩形和置信度列表。我们快完成了!最后⼀步是对我们的边界框应⽤⾮极⼤值抑制来抑制弱重叠边界框,然后显⽰结果⽂本预测:
# apply non-maxima suppression to suppress weak, overlapping bounding
# boxes
boxes = non_max_suppression(np.array(rects), probs=confidences)
# loop over the bounding boxes
for (startX, startY, endX, endY) in boxes:
# scale the bounding box coordinates based on the respective
# ratios
startX = int(startX * rW)
startY = int(startY * rH)
endX = int(endX * rW)
endY = int(endY * rH)
# draw the bounding box on the image
# show the output image
cv2.imshow("Text Detection", orig)
cv2.waitKey(0)
正如我在上⼀节中提到的,我⽆法在我的 OpenCV 4 安装 (cv2.dnn.NMSBoxes) 中使⽤⾮最⼤值抑制,因为 Python 绑定没有
返回值,最终导致 OpenCV 出错。我⽆法完全在 OpenCV 3.4.2 中进⾏测试,因此它可以在 v3.4.2 中运⾏。
相反,我使⽤了 imutils 包(第 114 ⾏)中提供的⾮最⼤值抑制实现。结果看起来还是不错的;但是,我⽆法将我的输出与NMSBoxes 函数进⾏⽐较以查看它们是否相同。循环我们的边界框,将坐标缩放回原始图像尺⼨,并将输出绘制到我们的原始图像。原始图像会⼀直显⽰,直到按下某个键。
作为最后的实现说明,我想提⼀下,我们⽤于循环分数和⼏何体的两个嵌套 for 循环将是⼀个很好的例⼦,说明您可以利⽤Cython 显着加速您的管道。我已经使⽤ OpenCV 和 Python 在快速优化的“for”像素循环中展⽰了 Cython 的强⼤功能。
OpenCV ⽂本检测结果
您准备好将⽂本检测应⽤于图像了吗?
下载frozen_east_text_detection,地址:
从那⾥,您可以在终端中执⾏以下命令(注意两个命令⾏参数):
$ python text_detection.py --image images/lebron_james.jpg \
--east frozen_east_text_detection.pb
您的结果应类似于下图:
在勒布朗·詹姆斯⾝上标识了三个⽂本区域。现在让我们尝试检测商业标志的⽂本:
$ python text_detection.py --image images/car_wash.png \
--east frozen_east_text_detection.pb
resized使⽤ 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
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论