【问题标题】:How to convert Bitmap to byte[,,] faster?如何更快地将位图转换为 byte[,,]?
【发布时间】:2011-06-08 03:23:45
【问题描述】:

我写了函数:

    public static byte[, ,] Bitmap2Byte(Bitmap image)
    {
        int h = image.Height;
        int w = image.Width;

        byte[, ,] result= new byte[w, h, 3];

        for (int i = 0; i < w; i++)
        {
            for (int j = 0; j < h; j++)
            {
                Color c= image.GetPixel(i, j);
                result[i, j, 0] = c.R;
                result[i, j, 1] = c.G;
                result[i, j, 2] = c.B;
            }
        }

        return result;
    }

但转换 1800x1800 图像需要将近 6 秒。我可以更快地做到这一点吗?

编辑:
好的,我找到了这个:http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx
有很好的例子。我唯一的问题是关于Marshal.Copy。我可以让它直接将数据复制到byte[,,]吗?

编辑 2: 好的,有时我得到奇怪的像素值,它们似乎不遵循 r0 g0 b0 r1 g1 b1 规则。为什么? 没关系。想通了。

编辑 3: 做好了。 0,13s 与 5,35s :)

【问题讨论】:

  • 尝试交换循环:您当前正在迭代图像的,但位图存储在中。交换循环允许更好的缓存行为,这应该在一定程度上提高性能(尽管可能不会太多)。
  • 你是对的。 5,65 秒前。 5.35 秒后。
  • @Miko:您可能想再次运行该测试几次。两者之间应该没有任何区别。
  • @MusiGenesis:虽然我无法消除在 这种情况 或通常在 C# 中无法检测到差异的可能性,但我可以向您保证其背后的理论是合理的。它与 CPU 缓存的工作方式有关:它始终存储 X 字节的块,即使请求较少,因此通过按实际存储的顺序迭代数据,可以更频繁地使用缓存。当然,这是一种微优化,但它不会损害可读性,如果您正在寻找使代码运行得更快的方法,那么每一点都会有所帮助。
  • @Miko:这是我告诉你谷歌的错,但不要使用Marshal.Copy。请参阅此答案:stackoverflow.com/questions/740555/…

标签: c# winforms image-processing bitmap


【解决方案1】:

您可以通过使用从Bitmap.LockBits 返回的BitmapData 对象来大大加快速度。谷歌“C# Bitmap LockBits”获取大量示例。

GetPixel 非常缓慢,非常缓慢,使其(具有讽刺意味的是)完全不适合处理单个像素。

【讨论】:

  • 是的,GetPixel 可能是访问位图最慢的方式。
  • @Gabe:我不同意。最慢的方法是将每个像素值手写在一张纸上,然后邮寄给居住在西伯利亚的开发人员。
  • LockBits 将为您提供一个像素数组,即 [row 0][row 1][row 2]...[row nRows-1],其中 [row] 是像素的列长度。只需记住公式 index = (j * rowLength) + i,它会为您提供像素数组中像素的偏移量。这也解释了迈克尔的评论。
  • +1 是的,GDI+ 中任何重要的图像操作都需要原始内存访问。
  • @MusiGenesis:亲自交给他们比邮寄要慢得多:)
【解决方案2】:

我一直在想这个问题。

在 .NET 4.0 中,Microsoft 引入了 Parallel 库。基本上,它的作用是有一个 Parallel.For 方法,它会自动产生许多线程来帮助工作。 例如,如果您最初有一个 For(int i =0;i

Color c
 lock(obraz) 
{
  c =  obraz.GetPixel(..)
}
...

获取像素时。

如果您需要更多关于并行性的解释,在您花一些时间研究它之前,我真的无法为您提供帮助,因为它是一个庞大的研究领域。

【讨论】:

  • 你能解释一下吗?我对并行循环一无所知。
  • @Miko:我不会去那里。由于位图在 .Net 中是如何工作的,并行化这个过程可能会使它变得更慢。
  • @MusiGenesis,实际上我已经在图像处理的类似领域做到了这一点,而且速度非常快。
  • 获取指向原始内存的指针会更快,为什么要复杂化呢?
  • @dko:并行化对于 .Net Bitmap 及其 GetPixel/SetPixel 方法以外的任何东西都将是一个巨大的好处。最好的解决方案可能是从 Bitmap 生成 BitmapData 对象,然后以并行方式操作 BitmapData。但是,大部分性能改进都来自于一开始就没有使用 GetPixel。
【解决方案3】:

我刚试过并行For
如果没有位图上的SyncLock,它将无法工作。
它表示该对象正在使用中。
所以它几乎可以正常工作,连续哈哈......真是一团糟。

    For xx As Integer = 0 To 319
        pq.ForAll(Sub(yy)
                      Dim Depth = getDepthValue(Image, xx, yy) / 2047
                      Dim NewColor = Depth * 128
                      Dim Pixel = Color.FromArgb(NewColor, NewColor, NewColor)
                      SyncLock Bmp2
                          Bmp2.SetPixel(xx, yy, Pixel)
                      End SyncLock
                  End Sub)
    Next

如果您想知道,这是转换 kinect 的 depth map -&gt; bitmap
Kinect 的深度范围是 11bit(0-2047),代表距离而不是颜色。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-08
    相关资源
    最近更新 更多