Search code examples
image-processinggpuimagecore-imageimagefilteradaptive-threshold

How to achieve adaptive threshold filter with color


I'm looking for a algorithm similar adaptive thresholding, but that keeps color. I'm trying take an image like this:

original image

And make it look like this:

processed image

If it matters, I'm working in ios.


Solution

  • Here's a CIKernel that works well on your sample image

    kernel vec4 coreImageKernel (sampler i)
    {
        vec2 dc = destCoord();
    
        // center pixel color
        vec4 c = unpremultiply(sample(i, samplerTransform(i,dc+vec2(0.0,0.0))));
    
        // for a whiteboard, the max of a neighborhood is likely to be the color 
        // of the whiteboard
        vec4 cmax = c;
        cmax = max(unpremultiply(sample(i, samplerTransform(i,dc+vec2(10.0,0.0)))), cmax);
        cmax = max(unpremultiply(sample(i, samplerTransform(i,dc+vec2(-10.0,0.0)))), cmax);
        cmax = max(unpremultiply(sample(i, samplerTransform(i,dc+vec2(0.0,10.0)))), cmax);
        cmax = max(unpremultiply(sample(i, samplerTransform(i,dc+vec2(0.0,-10.0)))), cmax);
    
        // normalize the center color according to the whiteboard color
        vec4 r = c / cmax;
        return premultiply(r);
    }
    

    So how does this work? Well the first part of the kernel, the part that calculates cmax, is calculating the local color of the whiteboard. This is the tricky part. Basically it determines (approximately) the color that whiteboard would be if there where no markings on it. To do this the kernel makes three key assumptions:

    1. the whiteboard color does not locally vary much
    2. the markers subtract from whiteboard color and
    3. that for each pixel, it or a nearby pixel (10 pixels N,S,E or W) don't have any markings. In effect the kernel assumes is that marked lines are thinner than 10 pixels though that constant could be adjusted)

    Here's what the output of cmax looks like:

    enter image description here

    Once the local whiteboard color is approximated it is just a matter of dividing the current pixel by the local background. This is similar to how a color cast is removed from an image.

    This algorithm is similar to the Haze Removal example from the WWDC13 Core Image presentation. In that example a local min is subtracted to make blacker blacks. In this case a local max is divided to make whiter whites.

    Result: