Могу ли я скопировать этот массив быстрее? - PullRequest
6 голосов
/ 02 марта 2011

Является ли это абсолютным быстрым Я мог бы возможно скопировать Bitmap в Byte[] в C#?

Если есть более быстрый способ, который я хотел бы узнать!

const int WIDTH = /* width */;
const int HEIGHT = /* height */;


Bitmap bitmap = new Bitmap(WIDTH, HEIGHT, PixelFormat.Format32bppRgb);
Byte[] bytes = new byte[WIDTH * HEIGHT * 4];

BitmapToByteArray(bitmap, bytes);


private unsafe void BitmapToByteArray(Bitmap bitmap, Byte[] bytes)
{
    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, WIDTH, HEIGHT), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);

    fixed(byte* pBytes = &bytes[0])
    {
        MoveMemory(pBytes, bitmapData.Scan0.ToPointer(), WIDTH * HEIGHT * 4);
    }

    bitmap.UnlockBits(bitmapData);
}

[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
private static unsafe extern void MoveMemory(void* dest, void* src, int size);

Ответы [ 4 ]

6 голосов
/ 02 марта 2011

Что ж, было бы разумнее использовать Marshal.Copy(), что, по крайней мере, сокращает (одноразовые) затраты на поиск экспорта DLL.Но это все, они оба используют функцию времени выполнения C memcpy().Скорость полностью ограничена пропускной способностью шины ОЗУ, ускорять ее может только покупка более дорогой машины.

Остерегайтесь того, что профилирование сложно, доступ к данным растрового изображения в первый раз приводит к сбоям страницы при передаче данных пикселей в память.,Время, необходимое для этого, зависит от работы вашего жесткого диска и состояния кэша файловой системы.

4 голосов
/ 26 октября 2013

Я читаю, что вы собираетесь делать пиксельные манипуляции с растровым изображением.Логично, что вы хотите получить доступ к пикселям как к массиву, чтобы сделать это.

Проблема, с которой вы столкнулись, в корне ошибочна, и другие парни не поняли это - и, возможно, эточто-то, что они тоже могут забрать?

По сути, вы до сих пор возились с указателями из растровых изображений, я собираюсь дать вам один из моих с трудом заработанных "секретов".

Вам не нужно копировать бит в карту.Просто используйте его как есть.

Небезопасные указатели - ваш друг в этом случае.Когда вы нажимаете «bitmapData.Scan0.ToPointer ()», вы пропускаете трюк.

Пока у вас есть указатель на первый пиксель и Stride, и вы знаете количество байтов назатем вы получите победителя.

Я определяю растровое изображение специально с 32 битами на пиксель (память эффективна для доступа к UInt32) и получаю указатель на первый пиксель.Затем я рассматриваю указатель как массив и могу как считывать, так и записывать данные пикселей как значения UInt32.

Код говорит громче, чем слова.посмотрите.

Я приветствую вас за то, что вы сделали это так далеко!

Это непроверенный код, но многое скопировано из моего источника.

public delegate void BitmapWork(UInt32* ptr, int stride);

    /// <summary>
    /// you don't want to forget to unlock a bitmap do you?  I've made that mistake too many times...
    /// </summary>
    unsafe private void UnlockBitmapAndDoWork(Bitmap bmp, BitmapWork work)
    {
        var s = new Rectangle (0, 0, bmp.Width, bmp.Height); 
        var locker = bmp.LockBits(new Rectangle(0, 0, 320, 200), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

        var ptr = (UInt32*)locker.Scan0.ToPointer();

        // so many times I've forgotten the stride is expressed in bytes, but I'm accessing with UInt32's.  So, divide by 4.
        var stride = locker.Stride / 4;

        work(ptr, stride);
        bmp.UnlockBits(locker);
    }

    //using System.Drawing.Imaging;
    unsafe private void randomPixels()
    {
        Random r = new Random(DateTime.Now.Millisecond);

        // 32 bits per pixel.  You might need to concern youself with the Alpha part depending on your use of the bitmap
        Bitmap bmp = new Bitmap(300, 200, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        UnlockBitmapAndDoWork(bmp, (ptr, stride) => 
        {
            var calcLength = 300 * 200; // so we only have one loop, not two.  It's quicker!
            for (int i = 0; i < calcLength; i++)
            {
                // You can use the pointer like an array.  But there's no bounds checking.
                ptr[i] = (UInt32)r.Next();
            }
        });            
    }
1 голос
/ 02 марта 2011

Я бы тоже посмотрел на System.Buffer.BlockCopy.
Эта функция также очень быстрая, и она может быть конкурентной в этой настройке, поскольку вы копируете из управляемого в управляемое в вашем случае. К сожалению, я не могу предоставить некоторые тесты производительности.

0 голосов
/ 02 марта 2011

Я думаю, что это быстрее (я действительно использовал это):

// Bitmap bytes have to be created via a direct memory copy of the bitmap
private byte[] BmpToBytes_MemStream (Bitmap bmp)
{
    MemoryStream ms = new MemoryStream();
    // Save to memory using the Jpeg format
    bmp.Save(ms, ImageFormat.Jpeg);

    // read to end
    byte[] bmpBytes = ms.GetBuffer();
    bmp.Dispose();
    ms.Close();

    return bmpBytes;
}

Оригинальный источник

...