And*_*rea 5 core-image ios cifilter metal metalkit
我正在尝试通过使用 Core Image 的金属着色语言CIFilter从该源移植一些内容。
我有一个由结构数组组成的调色板RGB,我想将它们作为参数传递给自定义 CI 彩色图像内核。
RGB 结构体被转换为SIMD3<Float>.
static func SIMD3Palette(_ palette: [RGB]) -> [SIMD3<Float>] {
return palette.map{$0.toFloat3()}
}
Run Code Online (Sandbox Code Playgroud)
内核应该采用simd_float3值数组,问题是当我启动过滤器时,它告诉我索引 1 处的参数需要一个NSData.
override var outputImage: CIImage? {
guard let inputImage = inputImage else
{
return nil
}
let palette = EightBitColorFilter.palettes[Int(inputPaletteIndex)]
let extent = inputImage.extent
let arguments = [inputImage, palette, Float(palette.count)] as [Any]
let final = colorKernel.apply(extent: extent, arguments: arguments)
return final
}
Run Code Online (Sandbox Code Playgroud)
这是内核:
float4 eight_bit(sample_t image, simd_float3 palette[], float paletteSize, destination dest) {
float dist = distance(image.rgb, palette[0]);
float3 returnColor = palette[0];
for (int i = 1; i < floor(paletteSize); ++i) {
float tempDist = distance(image.rgb, palette[i]);
if (tempDist < dist) {
dist = tempDist;
returnColor = palette[i];
}
}
return float4(returnColor, 1);
}
Run Code Online (Sandbox Code Playgroud)
我想知道如何将数据缓冲区传递到内核,因为将其转换为 NSData 似乎还不够。
我看到了一些例子,但他们使用的是“完整”着色语言,该语言不适用于核心图像,它是一种仅处理片段的子集。
我们现在已经弄清楚如何将数据缓冲区直接传递到 Core Image 内核中。CIImage不需要使用下面描述的 a ,但仍然可以。
假设您的原始数据为NSData,您可以在调用时将其传递给内核:
kernel.apply(..., arguments: [data, ...])\nRun Code Online (Sandbox Code Playgroud)\n注意:Data也可能有效,但我知道这NSData是一种参数类型,允许 Core Image 根据输入参数缓存过滤器结果。因此,当有疑问时,最好转换为NSData.
然后在内核函数中,您只需要声明适当的constant类型的参数:
extern "C" float4 myKernel(constant float3 data[], ...) {\n float3 data0 = data[0];\n // ...\n}\nRun Code Online (Sandbox Code Playgroud)\n上一个答案
\nCore Image 内核似乎不支持指针或数组参数类型。尽管 iOS 13 似乎会带来一些新的东西。从发行说明来看:
\n\n\nMetal CIKernel 实例支持具有任意结构化数据的参数。
\n
但是,就像 Core Image 经常出现的那样,似乎没有进一步的文档\xe2\x80\xa6
\n但是,您仍然可以使用传递缓冲区数据的“旧方法”,将其包装在 a 中CIImage并在内核中对其进行采样。例如:
let array: [Float] = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]\n let data = array.withUnsafeBufferPointer { Data(buffer: $0) }\n let dataImage = CIImage(bitmapData: data, bytesPerRow: data.count, size: CGSize(width: array.count/4, height: 1), format: .RGBAf, colorSpace: nil)\nRun Code Online (Sandbox Code Playgroud)\n请注意,没有CIFormat3 通道图像,因为 GPU 不支持这些图像。因此,您要么必须使用单通道.Rf并再次重新打包内核中的值float3,要么向数据中添加一些步幅并分别使用.RGBAf和float4(我推荐这样做,因为它减少了纹理获取)。
当您将该图像传递到内核时,您可能希望将采样模式设置为nearest,否则在两个像素之间采样时可能会得到插值:
kernel.apply(..., arguments: [dataImage.samplingNearest(), ...])\nRun Code Online (Sandbox Code Playgroud)\n在您的(Metal)内核中,您可以像使用普通输入图像一样通过以下方式评估数据sampler:
extern "C" float4 myKernel(coreimage::sampler data, ...) {\n float4 data0 = data.sample(data.transform(float2(0.5, 0.5))); // data[0]\n float4 data1 = data.sample(data.transform(float2(1.5, 0.5))); // data[1]\n // ...\n}\nRun Code Online (Sandbox Code Playgroud)\n请注意,我添加了0.5坐标,以便它们指向数据图像中像素的中间,以避免模糊和插值。
另请注意,您从 a 获得的像素值sampler始终有 4 个通道。因此,即使您使用 formate 创建数据图像.Rf,采样时也会得到 a float4(其他值填充0.0为 G 和 B 以及1.0alpha)。在这种情况下,你可以这样做
float data0 = data.sample(data.transform(float2(0.5, 0.5))).x;\nRun Code Online (Sandbox Code Playgroud)\n编辑
\n我之前忘记将样本坐标从绝对像素空间(其中(0.5, 0.5)将是第一个像素的中间)转换为相对采样器空间(其中(0.5, 0.5)将是整个缓冲区的中间)。现在已经修好了。
| 归档时间: |
|
| 查看次数: |
1996 次 |
| 最近记录: |