GPUImage 动画高斯模糊滤镜

Posted

技术标签:

【中文标题】GPUImage 动画高斯模糊滤镜【英文标题】:GPUImage animated gaussian blur filter 【发布时间】:2013-09-14 17:50:13 【问题描述】:

我使用 GPUImage 高斯模糊滤镜来模糊静止图像。我想将模糊大小与 UI 元素联系起来,并且随着用户更改元素,我会模糊图片。我现在这样做的方法是在发生显着(> 0.25)变化时更改 blurSize,重新应用过滤器并将新图像动画化到 imageView 中。

有没有更有效的方法让我这样做?

在 iPhone 5 上,虽然性能并不落后,但也不是超级流畅(但可能是因为这个操作太昂贵而无法超级流畅)。

【问题讨论】:

【参考方案1】:

有趣的问题。您最好的选择是在您的范围内以几个模糊比例预先计算和渲染 CGImage,然后使用两个堆叠的 UIImageView,顶部的一个显示较模糊的副本但部分透明。例如,如果您在半径 1、2、3、...、10 处渲染模糊,并且想要显示 3.7,则您将在底部显示图像 3,在顶部显示图像 4,不透明度为 70%。

// Created in answer to Nikhil Varma's question at http://***.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

【讨论】:

更好的解决方案是避免使用 UIImageView 作为模糊图像的输出目标。从框架中提取 UIImage 会产生很多开销,将其分配给 UIImageView 也会产生类似的开销。相反,模糊应该直接渲染到 GPUImageView 以便所有这些都保留在 GPU 上。这是一条更快的路线。 布拉德,首先你的图书馆做得很好!我正在使用 UIImageView,因为 UIImages 存储在 NSCache 中并且来回交叉淡化。 GPUImage 仅在每个模糊“tick”(BLUR_STEP)运行一次,结果使用 imageView2.alpha 进行插值(直到/除非 NSCache 由于内存压力而驱逐生成的 UIImage)。 @AdlaiHoller 感谢您提供详细的代码 sn-p。我的问题是,为什么我们使用堆叠图像视图方法?在 3 之上添加 70% 不透明的 4 是否会使模糊更加逼真(尽管这是一个主观术语)? @BradLarson 我将尝试使用 GPUImageView 类,因为我真的想优化这部分的速度和流动性。顺便说一句,很棒的图书馆(尽管我相信无数其他人已经告诉过你了)。 @NikhilVarma 堆叠的 imageView 方法让我们可以近似计算尚未计算的模糊量。假设用户将滑块设置为 3.71。如果我们已经在 4 和 3 保存了模糊图像,我们可以非常快速地显示它们混合在一起,而不是计算一个全新的模糊图像。如果用户随后拖动到 3.72,我们只需更改混合比例。 Brad 错了——GPUImageView 在这里根本无法实现流动性,因为高斯模糊非常昂贵。

以上是关于GPUImage 动画高斯模糊滤镜的主要内容,如果未能解决你的问题,请参考以下文章

高斯模糊滤镜反转:iOS

仅在 xCode 中的触摸区域应用高斯模糊滤镜

模糊边界外的 UIImage(Photoshop 样式)

详析数字图像中高斯模糊理论及实现

CSS3滤镜模糊关键帧动画

背景虚化 高斯模糊