How to Implement a Box or Gaussian Blur on iOS

Gaussian Blur Over Image - iOS 8

I have done a small example based on the photo you have there. My algorithm is as follows:

  • Extract a portion of image from bottom.
  • Apply gaussian filter to it and blur it.
  • Then, create a new image context, draw original image on it.
  • Then, draw the blurred portion of the image to place it exactly over the original image.
  • Extract the new image from the context.

Here is a source code for doing so,

 @implementation ViewController

- (void)viewDidLoad{

[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] init];
imageView.frame = self.view.bounds;
[self.view addSubview:imageView];
imageView.contentMode = UIViewContentModeScaleAspectFit;
UIImage *image = [UIImage imageNamed:@"monogram.jpg"];
imageView.image = [self imageWithBlurredImageWithImage: image andBlurInsetFromBottom: 200 withBlurRadius:3];
}

- (UIImage*)imageWithBlurredImageWithImage:(UIImage*)image andBlurInsetFromBottom:(CGFloat)bottom withBlurRadius:(CGFloat)blurRadius{
UIGraphicsBeginImageContext(image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -image.size.height);
CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, bottom), [self blurImage: image withBottomInset: bottom blurRadius: blurRadius].CGImage);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

- (UIImage*)blurImage:(UIImage*)image withBottomInset:(CGFloat)inset blurRadius:(CGFloat)radius{

image = [UIImage imageWithCGImage: CGImageCreateWithImageInRect(image.CGImage, CGRectMake(0, image.size.height - inset, image.size.width,inset))];

CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage];
CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
[filter setValue:ciImage forKey:kCIInputImageKey];
[filter setValue:@(radius) forKey:kCIInputRadiusKey];

CIImage *outputCIImage = filter.outputImage;
CIContext *context = [CIContext contextWithOptions:nil];

return [UIImage imageWithCGImage: [context createCGImage:outputCIImage fromRect:ciImage.extent]];

}
@end

And here is the screenshot of the result.

Sample Image

Gaussian blur on full screen

It is happening because the gaussian blur filter samples pixels outside the edges of the image. But because there are no pixels, you get this weird artefact. You can use "CIAffineClamp" filter to "extend" your image infinitely in all directions.

Please see this answer https://stackoverflow.com/a/18138742/762779

I tried running your code with chained 'CIAffineClamp-> CIGaussianBlur' filters and got good results.

let layer = UIApplication.sharedApplication().keyWindow?.layer
UIGraphicsBeginImageContext(view.frame.size)

layer!.renderInContext(UIGraphicsGetCurrentContext()!)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

let blurRadius = 5
let ciimage: CIImage = CIImage(image: screenshot)!

// Added "CIAffineClamp" filter
let affineClampFilter = CIFilter(name: "CIAffineClamp")!
affineClampFilter.setDefaults()
affineClampFilter.setValue(ciimage, forKey: kCIInputImageKey)
let resultClamp = affineClampFilter.valueForKey(kCIOutputImageKey)

// resultClamp is used as input for "CIGaussianBlur" filter
let filter: CIFilter = CIFilter(name:"CIGaussianBlur")!
filter.setDefaults()
filter.setValue(resultClamp, forKey: kCIInputImageKey)
filter.setValue(blurRadius, forKey: kCIInputRadiusKey)

let ciContext = CIContext(options: nil)
let result = filter.valueForKey(kCIOutputImageKey) as! CIImage!
let cgImage = ciContext.createCGImage(result, fromRect: ciimage.extent) // changed to ciiimage.extend

let finalImage = UIImage(CGImage: cgImage)

let blurImageView = UIImageView(frame: view.frame)
blurImageView.image = finalImage
blurImageView.sizeToFit()
blurImageView.contentMode = .ScaleAspectFit
blurImageView.center = view.center
view.addSubview(blurImageView)

How do I blur a CGImage, or UIImageView

You probably want something like a Gaussian Blur which you can google for loads of information and algorithms. I also found How to implement a box or gaussian blur on iphone post for you which has some tricks for the iPhone.

Fast blur on iOS

I would recommend Brad Larson's GPUImage which is fully backed by the GPU for a wide variety of image processing effects. It's very fast, and even fast enough that in his demo app he does real-time video processing from the camera and the frame-rate is excellent.

https://github.com/BradLarson/GPUImage

Here is a code snippet I wrote to apply a basic box blur which blurs the bottom and top thirds of the image but leaves the middle of the image un-blurred. His library is extremely extensive and contains almost every kind of image filter effect imaginable.

GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:[self screenshot]];

GPUImageTiltShiftFilter *boxBlur = [[GPUImageTiltShiftFilter alloc] init];
boxBlur.blurSize = 0.5;

[stillImageSource addTarget:boxBlur];

[stillImageSource processImage];

UIImage *processedImage = [stillImageSource imageFromCurrentlyProcessedOutput];

How to create ios blur effect with gles

Runtime blurring requires sampling input with a kernel, and averaging the samples. This is expensive, and grows more and more expensive with the size of the blur kernel. Gaussian filter with OpenGL Shaders explains how to implement this in OpenGL ES.

However, for efficiency, blurred shadows for things such as message boxes are done offline, because their shape does not change dynamically (even though their scale may). So, you would create an image (offline) that contains the blurred message box, and render a primitive underneath your message box geometry, with blurred image mapped on it.



Related Topics



Leave a reply



Submit