UIImage的高斯模糊和毛玻璃代码示例

最近用到了高斯模糊,谷歌了一下,发现挺多现成的例子,本来只是收藏一下网址的,但是在用的过程中,发现了一点问题,索性就把自己使用的汇总下,以后好参考。主要代码全部来源于参考链接里面。

一、发现的问题

在使用vImage渲染高斯模糊的时候,参考文章一中,测试发现如果仅仅使用静态图的时候,的确会模糊,但是仔细看的话,发现背景颜色居然变了,看评论的确也有其他人碰到。

所以就继续搜索,在知乎上面搜到了另外一个文章说高斯模糊的开销时,又附了一段代码,使用是OK的,就是参考文章二中的例子,所以就在项目中使用了。

当今天写这个例子的时候,居然发现不知道什么原因,参考文章一中的图片背景颜色是正常的,二中的背景颜色不对了,最终终于找到了一个可用的方案,总结下

二、高斯模糊

2.1、使用CoreImage进行高斯模糊

/**
 使用CoreImage进行高斯模糊

 @param image 需要模糊的图片
 @param blur 模糊的范围 可以1~99
 @return 返回已经模糊过的图片
 */
+(UIImage *)coreBlurImage:(UIImage *)image withBlurNumber:(CGFloat)blur
{
    CIContext *context = [CIContext contextWithOptions:nil];
    CIImage *inputImage= [CIImage imageWithCGImage:image.CGImage];
    //设置filter
    CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [filter setValue:inputImage forKey:kCIInputImageKey]; [filter setValue:@(blur) forKey: @"inputRadius"];
    //模糊图片
    CIImage *result=[filter valueForKey:kCIOutputImageKey];
    CGImageRef outImage=[context createCGImage:result fromRect:[result extent]];
    UIImage *blurImage=[UIImage imageWithCGImage:outImage];
    CGImageRelease(outImage);
    return blurImage;
}

2.2、使用Accelerate中的vImage进行高斯模糊

/**
 使用vImage进行高斯模糊
 
 @param image 需要模糊的图片
 @param blur 模糊的范围 0~1
 @return 返回已经模糊过的图片
 */
+(UIImage*)imageBlurImage:(UIImage *)image WithBlurNumber:(CGFloat)blur
{
    //    NSInteger boxSize = (NSInteger)(10 * 5);
    NSData *imageData = UIImageJPEGRepresentation(image, 1); // convert to jpeg
    UIImage* destImage = [UIImage imageWithData:imageData];
    
    
    if (blur < 0.f || blur > 1.f) {
        blur = 0.5f;
    }
    int boxSize = (int)(blur * 40);
    boxSize = boxSize - (boxSize % 2) + 1;
    
    CGImageRef img = destImage.CGImage;
    
    vImage_Buffer inBuffer, outBuffer;
    
    vImage_Error error;
    
    void *pixelBuffer;
    
    
    //create vImage_Buffer with data from CGImageRef
    
    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
    
    
    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);
    
    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);
    
    //create vImage_Buffer for output
    
    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
    
    if(pixelBuffer == NULL)
        NSLog(@"No pixelbuffer");
    
    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);
    
    // Create a third buffer for intermediate processing
    void *pixelBuffer2 = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
    vImage_Buffer outBuffer2;
    outBuffer2.data = pixelBuffer2;
    outBuffer2.width = CGImageGetWidth(img);
    outBuffer2.height = CGImageGetHeight(img);
    outBuffer2.rowBytes = CGImageGetBytesPerRow(img);
    
    //perform convolution
    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer2, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    if (error) {
        NSLog(@"error from convolution %ld", error);
    }
    error = vImageBoxConvolve_ARGB8888(&outBuffer2, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    if (error) {
        NSLog(@"error from convolution %ld", error);
    }
    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);
    if (error) {
        NSLog(@"error from convolution %ld", error);
    }
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
                                             outBuffer.width,
                                             outBuffer.height,
                                             8,
                                             outBuffer.rowBytes,
                                             colorSpace,
                                             (CGBitmapInfo)kCGImageAlphaNoneSkipLast);
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
    
    //clean up
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);
    
    free(pixelBuffer);
    free(pixelBuffer2);
    CFRelease(inBitmapData);
    
    CGImageRelease(imageRef);
    
    return returnImage;
}

2.3、高斯模糊总结

当然参考文章中说使用GPUImage这个库也可以搞定,但是对于模糊这个计算量大的操作来说,借用知乎上面的总结

  • CoreImage 的帧数会随着模糊大小而降低,cpu也蛮高;
  • Accelerate 的模糊是最靠谱的,速度和效果都非常完美,同时会占用更高的cpu;

所以还是使用Accelerate的vImage这个方案比较好

三、毛玻璃

毛玻璃是ios8.0之后的一个效果,可以使用UIVisualEffectView来实现,就是在UIImageView上面加一个view。

UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
effectView.frame = CGRectMake(0, 0, _imgView.frame.size.width*0.5, _imgView.frame.size.height);
[_imgView addSubview:effectView];

UIBlurEffectStyle有几个不同的样式,可以自己调整使用

typedef NS_ENUM(NSInteger, UIBlurEffectStyle) {
    UIBlurEffectStyleExtraLight,
    UIBlurEffectStyleLight,
    UIBlurEffectStyleDark,
    UIBlurEffectStyleExtraDark __TVOS_AVAILABLE(10_0) __IOS_PROHIBITED __WATCHOS_PROHIBITED,
    UIBlurEffectStyleRegular NS_ENUM_AVAILABLE_IOS(10_0), // Adapts to user interface style
    UIBlurEffectStyleProminent NS_ENUM_AVAILABLE_IOS(10_0), // Adapts to user interface style
} NS_ENUM_AVAILABLE_IOS(8_0);

四、Demo下载

Github下载:https://github.com/DamonHu/HudongBlogDemo/tree/master/ImgBlur

Gitee下载:https://gitee.com/DamonHoo/HudongBlogDemo/tree/master/ImgBlur

五、Demo效果

IMG_7424.PNG

六、参考文章

Last modification:September 21st, 2017 at 05:20 pm
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

2 comments

  1. 梦痕

    UIVisualEffectView 是 iOS8.0 之后加入的.

    1. 东东
      @梦痕

      恩啊,多谢指正,枚举那里写的8.0୧(๑•̀⌄•́๑)૭

Leave a Comment