web-dev-qa-db-fra.com

Appliquer un filtre noir et blanc à UIImage

J'ai besoin d'appliquer un filtre noir et blanc sur un UIImage. J'ai une vue dans laquelle il y a une photo prise par l'utilisateur, mais je n'ai aucune idée sur la transformation des couleurs de l'image.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.title = NSLocalizedString(@"#Paint!", nil);
    imageView.image = image;
}

Comment puis je faire ça?

19
user3422862

Objectif C

- (UIImage *)convertImageToGrayScale:(UIImage *)image {


    // Create image rectangle with current image width/height
    CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);

    // Grayscale color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();

    // Create bitmap content with current image size and grayscale colorspace
    CGContextRef context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaNone);

    // Draw image into current context, with specified rectangle
    // using previously defined context (with grayscale colorspace)
    CGContextDrawImage(context, imageRect, [image CGImage]);

    // Create bitmap image info from pixel data in current context
    CGImageRef imageRef = CGBitmapContextCreateImage(context);

    // Create a new UIImage object
    UIImage *newImage = [UIImage imageWithCGImage:imageRef];

    // Release colorspace, context and bitmap information
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);
    CFRelease(imageRef);

    // Return the new grayscale image
    return newImage; 
}

rapide

func convertToGrayScale(image: UIImage) -> UIImage {

    // Create image rectangle with current image width/height
    let imageRect:CGRect = CGRect(x:0, y:0, width:image.size.width, height: image.size.height)

    // Grayscale color space
    let colorSpace = CGColorSpaceCreateDeviceGray()
    let width = image.size.width
    let height = image.size.height

    // Create bitmap content with current image size and grayscale colorspace
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)

    // Draw image into current context, with specified rectangle
    // using previously defined context (with grayscale colorspace)
    let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
    context?.draw(image.cgImage!, in: imageRect)
    let imageRef = context!.makeImage()

    // Create a new UIImage object
    let newImage = UIImage(cgImage: imageRef!)

    return newImage
}
27
MCMatan

À en juger par la balise ciimage , peut-être que l'OP pensait (correctement) que Core Image fournirait un moyen rapide et facile de le faire?

Voici cela, à la fois dans ObjC:

- (UIImage *)grayscaleImage:(UIImage *)image {
    CIImage *ciImage = [[CIImage alloc] initWithImage:image];
    CIImage *grayscale = [ciImage imageByApplyingFilter:@"CIColorControls"
        withInputParameters: @{kCIInputSaturationKey : @0.0}];
    return [UIImage imageWithCIImage:grayscale];
}

et Swift:

func grayscaleImage(image: UIImage) -> UIImage {
    let ciImage = CIImage(image: image)
    let grayscale = ciImage.imageByApplyingFilter("CIColorControls",
        withInputParameters: [ kCIInputSaturationKey: 0.0 ])
    return UIImage(CIImage: grayscale)
}

CIColorControls n'est qu'un des nombreux filtres Core Image intégrés qui peuvent convertir une image en niveaux de gris. CIPhotoEffectMono , CIPhotoEffectNoir , et CIPhotoEffectTonal sont différents préréglages de mappage de tonalité (chacun ne prend aucun paramètre), et vous pouvez faire votre propre mappage de tonalité avec des filtres comme CIColorMap .

Contrairement aux alternatives qui impliquent de créer et de dessiner dans son propre CGBitmapContext, celles-ci préservent la taille/échelle et l'alpha de l'image d'origine sans travail supplémentaire.

25
rickster

Alors que la solution de PiratM fonctionne, vous perdez le canal alpha. Pour conserver le canal alpha, vous devez effectuer quelques étapes supplémentaires.

+(UIImage *)convertImageToGrayScale:(UIImage *)image {
    // Create image rectangle with current image width/height
    CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);

    // Grayscale color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();

    // Create bitmap content with current image size and grayscale colorspace
    CGContextRef context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaNone);

    // Draw image into current context, with specified rectangle
    // using previously defined context (with grayscale colorspace)
    CGContextDrawImage(context, imageRect, [image CGImage]);

    // Create bitmap image info from pixel data in current context
    CGImageRef imageRef = CGBitmapContextCreateImage(context);

    // Release colorspace, context and bitmap information
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);

    context = CGBitmapContextCreate(nil,image.size.width, image.size.height, 8, 0, nil, kCGImageAlphaOnly );
    CGContextDrawImage(context, imageRect, [image CGImage]);
    CGImageRef mask = CGBitmapContextCreateImage(context);

    // Create a new UIImage object
    UIImage *newImage = [UIImage imageWithCGImage:CGImageCreateWithMask(imageRef, mask)];
    CGImageRelease(imageRef);
    CGImageRelease(mask);

    // Return the new grayscale image
    return newImage;
}
19
Chris Stillwell

La version de @rickster semble bonne compte tenu du canal alpha. Mais un UIImageView sans .AspectFit ou Fill contentMode ne peut pas l'afficher. Par conséquent, l'UIImage doit être créé avec un CGImage. Cette version implémentée comme Swift UIImage extension conserve l'échelle actuelle et donne quelques paramètres d'entrée facultatifs:

import CoreImage

extension UIImage
{
    /// Applies grayscale with CIColorControls by settings saturation to 0.0.
    /// - Parameter brightness: Default is 0.0.
    /// - Parameter contrast: Default is 1.0.
    /// - Returns: The grayscale image of self if available.
    func grayscaleImage(brightness: Double = 0.0, contrast: Double = 1.0) -> UIImage?
    {
        if let ciImage = CoreImage.CIImage(image: self, options: nil)
        {
            let paramsColor: [String : AnyObject] = [ kCIInputBrightnessKey: NSNumber(double: brightness),
                                                      kCIInputContrastKey:   NSNumber(double: contrast),
                                                      kCIInputSaturationKey: NSNumber(double: 0.0) ]
            let grayscale = ciImage.imageByApplyingFilter("CIColorControls", withInputParameters: paramsColor)

            let processedCGImage = CIContext().createCGImage(grayscale, fromRect: grayscale.extent)
            return UIImage(CGImage: processedCGImage, scale: self.scale, orientation: self.imageOrientation)
        }
        return nil
    }
}

Le moyen le plus long mais le plus rapide est la version modifiée de la réponse @ChrisStillwells. Implémenté comme une extension UIImage compte tenu du canal alpha et de l'échelle actuelle dans Swift:

extension UIImage
{
    /// Create a grayscale image with alpha channel. Is 5 times faster than grayscaleImage().
    /// - Returns: The grayscale image of self if available.
    func convertToGrayScale() -> UIImage?
    {
        // Create image rectangle with current image width/height * scale
        let pixelSize = CGSize(width: self.size.width * self.scale, height: self.size.height * self.scale)
        let imageRect = CGRect(Origin: CGPointZero, size: pixelSize)
        // Grayscale color space
        if let colorSpace: CGColorSpaceRef = CGColorSpaceCreateDeviceGray()
        {
            // Create bitmap content with current image size and grayscale colorspace
            let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.None.rawValue)
            if let context: CGContextRef = CGBitmapContextCreate(nil, Int(pixelSize.width), Int(pixelSize.height), 8, 0, colorSpace, bitmapInfo.rawValue)
            {
                // Draw image into current context, with specified rectangle
                // using previously defined context (with grayscale colorspace)
                CGContextDrawImage(context, imageRect, self.CGImage)
                // Create bitmap image info from pixel data in current context
                if let imageRef: CGImageRef = CGBitmapContextCreateImage(context)
                {
                    let bitmapInfoAlphaOnly = CGBitmapInfo(rawValue: CGImageAlphaInfo.Only.rawValue)
                    if let contextAlpha = CGBitmapContextCreate(nil, Int(pixelSize.width), Int(pixelSize.height), 8, 0, nil, bitmapInfoAlphaOnly.rawValue)
                    {
                        CGContextDrawImage(contextAlpha, imageRect, self.CGImage)
                        if let mask: CGImageRef = CGBitmapContextCreateImage(contextAlpha)
                        {
                            // Create a new UIImage object
                            if let newCGImage = CGImageCreateWithMask(imageRef, mask)
                            {
                                // Return the new grayscale image
                                return UIImage(CGImage: newCGImage, scale: self.scale, orientation: self.imageOrientation)
                            }
                        }
                    }
                }
            }

        }
        // A required variable was unexpected nil
        return nil
    }
}
6
FBente

Version Swift 3.0:

extension UIImage {
    func convertedToGrayImage() -> UIImage? {
        let width = self.size.width
        let height = self.size.height
        let rect = CGRect(x: 0.0, y: 0.0, width: width, height: height)
        let colorSpace = CGColorSpaceCreateDeviceGray()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)

        guard let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else {
            return nil
        }
        guard let cgImage = cgImage else { return nil }

        context.draw(cgImage, in: rect)
        guard let imageRef = context.makeImage() else { return nil }
        let newImage = UIImage(cgImage: imageRef.copy()!)

        return newImage
    }
}
1
Anson Yao

Swift3 + GPUImage

import GPUImage

extension UIImage {
    func blackWhite() -> UIImage? {
        guard let image: GPUImagePicture = GPUImagePicture(image: self) else {
            print("unable to create GPUImagePicture")
            return nil
        }
        let filter = GPUImageAverageLuminanceThresholdFilter()
        image.addTarget(filter)
        filter.useNextFrameForImageCapture()
        image.processImage()
        guard let processedImage: UIImage = filter.imageFromCurrentFramebuffer(with: UIImageOrientation.up) else {
            print("unable to obtain UIImage from filter")
            return nil
        }
        return processedImage
    }
}
1
neoneye

Solution Swift 4

extension UIImage {            
    var withGrayscale: UIImage {    
        guard let ciImage = CIImage(image: self, options: nil) else { return self }    
        let paramsColor: [String: AnyObject] = [kCIInputBrightnessKey: NSNumber(value: 0.0), kCIInputContrastKey: NSNumber(value: 1.0), kCIInputSaturationKey: NSNumber(value: 0.0)]
        let grayscale = ciImage.applyingFilter("CIColorControls", parameters: paramsColor)    
        guard let processedCGImage = CIContext().createCGImage(grayscale, from: grayscale.extent) else { return self }
        return UIImage(cgImage: processedCGImage, scale: scale, orientation: imageOrientation)
    }
}
1
Charlton Provatas

Ce code (objectif C) fonctionne:

CIImage * ciimage = ...;
CIFilter * filter = [CIFilter filterWithName:@"CIColorControls" withInputParameters:@{kCIInputSaturationKey : @0.0,kCIInputContrastKey : @10.0,kCIInputImageKey : ciimage}];
CIImage * grayscale  = [filtre outputImage];

Le kCIInputContrastKey : @10.0 est d'obtenir une image presque en noir et blanc.

0
Jo Essfb

dans Swift 3.0

  func convertImageToGrayScale(image: UIImage) -> UIImage {
    // Create image rectangle with current image width/height
    let imageRect = CGRect(x: 0, y: 0,width: image.size.width, height : image.size.height)
    // Grayscale color space
    let colorSpace = CGColorSpaceCreateDeviceGray()
    // Create bitmap content with current image size and grayscale colorspace
    let context = CGContext(data: nil, width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue)
    // Draw image into current context, with specified rectangle
    // using previously defined context (with grayscale colorspace)
    context?.draw(image.cgImage!, in: imageRect)

    // Create bitmap image info from pixel data in current context
    let imageRef = context!.makeImage()
    // Create a new UIImage object
    let newImage = UIImage(cgImage: imageRef!)
    // Release colorspace, context and bitmap information
//MARK: ToolBar Button Methods

    // Return the new grayscale image
    return newImage
}
0
pradip kikani

Voici la version Swift 1.2

/// convert background image to gray scale
///
/// param: flag if true, image will be rendered in grays scale
func convertBackgroundColorToGrayScale(flag: Bool) {
    if flag == true {
        let imageRect = self.myImage.frame

        let colorSpace = CGColorSpaceCreateDeviceGray()
        let width = imageRect.width
        let height = imageRect.height

        let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.None.rawValue)
        var context = CGBitmapContextCreate(nil, Int(width), Int(height), 8, 0, colorSpace, bitmapInfo)
        let image = self.musicBackgroundColor.image!.CGImage

        CGContextDrawImage(context, imageRect, image)
        let imageRef = CGBitmapContextCreateImage(context)

        let newImage = UIImage(CGImage: CGImageCreateCopy(imageRef))

        self.myImage.image = newImage
    } else {
        // do something else
    }
}
0
Miralem Cebic

In Swift 5 , utilisant CoreImage pour faire un filtre d'image,

merci @rickster

extension UIImage{
    var grayscaled: UIImage?{
        let ciImage = CIImage(image: self)
        let grayscale = ciImage?.applyingFilter("CIColorControls",
                                                parameters: [ kCIInputSaturationKey: 0.0 ])
        if let gray = grayscale{
            return UIImage(ciImage: gray)
        }
        else{
            return nil
        }
    }
}
0
dengST30