metalcore-image

Metal CIKernel sampling generates garbage


I feel like I'm missing something really simple. I've got the simplest possible CIKernel, it looks like this:

extern "C" float4 Simple(coreimage::sampler s) {
    float2 current = s.coord();
    float2 anotherCoord = float2(current.x + 1.0, current.y);
    float4 sample = s.sample(anotherCoord);  // s.sample(current) works fine
    return sample;
}

It's (in my mind) incrementing the x position of the sampler by 1 and sampling the neighboring pixel. What I get in practice is a bunch of banded garbage (pictured below.) The sampler seems to be pretty much undocumented, so I have no idea whether I'm incrementing by the right amount to advance one pixel. The weird banding is still present if I clamp anootherCoord to s.extent(). Am I missing something really simple?

weird cikernel banding


Solution

  • The coordinates of coreimage:sampler are relative, between 0 and 1, where [0,0] is the lower left corner of the image and [1,1] is the upper right. So when you add 1.0 to that, you are effectively sampling outside the defined image space.

    Core Image provides access to the coreimage:destination to get absolute coordinates (pixels). Simply add a destination as the last parameter to your kernel function (no need to pass anything when you invoke the kernel with apply):

    extern "C" float4 Simple(coreimage::sampler s, coreimage::destination dest) {
        float2 current = dest.coord();
        float2 anotherCoord = current + float2(1.0, 0.0);
        float4 sample = s.sample(s.transform(anotherCoord));
        return sample;
    }
    

    dest.coord() gives you the coordinates in absolute (pixel) space, and s.transform translates it back into (relative) sampler space.