图像细化---Zhang-Suen算法


Zhang-Suen算法是一种经典的细化算法,后续很多在其基础上进行改进, 论文是1984年在IPCV(Image Processing and Computer Vision)发表的。
论文pdf: A fast parallel algorithm for thinning digital patterns

一、基本原理

输入:二值图
输出:细化后图像

思路:删除掉非骨架上的非零像素点

其思路比较简单,重点在于如何判断非零像素点是不是骨架点。
查找非骨架点
对于非零像素点,如下图所示考虑其8领域像素点,文中共分为两个步骤来判断该点是否需要删除。

  • 第一步
    判断该点是否满足如下条件,如果满足则删除,否则保留。

\[\begin{align} (a)\quad & 2 \leq B(P_1) \leq 6 \\ (b)\quad & A(P_1) = 1 \\ (c)\quad & P_2 * P_4 * P_6 = 0 \\ (d)\quad & P_4 * P_6 * P_8 = 0 \end{align} \]

其中\(A(P_1)\)表示按\(P_2\)\(P_9\)再到\(P_2\)的顺序,从0变到1的次数,如下图所示,其$A(P_1) = 2 $; \(B(P_1)\)表示其8领域非零像素个数,下图中\(B(P_1) = 3\)

  • 第二步
    类似第一步,参考图3,第一步的条件会删除东南边界点和西北的角点,这里只需要把式(c)和(d)更改为以下条件

\[\begin{align} (c)\quad & P_2 * P_4 * P_8 = 0 \\ (d)\quad & P_2 * P_6 * P_8 = 0 \end{align} \]

二、代码逻辑

**展开代码**

inline int GetObjectPixel(Image& image, int row, int col)
{
	if (row >= 0 && row < image.GetWidth() && col >= 0 && col < image.GetHeight())
	{
		return (*image.GetPixel(row, col) > 128) ? 1 : 0;
	}

	return 0;
}

inline bool IsDelete(Image& image, int i, int j, bool bSecond = false)
{
	int p2 = GetObjectPixel(image, i - 1, j );
	int p3 = GetObjectPixel(image, i - 1, j + 1);
	int p4 = GetObjectPixel(image, i, j + 1);
	int p5 = GetObjectPixel(image, i + 1, j + 1);
	int p6 = GetObjectPixel(image, i + 1, j);
	int p7 = GetObjectPixel(image, i + 1, j - 1);
	int p8 = GetObjectPixel(image, i, j - 1);
	int p9 = GetObjectPixel(image, i - 1, j - 1);

	// cacl AP
	int ap = 0;
	if (1 == p3 - p2) {
		ap += 1;
	}
	if (1 == p4 - p3) {
		ap += 1;
	}
	if (1 == p5 - p4) {
		ap += 1;
	}
	if (1 == p6 - p5) {
		ap += 1;
	}
	if (1 == p7 - p6) {
		ap += 1;
	}
	if (1 == p8 - p7) {
		ap += 1;
	}
	if (1 == p9 - p8) {
		ap += 1;
	}
	if (1 == p2 - p9) {
		ap += 1;
	}

	if (ap != 1) {
		return false;
	}

	// cacl PB
	int pb = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
	if (pb < 2 || pb > 6) {
		return false;
	}

	if (bSecond) {
		if (0 == p2 * p4 * p8 && 0 == p2 * p6 * p8) {
			return true;
		}
	}
	else {
		if (0 == p2 * p4 * p6 && 0 == p4 * p6 * p8) {
			return true;
		}
	}

	return false;
}

void Skeletonize(Image& image)
{
        const int row = image.GetWidth(), column = image.GetHeight();
	int c = 0;
	std::vector<int> XYPosition;
	while (true) {
		c = 0;
		// first
		for (int i = 0; i < row; ++i)
		{
			for (int j = 0; j < column; ++j)
			{
				if (*image.GetPixel(i, j) > 128) {
					if (IsDelete(image, i, j))
					{
						XYPosition.push_back(i);
						XYPosition.push_back(j);
						c += 1;
					}
				}
			}
		}
		if (c > 0) {
			for (int idx = 0; idx < c; ++idx){
				*image.GetPixel(XYPosition[idx * 2], XYPosition[idx * 2+ 1]) = 0;
			}
			XYPosition.clear();
			c = 0;
		}
		else {
			break;
		}
		// second
		for (int i = 0; i < row; ++i)
		{
			for (int j = 0; j < column; ++j)
			{
				if (*image.GetPixel(i, j) > 128) {
					if (IsDelete(image, i, j, true))
					{
						XYPosition.push_back(i);
						XYPosition.push_back(j);
						c += 1;
					}
				}
			}
		}

		if (c > 0) {
			for (int idx = 0; idx < c; ++idx) {
				*image.GetPixel(XYPosition[idx * 2], XYPosition[idx * 2 + 1]) = 0;
			}
			XYPosition.clear();
			c = 0;
		}
		else {
			break;
		}
	}
}

三、效果

四、其他参考资料

https://www.geometrictools.com/Documentation/Skeletons.pdf


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM