F# 如何从GetPixelSpan提取、上传和处理字节数组,然后保存回文件?
这可能是一件非常简单的事情,但我不太明白如何将这些片段组合在一起。以及在API文档中,所有这些都或多或少地暗示了答案,但我一直无法从它们中找到我需要的东西 所以现在我正在尝试实现一个天真的小程序来打开一个图像,将像素取出到一个数组中,对它们进行一点处理,然后将更新后的像素保存回一个新图像。在这个特殊的例子中,我想把每个像素周围3x3窗口的平均值作为一个简单的模糊。具体的操作并不太重要(肯定有更有效的方法,我现在特别尝试编写一个幼稚的版本,以便稍后与其他版本进行比较),但我还没有找到实现这一点的方法。现在我得到的是:F# 如何从GetPixelSpan提取、上传和处理字节数组,然后保存回文件?,f#,.net-core,imagesharp,F#,.net Core,Imagesharp,这可能是一件非常简单的事情,但我不太明白如何将这些片段组合在一起。以及在API文档中,所有这些都或多或少地暗示了答案,但我一直无法从它们中找到我需要的东西 所以现在我正在尝试实现一个天真的小程序来打开一个图像,将像素取出到一个数组中,对它们进行一点处理,然后将更新后的像素保存回一个新图像。在这个特殊的例子中,我想把每个像素周围3x3窗口的平均值作为一个简单的模糊。具体的操作并不太重要(肯定有更有效的方法,我现在特别尝试编写一个幼稚的版本,以便稍后与其他版本进行比较),但我还没有找到实现这一点的方
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
让accessClampedArrayWithDefault(arr:uint32[][])宽度高度定义x y:uint32[]=
如果x<0 | | x>宽度-1 | | y<0 | | y>高度-1,则
def
其他的
方位角[x+宽度*y]
let extractPixelParts(p:Rgba32)=
设R=uint32 p.R
设G=uint32 p.G
设B=uint32 p.B
设A=uint32 p.A
[R;G;B;A]
[]
让主argv=
使用img=Image.Load(@“D:\Users\sampleimage.jpg”)
让可变输出\u img=img.Clone()
让pxs=img.GetPixelSpan().ToArray()|>Array.map提取PixelParts
让可变(nps:uint32[]]])=Array.zeroCreate pxs.Length
设ac=AccessClampedArray,默认pxs img.宽度img.高度[|0u;0u;0u;0u |]
对于0..img.Width-1 do中的x
对于y,0..img.Height-1 do
设p=ac×y
对于z in-1..1 do
对于w in-1..1 do
设q=ac(x+z)(y+w)
nps。[x+y*img.Width]Array.map(乐趣(a,b)->a+b)
nps。[x+y*惯性宽度]浮动i/9.0 |>uint32)nps。[x+y*惯性宽度]
让rpx=Array.collect(funa->Array.map字节a)nps
释放\u img=Image.Load(img.GetConfiguration(),rpx,Formats.Jpeg.JpegDecoder())
打印fn“out\u img的宽度为%d,高度为%d”out\u img.width out\u img.height
但是它失败了,在释放\u img=
行出现异常。如果我没有包含JPEG解码器部分,那么我会得到一条关于丢失解码器的错误消息,但是如果我包含了它,那么我会得到一条关于丢失SOI的错误消息
因此,我的问题是,在将最终结果转换回字节之前,如何提取像素并以大于8位(例如32位)的可变大小处理它们/每个通道,以便执行不能以每个通道8位表示的中间操作,然后将其重新构造成可以作为图像保存到磁盘上的东西
我很可能忘了提一些重要的事情,所以请尽管要求澄清:)谢谢。我对F#不熟悉,但似乎有几个问题:
- 行
将尝试解码内存流中编码的Jpeg(作为Image.Load(img.GetConfiguration()、rpx、Formats.Jpeg.JpegDecoder())
提供)字节[]
- 关于你的问题: 因此,我可以执行不能以每个通道8位表示的中间操作
Rgba32[]
阵列呢?
不需要extractPixelParts…
之类的东西。将所有像素存储在锯齿阵列(uint32[][]
)中会导致代码执行非常缓慢,因为不必要的堆分配
编辑:
对不起,我误解了这一点。如果中间运算需要更高的精度,我建议使用Vector4
!您可以使用pixel.ToVector4()
和pixel.PackFromVector4(…)
我的建议(仍然没有优化,但可能很容易理解):
let pxs=img.GetPixelSpan().ToArray()创建一个Rgba32[]
(!!!)数组
arr[y*Width+x]=CreateMyNewRgbaPixelValueAtXY(…)
其中CreateMyNewRgbaPixelValueAtXY(…)
应返回一个Rgba32
image.LoadPixelData(pxs)
返回新图像。LoadPixelData
方法将通过将pxs:Rgba32[]
数据复制到图像中来创建新图像- 通过调用每个输入像素的
为中间数组创建一个pixel.ToVector4()
inputPixelData:Vector4[]
- 创建另一个数组
并通过处理outputPixelData:Vector4[]
inputPixelData
- 使用
将.PackFromVector4(outputPixelData[y*Width+x])
打包回一个outputPixelData
数组(不知道在F中使用这种方法的最佳方式是什么)像素:Rgba32[]
Image.LoadPixelData(像素)
也许有更好的方法,但我不熟悉F#。是的,用你的输入就可以了!非常感谢:)