C# BitmapData使用说明
C#专门为图像处理提供了BitmapData,这个是真正的对位图的处理,首先将位图锁定到内存中,然后对位图的每一个像素进行处理。最后再释放位图锁。
方法一1:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace WinFormDemo0102
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 根据图片路径, 返回一张灰度图
/// </summary>
/// <param name="strPicPath">图片路径</param>
/// <returns>灰度图对象</returns>
public Image GetGrayPicture(string strPicPath)
{
/*
* Stride: 图像扫描宽度
* 图像在内存里是按行存储的。扫描行宽度就是存储一行像素,用了多少字节的内存。
* 比如一个101×200大小的图像,每个像素是32位的(也就是每个像素4个字节),那么实际每行占用的内存大小是101*4=404个字节。
* 然后一般的图像库都会有个内存对齐。假设按照8字节对齐,那么404按照8字节对齐的话,实际是408个字节。这408个字节就是扫描行宽度。
*
* Interop: 托管/非托管代码之间的互操作
*
* Marshal: 类提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,
* 此外还提供了在与非托管代码交互时使用的其他杂项方法
*/
// Create a new bitmap.
Bitmap bitmap = new Bitmap(strPicPath);
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
// Lock the bitmap's bits.
//转成24rgb颜色 24色就是由r g b, 三个颜色, 每个颜色各用一字节(8位)表示亮度
// BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int iStride = bmpData.Stride; //图片一行象素所占用的字节
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int iBytes = iStride * bitmap.Height;
byte[] rgbValues = new byte[iBytes];
// Copy RGB values into the Array
Marshal.Copy(ptr, rgbValues, 0, iBytes);
for (int y = 0; y < bmpData.Height; ++y)
{
for (int x = 0; x < bmpData.Width; ++x)
{
//图像(x, y)坐标坐标中第1个像素中rgbValues中的索引位置(这儿x,y是从0开始记的)
//rgbValues每行是扫描宽个字节, 不是bitmap.Width * 3
int iThird = iStride * y + 3 * x;
byte avg = (byte)((rgbValues[iThird] * 0.299 + rgbValues[iThird + 1] * 0.587 + rgbValues[iThird + 2] * 0.114));//转化成灰度
rgbValues[iThird] = avg;
rgbValues[iThird + 1] = avg;
rgbValues[iThird + 2] = avg;
}
}
// copy到原来图像中
Marshal.Copy(rgbValues, 0, ptr, rgbValues.Length);
// Unlock the bits.
bitmap.UnlockBits(bmpData);
return bitmap;
}
private void button1_Click(object sender, EventArgs e)
{
Image2gray1();
}
private void Image2gray1()
{
Image image = GetGrayPicture(AppDomain.CurrentDomain.BaseDirectory + "qq1.jpg");
image.Save(AppDomain.CurrentDomain.BaseDirectory + "11.png");
// Console.WriteLine(image.ToString());
System.Diagnostics.Process.Start(AppDomain.CurrentDomain.BaseDirectory + "11.png");
// MessageBox.Show("finised!!");
}
}
}
方法二:(官方推荐)
msdn关于BitmapData原文解释地址:
http://msdn.microsoft.com/zh-cn/library/5ey6h79d(v=vs.110).aspx
以下是msdn原文给出的例子
private void LockUnlockBitsExample(PaintEventArgs e)
{
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
方法三:
自己理解的
/// 获取灰度值返回byte[]
/// <summary>
/// 获取灰度值返回byte[]
/// </summary>
/// <param name="srcBmp">源图像</param>
/// <param name="rect">要锁定的图像区域</param>
/// <returns>返回byte[]</returns>
public static byte[] GetGrayArray(Bitmap srcBmp, Rectangle rect)
{
//将Bitmap锁定到系统内存中
//rect是指源图像中需要锁定那一块矩形区域进行处理
//ImageLockMode.ReadWrite是指对图像出操作的权限,枚举有只读,只写,用户输入缓冲区,还是读写
//PixelFormat.Format24bppRgb
//参数确定了该图像信息时rgb存储还是Argb存储,如果是Format24ppRgb则处理的图像像素就是BGR方式存储,我们这里没有特别指出,均是Format24bppRgb方式存储处理
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
IntPtr srcPtr = srcBmpData.Scan0;
//将Bitmap对象的信息存放到byte数组中
//假设本图像的宽度和高度为5*3
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 //这里存储为一维数组,所以是一行,
宽度为5,高度为3,则像素总数为15,这里要清楚,每一个像素是rgb三个值,故而,一维数组中
存储为
1 2 3 4 5 6 7 8 9 10 11 12 13 14
15...44
这里分行是为了便于理解,一维数组存储类似是一行,但是我们的图像的宽度是5也就是我们图像的第一行是0123...到14,下一行是15...29,下一行是30...44,高度为3,所以就是3行
345 678 91011 121314
bgr bgr bgr bgr bgr
181920 ...
bgr bgr ...
这样存储
*/
//所以才有了这里的*3就是指一个像素是三个分量值
int scanWidth = rect.Width * 3;
//而每行的实际的字节数将变成大于等于它的那个离它最近的4的整倍数,此时的实际字节数就是Stride,如果上面的第三个参数Format24ppRgb如果设置为Format32ppRgb,这个时候存储的时候就是4位存储一个像素,如果是Format32bppArgb同样也是4为存储一个像素,这4位除了bgr三个分量之外还有透明度A的值
//至于为什么是24,32,这是因为计算机存储数据为8bit存储1个字节,
//bgr3个就是3*8=24,4个就是4*8=32了,为什么是16,8位等,索引图等原理是一样的
//int srcStride = srcBmpData.Stride;
int src_bytes = scanWidth * rect.Height; //这里就是计算出了需要存储的像素所占用的空间大小
byte[] srcValues = new byte[src_bytes]; //定义源图像的元信息
byte[] grayValues = new byte[rect.Width * rect.Height]; //定义转化为灰度后需要存储的数组
//复制GRB信息到byte数组,将从srcPtr开始的第一个扫描行开始扫描信息,然后读取到srcValues数组中
Marshal.Copy(srcPtr, srcValues, 0, src_bytes);
//解锁位图
srcBmp.UnlockBits(srcBmpData); //读取完元信息,这里就不用了,一定要记得解锁,否则会报错
//下面就是你想怎么处理都成了,,灰度化,转换空间模式,除噪声,腐蚀,膨胀,反色,二值化等等均可
//灰度化处理
int m = 0, j = 0;
int k = 0;
byte gray;
//根据重要性及其它指标,将三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感最高,对蓝色敏感最低,因此,按下式对RGB三分量进行加权平均能得到较合理的灰度图像
//根据Y = 0.299*R + 0.587*G + 0.114*B //加权平均法
for (int i = 0; i < rect.Height; i++)
{
for (j = 0; j < rect.Width; j++)
{
//注意位图结构中RGB按BGR的顺序存储
k = 3 * j;
gray = (byte)(srcValues[i * scanWidth + k + 2] * 0.299
+ srcValues[i * scanWidth + k + 1] * 0.587
+ srcValues[i * scanWidth + k + 0] * 0.114);
grayValues[m] = gray; //将灰度值存到double的数组中
m++;
}
}
return grayValues;
}
//接下来就很简单了,下面在给出获得到灰度值存储为2位数组的方法,按照习惯二维处理起来比较好理解
/// 获取灰度值存到二维double数组中,这个是将rgb转化为灰度值
/// <summary>
/// 获取灰度值存到二维double数组中,这个是将rgb转化为灰度值
/// </summary>
/// <param name="srcBmp"></param>
/// <returns>2Dimension</returns>
public static byte[,] GetGrayArray2D(Bitmap srcBmp,Rectangle rect)
{
int width = rect.Width;
int height = rect.Height;
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr srcPtr = srcBmpData.Scan0;
int scanWidth = width * 3;
int src_bytes = scanWidth * height;
//int srcStride = srcBmpData.Stride;
byte[] srcRGBValues = new byte[src_bytes];
byte[,] grayValues = new byte[height, width];
//RGB[] rgb = new RGB[srcBmp.Width * rows];
//复制GRB信息到byte数组
Marshal.Copy(srcPtr, srcRGBValues, 0, src_bytes);
//解锁位图
srcBmp.UnlockBits(srcBmpData);
//灰度化处理
int m = 0, i = 0, j = 0; //m表示行,j表示列
int k = 0;
byte gray;
for (i = 0; i < height; i++) //只获取图片的rows行像素值
{
for (j = 0; j < width; j++)
{
//只处理每行中图像像素数据,舍弃未用空间
//注意位图结构中RGB按BGR的顺序存储
k = 3 * j;
gray = (byte)(srcRGBValues[i * scanWidth + k + 2] * 0.299
+ srcRGBValues[i * scanWidth + k + 1] * 0.587
+ srcRGBValues[i * scanWidth + k + 0] * 0.114);
grayValues[m, j] = gray; //将灰度值存到double的数组中
}
m++;
}
return grayValues;
}
//此方法是直接得到灰度图
/// 获取灰度图像,将制定图片转化为灰度图
/// <summary>
/// 获取灰度图像,将制定图片转化为灰度图
/// </summary>
/// <param name="srcBmp"></param>
/// <returns></returns>
public static Bitmap GetGrayImage(Bitmap srcBmp)
{
Rectangle rect = new Rectangle(0, 0, srcBmp.Width, srcBmp.Height);
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr srcPtr = srcBmpData.Scan0;
int scanWidth = srcBmpData.Width * 3;
int src_bytes = scanWidth * srcBmp.Height;
byte[] srcRGBValues = new byte[src_bytes];
Marshal.Copy(srcPtr, srcRGBValues, 0, src_bytes);
//灰度化处理
int k = 0;
for (int i = 0; i < srcBmp.Height; i++)
{
for (int j = 0; j < srcBmp.Width; j++)
{
k = j * 3;
//0.299*R + 0.587*G + 0.144*B = 亮度或灰度
//只处理每行中图像像素数据,舍弃未用空间
//注意位图结构中RGB按BGR的顺序存储
byte intensity = (byte)(srcRGBValues[i * scanWidth + k + 2] * 0.299
+ srcRGBValues[i * scanWidth + k + 1] * 0.587
+ srcRGBValues[i * scanWidth + k + 0] * 0.114);
srcRGBValues[i * scanWidth + k + 0] = intensity;
srcRGBValues[i * scanWidth + k + 1] = intensity;
srcRGBValues[i * scanWidth + k + 2] = intensity;
}
}
Marshal.Copy(srcRGBValues, 0, srcPtr, src_bytes);
//解锁位图
srcBmp.UnlockBits(srcBmpData);
return srcBmp;
}
[1] https://www.cnblogs.com/barrysgy/archive/2011/11/29/2267936.html
[2]https://www.cnblogs.com/ching2009/p/4235766.html