What is the best Core Image filter to produce black and white effects?
Asked Answered
G

8

32

I am using Core Image and would like to produce a black and white effect on the chosen image.

Ideally I would like to have access to the same sort of options that are available on Photoshop i.e. Reds, Cyan, Greens, Blues and Magenta. The goal being to create different types of the black and white effect.

Does anyone know what filter would be best to manipulate these sort of options? If not does anyone know of a good approach to creating the black and white effect using other filters?

Thanks

Oliver

Gesture answered 5/4, 2012 at 14:34 Comment(0)
T
57
- (UIImage *)imageBlackAndWhite
{
    CIImage *beginImage = [CIImage imageWithCGImage:self.CGImage];

    CIImage *blackAndWhite = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, beginImage, @"inputBrightness", [NSNumber numberWithFloat:0.0], @"inputContrast", [NSNumber numberWithFloat:1.1], @"inputSaturation", [NSNumber numberWithFloat:0.0], nil].outputImage;
    CIImage *output = [CIFilter filterWithName:@"CIExposureAdjust" keysAndValues:kCIInputImageKey, blackAndWhite, @"inputEV", [NSNumber numberWithFloat:0.7], nil].outputImage; 

    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgiimage = [context createCGImage:output fromRect:output.extent];
    //UIImage *newImage = [UIImage imageWithCGImage:cgiimage];
    UIImage *newImage = [UIImage imageWithCGImage:cgiimage scale:image.scale orientation:image.imageOrientation];
    CGImageRelease(cgiimage);

    return newImage;
}

Upd.: For iOS6 there is CIColorMonochrome filter, but I played with it and found it not so good as mine.

Toyatoyama answered 5/4, 2012 at 18:13 Comment(2)
If anyone are interested. I used it here github.com/codler/Battery-Time-Remaining/commit/…Jephum
@Jephum After reading your comment about it not working on Yosemite, I kind of figured it wouldn't work on iOS 8, either. However, I just tested, and found that it works perfectly on iOS 8, still - even preserving layer transparencies. I did, however update the line: UIImage *newImage = [UIImage imageWithCGImage:cgiimage]; On mine to read: UIImage *newImage = [UIImage imageWithCGImage:cgiimage scale:image.scale orientation:image.imageOrientation]; So that I could preserve scale and orientation.Ratfink
P
14

here is example with CIColorMonochrome

- (UIImage *)imageBlackAndWhite
{
    CIImage *beginImage = [CIImage imageWithCGImage:self.CGImage];

    CIImage *output = [CIFilter filterWithName:@"CIColorMonochrome" keysAndValues:kCIInputImageKey, beginImage, @"inputIntensity", [NSNumber numberWithFloat:1.0], @"inputColor", [[CIColor alloc] initWithColor:[UIColor whiteColor]], nil].outputImage;

    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgiimage = [context createCGImage:output fromRect:output.extent];
    UIImage *newImage = [UIImage imageWithCGImage:cgiimage scale:self.scale orientation:self.imageOrientation];

    CGImageRelease(cgiimage);

    return newImage;
}
Pocketknife answered 3/12, 2012 at 17:30 Comment(1)
inputIntensity is 1 by default, no need to set it manually.Hapsburg
M
8

To create a pure monochrome effect, I’ve used CIColorMatrix with the R, G and B vector parameters all set to (0.2125, 0.7154, 0.0721, 0), and the alpha and bias vectors left with their defaults.

The values are RGB to greyscale conversion coefficients I looked up on the internets at some point. By changing these coefficients, you can change the contribution of the input channels. By scaling each copy of the vector, and optionally setting a bias vector, you can colourize the output.

Matchbox answered 5/4, 2012 at 15:11 Comment(1)
Anybody can check this post for how to use CIColorMatrix :#9473240Pantograph
M
8

Here is the top rated solution converted to Swift (iOS 7 and above):

func blackAndWhiteImage(image: UIImage) -> UIImage {
    let context = CIContext(options: nil)
    let ciImage = CoreImage.CIImage(image: image)!

    // Set image color to b/w
    let bwFilter = CIFilter(name: "CIColorControls")!
    bwFilter.setValuesForKeysWithDictionary([kCIInputImageKey:ciImage, kCIInputBrightnessKey:NSNumber(float: 0.0), kCIInputContrastKey:NSNumber(float: 1.1), kCIInputSaturationKey:NSNumber(float: 0.0)])
    let bwFilterOutput = (bwFilter.outputImage)!

    // Adjust exposure
    let exposureFilter = CIFilter(name: "CIExposureAdjust")!
    exposureFilter.setValuesForKeysWithDictionary([kCIInputImageKey:bwFilterOutput, kCIInputEVKey:NSNumber(float: 0.7)])
    let exposureFilterOutput = (exposureFilter.outputImage)!

    // Create UIImage from context
    let bwCGIImage = context.createCGImage(exposureFilterOutput, fromRect: ciImage.extent)
    let resultImage = UIImage(CGImage: bwCGIImage, scale: 1.0, orientation: image.imageOrientation)

    return resultImage
}
Mcmasters answered 15/12, 2015 at 10:24 Comment(0)
G
4

With regard to the answers suggesting to use CIColorMonochrome: There now are a few dedicated grayscale filters available from iOS7 (and OS X 10.9):

  • CIPhotoEffectTonal

    imitate black-and-white photography film without significantly altering contrast.

  • CIPhotoEffectNoir:

    imitate black-and-white photography film with exaggerated contrast

Source: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html

Gibrian answered 25/6, 2015 at 16:0 Comment(0)
U
3

Here is the most liked answer from @Shmidt written as an UIImage extension with a performance update in Swift:

import CoreImage

extension UIImage
{
    func imageBlackAndWhite() -> UIImage?
    {
        if let beginImage = CoreImage.CIImage(image: self)
        {
            let paramsColor: [String : AnyObject] = [kCIInputBrightnessKey: NSNumber(double: 0.0),
                                                     kCIInputContrastKey:   NSNumber(double: 1.1),
                                                     kCIInputSaturationKey: NSNumber(double: 0.0)]
            let blackAndWhite = beginImage.imageByApplyingFilter("CIColorControls", withInputParameters: paramsColor)

            let paramsExposure: [String : AnyObject] = [kCIInputEVKey: NSNumber(double: 0.7)]
            let output = blackAndWhite.imageByApplyingFilter("CIExposureAdjust", withInputParameters: paramsExposure)

            let processedCGImage = CIContext().createCGImage(output, fromRect: output.extent)
            return UIImage(CGImage: processedCGImage, scale: self.scale, orientation: self.imageOrientation)
        }
        return nil
    }
}
Unparliamentary answered 10/6, 2016 at 9:11 Comment(0)
S
3

macOS (NSImage) Swift 3 version of @FBente's conversion of @Shmidt's answer:

extension NSImage
{
    func imageBlackAndWhite() -> NSImage?
    {
        if let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil)
        {
            let beginImage = CIImage.init(cgImage: cgImage)
            let paramsColor: [String : AnyObject] = [kCIInputBrightnessKey: NSNumber(value: 0.0),
                                                     kCIInputContrastKey:   NSNumber(value: 1.1),
                                                     kCIInputSaturationKey: NSNumber(value: 0.0)]
            let blackAndWhite = beginImage.applyingFilter("CIColorControls", withInputParameters: paramsColor)

            let paramsExposure: [String : AnyObject] = [kCIInputEVKey: NSNumber(value: 0.7)]
            let output = blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure)

            if let processedCGImage = CIContext().createCGImage(output, from: output.extent) {
                return NSImage(cgImage: processedCGImage, size: self.size)
            }
        }
        return nil
    }
}
Spanos answered 13/12, 2016 at 5:19 Comment(0)
P
-1

I have tried Shmidt solution but it appears to me too over exposed on iPad Pro. I am using just the first part of his solution, without the exposure filter:

- (UIImage *)imageBlackAndWhite
{
    CIImage *beginImage = [CIImage imageWithCGImage:self.CGImage];

    CIImage *blackAndWhite = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, beginImage, @"inputBrightness", [NSNumber numberWithFloat:0.0], @"inputContrast", [NSNumber numberWithFloat:1.1], @"inputSaturation", [NSNumber numberWithFloat:0.0], nil].outputImage;
    CIImage *output = [blackAndWhite valueForKey:@"outputImate"]; 

    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgiimage = [context createCGImage:output fromRect:output.extent];
    //UIImage *newImage = [UIImage imageWithCGImage:cgiimage];
    UIImage *newImage = [UIImage imageWithCGImage:cgiimage scale:image.scale orientation:image.imageOrientation];
    CGImageRelease(cgiimage);

    return newImage;
}
Paralyze answered 7/2, 2017 at 11:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.