Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom CIKernel Displacement Map

I'm trying to create a displacement map cikernel for iOS 8 that shifts the pixels horizontally from the map R channel and vertically from the G channel. The map pixel coordinates must be picked relative to the source image size mapPixel = ((dest.x/source.width) * map.width, (dest.y / source.height) * map.height)

The input image size that I test with is 2048 x 2048 and the map is red-green perlin noise 2560 x 2560

In Quartz Composer the cikernel works almost as expected, except that the map is not applied to the whole image

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);
}

Here's what the filter function looks like:

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

But when I load the cikernel in CIFilter the result is far from what I see in Quartz Composer. Here's what my apply function looks like in the 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
}

I'm guessing the ROI is wrong and the sampler is tiled, but I can't figure it out.

like image 938
Nikolay Nankov Avatar asked Nov 09 '22 05:11

Nikolay Nankov


1 Answers

As it turns out the kernel was wrong. Here's a kernel that does the job

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);
}
like image 83
Nikolay Nankov Avatar answered Nov 15 '22 06:11

Nikolay Nankov