ios - Core Image颜色内核的Metal Shading语言,如何传递一个float3数组
问题描述
我正在尝试通过对 Core Image 使用金属着色语言来CIFilter
从这个源中移植一些内容。
我有一个由结构数组组成的调色板,RGB
我想将它们作为参数传递给自定义 CI 彩色图像内核。
RGB 结构被转换为SIMD3<Float>
.
static func SIMD3Palette(_ palette: [RGB]) -> [SIMD3<Float>] {
return palette.map{$0.toFloat3()}
}
内核应该采用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
}
这是内核:
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);
}
我想知道如何将数据缓冲区传递给内核,因为将其转换为 NSData 似乎还不够。
我看到了一些示例,但他们使用的“完整”着色语言不适用于 Core Image,这是一种仅用于处理片段的子集。
解决方案
Core Image 内核似乎不支持指针或数组参数类型。虽然 iOS 13 似乎有一些东西。来自发行说明:
Metal CIKernel 实例支持带有任意结构化数据的参数。
但是,与 Core Image 一样,似乎没有进一步的文档......</p>
但是,您仍然可以通过将缓冲区数据包装在 aCIImage
中并在内核中对其进行采样来使用传递缓冲区数据的“旧方式”。例如:
let array: [Float] = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]
let data = array.withUnsafeBufferPointer { Data(buffer: $0) }
let dataImage = CIImage(bitmapData: data, bytesPerRow: data.count, size: CGSize(width: array.count/4, height: 1), format: .RGBAf, colorSpace: nil)
请注意,没有CIFormat
3 通道图像,因为 GPU 不支持这些图像。因此,您要么必须使用单通道.Rf
并重新将内核中的值重新打包float3
,要么向您的数据添加一些步幅并分别使用.RGBAf
和float4
(我建议这样做,因为它减少了纹理提取)。
当您将该图像传递到内核时,您可能希望将采样模式设置为nearest
,否则在两个像素之间采样时可能会得到插值:
kernel.apply(..., arguments: [dataImage.samplingNearest(), ...])
在您的(金属)内核中,您可以像使用普通输入图像一样通过以下方式评估数据sampler
:
extern "C" float4 myKernel(coreimage::sampler data, ...) {
float4 data0 = data.sample(float2(0.5, 0.5)); // data[0]
float4 data1 = data.sample(float2(1.5, 0.5)); // data[1]
// ...
}
请注意,我添加0.5
了坐标,以便它们指向数据图像中像素的中间,以避免歧义和插值。
另请注意,您从 a 获得的像素值sampler
始终有 4 个通道。因此,即使您使用 formate 创建数据图像,在对其进行采样时.Rf
也会得到 a float4
(其他值填充0.0
为 G 和 B 以及1.0
alpha)。在这种情况下,你可以做
float data0 = data.sample(float2(0.5, 0.5)).x;
推荐阅读
- javascript - 按 Enter 提交表单并以模式显示结果而无需重新加载
- scala - 为什么在我使用 SortedSet 时出现“错误:...发散隐式扩展...”?
- vba - 我正在尝试在 VBA 上运行特定代码并看到以下错误 runtime error run time error-2147319765(8002802b)
- java - java文件没有出现在项目中
- reactjs - React app componentDidMount 渲染两次
- c++ - 假设有一个名为 A 的类。传递以下两个对象有什么区别:(a) A obj1 和 (b) A obj1()?
- java - Java - CodeName One:找不到符号文件输入/输出
- amazon-ec2 - 如何在 EC2 引导脚本中安全地传递密码?
- c++ - 在opencv C++中使用未声明的标识符'createFisherFaceRecognizer'
- reactjs - 按 3:1 对齐标签和输入