[算法说明]
二值图像的距离变换实际上就是将二值图像转换为灰度图像,在二值图像中我们将图像分为目标图像和背景图像,假设目标图像像素值为1,即为白色,背景像素为0即为黑色。在转换后的幅灰度图像中,每个连通域的各个像素点的灰度级与该像素点到其背景像素的最近距离有关。其中灰度级最大点的集合为目标图像的骨架,就是目标图像中心部分的像素的集合,灰度级反应了背景像素与目标图像边界的影响关系。用数学语言表示如下:
假设二值图像I包含一个连通域S,其中有目标O和背景B,距离图为D,则距离变换定义如下:
其中disf()为距离函数,如果用欧拉距离公式表示,如下:
其中p,q分别为目标和背景图像像素点。
距离变换的具体步骤为:
1,将图像中的目标像素点分类,分为内部点,外部点和孤立点。
以中心像素的四邻域为例,如果中心像素为目标像素(值为1)且四邻域都为目标像素(值为1),则该点为内部点。如果该中心像素为目标像素,四邻域为背景像素(值为0),则该中心点为孤立点,如下图所示。除了内部点和孤立点之外的目标区域点为边界点。
内部点 孤立点
Fig.1内部点与孤立点图示
2,计算图像中所有的内部点和非内部点,点的集合分别记为S1,S2。
3,对于S1中的每一个内部点(x,y),使用距离公式disf()计算其在S2中的最小距离,这些最小距离构成集合S3。
4,计算S3中的最大值和最小值Max,Min
5,对于每一个内部点,转换后的灰度值G计算如下:
其中s3(x,y)表示S1中内部点(x,y)在S2中的最小距离。
6,对于孤立点保持不变。
以上的距离变换方法由于计算量大,比较耗时,因此在实际应用中,我们采用一种倒角模版算法,只需要对图像进行两次扫描就可以实现距离变换。该方法称为Chamfer倒角距离变换法。
该方法使用两个模版,分别为前向模版和后向模板,如下图所示:
前向模板 后向模板
Fig.2前后向模板图示
计算步骤如下:
1,使用前向模板,对图像从上到下,从左到右进行扫描,模板中心0点对应的像素值如果为0则跳过,如果为1则计算模板中每个元素与其对应的像素值的和,分别为Sum1,Sum2,Sum3,Sum4,Sum5,而中心像素值为这五个和值中的最小值。
2,使用后向模板,对图像从下到上,从右到左进行扫描,方法同上。
3,一般我们使用的模板为3*3和5*5,分别如下图所示:
Fig.4模板图示
[函数代码]
///
/// Distance transform of binary image.
///
/// The source image.
///
public static WriteableBitmap DistanceTransformProcess(WriteableBitmap src)////25二值图像距离变换
{
if (src != null)
{
int w = src.PixelWidth;
int h = src.PixelHeight;
WriteableBitmap expansionImage = new WriteableBitmap(w, h);
byte[] temp = src.PixelBuffer.ToArray();
int t1, t2, t3, t4, t5, min = 0;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w * 4 - 4; x += 4)
{
if (y == 0 || x == 0)
{
temp[x + y * w * 4] = 0;
temp[x + 1 + y * w * 4] = 0;
temp[x + 2 + y * w * 4] = 0;
}
else
{
if (temp[x + y * w * 4] != 0)
{
t1 = temp[x - 3 + (y - 1) * w * 4] + 4;
t2 = temp[x + (y - 1) * w * 4] + 3;
t3 = temp[x + 3 + (y - 1) * w * 4] + 4;
t4 = temp[x - 3 + y * w * 4] + 3;
t5 = temp[x + y * w * 4];
min = GetMin(t1, t2, t3, t4, t5);
temp[x + y * w * 4] = (byte)min;
temp[x + 1 + y * w * 4] = (byte)min; temp[x + 2 + y * w * 4] = (byte)min;
}
t2 = 0; t3 = 0; t4 = 0; t5 = 0; min = 0;
}
}
}
for (int y = h - 2; y > 0; y--)
{
for (int x = w * 4 - 4; x > 0; x -= 4)
{
if (y == 1 || x == 3)
{
temp[x + y * w * 4] = 0;
temp[x + 1 + y * w * 4] = 0;
temp[x + 2 + y * w * 4] = 0;
}
else
{
if (temp[x + y * w * 4] != 0)
{
t1 = temp[x - 3 + (y + 1) * w * 4] + 4;
t2 = temp[x + (y + 1) * w * 4] + 3;
t3 = temp[x + 3 + (y + 1) * w * 4] + 4;
t4 = temp[x + 3 + y * w * 4] + 3;
t5 = temp[x + y * w * 4];
min = GetMin(t1, t2, t3, t4, t5);
temp[x + y * w * 4] = (byte)min;
temp[x + 1 + y * w * 4] = (byte)min; temp[x + 2 + y * w * 4] = (byte)min;
}
t2 = 0; t3 = 0; t4 = 0; t5 = 0; min = 0;
}
}
}
Stream sTemp = expansionImage.PixelBuffer.AsStream();
sTemp.Seek(0, SeekOrigin.Begin);
sTemp.Write(temp, 0, w * 4 * h);
return expansionImage;
}
else
{
return null;
}
}
private static int GetMin(int a, int b, int c, int d, int e)
{
int t = (a < b ? a : b) < c ? (a < b ? a : b) : c;
return ((t < d ? t : d) < e ? (t < d ? t : d) : e);
}
[图像效果]
Fig.5原图 Fig.6效果图
demo 下载:http://www.zealfilter.com/forum.php?mod=viewthread&tid=19&extra=page%3D2