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
参考