如何从 GetPixelSpan 中提取、上传和处理字节数组,然后保存回文件?
How to extract, upcast and process an array of bytes from GetPixelSpan then save back to a file?
这可能是一件非常简单的事情,但我不太明白如何将各个部分组合在一起。 API 文档中的 This question and as well as this page 都在某种程度上暗示了答案,但我无法从他们那里找出我需要的东西。
所以现在我正在尝试实现一个简单的小程序来打开图像,将像素输出到数组中,对它们进行一些处理,然后将更新后的像素作为新图像保存回来。在这种特殊情况下,我想将每个像素周围 3x3 window 的平均值作为简单的模糊。具体操作不太重要(肯定有更有效的方法,我现在专门尝试编写一个简单的版本,以便稍后与其他版本进行比较),但我一直无法弄清楚如何制作这个发生。现在我拥有的是:
let accessClampedArrayWithDefault (arr: uint32[][]) width height def x y : uint32[] =
if x < 0 || x > width-1 || y < 0 || y > height-1 then
def
else
arr.[x + width * y]
let extractPixelParts (p: Rgba32) =
let R = uint32 p.R
let G = uint32 p.G
let B = uint32 p.B
let A = uint32 p.A
[|R; G; B; A|]
[<EntryPoint>]
let main argv =
use img = Image.Load(@"D:\Users\sampleimage.jpg")
let mutable out_img = img.Clone()
let pxs = img.GetPixelSpan().ToArray() |> Array.map extractPixelParts
let mutable (nps: uint32[][]) = Array.zeroCreate pxs.Length
let ac = accessClampedArrayWithDefault pxs img.Width img.Height [|0u;0u;0u;0u|]
for x in 0..img.Width-1 do
for y in 0..img.Height-1 do
let p = ac x y
for z in -1..1 do
for w in -1..1 do
let q = ac (x + z) (y + w)
nps.[x + y * img.Width] <- Array.zip p q |> Array.map (fun (a,b) -> a + b)
nps.[x + y * img.Width] <- Array.map (fun i -> float i / 9.0 |> uint32 ) nps.[x + y * img.Width]
let rpx = Array.collect (fun a -> Array.map byte a) nps
let out_img = Image.Load<Rgba32>(img.GetConfiguration(), rpx, Formats.Jpeg.JpegDecoder())
printfn "out_img's width is %d and height is %d" out_img.Width out_img.Height
但它失败了,let out_img =
行出现异常。如果我不包含 JpegDecoder 部分,则会收到有关缺少解码器的错误消息,但如果包含它,则会收到有关缺少 SOI 的错误消息。
所以,我的问题是,如何提取像素并使用比 8 位(例如 32 位)更大的可变大小的 them/each 通道,以便我可以执行无法表示的中间操作在每个通道 8 位中,在将最终结果转换回字节之前,然后将其重构回可以作为图像保存到磁盘的东西?
我很可能忘记提及一些重要的事情,所以请随时要求澄清 :) 谢谢。
我不熟悉 F#,但看起来有几个问题:
Image.Load<Rgba32>(img.GetConfiguration(), rpx, Formats.Jpeg.JpegDecoder())
行将尝试解码一个Jpeg编码的内存流(提供为byte[]
)。
关于您的问题:
so that I can perform intermediate operations that cannot be represented in 8 bits per channel
你为什么不只处理 Rgba32[]
数组?
不需要 extractPixelParts ...
东西。由于不必要的堆分配,将所有像素存储在锯齿状数组 (uint32[][]
) 中会导致代码执行非常缓慢。
编辑:
对不起,我误解了这一点。如果你需要更高的中间操作精度,我建议使用Vector4
!你可以使用 pixel.ToVector4()
和 pixel.PackFromVector4(...)
我的建议(仍未优化但可能易于理解):
- 请勿复制图片。只需通过
let pxs = img.GetPixelSpan().ToArray()
创建一个 Rgba32[]
(!!!) 数组
- 使用公式
arr[y * Width + x] = CreateMyNewRgbaPixelValueAtXY(....)
处理数组中的值,其中 CreateMyNewRgbaPixelValueAtXY(...)
应该 return 和 Rgba32
- Return
Image.LoadPixelData(pxs)
的新图片。 LoadPixelData
方法将通过将您的 pxs: Rgba32[]
数据复制到其中来创建一个新图像。
- 处理你的原图!
编辑 2
为了高效地执行中间操作,我建议如下:
- 通过为每个输入像素调用
pixel.ToVector4()
为填充的中间数组创建一个 inputPixelData:Vector4[]
- 创建另一个数组
outputPixelData:Vector4[]
并通过处理inputPixelData
填充它
- 使用
.PackFromVector4(outputPixelData[y * Width + x])
将 outputPixelData
打包回 pixels:Rgba32[]
数组(不知道在 F# 中最好的方法是什么)
Image.LoadPixelData(pixels)
可能有更好的方法,但我不熟悉 F#。
这可能是一件非常简单的事情,但我不太明白如何将各个部分组合在一起。 API 文档中的 This question and
所以现在我正在尝试实现一个简单的小程序来打开图像,将像素输出到数组中,对它们进行一些处理,然后将更新后的像素作为新图像保存回来。在这种特殊情况下,我想将每个像素周围 3x3 window 的平均值作为简单的模糊。具体操作不太重要(肯定有更有效的方法,我现在专门尝试编写一个简单的版本,以便稍后与其他版本进行比较),但我一直无法弄清楚如何制作这个发生。现在我拥有的是:
let accessClampedArrayWithDefault (arr: uint32[][]) width height def x y : uint32[] =
if x < 0 || x > width-1 || y < 0 || y > height-1 then
def
else
arr.[x + width * y]
let extractPixelParts (p: Rgba32) =
let R = uint32 p.R
let G = uint32 p.G
let B = uint32 p.B
let A = uint32 p.A
[|R; G; B; A|]
[<EntryPoint>]
let main argv =
use img = Image.Load(@"D:\Users\sampleimage.jpg")
let mutable out_img = img.Clone()
let pxs = img.GetPixelSpan().ToArray() |> Array.map extractPixelParts
let mutable (nps: uint32[][]) = Array.zeroCreate pxs.Length
let ac = accessClampedArrayWithDefault pxs img.Width img.Height [|0u;0u;0u;0u|]
for x in 0..img.Width-1 do
for y in 0..img.Height-1 do
let p = ac x y
for z in -1..1 do
for w in -1..1 do
let q = ac (x + z) (y + w)
nps.[x + y * img.Width] <- Array.zip p q |> Array.map (fun (a,b) -> a + b)
nps.[x + y * img.Width] <- Array.map (fun i -> float i / 9.0 |> uint32 ) nps.[x + y * img.Width]
let rpx = Array.collect (fun a -> Array.map byte a) nps
let out_img = Image.Load<Rgba32>(img.GetConfiguration(), rpx, Formats.Jpeg.JpegDecoder())
printfn "out_img's width is %d and height is %d" out_img.Width out_img.Height
但它失败了,let out_img =
行出现异常。如果我不包含 JpegDecoder 部分,则会收到有关缺少解码器的错误消息,但如果包含它,则会收到有关缺少 SOI 的错误消息。
所以,我的问题是,如何提取像素并使用比 8 位(例如 32 位)更大的可变大小的 them/each 通道,以便我可以执行无法表示的中间操作在每个通道 8 位中,在将最终结果转换回字节之前,然后将其重构回可以作为图像保存到磁盘的东西?
我很可能忘记提及一些重要的事情,所以请随时要求澄清 :) 谢谢。
我不熟悉 F#,但看起来有几个问题:
Image.Load<Rgba32>(img.GetConfiguration(), rpx, Formats.Jpeg.JpegDecoder())
行将尝试解码一个Jpeg编码的内存流(提供为byte[]
)。关于您的问题:
so that I can perform intermediate operations that cannot be represented in 8 bits per channel
你为什么不只处理 Rgba32[]
数组?
不需要 extractPixelParts ...
东西。由于不必要的堆分配,将所有像素存储在锯齿状数组 (uint32[][]
) 中会导致代码执行非常缓慢。
编辑:
对不起,我误解了这一点。如果你需要更高的中间操作精度,我建议使用Vector4
!你可以使用 pixel.ToVector4()
和 pixel.PackFromVector4(...)
我的建议(仍未优化但可能易于理解):
- 请勿复制图片。只需通过
let pxs = img.GetPixelSpan().ToArray()
创建一个 - 使用公式
arr[y * Width + x] = CreateMyNewRgbaPixelValueAtXY(....)
处理数组中的值,其中CreateMyNewRgbaPixelValueAtXY(...)
应该 return 和Rgba32
- Return
Image.LoadPixelData(pxs)
的新图片。LoadPixelData
方法将通过将您的pxs: Rgba32[]
数据复制到其中来创建一个新图像。 - 处理你的原图!
Rgba32[]
(!!!) 数组
编辑 2
为了高效地执行中间操作,我建议如下:
- 通过为每个输入像素调用
pixel.ToVector4()
为填充的中间数组创建一个inputPixelData:Vector4[]
- 创建另一个数组
outputPixelData:Vector4[]
并通过处理inputPixelData
填充它
- 使用
.PackFromVector4(outputPixelData[y * Width + x])
将outputPixelData
打包回pixels:Rgba32[]
数组(不知道在 F# 中最好的方法是什么) Image.LoadPixelData(pixels)
可能有更好的方法,但我不熟悉 F#。