Как я могу скопировать данные пикселей из растрового изображения с отрицательным шагом? - PullRequest
5 голосов
/ 26 июля 2011

Я искал самый быстрый способ конвертировать растровое изображение в 8bpp. Я нашел 2 способа:

1.

        public static System.Drawing.Image ConvertTo8bpp(Bitmap oldbmp)
    {
        using (var ms = new MemoryStream())
        {
            oldbmp.Save(ms, ImageFormat.Gif);
            ms.Position = 0;
            return System.Drawing.Image.FromStream(ms);
        }
    }

2. http://www.wischik.com/lu/programmer/1bpp.html

Но: 1. Результат очень низкого качества (плохой поддон)

и 2 дает мне Битовое изображение с отрицательным шагом, когда я пытаюсь блокировать биты и копировать данные в байтовый массив, я получаю исключение: Попытка чтения или записи защищенной памяти. Это часто указывает на то, что другая память повреждена.

        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

        this.stride = bmpData.Stride;
        this.bytesPerPixel = GetBytesPerPixel(bmp.PixelFormat);
        int length = bmpData.Stride * bmp.Height;
        if (this.stride < 0)
            this.data = new byte[-length];
        else
            this.data = new byte[length];
        Marshal.Copy(bmpData.Scan0, data, 0, length);

        //Unlock the bitmap
        bmp.UnlockBits(bmpData);

Как я могу сделать 2 дает положительный шаг? Или как я могу скопировать данные, используя блокировку отрицательного шага ??

Ответы [ 5 ]

8 голосов
/ 14 июня 2013

Проблема здесь в том, что Scan0 указывает на начало первой строки сканирования, а не на начало первого байта данных.В восходящем растровом изображении первая строка сканирования составляет Stride байтов от конца растровых данных.

Когда вы вызываете Marshal.Copy для копирования данных из Scan0, он пытается скопировать (Height*Stride) байтов, начиная с позиции ((Height-1)*Stride).Понятно, что это уйдет в сорняки.

Если вы просто хотите скопировать растровые данные, вы должны рассчитать начальный адрес с помощью Scan0 - (Height-1)*Stride.Это начнет вас в начале растровых данных.Вы можете передать этот вычисленный адрес на Marshal.Copy.

Если вы хотите скопировать строки сканирования по порядку (т. Е. Вверху, следующему, следующему, ... внизу), то вам нужно скопировать строку навремя: скопируйте Stride байтов из Scan0, затем добавьте Stride (что отрицательно), скопируйте эту строку и т. д. У Рика Брюстера был правильный ответ: https://stackoverflow.com/a/10360753/56778

6 голосов
/ 28 апреля 2012

Копировать 1 строку за раз, вычисляя начальный указатель для строки как ((byte*)scan0 + (y * stride)).Код будет одинаковым как для положительного, так и для отрицательного шага.

4 голосов
/ 27 апреля 2012

Я не знаю, почему в растровом изображении, созданном методом FromHbitmap, есть что-то странное, но я знаю, что вы можете это исправить, используя Bitmap bmpClone = (Bitmap)bmp.Clone(); и выполняя LockBits на bmpClone.

Кроме того, я обнаружил, что если вы используете bmp.Clone(), вы не можете удалить () bmp, пока не закончите с клоном.

Это также работает, и давайте избавимся от изображения негативного шага раньше, чем позже:

        Bitmap bmp = null;
        using (Bitmap bmpT = CopyToBpp(bmpO, 1))
        {
            bmp = new Bitmap(bmpT);
        }
1 голос
/ 26 июля 2011

Из документации C # по BitmapData: шаг - это ширина одного ряда пикселей (строки сканирования), округленная до четырехбайтовой границы. Если шаг положительный, растровое изображение идет сверху вниз. Если шаг отрицательный, растровое изображение снизу вверх

0 голосов
/ 03 мая 2012

Я предполагаю, что вы получаете исключение из-за

this.data = new byte[-length];

И затем попытка скопировать данные в байтовый массив отрицательного размера (я не вижу, как это даже компилируется действительно ...).

...