如何更改UIImage的饱和度?

Tha*_*nks 16 iphone cocoa-touch core-graphics uikit

我有一个UIImage,并希望将其饱和度调整为+ 10%左右.是否有可用于此的标准方法或功能?

Mik*_*ard 16

有一个CoreImage过滤器.CIColorControls 只需将inputSaturation<1.0 设置为去饱和度或> 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;
}
Run Code Online (Sandbox Code Playgroud)


wil*_*lc2 9

从基于视图的应用程序模板开始,创建一个新的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
Run Code Online (Sandbox Code Playgroud)

现在在视图控制器的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   
}  
Run Code Online (Sandbox Code Playgroud)

像这样改变饱和度:

 dv.saturation = 0.8; // desaturate by 80%  
Run Code Online (Sandbox Code Playgroud)

显然,如果你想在单个方法之外使用它,你应该使dv成为视图控制器的ivar.希望这可以帮助.

  • 所以这会降低饱和度,你会如何_增加饱和度? (3认同)

Jim*_*y_m 7

斯威夫特 5

extension UIImage {
    
    func withSaturationAdjustment(byVal: CGFloat) -> UIImage {
        guard let cgImage = self.cgImage else { return self }
        guard let filter = CIFilter(name: "CIColorControls") else { return self }
        filter.setValue(CIImage(cgImage: cgImage), forKey: kCIInputImageKey)
        filter.setValue(byVal, forKey: kCIInputSaturationKey)
        guard let result = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return self }
        guard let newCgImage = CIContext(options: nil).createCGImage(result, from: result.extent) else { return self }
        return UIImage(cgImage: newCgImage, scale: UIScreen.main.scale, orientation: imageOrientation)
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 请不要发布相同的答案。相反,标记为重复 (2认同)
  • @Jean-FrançoisFabre 仔细看,它并不相同 (2认同)

Jes*_*sse 5

这是Bessey的hack的实现(将此代码放在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];
}
Run Code Online (Sandbox Code Playgroud)

任何人都有任何关于如何在不进行完整HSV转换的情况下改善这一点的想法?或者更好的是,真正的实现:

- (UIImage*) imageWithHueOffset:(CGFloat)h saturation:(CGFloat)s value:(CGFloat)v
Run Code Online (Sandbox Code Playgroud)


Mar*_*sey 4

没有什么那么简单。最简单的解决方案可能是使用已知的像素格式创建一个内存中的 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
Run Code Online (Sandbox Code Playgroud)

这将使偏离均值的通道进一步远离 10% 左右。这几乎就像增加饱和度一样,尽管它会给某些颜色带来色调变化。