OpenCV watershed algorithm implements image segmentation (coloring soybeans) C#

The watershed segmentation method is a mathematical morphological segmentation method based on topology theory. Its basic idea is to regard the image as a topological topography in geodesy. The gray value of each pixel in the image represents the altitude of that point. Each local minimum and its area of ​​influence is called a catchment basin, and the boundaries of the catchment basin form a watershed. The concept and formation of watersheds can be illustrated by simulating the immersion process. A small hole is pierced on the surface of each local minimum, and then the entire model is slowly immersed in water. As the immersion deepens, the influence domain of each local minimum slowly expands outward. Constructing a dam at the confluence of basins forms a watershed.
Insert image description here

General steps for watershed image segmentation:

  1. "Dam" preprocessing of the original image: Depending on the image situation, the edge features of the original image are enhanced through masks;
  2. Labeling of water injection points in each basin: Image denoising -> Binarization -> Skeleton extraction through distance transformation -> Normalization -> Binarization to extract basin center -> Contour search -> Pixel filling to mark water injection points;
  3. Perform watershed changes on preprocessed images with obvious "dam" features and images with water points annotated;
  4. Output image fill color,visualization.
    Insert image description here

API:

public static void Watershed(InputArray image, InputOutputArray markers);

InputArray: image with obvious "dam" features after preprocessing;
InputOutputArray: water injection point labeling map

Key steps: water injection point marking
Insert image description here

Code demo:

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);
}

Insert image description here

Guess you like

Origin blog.csdn.net/weixin_40671962/article/details/128056798
Recommended