Конвертиране на растерно изображение в специален байтов масив в сива скала

Трябва да създам изображение в паметта (може да бъде огромно изображение!) и да извлека от него байтов масив с размер ширина 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 изображение? Гледал ли си lockbits? Необходимо е да се правят нещата бързо.   -  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 изображение с подходящата цветова таблица и начертайте изображението в сивата скала от 16 bpp върху това изображение. Това ще направи преобразуването вместо вас, преобразувайки 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 ms Fix array отне 2405,2405 ms (това е чрез преминаване през всички пиксели) - person Yariv; 07.08.2014