'Binarize Picture with Core Image on iOS

I was wondering if it is possible to binarize an image (convert to black and white only) with Core Image?

I made it with OpenCV and GPUImage, but would prefer it to use Apple Core Image, if that's possible



Solution 1:[1]

You can use MetalPerformanceShaders for that. And the CIImageProcessingKernel. https://developer.apple.com/documentation/coreimage/ciimageprocessorkernel

Here is the code of the class needed.

 class ThresholdImageProcessorKernel: CIImageProcessorKernel {
    static let device = MTLCreateSystemDefaultDevice()
    override class func process(with inputs: [CIImageProcessorInput]?, arguments: [String : Any]?, output: CIImageProcessorOutput) throws {
        guard
            let device = device,
            let commandBuffer = output.metalCommandBuffer,
            let input = inputs?.first,
            let sourceTexture = input.metalTexture,
            let destinationTexture = output.metalTexture,
            let thresholdValue = arguments?["thresholdValue"] as? Float else  {
                return
        }

        let threshold = MPSImageThresholdBinary(
            device: device,
            thresholdValue: thresholdValue,
            maximumValue: 1.0,
            linearGrayColorTransform: nil)

        threshold.encode(
            commandBuffer: commandBuffer,
            sourceTexture: sourceTexture,
            destinationTexture: destinationTexture)
    }
}

And this is how you can use it:

    let context = CIContext(options: nil)

            if let binaryCIImage = try? ThresholdImageProcessorKernel.apply(
                withExtent: croppedCIImage.extent,
                inputs: [croppedCIImage],
                arguments: ["thresholdValue": Float(0.2)]) {
                if let cgImage = context.createCGImage(binaryCIImage, from: binary.extent) {
                    DispatchQueue.main.async {
                        let resultingImage = UIImage(cgImage: cgImage)
                        if resultingImage.size.width > 100 {
                            print("Received an image \(resultingImage.size)")
                        }
                    }
                }
            }

Solution 2:[2]

I have had success by converting it to greyscale using CIPhotoEffectMono or equivalent, and then using CIColorControls with a ridiculously high inputContrast number (I used 10000). This effectively makes it black and white and thus binarized. Useful for those who don't want to mess with custom kernels.

Also, you can use an example like Apple's "Chroma Key" filter which uses Hue to filter, but instead of looking at Hue you just give the rules for binarizing the data (ie: when to set RGB all to 1.0 and when to set to 0.0).

https://developer.apple.com/documentation/coreimage/applying_a_chroma_key_effect

Solution 3:[3]

let outputImage = inputImage.applyingFilter("CIColorMonochrome",
                            parameters: [kCIInputColorKey: CIColor.white])

In you want to play with every out of 250 CIFilters please check this app out: https://apps.apple.com/us/app/filter-magic/id1594986951

Solution 4:[4]

Found this thread from a Google search, and thought I'd mention that as of iOS 14 and OSX 11.0, CoreImage includes CIColorThreshold and CIColorThresholdOtsu filters (the latter using Otsu's method to calculate the threshold value from the image histogram)

See:

https://cifilter.io/CIColorThreshold/

https://cifilter.io/CIColorThresholdOtsu/

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Naloiko Eugene
Solution 2 Tom Wilson
Solution 3 Vadim Dagman
Solution 4 Andrew Chinery