用GDI+生成的BMP图片颜色位数一般都是24位,我要怎样生成1位的BMP图片呢?
我在网上找了几种方法,有的方法效果是达到了,但是效率非常低,我是要生成几万张甚至几十万张的,所以要求速度要够快。
不知道大家是否有这方面的经验请多多指教,非常感谢大家!!
我现在知道实现的思路有两种,但是两种都有问题。
思路1及问题:
新建BMP对象的时候直接就定义它是1位的,Bitmap b = new System.Drawing.Bitmap(30, 30, PixelFormat.Format1bppIndexed); 但是这样定义后,图片是使用索引颜色,所以不能在它的基础上创建Graphics画图。思路2及问题:先生成普通24位色的BMP图片,然后将它转换成1位色的BMP,我在网上找了这么一段代码来转换,但是转换的效率很低。
Image img = Image.FromFile("r.bmp");
            ImageAttributes ta = new ImageAttributes();            /* 下面用Graphics类改变像点颜色,是靠ImageAttributes来把
            * 彩色变成灰度,或者颠倒黑白,发现用矩阵处理还是很方便的
            */
            //如实际发现几个简单又好用的矩阵:
            float[][] mm = new float[][]{ //彩色变灰度的矩阵
                                          new float[]{0.4f, 0.4f, 0.4f, 0, 0},
                                          new float[]{0.3f, 0.3f, 0.3f, 0, 0},
                                          new float[]{0.3f, 0.3f, 0.3f, 0, 0},
                                          new float[]{0, 0, 0, 1, 0},
                                          new float[]{0, 0, 0, 0, 1}
                                        };
            /*
            float[][] mm1=new float[][]{ //彩色反相的矩阵
              new float[]{0, 0.3f, 0.5f, 0, 0},
              new float[]{0.5f, 0.3f, 0.5f, 0, 0},
              new float[]{0.5f, 0.4f, 0, 0, 0},
              new float[]{0, 0, 0, 1, 0},
              new float[]{0, 0, 0, 0, 1}
            };
            float[][] mm2=new float[][]{ //彩色变反相灰度的矩阵
              new float[]{-0.4f, -0.4f, -0.4f, 0, 0},
              new float[]{-0.3f, -0.3f, -0.3f, 0, 0},
              new float[]{-0.3f, -0.3f, -0.3f, 0, 0},
              new float[]{1, 1, 1, 1, 0},
              new float[]{0, 0, 0, 0, 1}
            };
            */            ColorMatrix cmt = new ColorMatrix(mm);
            ta.SetColorMatrix(cmt);
            /* //如果确知图像里仅有纯黑白二色,也可用ColorMap来反相,它可逐色改变
            ColorMap map1=new ColorMap();
            map1.OldColor=Color.Black;
            map1.NewColor=Color.White;
            ColorMap map2=new ColorMap();
            map2.OldColor=Color.White;
            map2.NewColor=Color.Black;
            ta.SetRemapTable(new ColorMap[]{map1,map2},ColorAdjustType.Bitmap);
            */
            /* 有的图像比如索引格式的位图或GIF是无法创建Graphics的,
            * 需要新建一非索引色位图取得Graphics对象以便做画或改变像点颜色。
            */
            Bitmap bmp = new Bitmap(img.Width, img.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(img, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, ta);
            //g.DrawString("Foxit PDF Reader",new Font("宋体",8),new SolidBrush(Color.White),0,0);
            g.Dispose();
            /* 在如下构造图像数据之前,也可以先创建一单色位图并锁定数据,
            * 利用它现成的Stride简单计算出实际每行有效数据之后的填充字节数,而且可
            * 在下面循环里直接写点Marshal.WriteByte(dt.Scan0,k,val);而不用数组拷贝
            */
            //以下,把反相或者涂画后的像点数据每一行的每8点简单合并成1byte存储
            int midrgb = Color.FromArgb(128, 128, 128).ToArgb();
            int stride;//简单公式((width/8)+3)&(~3)
            stride = (bmp.Width % 8) == 0 ? (bmp.Width / 8) : (bmp.Width / 8) + 1;
            stride = (stride % 4) == 0 ? stride : ((stride / 4) + 1) * 4;
            int k = bmp.Height * stride;
            byte[] buf = new byte[k];
            for (int j = 0; j < bmp.Height; j++)
            {
                k = j * stride;//因图像宽度不同、有的可能有填充字节需要跳越
                int x = 0, ab = 0;
                for (int i = 0; i < bmp.Width; i++)
                {
                    //从灰度变单色(下法如果直接从彩色变单色效果不太好,不过反相也可以在这里控制)
                    if ((bmp.GetPixel(i, j)).ToArgb() > midrgb) ab = ab * 2 + 1; else ab = ab * 2;
                    x++;
                    if (x == 8)
                    {
                        buf[k++] = (byte)ab;
                        ab = 0;
                        x = 0;
                    }
                }
                if (x > 0)
                {
                    //循环实现:剩余有效数据不满1字节的情况下须把它们移往字节的高位部分
                    for (int t = x; t < 8; t++) ab = ab * 2;
                    buf[k++] = (byte)ab;
                }
            }
            Bitmap bb = new Bitmap(img.Width, img.Height, PixelFormat.Format1bppIndexed);
            BitmapData dt = bb.LockBits(new Rectangle(0, 0, bb.Width, bb.Height), ImageLockMode.ReadWrite, bb.PixelFormat);
            Marshal.Copy(buf, 0, dt.Scan0, buf.Length);
            bb.UnlockBits(dt);
            bb.Save("w.bmp", ImageFormat.Bmp);
            bb.Dispose();
            bmp.Dispose();
            img.Dispose();

解决方案 »

  1.   

    你要对1位图像做什么处理啊,1位图像是不能绘制直线圆之类的。 那个转换效率低是因为GetPixel慢,要用lockbits的,苏都都是能达到毫秒级别的。并且24位转换为1位如果不抖动的话, 效果很差。
      

  2.   

    楼上说的对,参考一下这个, http://blog.csdn.net/hemmingway/article/details/8909662
      

  3.   

            /// <summary>
            /// 转换灰度的算法
            /// </summary>
            /// <param name="sourcebm"></param>
            private void SetLuma(Bitmap sourcebm)
            {
                int critical_value = 80;
                for (int x = 0; x < sourcebm.Width; x++)
                {
                    for (int y = 0; y < sourcebm.Height; y++)
                    {
                        Color c = sourcebm.GetPixel(x, y);
                        int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11);//转换灰度的算法
                        Color c2 = Color.FromArgb(luma, luma, luma);
                        if (c2.R >= critical_value)
                        {
                            sourcebm.SetPixel(x, y, Color.FromArgb(255, 255, 255));
                        }
                        else
                        {
                            sourcebm.SetPixel(x, y, Color.FromArgb(0, 0, 0));
                        }                }
                }        }
      

  4.   

    我现在用程序生成了一个里面都是黑点的图片,然后要发给印刷厂的喷码设备去喷码,但是他们的设置只支持1位色的bmp图片,所以我用C#生成的普通bmp图片需要转到1位色给他们,现在遇到的问题就是转换的速度太慢了。
      

  5.   

     我现在慢就慢在这两个循环中,但是好像必须要有这两个循环来转换颜色
    for (int j = 0; j < bmp.Height; j++)
                    {
                        k = j * stride;//因图像宽度不同、有的可能有填充字节需要跳越
                        int x = 0, ab = 0;
                        for (int i = 0; i < bmp.Width; i++)
                        {
                            //从灰度变单色(下法如果直接从彩色变单色效果不太好,不过反相也可以在这里控制)
                            if ((bmp.GetPixel(i, j)).ToArgb() > midrgb) ab = ab * 2 + 1; else ab = ab * 2;
                            x++;
                            if (x == 8)
                            {
                                buf[k++] = (byte)ab;
                                ab = 0;
                                x = 0;
                            }
                        }
                        if (x > 0)
                        {
                            //循环实现:剩余有效数据不满1字节的情况下须把它们移往字节的高位部分
                            for (int t = x; t < 8; t++) ab = ab * 2;
                            buf[k++] = (byte)ab;
                        }
                    }
      

  6.   

    不多说。在项目的属性里要勾选 容许不安全代码。
     public Bitmap ConvertTo24bppTo1bpp(Bitmap SrcImg)
            {
                unsafe
                {
                    byte* SrcPointer, DestPointer;
                    int Width, Height, SrcStride, DestStride;
                    int X, Y, Index, Sum; ;
                    Bitmap DestImg = new Bitmap(SrcImg.Width, SrcImg.Height, PixelFormat.Format1bppIndexed);
                    BitmapData SrcData = new BitmapData();
                    SrcImg.LockBits(new Rectangle(0, 0, SrcImg.Width, SrcImg.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb, SrcData);
                    BitmapData DestData = new BitmapData();
                    DestImg.LockBits(new Rectangle(0, 0, SrcImg.Width, SrcImg.Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed, DestData);
                    Width = SrcImg.Width; Height = SrcImg.Height; SrcStride = SrcData.Stride; DestStride = DestData.Stride;
                    for (Y = 0; Y < Height; Y++)
                    {
                        SrcPointer = (byte *)SrcData.Scan0 + Y * SrcStride;
                        DestPointer = (byte*)DestData.Scan0 + Y * DestStride;
                        Index = 7; Sum = 0;
                        for (X = 0; X < Width; X++)
                        {
                            if (*SrcPointer + (*(SrcPointer + 1) << 1) + *(SrcPointer + 2)>= 512) Sum += (1 << Index);
                            if (Index == 0)
                            {
                                *DestPointer = (byte)Sum;
                                Sum = 0;
                                Index = 7;
                                DestPointer++;
                            }
                            else
                                Index--;
                            SrcPointer+=3;
                        }
                        if (Index != 7) *DestPointer = (byte)Sum;
                    }
                    SrcImg.UnlockBits(SrcData);
                    DestImg.UnlockBits(DestData);
                    return DestImg;
                }
            }
      

  7.   

    这样改就OK了:
     System.Drawing.Imaging.BitmapData srcData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
                IntPtr ptr = srcData.Scan0;
                int strideBmp=srcData.Stride;
                int bytes = bmp.Height * strideBmp;
                byte[] srcValues = new byte[bytes];
                System.Runtime.InteropServices.Marshal.Copy(ptr, srcValues, 0, bytes);
                bmp.UnlockBits(srcData);
                Color temp;            int midrgb = Color.FromArgb(128, 128, 128).ToArgb();
                int stride;//简单公式((width/8)+3)&(~3)
                stride = (bmp.Width % 8) == 0 ? (bmp.Width / 8) : (bmp.Width / 8) + 1;
                stride = (stride % 4) == 0 ? stride : ((stride / 4) + 1) * 4;
                int k = bmp.Height * stride;
                byte[] buf = new byte[k];
                for (int j = 0; j < bmp.Height; j++)
                {
                    k = j * stride;//因图像宽度不同、有的可能有填充字节需要跳越
                    int x = 0, ab = 0;
                    for (int i = 0; i < bmp.Width; i++)
                    {
                        temp = Color.FromArgb(srcValues[i * 4 +2+ j * strideBmp], srcValues[i * 4 + 1 + j * strideBmp], srcValues[i * 4 + j * strideBmp]);
                        //从灰度变单色(下法如果直接从彩色变单色效果不太好,不过反相也可以在这里控制)
                        if (temp.ToArgb() > midrgb) ab = ab * 2 + 1; else ab = ab * 2;
                        x++;
                        if (x == 8)
                        {