图像处理工具箱
还只是个菜鸟,不过想开个博客,记录一下自己的学习过程!
最近在一家医疗软件公司实习,负责的是OCR(光学字符识别)部分!进行OCR之前难免会碰到图像处理,因此写了一个图像处理工具箱(ImageProcessUtilities),看网上有人讲过在进行OCR之前,进行图像二值化,图像锐化,中值滤波,线性灰度变化,转换为黑白灰度图完全就足够了!
这个工具箱大概也是实现这些功能,首先类视图,
下面给出代码,希望大家多批评指下,之后会后续发布OCR检索其他方面的工具,诸如文字后处理的正则表达等!
public class ImageProcessUtility { //暂时只对24位图和8位图进行处理 #region 图像扩边函数 /// <summary> /// 图像扩边函数 /// </summary> /// <param name="width">扩边后图像的宽度</param> /// <param name="height">扩边后图像的高度</param> /// <param name="inputBitmap">需要扩边的图像</param> /// <returns>完成扩边后的图像</returns> public static Bitmap ImageExpand(int width, int height, Bitmap inputBitmap) { try { Bitmap bitmap = new Bitmap(width, height); Graphics g = Graphics.FromImage(bitmap); g.Clear(Color.Black); int start_X = (width - inputBitmap.Width) / 2; int start_Y = (height - inputBitmap.Height) / 2; g.DrawImage(inputBitmap, new Point(start_X, start_Y)); g.Dispose(); return bitmap; } catch { return null; } } #endregion #region 图像缩放函数 /// <summary> /// 图像缩放函数 /// </summary> /// <param name="scale">缩放因子</param> /// <param name="inputBitmap">需要缩放的图像</param> /// <returns>缩放后的图像</returns> public static Bitmap ImageResize(double scale, Bitmap inputBitmap) { try { int newWidth = Convert.ToInt32(inputBitmap.Width * scale); int newHeight = Convert.ToInt32(inputBitmap.Height * scale); Bitmap bitmap = new Bitmap(newWidth, newHeight); Graphics g = Graphics.FromImage(bitmap); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.DrawImage(inputBitmap, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0,0,inputBitmap.Width, inputBitmap.Height), GraphicsUnit.Pixel); g.Dispose(); return bitmap; } catch { return null; } } #endregion #region 转换为8位灰度图像 /// <summary> /// 将图像转换成8位Bitmap图像函数 /// </summary> /// <param name="inputBitmap">要转换的图像</param> /// <returns>转换后的图像</returns> public static Bitmap ToGray(Bitmap inputBitmap) { Bitmap bmp = new Bitmap(inputBitmap.Width, inputBitmap.Height, PixelFormat.Format8bppIndexed); //设定实例BitmapData相关信息 Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData data = inputBitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); //锁定bmp到系统内存中 BitmapData data2 = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); //获取位图中第一个像素数据的地址 IntPtr ptr = data.Scan0; IntPtr ptr2 = data2.Scan0; int numBytes = data.Stride * data.Height; int numBytes2 = data2.Stride * data2.Height; int n2 = data2.Stride - bmp.Width; //// 显示宽度与扫描线宽度的间隙 byte[] rgbValues = new byte[numBytes]; byte[] rgbValues2 = new byte[numBytes2]; //将bmp数据Copy到申明的数组中 Marshal.Copy(ptr, rgbValues, 0, numBytes); Marshal.Copy(ptr2, rgbValues2, 0, numBytes2); int n = 0; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width * 3; x += 3) { int i = data.Stride * y + x; double value = rgbValues[i + 2] * 0.299 + rgbValues[i + 1] * 0.587 + rgbValues[i] * 0.114; //计算灰度 rgbValues2[n] = (byte)value; n++; } n += n2; //跳过差值 } //将数据Copy到内存指针 Marshal.Copy(rgbValues, 0, ptr, numBytes); Marshal.Copy(rgbValues2, 0, ptr2, numBytes2); //// 下面的代码是为了修改生成位图的索引表,从伪彩修改为灰度 ColorPalette tempPalette; using (Bitmap tempBmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed)) { tempPalette = tempBmp.Palette; } for (int i = 0; i < 256; i++) { tempPalette.Entries[i] = Color.FromArgb(i, i, i); } bmp.Palette = tempPalette; //从系统内存解锁bmp inputBitmap.UnlockBits(data); bmp.UnlockBits(data2); return bmp; } #endregion #region 转换为二值图像 /// <summary> /// 图像二值化函数 /// </summary> /// <param name="grayValue">需要二值化的图像</param> /// <param name="inputBitmap">阈值</param> /// <returns>完成二值化后的图像</returns> public static Bitmap ToBinary(int grayValue, Bitmap inputBitmap) { //获取图像长宽 int w = inputBitmap.Width; int h = inputBitmap.Height; //锁定位图信息 Bitmap dstBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); System.Drawing.Imaging.BitmapData srcData = inputBitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb); System.Drawing.Imaging.BitmapData dstData = dstBitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb); unsafe { byte* pIn = (byte*)srcData.Scan0.ToPointer(); byte* pOut = (byte*)dstData.Scan0.ToPointer(); byte* p; int stride = srcData.Stride; int r, g, b; //二值化过程 for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { p = pIn; r = p[2]; g = p[1]; b = p[0]; //灰度值等于V的为黑色,反之为白色 pOut[0] = pOut[1] = pOut[2] = (byte)(((byte)(r << 16 | g << 8 | b) == grayValue) ? 255 : 0); pIn += 3; pOut += 3; } pIn += srcData.Stride - w * 3; pOut += srcData.Stride - w * 3; } //解除锁定 inputBitmap.UnlockBits(srcData); dstBitmap.UnlockBits(dstData); //返回处理后的图像 return dstBitmap; } } #endregion #region 中值滤波 /// <summary> /// 图像中值滤波,在进行滤波前, /// 若是RGB图,需要将其转换为灰度图 /// </summary> /// <param name="mode">窗口模式: /// 0:3*3窗口 /// 1:5*5窗口 /// </param> /// <param name="inputBitmap"></param> /// <returns></returns> public static Bitmap ImageMedianFilter(int mode, Bitmap inputBitmap) { try { if (inputBitmap.PixelFormat == PixelFormat.Format24bppRgb) inputBitmap = ToGray(inputBitmap); Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); BitmapData bmpData = inputBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); IntPtr ptr = bmpData.Scan0; int bytes = bmpData.Stride * bmpData.Height; byte[] grayValues = new byte[bytes]; Marshal.Copy(ptr, grayValues, 0, bytes); byte[] tempArray = new byte[bytes]; for (int i = 0; i < bytes; i++) { tempArray[i] = 0; } switch (mode) { case 0: for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { List<byte> sortArray = new List<byte>(); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j - 1]); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j]); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j + 1]); sortArray.Add(grayValues[i * bmpData.Stride + j - 1]); sortArray.Add(grayValues[i * bmpData.Stride + j]); sortArray.Add(grayValues[i * bmpData.Stride + j + 1]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j - 1]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j + 1]); sortArray.Sort(); tempArray[i * bmpData.Stride + j] = sortArray[4]; } } break; case 1: for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { List<byte> sortArray = new List<byte>(); sortArray.Add(grayValues[(i - 2) * bmpData.Stride + j - 2]); sortArray.Add(grayValues[(i - 2) * bmpData.Stride + j - 1]); sortArray.Add(grayValues[(i - 2) * bmpData.Stride + j]); sortArray.Add(grayValues[(i - 2) * bmpData.Stride + j + 1]); sortArray.Add(grayValues[(i - 2) * bmpData.Stride + j + 2]); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j - 2]); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j - 1]); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j]); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j + 1]); sortArray.Add(grayValues[(i - 1) * bmpData.Stride + j + 2]); sortArray.Add(grayValues[i * bmpData.Stride + j - 2]); sortArray.Add(grayValues[i * bmpData.Stride + j - 1]); sortArray.Add(grayValues[i * bmpData.Stride + j]); sortArray.Add(grayValues[i * bmpData.Stride + j + 1]); sortArray.Add(grayValues[i * bmpData.Stride + j + 2]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j - 2]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j - 1]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j + 1]); sortArray.Add(grayValues[(i + 1) * bmpData.Stride + j + 2]); sortArray.Add(grayValues[(i + 2) * bmpData.Stride + j - 2]); sortArray.Add(grayValues[(i + 2) * bmpData.Stride + j - 1]); sortArray.Add(grayValues[(i + 2) * bmpData.Stride + j]); sortArray.Add(grayValues[(i + 2) * bmpData.Stride + j + 1]); sortArray.Add(grayValues[(i + 2) * bmpData.Stride + j + 2]); sortArray.Sort(); tempArray[i * bmpData.Stride + j] = sortArray[4]; } } break; default: break; } grayValues = (byte[])tempArray.Clone(); Marshal.Copy(grayValues, 0, ptr, bytes); inputBitmap.UnlockBits(bmpData); return inputBitmap; } catch { return null; } } #endregion #region 图像锐化 /// <summary> /// 图像锐化 /// </summary> /// <param name="depth"> /// 锐化的程序,分布在[0,1],值越大锐化程序越高 /// </param> /// <param name="inputBitmap">需要锐化的图像</param> /// <returns>锐化的结果图</returns> public static Bitmap ImageSharpen(double depth, Bitmap inputBitmap) { if (inputBitmap == null) return null; if (depth < 0.0) depth = 0.0; else if (depth > 1.0) depth = 1.0; if (inputBitmap.PixelFormat == PixelFormat.Format24bppRgb) inputBitmap = ToGray(inputBitmap); BitmapData srcData = inputBitmap.LockBits(new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); IntPtr ptr = srcData.Scan0; int bytes = srcData.Stride * srcData.Height; byte[] grayValues = new byte[bytes]; Marshal.Copy(ptr, grayValues, 0, bytes); //存储周边边缘的差值 double temp; for (int i = 1; i < srcData.Height - 1; i++) { for (int j = 1; j < srcData.Width - 1; j++) { temp = grayValues[i * srcData.Stride + j] - ((grayValues[(i - 1) * srcData.Stride + j - 1] + grayValues[(i - 1) * srcData.Stride + j] + grayValues[(i - 1) * srcData.Stride + j + 1] + grayValues[i * srcData.Stride + j - 1] + grayValues[i * srcData.Stride + j + 1] + grayValues[(i + 1) * srcData.Stride + j - 1] + grayValues[(i + 1) * srcData.Stride + j] + grayValues[(i + 1) * srcData.Stride + j + 1])) / 8; if (temp < 0.0) temp = 0.0; grayValues[i * srcData.Stride + j] -= Convert.ToByte(temp * depth); } } Marshal.Copy(grayValues, 0, ptr, bytes); inputBitmap.UnlockBits(srcData); return inputBitmap; } #endregion #region 图像膨胀函数 /// <summary> /// 图像膨胀函数 /// </summary> /// <param name="mode">结构元素 /// 0:3位水平方向的结构元素 /// 1:5位水平方向的结构元素 /// 2:3位垂直方向的结构元素 /// 3:5位垂直方向的结构元素 /// 4:3位十字状结构元素 /// 5:5位十字状结构元素 /// 6:3位方形结构元素 /// 7:5位方形结构元素 /// </param> /// <param name="inputBitmap">需要膨胀的图像</param> /// <returns>返回膨胀后的图像</returns> public static Bitmap ImageDilate(int mode, Bitmap inputBitmap) { try { //先以255作为阈值分割点 inputBitmap = ToGray(inputBitmap); inputBitmap = ToBinary(255, inputBitmap); Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); //以读写的方式锁定全部位图像素 System.Drawing.Imaging.BitmapData bmpData = inputBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); IntPtr ptr = bmpData.Scan0; //将图像数据复制到数组中,用于膨胀操作 int bytes = bmpData.Stride * bmpData.Height; byte[] grayValues = new byte[bytes]; Marshal.Copy(ptr, grayValues, 0, bytes); //设置临时数组,用来存储膨胀操作的结果 byte[] tempArray = new byte[bytes]; for (int i = 0; i < bytes; i++) tempArray[i] = 0; switch (mode) { //3位水平方向的结构元素 case 0: for (int i = 0; i < bmpData.Height; i++) { for (int j = 1; j < bmpData.Width-1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; //5位水平方向的结构元素 case 1: for (int i = 0; i < bmpData.Height; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j + 2] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[i * bmpData.Stride + j - 2] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; //3位垂直方向的结构元素 case 2: for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 0; j < bmpData.Width; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; //5位垂直方向的结构元素 case 3: for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 0; j < bmpData.Width; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[(i - 2) * bmpData.Stride + j] == 255 || grayValues[(i + 2) * bmpData.Stride + j] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; //3位十字形状结构元素 case 4: for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; //5位十字形状结构元素 case 5: for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i - 2) * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[i * bmpData.Stride + j - 2] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; //3位方形结构元素 case 6: for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i - 1) * bmpData.Stride + j + 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j + 1] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; //5位方形结构元素 case 7: for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[(i - 2) * bmpData.Stride + j - 2] == 255 || grayValues[(i - 2) * bmpData.Stride + j - 1] == 255 || grayValues[(i - 2) * bmpData.Stride + j] == 255 || grayValues[(i - 2) * bmpData.Stride + j + 1] == 255 || grayValues[(i - 2) * bmpData.Stride + j + 2] == 255 || grayValues[(i - 1) * bmpData.Stride + j - 2] == 255 || grayValues[(i - 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j + 1] == 255 || grayValues[(i - 1) * bmpData.Stride + j + 2] == 255 || grayValues[i * bmpData.Stride + j - 2] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[i * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j + 2] == 255 || grayValues[(i + 1) * bmpData.Stride + j - 2] == 255 || grayValues[(i + 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j + 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j + 2] == 255 || grayValues[(i + 2) * bmpData.Stride + j - 2] == 255 || grayValues[(i + 2) * bmpData.Stride + j - 1] == 255 || grayValues[(i + 2) * bmpData.Stride + j - 1] == 255 || grayValues[(i + 2) * bmpData.Stride + j] == 255 || grayValues[(i + 2) * bmpData.Stride + j + 1] == 255 || grayValues[(i + 2) * bmpData.Stride + j + 2] == 255) tempArray[i * bmpData.Stride + j] = 255; } } break; default: break; } grayValues = (byte[])tempArray.Clone(); Marshal.Copy(grayValues, 0, ptr, bytes); inputBitmap.UnlockBits(bmpData); return inputBitmap; } catch { return null; } } #endregion #region 图像开运算 /// <summary> /// 图像开运算 /// </summary> /// <param name="mode">结构元素 /// 0:3位水平方向的结构元素 /// 1:5位水平方向的结构元素 /// 2:3位垂直方向的结构元素 /// 3:5位垂直方向的结构元素 /// 4:3位十字状结构元素 /// 5:5位十字状结构元素 /// 6:3位方形结构元素 /// 7:5位方形结构元素 /// </param> /// <param name="inputBitmap">需要开运算的图像</param> /// <returns>开运算后的图像</returns> public static Bitmap ImageOpening(int mode, Bitmap inputBitmap) { try { //先以255作为阈值分割点 inputBitmap = ToGray(inputBitmap); inputBitmap = ToBinary(255, inputBitmap); Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); System.Drawing.Imaging.BitmapData bmpData = inputBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); IntPtr ptr = bmpData.Scan0; int bytes = bmpData.Stride * bmpData.Height; byte[] grayValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes); byte[] temp1Array = new byte[bytes]; byte[] tempArray = new byte[bytes]; for (int i = 0; i < bytes; i++) { tempArray[i] = temp1Array[i] = 0; } switch (mode) { case 0: //3位水平结构元素 //腐蚀运算 for (int i = 0; i < bmpData.Height; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 && grayValues[i * bmpData.Stride + j + 1] == 255 && grayValues[i * bmpData.Stride + j - 1] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 0; i < bmpData.Stride; i++) { for (int j = 1; j < bmpData.Stride - 1; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[i * bmpData.Stride + j + 1] == 255 || temp1Array[i * bmpData.Stride + j - 1] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 1: //5位水平结构元素 //腐蚀运算 for (int i = 0; i < bmpData.Height; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[i * bmpData.Stride + j] == 255 && grayValues[i * bmpData.Stride + j + 1] == 255 && grayValues[i * bmpData.Stride + j - 1] == 255 && grayValues[i * bmpData.Stride + j + 2] == 255 && grayValues[i * bmpData.Stride + j - 2] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 0; i < bmpData.Height; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[i * bmpData.Stride + j + 1] == 255 || temp1Array[i * bmpData.Stride + j - 1] == 255 || temp1Array[i * bmpData.Stride + j + 2] == 255 || temp1Array[i * bmpData.Stride + j - 2] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 2: //3位垂直结构元素 //腐蚀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 0; j < bmpData.Width; j++) { if (grayValues[i * bmpData.Stride + j] == 255 && grayValues[(i - 1) * bmpData.Stride + j] == 255 && grayValues[(i + 1) * bmpData.Stride + j] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 0; j < bmpData.Width; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[(i - 1) * bmpData.Stride + j] == 255 || temp1Array[(i + 1) * bmpData.Stride + j] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 3: //5位垂直结构元素 //腐蚀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 0; j < bmpData.Width; j++) { if (grayValues[i * bmpData.Stride + j] == 255 && grayValues[(i - 1) * bmpData.Stride + j] == 255 && grayValues[(i + 1) * bmpData.Stride + j] == 255 && grayValues[(i - 2) * bmpData.Stride + j] == 255 && grayValues[(i + 2) * bmpData.Stride + j] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 0; j < bmpData.Width; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[(i - 1) * bmpData.Stride + j] == 255 || temp1Array[(i + 1) * bmpData.Stride + j] == 255 || temp1Array[(i - 2) * bmpData.Stride + j] == 255 || temp1Array[(i + 2) * bmpData.Stride + j] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 4: //3位十字形结构元素 //腐蚀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 && grayValues[(i - 1) * bmpData.Stride + j] == 255 && grayValues[(i + 1) * bmpData.Stride + j] == 255 && grayValues[i * bmpData.Stride + j + 1] == 255 && grayValues[i * bmpData.Stride + j - 1] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[(i - 1) * bmpData.Stride + j] == 255 || temp1Array[(i + 1) * bmpData.Stride + j] == 255 || temp1Array[i * bmpData.Stride + j + 1] == 255 || temp1Array[i * bmpData.Stride + j - 1] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 5: //5位十字形结构元素 //腐蚀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[i * bmpData.Stride + j] == 255 && grayValues[(i - 1) * bmpData.Stride + j] == 255 && grayValues[(i + 1) * bmpData.Stride + j] == 255 && grayValues[(i - 2) * bmpData.Stride + j] == 255 && grayValues[(i + 2) * bmpData.Stride + j] == 255 && grayValues[i * bmpData.Stride + j + 1] == 255 && grayValues[i * bmpData.Stride + j - 1] == 255 && grayValues[i * bmpData.Stride + j + 2] == 255 && grayValues[i * bmpData.Stride + j - 2] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[(i - 1) * bmpData.Stride + j] == 255 || temp1Array[(i + 1) * bmpData.Stride + j] == 255 || temp1Array[(i - 2) * bmpData.Stride + j] == 255 || temp1Array[(i + 2) * bmpData.Stride + j] == 255 || temp1Array[i * bmpData.Stride + j + 1] == 255 || temp1Array[i * bmpData.Stride + j - 1] == 255 || temp1Array[i * bmpData.Stride + j + 2] == 255 || temp1Array[i * bmpData.Stride + j - 2] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 6: //3位方形结构元素 //腐蚀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 && grayValues[(i - 1) * bmpData.Stride + j] == 255 && grayValues[(i + 1) * bmpData.Stride + j] == 255 && grayValues[i * bmpData.Stride + j + 1] == 255 && grayValues[i * bmpData.Stride + j - 1] == 255 && grayValues[(i - 1) * bmpData.Stride + j - 1] == 255 && grayValues[(i + 1) * bmpData.Stride + j - 1] == 255 && grayValues[(i - 1) * bmpData.Stride + j + 1] == 255 && grayValues[(i + 1) * bmpData.Stride + j + 1] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[(i - 1) * bmpData.Stride + j] == 255 || temp1Array[(i + 1) * bmpData.Stride + j] == 255 || temp1Array[i * bmpData.Stride + j + 1] == 255 || temp1Array[i * bmpData.Stride + j - 1] == 255 || temp1Array[(i - 1) * bmpData.Stride + j - 1] == 255 || temp1Array[(i + 1) * bmpData.Stride + j - 1] == 255 || temp1Array[(i - 1) * bmpData.Stride + j + 1] == 255 || temp1Array[(i + 1) * bmpData.Stride + j + 1] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 7: //5位方形结构元素 //腐蚀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[(i - 2) * bmpData.Stride + j - 2] == 255 && grayValues[(i - 2) * bmpData.Stride + j - 1] == 255 && grayValues[(i - 2) * bmpData.Stride + j] == 255 && grayValues[(i - 2) * bmpData.Stride + j + 1] == 255 && grayValues[(i - 2) * bmpData.Stride + j + 2] == 255 && grayValues[(i - 1) * bmpData.Stride + j - 2] == 255 && grayValues[(i - 1) * bmpData.Stride + j - 1] == 255 && grayValues[(i - 1) * bmpData.Stride + j] == 255 && grayValues[(i - 1) * bmpData.Stride + j + 1] == 255 && grayValues[(i - 1) * bmpData.Stride + j + 2] == 255 && grayValues[i * bmpData.Stride + j - 2] == 255 && grayValues[i * bmpData.Stride + j - 1] == 255 && grayValues[i * bmpData.Stride + j] == 255 && grayValues[i * bmpData.Stride + j + 1] == 255 && grayValues[i * bmpData.Stride + j + 2] == 255 && grayValues[(i + 2) * bmpData.Stride + j - 2] == 255 && grayValues[(i + 2) * bmpData.Stride + j - 1] == 255 && grayValues[(i + 2) * bmpData.Stride + j] == 255 && grayValues[(i + 2) * bmpData.Stride + j + 1] == 255 && grayValues[(i + 2) * bmpData.Stride + j + 2] == 255 && grayValues[(i + 1) * bmpData.Stride + j - 2] == 255 && grayValues[(i + 1) * bmpData.Stride + j - 1] == 255 && grayValues[(i + 1) * bmpData.Stride + j] == 255 && grayValues[(i + 1) * bmpData.Stride + j + 1] == 255 && grayValues[(i + 1) * bmpData.Stride + j + 2] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //膨胀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (temp1Array[(i - 2) * bmpData.Stride + j - 2] == 255 || temp1Array[(i - 2) * bmpData.Stride + j - 1] == 255 || temp1Array[(i - 2) * bmpData.Stride + j] == 255 || temp1Array[(i - 2) * bmpData.Stride + j + 1] == 255 || temp1Array[(i - 2) * bmpData.Stride + j + 2] == 255 || temp1Array[(i - 1) * bmpData.Stride + j - 2] == 255 || temp1Array[(i - 1) * bmpData.Stride + j - 1] == 255 || temp1Array[(i - 1) * bmpData.Stride + j] == 255 || temp1Array[(i - 1) * bmpData.Stride + j + 1] == 255 || temp1Array[(i - 1) * bmpData.Stride + j + 2] == 255 || temp1Array[i * bmpData.Stride + j - 2] == 255 || temp1Array[i * bmpData.Stride + j - 1] == 255 || temp1Array[i * bmpData.Stride + j] == 255 || temp1Array[i * bmpData.Stride + j + 1] == 255 || temp1Array[i * bmpData.Stride + j + 2] == 255 || temp1Array[(i + 2) * bmpData.Stride + j - 2] == 255 || temp1Array[(i + 2) * bmpData.Stride + j - 1] == 255 || temp1Array[(i + 2) * bmpData.Stride + j] == 255 || temp1Array[(i + 2) * bmpData.Stride + j + 1] == 25 || temp1Array[(i + 2) * bmpData.Stride + j + 2] == 255 || temp1Array[(i + 1) * bmpData.Stride + j - 2] == 255 || temp1Array[(i + 1) * bmpData.Stride + j - 1] == 255 || temp1Array[(i + 1) * bmpData.Stride + j] == 255 || temp1Array[(i + 1) * bmpData.Stride + j + 1] == 255 || temp1Array[(i + 1) * bmpData.Stride + j + 2] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; default: break; } grayValues = (byte[])tempArray.Clone(); System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes); inputBitmap.UnlockBits(bmpData); return inputBitmap; } catch { return null; } } #endregion #region 图像闭运算 /// <summary> /// 图像闭运算 /// </summary> /// <param name="mode">结构元素 /// 0:3位水平方向的结构元素 /// 1:5位水平方向的结构元素 /// 2:3位垂直方向的结构元素 /// 3:5位垂直方向的结构元素 /// 4:3位十字状结构元素 /// 5:5位十字状结构元素 /// 6:3位方形结构元素 /// 7:5位方形结构元素 /// </param> /// <param name="inputBitmap">需要闭运算的图像</param> /// <returns>闭运算后的结果</returns> public static Bitmap ImageClosing(int mode, Bitmap inputBitmap) { try { //先以255作为阈值分割点 inputBitmap = ToGray(inputBitmap); inputBitmap = ToBinary(255, inputBitmap); Rectangle rect = new Rectangle(0, 0, inputBitmap.Width, inputBitmap.Height); System.Drawing.Imaging.BitmapData bmpData = inputBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); IntPtr ptr = bmpData.Scan0; int bytes = bmpData.Stride * bmpData.Height; byte[] grayValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes); byte[] temp1Array = new byte[bytes]; byte[] tempArray = new byte[bytes]; for (int i = 0; i < bytes; i++) { tempArray[i] = temp1Array[i] = 0; } switch (mode) { case 0: //3位水平结构元素 //膨胀运算 for (int i = 0; i < bmpData.Height; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 0; i < bmpData.Stride; i++) { for (int j = 1; j < bmpData.Stride - 1; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[i * bmpData.Stride + j + 1] == 255 && temp1Array[i * bmpData.Stride + j - 1] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 1: //5位水平结构元素 //膨胀运算 for (int i = 0; i < bmpData.Height; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[i * bmpData.Stride + j + 2] == 255 || grayValues[i * bmpData.Stride + j - 2] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 0; i < bmpData.Height; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[i * bmpData.Stride + j + 1] == 255 && temp1Array[i * bmpData.Stride + j - 1] == 255 && temp1Array[i * bmpData.Stride + j + 2] == 255 && temp1Array[i * bmpData.Stride + j - 2] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 2: //3位垂直结构元素 //膨胀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 0; j < bmpData.Width; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 0; j < bmpData.Width; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[(i - 1) * bmpData.Stride + j] == 255 && temp1Array[(i + 1) * bmpData.Stride + j] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 3: //5位垂直结构元素 //膨胀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 0; j < bmpData.Width; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[(i - 2) * bmpData.Stride + j] == 255 || grayValues[(i + 2) * bmpData.Stride + j] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 0; j < bmpData.Width; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[(i - 1) * bmpData.Stride + j] == 255 && temp1Array[(i + 1) * bmpData.Stride + j] == 255 && temp1Array[(i - 2) * bmpData.Stride + j] == 255 && temp1Array[(i + 2) * bmpData.Stride + j] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 4: //3位十字形结构元素 //膨胀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[(i - 1) * bmpData.Stride + j] == 255 && temp1Array[(i + 1) * bmpData.Stride + j] == 255 && temp1Array[i * bmpData.Stride + j + 1] == 255 && temp1Array[i * bmpData.Stride + j - 1] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 5: //5位十字形结构元素 //膨胀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[(i - 2) * bmpData.Stride + j] == 255 || grayValues[(i + 2) * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[i * bmpData.Stride + j + 2] == 255 || grayValues[i * bmpData.Stride + j - 2] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[(i - 1) * bmpData.Stride + j] == 255 && temp1Array[(i + 1) * bmpData.Stride + j] == 255 && temp1Array[(i - 2) * bmpData.Stride + j] == 255 && temp1Array[(i + 2) * bmpData.Stride + j] == 255 && temp1Array[i * bmpData.Stride + j + 1] == 255 && temp1Array[i * bmpData.Stride + j - 1] == 255 && temp1Array[i * bmpData.Stride + j + 2] == 255 && temp1Array[i * bmpData.Stride + j - 2] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 6: //3位方形结构元素 //膨胀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (grayValues[i * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[(i - 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i - 1) * bmpData.Stride + j + 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j + 1] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 1; i < bmpData.Height - 1; i++) { for (int j = 1; j < bmpData.Width - 1; j++) { if (temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[(i - 1) * bmpData.Stride + j] == 255 && temp1Array[(i + 1) * bmpData.Stride + j] == 255 && temp1Array[i * bmpData.Stride + j + 1] == 255 && temp1Array[i * bmpData.Stride + j - 1] == 255 && temp1Array[(i - 1) * bmpData.Stride + j - 1] == 255 && temp1Array[(i + 1) * bmpData.Stride + j - 1] == 255 && temp1Array[(i - 1) * bmpData.Stride + j + 1] == 255 && temp1Array[(i + 1) * bmpData.Stride + j + 1] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; case 7: //5位方形结构元素 //膨胀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (grayValues[(i - 2) * bmpData.Stride + j - 2] == 255 || grayValues[(i - 2) * bmpData.Stride + j - 1] == 255 || grayValues[(i - 2) * bmpData.Stride + j] == 255 || grayValues[(i - 2) * bmpData.Stride + j + 1] == 255 || grayValues[(i - 2) * bmpData.Stride + j + 2] == 255 || grayValues[(i - 1) * bmpData.Stride + j - 2] == 255 || grayValues[(i - 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i - 1) * bmpData.Stride + j] == 255 || grayValues[(i - 1) * bmpData.Stride + j + 1] == 255 || grayValues[(i - 1) * bmpData.Stride + j + 2] == 255 || grayValues[i * bmpData.Stride + j - 2] == 255 || grayValues[i * bmpData.Stride + j - 1] == 255 || grayValues[i * bmpData.Stride + j] == 255 || grayValues[i * bmpData.Stride + j + 1] == 255 || grayValues[i * bmpData.Stride + j + 2] == 255 || grayValues[(i + 2) * bmpData.Stride + j - 2] == 255 || grayValues[(i + 2) * bmpData.Stride + j - 1] == 255 || grayValues[(i + 2) * bmpData.Stride + j] == 255 || grayValues[(i + 2) * bmpData.Stride + j + 1] == 255 || grayValues[(i + 2) * bmpData.Stride + j + 2] == 255 || grayValues[(i + 1) * bmpData.Stride + j - 2] == 255 || grayValues[(i + 1) * bmpData.Stride + j - 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j] == 255 && grayValues[(i + 1) * bmpData.Stride + j + 1] == 255 || grayValues[(i + 1) * bmpData.Stride + j + 2] == 255) { temp1Array[i * bmpData.Stride + j] = 255; } } } //腐蚀运算 for (int i = 2; i < bmpData.Height - 2; i++) { for (int j = 2; j < bmpData.Width - 2; j++) { if (temp1Array[(i - 2) * bmpData.Stride + j - 2] == 255 && temp1Array[(i - 2) * bmpData.Stride + j - 1] == 255 && temp1Array[(i - 2) * bmpData.Stride + j] == 255 && temp1Array[(i - 2) * bmpData.Stride + j + 1] == 255 && temp1Array[(i - 2) * bmpData.Stride + j + 2] == 255 && temp1Array[(i - 1) * bmpData.Stride + j - 2] == 255 && temp1Array[(i - 1) * bmpData.Stride + j - 1] == 255 && temp1Array[(i - 1) * bmpData.Stride + j] == 255 && temp1Array[(i - 1) * bmpData.Stride + j + 1] == 255 && temp1Array[(i - 1) * bmpData.Stride + j + 2] == 255 && temp1Array[i * bmpData.Stride + j - 2] == 255 && temp1Array[i * bmpData.Stride + j - 1] == 255 && temp1Array[i * bmpData.Stride + j] == 255 && temp1Array[i * bmpData.Stride + j + 1] == 255 && temp1Array[i * bmpData.Stride + j + 2] == 255 && temp1Array[(i + 2) * bmpData.Stride + j - 2] == 255 && temp1Array[(i + 2) * bmpData.Stride + j - 1] == 255 && temp1Array[(i + 2) * bmpData.Stride + j] == 255 && temp1Array[(i + 2) * bmpData.Stride + j + 1] == 25 && temp1Array[(i + 2) * bmpData.Stride + j + 2] == 255 && temp1Array[(i + 1) * bmpData.Stride + j - 2] == 255 && temp1Array[(i + 1) * bmpData.Stride + j - 1] == 255 && temp1Array[(i + 1) * bmpData.Stride + j] == 255 && temp1Array[(i + 1) * bmpData.Stride + j + 1] == 255 && temp1Array[(i + 1) * bmpData.Stride + j + 2] == 255) { tempArray[i * bmpData.Stride + j] = 255; } } } break; default: break; } grayValues = (byte[])tempArray.Clone(); System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes); inputBitmap.UnlockBits(bmpData); return inputBitmap; } catch { return null; } } #endregion }