本篇内容介绍了“C#的Bitmap图像处理方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义的图像的对象.该类的主要方法和属性如下:
1. GetPixel方法和SetPixel方法:获取和设置一个图像的指定像素的颜色.
2. PixelFormat属性:返回图像的像素格式.
3. Palette属性:获取和设置图像所使用的颜色调色板.
4. Height Width属性:返回图像的高度和宽度.
5. LockBits方法和UnlockBits方法:分别锁定和解锁系统内存中的位图像素.在基于像素点的图像处理方法中使用LockBits和UnlockBits是一个很好的方式,这两种方法可以使我们指定像素的范围来控制位图的任意一部分,从而消除了通过循环对位图的像素逐个进行处理,每调用LockBits之后都应该调用一次UnlockBits.
BitmapData对象指定了位图的属性
1. Height属性:被锁定位图的高度.
2. Width属性:被锁定位图的宽度.
3. PixelFormat属性:数据的实际像素格式.
4. Scan0属性:被锁定数组的首字节地址,如果整个图像被锁定,则是图像的第一个字节地址.
5. Stride属性:步幅,也称为扫描宽度.
这里要重点说说Stride属性,这个和Width有什么区别呢,可以这么说,如果你的图片大小也就是图片字节是4的整数倍,那么Stride与Width是相等的,否则Stride就是大于Width的最小4的整数倍。在处理过程中,Stride肯定是4的整数倍,这里是个坑啊。。。
例1:有一个一维像素点阵数组,里面放的是每个像素点的灰度值,知道宽和高,要转换成bitmap
/// <summary> /// 像素点阵转换为bitmap /// </summary> /// <param name="rawValues">byte[]数组</param> /// <param name="width">图片的宽度</param> /// <param name="height">图片的高度</param> /// <returns>bitmap图片</returns> public static Bitmap ToGrayBitmap(byte[] rawValues, int width, int height) { Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); 获取图像参数 //bmpData.Stride = width; int stride = bmpData.Stride; // 扫描线的宽度 int offset = stride - width; // 显示宽度与扫描线宽度的间隙 IntPtr iptr = bmpData.Scan0; // 获取bmpData的内存起始位置 int scanBytes = stride * height;// 用stride宽度,表示这是内存区域的大小 下面把原始的显示大小字节数组转换为内存中实际存放的字节数组 int posScan = 0, posReal = 0;// 分别设置两个位置指针,指向源数组和目标数组 byte[] pixelValues = new byte[scanBytes]; //为目标数组分配内存 for (int x = 0; x < height; x++) { 下面的循环节是模拟行扫描 for (int y = 0; y < width; y++) { pixelValues[posScan++] = rawValues[posReal++]; } posScan += offset; //行扫描结束,要将目标位置指针移过那段“间隙” } 用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中 System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, iptr, scanBytes); bmp.UnlockBits(bmpData); // 解锁内存区域 下面的代码是为了修改生成位图的索引表,从伪彩修改为灰度 ColorPalette tempPalette; using (Bitmap tempBmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed)) { tempPalette = tempBmp.Palette; } for (int i = 0; i < 256; i++) { tempPalette.Entries[i] = System.Drawing.Color.FromArgb(i, i, i); } bmp.Palette = tempPalette; 算法到此结束,返回结果 return bmp; }
至于24位位图数据其实就是 一个像素点有rgb三个值而已,道理一样。
例2::根据图片得到他的灰度数组
//8位位图得到除去文件头信息的一位灰度数组 BitmapData bmpData = map.LockBits(new System.Drawing.Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); 获取图像参数 int stride = bmpData.Stride; // 扫描线的宽度 int offset = stride - map.Width; // 显示宽度与扫描线宽度的间隙 IntPtr iptr = bmpData.Scan0; // 获取bmpData的内存起始位置 int scanBytes = stride * map.Height;// 用stride宽度,表示这是内存区域的大小 下面把原始的显示大小字节数组转换为内存中实际存放的字节数组 mapdata = new byte[scanBytes]; //为目标数组分配内存 System.Runtime.InteropServices.Marshal.Copy(iptr, mapdata, 0, scanBytes); //copy内存中数据到数组中
这里对与bitmapdata的操作方式是ReadOnly
下面的三个例子分别基于像素(GetPixel和SetPixel)、基于内存、基于指针这三种方法增强图片对比度。均测试通过
运行时间:
1)基于像素:400-600ms
2)基于内存:17-18ms
3)基于指针:20-23ms
利用LUT,应该可以进一步减少运行时间
// 第一种方法:像素提取法。速度慢 public Bitmap MethodBaseOnPixel(Bitmap bitmap,int degree) { Color curColor; int grayR, grayG, grayB; double Deg = (100.0 + degree) / 100.0; for (int i = 0; i < bitmap.Width; i++) { for (int j = 0; j < bitmap.Height; j++) { curColor = bitmap.GetPixel(i, j); grayR =Convert.ToInt32((((curColor.R / 255.0 - 0.5) * Deg + 0.5)) * 255); grayG = Convert.ToInt32((((curColor.G / 255.0 - 0.5) * Deg + 0.5)) * 255); grayB = Convert.ToInt32((((curColor.B / 255.0 - 0.5) * Deg + 0.5)) * 255); if (grayR < 0) grayR = 0; else if (grayR > 255) grayR = 255; if (grayB < 0) grayB = 0; else if (grayB > 255) grayB = 255; if (grayG < 0) grayG = 0; else if (grayG > 255) grayG = 255; bitmap.SetPixel(i, j, Color.FromArgb(grayR, grayG, grayB)); } } return bitmap; }
// 第二种方法:基于内存 public unsafe Bitmap MethodBaseOnMemory(Bitmap bitmap, int degree) { if (bitmap == null) { return null; } double Deg = (100.0 + degree) / 100.0; int width = bitmap.Width; int height = bitmap.Height; int length = height * 3 * width; byte[] RGB = new byte[length]; BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); System.IntPtr Scan0 = data.Scan0; System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length); double gray = 0; for (int i = 0; i < RGB.Length; i += 3) { for (int j = 0; j < 3; j++) { gray = (((RGB[i + j] / 255.0 -0.5) * Deg+0.5)) * 255.0; if (gray > 255) gray = 255; if (gray < 0) gray = 0; RGB[i + j] = (byte) gray; } } System.Runtime.InteropServices.Marshal.Copy(RGB, 0, Scan0, length);// 此处Copy是之前Copy的逆操作 bitmap.UnlockBits(data); return bitmap; } }
//第三种方法:基于指针 public unsafe Bitmap MethodBaseOnPtr(Bitmap b, int degree) { if (b == null) { return null; } try { double num = 0.0; double num2 = (100.0 + degree) / 100.0; num2 *= num2; int width = b.Width; int height = b.Height; BitmapData bitmapdata = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte* numPtr = (byte*)bitmapdata.Scan0; int offset = bitmapdata.Stride - (width * 3); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { for (int k = 0; k < 3; k++) { num = ((((((double)numPtr[k]) / 255.0) - 0.5) * num2) + 0.5) * 255.0; if (num < 0.0) { num = 0.0; } if (num > 255.0) { num = 255.0; } numPtr[k] = (byte)num; } numPtr += 3; } numPtr += offset; } b.UnlockBits(bitmapdata); return b; } catch { return b; } }
“C#的Bitmap图像处理方法有哪些”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。