Custom Core Image filter help

  • Hello everyone, I'm a complete newbie when it comes to Core Image, so
    I'm hoping some experienced people might be able to help...

    I'm thinking about writing an image processing operation as a core
    image filter, but I'm just not sure if it's actually possible - and
    hopefully someone here can tell me if it is, or not.

    I'm trying to compute discrete orthogonal image moments, using
    Tchebichef polynomials, and essentially, the output image is the same
    size as the input image, and each output pixel, T(m,n) is defined
    mathematically as:

    T(m,n) = SUM_x { SUM_y {  t(m, x) * t(n,y) * f(x,y) }}

    where, m and n are output coordinates, x and y iterate over the input
    image, f.

    So, in other words, each output pixel is the sum over all the input
    pixels, of the product, of each input pixel and the values of two
    Tchebichef polynomials. These polynomials can be pre-computed, and are
    essentially just another image, so t(m,x) means the mth order
    polynomial at position x.

    I'm not sure if this can be done in Core Image because the kernel will
    need to loop over the entire input image domain... and to use the for
    control statement, "the loop condition must be inferred at the time
    the code compiles" - which leads me to think that such a kernel will
    not work for different sized images?

    Any thoughts you all have are much appreciated!

    Cheers
    Dan
  • On 18 Feb 2008, at 12:42, Daniel Thorpe wrote:

    > Hello everyone, I'm a complete newbie when it comes to Core Image,
    > so I'm hoping some experienced people might be able to help...

    We can but try :-)

    > I'm trying to compute discrete orthogonal image moments, using
    > Tchebichef polynomials,
    ...
    > T(m,n) = SUM_x { SUM_y {  t(m, x) * t(n,y) * f(x,y) }}

    ...
    > I'm not sure if this can be done in Core Image because the kernel
    > will need to loop over the entire input image domain... and to use
    > the for control statement, "the loop condition must be inferred at
    > the time the code compiles" - which leads me to think that such a
    > kernel will not work for different sized images?

    The big pain here is that Apple don't support the OpenGL
    matrixCompMult function in their dialect of the shading language.
    That said, observe that CIKernel objects get initialised with a
    string, which you can construct on the fly, so needing to set the size
    at 'compile' time only means at the time that the kernel object is
    created.

    Apple do support the 'dot'.  I would therefore suggest that you re-
    write your equation like this:

    T(m,n) = SUM_y { t(n,y) * (SUM_x {  t(m, x) * f(x,y) } ) }

    You can then construct an 'image' representing the t function, with
    the pixel at x,y representing t(y,x).  This allows you to compute the
    inner sum by selecting row m from the image and taking its dot-product
    with each row of the input image.  You can then use a for loop to sum
    up those sums scaled by the values taken from row n.

    I hope this helps.

    Nicko
  • Hey Nicko, thanks for getting back to me...

    I think I've got around the problem of having a kernel for different
    sized images, although it is a bit of a hack. I've written a script
    that generates a .cikernel file containing as many kernel functions I
    want with the correct width set as a local variable in each one. Then
    when I load the kernels in the CIFilter subclass, I store an array of
    all the kernels, and select the correct one in the outputImage method.
    This seems to work, or at least it compiles and runs without any errors.

    I am having some problems with my kernel code however, which I've
    changed as you suggested (I had already done this in my Obj-C
    implementation)...

    This is my kernel code:

    kernel vec4 ComputeTchebichefMomentsForN_%03.lf(sampler src, sampler
    tchebichef) {

    const float N = %.1lf;
    float x, y;
    vec4 ans = (0.0, 0.0, 0.0, 1.0);

    for(y=0.0; y<N; y++) {

      vec2 ny;
      ny.x = y;
      ny.y = destCoord().x;
      vec4 tmp = (0.0, 0.0, 0.0, 1.0);

      for(x=0.0; x<N; x++) {

      vec2 mx;
      mx.x = x;
      mx.y = destCoord().y;

      vec2 xy;
      xy.x = x;
      xy.x = y;

      tmp += ( sampleWorking(tchebichef, mx) * sampleWorking(src, xy) );
      }

      ans += ( sampleWorking(tchebichef, ny) * tmp );

    }

    return ans;
    }

    and

    vec4 sampleWorking(sampler src, vec2 pos) {
    return sample(src, samplerTransform(src, pos));
    }

    Unfortunately, this doesn't seem to work yet, as it crashed
    QuartzComposer instantly (as in as soon as I type in the kernel stuff
    with a suitable N. Which leads me to think, that it's syntactically
    correct, but essentially wrong....

    So... have you got any other thoughts?

    Thanks a lot for your help thus far - it's much appreciated!

    Cheers
    Dan

    On 20 Feb 2008, at 10:12, Nicko van Someren wrote:

    > On 18 Feb 2008, at 12:42, Daniel Thorpe wrote:
    >
    >> Hello everyone, I'm a complete newbie when it comes to Core Image,
    >> so I'm hoping some experienced people might be able to help...
    >
    > We can but try :-)
    >
    >> I'm trying to compute discrete orthogonal image moments, using
    >> Tchebichef polynomials,
    > ...
    >> T(m,n) = SUM_x { SUM_y {  t(m, x) * t(n,y) * f(x,y) }}
    >
    > ...
    >> I'm not sure if this can be done in Core Image because the kernel
    >> will need to loop over the entire input image domain... and to use
    >> the for control statement, "the loop condition must be inferred at
    >> the time the code compiles" - which leads me to think that such a
    >> kernel will not work for different sized images?
    >
    > The big pain here is that Apple don't support the OpenGL
    > matrixCompMult function in their dialect of the shading language.
    > That said, observe that CIKernel objects get initialised with a
    > string, which you can construct on the fly, so needing to set the
    > size at 'compile' time only means at the time that the kernel object
    > is created.
    >
    > Apple do support the 'dot'.  I would therefore suggest that you re-
    > write your equation like this:
    >
    > T(m,n) = SUM_y { t(n,y) * (SUM_x {  t(m, x) * f(x,y) } ) }
    >
    > You can then construct an 'image' representing the t function, with
    > the pixel at x,y representing t(y,x).  This allows you to compute
    > the inner sum by selecting row m from the image and taking its dot-
    > product with each row of the input image.  You can then use a for
    > loop to sum up those sums scaled by the values taken from row n.
    >
    > I hope this helps.
    >
    > Nicko
    >
  • On 20 Feb 2008, at 18:07, Daniel Thorpe wrote:

    > Hey Nicko, thanks for getting back to me...

    No problem!

    > I am having some problems with my kernel code however, which I've
    > changed as you suggested (I had already done this in my Obj-C
    > implementation)...
    >
    > This is my kernel code:
    ...
    > const float N = %.1lf;

    I'd be inclined to make this an integer, since it will be easier for
    the compiler to deal optimise it out.

    > for(x=0.0; x<N; x++) {
    ...
    > }

    You should be able to replace this with a call to dot(), if you set
    your Tchebichef image up correctly.

    > Unfortunately, this doesn't seem to work yet, as it crashed
    > QuartzComposer instantly (as in as soon as I type in the kernel
    > stuff with a suitable N. Which leads me to think, that it's
    > syntactically correct, but essentially wrong....

    That's odd, though I've managed to write kernel code that would crash
    my machine in the (distant) past.

    > So... have you got any other thoughts?

    Making 'N' an integer might help the compiler.  Other than that I
    don't have any ideas just now :-(

    Cheers,
      Nicko