Преобразование растрового изображения в специальный байтовый массив серой шкалы

Мне нужно создать изображение в памяти (изображение может быть огромным!) И извлечь из него байтовый массив размером ширины x высоты. Каждый байт должен иметь значение 0–255 (256 значений шкалы серого: 0 для белого и 255 для черного). Часть создания изображения проста, вот простой пример моего кода:

img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
drawing.Clear(Color.Black);// paint the background
drawing.DrawString(text, font, Brushes.White, 0, 0);

Проблема в том, чтобы преобразовать его в "мой" специальный байтовый массив серой шкалы. Когда я использую любой формат пикселей, кроме Format8bppIndexed, массив байтов, который я получаю из растрового изображения, не того размера, который мне нужен (ширина * длина), поэтому мне нужно преобразование, которое занимает слишком много времени. Когда я использую Format8bppIndexed, я получаю массив байтов очень быстро и нужного размера, но каждый байт / пиксель равен 0-15.

Изменение палитры растровых изображений не влияет:

var pal = img.Palette;
for (int i = 1; i < 256; i++){
   pal.Entries[i] = Color.FromArgb(255, 255, 255);
}
img.Palette = pal;

Есть идеи, как это сделать?

Изменить: Полный код:

// assume font can be Times New Roman, size 7500!
static private Bitmap DrawText(String text, Font font)
{
    //first, create a dummy bitmap just to get a graphics object
    var img = new Bitmap(1, 1);
    var drawing = Graphics.FromImage(img);

    //measure the string to see how big the image needs to be
    var textSize = drawing.MeasureString(text, font);

    //free up the dummy image and old graphics object
    img.Dispose();
    drawing.Dispose();

    //create a new image of the right size (must be multiple of 4)
    int width = (int) (textSize.Width/4) * 4;
    int height = (int)(textSize.Height / 4) * 4;
    img = new Bitmap(width, height);

    drawing = Graphics.FromImage(img);

    // paint the background
    drawing.Clear(Color.Black);

    drawing.DrawString(text, font, Brushes.White, 0, 0);

    var bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height),    ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);

    var newBitmap = new Bitmap(width, height, bmpData.Stride, PixelFormat.Format8bppIndexed, bmpData.Scan0);

    drawing.Dispose();

    return newBitmap;
}

private static byte[] GetGrayscleBytesFastest(Bitmap bitmap)
{
    BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
    int numbytes = bmpdata.Stride * bitmap.Height;
    byte[] bytedata = new byte[numbytes];
    IntPtr ptr = bmpdata.Scan0;

    Marshal.Copy(ptr, bytedata, 0, numbytes);

    bitmap.UnlockBits(bmpdata);

    return bytedata;
}

person Yariv    schedule 06.08.2014    source источник
comment
Это C # / .NET? Пожалуйста, отметьте это   -  person geedubb    schedule 06.08.2014
comment
Несколько примечаний: вы нацелены на изображение или необработанный массив байтов? Ваш код создания палитры должен быть pal.Entries[i] = Color.FromArgb(i, i, i); от 0 до 255. В остальном мире 0 = черный и 255 = белый. Не могли бы вы показать нам реальный код для создания изображения Format8bppIndexed? Вы смотрели замки? Необходимо, чтобы все было быстро.   -  person TaW    schedule 06.08.2014
comment
Я добавил полный код. Это самый быстрый способ, который я нашел, только с проблемой получения 0-15 значений на пиксель.   -  person Yariv    schedule 07.08.2014
comment
Кстати, я попробовал pal.Entries [i] = Color.FromArgb (i, i, i) перед вызовом моего GetGrayscleBytesFastest. Это не повлияло (все еще 0-15 значений)   -  person Yariv    schedule 07.08.2014


Ответы (1)


Вероятно, вы захотите сделать это в два этапа. Сначала создайте копию исходного изображения в оттенках серого 16bpp, как описано в Преобразование изображения в оттенки серого.

Затем создайте изображение 8bpp с соответствующей таблицей цветов и нарисуйте изображение в оттенках серого 16bpp на это изображение. Это выполнит преобразование за вас, преобразовав 16-битные значения оттенков серого в ваши 256 различных цветов.

У вас должно получиться изображение 8bpp с 256 различными оттенками серого. Затем вы можете вызвать LockBits, чтобы получить доступ к битам растрового изображения, которые будут значениями индекса в диапазоне от 0 до 255.

person Jim Mischel    schedule 06.08.2014
comment
С помощью этого решения мне нужно перебрать все пиксели. Я проверил, это может занять до 25 секунд, когда растровое изображение имеет размер 75 см x 50 см (мне нужно поддерживать такой размер). - person Yariv; 07.08.2014
comment
2,5 секунды, мое плохое. Из моего журнала: - person Yariv; 07.08.2014
comment
Самое быстрое преобразование в оттенки серого заняло 159,0159 мс. Для массива исправлений потребовалось 2405,2405 мс (это путем перебора всех пикселей) - person Yariv; 07.08.2014