Tengo un UIImage y quiero cambiar su saturación alrededor de + 10%. ¿Hay métodos o funciones estándar que puedan usarse para esto?¿Cómo puedo cambiar la saturación de un UIImage?
Respuesta
Nada tan sencillo. La solución más fácil es, probablemente, crear un CGContext en memoria con un formato de píxel conocido, dibujar la imagen en ese contexto y luego leer/modificar los píxeles en el búfer de formato conocido.
No creo que CG admita un espacio de color con un canal de saturación separado, por lo que deberá convertir de RGB a HSV o HSL, o hacer los cálculos directamente en el espacio RGB.
Una forma de hacer el cálculo directamente en RGB podría ser algo como esto:
average = (R + G + B)/3;
red_delta = (R - average) * 11/10;
green_delta = (G - average) * 11/10;
blue_delta = (B - average) * 11/10;
R = average + red_delta;
G = average + green_delta;
B = average + blue_delta;
// clip R,G,B to be in 0-255 range
Esto moverá los canales que están lejos de la media un 10% más lejos. Esto es casi como aumentar la saturación, aunque dará cambios de tono para algunos colores.
A partir de una plantilla de aplicación basada en Ver, crear una nueva subclase de UIView así:
// header file
@interface DesatView : UIView {
UIImage *image;
float saturation;
}
@property (nonatomic, retain) UIImage *image;
@property (nonatomic) float desaturation;
@end
// implementation file
#import "DesatView.h"
@implementation DesatView
@synthesize image, desaturation;
-(void)setSaturation:(float)sat;
{
saturation = sat;
[self setNeedsDisplay];
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor]; // else background is black
desaturation = 0.0; // default is no effect
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, self.bounds.size.height); // flip image right side up
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, rect, self.image.CGImage);
CGContextSetBlendMode(context, kCGBlendModeSaturation);
CGContextClipToMask(context, self.bounds, image.CGImage); // restricts drawing to within alpha channel
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, desaturation);
CGContextFillRect(context, rect);
CGContextRestoreGState(context); // restore state to reset blend mode
}
@end
Ahora en el método viewDidLoad de su controlador de vista, poner la vista en la pantalla y ajustar es la saturación de la siguiente manera:
- (void)viewDidLoad {
[super viewDidLoad];
DesatView *dv = [[DesatView alloc] initWithFrame:CGRectZero];
dv.image = [UIImage imageNamed:@"someImage.png"];
dv.frame = CGRectMake(0, 0, dv.image.size.width, dv.image.size.height);
dv.center = CGPointMake(160, 240); // put it mid-screen
dv.desaturation = 0.2; // desaturate by 20%,
[self.view addSubview:dv]; // put it on screen
}
cambiar la saturación de la siguiente manera:
dv.saturation = 0.8; // desaturate by 80%
Ob viously si desea utilizarlo fuera de un único método, debe hacer dv un ivar del controlador de vista. Espero que esto ayude.
Aquí hay una implementación del truco de Bessey (ponga este código en una categoría UIImage). No es rápido y definitivamente cambia de color, pero funciona de alguna manera.
+ (CGFloat) clamp:(CGFloat)pixel
{
if(pixel > 255) return 255;
else if(pixel < 0) return 0;
return pixel;
}
- (UIImage*) saturation:(CGFloat)s
{
CGImageRef inImage = self.CGImage;
CFDataRef ref = CGDataProviderCopyData(CGImageGetDataProvider(inImage));
UInt8 * buf = (UInt8 *) CFDataGetBytePtr(ref);
int length = CFDataGetLength(ref);
for(int i=0; i<length; i+=4)
{
int r = buf[i];
int g = buf[i+1];
int b = buf[i+2];
CGFloat avg = (r + g + b)/3.0;
buf[i] = [UIImage clamp:(r - avg) * s + avg];
buf[i+1] = [UIImage clamp:(g - avg) * s + avg];
buf[i+2] = [UIImage clamp:(b - avg) * s + avg];
}
CGContextRef ctx = CGBitmapContextCreate(buf,
CGImageGetWidth(inImage),
CGImageGetHeight(inImage),
CGImageGetBitsPerComponent(inImage),
CGImageGetBytesPerRow(inImage),
CGImageGetColorSpace(inImage),
CGImageGetAlphaInfo(inImage));
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ref);
CGContextRelease(ctx);
return [UIImage imageWithCGImage:img];
}
¿Alguien tiene alguna idea sobre cómo mejorar esto sin hacer una conversión de HSV completa? O mejor aún, una verdadera implementación para:
- (UIImage*) imageWithHueOffset:(CGFloat)h saturation:(CGFloat)s value:(CGFloat)v
Hay un filtro CoreImage para esto. CIColorControls
Simplemente configure inputSaturation
en < 1.0 para desaturar o> 1.0 para aumentar la saturación ...
por ejemplo. Aquí hay un método que agregué en una categoría en UIImage
para desaturar una imagen.
-(UIImage*) imageDesaturated {
CIContext *context = [CIContext contextWithOptions:nil];
CIImage *ciimage = [CIImage imageWithCGImage:self.CGImage];
CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"];
[filter setValue:ciimage forKey:kCIInputImageKey];
[filter setValue:@0.0f forKey:kCIInputSaturationKey];
CIImage *result = [filter valueForKey:kCIOutputImageKey];
CGImageRef cgImage = [context createCGImage:result fromRect:[result extent]];
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
return image;
}
+1. Creo que esto potencialmente filtra el 'cgImage', aunque insertar' CFAutorelease (cgImage); 'antes del' return' debería arreglar eso. También puede usar 'kCIInputImageKey' y' kCIInputSaturationKey' en lugar de '@" inputImage "' y '@" inputSaturation "', y '@ 0.0f' en lugar de' [NSNumber numberWithFloat: 0.0f] '. – Isaac
Gracias @Isaac, se han actualizado con sus sugerencias. –
Mike, sugiero que use 'UIImage * image = [UIImage imageWithCGImage: cgImage scale: self.scale orientation: self.imageOrientation];' ya que asegura que la escala y la orientación se mantienen desde el original, lo cual es especialmente importante cuando se trata de dispositivos retina. – devios1
que he acabo de probar método Mike Pollard 's y es correcta.
Aquí es el rápida 3 versión
let ciimage = CIImage.init(cgImage: self.givenImage.cgImage)
let filter = CIFilter.init(name: "CIColorControls")
filter?.setValue(ciimage, forKey: kCIInputImageKey)
filter?.setValue(0.0, forKey: kCIInputSaturationKey)
let result = filter?.value(forKey: kCIOutputImageKey) as! CIImage
let cgimage = CIContext.init(options: nil).createCGImage(result, from: result.extent)
let image = UIImage.init(cgImage: cgimage!)
- 1. ¿Cómo cambiar los valores de saturación con opencv?
- 2. Cambiar la imagen de una vista UIImage con un botón
- 3. ¿Cómo puedo cambiar la imagen mostrada en un UIImageView programáticamente?
- 4. ¿Cómo desaturo un UIImage?
- 5. JQuery cambiar el tono, la saturación, la gama de <img>?
- 6. ¿Cómo puedo guardar un UIImage en un NSDictionary?
- 7. iOS: ¿Cómo puedo cambiar la orientación de un UICollectionViewCell?
- 8. Cambiar el tamaño de la imagen con GDI en .NET da baja saturación
- 9. ¿Cómo puedo hacer Pdf desde UIImage?
- 10. iPhone: ¿Cómo puedo almacenar UIImage usando NSUserDefaults?
- 11. iPhone SDK - ¿Cómo dibujar un UIImage en otro UIImage?
- 12. enmascarando un UIImage
- 13. ¿Cómo guardo un UIImage en un archivo?
- 14. ¿Cómo obtengo UIImage de un CGContextRef?
- 15. UIBezierpath de enmascarar un UIImage
- 16. UIImage Cambiar el tamaño sin pérdida de calidad
- 17. Cómo estirar un UIImage conservando las esquinas
- 18. ¿Cómo puedo obtener la burbuja estándar de copia de iPhone para que aparezca en un UIImage?
- 19. ¿cómo puedo cambiar la parte de color de un TextView?
- 20. Cómo establecer la opacidad/alfa de un UIImage?
- 21. ¿Cómo puedo cambiar la resolución de un ráster usando GDAL?
- 22. ¿Cómo puedo cambiar la imagen de un ImageView?
- 23. ¿Cómo puedo cambiar la prioridad de un mensaje en MSMQ?
- 24. ¿Cómo puedo cambiar la codificación de un archivo con vim?
- 25. Cómo cambiar el tamaño de un UIImage mientras se mantiene su relación de aspecto
- 26. ¿Cómo puedo reflejar una imagen UIImage de UIImagePickerController
- 27. cómo convertir un CVImageBufferRef a UIImage
- 28. Recortar UIImage como un camscanner
- 29. Creando un UIImage de otro UIImage usando imageWithData devuelve nil
- 30. cambiar el tamaño de un UIImage sin cargarlo por completo en la memoria?
¿Por qué no llaman [auto setNeedsDisplay] en la incubadora para la desaturación? –
Mi cerebro rehuyó anular el setter sintetizado por algún motivo desconocido, pero tiene razón. Cambiaré la respuesta. – willc2
Entonces, esto reduce la saturación, ¿cómo aumentaría _ la saturación? – Shizam