2011-01-06 80 views
0

我想将图像像素延迟加载到3维整数数组。 例如,在简单的方式,它看起来是这样的:F#懒惰像素读取

for i=0 to Width 
    for j=0 to Height 
     let point=image.GetPixel(i,j) 
     pixels.[0,i,j] <- point.R 
     pixels.[1,i,j] <- point.G 
     pixels.[2,i,j] <- point.B 

如何能在慵懒的方式进行?

回答

5

什么会慢是呼叫GetPixel如果你想打电话给它只是在需要时,你可以使用类似这样的东西:

open System.Drawing 

let lazyPixels (image:Bitmap) = 
    let Width = image.Width 
    let Height = image.Height 
    let pixels : Lazy<byte>[,,] = Array3D.zeroCreate 3 Width Height 
    for i = 0 to Width-1 do 
     for j = 0 to Height-1 do 
      let point = lazy image.GetPixel(i,j) 
      pixels.[0,i,j] <- lazy point.Value.R 
      pixels.[1,i,j] <- lazy point.Value.G 
      pixels.[2,i,j] <- lazy point.Value.B 
    pixels 

GetPixel对于每个像素最多会被调用一次,然后重新用于其他组件。

解决这个问题的另一种方法是对整个图像进行批量加载。这比反复调用GetPixel要快得多。

open System.Drawing 
open System.Drawing.Imaging 

let pixels (image:Bitmap) = 
    let Width = image.Width 
    let Height = image.Height 
    let rect = new Rectangle(0,0,Width,Height) 

    // Lock the image for access 
    let data = image.LockBits(rect, ImageLockMode.ReadOnly, image.PixelFormat) 

    // Copy the data 
    let ptr = data.Scan0 
    let stride = data.Stride 
    let bytes = stride * data.Height 
    let values : byte[] = Array.zeroCreate bytes 
    System.Runtime.InteropServices.Marshal.Copy(ptr,values,0,bytes) 

    // Unlock the image 
    image.UnlockBits(data) 

    let pixelSize = 4 // <-- calculate this from the PixelFormat 

    // Create and return a 3D-array with the copied data 
    Array3D.init 3 Width Height (fun i x y -> 
     values.[stride * y + x * pixelSize + i]) 

+0

感谢如何使用LockBits这个例子。你在创建3D阵列的最后一行做什么?它不适合我。 – Goswin 2013-06-24 17:35:03

+0

@Gos Doh。我正在返回索引,而不是值。现在修复。 – 2013-06-24 17:59:46

2

你什么意思是懒惰?

数组不是懒惰的数据类型,这意味着如果要使用数组,则需要在初始化期间加载所有像素。如果我们使用的是一维数组,则另一种方法是使用seq<_>这是懒惰的(但您只能按顺序访问元素)。对于多维数组,没有什么像seq<_>,所以你需要使用别的东西。

也许最接近的选择是使用三维数组的懒值(Lazy<int>[,,])。这是一个访问像素的延迟thunks数组,只有在您实际读取该位置处的值时才进行评估。你可以像这样初始化:

for i=0 to Width 
    for j=0 to Height 
    let point = lazy image.GetPixel(i,j) 
    pixels.[0,i,j] <- lazy point.Value.R 
    pixels.[1,i,j] <- lazy point.Value.G 
    pixels.[2,i,j] <- lazy point.Value.B 

的片断创建一个懒惰的值读取像素(point),然后三个懒汉值获取单个颜色分量。在访问颜色分量时,将评估point值(通过访问Value)。

在你的代码的其余部分的唯一区别是,你需要调用Value(如pixels.[0,10,10].Value得到像素的实际颜色分量。

你可以定义更复杂的数据结构(如您自己的类型,支持索引和懒惰),但我认为该数组的懒惰值应该是一个很好的起点

2

正如您可以使用懒人像素加载3D阵列但只想让与getPixel操作懒,而不是其他的意见已经提到的(从Bitmap.LockBits的C#示例通过)当您调用Array3D的create方法时,已经分配了数组的内存分配。

如果你想使内存分配以及GetPixel懒惰,那么你可以使用序列如图下面的代码:

let getPixels (bmp:Bitmap) = 
    seq { 
     for i = 0 to bmp.Height-1 do 
      yield seq { 
          for j = 0 to bmp.Width-1 do 
           let pixel = bmp.GetPixel(j,i) 
           yield (pixel.R,pixel.G,pixel.B) 
         } 
    }