图像分割 分水岭法 watershed(C#)

2024-01-03 15:46:03

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。

本文的VB版本请访问:
图像分割 分水岭法 watershed-CSDN博客

Watershed算法是一种图像处理算法,它是基于形态学的图像分割方法。在实现中,Watershed算法通常需要先对图像进行预处理,例如去除噪声、边缘检测等操作,以便更好的构建隔板。在填充山谷时,可以使用队列或其他数据结构来实现,以便管理待处理的像素。对于连通的区域,可以使用标记或者颜色来区分。最终得到的分割结果可以用于物体识别、形状分析等应用场景。

CvInvoke.Watershed方法用于执行图像的分水岭分割操作。该方法声明如下:

public static void Watershed(

?????????? IInputArray image,

?????????? IInputOutputArray markers

)

参数说明:

  1. image:输入图像,必须是CV8U三通道彩色图。
  2. markers:指定的标记图像,这是一个CV32S的单通道图像,并且与输入图像具有相同的尺寸。可以通过FindContours和DrawContours来获得这个图像,使用像数值1、2、3……作为连通分量,来粗略勾勒出图像期望分割的区域,而未确定的区域未标记为0。

该方法没有返回值,而是直接在markers图像上进行分割操作。最终输出的图像,两个区域间使用-1作为分割线。

       //分水岭法 watershed
        private void Button4_Click(object sender, EventArgs e)
        {
            Mat m = new Mat("c:\\learnEmgucv\\tower.jpg", ImreadModes.Color);
            //灰度
            Mat mgray = new Mat();
            CvInvoke.CvtColor(m, mgray, ColorConversion.Bgr2Gray);

            //滤波
            Mat mgaussian = new Mat();
            CvInvoke.GaussianBlur(mgray, mgaussian, new Size(5, 5), 2);
            ImageBox1.Image = mgaussian;

            //边缘检测
            Mat mcanny = new Mat();
            CvInvoke.Canny(mgaussian, mcanny, 60, 120);
            //ImageBox2.Image = mcanny;

            //获取轮廓
            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            VectorOfRect hierarchy = new VectorOfRect();
            CvInvoke.FindContours(mcanny, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxNone);

            Mat mmark = new Mat();
            mmark = Mat.Zeros(mcanny.Rows, mcanny.Cols, DepthType.Cv32S, 1);

            //仅为查看轮廓使用
            Mat mcontours = new Mat(mcanny.Rows, mcanny.Cols, DepthType.Cv8U, 1);
            for (int i = 0; i < contours.Size; i++)
            {
                //标记连通分量索引
                CvInvoke.DrawContours(mmark, contours, i, new MCvScalar(i), 1, LineType.EightConnected, hierarchy);
                //仅为查看轮廓使用
                CvInvoke.DrawContours(mcontours, contours, i, new MCvScalar(255), 1, LineType.EightConnected, hierarchy);
            }
            //仅为查看轮廓使用
            CvInvoke.Imshow("mcontours", mcontours);

            //mmark是32S,显示不了的
            //CvInvoke.Imshow("mmark", mmark);

            //分水岭法
            CvInvoke.Watershed(m, mmark);

            //仅演示,watershed后的数据
            Mat mc = new Mat();
            mc = mmark.Clone();
            //转为可以显示的CV8U
            mc.ConvertTo(mc, DepthType.Cv8U);
            CvInvoke.Imshow("mc", mc);

            //使用Matrix和Image便于生成图像(Mat操作像素点比较麻烦)
            Matrix<Int32> matrwater = new Matrix<Int32>(mmark.Rows, mmark.Cols);
            mmark.CopyTo(matrwater);

            Image<Bgr, byte> imgwater = new Image<Bgr, byte>(mmark.Cols, mmark.Rows);

            //为每个区域填充不同的颜色
            Hashtable HSpointcolor = new Hashtable();       //index,bgr

            int count = 0;
            //对每个像素点操作
            for (int i = 0; i < matrwater.Rows; i++)
            {
                for (int j = 0; j < matrwater.Cols; j++)
                {
                    //获得连通分量(像素点值)
                    Int32 index = matrwater[i, j];
                    //如果是区域的分割线
                    if (index == -1)
                    {
                        imgwater.Data[i, j, 0] = 255;
                        imgwater.Data[i, j, 1] = 255;
                        imgwater.Data[i, j, 2] = 255;
                     }
                    else
                    {
                        Bgr pointcolor;
                        //检查是否已经存在索引对应的颜色
                        if (HSpointcolor.ContainsKey(index))
                        {
                            pointcolor = (Bgr)HSpointcolor[index];
                        }
                        else
                        {
                            //获得随机颜色
                            pointcolor = getRadomBgr();
                            //将已经使用的随机颜色加入List
                            HSpointcolor.Add(index, pointcolor);
                            count += 1;
                        }
                        //像素点填充颜色
                        imgwater.Data[i, j, 0] = (byte)pointcolor.Blue;
                        imgwater.Data[i, j, 1] = (byte)pointcolor.Green;
                        imgwater.Data[i, j, 2] = (byte)pointcolor.Red;
                    }
                }
            }
            //输出使用分水岭法后的结果
            CvInvoke.Imshow("imgwater", imgwater);
        }

            //获得随机颜色
        private Bgr getRadomBgr()
        {
            Random rand = new Random(DateTime.Now.Millisecond);
            byte b = (byte)rand.Next(0, 256);
            byte g = (byte)rand.Next(0, 256);
            byte r = (byte)rand.Next(0, 256);
            return new Bgr(b, r, g);
        }

输出结果如下图所示:

图8-4 分水岭法使用随机颜色填充

由于.net平台下C#和vb.NET很相似,本文也可以为C#爱好者提供参考。

学习更多vb.net知识,请参看vb.net 教程 目录

文章来源:https://blog.csdn.net/UruseiBest/article/details/135361719
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。