//方法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种方法就能提高效率了。
麻烦讲下原理。谢谢!

解决方案 »

  1.   


    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则快多了
      

  2.   

    请教isjoe:
    那BMP数据本身不就是在内存中了吗?
    另外像GetPixel与SetPixel取得设置像素需要转换的(不知主要是哪方面的转换)?
    谢谢!
      

  3.   

    下面是。Net类库的反编译代码,你看看下面的效率和直接修改RGB能一样吗?
    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);
        }
    }