26

I have been searching for an answer to this question in a few hours now, and I just can't figure it out. I want to add a gaussian blur effect to the image when i press the button "Button". The user is the one that is adding the image.

I have created an action for the "Button" based on sources from SO and other places on the web. It will not work. What am I doing wrong? Any code would be greatly appreciated. Here is my "Button" action:

- (IBAction)test:(id)sender {
    CIFilter *gaussianBlurFilter = [CIFilter filterWithName: @"CIGaussianBlur"];
    [gaussianBlurFilter setValue:imageView.image forKey: @"inputImage"];
    [gaussianBlurFilter setValue:[NSNumber numberWithFloat: 10] forKey: @"inputRadius"];
}

If you need anything else that I have done to answer the question, please let me know :D

Here is my UI

Rob
  • 371,891
  • 67
  • 713
  • 902
user2891448
  • 353
  • 1
  • 4
  • 7

6 Answers6

74

Three observations:

  1. You need to set the inputImage to be the the CIImage from your UIImage:

    [gaussianBlurFilter setValue:[CIImage imageWithCGImage:[imageView.image CGImage]] forKey:kCIInputImageKey];
    
  2. I don't see you grabbing the outputImage, e.g.:

    CIImage *outputImage = [gaussianBlurFilter outputImage];
    
  3. And you presumably want to convert that CIImage back to a UIImage.

So, putting that all together:

CIFilter *gaussianBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
[gaussianBlurFilter setDefaults];
CIImage *inputImage = [CIImage imageWithCGImage:[imageView.image CGImage]];
[gaussianBlurFilter setValue:inputImage forKey:kCIInputImageKey];
[gaussianBlurFilter setValue:@10 forKey:kCIInputRadiusKey];

CIImage *outputImage = [gaussianBlurFilter outputImage];
CIContext *context   = [CIContext contextWithOptions:nil];
CGImageRef cgimg     = [context createCGImage:outputImage fromRect:[inputImage extent]];  // note, use input image extent if you want it the same size, the output image extent is larger
UIImage *image       = [UIImage imageWithCGImage:cgimg];
CGImageRelease(cgimg);

Alternatively, if you go to the WWDC 2013 sample code (paid developer subscription required) and download iOS_UIImageEffects, you can then grab the UIImage+ImageEffects category. That provides a few new methods:

- (UIImage *)applyLightEffect;
- (UIImage *)applyExtraLightEffect;
- (UIImage *)applyDarkEffect;
- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;

So, to blur and image and lightening it (giving that "frosted glass" effect) you can then do:

UIImage *newImage = [image applyLightEffect];

Interestingly, Apple's code does not employ CIFilter, but rather calls vImageBoxConvolve_ARGB8888 of the vImage high-performance image processing framework.

This technique is illustrated in WWDC 2013 video Implementing Engaging UI on iOS.


I know the question was about iOS 7, but now in iOS 8 one can add a blur effect to any UIView object with UIBlurEffect:

UIVisualEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];    
effectView.frame = imageView.bounds;
[imageView addSubview:effectView];
Rob
  • 371,891
  • 67
  • 713
  • 902
  • lets say i do not have any code in the ibaction, what excatcly would I write in my ibaction? Is it the last piece of code that you wrote? Thanks for a great reply btw, and sorry for not understanding quite as much :D – user2891448 Oct 17 '13 at 17:26
  • I get a warning, "Unused variable Image" that is the second last line of code – user2891448 Oct 17 '13 at 17:30
  • 1
    @user2891448 I wasn't sure what you wanted to do with the `image`. Perhaps update the `imageView.image = image;`? – Rob Oct 17 '13 at 17:32
  • Just one last thing, I want set the value for the amount of blur with a variable, how do I do this? This is in [gaussianBlurFilter setValue:@10 forKey:kCIInputRadiusKey]; – user2891448 Oct 17 '13 at 17:43
  • I want it to be something like [gaussianBlurFilter setValue:@"%f",variable forKey:kCIInputRadiusKey]; do I have to make that a NSString or what? – user2891448 Oct 17 '13 at 17:45
  • 2
    @user2891448 Let's say you had some `CGFloat` variable called `radius`, then you'd do `[gaussianBlurFilter setValue:@(radius) forKey:kCIInputRadiusKey];` – Rob Oct 17 '13 at 17:45
  • I knew i said the last, was the last, but when i press the button everything works perfectly fine. The only thing is that the image shrinks (I know it really expands) but what do I have to implement to make it look the same size as before? Do this have anything to do about CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]]; ? – user2891448 Oct 17 '13 at 18:05
  • @user2891448 I had never noticed that as I've been using `UIViewContentModeCenter`. I've added code to alter the `CGRect` obtained from `[outputImage extent]` so that it will result in an image of the same size. – Rob Oct 17 '13 at 19:52
  • @user2891448 FYI, I've added WWDC 2013 link that shows how Apple is blurring images, not using `CIFilter`, but rather `vImage` framework. – Rob Oct 27 '13 at 14:33
  • Can we use that in our products? – KarenAnne Nov 28 '13 at 08:05
  • @KarenAnne Again, see the license contained within the .m file. That makes it pretty clear. To paraphrase, if you redistribute their source, you must keep their copyright there, but you otherwise appear to have the right to use their source. That appears to be consistent with most things you see on S.O., which is generally [cc-by-sa](http://blog.stackoverflow.com/2009/06/attribution-required/). – Rob Nov 28 '13 at 08:16
  • Thanks for the link! :) – KarenAnne Nov 28 '13 at 08:35
4

This worked for me, however it is slow. I will do the transformation on app loading and use the once transformed image in many places.

This blur effect is for iOS 8.4. Swift language.

override func viewDidLoad()
{
    super.viewDidLoad()

    var backgroundImage = self.blurImage(UIImage(named: "background.jpg")!)

    self.view.backgroundColor = UIColor(patternImage:backgroundImage)
}

func blurImage(imageToBlur:UIImage) -> UIImage
{
    var gaussianBlurFilter = CIFilter(name: "CIGaussianBlur")
    gaussianBlurFilter.setValue(CIImage(CGImage: imageToBlur.CGImage), forKey:kCIInputImageKey)

    var inputImage = CIImage(CGImage: imageToBlur.CGImage)

    var outputImage = gaussianBlurFilter.outputImage
    var context = CIContext(options: nil)
    var cgimg = context.createCGImage(outputImage, fromRect: inputImage.extent())

    return UIImage(CGImage: cgimg)!
}
MB_iOSDeveloper
  • 4,076
  • 4
  • 21
  • 35
  • 1
    It's very interesting.. but also very slow.. :/ at least on Apple TV – marco.marinangeli Dec 14 '15 at 01:18
  • Yes it is slow :/. You can do "onDemand" blurring, that might help, or if you have some in app loading, then call the blurImage function. At some point I saved the blurred images, so when I need them I can load them from memory or elsewhere rather then calling the function again. Hope this helps or points you in the correct direction :))) – MB_iOSDeveloper Dec 15 '15 at 09:24
1

The input of a CIFilter needs to be a CIImage not a UIImage.

Mihai Maruseac
  • 19,378
  • 6
  • 52
  • 107
Richard Stelling
  • 25,151
  • 27
  • 105
  • 186
1

Swift 4 UIImage extension without force unwrapping:

extension UIImage {
    /// Applies a gaussian blur to the image.
    ///
    /// - Parameter radius: Blur radius.
    /// - Returns: A blurred image.
    func blur(radius: CGFloat = 6.0) -> UIImage? {
        let context = CIContext()
        guard let inputImage = CIImage(image: self) else { return nil }

        guard let clampFilter = CIFilter(name: "CIAffineClamp") else { return nil }
        clampFilter.setDefaults()
        clampFilter.setValue(inputImage, forKey: kCIInputImageKey)

        guard let blurFilter = CIFilter(name: "CIGaussianBlur") else { return nil }
        blurFilter.setDefaults()
        blurFilter.setValue(clampFilter.outputImage, forKey: kCIInputImageKey)
        blurFilter.setValue(radius, forKey: kCIInputRadiusKey)

        guard let blurredImage = blurFilter.value(forKey: kCIOutputImageKey) as? CIImage,
            let cgImage = context.createCGImage(blurredImage, from: inputImage.extent) else { return nil }

        let resultImage = UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
        return resultImage
    }
}

I highly recommend to blur the image on the background thread:

let image = UIImage(named: "myImage")
DispatchQueue.global(qos: .userInitiated).async {
    let blurredImage = image.blur()
    DispatchQueue.main.async {
        self.myImageView.image = blurredImage
    }
}
Andrey Gordeev
  • 23,709
  • 8
  • 108
  • 140
0

Swift 2.0

Make an extension of UIImageView. (File-New-File-Empty Swift File -Name it Extensions. )

import Foundation
import UIKit

extension UIImageView{

 //Method 1
 func makeBlurImage(targetImageView:UIImageView?)
 {
  let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.Dark)
  let blurEffectView = UIVisualEffectView(effect: blurEffect)
  blurEffectView.frame = targetImageView!.bounds

  blurEffectView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] // for supporting device rotation
  targetImageView?.addSubview(blurEffectView)
 }
 //Method 2
 func convertToBlurImage(imageToBlur:UIImage) -> UIImage
 {
  let gaussianBlurFilter = CIFilter(name: "CIGaussianBlur")
  gaussianBlurFilter!.setValue(CIImage(CGImage: imageToBlur.CGImage!), forKey:kCIInputImageKey)

  let initialImage = CIImage(CGImage: imageToBlur.CGImage!)

  let finalImage = gaussianBlurFilter!.outputImage
  let finalImagecontext = CIContext(options: nil)

  let finalCGImage = finalImagecontext.createCGImage(finalImage!, fromRect: initialImage.extent)
  return UIImage(CGImage: finalCGImage)
 }
}

Usage:

Using Method 1:

override func viewDidLoad() {
  super.viewDidLoad()

  let sampleImageView = UIImageView(frame: CGRectMake(0, 200, 300, 325))
  let sampleImage:UIImage = UIImage(named: "ic_120x120")!
  sampleImageView.image =  sampleImage

  //Convert To Blur Image Here
  sampleImageView.makeBlurImage(sampleImageView)

  self.view.addSubview(sampleImageView)
 }

Using Method 2:

   override func viewDidLoad() {
      super.viewDidLoad()

      let sampleImageView = UIImageView(frame: CGRectMake(0, 200, 300, 325))
      let sampleImage:UIImage = UIImage(named: "ic_120x120")!

      //Convert to Blurred Image Here
      let sampleImage2 =  sampleImageView.convertToBlurImage(sampleImage)
      sampleImageView.image =  sampleImage2

      self.view.addSubview(sampleImageView)
     }
A.G
  • 13,048
  • 84
  • 61
0

Here's an extension for image blur on

Swift 3

extension UIImage {

    public func blurImage(radius: CGFloat) -> UIImage {

        let inputImage = CIImage(image: self)!

        let parameters: [String:Any] = [
            kCIInputRadiusKey: radius,
            kCIInputImageKey: inputImage
        ]
        let filter = CIFilter(name: "CIGaussianBlur",
                              withInputParameters: parameters)!

        let cgimg = CIContext().createCGImage(filter.outputImage!, from: inputImage.extent)
        return UIImage(cgImage: cgimg!)
    }

}
Alex Shubin
  • 3,111
  • 1
  • 23
  • 31