iphone - pasar - ¿Es posible invertir los colores de una imagen mediante programación?
mi iphone cambio de color la pantalla (6)
Quiero tomar una imagen e invertir los colores en iOS.
Claro, es posible: una forma es usar el modo de mezcla "diferencia" ( kCGBlendModeDifference
). Vea esta pregunta (entre otras) para el esquema del código para configurar el procesamiento de la imagen. Use su imagen como la imagen inferior (base) y luego dibuje un mapa de bits en blanco puro sobre ella.
También puede realizar la operación por píxel manualmente obteniendo el CGImageRef
y dibujándolo en un contexto de mapa de bits, y luego haciendo un bucle sobre los píxeles en el contexto de mapa de bits.
Con 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
Creé una extensión rápida para hacer justamente esto. También porque la UIImage basada en CIImage se descompone (la mayoría de las bibliotecas suponen que CGImage está configurada) agregué una opción para devolver un UIImage que se basa en un CIImage modificado:
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)
}
}
La respuesta de Tommy es LA respuesta, pero me gustaría señalar que podría ser una tarea realmente intensa y lenta para imágenes más grandes. Hay dos marcos que podrían ayudarte en la manipulación de imágenes:
- CoreImage
- Acelerador
Y realmente vale la pena mencionar el increíble marco GPUImage de Brad Larson, GPUImage hace que las rutinas se ejecuten en la GPU utilizando un sombreador de fragmentos personalizado en el entorno OpenGlES 2.0, con una notable mejora de la velocidad. Con CoreImge, si hay un filtro negativo disponible, puede elegir la CPU o la GPU. Con Accelerator, todas las rutinas se ejecutan en la CPU pero con el procesamiento de imágenes vectoriales.
Para ampliar la respuesta de Quixoto y porque tengo un código fuente relevante de un proyecto propio, si tuviera que utilizar la manipulación de píxeles en la CPU, lo siguiente, al que he agregado la exposición, debería hacer el truco:
@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
Entonces eso agrega un método de categoría a UIImage que:
- crea un claro contexto de mapa de bits CoreGraphics que puede acceder a la memoria de
- atrae a la UIImage
- corre a través de cada píxel, convirtiendo de alfa premultiplicado a RGB sin inflexión, invirtiendo cada canal por separado, multiplicando por alfa de nuevo y almacenando de nuevo
- obtiene una imagen del contexto y la envuelve en un UIImage
- limpia después de sí mismo, y devuelve el UIImage
Actualización de Swift 3: (de @BadPirate Answer)
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)
}
}