web-dev-qa-db-fra.com

Convertissez UIImage en niveaux de gris en conservant la qualité d'image

J'ai cette extension (trouvée dans obj-c et je l'ai convertie en Swift3) pour obtenir la même UIImage mais en niveaux de gris:

public func getGrayScale() -> UIImage
{
    let imgRect = CGRect(x: 0, y: 0, width: width, height: height)

    let colorSpace = CGColorSpaceCreateDeviceGray()

    let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).rawValue)
    context?.draw(self.cgImage!, in: imgRect)

    let imageRef = context!.makeImage()
    let newImg = UIImage(cgImage: imageRef!)

    return newImg
}

Je peux voir l’image grise mais sa qualité est plutôt mauvaise ... La seule chose que je peux voir qui est liée à la qualité est bitsPerComponent: 8 dans le contexte du constructeur. Cependant, en regardant la documentation d'Apple, voici ce que je comprends:

 enter image description here

Cela montre qu'iOS ne supporte que 8bpc ... Ainsi, pourquoi ne puis-je pas améliorer la qualité?

13
Dliix

Essayez ci-dessous le code: 

Remarque: le code a été mis à jour et l'erreur a été corrigée ...

  • Code testé dans Swift 3.
  • originalImage est l'image que vous essayez de convertir.

Réponse 1:

     var context = CIContext(options: nil)

Update:CIContext est le composant Image principale qui gère rendering et tout le traitement d'une image principale s'effectue dans une CIContext. Ceci est un peu similaire à Core Graphics ou OpenGL context. Pour plus d'informations disponibles dans Apple Doc.

     func Noir() {

        let currentFilter = CIFilter(name: "CIPhotoEffectNoir") 
        currentFilter!.setValue(CIImage(image: originalImage.image!), forKey: kCIInputImageKey)
        let output = currentFilter!.outputImage 
        let cgimg = context.createCGImage(output!,from: output!.extent)
        let processedImage = UIImage(cgImage: cgimg!)
        originalImage.image = processedImage
      }

Vous devez aussi considérer le filtre suivant qui peut produire un effet similaire

  • CIPhotoEffectMono
  • CIPhotoEffectTonal

Résultat de la réponse 1:

 enter image description here

Résultat de la réponse 2:

 enter image description here

Réponse améliorée:

Réponse 2: Réglage automatique de l'image d'entrée avant l'application du filtre coreImage

var context = CIContext(options: nil)

func Noir() {


    //Auto Adjustment to Input Image
    var inputImage = CIImage(image: originalImage.image!)
    let options:[String : AnyObject] = [CIDetectorImageOrientation:1 as AnyObject]
    let filters = inputImage!.autoAdjustmentFilters(options: options)

    for filter: CIFilter in filters {
       filter.setValue(inputImage, forKey: kCIInputImageKey)
   inputImage =  filter.outputImage
      }
    let cgImage = context.createCGImage(inputImage!, from: inputImage!.extent)
    self.originalImage.image =  UIImage(cgImage: cgImage!)

    //Apply noir Filter
    let currentFilter = CIFilter(name: "CIPhotoEffectTonal") 
    currentFilter!.setValue(CIImage(image: UIImage(cgImage: cgImage!)), forKey: kCIInputImageKey)

    let output = currentFilter!.outputImage 
    let cgimg = context.createCGImage(output!, from: output!.extent)
    let processedImage = UIImage(cgImage: cgimg!)
    originalImage.image = processedImage

}

Note: Si vous voulez voir le meilleur résultat. Vous devriez tester votre code sur real device et non dans la simulator...

28
Joe

A Swift 4.0 extension qui renvoie une variable optionnelle UIImage afin d’éviter les collisions éventuelles.

import UIKit

extension UIImage {
    var noir: UIImage? {
        let context = CIContext(options: nil)
        guard let currentFilter = CIFilter(name: "CIPhotoEffectNoir") else { return nil }
        currentFilter.setValue(CIImage(image: self), forKey: kCIInputImageKey)
        if let output = currentFilter.outputImage,
            let cgImage = context.createCGImage(output, from: output.extent) {
            return UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
        }
        return nil
    }
}

Pour utiliser ceci:

let image = UIImage(...)
let noirImage = image.noir // noirImage is an optional UIImage (UIImage?)
13
CodeBender

La réponse de Joe sous la forme d'une extension UIImage pour Swift 4 fonctionnant correctement pour différentes échelles:

extension UIImage {
    var noir: UIImage {
        let context = CIContext(options: nil)
        let currentFilter = CIFilter(name: "CIPhotoEffectNoir")!
        currentFilter.setValue(CIImage(image: self), forKey: kCIInputImageKey)
        let output = currentFilter.outputImage!
        let cgImage = context.createCGImage(output, from: output.extent)!
        let processedImage = UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)

        return processedImage
    }
}
9
Peter Prokop

Voici une catégorie de l’objectif C. Notez que cette version prend en compte l’échelle.

- (UIImage *)grayscaleImage{
    return [self imageWithCIFilter:@"CIPhotoEffectMono"];
}

- (UIImage *)imageWithCIFilter:(NSString*)filterName{
    CIImage *unfiltered = [CIImage imageWithCGImage:self.CGImage];
    CIFilter *filter = [CIFilter filterWithName:filterName];
    [filter setValue:unfiltered forKey:kCIInputImageKey];
    CIImage *filtered = [filter outputImage];
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef cgimage = [context createCGImage:filtered fromRect:CGRectMake(0, 0, self.size.width*self.scale, self.size.height*self.scale)];
    // Do not use initWithCIImage because that renders the filter each time the image is displayed.  This causes slow scrolling in tableviews.
    UIImage *image = [[UIImage alloc] initWithCGImage:cgimage scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(cgimage);
    return image;
}
4
arsenius

Je voudrais utiliser CoreImage, qui peut garder la qualité.

func convertImageToBW(image:UIImage) -> UIImage {

    let filter = CIFilter(name: "CIPhotoEffectMono")

    // convert UIImage to CIImage and set as input

    let ciInput = CIImage(image: image)
    filter?.setValue(ciInput, forKey: "inputImage")

    // get output CIImage, render as CGImage first to retain proper UIImage scale

    let ciOutput = filter?.outputImage
    let ciContext = CIContext()
    let cgImage = ciContext.createCGImage(ciOutput!, from: (ciOutput?.extent)!)

    return UIImage(cgImage: cgImage!)
}

Selon la manière dont vous utilisez ce code, vous pouvez créer le CIContext en dehors de celui-ci pour des raisons de performances.

3
dfd

enregistrer l'orientation et l'échelle actuelles: 

extension UIImage {

func noir() -> UIImage {
    let context = CIContext(options: nil)

    let currentFilter = CIFilter(name: "CIPhotoEffectNoir")
    currentFilter!.setValue(CIImage(image: self), forKey: kCIInputImageKey)
    let output = currentFilter!.outputImage
    let cgimg = context.createCGImage(output!, from: output!.extent)
    let processedImage = UIImage(cgImage: cgimg!, scale: scale, orientation: imageOrientation)
    return processedImage
}}
1
RomanV

Selon Joe answer, nous avons facilement converti Original en N & B. Mais retour à l'original image fait référence à ce code:

var context = CIContext(options: nil)
var startingImage : UIImage = UIImage()

func Noir() {     
    startingImage = imgView.image!
    var inputImage = CIImage(image: imgView.image!)!
    let options:[String : AnyObject] = [CIDetectorImageOrientation:1 as AnyObject]
    let filters = inputImage.autoAdjustmentFilters(options: options)

    for filter: CIFilter in filters {
        filter.setValue(inputImage, forKey: kCIInputImageKey)
        inputImage =  filter.outputImage!
    }
    let cgImage = context.createCGImage(inputImage, from: inputImage.extent)
    self.imgView.image =  UIImage(cgImage: cgImage!)

    //Filter Logic
    let currentFilter = CIFilter(name: "CIPhotoEffectNoir")
    currentFilter!.setValue(CIImage(image: UIImage(cgImage: cgImage!)), forKey: kCIInputImageKey)

    let output = currentFilter!.outputImage
    let cgimg = context.createCGImage(output!, from: output!.extent)
    let processedImage = UIImage(cgImage: cgimg!)
    imgView.image = processedImage
}

func Original(){ 
    imgView.image = startingImage
}
0
SPooja