Dilate膨胀
函数说明:用特定的结构元素膨胀图像。膨胀可以看成是最大值滤波,即用最大值替换中心像素点。该函数就地模式,可以指定迭代次数,多通道图像的话,每个通道分开处理。
用3x3矩形结构元素膨胀

//函数原型
void Dilate(InputArray src,OutputArray dst,InputArray? element,Point? anchor = null,int iterations = 1,BorderTypes borderType = BorderTypes.Constant,Scalar? borderValue = null)
参数 | 说明 |
InputArray src | 输入图像,通道数可以任意,类型必须为CV_8U,CV_16U, CV_16S, CV_32F或CV_64F。 |
OutputArray dst | 输出图像,与输入图像有相同的大小、类型和通道数。(支持就地模式) |
InputArray? element | 用于膨胀的结构元素。为null或空Mat时,默认使用3x3矩形结构元素。可通过GetStructuringElement函数生成。 |
Point? anchor = null | 锚点,结构元素的锚点。默认为(-1,-1),表示结构元素的中心点 |
int iterations = 1 | 膨胀的次数 |
BorderTypes borderType = BorderTypes.Constant | 边界填充方式。不支持Wrap方式 |
Scalar? borderValue = null | 当边界填充方式为Constant时的填充值,默认是Scalar.All(double.MaxValue) |
GetStructuringElement获取形态操作的结构元素
函数说明:返回一个指定大小、形状的结构元素,用于膨胀、腐蚀或其它形态操作。
//函数原型1
Mat GetStructuringElement(MorphShapes shape,Size ksize)//函数原型2
Mat GetStructuringElement(MorphShapes shape, Size ksize, Point anchor)
参数 | 说明 |
MorphShapes shape | 形态形状:Rect矩形、Cross十字、Ellipse椭圆 |
Size ksize | 元素大小 |
Point anchor | 元素内的锚定位置。默认值(-1,-1)表示锚点位于中心。注意,只有十字形元素的形状取决于锚点位置。在其他情况下,锚只是调节形态学操作的结果被转移了多少。 |
返回值Mat | 结构元素矩阵 |
Erode腐蚀
函数说明:用特定的结构元素腐蚀图像。腐蚀可以看成是最小值滤波,即用最小值替换中心像素点。
该函数就地模式,可以指定迭代次数,多通道图像的话,每个通道分开处理。
用3x3矩形结构元素腐蚀

//函数原型
void Erode(InputArray src,OutputArray dst,InputArray? element,Point? anchor = null,int iterations = 1,BorderTypes borderType = BorderTypes.Constant,Scalar? borderValue = null)
参数 | 说明 |
InputArray src | 输入图像,通道数可以任意,类型必须为CV_8U,CV_16U, CV_16S, CV_32F或CV_64F。 |
OutputArray dst | 输出图像,与输入图像有相同的大小、类型和通道数。 (支持就地模式) |
InputArray? element | 用于腐蚀的结构元素。为null或空Mat时,默认使用3x3矩形结构元素。可通过GetStructuringElement函数生成。 |
Point? anchor = null | 锚点,结构元素的锚点。 默认为(-1,-1),表示结构元素的中心点 |
int iterations = 1 | 腐蚀次数 |
BorderTypes borderType = BorderTypes.Constant | 边界填充方式。 不支持Wrap方式 |
Scalar? borderValue = null | 当边界填充方式为Constant时的填充值,默认是Scalar.All(double.MaxValue) |
图像示例
原图

膨胀与腐蚀

定位下划线位置

源码示例
public void Run() {TestDilateAndErode();MorphologyDemo();LocateGapFilling();
}/// <summary>
/// 定位下划线
/// </summary>
/// <exception cref="Exception"></exception>
private void LocateGapFilling() {using (var src = Cv2.ImRead(ImagePath.GapFilling, ImreadModes.Grayscale)) {if (src.Empty()) throw new Exception($"图像打开有误:{ImagePath.GapFilling}");Cv2.Threshold(src, src, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);Cv2.ImShow("GapFilling", src);using var dst = new Mat();var element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(15, 1));//可消除部分垂直线Cv2.Erode(src, dst, element);ShowMat("Location", element, dst);Cv2.WaitKey();Cv2.DestroyAllWindows();}
}/// <summary>
/// 膨胀、腐蚀
/// </summary>
/// <exception cref="Exception"></exception>
private void MorphologyDemo() {using (var src = Cv2.ImRead(ImagePath.Morphology, ImreadModes.Grayscale)) {if (src.Empty()) throw new Exception($"图像打开有误:{ImagePath.Morphology}");Cv2.Threshold(src, src, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);Cv2.ImShow("gray", src);using var dst = new Mat();var element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(15, 15));//膨胀Cv2.Dilate(src, dst, element);ShowMat("Dilate Rect", element, dst);//腐蚀Cv2.Erode(src, dst, element);ShowMat("Erode Rect", element, dst);Cv2.WaitKey();Cv2.DestroyAllWindows();}
}private void ShowMat(string winNameFix, Mat element, Mat dst) {Cv2.ImShow($"{winNameFix},{element.Size()}", dst);
}/// <summary>
/// 演示Dilate与Erode,细看其中的不同。
/// 注意:一幅图像膨胀后再腐蚀的图像,不一定与原图一样。
/// </summary>
private void TestDilateAndErode() {using (var mat = new Mat(11, 15, MatType.CV_8UC1, new byte[] { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0})) {Helper.Dump(mat);var element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));//膨胀Cv2.Dilate(mat, mat, null, iterations: 2);Console.WriteLine("Dilate X 2");Dump(mat);using var dst = new Mat();//加上空白边缘Cv2.CopyMakeBorder(mat, dst, 1, 1, 1, 1, BorderTypes.Constant, new Scalar(0, 0, 0));Cv2.Erode(mat, mat, null);Console.WriteLine("The first time of Erode");Dump(mat);Cv2.Erode(mat, mat, null);//可连接腐蚀多次//Cv2.Erode(mat,mat,null,iterations: 2);Console.WriteLine("The second time of Erode");Dump(mat);//加了空白边缘的图像Console.WriteLine("dst");Dump(dst);//对比前面的腐蚀结果//腐蚀Cv2.Erode(dst, dst, null);Console.WriteLine("ErodeX1");Dump(dst);Cv2.Erode(dst, dst, null);Console.WriteLine("ErodeX2");Dump(dst);}
}#region Dump
/// <summary>
/// 控制台显示矩阵
/// </summary>
/// <param name="mat">待显示的矩阵</param>
/// <param name="dumpChannel">要显示通道</param>
public static void Dump(Mat mat, FormatType formatType = FormatType.Default) {Dump(mat, new OpenCvSharp.Range(0, mat.Channels() - 1), formatType);
}
/// <summary>
/// 控制台显示矩阵
/// </summary>
/// <param name="mat">等显示的矩阵</param>
/// <param name="channelRange">要显示的通道范围</param>
public static void Dump(Mat mat, OpenCvSharp.Range channelRange, FormatType formatType = FormatType.Default) {var nChannels = mat.Channels();if (nChannels == 1) {Console.WriteLine(mat.Dump(formatType));return;}var splits = mat.Split();for (int c = channelRange.Start; c <= channelRange.End; c++) {if (c < nChannels) {Console.WriteLine(splits[c].Dump(formatType));}else {Console.WriteLine($"通道值{c}必须小于{nChannels}");}}
}
#endregion
OpenCvSharp函数示例(目录)
参考
https://docs.opencv.org/
https://edu.csdn.net/learn/38286/608282