MorphologyEx形态操作
函数说明:高级形态操作,包含膨胀、腐蚀、开运算、闭运算、梯度、顶帽、黑帽和击中击不中操作。支持就地操作,可处理多通道图像,各通道分开处理。
//函数原型
void MorphologyEx(InputArray src,
OutputArray dst,
MorphTypes op,
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。HitMiss时只能CV_8UC1。 |
OutputArray dst | 输出图像,与输入图像有相同的大小、类型和通道数。(支持就地模式) |
MorphTypes op | 形态操作类型 |
InputArray? element | 结构元素,可通过GetStructuringElement生成 |
Point? anchor | 锚点,结构元素的锚点。默认为(-1,-1),表示结构元素的中心点 |
int iterations | 形态操作次数,默认为1次 |
BorderTypes borderType | 边缘填充方式,不支持Wrap。 |
Scalar? borderValue | 当边界填充方式为Constant时的填充值,默认是Scalar.All(double.MaxValue) |
MorphTypes形态操作类型
值 | 说明 |
Erode | 腐蚀 |
Dilate | 膨胀 |
Open | 开运算:先腐蚀,再膨胀。效果:删除小干扰块 dst=open(src,element)=dilate(erode(src,element)) |
Close | 闭运算:先膨胀,再腐蚀。效果:填充封闭区域 dst=close(src,element)=erode(dilate(src,element)) |
Gradient | 梯度操作:膨胀的结果减腐蚀的结果 dst=morph_grad(src,element)=dilate(src,element)−erode(src,element) |
TopHat | 顶帽操作:原图减开运算 dst=tophat(src,element)=src−open(src,element) |
BlackHat | 黑帽操作:闭运算 减 原图 dst=blackhat(src,element)=close(src,element)−src |
HitMiss | 击中与击不中操作:只支持CV-8UC1的二值图 |
图像示例
HitMiss击中与击不中
分析:图A为待测试的原图,使用图B当HitMiss的结构元素时得到图C,使用图D当HitMiss的结构元素时得到图E(注意,图C和图E右下角,有个边缘填充数字123)。图F、图G为击中时的两个示例。
网上关于HitMiss介绍的资料不多,自己分析,认为HitMiss的算法大概是,使用结构元素大小的核扫描原图,若结构元素中为1的位置在原图中也为1(结构元素为0的位置,不校验;结构元素为-1的位置,匹配原图的0),则在锚点位置标为1,表示击中;若不能完全匹配,则在锚点位置标为0,表示不击中。边缘的填充与填充方式和结构元素有关。
源码示例
private string winName = "MorphologyEx Demo";
int morph_elem = 0;//结构元素形状
int morph_size = 3;//结构元素大小,实际大小为2*morph_size+1
int morph_operator = 0;//形态操作类型
const int max_operator = 7;
const int max_elem = 2;
const int max_kernel_size = 21;
Mat src;
public void Run() {
TestHitMiss();
src = Cv2.ImRead(ImagePath.Morphology, ImreadModes.Color);
if (src.Empty()) throw new Exception($"图像打开有误:{ImagePath.Morphology}");
Cv2.ImShow("src", src);
Cv2.NamedWindow(winName, WindowFlags.GuiExpanded);
//滚动条拉动太多次时,会导致 morph_operator 值无法与pos一致?原因待分析。
//形态操作类型
Cv2.CreateTrackbar("Operator", winName, ref morph_operator, max_operator, Morphology_Operations);
//结构元素形状
Cv2.CreateTrackbar("Shapes", winName, ref morph_elem, max_elem, Morphology_Operations);
//结构元素大小
Cv2.CreateTrackbar("Size:2n +1", winName, ref morph_size, max_kernel_size, Morphology_Operations);
Cv2.WaitKey();
Cv2.DestroyAllWindows();
src.Dispose();
}
/// <summary>
/// 测试HitMiss
/// </summary>
private void TestHitMiss() {
using (var src = 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,
1,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})) {
//控制台中输出矩阵,可参考https://blog.csdn.net/TyroneKing/article/details/129484746
Helper.Dump(src);
var element = new Mat(3, 3, MatType.CV_8UC1, new Byte[] {0,0,0,
0,0,1,
0,1,1});
Helper.Dump(element);
using var dst = new Mat();
Cv2.MorphologyEx(src, dst, MorphTypes.HitMiss, element, borderValue: new Scalar(123));
Helper.Dump(dst);
element = new Mat(3, 3, MatType.CV_8UC1, new Byte[] {0,0,1,
0,0,1,
1,1,1});
Helper.Dump(element);
Cv2.MorphologyEx(src, dst, MorphTypes.HitMiss, element, borderValue: new Scalar(123));
Helper.Dump(dst);
//全0结构元素测试,结果与原图一样
element = new Mat(3, 3, MatType.CV_8UC1, new Byte[] {0,0,0,
0,0,0,
0,0,0});
Helper.Dump(element);
Cv2.MorphologyEx(src, dst, MorphTypes.HitMiss, element, borderValue: new Scalar(123));
Helper.Dump(dst);
}
}
/// <summary>
/// 形态操作,
/// </summary>
/// <param name="pos"></param>
/// <param name="userdata"></param>
private void Morphology_Operations(int pos, IntPtr userdata) {
//结构元素的大小
var mSize = 2 * morph_size + 1;
//获取结构元素
Mat element = Cv2.GetStructuringElement((MorphShapes)morph_elem, new Size(mSize, mSize), new Point(morph_size, morph_size));
var dst = new Mat();
if (morph_operator == 7) {
//HitMiss只支持CV_8UC1
Cv2.CvtColor(src, dst, ColorConversionCodes.BGR2GRAY);
Cv2.MorphologyEx(dst, dst, (MorphTypes)morph_operator, element);
dst = dst.CvtColor(ColorConversionCodes.GRAY2BGR);
}
else {
//形态操作
Cv2.MorphologyEx(src, dst, (MorphTypes)morph_operator, element);
}
var texts = new string[] { $"MorphTypes={(MorphTypes)morph_operator}", $"MorphShapes={(MorphShapes)morph_elem}", $"ElementSize={mSize}" };
var colors = new Scalar[] { Scalar.Red, Scalar.Green, Scalar.Blue };
DrawLabels(dst, texts, colors);
Cv2.ImShow(winName, dst);
}
public void DrawLabels(Mat dst,string[] texts, Scalar[] colors) {
int vPos = 0;
for (int i = 0; i < texts.Length; i++) {
Size textsize = Cv2.GetTextSize(texts[i], HersheyFonts.HersheyComplex, 1, 1, out int baseLine);
vPos += (int)(textsize.Height * 1.4);
Point org = new Point((src.Cols - textsize.Width), vPos);
Cv2.PutText(dst, texts[i], org, HersheyFonts.HersheyComplex, 1, colors[i], 1, LineTypes.Link8);
}
}
参考