1. 程式人生 > >二值影象的骨架提取

二值影象的骨架提取

  本文介紹的二值影象細化演算法是來自 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 二值圖)的點,即:

B(P1)=P2+P3+P4+P5+P6+P7+P8+P9
A(P1) 為 P1 8 領域按照上圖排序時(P9 後面接 P2),序列 0-1 的個數:

這裡寫圖片描述

若 P1 8 鄰域的值如上圖所示,則有 A(P1)= 2。

我們遍歷影象的每個點,對每個畫素為 1 的點做如下判斷:
1. 2<=B(P1)<=6
2. A(P1)=1
3. 當為奇數次迭代時,判斷 P2P4P6=0,P4P6P8=0;當為偶數次迭代時,判斷 P2P4P8=0,P2P6P8=0
如果滿足上述條件,則刪除該點。
迴圈遍歷影象,直至沒有點被刪除。

演算法流程

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(); } }

測試

這裡寫圖片描述