一种二值图像封闭孔洞的高效填充算法

基本概念

首先,我们先定义何谓封闭孔洞?我们认为如果一个特征的边缘完全被另外一个特征包围,则认为其为一个封闭的特征,比如在下图中:
一种二值图像封闭孔洞的高效填充算法_第1张图片

1所标注处就是封闭孔洞,2所标注处就是开式孔洞。
我们第一直觉,想到的就是用FloodFill。漫水填充法是一种常见的图像处理方法,通过选中和种子点相连相近的区域,将其转换为指定颜色,以达到标记或者分离图像的目的,进而完成对图像的一些分析和处理。

不过如果直接用FloodFill,我们无法直接定位那些未知需要进行种子填充的。Gabriel Landini, G.Landini 在2008年5月给我们写了个非常简单的代码实现了这一过程。代码的核心思想是用逆向思维,既然我们不知道哪些前景区域需要填充,我们就把不被前景区域包围的背景区域用FloodFill找出了,剩下的部分都作为前景区域,不也一样实现目标。

如何把不被前景区域包围的背景区域用FloodFill找出?先把每一行的起点和终点为种子点(如果是背景),进行种子填充,然后把每一列的起点和终点为种子点(如果是背景),进行种子填充行。

示例演示

我们基于OpenCV快速实现该算法。

#include  
#include 

using namespace cv;
using namespace std;

void FillHole(Mat& src, bool fillBackGround = false)
{
	assert(src.channels() == 1);
	int width = src.cols, height = src.rows;
	int color = fillBackGround == false ? 255 : 0;

	for (int y = 0; y < height; y++)
	{
		if (src.at<uchar>(y, 0) == color) 
			floodFill(src, cv::Point(0, y), Scalar(127));
		if (src.at<uchar>(y, width - 1) == color) 
			floodFill(src, cv::Point(width - 1, y), Scalar(127));
	}

	for (int x = 0; x < width; x++)
	{
		if (src.at<uchar>(0, x) == color)
			floodFill(src, cv::Point(x, 0), Scalar(127));
		if (src.at<uchar>(height - 1, x) == color)
			floodFill(src, cv::Point(x, height - 1), Scalar(127));
	}

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			if (src.at<uchar>(y, x) == 127)
				src.at<uchar>(y, x) = color;
			else
				src.at<uchar>(y, x) = 255 - color;
		}
	}
}


int main(void)
{
	string testImage = "test.png";
	Mat1b src = imread(testImage, 0);

	if (src.empty()){
		cout << "The specified image '" << testImage << "' does not exists" << endl;
		exit(-1);
	}

	imshow("Origin", src);

	FillHole(src);

	imshow("Result", src);

	waitKey(0);

	return 0;
}

一种二值图像封闭孔洞的高效填充算法_第2张图片

你可能感兴趣的:(OpenCV实例,算法,opencv,计算机视觉)