connectedComponentsWithStats在汉字定位中的应用
opencv3.0及以上版本中有一个名为connectedComponentsWithStats的函数,该函数不仅可以得到输入图像的连通域的标记图,还可以返回图像中每一个连通域外接矩形的左上角坐标,宽和高,以及连通域包含的像素总数,连通域的质心等等重要属性。
connectedComponentsWithStats有一个姊妹函数connectedComponents,connectedComponents仅可以得到一张连通域的标记图,这两个函数的返回值均为联通区域的数量N,范围为[0, N-1],其中0代表背景;大家可以根据其功能特征和实际需求各取所需。
在《基于笔画宽度转换(SWT)和连通域的汉字检测方法》一文中提到使用connectedComponentsWithStats结合SWT来定位复杂背景中的汉字,本文将在该文章的基础上详述connectedComponentsWithStats在汉字定位中的应用,相关代码已上传至Github:ExtractChar
connectedComponentsWithStats使用示例
按照惯例,我们先给出一张连通域作用之后的效果图。这是《基于笔画宽度转换(SWT)和连通域的汉字检测方法》一文中提到的图像,获取SWT图像并进行开操作之后,再使用函数connectedComponentsWithStats得到的效果图。为了区分出不同的连通域,对函数检测到的连通域随机分配了不同的颜色:
使用connectedComponentsWithStats获取给定图片连通域并为不同连通域随机分配颜色的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <opencv2/core/utility.hpp> #include "opencv2/imgproc.hpp" #include "opencv2/highgui.hpp" #include <iostream> using namespace cv; using namespace std; Mat img; int threshval = 227; static void TestConnectedComponentsWithStats(int threshval) { Mat bw = threshval < 128 ? (img < threshval) : (img > threshval); Mat labelImage(img.size(), CV_32S); Mat stats, centroids; int nLabels = connectedComponentsWithStats(bw, labelImage, stats, centroids); // Show connected components with random colors std::vector<Vec3b> colors(nLabels); colors[0] = Vec3b(0, 0, 0);//background for(int label = 1; label < nLabels; ++label){ colors[label] = Vec3b( (rand()&200), (rand()&200), (rand()&200) ); } Mat dst(img.size(), CV_8UC3); for(int r = 0; r < dst.rows; ++r){ for(int c = 0; c < dst.cols; ++c){ int label = labelImage.at<int>(r, c); Vec3b &pixel = dst.at<Vec3b>(r, c); pixel = colors[label]; } } // Text labels with area of each cc (except background) for (int i=1; i< nLabels;i++) { float a=stats.at<int>(i,CC_STAT_AREA); Point org(centroids.at<double>(i,0), centroids.at<double>(i,1)); String txtarea; std::ostringstream buff; buff << a; txtarea=buff.str(); putText( dst, txtarea, org,FONT_HERSHEY_COMPLEX_SMALL, 1, Scalar(255,255,255), 1); } imshow( "Connected Components", dst ); } |
connectedComponentsWithStats参数说明
我们先来看connectedComponentsWithStats函数的说明。大家可以在connectedComponentsWithStats查看该函数的详细说明,我这里总结一些我认为比较重要的属性。
函数说明中并没有明确connectedComponentsWithStats所获取的连通域序号号的排序方式,我们将该函数检测到的连通域的信息依连通域序号在控制台打印出来,从中我们还可以观察到connectedComponentsWithStats连通域的编号规律。下图是图形过滤之后的连通域,相应区域中的数值表示该连通域的在所有连通域中的序号,右侧控制台中依照label centroid(label,0) centroid(label,1) stats(label,CC_STAT_WIDTH) stats(label,CC_STAT_HEIGHT) stats(label,CC_STAT_LEFT) stats(label,CC_STAT_TOP) 的顺序打印出相应信息。根据这些信息的排列规律,我们可以发现connectedComponentsWithStats根据连通域外接矩形的CC_STAT_TOP由小到大的顺序来编号。
connectedComponentsWithStats在汉字定位中的应用
在本系列文章研究的图像集中,获取到相应图像的笔画宽度图之后,剩余的主要任务就是过滤掉非文字区域。经过观察和分析,可以总结出文字区域和非文字区域比较明确的特征,而connectedComponentsWithStats的作用就是根据这些这些特征和获取的连通域属性对非文字区域进行过滤。
笔画宽度图中汉字所在区域特征如下:
1. 汉字所在区域连通域面积较大,且连通域宽度相对比较统一;非汉字所在区域存在极小的斑点区域,这些区域是明显可以将其过滤的;
2. 汉字所在区域接近于正方形,且其面积在一定范围内(源图像集合中并未出现“一”等细长型的汉字,因此我们姑且认为汉字所在区域均为正方形。);非汉字所在区域的连通域可能会出现特别窄而长的区域;
3. 汉字所在区域连通域有可能包含不是汉字笔画的区域;
4. 同属一个汉字的笔画不一定属于一个连通域,可能由不同的多个连通域合成;
已知以上规则后,我们就可以根据connectedComponentsWithStats函数获取的连通域属性对非文字区域进行过滤。
除上述过滤规则外,我们还可以统计包含汉字的连通域和不包含汉字的连通域中值的差异,即不同连通域中笔画宽度的直方图,相关代码如下:
经过对源图像SWT图像的初步分析,包含汉字的SWT图像值大多数在5~9之间。根据此规律,我们淘汰了笔画宽度值为10~14和笔画宽度为1~3所占比超过0.5的连通域。此项过滤原则过滤之后的图像如下图所示。
根据汉字的特征,很多汉字的笔画并不属于同一个连通域,因此过滤完成后,我们还需要考虑连通域的合并,这样我们才能比较准确并完整的获取到一个汉字所在区域。连通域合并的原则:两个连通域质心的X轴差值,Y轴差值同时小于预设值时,认为这两个连通域可以合并。合并后,在原图中标示出过滤及合并后的连通域,理论上而言,所有包含汉字的区域应该都在其中,有可能会包含一部分非汉字区域,不过实际效果容易被原图汉字与背景的对比度以及背景的复杂程度影响。
总结
connectedComponentsWithStats定位汉字的效果比较依赖于各过滤条件的阈值,相同的代码用于定位其他类型的汉字可能就一无是处,要想获得比较好的效果还要“因地制宜”,后续会尝试用机器学习的方式来定位该项目中的汉字,欢迎交流探讨。
请问一块透明玻璃,logo印在正面。怎样通过拍到的照片来判断这块玻璃是正面还是反面?用surf的可以么
要看LOGO的正反差异是否明显,需要实验验证哪种特征效果比较好