膨胀是腐蚀运算的对偶运算,可以通过对补集的腐蚀来定义。我们以Ac表示集合A的补集,Bv表示B关于坐标原点的反射。那么,集合A被集合B膨胀表示为 A⊕B,其定义为
为了利用结构元素B膨胀集合A,可将B相对原点旋转180度得到Bv,再利用Bv对Ac进行腐蚀。
下面以 QR 码为例说明膨胀操作一种应用。QR 码是一种普遍使用的矩阵式二维条码,除具有二 维条码所具有的信息容量大、可靠性高、可表示汉字及图象多种信息、保密防伪性强等优点外,还具 有以下特点:①超高速识读,从 QR 是英文“Quick Response(快速反应)”的缩写就不难理解其适宜 应用于工业自动化生产线管理等领域;②全方位识读识读特点;③能够有效地表示汉字。QR 码的 识别软件甚至可以安装在智能手机上,为用户提供了极大的方便。
通过图像的采集设备,我们得到含有条码的图像,此后主要经过条码定位、分割和解码三个步骤 实现条码的识别。条码的定位就是找到条码符号的图像区域,对有明显条码特征的区域进行定位。然 后,根据不同条码的定位图形结构特征对不同的条码符号进行下一步的处理。实现条码的定位采用以 下步骤:①对图像进行二值化处理;②对其二值化图像进行膨胀运算;③对膨胀后的图像进行边缘 检测得到条码区域的轮廓。找到条码区域后,还要进一步区分矩阵式条码的类型。
QR 码的解码过程如下:得到一幅标准的条码图像后,对该符号进行网格采样,对网格每一个交 点上的图像像素取样,并根据阈值确定是深色块还是浅色块。构造一个位图,用二进制的“1”表示深 色像素,“0”表示浅色像素,从而得到条码的原始二进制序列值,然后对这些数据进行纠错和译码, 最后根据条码的逻辑编码规则把这些原始的数据位流转换成数据码字。
膨胀和腐蚀这两种运算是紧密联系在一起的,一个运算对图像目标的操作相当于另一个运算对图像背景的操作,其对偶性可表示为
由以上公式和图示可以得出:腐蚀是对图像内部做滤波处理,而膨胀是利用结构元素对图像补集进行填充,因而它是对图像外部做滤波处理。腐蚀具有收缩图像的作用,膨胀具有扩大图像的作用。
示例:数学形态学膨胀图像
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace ImageDilate
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Image image2 = DilateImage(pictureBox1.Image);
pictureBox2.Image = image2;
}
//数学形态学—膨胀图片
private Image DilateImage(Image image)
{
//定义一个3x3的结构模板,原点[0,0]。
int[,] templet = new int[,] {
{ 1, 1, 1 },
{ 1, 1, 1 },
{ 1, 1, 1 }
};
Bitmap bmp = new Bitmap(image);
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
Bitmap bmp_clone = bmp.Clone(rect, PixelFormat.Format24bppRgb);
for (int y=0; y<bmp.Height; y++)
{
for (int x=0; x< bmp.Width; x++)
{
if (IsMatchTemplet(x, y, bmp, templet))
{
//膨胀(即,腐蚀背景)
DilateTemplet(x, y, bmp_clone, templet);
}
}
}
return bmp_clone;
}
//判断模板是否击中目标图像
//模板中1对应目标图像,0对应背景
private bool IsMatchTemplet(int x, int y, Bitmap bmp, int[,] templet)
{
bool match = false;
int rowLength = templet.GetLength(0);
int colLength = templet.GetLength(1);
for (int i=0; i<rowLength; i++)
{
if (y+i >= bmp.Height)
{
goto EXIT;
}
for (int j=0; j<colLength; j++)
{
if (x+j >= bmp.Width)
{
break;
}
Color c = bmp.GetPixel(x+j, y+i);
//需要根据实际情况做判断,本示例仅判断R通道
int v = templet[i, j];
//模板中有一个为1的位置击中目标图像,则认为匹配成功
match = (c.R == 255 && v == 1);
if (match)
goto EXIT;
}
}
EXIT:
return match;
}
//对模板覆盖区域进行膨胀处理
private void DilateTemplet(int x, int y, Bitmap bmp, int[,] templet)
{
int rowLength = templet.GetLength(0);
int colLength = templet.GetLength(1);
for (int i = 0; i < rowLength; i++)
{
if (y + i >= bmp.Height)
break;
for (int j = 0; j < colLength; j++)
{
if (x + j >= bmp.Width)
break;
int v = templet[i, j];
//模板为1的位置全部替换成目标图像
if (v == 1)
bmp.SetPixel(x+j, y+i, Color.White);
}
}
}
}
}