我怎样才能找到 CIImage 中的最亮点(也许在 Metal 中)?

How can I find the brightest point in a CIImage (in Metal maybe)?

我在 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 的警告是它的 return 类型是代表像素新颜色的 float4。理想情况下,我想要 image -> array 类型的交易,而不是 image -> image 过滤器。例如。减少而不是映射。我有一种不好的预感,这将需要我渲染它并在 CPU.

上处理它

最终我想在我的 swift 函数中检索符合条件的坐标(每个图像可以有多个坐标)。

最终解决方案编辑:

根据答案的建议,我在 GPU 上进行大量的每像素计算,并在 CPU 上进行一些数学运算。我设计了 2 个额外的内核,它们的工作方式类似于内置的缩减内核。一个内核 return 是每列中最大值的 1 像素高图像,另一个内核 return 是最大值的归一化 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 的每个像素,但就我的目的而言,它 return 足够了:每列中的最大值及其位置。

专业版:仅将(2 * 图像宽度)字节复制到 CPU 而不是每个像素可以节省大量时间(几毫秒)。

缺点:如果同一列中有两个主要的白点,您永远不会知道。如果适合您的用例,您可能必须更改此设置并按行而不是列进行计算。

跟进:

渲染输出时似乎有问题。 metal 中的浮点值 return 与我在 swift.

中得到的 UInt8 值不相关

This unanswered question描述了问题。

编辑: 提供了一个非常方便的 metal 函数。当您在金属值(例如 0.5)和 return 上调用它时,您将得到正确的值(例如 128)在 CPU.

查看 CICategoryReduction 中的过滤器(如 CIAreaAverage)。它们 return 图像只有几个像素高,包含缩小结果。但是您仍然必须渲染它们才能读取 Swift 函数中的值。

使用这种方法解决问题的问题是您事先不知道要 return 的坐标数。不过,Core Image 在调用内核时需要知道输出的扩展。您可以 假设一个静态的最大坐标数,但这听起来很乏味。

我认为您最好使用 Accelerate APIs 在 CPU 上迭代图像的像素(并行化,超级高效)以找到相应的坐标。

您可以采用混合方法,使用 Core Image 在 GPU 上进行 per-pixel 繁重的数学运算,然后使用 Accelerate 在 CPU 上进行分析。您甚至可以使用 CIImageProcessorKernel.

将 CPU 部分集成到您的 Core Image 管道中