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