Search code examples
ioscore-imagecifiltercikernel

How to use CoreImage to create a smooth threshold filter?


Are there existing filters that could be used as a smooth threshold? By this I mean that pixel values should be thresholded between two values, but the intermediate values are interpolated.

Basically, I'm trying to implement this:

https://www.filterforge.com/wiki/index.php/Thresholds#Interpolation_.28.22Blending.22.29


Solution

  • You'll need to write your own using Core Image Kernel Language and a CIColorKernel.

    I would use a smoothstep and pass in two edge values along with the luminance of the current pixel. The CIKL could look something like this:

    kernel vec4 color(__sample pixel, float inputEdgeO, float inputEdge1)
    {
        float luma = dot(pixel.rgb, vec3(0.2126, 0.7152, 0.0722));
        float threshold = smoothstep(inputEdgeO, inputEdge1, luma);
        return vec4(threshold, threshold, threshold, 1.0);
    }
    

    You can wrap this into a CIFilter with the following code:

    class SmoothThreshold: CIFilter
    {
        var inputImage : CIImage?
        var inputEdgeO: CGFloat = 0.25
        var inputEdge1: CGFloat = 0.75
    
        var colorKernel = CIColorKernel(string:
            "kernel vec4 color(__sample pixel, float inputEdgeO, float inputEdge1)" +
            "{" +
            "    float luma = dot(pixel.rgb, vec3(0.2126, 0.7152, 0.0722));" +
            "    float threshold = smoothstep(inputEdgeO, inputEdge1, luma);" +
            "    return vec4(threshold, threshold, threshold, 1.0);" +
            "}"
        )
    
        override var outputImage: CIImage!
        {
            guard let inputImage = inputImage,
                colorKernel = colorKernel else
            {
                return nil
            }
    
            let extent = inputImage.extent
            let arguments = [inputImage,
                             inputEdgeO,
                             inputEdge1]
    
            return colorKernel.applyWithExtent(extent,
                                               arguments: arguments)
        }
    }
    

    Note that you'll need to ensure inputEdge0 is less than inputEdge1, or the smoothstep will go a little crazy. You may want to saturate them too, to ensure they're between 0 and 1.

    Have a look at CIFilter.registerFilterName which will allow you to create an instance of SmoothThreshold with familiar syntax, although this will work fine:

    let image = CIImage(image: UIImage(named: "monalisa.jpg")!)!
    
    let filter = SmoothThreshold()
    filter.inputImage = image
    
    let final = filter.outputImage
    

    Cheers!

    Simon