灰度图像的腐蚀运算的数学定义为:
其中,g(x,y)为腐蚀后的灰度图像,f(x,y)为原灰度图像,B为结构元素。
用自然语言描述即:
腐蚀运算是由结构元素确定的邻域块中选取图像值与结构元素值的差的最小值。
灰度图像的膨胀运算的数学定义为:
用自然语言描述即:
膨胀运算是由结构元素确定的邻域块中选取图像值与结构元素值的和的最大值
在灰度形态学中,一般选择平坦的结构元素。所谓“平坦”,就是指结构元素的高度为零。因此这样的结构元素,B的值在Db 的定义域内的所有坐标均处均为0,则上面两个公式可以重写为:
我在这对RGB三个通道都进行了处理使得灰度膨胀和腐蚀运算对彩色图像同样有效。但目前对彩色图像的腐蚀和膨胀没有统一的定义,此方法仅作参考。
以下是算法实现(选择的模板为3*3):
/// <summary>
/// 腐蚀运算
/// </summary>
/// <param name="srcBmp">原始图像</param>
/// <param name="dstBmp">目标图像</param>
/// <returns>处理成功 true 失败 false</returns>
public static bool Erode(Bitmap srcBmp, out Bitmap dstBmp) {
if (srcBmp == null) {
dstBmp = null;
return false;
}
Bitmap grayBmp = null;
dstBmp = new Bitmap(srcBmp);
BitmapData bmpDataGray = null;
ToGray(srcBmp, out grayBmp);
bmpDataGray = grayBmp.LockBits(new Rectangle(0, 0, grayBmp.Width, grayBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData bmpDataSrc = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData bmpDataDst = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe {
int[] grayValueArray = new int[25];
long[] index = new long[25];
for (int i = 0; i < 25; i++) {
grayValueArray[i] = 255;
}
byte* ptrSrc = (byte*)bmpDataSrc.Scan0;
byte* ptrDst = (byte*)bmpDataDst.Scan0;
byte* ptrGray = null;
ptrGray = (byte*)bmpDataGray.Scan0;
for (int i = 0; i < bmpDataSrc.Height; i++) {//循环延拓,解决图像四周不能处理的问题
for (int j = 0; j < bmpDataSrc.Width; j++) {
grayValueArray[0] = ptrGray[Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3];
grayValueArray[1] = ptrGray[Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3];
grayValueArray[2] = ptrGray[Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3];
grayValueArray[3] = ptrGray[i * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3];
grayValueArray[4] = ptrGray[i * bmpDataSrc.Stride + j * 3];
grayValueArray[5] = ptrGray[i * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3];
grayValueArray[6] = ptrGray[(i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3];
grayValueArray[7] = ptrGray[(i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3];
grayValueArray[8] = ptrGray[(i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3];
index[0] = Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3;
index[1] = Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3;
index[2] = Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3;
index[3] = i * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3;
index[4] = i * bmpDataSrc.Stride + j * 3;
index[5] = i * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3;
index[6] = (i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3;
index[7] = (i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3;
index[8] = (i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3;
Array.Sort(grayValueArray, index);
ptrDst[i * bmpDataDst.Stride + j * 3] = ptrSrc[index[0]];
ptrDst[i * bmpDataDst.Stride + j * 3 + 1] = ptrSrc[index[0] + 1];
ptrDst[i * bmpDataDst.Stride + j * 3 + 2] = ptrSrc[index[0] + 2];
}
}
srcBmp.UnlockBits(bmpDataSrc);
dstBmp.UnlockBits(bmpDataDst);
}
return true;
}
/// <summary> /// 膨胀运算 /// </summary> /// <param name="srcBmp">原始图像</param> /// <param name="dstBmp">目标图像</param> /// <returns>处理成功 true 失败 false</returns> public static bool Erode(Bitmap srcBmp, out Bitmap dstBmp) { if (srcBmp == null) { dstBmp = null; return false; } Bitmap grayBmp = null; dstBmp = new Bitmap(srcBmp); BitmapData bmpDataGray = null; ToGray(srcBmp, out grayBmp); bmpDataGray = grayBmp.LockBits(new Rectangle(0, 0, grayBmp.Width, grayBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); BitmapData bmpDataSrc = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); BitmapData bmpDataDst = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { int[] grayValueArray = new int[25]; long[] index = new long[25]; for (int i = 0; i < 25; i++) { grayValueArray[i] = 255; } byte* ptrSrc = (byte*)bmpDataSrc.Scan0; byte* ptrDst = (byte*)bmpDataDst.Scan0; byte* ptrGray = null; ptrGray = (byte*)bmpDataGray.Scan0; for (int i = 0; i < bmpDataSrc.Height; i++) { for (int j = 0; j < bmpDataSrc.Width; j++) {//循环延拓,解决图像四周不能处理的问题 grayValueArray[0] = ptrGray[Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3]; grayValueArray[1] = ptrGray[Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3]; grayValueArray[2] = ptrGray[Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3]; grayValueArray[3] = ptrGray[i * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3]; grayValueArray[4] = ptrGray[i * bmpDataSrc.Stride + j * 3]; grayValueArray[5] = ptrGray[i * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3]; grayValueArray[6] = ptrGray[(i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3]; grayValueArray[7] = ptrGray[(i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3]; grayValueArray[8] = ptrGray[(i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3]; index[0] = Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3; index[1] = Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3; index[2] = Math.Abs(i - 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3; index[3] = i * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3; index[4] = i * bmpDataSrc.Stride + j * 3; index[5] = i * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3; index[6] = (i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + Math.Abs(j - 1) % bmpDataSrc.Width * 3; index[7] = (i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + j * 3; index[8] = (i + 1) % bmpDataSrc.Height * bmpDataSrc.Stride + (j + 1) % bmpDataSrc.Width * 3; Array.Sort(grayValueArray, index); ptrDst[i * bmpDataDst.Stride + j * 3] = ptrSrc[index[8]]; ptrDst[i * bmpDataDst.Stride + j * 3 + 1] = ptrSrc[index[8] + 1]; ptrDst[i * bmpDataDst.Stride + j * 3 + 2] = ptrSrc[index[8] + 2]; } } srcBmp.UnlockBits(bmpDataSrc); dstBmp.UnlockBits(bmpDataDst); } return true; }
/// <summary> /// 图像灰度化处理 /// </summary> /// <param name="srcImg">原始图像</param> /// <param name="dstImg">处理后图像</param> /// <returns>处理成功 true 失败 false</returns> public static bool ToGray(Bitmap srcImg, out Bitmap dstImg) { if (srcImg == null) { dstImg = null; return false; } dstImg = new Bitmap(srcImg); Rectangle ret = new Rectangle(0, 0, dstImg.Width, dstImg.Height);//划定要处理的像素区域 BitmapData dstbmpData = dstImg.LockBits(ret, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);//获取像素数据 unsafe { byte temp = 0; byte* dsttempPtr = (byte*)dstbmpData.Scan0; for (int i = 0; i < dstbmpData.Height; i++) { dsttempPtr = (byte*)dstbmpData.Scan0 + i * dstbmpData.Stride; for (int j = 0; j < dstbmpData.Width; j++) {//在内存中的数据顺序是BGR temp = (byte)(0.299 * dsttempPtr[j * 3 + 2] + 0.587 * dsttempPtr[j * 3 + 1] + 0.114 * dsttempPtr[j * 3]); dsttempPtr[j * 3 + 2] = dsttempPtr[j * 3 + 1] = dsttempPtr[j * 3] = temp; } } } dstImg.UnlockBits(dstbmpData);//解锁 return true; }效果图