具有自定义金属内核的核心图像过滤器不起作用

Core image filter with custom metal kernel doesn't work

我已经根据自定义内核制作了自定义 CIFilter,我无法使其工作,输出图像充满黑色,我不明白为什么。 这是着色器:

// MARK: Custom kernels
    float4 eight_bit(sampler image, sampler palette_image, float paletteSize) {
        float4 color = image.sample(image.coord());
        float dist = distance(color, palette_image.sample(float2(0,0)));
        float4 returnColor = palette_image.sample(float2(0,0));
        for (int i = 1; i < floor(paletteSize); ++i) {
            float tempDist = distance(color, palette_image.sample(float2(i,0)));
            if (tempDist < dist) {
                dist = tempDist;
                returnColor = palette_image.sample(float2(i,0));
            }
        }
        return returnColor;
    }

第一个采样器是需要详细说明的图像,第二个图像是包含必须在该图像中使用的特定调色板颜色的图像。
调色板图像是从 RGBA 值的数组创建的,传递到 Data 缓冲区,并使用此 CIImage 初始值设定项 init(bitmapData data: Data, bytesPerRow: Int, size: CGSize, format: CIFormat, colorSpace: CGColorSpace?) 创建。图片的高度为 1px,宽度为 color 数量。图片获取正确,看起来是这样的:

尝试检查我发现的着色器:

我开始认为 palette_image 以某种方式未正确通过。这里图像是如何通过过滤器的:

 override var outputImage: CIImage? {
        guard let inputImage = inputImage else
        {
            return nil
        }
        let palette = EightBitColorFilter.palettes[Int(0)]
        let paletteImage = EightBitColorFilter.image(from: palette)
        let extent = inputImage.extent
        let pixellateImage = inputImage.applyingFilter("CIPixellate", parameters: [kCIInputScaleKey: inputScale])
//        let sampler = CISampler(image: paletteImage)
        let arguments = [pixellateImage, paletteImage, Float(palette.count)] as [Any]

        let final = kernel.apply(extent: extent, roiCallback: {
                (index, rect) in
                return rect
        }, arguments: arguments)

        return final
    }

你的采样坐标不对。

采样器在Core Image中使用相对坐标,即(0,0)对应整个输入图像的左上角,(1,1)右下角 .

所以尝试这样的事情:

float4 eight_bit(sampler image, sampler palette_image, float paletteSize) {
    float4 color = image.sample(image.coord());
    // initial offset to land in the middle of the first pixel
    float2 firstPaletteCoord = float2(1.0 / (2.0 * palletSize), 0.5);
    float dist = distance(color, palette_image.sample(firstPaletteCoord));
    float4 returnColor = palette_image.sample(firstPaletteCoord);
    for (int i = 1; i < floor(paletteSize); ++i) {
        // step one pixel further
        float2 paletteCoord = firstPaletteCoord + float2(1.0 / paletteSize, 0.0);
        float4 paletteColor = palette_image.sample(paletteCoord);
        float tempDist = distance(color, paletteColor);
        if (tempDist < dist) {
            dist = tempDist;
            returnColor = paletteColor;
        }
    }
    return returnColor;
}