二值影象的骨架提取
阿新 • • 發佈:2019-02-15
本文介紹的二值影象細化演算法是來自 T.Y. Zhang and C.Y. Suen 1984 年發表的論文 “A fast parallel algorithm for thinning digital patterns” 中所介紹的演算法。
演算法介紹
我們先進行如下定義:
P1 為我們要判斷是否刪去的點,則對其 8 鄰域分別按照上圖順序標記為 P2 P3 P4 P5 P6 P7 P8 P9;
B(P1) 為 P1 8 鄰域值為 1(影象為 0-1 二值圖)的點,即:
A(P1) 為 P1 8 領域按照上圖排序時(P9 後面接 P2),序列 0-1 的個數:
若 P1 8 鄰域的值如上圖所示,則有 A(P1)= 2。
我們遍歷影象的每個點,對每個畫素為 1 的點做如下判斷:
1.
2.
3. 當為奇數次迭代時,判斷
如果滿足上述條件,則刪除該點。
迴圈遍歷影象,直至沒有點被刪除。
演算法流程
while point are deleted :
for P1 in all pixels :
if ((1)P1=1
(2)2 <=B(P1)<=6
(3)A(P1)=1
(4)in odd iterations:
P2*P4*P6=0 && P4*P6*P8=0
in even iterations:
P2*P4*P8=0 && P2*P6*P8=0)
delete P1
end for
end while
程式碼
void zhangSkeleton(Mat &srcimage)
{
int kernel[9 ];
int nl = srcimage.rows;
int nc = srcimage.cols;
vector<Point> delete_list;
int A, B;
while (true)
{
for (int j = 1; j < nl - 1; j++)
{
uchar* data_pre = srcimage.ptr<uchar>(j - 1);
uchar* data = srcimage.ptr<uchar>(j);
uchar* data_next = srcimage.ptr<uchar>(j + 1);
for (int i = 1; i < (nc - 1); i++)
{
if (data[i] == 255)
{
kernel[0] = 1;
if (data_pre[i] == 255) kernel[1] = 1;
else kernel[1] = 0;
if (data_pre[i + 1] == 255) kernel[2] = 1;
else kernel[2] = 0;
if (data[i + 1] == 255) kernel[3] = 1;
else kernel[3] = 0;
if (data_next[i + 1] == 255) kernel[4] = 1;
else kernel[4] = 0;
if (data_next[i] == 255) kernel[5] = 1;
else kernel[5] = 0;
if (data_next[i - 1] == 255) kernel[6] = 1;
else kernel[6] = 0;
if (data[i - 1] == 255) kernel[7] = 1;
else kernel[7] = 0;
if (data_pre[i - 1] == 255) kernel[8] = 1;
else kernel[8] = 0;
B=0;
for (int k = 1; k < 9; k++)
{
B = B + kernel[k];
}
if ((B >= 2) && (B <= 6))
{
A = 0;
if (!kernel[1] && kernel[2]) A++;
if (!kernel[2] && kernel[3]) A++;
if (!kernel[3] && kernel[4]) A++;
if (!kernel[4] && kernel[5]) A++;
if (!kernel[5] && kernel[6]) A++;
if (!kernel[6] && kernel[7]) A++;
if (!kernel[7] && kernel[8]) A++;
if (!kernel[8] && kernel[1]) A++;
//
if (A == 1)
{
if ((kernel[1] * kernel[3] * kernel[5] == 0)
&& (kernel[3] * kernel[5] * kernel[7] == 0))
{
delete_list.push_back(Point(i, j));
}
}
}
}
}
}
int size = delete_list.size();
if (size == 0)
{
break;
}
for (int n = 0; n < size; n++)
{
Point tem;
tem = delete_list[n];
uchar* data = srcimage.ptr<uchar>(tem.y);
data[tem.x] = 0;
}
delete_list.clear();
for (int j = 1; j < nl - 1; j++)
{
uchar* data_pre = srcimage.ptr<uchar>(j - 1);
uchar* data = srcimage.ptr<uchar>(j);
uchar* data_next = srcimage.ptr<uchar>(j + 1);
for (int i = 1; i < (nc - 1); i++)
{
if (data[i] == 255)
{
kernel[0] = 1;
if (data_pre[i] == 255) kernel[1] = 1;
else kernel[1] = 0;
if (data_pre[i + 1] == 255) kernel[2] = 1;
else kernel[2] = 0;
if (data[i + 1] == 255) kernel[3] = 1;
else kernel[3] = 0;
if (data_next[i + 1] == 255) kernel[4] = 1;
else kernel[4] = 0;
if (data_next[i] == 255) kernel[5] = 1;
else kernel[5] = 0;
if (data_next[i - 1] == 255) kernel[6] = 1;
else kernel[6] = 0;
if (data[i - 1] == 255) kernel[7] = 1;
else kernel[7] = 0;
if (data_pre[i - 1] == 255) kernel[8] = 1;
else kernel[8] = 0;
B = 0;
for (int k = 1; k < 9; k++)
{
B = B + kernel[k];
}
if ((B >= 2) && (B <= 6))
{
A = 0;
if (!kernel[1] && kernel[2]) A++;
if (!kernel[2] && kernel[3]) A++;
if (!kernel[3] && kernel[4]) A++;
if (!kernel[4] && kernel[5]) A++;
if (!kernel[5] && kernel[6]) A++;
if (!kernel[6] && kernel[7]) A++;
if (!kernel[7] && kernel[8]) A++;
if (!kernel[8] && kernel[1]) A++;
//
if (A == 1)
{
if ((kernel[1] * kernel[3] * kernel[7] == 0)
&& (kernel[1] * kernel[5] * kernel[7] == 0))
{
delete_list.push_back(Point(i, j));
}
}
}
}
}
}
if (size == 0)
{
break;
}
for (int n = 0; n < size; n++)
{
Point tem;
tem = delete_list[n];
uchar* data = srcimage.ptr<uchar>(tem.y);
data[tem.x] = 0;
}
delete_list.clear();
}
}