Est-ce que l'inversion par programmation des colors d'une image est possible?

Je veux prendre une image et inverser les colors dans iOS.

Pour développer la réponse de quixoto et parce que j'ai un code source pertinent d'un projet personnel, si vous deviez passer à la manipulation de pixels sur CPU, alors ce qui suit, auquel j'ai ajouté une exposition, devrait faire l'affaire:

@implementation UIImage (NegativeImage) - (UIImage *)negativeImage { // get width and height as integers, since we'll be using them as // array subscripts, etc, and this'll save a whole lot of casting CGSize size = self.size; int width = size.width; int height = size.height; // Create a suitable RGB+alpha bitmap context in BGRA colour space CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); unsigned char *memoryPool = (unsigned char *)calloc(width*height*4, 1); CGContextRef context = CGBitmapContextCreate(memoryPool, width, height, 8, width * 4, colourSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colourSpace); // draw the current image to the newly created context CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]); // run through every pixel, a scan line at a time... for(int y = 0; y < height; y++) { // get a pointer to the start of this scan line unsigned char *linePointer = &memoryPool[y * width * 4]; // step through the pixels one by one... for(int x = 0; x < width; x++) { // get RGB values. We're dealing with premultiplied alpha // here, so we need to divide by the alpha channel (if it // isn't zero, of course) to get uninflected RGB. We // multiply by 255 to keep precision while still using // integers int r, g, b; if(linePointer[3]) { r = linePointer[0] * 255 / linePointer[3]; g = linePointer[1] * 255 / linePointer[3]; b = linePointer[2] * 255 / linePointer[3]; } else r = g = b = 0; // perform the colour inversion r = 255 - r; g = 255 - g; b = 255 - b; // multiply by alpha again, divide by 255 to undo the // scaling before, store the new values and advance // the pointer we're reading pixel data from linePointer[0] = r * linePointer[3] / 255; linePointer[1] = g * linePointer[3] / 255; linePointer[2] = b * linePointer[3] / 255; linePointer += 4; } } // get a CG image from the context, wrap that into a // UIImage CGImageRef cgImage = CGBitmapContextCreateImage(context); UIImage *returnImage = [UIImage imageWithCGImage:cgImage]; // clean up CGImageRelease(cgImage); CGContextRelease(context); free(memoryPool); // and return return returnImage; } @end 

Donc, cela ajoute une méthode de catégorie à UIImage qui:

  1. crée un context bitmap CoreGraphics clair qu'il peut accéder à la memory de
  2. attire l'UIImage
  3. parcourt chaque pixel, convertissant l'alpha prémultiplié en RVB non réfléchi, inversant chaque canal séparément, multipliant par alpha à nouveau et stockant
  4. obtient une image du context et l'enveloppe dans un UIImage
  5. nettoie après lui-même, et renvoie l'UIImage

Avec CoreImage:

 #import <CoreImage/CoreImage.h> @implementation UIImage (ColorInverse) + (UIImage *)inverseColor:(UIImage *)image { CIImage *coreImage = [CIImage imageWithCGImage:image.CGImage]; CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; [filter setValue:coreImage forKey:kCIInputImageKey]; CIImage *result = [filter valueForKey:kCIOutputImageKey]; return [UIImage imageWithCIImage:result]; } @end 

Bien sûr, c'est possible – une façon d'utiliser le mode de fusion "différence" ( kCGBlendModeDifference ). Voir cette question (parmi d'autres) pour le contour du code pour mettre en place le image processing. Utilisez votre image comme image du bas (de base), puis dessinez un bitmap blanc pur dessus.

Vous pouvez également effectuer l'opération par pixel manuellement en récupérant CGImageRef et en l' CGImageRef dans un context bitmap, puis en bouclant les pixels dans le context bitmap.

La réponse de Tommy est LA réponse, mais je voudrais souligner que cela pourrait être une tâche vraiment intense et longue pour des images plus grandes. Voici deux frameworks qui pourraient vous aider à manipuler des images:

  1. CoreImage
  2. Accélérateur

    Et il vaut vraiment la peine de mentionner l'incroyable framework GPUImage de Brad Larson, GPUImage fait fonctionner les routines sur le GPU en utilisant un fragment shader personnalisé dans l'environnement OpenGlES 2.0, avec une amélioration remarquable de la vitesse. Avec CoreImge, si un filter négatif est disponible, vous pouvez choisir CPU ou GPU, en utilisant Accelerator, toutes les routines fonctionnent sur CPU mais en utilisant le image processing mathématique vectorielle.

Créé une extension rapide pour faire juste cela. De plus, parce que les UIImages basés sur CIImage se décomposent (la plupart des bibliothèques supposent que CGImage est défini), j'ai ajouté une option pour returnner un UIImage basé sur un CIImage modifié:

 extension UIImage { func inverseImage(cgResult: Bool) -> UIImage? { let coreImage = UIKit.CIImage(image: self) guard let filter = CIFilter(name: "CIColorInvert") else { return nil } filter.setValue(coreImage, forKey: kCIInputImageKey) guard let result = filter.valueForKey(kCIOutputImageKey) as? UIKit.CIImage else { return nil } if cgResult { // I've found that UIImage's that are based on CIImages don't work with a lot of calls properly return UIImage(CGImage: CIContext(options: nil).createCGImage(result, fromRect: result.extent)) } return UIImage(CIImage: result) } } 

Mise à jour de Swift 3: (à partir de la réponse @BadPirate)

 extension UIImage { func inverseImage(cgResult: Bool) -> UIImage? { let coreImage = UIKit.CIImage(image: self) guard let filter = CIFilter(name: "CIColorInvert") else { return nil } filter.setValue(coreImage, forKey: kCIInputImageKey) guard let result = filter.value(forKey: kCIOutputImageKey) as? UIKit.CIImage else { return nil } if cgResult { // I've found that UIImage's that are based on CIImages don't work with a lot of calls properly return UIImage(cgImage: CIContext(options: nil).createCGImage(result, from: result.extent)!) } return UIImage(ciImage: result) } }