我想使用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数组。
因此3D阵列是复制的图像,如何编辑3D阵列的像素,例如改变颜色? pixelSize是什么用的?为什么要将图像存储在3D字节数组中而不是2D?
例如,如果我们想要使用2D数组,并且我想更改指定像素的颜色,我们该怎么做呢?
我们是否在bytearray OUTSIDE像素函数中对给定的复制图像进行操作,或者在解锁图像之前在像素函数中进行操作?
如果我们不再使用GetPixel或SetPixel?如何从复制的图像字节[]中检索像素的颜色?
如果您不理解我的问题,请解释如何使用上述代码进行操作,例如&#34;添加50&#34;到给定图像的每个像素的R,G,B,没有getPixel,setPixel
答案 0 :(得分:3)
3D阵列的第一个组件是颜色组件。因此,在索引1,78,218处,像素的蓝色分量的值为78,218。
像这样:
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)
由于图像被复制,如果在解锁图像之前或之后弄乱图像,则没有任何区别。锁定是为了确保在您进行实际复制时没有人更改图像。
values
数组是将2D数组展平为平面数组。 2D索引.[x,y]
位于stride * y + x * pixelSize
。然后RGB组件各有一个字节。这解释了为什么在x,y:
values.[stride * y + x * pixelSize + i] |> int
要为每个像素添加50,它更容易使用原始3D阵列。假设您有一张图片myImage
:
pixels (myImage) |> Array3D.map ((+) 50)
此类型为Array3D<Color>
,而非Image
。如果您需要Image
,则需要从现有的Array3D
构建{{1}}。