OpenCV dnn text_detection

本文记录opencv自带的DNN模块中其中一个范例的测试记录和学习笔记,该范例用于检测自然场景文本,功能及其强大,按要求加载训练模型以后,不需要更改任何参数,可检测多种语言自然场景中的的文本。该范例源代码路径如下https://github.com/opencv/opencv/blob/master/samples/dnn/text_detection.cpp.

测试环境

window10 + VS2017+opencv3.4.2及以上版本(V3.4.2及以上版本有该范例)

dnn text_detection.cpp运行说明

在运行之前,我们先浏览一下text_detection.cpp的源代码。如下所示,源代码起始有定义终端所需要输入的参数及其意义。这里我们需要特别注意的是参数width和height的说明,这两个参数都必须是32的倍数。

接下来在主函数main()中有如下代码,有5个变量用于接收从终端输入的参数。结合const char* keys中的定义,期中参数confThreshold 、nmsThreshold 、inpWidth 、inpHeight 有默认值,我们可以暂时使用系统默认值

其中参数model是dnn需要加载的对应的已训练好的Tensorflow模型在本地的路径,因此在正式运行该代码之前, 我们需要自行下载该模型并放在范例所运行的工程路径下,该模型的下载地址https://www.dropbox.com/s/r2ingd0l3zt8hxs/frozen_east_text_detection.tar.gz?dl=1

opencv dnn模块中其他范例所需模型均可以从这个文件中找到相应模型的下载地址:

https://github.com/opencv/opencv_extra/blob/master/testdata/dnn/download_models.py

模型下载完成后,源代码可以开始试运行,按照运行提示输入图像或视频文件路径,以及待加载的.pb文件路径即可。

dnn text_detection.cpp测试记录

为方便测试,我个人喜欢将待加载模型和待测图像放在指定文件夹中,在程序中设定相应路径,程序开始运行后自动读取。因此我将范例的源代码进行了简单的修改,修改后的代码如下,其中有一些简单的注释。需要参考的同学拷贝以下代码到新建工程中,根据个人电脑中的模型与待测图像途径修改相应程序即可。

两张图片的测试结果如下图所示,检测到的文字以红色矩形框标示出来。

dnn text_detection学习笔记

1. dnn text_detection的重点函数与基本工作流程

  • readNet:为DNN网络加载训练模型
  • blobFromImage:对输入图像的size,depth,swapRB,mean,scalefactor做处理,保证图像满足NCHW数据格式
  • setInput:为DNN网络设置输入图像
  • forward:输出整个网络的运行结果
  • decode:将DNN网络的运行结果解析为bounding boxes.
  • NMSBoxes:对得到的检测区域和相应的评分应用非最大抑制程序,得到更合理的检测结果

不知道是否有读者跟我一样对blobFromImage函数中的参数Scalar(123.68, 116.78, 103.94)感到好奇?我查了一些资料,在Deep learning: How OpenCV’s blobFromImage works有原理讲解。

在这一篇专栏https://zhuanlan.zhihu.com/p/51928656有简单的说明。我并没有看懂全部内容,但在知乎专栏中说到,参数Scalar(123.68, 116.78, 103.94)是在googleNet训练的时候设定的。我想对于初级使用者而言,我们知道这个参数有固定值,一般不需要修改,与confThreshold 、nmsThreshold类似。同时这篇文章里有函数blobFromImage的简单注释,我转载过来,感谢原作者。

另外,关于函数NMSBoxes,它有三个重载函数,主要差异是输入的文字区域可以是Rect/Rect2d/RotatedRect的格式,我们在使用过程中可以按照实际需求选择。

2. 关于参数inpWidth/inpHeight

从以上注释中,我们知道输入的图像会被缩放为inpWidth/inpHeight这两个参数设置的尺寸,然后再进行进一步的处理。关于这两个参数,我有两个疑问:1. 若inpWidth/inpHeight设定值相对于原始尺寸而言,原始图像中的文字有扭曲或者缩放,是否会影响检测结果?2. 假如第一个问题的答案是有影响,这两个参数是否可以根据图像大小动态设定呢?

我们根据第一个问题来做一个实验。如下图所示,我们为该图片命名为PVANet,它的原始大小为161*517,图中分别为按我所修改的代码放大1倍,3倍和使用320*320的测试效果。从测试结果来看,按原始尺寸放大1倍和放大3倍的检测效果基本相当,但放大3倍的效果跟好一些,相对而言,使用320*320的检测效果是最差的。,但

第二个测试图像,如下图所示,其原始尺寸为292*41,分别按原始尺寸放大1倍,3倍和使用固定尺寸320*320。从输出结果来看,使用原始尺寸按比例放大,检测效果会好一些。

由以上的实验结果可以知道,扭曲比较严重的图像会令检测结果不准确。因此我认为缩放尺寸最好是按照源图像的比例进行缩放,保证缩放后的长与宽均为32的倍数即可。这个结论并没有理论依据,只是多张图片的实验结果,欢迎探讨。

接下来我们看第二个问题,这两个参数是否可以动态修改?

这里的动态修改,指的是cv::dnn::Net net作为全局变量初始化一次,而blobFromImage每一次调用时这两个参数可以设定的值。这个问题的出发点其实与第一个疑问一致,Dnn读取模型执行完成一次初始化之后,我们可以用它来检测不同图像中的文字,那么如果我每一次输入图像的尺寸不一致,为了尽量保持源图像无变形,inpWidth/inpHeight的尺寸需要根据每张图像的尺寸的变化而变化。实验结论是这两个参数不能动态修改,我们在dnn.cpp中可以看到该函数对这两个参数的定义是const.

在学习过程中,我看到readNet函数有如下形式的重载函数,请参考https://docs.opencv.org/master/d6/d0f/group__dnn.html#ga9d118d70a1659af729d01b10233213ee

在这个重载函数中,训练模型以const std::vector< uchar > 的类型存在。这是否表示我可以在初始化函数中,将.pb文件读取到const std::vector< uchar > & bufferModel变量中;在需要的时候用readNet来初始化cv::dnn::Net net。这样操作是否能确保我们在连续处理不同图像时,可以动态设置inpWidth/inpHeight的值,并且又比较节省时间呢?

针对这个问题,我查阅了很多资料,但可惜的是我暂时未找到将.pb文件读取并存储为const std::vector< uchar > 变量的方式,若后续找到的方式,会验证我的疑虑在此分享更新。

3. dnn text_detection的工作效率

该方法功能如此强大,它的检测效率如何呢?

我修改的代码中,加入了测试读取模型的时间以及图像处理的时间。针对图像PVAnet的测试时间,总结如下表所示。

由测试结果可知,读取模型的时间还是比较久的,一般而言读取模型只需要在程序初始化时做一次即可。但是假如我们需要将此功能封装为CLR被调用,为了提高程序的执行效率,此时cv::dnn::Net net就需要定义为一个全局变量,将读取模型的动作放在初始化函数中进行且仅调用一次。

但是目前opencv4.0版本中并不支持直接将cv::dnn::Net net按以下代码定义,程序会报错Linker Error LNK2022,也就是说它并不支持将cv::dnn::Net net定义为全局变量。



具体说明大家可以参考这里   Linker Error LNK2022 of dnn module in CLR project,从这个链接中我们也可以找到这种需求正确的解决方案:


本文到此结束,感谢阅读,欢迎关注。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

Fork me on GitHub