我在 Metal 中创建了一个自定义 CIKernel。这很有用,因为它接近实时。我正在避免任何可能实时滞后的 cgcontext 或 cicontext。我的内核本质上是进行霍夫变换,但我似乎无法弄清楚如何从图像缓冲区中读取白点。
这是kernel.metal:
#include <CoreImage/CoreImage.h>
extern "C" {
namespace coreimage {
float4 hough(sampler src) {
// Math
// More Math
// eventually:
if (luminance > 0.8) {
uint2 position = src.coord()
// Somehow add this to an array because I need to know the x,y pair
}
return float4(luminance, luminance, luminance, 1.0);
}
}
}
如果这部分可以提取到不同的内核或函数,我很好。CIKernel 的警告是,它的返回类型是一个代表像素新颜色的 float4。image -> image
理想情况下,我想要一种image -> array
交易,而不是过滤器。例如reduce而不是map。我有一种不好的预感,这将需要我在 CPU 上渲染并处理它。
最终,我想在我的 swift 函数中检索符合条件的坐标(每个图像可以有多个)。
最终解决方案编辑:
根据答案的建议,我正在 GPU 上进行大量的每像素计算,并在 CPU 上进行一些数学运算。我设计了 2 个额外的内核,它们的工作方式与内置缩减内核类似。一个内核返回每列中最大值的 1 像素高图像,另一个内核返回最大值的归一化 y 坐标的 1 像素高图像:
/// Returns the maximum value in each column.
///
/// - Parameter src: a sampler for the input texture
/// - Returns: maximum value in for column
float4 maxValueForColumn(sampler src) {
const float2 size = float2(src.extent().z, src.extent().w);
/// Destination pixel coordinate, normalized
const float2 pos = src.coord();
float maxV = 0;
for (float y = 0; y < size.y; y++) {
float v = src.sample(float2(pos.x, y / size.y)).x;
if (v > maxV) {
maxV = v;
}
}
return float4(maxV, maxV, maxV, 1.0);
}
/// Returns the normalized coordinate of the maximum value in each column.
///
/// - Parameter src: a sampler for the input texture
/// - Returns: normalized y-coordinate of the maximum value in for column
float4 maxCoordForColumn(sampler src) {
const float2 size = float2(src.extent().z, src.extent().w);
/// Destination pixel coordinate, normalized
const float2 pos = src.coord();
float maxV = 0;
float maxY = 0;
for (float y = 0; y < size.y; y++) {
float v = src.sample(float2(pos.x, y / size.y)).x;
if (v > maxV) {
maxY = y / size.y;
maxV = v;
}
}
return float4(maxY, maxY, maxY, 1.0);
}
这不会给出亮度大于 0.8 的每个像素,但出于我的目的,它会返回足够的值:每列中的最大值及其位置。
Pro:仅将(2 * 图像宽度)字节复制到 CPU 而不是每个像素都可以节省大量时间(几毫秒)。
缺点:如果你在同一列中有两个主要的白点,你永远不会知道。如果适合您的用例,您可能必须更改它并按行而不是按列进行计算。
跟进:
渲染输出似乎有问题。金属中返回的浮点值与我快速获得的 UInt8 值无关。
这个未回答的问题描述了这个问题。
编辑: 这个回答的问题提供了一个非常方便的金属功能。当您在金属值(例如 0.5 )上调用它并返回它时,您将在 CPU 上获得正确的值(例如 128 )。