對二值圖進行細化(骨架提取),也就是把每根線條細化到一個像素的寬度。有兩個比較成熟的算法實現此功能,分別是Zhang-Suen算法和Guo-Hall算法。
我們下面使用OpenCVSharp,使用C#實現上述兩個算法:
private static Mat ThinningIteration(Mat img, int iter, int thinningType)
{Mat marker = Mat.Zeros(img.Size(), MatType.CV_8UC1);int rows = img.Rows;int cols = img.Cols;marker.Col(0).SetTo(1);marker.Col(cols - 1).SetTo(1);marker.Row(0).SetTo(1);marker.Row(rows - 1).SetTo(1);//THINNING_ZHANGSUENif (thinningType == 1){marker.ForEachAsByte((byte* value, int* position) =>{int i = position[0];int j = position[1];if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)return;var ptr = (byte*)img.Ptr(i, j).ToPointer();// p9 p2 p3// p8 p1 p4// p7 p6 p5byte p2 = ptr[-cols];byte p3 = ptr[-cols + 1];byte p4 = ptr[1];byte p5 = ptr[cols + 1];byte p6 = ptr[cols];byte p7 = ptr[cols - 1];byte p8 = ptr[-1];byte p9 = ptr[-cols - 1];int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);if (iter == 0)*value = lut_zhang_iter0[neighbors];else*value = lut_zhang_iter1[neighbors];});}//THINNING_GUOHALLelse if (thinningType == 2){marker.ForEachAsByte((byte* value, int* position) =>{int i = position[0];int j = position[1];if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)return;var ptr = (byte*)img.Ptr(i, j).ToPointer();// p9 p2 p3// p8 p1 p4// p7 p6 p5byte p2 = ptr[-cols];byte p3 = ptr[-cols + 1];byte p4 = ptr[1];byte p5 = ptr[cols + 1];byte p6 = ptr[cols];byte p7 = ptr[cols - 1];byte p8 = ptr[-1];byte p9 = ptr[-cols - 1];int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);if (iter == 0)*value = lut_guo_iter0[neighbors];else*value = lut_guo_iter1[neighbors];});}img &= marker;return img;
}public static void Thinning(InputArray input, OutputArray output, int thinningType)
{Mat processed = input.GetMat().Clone();processed /= 255;Mat prev = processed.Clone();Mat diff = new Mat();do{processed = ThinningIteration(processed, 0, thinningType);processed = ThinningIteration(processed, 1, thinningType);Cv2.Absdiff(processed, prev, diff);if (diff.CountNonZero() == 0) break;processed.CopyTo(prev);}while (true);processed *= 255;processed.CopyTo(output);
}
我們的測試框圖如下圖所示:
原二值圖如下圖所示:
細化(提取骨骼)之后的結果圖如下:
Zhang-Suen算法
Guo-Hall算法