我有一个 UIImage,想将其饱和度调整大约 +10%。是否有可用于此目的的标准方法或函数?
为此有一个 CoreImage 过滤器。
CIColorControls
只需将 inputSaturation
设置为 < 1.0 to desaturate or > 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;
}
从基于视图的应用程序模板开始,创建 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。希望这有帮助。
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)
}
}
这是 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];
}
有人对如何在不进行完整 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%。这“几乎”就像增加饱和度一样,尽管它会给某些颜色带来色调变化。
的方法,它是正确的。 这是
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!)
extension UIImage {
/// Returns desaturated image.
func desaturated() -> UIImage? {
let filter = CIFilter.colorControls()
filter.inputImage = CIImage(image: self)
filter.saturation = 0 // 0 means no saturation
guard let outputCIImage = filter.outputImage else { return nil }
return UIImage(ciImage: outputCIImage)
}
}