【问题标题】:Creating 8-bit images创建 8 位图像
【发布时间】:2013-03-26 02:36:38
【问题描述】:

我正在尝试创建具有纯色背景色的 8 位图像。看起来应该很简单,但文件上的详细信息将其列为 32 位色深。我错过了什么?

    public void CreateImage()
    {
        var bmpOut = new Bitmap(300, 300);
        var g = Graphics.FromImage(bmpOut);
        g.FillRectangle(new SolidBrush(Color.Gray), 0, 0, 300, 300);

        var stream = new MemoryStream();
        bmpOut.Save(stream, GetPngCodecInfo(), GetEncoderParameters());

        bmpOut.Save(@"C:\image.png", GetPngCodecInfo(), GetEncoderParameters());
    }

    public EncoderParameters GetEncoderParameters()
    {
        var parameters = new EncoderParameters();
        parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8);

        return parameters;
    }

    public ImageCodecInfo GetPngCodecInfo()
    {
        var encoders = ImageCodecInfo.GetImageEncoders();

        ImageCodecInfo codecInfo = null;

        foreach (var imageCodecInfo in encoders)
        {
            if (imageCodecInfo.FormatID != ImageFormat.Png.Guid)
                continue;

            codecInfo = imageCodecInfo;
            break;
        }

        return codecInfo;
    }

【问题讨论】:

  • 据我所知,您缺少的是调色板...

标签: c# image


【解决方案1】:

使用此构造函数指定像素格式:http://msdn.microsoft.com/en-us/library/3z132tat.aspx

由于您无法从索引像素格式创建图形,因此您只能将原始像素写入 8 位图像。

Bitmap bitmap = new Bitmap(32, 32, PixelFormat.Format8bppIndexed);
var bitmapData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadWrite, bitmap.PixelFormat);
Random random=new Random();
byte[] buffer=new byte[bitmap.Width*bitmap.Height];
random.NextBytes(buffer);
Marshal.Copy(buffer,0,bitmapData.Scan0,buffer.Length);
bitmap.UnlockBits(bitmapData);
bitmap.Save("test.bmp",ImageFormat.Bmp);

您可以在 WinForms 上使用此类代码:http://www.codeproject.com/Articles/17162/Fast-Color-Depth-Change-for-Bitmaps

或者如果你可以从 WPF 中引用这个类,它会容易得多: http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.formatconvertedbitmap(v=vs.85).aspx

【讨论】:

  • 不过要小心。仅指定像素格式是不够的,因为创建 8 位索引图像将在下一行失败 - 从图像创建 Graphics
  • 从索引格式创建图形会引发异常:无法从具有索引像素格式的图像创建图形对象。
  • Random 到底在做什么?
  • @Nyerguds 被用来创建随机图像,我猜?事实上,我偶然发现了这个答案,寻找一种可视化我的随机数据的方法
  • 顺便说一句,这个答案实际上并没有回答这个问题。这绝不允许用户确定如何选择和绘制特定颜色。为此,您需要编辑调色板。据我所知,由此产生的随机图像仅使用使用 8 位图像创建的默认调色板的颜色。
【解决方案2】:

您还可以以更高的比特率创建图像,然后在保存之前将其转换为 8 位。这将允许您在创建图像时使用图形上下文。有关如何转换为 8 位的建议,请参阅此问题:C# - How to convert an Image into an 8-bit color Image?

【讨论】:

    【解决方案3】:
    • ImageExtensions.cs

      using System.Runtime.InteropServices;
      using System.Linq;
      
      using System.Drawing.Imaging;
      using System.Drawing;
      using System;
      
      public static partial class ImageExtensions {
          public static ColorPalette ToGrayScale(this ColorPalette palette) {
              var entries=palette.Entries;
      
              for(var i=entries.Length; i-->0; entries[i]=entries[i].ToGrayScale())
                  ;
      
              return palette;
          }
      
          public static Color ToGrayScale(this Color color, double[] luminance=null) {
              var list=(luminance??new[] { 0.2989, 0.5870, 0.1140 }).ToList();
              var channel=new[] { color.R, color.G, color.B };
              var c=(byte)Math.Round(list.Sum(x => x*channel[list.IndexOf(x)]));
              return Color.FromArgb(c, c, c);
          }
      
          public static Bitmap To8bppIndexed(this Bitmap original) {
              var rect=new Rectangle(Point.Empty, original.Size);
              var pixelFormat=PixelFormat.Format8bppIndexed;
              var destination=new Bitmap(rect.Width, rect.Height, pixelFormat);
      
              using(var source=original.Clone(rect, PixelFormat.Format32bppArgb)) {
                  var destinationData=destination.LockBits(rect, ImageLockMode.WriteOnly, pixelFormat);
                  var sourceData=source.LockBits(rect, ImageLockMode.ReadOnly, source.PixelFormat);
      
                  var destinationSize=destinationData.Stride*destinationData.Height;
                  var destinationBuffer=new byte[destinationSize];
      
                  var sourceSize=sourceData.Stride*sourceData.Height;
                  var sourceBuffer=new byte[sourceSize];
      
                  Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, sourceSize);
                  source.UnlockBits(sourceData);
      
                  destination.Palette=destination.Palette.ToGrayScale();
                  var list=destination.Palette.Entries.ToList();
      
                  for(var y=destination.Height; y-->0; ) {
                      for(var x=destination.Width; x-->0; ) {
                          var pixelIndex=y*destination.Width+x;
                          var sourceIndex=4*pixelIndex;
      
                          var color=
                              Color.FromArgb(
                                  sourceBuffer[0+sourceIndex],
                                  sourceBuffer[1+sourceIndex],
                                  sourceBuffer[2+sourceIndex],
                                  sourceBuffer[3+sourceIndex]
                                  ).ToGrayScale();
      
                          destinationBuffer[pixelIndex]=(byte)list.IndexOf(color);
                      }
                  }
      
                  Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, destinationSize);
                  destination.UnlockBits(destinationData);
              }
      
              return destination;
          }
      }
      

    在保存到文件之前调用bmpOut=bmpOut.To8bppIndexed();

    【讨论】:

    • 将调色板转换为灰度以获取新图像没有意义。只需使用从 00,00,00 到 FF,FF,FF 的颜色值填充调色板。使用这样的调色板,您可以 100% 确定所有灰度值,并且放在最终像素上的 8 位值简单地等于实际灰度像素颜色的三个颜色分量中的任何一个。不再需要查找。
    猜你喜欢
    • 2022-01-02
    • 2012-08-22
    • 2010-09-14
    • 2011-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多