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