【问题标题】:Bitmap byte manipulation in C#C# 中的位图字节操作
【发布时间】:2011-10-14 11:30:09
【问题描述】:

努力处理 Bitmap 对象的底层字节以消除图像的透明度。 我有一个静态删除透明度方法,它需要一个 Bitmap 对象,相关代码:http://pastebin.com/ZjjPSdx8

现在,当我使用基于文件的位图对象调用它时,位图不会发生突变。如果我理解正确,这是因为它基于http://support.microsoft.com/kb/814675 的文件

于是创建了一个复制位图的方法: http://pastebin.com/9rXRJ6cx

private Bitmap LoadBmp(string name)
    {
        Assembly asm = Assembly.GetExecutingAssembly();
        string loc = Path.GetDirectoryName(asm.Location);
        string path = Path.Combine(loc, "Images\\" + name);
        Bitmap notsafe = (Bitmap)Bitmap.FromFile(path);
        return ImageProcessor.SafeBitmap(notsafe);
...

一切都很好,花花公子。适用于 PNG,但不适用于 GIF。他们可怕地出来 变形。

尝试了另一种方法将文件写入字节数组,然后将位图基于此:

        byte[] b = new byte[2048];
        int c;
        byte[] imgArr;
        using (FileStream fs = new FileStream(path, FileMode.Open))
        {
            using (MemoryStream ms = new MemoryStream())
            {
                while ((c = fs.Read(b, 0, b.Length)) > 0)
                    ms.Write(b, 0, c);
                imgArr = ms.ToArray();
            }
        }

        return (Bitmap)Bitmap.FromStream(new MemoryStream(imgArr));

这不会扭曲 gif。但是我的删除透明度方法不再适用于 PNG! 显然我做错了什么,希望有人能帮忙!

【问题讨论】:

  • 与您的问题没有直接关系,但如果File 对象上有一个很棒的ReadAllBytes() 方法,System.IO.File.ReadAllBytes()

标签: c# bitmap png transparency gif


【解决方案1】:

编辑:

当从文件加载时,您可以解决不可变位图问题:

var bmp = new Bitmap(Image.FromFile("C:\bmp.bmp"))

这使您不必执行任何 Marshal.Copy(这很慢),它还解决了您的 GIF 被肢解和扭曲的问题。如果您有兴趣,执行此操作的内部 .NET 代码如下:

 g = Graphics.FromImage(this);
 g.Clear(Color.Transparent); 
 g.DrawImage(original, 0, 0, width, height);

所以它本质上是我在下面给你的代码,用于解决你的透明度问题,它不符合你的速度需求。

但是,如果您可以通过 g.FromImage 同时进行加载和复制,我的速度是 (5.6 seconds g.FromImage vs 6.4 seconds yours with 1000 gifs @ 543x341)

理论上,如果您将复制和删除透明度组合到同一个功能中,您可以使您的速度更快。如果您在不重写图像的情况下加载 GIF,它将采用不同的像素格式,实际上是8bppindexed,这意味着您的函数将无法按原样工作。无论哪种方式,上面的解决方案都会在使用 CheckIfTransparent 函数时解决问题,而下面的解决方案(我必须对其进行一些编辑)会更快。如果我这样做了,我可能会花一些时间来寻找它。我觉得这个问题很有趣。

我确实注意到您删除 alpha 的算法并不完全准确。我不确定确切的位置,但是当与透明渐变一起使用时,您的算法看起来不对。使用直接 GDI 的那个看起来和预期的一样。我会确保您正确转换颜色。


如果你只是想删除 png 或 gif 的透明度,做所有这些Marshal.Copy 和不安全的代码,为什么不创建一个新的位图,将背景绘制为白色,然后覆盖现有的 png/gif它?看起来不太复杂的代码。

编辑:像这样的东西:

    public static Bitmap ManagedSafeBitmap(string file)
    {
        using (var img = Image.FromFile(file))
        {
            var bmp = new Bitmap(img.Width, img.Height);
            bmp.SetResolution(img.HorizontalResolution, img.VerticalResolution);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.Clear(Color.White);
                g.DrawImage(img, 0, 0);
            }

            return bmp;
        }
    }

这将为您提供一个具有白色背景的新图像,并将源图像放在上面,有效地将所有透明像素更改为Color.White 或您想要传递的任何颜色g.Clear()

【讨论】:

  • 从大局出发,做得很好!
  • 是的,最初是这样做的,但想从中获得更快的速度。托管方法比 MakeSafe 和 CleanTransparent 的组合慢约 50%。如果没有 makesafe(尽管无可否认,没有它它就无法工作..),它会慢 300%。我将处理大量图像,因此速度差异很重要……(数以百万计)。
  • 谢谢!我自己做了一些速度测试,结合负载和透明(托管与非托管)我得到了 5.1 与 5.3 p/1000 的速度。然而,优点是当图像没有透明度时,非托管方法更快(可选地仅对支持它的图像删除透明度) - 我们的大量图像也将是 jpg .. 不管在这方面花费了太多时间,所以现在我要使用你的方法 - 截止日期和所有......但是仍然对图像失真的原因非常感兴趣,我很想让 orig 方法工作.. 如果有任何帮助:)
  • 我认为图像在复制过程中失真,而不是透明度变化。加载的 GIF 加载了索引调色板,我认为 Marshal.Copy 正在将现有数据复制到不同的像素格式中。 Bitmap.LockBits 希望您知道 PixelFormat,我认为它应该设置为 32bitArgb,而它应该是 8bppindexed。当然,您不能执行 Marshal.Copy 并期望得到正确的结果,因为您必须转换每个像素,无论如何这通常是有损转换。
【解决方案2】:

我会尝试使用 WriteableBitmap 类。我听说它是​​为使用托管代码进行像素操作而创建的。

【讨论】:

    猜你喜欢
    • 2020-05-22
    • 1970-01-01
    • 2015-03-21
    • 2011-06-05
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多