看了网上的方法,如何将24位真彩图转化为8位灰度图像,核心问题就是重置颜色灰阶,但是用下面两种方法保存的图片为嘛会出现不一样?急求助 private void convert_Click(object sender, EventArgs e)
        {
            if (curBitmap != null)
            {
                //新的我要的8位图像容器
                Bitmap bit = new Bitmap(curBitmap.Width, curBitmap.Height, PixelFormat.Format8bppIndexed);
                //可读写的方式锁定全部原图像
                BitmapData data = curBitmap.LockBits(new Rectangle(0, 0, curBitmap.Width, curBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                //得到首地址
                IntPtr ptr1 = data.Scan0;
                //可读写的方式锁定全部容器
                BitmapData data2 = bit.LockBits(new Rectangle(0, 0, bit.Width, bit.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
                //得到首地址
                IntPtr ptr2 = data2.Scan0;
                //计算24位图像的字节数
                int bytes = curBitmap.Width * curBitmap.Height * 3;
                //定义位图数组
                byte[] grayValues = new byte[bytes];
                //复制被锁定的图像到该数组
                System.Runtime.InteropServices.Marshal.Copy(ptr1, grayValues, 0, bytes);
                //同上
                int byte8 = curBitmap.Width * curBitmap.Height;
                byte[] gray8Values = new byte[byte8];
                System.Runtime.InteropServices.Marshal.Copy(ptr2, gray8Values, 0, byte8);                for (int i = 0, n = 0; i < bytes; i += 3, n++)
                {
                    //灰度变换
                    double colorTemp = grayValues[i + 2] * 0.299 + grayValues[i + 1] * 0.589 + grayValues[i] * 0.114;
                   
                    //将灰度值赋值给容器    这是新图8位
                    gray8Values[n] = (byte)colorTemp;
                }
                //送回数组
                System.Runtime.InteropServices.Marshal.Copy(gray8Values, 0, ptr2, byte8);
                System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr1, bytes);
                //解锁
                curBitmap.UnlockBits(data);
                bit.UnlockBits(data2);
                //调色板
                ColorPalette palette = bit.Palette;
                //调色板结构体数组  填充  
                for (int i = 0; i != palette.Entries.Length; i++)
                {
                    palette.Entries[i] = Color.FromArgb(i, i, i);
                }
                bit.Palette = palette;                Bitmap b = bit.Clone(new Rectangle(0, 0, bit.Width, bit.Height), PixelFormat.Format8bppIndexed);
保存方法一、
                b.Save(@"F:\C#\1.bmp", ImageFormat.Bmp);
               Image image = (Bitmap)System.Drawing.Image.FromHbitmap(b.GetHbitmap());
                pictureBox2.Image = image;
                b.Dispose();
                bit.Dispose();
               
               
                Invalidate();            }
        }
保存方法二、
 private void save_Click(object sender, EventArgs e)
        {
            //如果没有创建图像,则退出
            if (pictureBox2.Image == null)
            {
                return;
            }
            //调用SaveFileDialog
            SaveFileDialog saveDlg = new SaveFileDialog();
            //设置对话框标题
            saveDlg.Title = "保存为";
            //读写已存在文件时提示用户
            saveDlg.OverwritePrompt = true;
            //为图像选择一个筛选器
            saveDlg.Filter = "BMP文件(*.bmp)|*.bmp|" + "Gif文件(*.gif)|*.gif|" +
                "JPEG文件(*.jpg)|*.jpg|" + "PNG文件(*.png)|*.png";
            //启用“帮助”按钮
            saveDlg.ShowHelp = true;
            //如果选择了格式,则保存图像
            if (saveDlg.ShowDialog() == DialogResult.OK)
            {
                //获取用户选择的文件名
                string fileName = saveDlg.FileName;
                //保存文件
                Bitmap a = new Bitmap(pictureBox2.Image);
                a.Save(fileName,ImageFormat.Bmp);
               
                      
            }
        }

解决方案 »

  1.   

    猜到你的问题了,你保存方法一把保存的图像是8位的,而方法二保存的是32位的,这是由于Image image = (Bitmap)System.Drawing.Image.FromHbitmap(b.GetHbitmap())这句代码造成的。FromHbitmap函数有两个构造函数,其中另外一个需要传入调色板,而你的 Format8bppIndexed显然是具有调色板的图像,你没有给他传入这个参数,他则认为你创建的是一个32位图像。你从网络上找的这段 代码太垃圾了,并且还有不少错误。给你一段代码参考,速度和可读性都比你的强。  private void GetGrayModeImage(Bitmap SrcImg,ref  Bitmap DestImg)
            {
                int X,Y;
                int SrcStride,DestStride,Width,Height;
                byte * SrcData,DestData;
                
                BitmapData SrcBmpData = SrcImg.LockBits(new Rectangle(0, 0, SrcImg.Width, SrcImg.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                DestImg = new Bitmap(SrcImg.Width, SrcImg.Height, PixelFormat.Format8bppIndexed);
                ColorPalette Pal = DestImg.Palette;
                for (Y=0;Y<Pal.Entries.Length;Y++)             // 设置灰度图像的调色板
                    Pal.Entries[Y]=Color.FromArgb(255,Y,Y,Y);
                DestImg.Palette = Pal;
                /*  LockBits 在第一个参数和图像一样大,以及读取格式和原始一样的情况
                 * 下,调用函数的时间为0,且每次调用后BitmapData的Scan0都相同,而在
                 * 其他的大部分情况下同样参数调用该函数返回的Scan0都不同,这就说明在
                 * 在程序内部,GDI+为在创建图像时还是分配了和对应位图一样大小内存空间,
                 * 这样我们就可以再加载时调用一次该函数,并记住Scan0的值,然后直接用
                 * 指针操作这一片区域,就相当于操作了图像。而不用每次都LOCK和UNLOCK了
                 * 从这个层次上说,该函数和GetDibits类似。
                */            BitmapData DestBmpData = DestImg.LockBits(new Rectangle(0, 0, DestImg.Width, DestImg.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
                Width =SrcBmpData.Width ;Height =SrcBmpData.Height ;
                SrcStride =SrcBmpData.Stride;DestStride =DestBmpData.Stride ;           //  这个值并不一定就等于width*height*色深/8
                for (Y=0;Y<Height;Y++)
                {
                    SrcData = (byte*)SrcBmpData.Scan0 + Y * SrcStride;                  // 必须在某个地方开启unsafe功能,其实C#中的unsafe很safe,搞的好吓人。
                    DestData= (byte *) DestBmpData.Scan0 + Y*DestStride;
                    for (X=0;X<Width;X++)
                    {
                        * DestData =(byte)((*SrcData*299 + *(SrcData+1)*587+*(SrcData+2)*114)/1000);        //优化算法是通过移位实现的。
                        SrcData+=3;
                        DestData++;
                    }
                }
                SrcImg.UnlockBits(SrcBmpData);
                DestImg.UnlockBits(DestBmpData);
            }调用: private void button1_Click(object sender, EventArgs e)
            {
                Bitmap DestImg =null;
                GetGrayModeImage ((Bitmap)pictureBox1.Image, ref DestImg);
                pictureBox2.Image = DestImg;
                pictureBox2.Refresh();
            }
      

  2.   

    这个也太简单了吧,看这个代码,在写个一般的保存函数bitmap.save("1.bmp")就行了:
    http://dongtingyueh.blog.163.com/blog/static/461945320114865323592/
      

  3.   

    大哥,你说的对,但是我照你说的那段代码最后出来的还是32位的,我就保存了一下pictureBox2中的图片有问题吗?  private void save_Click(object sender, EventArgs e)
            {
                //如果没有创建图像,则退出
                if (pictureBox2.Image == null)
                {
                    return;
                }
                //调用SaveFileDialog
                SaveFileDialog saveDlg = new SaveFileDialog();
                //设置对话框标题
                saveDlg.Title = "保存为";
                //读写已存在文件时提示用户
                saveDlg.OverwritePrompt = true;
                //为图像选择一个筛选器
                saveDlg.Filter = "BMP文件(*.bmp)|*.bmp|" + "Gif文件(*.gif)|*.gif|" +
                    "JPEG文件(*.jpg)|*.jpg|" + "PNG文件(*.png)|*.png";
                //启用“帮助”按钮
                saveDlg.ShowHelp = true;
                //如果选择了格式,则保存图像
                if (saveDlg.ShowDialog() == DialogResult.OK)
                {
                    //获取用户选择的文件名
                    string fileName = saveDlg.FileName;
                    //保存文件
                    Bitmap a = new Bitmap(pictureBox2.Image);
                    a.Save(fileName,ImageFormat.Bmp);
                   
                          
                }
            }
    这样对整体有影响吗?
      

  4.   

    确实是这样,还是改变了色深。
        Bitmap a = new Bitmap(pictureBox2.Image); 
    说明.NET的这个构造函数确实会不管原始图像时申明色深,统一都创建为32位的。你要创建一个完全一样的,可以这样用
    Bitmap a =(Bitmap) pictureBox1.Image.Clone();
      

  5.   

    如何在保存程序中直接把转化的那个Bitmap b读写出来是不是就可以了保存方法一、上面那一条
      

  6.   

    看不懂你在讲什么。
     Bitmap a = new Bitmap(pictureBox2.Image); 
    这句代码,如果 pictureBox2.Image是灰度图,创建的a却是32位的,两者不一致,当然保存后的就不一样了啊。
      

  7.   


    通过这条语句测试Bitmap a =(Bitmap) pictureBox1.Image.Clone();
    a可以输出的是pictureBox1中的24位图像,也就是原始图像,所以不存在a创建的本身就是32位这一说