//方法1
public static Bitmap RGB2Gray(Bitmap srcBitmap) { Color srcColor; int wide = srcBitmap.Width; int height = srcBitmap.Height; for (int y = 0; y < height; y++) for (int x = 0; x < wide; x++) { //获取像素的RGB颜色值 srcColor = srcBitmap.GetPixel(x, y); byte temp = (byte)(srcColor.R * .299 + srcColor.G * .587 + srcColor.B * .114); //设置像素的RGB颜色值 srcBitmap.SetPixel(x, y, Color.FromArgb(temp, temp, temp)); } return srcBitmap ; }//方法2
public static Bitmap RGB2Gray(Bitmap srcBitmap) { int wide = srcBitmap.Width; int height = srcBitmap.Height; Rectangle rect = new Rectangle(0, 0, wide, height); // 将Bitmap锁定到系统内存中, 获得BitmapData BitmapData srcBmData = srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //创建Bitmap Bitmap dstBitmap = CreateGrayscaleImage(wide, height);//这个函数在后面有定义 BitmapData dstBmData = dstBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); // 位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行 System.IntPtr srcPtr = srcBmData.Scan0; System.IntPtr dstPtr = dstBmData.Scan0; // 将Bitmap对象的信息存放到byte数组中 int src_bytes = srcBmData.Stride * height; byte[] srcValues = new byte[src_bytes]; int dst_bytes = dstBmData.Stride * height; byte[] dstValues = new byte[dst_bytes]; //复制GRB信息到byte数组 System.Runtime.InteropServices.Marshal.Copy(srcPtr, srcValues, 0, src_bytes); System.Runtime.InteropServices.Marshal.Copy(dstPtr, dstValues, 0, dst_bytes); // 根据Y=0.299*R+0.114*G+0.587B,Y为亮度 for (int i = 0; i < height; i++) for (int j = 0; j < wide; j++) { //只处理每行中图像像素数据,舍弃未用空间 //注意位图结构中RGB按BGR的顺序存储 int k = 3 * j; byte temp = (byte)(srcValues[i * srcBmData.Stride + k + 2] * .299 + srcValues[i * srcBmData.Stride + k + 1] * .587 + srcValues[i * srcBmData.Stride + k] * .114); dstValues[i * dstBmData.Stride + j] = temp; } //将更改过的byte[]拷贝到原位图 System.Runtime.InteropServices.Marshal.Copy(dstValues, 0, dstPtr, dst_bytes);
// 解锁位图 srcBitmap.UnlockBits(srcBmData); dstBitmap.UnlockBits(dstBmData); return dstBitmap; }
关于两个Bitamp的操作(例如两图像对比,网上很多是这两种方法,当然还有其他的)
网上说,第1种是逐象素操作,而第2种是内存操作,但我对其中的原理不太明白。
第1种的GetPixel与SetPixel不也是针对内存的操作吗?(Bitamp对象不也保存在内存中?)。
我对图像方面的了解的不是很多,但看了这2种方法,猜测图像似乎与二维数组的结构差不多。
GetPixel(x,y)或SetPixel(x,y) 似乎就是 数组名[x,y]吧,也就是直接访问内存呀。
那为何第2种方法就能提高效率了。
麻烦讲下原理。谢谢!
public static Bitmap RGB2Gray(Bitmap srcBitmap) { Color srcColor; int wide = srcBitmap.Width; int height = srcBitmap.Height; for (int y = 0; y < height; y++) for (int x = 0; x < wide; x++) { //获取像素的RGB颜色值 srcColor = srcBitmap.GetPixel(x, y); byte temp = (byte)(srcColor.R * .299 + srcColor.G * .587 + srcColor.B * .114); //设置像素的RGB颜色值 srcBitmap.SetPixel(x, y, Color.FromArgb(temp, temp, temp)); } return srcBitmap ; }//方法2
public static Bitmap RGB2Gray(Bitmap srcBitmap) { int wide = srcBitmap.Width; int height = srcBitmap.Height; Rectangle rect = new Rectangle(0, 0, wide, height); // 将Bitmap锁定到系统内存中, 获得BitmapData BitmapData srcBmData = srcBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //创建Bitmap Bitmap dstBitmap = CreateGrayscaleImage(wide, height);//这个函数在后面有定义 BitmapData dstBmData = dstBitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); // 位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行 System.IntPtr srcPtr = srcBmData.Scan0; System.IntPtr dstPtr = dstBmData.Scan0; // 将Bitmap对象的信息存放到byte数组中 int src_bytes = srcBmData.Stride * height; byte[] srcValues = new byte[src_bytes]; int dst_bytes = dstBmData.Stride * height; byte[] dstValues = new byte[dst_bytes]; //复制GRB信息到byte数组 System.Runtime.InteropServices.Marshal.Copy(srcPtr, srcValues, 0, src_bytes); System.Runtime.InteropServices.Marshal.Copy(dstPtr, dstValues, 0, dst_bytes); // 根据Y=0.299*R+0.114*G+0.587B,Y为亮度 for (int i = 0; i < height; i++) for (int j = 0; j < wide; j++) { //只处理每行中图像像素数据,舍弃未用空间 //注意位图结构中RGB按BGR的顺序存储 int k = 3 * j; byte temp = (byte)(srcValues[i * srcBmData.Stride + k + 2] * .299 + srcValues[i * srcBmData.Stride + k + 1] * .587 + srcValues[i * srcBmData.Stride + k] * .114); dstValues[i * dstBmData.Stride + j] = temp; } //将更改过的byte[]拷贝到原位图 System.Runtime.InteropServices.Marshal.Copy(dstValues, 0, dstPtr, dst_bytes);
// 解锁位图 srcBitmap.UnlockBits(srcBmData); dstBitmap.UnlockBits(dstBmData); return dstBitmap; }
关于两个Bitamp的操作(例如两图像对比,网上很多是这两种方法,当然还有其他的)
网上说,第1种是逐象素操作,而第2种是内存操作,但我对其中的原理不太明白。
第1种的GetPixel与SetPixel不也是针对内存的操作吗?(Bitamp对象不也保存在内存中?)。
我对图像方面的了解的不是很多,但看了这2种方法,猜测图像似乎与二维数组的结构差不多。
GetPixel(x,y)或SetPixel(x,y) 似乎就是 数组名[x,y]吧,也就是直接访问内存呀。
那为何第2种方法就能提高效率了。
麻烦讲下原理。谢谢!
private void button4_Click(object sender, EventArgs e)
{
Bitmap pic1 = new Bitmap(pictureBox2.Image);
int w, h;
w = pic1.Width;
h = pic1.Height;
Bitmap pic2 = new Bitmap(w, h, PixelFormat.Format32bppArgb);
BitmapData obmp = pic1.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData nbmp = pic2.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* op = (byte*)obmp.Scan0.ToPointer();
byte* np = (byte*)nbmp.Scan0.ToPointer();
int i, j, r, g, b, cc; int i, j, r, g, b, cc;
for (j = 0; j < h; j++)
{
for (i = 0; i < w; i++)
{
r = op[0];
g = op[1];
b = op[2];
cc = (r + g + b) / 3;
np[0] = (byte)cc;
np[1] = (byte)cc;
np[2] = (byte)cc;
op += 3;
np += 3;
}
op += obmp.Stride - w * 3;
np += nbmp.Stride - w * 3;
}
}
pic1.UnlockBits(obmp);
pic2.UnlockBits(nbmp);
pictureBox2.Image=pic2;
}看上面这段用指针的。像GetPixel与SetPixel取得设置像素需要转换的。
而将BMP数据读到内存,直接处理每一个像素中RGB则快多了
那BMP数据本身不就是在内存中了吗?
另外像GetPixel与SetPixel取得设置像素需要转换的(不知主要是哪方面的转换)?
谢谢!
public Color GetPixel(int x, int y)
{
int argb = 0;
if ((x < 0) || (x >= base.Width))
{
throw new ArgumentOutOfRangeException("x", SR.GetString("ValidRangeX"));
}
if ((y < 0) || (y >= base.Height))
{
throw new ArgumentOutOfRangeException("y", SR.GetString("ValidRangeY"));
}
int status = SafeNativeMethods.Gdip.GdipBitmapGetPixel(new HandleRef(this, base.nativeImage), x, y, out argb);
if (status != 0)
{
throw SafeNativeMethods.Gdip.StatusException(status);
}
return Color.FromArgb(argb);
}
public void SetPixel(int x, int y, Color color)
{
if ((base.PixelFormat & PixelFormat.Indexed) != PixelFormat.Undefined)
{
throw new InvalidOperationException(SR.GetString("GdiplusCannotSetPixelFromIndexedPixelFormat"));
}
if ((x < 0) || (x >= base.Width))
{
throw new ArgumentOutOfRangeException("x", SR.GetString("ValidRangeX"));
}
if ((y < 0) || (y >= base.Height))
{
throw new ArgumentOutOfRangeException("y", SR.GetString("ValidRangeY"));
}
int status = SafeNativeMethods.Gdip.GdipBitmapSetPixel(new HandleRef(this, base.nativeImage), x, y, color.ToArgb());
if (status != 0)
{
throw SafeNativeMethods.Gdip.StatusException(status);
}
}