自定义 CIKernel 位移图

Custom CIKernel Displacement Map

我正在尝试为 iOS 8 创建一个位移贴图 cikernel,它从贴图 R 通道水平移动像素,从 G 通道垂直移动像素。 必须相对于源图像大小选择地图像素坐标 mapPixel = ((dest.x/source.width) * map.width, (dest.y / source.height) * map.height)

我测试的输入图像大小是 2048 x 2048 地图是红绿柏林噪声 2560 x 2560

在 Quartz Composer 中,cikernel 几乎按预期工作,只是贴图没有应用于整个图像

kernel vec4 coreImageKernel(sampler image, sampler displaceMap, float scaleX, float scaleY)
{
 vec2 destination = destCoord();

 vec2 imageSize = samplerSize(image);
 float xPercent = destination.x / imageSize.x;
 float yPercent = destination.y / imageSize.y;

 vec2 mapSize = samplerSize(displaceMap);
 vec2 mapCoord = vec2(mapSize.x * xPercent, mapSize.y * yPercent);

 vec4 mapPixel = sample(displaceMap, mapCoord);
 float ratioShiftX = ((mapPixel.x) * 2.0) - 1.0;
 float ratioShiftY = ((mapPixel.y) * 2.0) - 1.0;
 vec2 pixelShift = vec2(ratioShiftX * scaleX, ratioShiftY * scaleY);

 return sample(image, destination - pixelShift);
}

过滤器函数如下所示:

function __image main(__image image, __image displaceMap, __number scaleX, __number scaleY) {
  return coreImageKernel.apply(image.definition, null, image, displaceMap, scaleX, scaleY);
}

但是当我在 CIFilter 中加载 cikernel 时,结果与我在 Quartz Composer 中看到的相去甚远。 这是我的应用函数在 CIFilter

中的样子
override var outputImage:CIImage? {
    if let inputImage = inputImage {
        if let inputMap = inputMap {
            let args = [inputImage as AnyObject, inputMap as AnyObject, inputScaleX, inputScaleY]

            return CIDisplacementMapFilter.kernel?.applyWithExtent(inputImage.extent, roiCallback: {
                    (index, rect) in

                    if index == 0 {
                        return rect
                    }

                    return CGRectInfinite
                }, arguments: args)
        }
    }

    return nil
}

我猜 ROI 是错误的,采样器是平铺的,但我想不通。

事实证明内核是错误的。 这是一个可以完成这项工作的内核

kernel vec4 displace(sampler source, sampler map, float scaleX, float scaleY)
{

vec2 d = destCoord();
vec4 mapPixel = sample(map, samplerTransform(map, d));
float shiftX = ((mapPixel.x * 2.0) - 1.0) * scaleX;
float shiftY = ((mapPixel.y * 2.0) - 1.0) * scaleY;

vec2 s = samplerTransform(source, d + vec2(shiftX, shiftY));

return sample(source, s);
}

这与 Metal

的代码相同
#include <metal_stdlib>
using namespace metal;
#include <CoreImage/CoreImage.h>

extern "C" {
  namespace coreimage {
    float4 displaceFilterKernel(sampler source, sampler map, float scaleX, float scaleY)
    {
      float2 d = map.coord();
      float4 mapPixel = map.sample(d);
      float shiftX = ((mapPixel.x * 2.0) - 1.0) * scaleX;
      float shiftY = ((mapPixel.y * 2.0) - 1.0) * scaleY;
      float2 s = float2(d.x, 1.0 - d.y) + float2(shiftX, shiftY);
      return sample(source, s);
    }
  }
}