Как я могу изменить насыщенность UIImage?

У меня есть UIImage и я хочу изменить его насыщенность примерно на + 10%. Существуют ли стандартные методы или функции, которые можно использовать для этого?

Ответы на вопрос(5)

Для этого есть фильтр CoreImage.CIColorControls Просто установитеinputSaturation & lt; 1,0 для обесцвечивания или & gt; 1,0 для увеличения насыщенности ...

например. Вот способ, который я добавил в категорию наUIImage обесцветить изображение.

-(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;
}
 18 авг. 2014 г., 12:04
Спасибо @Isaac, обновил ваши предложения.
 05 мар. 2015 г., 06:44
Хотя это работает, на самом деле требуется много обработки! Удостоверьтесь, чтобы сделать это на отправленной нити.
 14 авг. 2014 г., 22:23
+1. Я думаю, что это потенциально утечкаcgImageхоть вставляяCFAutorelease(cgImage); передreturn должен это исправить. Вы также можете использоватьkCIInputImageKey а такжеkCIInputSaturationKey вместо@"inputImage" а также@"inputSaturation", а также@0.0f вместо[NSNumber numberWithFloat:0.0f].
 04 дек. 2014 г., 20:23
Майк, я предлагаю тебе использоватьUIImage *image = [UIImage imageWithCGImage:cgImage scale:self.scale orientation:self.imageOrientation]; так как он гарантирует, что масштаб и ориентация сохраняются от оригинала, что особенно важно при работе с устройствами сетчатки.

Я только что проверилMike Pollardметод, и это правильно.

Здесьswift 3 версия

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!)

Начиная с шаблона приложения на основе представления, создайте новый подкласс UIView следующим образом:

// 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

Теперь в методе viewDidLoad контроллера представления поместите представление на экран и установите его насыщенность следующим образом:

- (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   
}  

Измените насыщенность следующим образом:

 dv.saturation = 0.8; // desaturate by 80%  

Очевидно, что если вы хотите использовать его вне одного метода, вы должны сделать dv ivar контроллера представления. Надеюсь это поможет.

 30 сент. 2010 г., 03:33
Так что это уменьшает насыщенность, как бы выincrease Насыщение?
 17 янв. 2010 г., 03:01
Мой мозг уклонялся от переопределения синтезированного сеттера по неизвестной причине, но вы правы. Я изменю ответ.
 16 мар. 2011 г., 18:49
Мне бы очень хотелось увидеть код, который также увеличил насыщенность.
 16 янв. 2010 г., 16:46
Почему бы просто не вызвать [self setNeedsDisplay] в сеттере для десатурации?

Вот реализация взлома Бесси (поместите этот код в категорию UIImage). Это не быстро и определенно меняет оттенки, но это работает.

+ (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];
}

У кого-нибудь есть идеи о том, как улучшить это, не делая полное преобразование HSV? Или еще лучше, истинная реализация для:

- (UIImage*) imageWithHueOffset:(CGFloat)h saturation:(CGFloat)s value:(CGFloat)v

Ничего такого простого. Возможно, самое простое решение - создать в памяти CGContext с известным форматом пикселей, нарисовать изображение в этом контексте, а затем прочитать / изменить пиксели в буфере известного формата.

Я не думаю, что CG поддерживает цветовое пространство с отдельным каналом насыщения, поэтому вам придется либо конвертировать из RGB в HSV или HSL, либо выполнять вычисления непосредственно в пространстве RGB.

Один из способов сделать расчет непосредственно в RGB может быть примерно таким:

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

Это сместит каналы, которые находятся вдали от среднего значения, примерно на 10% дальше. Этоalmost как увеличение насыщенности, хотя это даст сдвиги оттенков для некоторых цветов.

Ваш ответ на вопрос