二值圖像距離變換函數
[算法說明]
二值圖像的距離變換實際上就是將二值圖像轉換爲灰度圖像,在二值圖像中我們將圖像分爲目標圖像和背景圖像,假設目標圖像像素值爲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