我正在使用GPUImage高斯模糊滤镜来模糊静止图像。我想将模糊大小绑定(bind)到UI元素,并且当用户更改该元素时,我会模糊图片。我现在的操作方式是在发生重大变化(> 0.25)时更改blurSize,重新应用滤镜并将新图像设置为imageView的动画。
我有一种更有效的方法吗?
在iPhone 5上,虽然性能并不差,但也不是 super 平滑(但这也许是因为此操作过于昂贵而无法实现 super 平滑)。
最佳答案
有趣的问题。最好的选择是在范围内的几个模糊比例下预先计算和渲染CGImage,然后使用两个堆叠的UIImageView,最上面的一个显示模糊副本,但部分透明。例如,如果以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