为什么我在使用SetPixel时遇到异常:具有索引像素格式的图像不支持SetPixel?

Jam*_*ron 4 .net c# winforms

在我正在做的课程的顶部:

private static Bitmap bmp2 = new Bitmap(@"C:\Temp\New folder (17)\radar001486.GIF");
Run Code Online (Sandbox Code Playgroud)

然后在我正在做的方法内:

private void test()
   {
    int current_list_length = pointtocolor.Count;
                for (int kk=0;kk<current_list_length;kk++)
                {

                    PointF pt = pointtocolor[kk];
                    e.FillEllipse(cloudColors[cloudColorIndex], pt.X * (float)currentFactor, pt.Y * (float)currentFactor, radius, radius);
                    bmp2.SetPixel((int)pt.X * (int)currentFactor, (int)pt.Y * (int)currentFactor, Color.Yellow);

                }
                bmp2.Save(@"c:\temp\yellowbmpcolor.bmp");
   }
Run Code Online (Sandbox Code Playgroud)

一旦它进入循环,就会在线上产生异常:

bmp2.SetPixel((int)pt.X * (int)currentFactor, (int)pt.Y * (int)currentFactor, Color.Yellow);
Run Code Online (Sandbox Code Playgroud)

如果我将从以下位置更改bmp2的实例:

private static Bitmap bmp2 = new Bitmap(@"C:\Temp\New folder (17)\radar001486.GIF");
Run Code Online (Sandbox Code Playgroud)

private static Bitmap bmp2 = new Bitmap(512,512);
Run Code Online (Sandbox Code Playgroud)

然后它会工作,但我想SetPixel原始radar001486.GIF上的像素,而不是一个新的空位图.

Dan*_*yak 7

问题是你正在使用GIF,因为它有索引像素.如果可以,尝试将其转换为png; 或者,如果你不能,使用以下方法将其转换为非索引图像:

public Bitmap CreateNonIndexedImage(Image src)
{
    Bitmap newBmp = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    using (Graphics gfx = Graphics.FromImage(newBmp)) {
        gfx.DrawImage(src, 0, 0);
    }

    return newBmp;
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您可以选择这样做(即未下载的图像,或者您可以访问服务器),请务必将这些图像转换为PNG.


Mic*_*eld 5

您尝试更改的图像是带索引的 GIF。这意味着图像不包含一系列具有各自颜色值的像素(就像你的新像素Bitmap那样);相反,它包含一个调色板,结合了一系列像素及其索引值到调色板中。从磁盘加载的图像的像素格式可能类似于Format8bppIndexed.

您不能SetPixel在这种图像上使用,因为SetPixel要直接为像素设置 R、G 和 B 值。这不是索引图像的工作方式。

要更改这种图像,您有几个选择:

  • 最好的选择是使用 WPF,它有一个GifBitmapEncoderGifBitmapDecoder。这使您可以将 GIF 数据解码为 WPF 可以绘制的内容,然后将其转换回来。由于它使用 DirectX 而不是 GDI+,因此它没有SetPixel. 如果可以,我真的非常建议您走这条路,但如果不能:

  • 使用 GDI+将图像转换为非索引类型的图像,对其进行更改,然后再将其转换回来。这通常是一个糟糕的主意:GDI+ 和索引格式不兼容,这包括将位图编码为索引 GIF。图像质量可能很糟糕。

  • 直接编辑字节数据。为此,您需要将 GIF 数据提取到一个数组中,并将像素设置为正确的索引值。这里的技巧是找出正确的索引值;或者,如果调色板中碰巧有一个空的,您可以添加另一个。您需要深入研究 GIF 格式才能弄清楚这一点,尽管它在不降低图像质量的情况下在空间和速度方面可能是最有效的。一旦知道要写入的索引值,就可以执行以下操作:

    var data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), 
        ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
    
    var bytes = new byte[data.Height * data.Stride];
    Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
    
    bytes[5 * data.Stride + 5] = 1; // Set the pixel at (5, 5) to the color #1
    
    Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
    
    image.UnlockBits(data);
    
    Run Code Online (Sandbox Code Playgroud)