2013-09-14 2 views
2

Я использовал фильтр гауссовского размытия GPUImage, чтобы размыть неподвижное изображение. Я хочу связать размер размытия с элементом пользовательского интерфейса, и когда элемент изменен пользователем, я размываю изображение. То, как я делаю это прямо сейчас, - это изменить blurSize, когда произошла значительная (> 0,25) смена, повторно применить фильтр и оживить новое изображение в imageView.GPUImage анимированный гауссовский фильтр размытия

Есть ли более эффективный способ для меня сделать это?

На iPhone 5, хотя производительность не лагги, она также не является супер гладкой (но, возможно, это потому, что эта операция слишком дорога, чтобы быть супер гладкой).

ответ

7

интересный вопрос. Лучше всего предварительно вычислить и отобразить CGImages на пару размытых весов в вашем диапазоне, а затем использовать два уложенных UIImageViews, а один сверху показывает более грубую копию, но частично прозрачную. Например, если вы сделали размытым по радиусам 1,2,3, ..., 10, и вы хотели показать 3,7, вы бы показали изображение 3 внизу и изображение 4 сверху, с непрозрачностью 70%.

// Created in answer to Nikhil Varma's question at http://stackoverflow.com/questions/18804668/gpuimage-animated-gaussian-blur-filter 
#import "GPUImage.h" 
#define BLUR_STEP 2. 
@interface AHViewController : UIViewController 

@property (weak, nonatomic) IBOutlet UIImageView *imageView2; 
@property (weak, nonatomic) IBOutlet UIImageView *imageView; 
@property (nonatomic) CGFloat blurRadius; 
@property (nonatomic, strong) UIImage *image; 
@property (nonatomic, strong) GPUImageGaussianBlurFilter *blurFilter; 
@property (nonatomic, strong) NSCache *blurredImageCache; 
@property (nonatomic, strong) dispatch_queue_t blurQueue; 
@property (nonatomic, strong) NSMutableSet *blurAmountsBeingRendered; 
@end 

@implementation AHViewController 

-(void)viewDidLoad { 
    [super viewDidLoad]; 
    self.image = [UIImage imageNamed:@"ph.jpg"]; 
    self.blurFilter = [GPUImageGaussianBlurFilter new]; 
    self.blurredImageCache = [NSCache new]; 
    self.blurQueue = dispatch_queue_create("Image Blur Queue", DISPATCH_QUEUE_SERIAL); 
    self.blurAmountsBeingRendered = [NSMutableSet set]; 
    self.blurRadius = 1.0; 
} 
- (IBAction)sliderDidMove:(UISlider *)sender { 
    self.blurRadius = 10 * sender.value; 
} 
-(void)setBlurRadius:(CGFloat)blurRadius { 
    _blurRadius = blurRadius; 
    CGFloat smaller = self.blurRadius - fmodf(self.blurRadius, BLUR_STEP); 
    [self asyncGenerateImageWithBlurAmount:smaller]; 
    CGFloat larger = smaller + BLUR_STEP; 
    [self asyncGenerateImageWithBlurAmount:larger]; 

} 
-(UIImage *)cachedImageWithBlurAmount:(CGFloat)blur { 
    return [self.blurredImageCache objectForKey:@(blur)]; 
} 
-(void)asyncGenerateImageWithBlurAmount:(CGFloat)blur { 
    // This image is already available. 
    if([self.blurredImageCache objectForKey:@(blur)]) { 
     [self imageIsAvailableWithBlur:blur]; 
     return; 
    } 
    // There's already a render going on for this. Just return. 
    if([self.blurAmountsBeingRendered containsObject:@(blur)]) 
     return; 

    // Start a render 
    [self.blurAmountsBeingRendered addObject:@(blur)]; 
    dispatch_async(self.blurQueue, ^{ 
     self.blurFilter.blurSize = blur; 
     UIImage *result = [self.blurFilter imageByFilteringImage:self.image]; 
     [self.blurredImageCache setObject:result forKey:@(blur)]; 
     [self.blurAmountsBeingRendered removeObject:@(blur)]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [self imageIsAvailableWithBlur:blur]; 
     }); 
    }); 
} 

-(void)imageIsAvailableWithBlur:(CGFloat)blurAmount { 
    CGFloat smaller = self.blurRadius - fmodf(self.blurRadius, BLUR_STEP); 
    CGFloat larger = smaller + BLUR_STEP; 

    UIImage *sharperImage = [self cachedImageWithBlurAmount:smaller]; 
    UIImage *blurrier = [self cachedImageWithBlurAmount:larger]; 
    if(sharperImage && blurrier) { 
     if(![self.imageView.image isEqual:sharperImage]) 
      self.imageView.image = sharperImage; 
     if(![self.imageView2.image isEqual:blurrier]) { 
      self.imageView2.image = blurrier; 
     } 
     self.imageView2.alpha = (self.blurRadius - smaller)/BLUR_STEP; 
    } 
} 


@end 
+3

Лучшим решением было бы избежать использования UIImageView в качестве выходного адресата для размытого изображения. В извлечении UIImage из фреймворка много накладных расходов и аналогичная сумма при присвоении ему UIImageView. Вместо этого размытие должно отображаться непосредственно в GPUImageView, чтобы все это оставалось на графическом процессоре. Это гораздо более быстрый маршрут. –

+0

Брэд, прежде всего отличная работа над вашей библиотекой! Я использую UIImageView, потому что UIImages хранятся в NSCache и кроссфейдируются взад и вперед. GPUImage запускается только один раз за размытие «tick» (BLUR_STEP), а результаты интерполируются с использованием imageView2.alpha (до тех пор, пока NSCache не выдворяет полученный UII-файл из-за давления памяти). –

+0

@AdlaiHoller Спасибо за подробный фрагмент кода. Мой вопрос заключается в том, почему мы используем метод представления изображений в виде стоп-кадра? Имеет ли 70% непрозрачный 4 в верхней части 3, делает размытие более реалистичным (хотя это субъективный термин)? –

Смежные вопросы