Bitmap.Clone просто выдает ошибку «Недостаточно памяти».Есть ли альтернативы? - PullRequest
1 голос
/ 17 апреля 2011

Мне нужно преобразовать Drawing.Bitmap в 4-битную шкалу серого. Есть ли способ сделать это? Я пытался использовать Bitmap.Clone, но получаю только обычное печально известное исключение «Недостаточно памяти». Будет ли это в градациях серого, даже если его удалось преобразовать в 4 бит?

Любые советы будут с благодарностью.

Спасибо

Нельсон Х

Ответы [ 3 ]

2 голосов
/ 17 апреля 2011

Нет формата изображения в оттенках серого 4bpp.Следующим лучшим является индекс 4bpp с палитрой, содержащей 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);
    }

Пример изображения, преобразованного с помощью этого кода:

enter image description here

Это не особенно быстро, и цветовая палитра может использовать некоторую гамма-коррекцию дляСтарайтесь не создавать слишком темные изображения.

0 голосов
/ 17 апреля 2011

Предполагается 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 секунд), но это не плохо.

0 голосов
/ 17 апреля 2011

Исключение из-за недостатка памяти возникает, если вы пытаетесь выполнить обработку изображений на веб-сайте ASP.NET, поскольку пул приложений IIS ограничивает объем выделяемой памяти. Поэтому мы создали отдельную службу Windows, которая работает независимо от IIS и выполняет преобразование. А на веб-сайте IIS мы просто запускаем службу в форме WCF или именованных каналов.

...