OpenCVSharp分水岭算法(带注释)

4 篇文章 0 订阅
using System;
using OpenCvSharp;

namespace ConsoleApp1
{
    class Program
    {
        public static void Main()
        {
            // 加载图像      
            Mat src = Cv2.ImRead(@"C:\Users\Tesseract\Desktop\666.png"); // 原图像,后面没怎么用到了
            if (src.Empty())
            {
                Console.WriteLine("Could not open or find the image!");
                return;
            }
            // 显示源图像
            //Cv2.ImShow("Source Image", src);
            // 将背景从白色更改为黑色,因为这将有助于以后在使用“距离变换”时提取更好的结果 -------------------[可以省略]
            /*for (int i = 0; i < src.Rows; i++)
            {
                for (int j = 0; j < src.Cols; j++)
                {
                    if (src.At<Vec3b>(i, j) == new Vec3b(255, 255, 255))
                    {
                        src.At<Vec3b>(i, j)[0] = 0;
                        src.At<Vec3b>(i, j)[1] = 0;
                        src.At<Vec3b>(i, j)[2] = 0;
                    }
                }
            }*/
            // 显示输出图像
            //Cv2.ImShow("Black Background Image", src);
            // 创建一个内核,用于锐化图像
            double[,] k = { {1.0, 1.0, 1.0 },
                          { 1, -8, 1 },
                          { 1, 1, 1} };
            Mat kernel = Mat.FromArray(k);
            // 二阶导数的近似,一个相当强的核做拉普拉斯滤波,因为它是好的
            // ,我们需要把所有的东西转换成比CV_8U更深的东西,因为核有一
            // 些负值,一般来说,我们可以期望拉普拉斯图像具有负值,但8位无
            // 符号整数(我们正在处理的整数)可以包含0到255的值,因此可能
            // 的负数将被截断
            Mat imgLaplacian = new Mat();                               // 拉普拉斯卷积,后面没再用到
            Cv2.Filter2D(src, imgLaplacian, MatType.CV_32F, kernel);    // ------------------[Filter2D卷积函数]
            Mat sharp = new Mat();                                      // 应该是用于锐化处理的中间变量,后面没再用到         
            src.ConvertTo(sharp, MatType.CV_32F);
            Mat imgResult = sharp - imgLaplacian;                       // ------------------[锐化处理后的图像(应该是)]
            // 转换回8位彩图
            imgResult.ConvertTo(imgResult, MatType.CV_8UC3);
            imgLaplacian.ConvertTo(imgLaplacian, MatType.CV_8UC3);
            //Cv2.ImShow( "Laplace Filtered Image", imgLaplacian );
            //Cv2.ImShow("New Sharped Image", imgResult);
            // 从源图像创建二进制图像
            Mat bw = new Mat();                                         // 二值化后的图像,距离变换后就没用到了
            Cv2.CvtColor(imgResult, bw, ColorConversionCodes.BGR2GRAY); // 锐化后的图像转灰度
            Cv2.Threshold(bw, bw, 40, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);    //二值化
            //Cv2.ImShow("Binary Image", bw);
            // ---------------------------- 所以可以我自己预处理成二值化图像(8UC1)后直接从这里开始 ------------------------------
            // 执行距离变换算法
            Mat dist = new Mat();                                       // ------------------[距离图像,后面持续用到]
            Cv2.DistanceTransform(bw, dist, DistanceTypes.L2, DistanceTransformMasks.Mask3);
            // 规格化范围为{0.0,1.0}的距离图像
            // 这样我们就可以将其可视化并设置阈值
            Cv2.Normalize(dist, dist, 0, 1.0, NormTypes.MinMax);        // 距离图像归一化
            Cv2.ImShow("Distance Transform Image", dist);
            // 获得峰值的阈值
            // 这将是前景对象的标记
            Cv2.Threshold(dist, dist, 0.4, 1.0, ThresholdTypes.Binary);
            // 稍微膨胀一下dist图像
            Mat kernel1 = Mat.Ones(new Size(3, 3), MatType.CV_8U);
            Cv2.Dilate(dist, dist, kernel1);                            // 膨胀
            Cv2.ImShow("Peaks", dist);                                  // 峰
            // 创建距离图像的CV_8U版本
            // findContours()需要它
            Mat dist_8u = new Mat();
            dist.ConvertTo(dist_8u, MatType.CV_8U);                     // 距离图像的8位版本
            // 查找总标记
            Point[][] contours;
            HierarchyIndex[] hierarchyIndices;
            Cv2.FindContours(dist_8u, out contours, out hierarchyIndices, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
            // 为分水岭算法创建标记图像 ----------------------------------------------------------[后面大面积用到]
            Mat markers = Mat.Zeros(dist.Size(), MatType.CV_32S);       // 注水点据说是,但是没太看懂,opencv的是基于标记点的分水岭
            // 绘制前景标记
            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(5, 5), 3, new Scalar(255), -1);// 仅用于显示哪里的部分是背景
            Mat markers1 = markers * 10000;                             // 仅用于显示出标记点
            markers1.ConvertTo(markers1, MatType.CV_16S);
            Cv2.ImShow("Markers", markers1);
            // 执行分水岭算法
            Cv2.Watershed(imgResult, markers);                          // 分水岭处理仅需未灰度处理的原图像以及标记点
            Mat mark = new Mat();
            markers.ConvertTo(mark, MatType.CV_8U);
            Cv2.BitwiseNot(mark, mark);
            //Cv2.ImShow("Markers_v2", mark); // 如果您想查看标记图像在该点的外观,请取消注释
            // 生成随机颜色
            Vec3b[] colors = new Vec3b[contours.Length];
            RNG rng = new RNG((ulong)DateTime.Now.Ticks);
            for (int i = 0; i < contours.Length; i++)
            {
                colors[i] = new Vec3b((byte)rng.Uniform(0, 256), (byte)rng.Uniform(0, 256), (byte)rng.Uniform(0, 256));
            }
            // 创建结果图像
            Mat dst = 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)
                    {
                        dst.At<Vec3b>(i, j) = colors[index - 1];
                    }
                }
            }
            // 可视化最终图像
            Cv2.ImShow("Final Result", dst);
            Cv2.WaitKey();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值