我可以更快地复制这个数组吗?

时间:2011-03-02 04:27:59

标签: c# optimization bitmap copy bytearray

这是绝对最快我可以可能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 个答案:

答案 0 :(得分:6)

好吧,使用Marshal.Copy()会更明智,至少可以减少查找DLL导出的(一次)成本。但就是这样,它们都使用C运行时memcpy()函数。速度完全受到RAM总线带宽的限制,只需购买更昂贵的机器就可以加快速度。

请注意,分析很棘手,第一次访问位图数据会导致页面错误,从而将像素数据导入内存。这需要多长时间,这取决于您的硬盘正在做什么以及文件系统缓存的状态。

答案 1 :(得分:4)

我正在读到这一点,你正在寻找对位图进行像素处理。 所以从逻辑上讲,你想要像素一样访问像素,以便做到这一点。

你面临的问题从根本上是有缺陷的,其他人没有接受这个 - 也许这也是他们可以带走的东西?

基本上,你到目前为止要搞乱位图的指针,我会给你一个我辛苦赚来的“秘密”。

您不需要将BITMAP复制到阵列中。只要按原样使用它。

在这种情况下,不安全的指针是你的朋友。当你点击“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();
            }
        });            
    }

答案 2 :(得分:1)

我也会看System.Buffer.BlockCopy
此功能也非常快,在您可以在此设置中具有竞争力,因为您正在从托管复制到托管。不幸的是,我无法提供一些性能测试。

答案 3 :(得分:-2)

我认为这更快(我实际上使用它):

// 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;
}

Original Source