2009-07-17 27 views

Trả lời

4

Không có gì khá đơn giản. Giải pháp dễ nhất có lẽ là tạo một CGContext trong bộ nhớ với định dạng pixel đã biết, vẽ hình ảnh vào ngữ cảnh đó, sau đó đọc/sửa đổi các pixel trong bộ đệm định dạng đã biết. Tôi không nghĩ rằng CG hỗ trợ một không gian màu với một kênh bão hòa riêng biệt, vì vậy bạn sẽ phải chuyển đổi từ RGB sang HSV hoặc HSL, hoặc thực hiện các phép tính trực tiếp trong không gian RGB.


Một cách để làm việc tính toán trực tiếp trong RGB có thể là một cái gì đó như thế này:

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 

này sẽ di chuyển các kênh mà là ra khỏi nghĩa khoảng 10% xa hơn. Đây là gần như như tăng độ bão hòa, mặc dù nó sẽ cho màu sắc thay đổi cho một số màu.

9

Bắt đầu với một xem dựa Mẫu đơn đăng ký, tạo một lớp con mới của UIView như vậy:

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

Bây giờ trong phương pháp viewDidLoad điều khiển quan điểm của bạn, đưa quan điểm trên màn hình và thiết lập nó là bão hòa như thế này:

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

Thay đổi độ bão hòa như thế này:

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

Ob viously nếu bạn muốn sử dụng nó bên ngoài của một phương pháp duy nhất, bạn nên làm cho dv một ivar của bộ điều khiển xem. Hi vọng điêu nay co ich.

+1

Tại sao không chỉ cần gọi [tự setNeedsDisplay] trong setter cho bão hòa? –

+0

Não của tôi tránh xa việc ghi đè bộ tổng hợp cho một số lý do không rõ nhưng bạn đúng. Tôi sẽ thay đổi câu trả lời. – willc2

+3

Vì vậy, điều này làm giảm độ bão hòa, làm thế nào bạn sẽ _increase_ bão hòa? – Shizam

5

Dưới đây là triển khai thực hiện tấn công của Bessey (đặt mã này vào danh mục UIImage). Nó không phải là nhanh chóng và nó chắc chắn thay đổi màu sắc, nhưng nó loại công trình.

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

Bất kỳ ai có ý tưởng về cách cải thiện điều này mà không thực hiện chuyển đổi HSV đầy đủ? Hoặc tốt hơn, một triển khai thực sự cho:

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

Có bộ lọc CoreImage cho việc này. CIColorControls Chỉ cần đặt inputSaturation thành < 1.0 để khử bão hòa hoặc> 1.0 để tăng độ bão hòa ...

ví dụ: Đây là phương pháp tôi đã thêm vào một danh mục trên UIImage để khử bão hòa một hình ảnh.

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

+1. Tôi nghĩ rằng điều này có khả năng rò rỉ 'cgImage', mặc dù chèn' CFAutorelease (cgImage); 'trước khi' return' sửa lỗi đó. Bạn cũng có thể sử dụng 'kCIInputImageKey' và' kCIInputSaturationKey' thay vì '@" inputImage "' và '@" inputSaturation "' và '@ 0.0f' thay vì' [NSNumber numberWithFloat: 0.0f] '. – Isaac

+1

Cảm ơn @Isaac, đã cập nhật các đề xuất của bạn. –

+0

Mike, tôi đề nghị bạn sử dụng 'UIImage * image = [Hình ảnh UIImageWithCGImage: cgImage scale: self.scale orientation: self.imageOrientation];' vì nó đảm bảo quy mô và hướng được duy trì từ bản gốc, đặc biệt quan trọng khi xử lý thiết bị võng mạc. – devios1

2

Tôi vừa thử nghiệm phương pháp Mike Pollard 's và nó là đúng.

Đây là nhanh chóng 3 phiên bả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!) 
Các vấn đề liên quan