0

我最近开始研究图像处理的主题。我认为我应该做的第一件事就是了解图像的工作原理。我的最新项目涉及制作图像的新副本。我想尽可能快地做到这一点,所以我试图想出尽可能多的方法。我为每种方法编写了一个方法,然后计算调用该方法 100 次所需的时间。这些是我的结果:

Marshal:    0.45584
Instance:   1.69299
Clone:      0.30687
GetSet:   341.74056
Pointer:    2.54130
Graphics:   1.07960

每个方法都传递一个源图像和目标图像。最终目标是将第一张图像中的所有像素复制到第二张图像中。

private void MarshalCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    // Lock the bitmap's bits.
    Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
    BitmapData readData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
    BitmapData writeData = destinationImage.LockBits(rect, ImageLockMode.WriteOnly, sourceImage.PixelFormat);

    // Get the address of the first line.
    IntPtr sourcePtr = readData.Scan0;
    IntPtr destinationPtr = writeData.Scan0;
    byte[] rgbValues = new byte[readData.Stride * readData.Height];

    Marshal.Copy(sourcePtr, rgbValues, 0, rgbValues.Length);
    Marshal.Copy(rgbValues, 0, destinationPtr, rgbValues.Length);

    sourceImage.UnlockBits(readData);
    destinationImage.UnlockBits(writeData);
}
private void PointerCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    // Lock the bitmap's bits.
    Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
    BitmapData readData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
    BitmapData writeData = destinationImage.LockBits(rect, ImageLockMode.WriteOnly, sourceImage.PixelFormat);
    unsafe
    {
        // Get the address of the first line.
        byte* readPointer = (byte*)readData.Scan0.ToPointer();
        byte* writePointer = (byte*)writeData.Scan0.ToPointer();

        int lengthOfData = readData.Stride * readData.Height;
        for (int i = 0; i < lengthOfData; i++)
        {
            *writePointer++ = *readPointer++;
        }
    }
    sourceImage.UnlockBits(readData);
    destinationImage.UnlockBits(writeData);
}
private void InstanceCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    destinationImage = new Bitmap(sourceImage);
}
private void CloneRegionMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    destinationImage = sourceImage.Clone(new Rectangle(860, 440, 200, 200), sourceImage.PixelFormat);
}
private void CloneCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    destinationImage = (Bitmap)sourceImage.Clone();
}
private void GetSetPixelCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    for (int y = 0; y < sourceImage.Height; y++)
    {
        for (int x = 0; x < sourceImage.Width; x++)
        {
            destinationImage.SetPixel(x, y, destinationImage.GetPixel(x, y));
        }
    }
}
private void GraphicsCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    using(Graphics g = Graphics.FromImage(destinationImage))
    {
        g.DrawImage(sourceImage, new Point(0, 0));
    }
}

以下两行也添加到每个方法的末尾:

destinationImage.SetPixel(955, 535, Color.Red);
destinationImage.SetPixel(965, 545, Color.Green);

我这样做是因为我读到了一些关于 Image.Clone() 的内容。大意是在您修改了克隆的一部分之前,实际上并未创建副本。如果不设置这些像素,Clone() 方法的完成速度似乎快了 1000 倍。我不太确定那里到底发生了什么。

结果似乎与我在网上阅读的内容有关。但是,指针方法是我在 Get/Set Pixel 方法之外实现的最慢的方法。从我的个人研究来看,我希望指针是最快的,如果不是最快的话。

我有几个与我的项目有关的问题。在这种情况下,我是否最适合使用指针?为什么更改克隆图像中的像素会影响克隆方法?是否有另一种方法可以在更短的时间内复制图像?还有其他建议/提示吗?谢谢。

4

1 回答 1

1

数字看起来很合理。概括:

  • GetPixel/SetPixel很慢
  • 专门编写的代码更快
  • 编写快速版本memcpy非常困难,在任何语言的一般情况下几乎不可能击败库版本(可以期望在特定大小/目标 CPU 等特殊情况下获得更好的性能)。

如果您想更多地使用指针 - 尝试和测量: - 在常规 C#(索引)中尝试相同的代码 - 尝试切换到int复制 - 注意每一行都是 DWORD 对齐的 - 不需要特殊情况来处理尾部。- 从封送样本中重新实现块副本

于 2013-06-01T23:28:49.293 回答