[英]Metal Shading language for Core Image color kernel, how to pass an array of 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 一样,似乎没有进一步的文档……
但是,您仍然可以通过将缓冲区数据包装在CIImage
并在内核中对其进行采样来使用传递缓冲区数据的“旧方法”。 例如:
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)
请注意,3 通道图像没有CIFormat
,因为 GPU 不支持这些。 因此,您要么必须使用单通道.Rf
并将内核中的值重新打包为float3
,要么向数据添加一些步幅并分别使用.RGBAf
和float4
(我建议这样做,因为它减少了纹理提取) .
我做到了,如果答案是好的并且还部署到较低的目标,结果并不完全符合我的预期。 以字符串形式编写的原始内核与上述创建用作数据源的图像的方法之间的差异很大。
没有得到确切的原因,但我作为调色板来源传递的图像在大小和颜色上与创建的图像有点不同(可能是由于色彩空间)。
由于没有关于此声明的文档:
Metal CIKernel 实例支持具有任意结构化数据的参数。
我在业余时间尝试了很多,然后想到了这个。
首先是着色器:
float4 eight_bit_buffer(sampler image, constant simd_float3 palette[], float paletteSize, destination dest) {
float4 color = image.sample(image.transform(dest.coord()));
float dist = distance(color.rgb, palette[0]);
float3 returnColor = palette[0];
for (int i = 1; i < floor(paletteSize); ++i) {
float tempDist = distance(color.rgb, palette[i]);
if (tempDist < dist) {
dist = tempDist;
returnColor = palette[i];
}
}
return float4(returnColor, 1);
}
其次将调色板转换为SIMD3<Float>
:
static func toSIMD3Buffer(from palette: [RGB]) -> Data {
var simd3Palette = SIMD3Palette(palette)
let size = MemoryLayout<SIMD3<Float>>.size
let count = palette.count * size
let palettePointer = UnsafeMutableRawPointer.allocate(
byteCount: simd3Palette.count * MemoryLayout<SIMD3<Float>>.stride,
alignment: MemoryLayout<SIMD3<Float>>.alignment)
let simd3Pointer = simd3Palette.withUnsafeMutableBufferPointer { (buffer) -> UnsafeMutablePointer<SIMD3<Float>> in
let p = palettePointer.initializeMemory(as: SIMD3<Float>.self,
from: buffer.baseAddress!,
count: buffer.count)
return p
}
let data = Data(bytesNoCopy: simd3Pointer, count: count * MemoryLayout<SIMD3<Float>>.stride, deallocator: .free)
return data
}
我第一次尝试将 SIMD3 附加到 Data 对象,但可能由于内存对齐而无法正常工作。 请记住释放使用后创建的内存。
希望能帮助别人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.