检测图片中前景区域面积占比

检测图片中前景区域面积占比

OpenCV对于图像处理是非常重要的工具,现在有个小模块需要实现这样的功能:探测扫描的文档中表格区域占总页面的多少比例。最初想用HOG等检测器扫描定位表格,但是工作量比较大,并且文档的检测仅用传统的图像特征来检测出错较多。而传统图像法对图片进行一些预处理就可以比较快速而相对准确地检测到非背景的区域。

像这样已经提取出了照片与表格的图(这是PDF转换步骤进行的,文字提取到了另一层上)。实现方法为如下的步骤:

  1. 边缘提取
  2. 反色
  3. 图片腐蚀(即加粗黑色),kernal size = 5×5
  4. 连通区域上色
  5. 以1为阈值进行二值化
  6. 图片膨胀操作(黑色变细),kernalsize比5×5要大一些,这样前景的表格就成为一个连续的白色区域
  7. FindContours查找轮廓,并输出每个轮廓的像素数/总像素数即为每个轮廓的面积比

上图作为输入最后处理的效果如下所示:

完整的程序如下,OpenCV的版本为C#的OpenCvSharp v4.0.30319

Mat src;
src = Cv2.ImRead(@"blankSample.png").CvtColor(ColorConversionCodes.BGR2GRAY);
src.ConvertTo(src, MatType.CV_8U);

Cv2.Canny(src, src, 0, 255, 3, false);            
Cv2.BitwiseNot(src, src);
Cv2.Erode(src, src,
    Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(5, 5)));

Mat background = new Mat(src.Size(), MatType.CV_8U);

Cv2.ConnectedComponents(src, background, PixelConnectivity.Connectivity8);
background.ConvertTo(background, MatType.CV_8U);
Cv2.Threshold(background, background, 1, 255, ThresholdTypes.Binary);
Cv2.Dilate(background, background,
    Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(30, 30)));

OpenCvSharp.Point[][] points;
OpenCvSharp.HierarchyIndex[] h1;
Cv2.FindContours(background,out points, out h1, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
Console.WriteLine(points.Length);

接下来将介绍这些函数的用法

边缘提取

OpenCV中边缘提取也有多种算法,这里使用效果较好的Canny算子进行。Canny前2个参数分别是输入和输出Mat,第3个参数和第4个参数分别是检测上下限,低于下限与高于上限的像素不认为是边缘,第5个参数是Sobel的Kernal Size,默认值即3。由于图像的

反色

图像已经处理成为了灰度图,反色操作使用bitwiseNot即可实现。

图片腐蚀/膨胀

Erode是腐蚀,但是它的效果是将黑色变粗,这可能和我们直觉相违背:腐蚀应该是变得更细,就像钢铁腐蚀越来越小。站在图像的角度,对于灰度图,白色值为255,黑色值为0,腐蚀的话就是将较大的值的区域进行“ 腐蚀 ”,白色区域减小了,则相应黑色的区域就变粗了。
相应地Dilate是膨胀,白色区域膨胀后,黑色区域就变细了。

腐蚀和膨胀函数的参数类似,这里以Erode为例,前两个参数是输入Mat和输出Mat,第3个参数是Kernal,使用Cv2.GetStructuringElement可以指定Rect,Cross,Ellipse作为Kernal的形状,Size则指定Kernal的尺寸。 膨胀操作的Kernal size需要大一些,从而保证不仅能够把表格的细黑线完全消去,还要让文档中的图片区域相近部分合并到同一个块。

连通区域

ConnectedComponents只有两个必选参数,即一个输入Mat一个输出Mat。输出Mat的尺寸与输入Mat的尺寸一致,输出Mat的各值为0到N-1的整数,相同的连通区域用同一个整数表示,其中背景总是用0来表示,如果此时将这个输出Mat展示出来,多个不同的连续区域将显示为不同的灰阶。

阈值变换

Threshold可以非常方便地把图片转换为二值图,Threshold接受5个参数:输入Mat,输出Mat,阈值,调整值,变换类型。将变换类型设为Binary则可以把图片中所有大于阈值的像素全部置为调整值。

上一步当中经过连通区域的变换,背景已经变换为0,所以像素值在为1以上的区域则是前景,于是可以将前景变换为白色255。

检测轮廓数

使用FindContours可以对二值图进行轮廓数的提取,传入的参数有输入Mat,二维点集保存区域轮廓,拓扑信息结构,检索方式,轮廓近似模式。

这里必要的就是前2个参数,以out方式传入 OpenCvSharp.Point[][] contours,执行后contours.Length为轮廓数,contours[i]为第i个轮廓的四角坐标, contours[i][0]到 contours[i][3] 即四角的Point,通过各坐标Point的X与Y之差就可以算出一个轮廓中的像素数,根据整个src的width与Height之积可知全图的像素数,两值相除即知面积比。

发表评论

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