位图图像处理

时间:2014-04-04 05:07:51

标签: f# f#-interactive f#-3.0

我想使用LockBits方法替换GetPixel和SetPixel,所以我遇到了这个F# lazy pixels reading

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])

在代码的最后,它返回一个包含复制数据的3D数组。

  1. 因此3D阵列是复制的图像,如何编辑3D阵列的像素,例如改变颜色? pixelSize是什么用的?为什么要将图像存储在3D字节数组中而不是2D?

  2. 例如,如果我们想要使用2D数组,并且我想更改指定像素的颜色,我们该怎么做呢?

  3. 我们是否在bytearray OUTSIDE像素函数中对给定的复制图像进行操作,或者在解锁图像之前在像素函数中进行操作?

  4. 如果我们不再使用GetPixel或SetPixel?如何从复制的图像字节[]中检索像素的颜色?

  5. 如果您不理解我的问题,请解释如何使用上述代码进行操作,例如&#34;添加50&#34;到给定图像的每个像素的R,G,B,没有getPixel,setPixel

1 个答案:

答案 0 :(得分:3)

  1. 3D阵列的第一个组件是颜色组件。因此,在索引1,78,218处,像素的蓝色分量的值为78,218。

  2. 像这样:

    Array2D.init Width Height (fun x y -> 
        let color i = values.[stride * y + x * pixelSize + i] |> int
        new Color(color 0, color 1, color 2)
    
  3. 由于图像被复制,如果在解锁图像之前或之后弄乱图像,则没有任何区别。锁定是为了确保在您进行实际复制时没有人更改图像。

  4. values数组是将2D数组展平为平面数组。 2D索引.[x,y]位于stride * y + x * pixelSize。然后RGB组件各有一个字节。这解释了为什么在x,y:

    处找到第i个颜色分量
     values.[stride * y + x * pixelSize + i] |> int
    
  5. 要为每个像素添加50,它更容易使用原始3D阵列。假设您有一张图片myImage

    pixels (myImage) |> Array3D.map ((+) 50) 
    

    此类型为Array3D<Color>,而非Image。如果您需要Image,则需要从现有的Array3D构建{{1}}。