【问题标题】:Bitmap.Clone just throws "Out of memory" error. Are there alternatives?Bitmap.Clone 只是抛出“内存不足”错误。有替代品吗?
【发布时间】:2011-08-07 03:44:26
【问题描述】:

我需要将 Drawing.Bitmap 转换为 4 位灰度。有没有办法做到这一点?我试过使用 Bitmap.Clone 但我只得到了通常臭名昭著的“内存不足”异常。即使它设法转换为4位,这也会是灰度吗?

任何提示将不胜感激。

谢谢,

纳尔逊·H

【问题讨论】:

标签: .net vb.net bitmap out-of-memory system.drawing


【解决方案1】:

没有 4bpp 灰度图像格式。下一个最好的是 4bppIndexed,其调色板包含 16 种灰色。 GDI+ 对这种格式的支持很差,设置像素的唯一方法是直接用 Bitmap.LockBits() 写入。这在 VB.NET 中很难做到,C# 更喜欢用指针来操作位图数据。像这样:

    public unsafe static void Save4bppGrayscale(Bitmap src, string path) {
        var bmp = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format4bppIndexed);

        // Create gray-scale palette
        var pal = bmp.Palette;
        for (int ix = 0; ix < 16; ++ix) {
            var c = 255 * ix / 15;
            pal.Entries[ix] = Color.FromArgb(c, c, c);
        }
        bmp.Palette = pal;

        // Map pixels
        var data = bmp.LockBits(new Rectangle(0, 0, src.Width, src.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format4bppIndexed);
        for (int y = 0; y < src.Height; ++y) {
            byte* line = (byte*)(IntPtr)((long)data.Scan0 + y * data.Stride);
            for (int x = 0; x < src.Width; ++x) {
                var pix = src.GetPixel(x, y);
                var c = (int)(15 * pix.GetBrightness());
                if (x % 2 == 1) c <<= 4;
                *(line + x / 2) |= (byte)c;
            }
        }
        bmp.UnlockBits(data);

        bmp.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
    }

使用此代码转换的示例图像:

它不是特别快,调色板可以使用一些伽马校正来避免生成太暗的图像。

【讨论】:

  • 对你来说速度很慢的一个原因是你使用 GetPixel 而不是从指针读取和写入。
  • 是的,根据需要改进和修饰。
【解决方案2】:

如果您尝试在 ASP.NET 网站上完成图像处理,则会发生内存不足异常,因为 IIS 应用程序池会限制分配的内存量。因此,我们创建了一个独立于 IIS 的独立 Windows 服务,并进行转换。而在 IIS 网站中,我们只是以 WCF 或命名管道的形式触发服务。

【讨论】:

  • 问题发帖者没有说任何关于 ASP.NET 的内容;反正这不是他的问题。另外,我在 ASP.NET 中完成了图像处理,没有遇到任何此类异常。
【解决方案3】:

假设Imports System.Runtime.InteropServices, System.Drawing.Imaging 在代码文件的顶部。 (LockBits 并不难,我用它做了很多图像处理,而且我更喜欢 VB.NET 而不是 C#。)

Private Sub To4BitGrayScale(ByVal b As Bitmap)
    Dim bd As BitmapData = b.LockBits(New Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, ImageFormat.Format24BppRgb)
    Dim arr(bd.Width * bd.Height * 3 - 1) As Byte
    Marshal.Copy(bd.Scan0, arr, 0, arr.Length)

    For i As Integer = 0 To arr.Length - 1 Step 3
        Dim c As Color = Color.FromArgb(255, arr(i), arr(i + 1), arr(i + 2))

        ' Convert c to grayscale however you want; weighted, average, whatever.

        arr(i) = c.R
        arr(i + 1) = c.G
        arr(i + 2) = c.B
    Next

    Marshal.Copy(arr, 0, bd.Scan0, arr.Length)
    b.UnlockBits(bd)
End Sub

当然,这种方法并不快(对我来说,一张 8 兆像素的图像大约需要 1-2 秒)但也不错。

【讨论】:

  • 你能再解释一下这段代码吗?更具体地说,如何调整代码以实现“随便”的结果?我正在寻找创建 1bpp 图像。我需要改变什么才能实现这一目标?
猜你喜欢
  • 2014-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-11
  • 1970-01-01
  • 1970-01-01
  • 2014-12-28
  • 2019-10-09
相关资源
最近更新 更多