OpenCV分水岭算法实现图像分割(给大豆上色) C#

分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
在这里插入图片描述

分水岭图像分割的一般步骤:
  1. 原始图像的”水坝“预处理:视图像情况,通过掩膜对原始图像的边缘特征增强;
  2. 各盆地注水点标注:图像降噪->二值化->通过距离变换提取骨架->归一化->二值化提取盆地中心->轮廓查找->像素填充标注注水点;
  3. 将预处理后”水坝“特征明显的图像和标注水点的图像进行分水岭变化;
  4. 输出图像填充颜色,可视化。
    在这里插入图片描述
API:
public static void Watershed(InputArray image, InputOutputArray markers);

InputArray:预处理后”水坝“特征明显的图像;
InputOutputArray:注水点标注图

关键步骤:注水点标注
在这里插入图片描述

代码演示:

if (fileDialog.ShowDialog() == DialogResult.OK)
{
    picFile = fileDialog.FileName;

    //展示图
    Mat displayImg = Cv2.ImRead(picFile);

    //均值滤波,降噪
    Cv2.Blur(displayImg, displayImg, new OpenCvSharp.Size(3, 3), new Point(-1, -1));

    //标记图
    Mat srcMarkerImg = new Mat(picFile, ImreadModes.Grayscale);

    //均值滤波,降噪
    Cv2.Blur(srcMarkerImg, srcMarkerImg, new OpenCvSharp.Size(3, 3), new Point(-1, -1));

    Cv2.Threshold(srcMarkerImg, srcMarkerImg, 93, 255, ThresholdTypes.BinaryInv);

    var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(12, 12));

    //闭运算,去除图像中的小黑点
    Cv2.MorphologyEx(srcMarkerImg, srcMarkerImg, MorphTypes.Close, kernel);

    Mat distanceImg = new Mat();
    //距离变换
    Cv2.DistanceTransform(srcMarkerImg, distanceImg, DistanceTypes.L1, DistanceTransformMasks.Mask3, 5);

    //归一化
    Cv2.Normalize(distanceImg, distanceImg, 0, 1.0, NormTypes.MinMax);


    //二值化
    Cv2.Threshold(distanceImg, distanceImg, 0.5, 1, ThresholdTypes.Binary);

    distanceImg.ConvertTo(distanceImg, MatType.CV_8UC1);

    //标记结果,即前景色图像
    Mat markers = Mat.Zeros(srcMarkerImg.Size(), MatType.CV_32SC1);

    //找轮廓轮廓
    Cv2.FindContours(distanceImg, out OpenCvSharp.Point[][] contours, out HierarchyIndex[] outputArray, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

    for (int i = 0; i < contours.Length; i++)
           {
               //对各个标记区域填充不同的像素值,后阶段根据像素值区分区域
               Cv2.DrawContours(markers, contours, (int)i, new Scalar((int)i + 1), -1);
           }

    //标记背景
    Cv2.Circle(markers, new Point(15, 15), 10, new Scalar(255), -1);


    Mat displayMakers = new Mat();
    markers.ConvertTo(displayMakers, MatType.CV_8UC1);
    picBox_Process.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(displayMakers * 100);


    displayImg.ConvertTo(displayImg, MatType.CV_8UC3);

    //分水岭操作
    Cv2.Watershed(displayImg, markers);

    //生成随机颜色数组
    Vec3b[] colors = new Vec3b[contours.Length];
    Random rB = new Random();
    Random rG = new Random();
    Random rR = new Random();
    for (int i = 0; i < contours.Length; i++)
           {
               var B = rB.Next(50, 98);
               var G = rG.Next(47, 255);
               var R = rR.Next(56, 120);

               RNG rngB = new RNG((ulong)B);
               RNG rngG = new RNG((ulong)G);
               RNG rngR = new RNG((ulong)R);

               colors[i] = new Vec3b((byte)rngB.Uniform(0, 255), (byte)rngG.Uniform(0, 255), (byte)rngR.Uniform(0, 255));
               Thread.Sleep(200);
           }

    //输出图像
    Mat resultImg = Mat.Zeros(markers.Size(), MatType.CV_8UC3);

    for (int i = 0; i < markers.Rows; i++)
           {
               for (int j = 0; j < markers.Cols; j++)
                    {
                        int index = markers.At<int>(i, j);
                        if (index > 0 && index <= contours.Length)
                        {
                            resultImg.At<Vec3b>(i, j) = colors[index - 1];
                        }
                        else
                        {
                            //填充背景为蓝色
                            resultImg.At<Vec3b>(i, j) = new Vec3b(255, 0, 0);
                        }
                    }
           }

    
    //同原图相加
    Cv2.Absdiff(displayImg, resultImg, resultImg);
    picBox_After.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(resultImg);

    picBox_Display.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(displayImg);
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于分水岭算法图像分割是一种常用的图像处理技术,可以将图像分割成多个区域,每个区域内的像素具有相似的特征。在 OpenCV 中,可以使用 cv2.watershed() 函数实现基于分水岭算法图像分割。 下面是一个简单的 Python 示例,演示如何使用基于分水岭算法图像分割: ```python import cv2 import numpy as np # 读取图像 img = cv2.imread('image.jpg') # 转换为灰度图像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 阈值分割 ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) # 形态学操作 kernel = np.ones((3,3),np.uint8) opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2) # 距离变换 dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5) ret, sure_fg = cv2.threshold(dist_transform,0.1*dist_transform.max(),255,0) # 背景区域 sure_bg = cv2.dilate(opening,kernel,iterations=3) # 不确定区域 sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg,sure_fg) # 标记连通区域 ret, markers = cv2.connectedComponents(sure_fg) markers = markers + 1 markers[unknown==255] = 0 # 应用分水岭算法 markers = cv2.watershed(img,markers) img[markers == -1] = [255,0,0] # 显示结果 cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在上面的示例中,首先读取一张图像,并将其转换为灰度图像。然后使用阈值分割算法将图像二值化。接下来,进行形态学操作,以去除图像中的噪声。然后使用距离变换算法计算前景区域,并将其阈值化。接着,使用形态学操作计算背景区域。最后,使用 cv2.connectedComponents() 函数计算不确定区域,并使用标记连通区域的方法生成分水岭算法的输入标记图像。最后,应用 cv2.watershed() 函数进行图像分割,并在窗口中显示结果。 需要注意的是,分水岭算法的结果依赖于输入标记图像的质量,因此需要根据具体情况进行调整,比如阈值分割的参数、形态学操作的参数、距离变换的参数等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值