OpenCV实现二值图细化(骨架提取)

对二值图进行细化(骨架提取),也就是把每根线条细化到一个像素的宽度。有两个比较成熟的算法实现此功能,分别是Zhang-Suen算法和Guo-Hall算法。

我们下面使用OpenCVSharp,使用C#实现上述两个算法:

cs 复制代码
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_ZHANGSUEN
    if (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 p5
            byte 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_GUOHALL
    else 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 p5
            byte 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算法

相关推荐
晨间晓2 天前
图片里的高频和低频是什么?用华为云码道开发 OpenCV 傅里叶频谱分析工具
opencv·计算机视觉·华为云
晨间晓2 天前
图片里的阴影能不能减弱?用华为云码道开发 OpenCV 光照校正工具
人工智能·opencv·华为云
晨间晓2 天前
图片里的细小噪点怎么平滑?用华为云码道开发 OpenCV 图像滤波对比工具
人工智能·opencv·华为云
偷吃米饭2 天前
图片特征怎么描述?用华为云码道开发 OpenCV ORB 特征提取工具
人工智能·opencv·华为云
晨间晓2 天前
图片里的关键点怎么找?用华为云码道开发 OpenCV FAST 特征点检测工具
人工智能·opencv·华为云
晨间晓2 天前
二值图里的噪点怎么去掉?用华为云码道开发 OpenCV 形态学操作工具
人工智能·opencv·华为云
晨间晓2 天前
图片里的角点在哪里?用华为云码道开发 OpenCV Harris 角点检测工具
人工智能·opencv·华为云
晨间晓3 天前
图片细节丰富不丰富?用华为云码道开发 OpenCV 纹理复杂度分析工具
人工智能·opencv·华为云
richard_yuu3 天前
OpenCV 实战第 15 篇:G-API 图计算,工业流水线的 CPU/GPU 混合执行
人工智能·opencv·计算机视觉
老唐7773 天前
OpenCV5实测:纯CPU跑YOLO26目标检测
图像处理·人工智能·opencv·yolo·目标检测·计算机视觉