objective c - Cultivo correcto de CIGaussianBlur
objective-c macos (7)
Aquí está la versión Swift:
func applyBlurEffect(image: UIImage) -> UIImage {
let context = CIContext(options: nil)
let imageToBlur = CIImage(image: image)
let blurfilter = CIFilter(name: "CIGaussianBlur")
blurfilter!.setValue(imageToBlur, forKey: "inputImage")
blurfilter!.setValue(5.0, forKey: "inputRadius")
let resultImage = blurfilter!.valueForKey("outputImage") as! CIImage
let cgImage = context.createCGImage(resultImage, fromRect: resultImage.extent)
let blurredImage = UIImage(CGImage: cgImage)
return blurredImage
}
Como noté cuando se aplica CIGaussianBlur a la imagen, las esquinas de la imagen se vuelven borrosas para que parezcan más pequeñas que las originales. Así que me di cuenta de que necesitaba recortarlo correctamente para evitar tener bordes transparentes de la imagen. Pero, ¿cómo calcular cuánto necesito recortar en función de la cantidad de desenfoque?
Ejemplo:
Imagen original:
Imagen con 50 entradasRadio de CIGaussianBlur (el color azul es el fondo de todo):
Esto funciona para mi :)
CIContext *context = [CIContext contextWithOptions:nil];
CIImage *inputImage = [[CIImage alloc] initWithImage:image];
CIFilter *blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
[blurFilter setDefaults];
[blurFilter setValue:inputImage forKey:@"inputImage"];
CGFloat blurLevel = 20.0f; // Set blur level
[blurFilter setValue:[NSNumber numberWithFloat:blurLevel] forKey:@"inputRadius"]; // set value for blur level
CIImage *outputImage = [blurFilter valueForKey:@"outputImage"];
CGRect rect = inputImage.extent; // Create Rect
rect.origin.x += blurLevel; // and set custom params
rect.origin.y += blurLevel; //
rect.size.height -= blurLevel*2.0f; //
rect.size.width -= blurLevel*2.0f; //
CGImageRef cgImage = [context createCGImage:outputImage fromRect:rect]; // Then apply new rect
imageView.image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
Hay dos cuestiones. La primera es que el filtro de desenfoque muestrea píxeles fuera de los bordes de la imagen de entrada. Estos píxeles son transparentes. De ahí vienen los píxeles transparentes. El truco es extender los bordes antes de aplicar el filtro de desenfoque. Esto se puede hacer mediante un filtro de pinza, por ejemplo, de esta manera:
CIFilter *affineClampFilter = [CIFilter filterWithName:@"CIAffineClamp"];
CGAffineTransform xform = CGAffineTransformMakeScale(1.0, 1.0);
[affineClampFilter setValue:[NSValue valueWithBytes:&xform
objCType:@encode(CGAffineTransform)]
forKey:@"inputTransform"];
Este filtro extiende los bordes infinitamente y elimina la transparencia. El siguiente paso sería aplicar el filtro de desenfoque.
El segundo tema es un poco raro. Algunos renderizadores producen una imagen de salida más grande para el filtro de desenfoque y debe adaptar el origen del mensaje CII resultante con algún desplazamiento, por ejemplo, como este
CGImageRef cgImage = [context createCGImage:outputImage
fromRect:CGRectOffset([inputImage extend],
offset, offset)];
El procesador de software en mi iPhone necesita tres veces el radio de desenfoque como compensación. El renderizador de hardware en el mismo iPhone no necesita ningún tipo de compensación. Maybee podría deducir el desplazamiento de la diferencia de tamaño de las imágenes de entrada y salida, pero no lo intenté ...
Intente esto, deje que la extensión de la entrada sea -createCGImage:fromRect:
''s parámetro:
-(UIImage *)gaussianBlurImageWithRadius:(CGFloat)radius {
CIContext *context = [CIContext contextWithOptions:nil];
CIImage *input = [CIImage imageWithCGImage:self.CGImage];
CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
[filter setValue:input forKey:kCIInputImageKey];
[filter setValue:@(radius) forKey:kCIInputRadiusKey];
CIImage *output = [filter valueForKey:kCIOutputImageKey];
CGImageRef imgRef = [context createCGImage:output
fromRect:input.extent];
UIImage *outImage = [UIImage imageWithCGImage:imgRef
scale:UIScreen.mainScreen.scale
orientation:UIImageOrientationUp];
CGImageRelease(imgRef);
return outImage;
}
Para obtener una buena versión borrosa de una imagen con bordes duros, primero debe aplicar un CIAffineClamp a la imagen de origen, extendiendo sus bordes hacia afuera y luego debe asegurarse de que utiliza las extensiones de la imagen de entrada al generar la imagen de salida.
El código es el siguiente:
CIContext *context = [CIContext contextWithOptions:nil];
UIImage *image = [UIImage imageNamed:@"Flower"];
CIImage *inputImage = [[CIImage alloc] initWithImage:image];
CIFilter *clampFilter = [CIFilter filterWithName:@"CIAffineClamp"];
[clampFilter setDefaults];
[clampFilter setValue:inputImage forKey:kCIInputImageKey];
CIFilter *blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
[blurFilter setValue:clampFilter.outputImage forKey:kCIInputImageKey];
[blurFilter setValue:@10.0f forKey:@"inputRadius"];
CIImage *result = [blurFilter valueForKey:kCIOutputImageKey];
CGImageRef cgImage = [context createCGImage:result fromRect:[inputImage extent]];
UIImage result = [[UIImage alloc] initWithCGImage:cgImage scale:image.scale orientation:UIImageOrientationUp];
CGImageRelease(cgImage);
Tenga en cuenta que este código fue probado en iOS. Debería ser similar para OS X (sustituyendo NSImage por UIImage).
Tome el siguiente código como ejemplo ...
CIContext *context = [CIContext contextWithOptions:nil];
CIImage *inputImage = [[CIImage alloc] initWithImage:image];
CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
[filter setValue:inputImage forKey:kCIInputImageKey];
[filter setValue:[NSNumber numberWithFloat:5.0f] forKey:@"inputRadius"];
CIImage *result = [filter valueForKey:kCIOutputImageKey];
CGImageRef cgImage = [context createCGImage:result fromRect:[result extent]];
Esto da como resultado las imágenes que proporcionó anteriormente. Pero si, en cambio, utilizo las imágenes originales para crear la imagen CG fuera del contexto, la imagen resultante tiene el tamaño deseado.
CGImageRef cgImage = [context createCGImage:result fromRect:[inputImage extent]];
Vea a continuación dos implementaciones para Xamarin (C #).
1) Funciona para iOS 6
public static UIImage Blur(UIImage image)
{
using(var blur = new CIGaussianBlur())
{
blur.Image = new CIImage(image);
blur.Radius = 6.5f;
using(CIImage output = blur.OutputImage)
using(CIContext context = CIContext.FromOptions(null))
using(CGImage cgimage = context.CreateCGImage (output, new RectangleF(0, 0, image.Size.Width, image.Size.Height)))
{
return UIImage.FromImage(cgimage);
}
}
}
2) Implementación para iOS 7
Usar la forma que se muestra arriba ya no funciona correctamente en iOS 7 (al menos en este momento con Xamarin 7.0.1). Así que decidí agregar el recorte de otra manera (las medidas pueden depender del radio de desenfoque).
private static UIImage BlurImage(UIImage image)
{
using(var blur = new CIGaussianBlur())
{
blur.Image = new CIImage(image);
blur.Radius = 6.5f;
using(CIImage output = blur.OutputImage)
using(CIContext context = CIContext.FromOptions(null))
using(CGImage cgimage = context.CreateCGImage (output, new RectangleF(0, 0, image.Size.Width, image.Size.Height)))
{
return UIImage.FromImage(Crop(CIImage.FromCGImage(cgimage), image.Size.Width, image.Size.Height));
}
}
}
private static CIImage Crop(CIImage image, float width, float height)
{
var crop = new CICrop
{
Image = image,
Rectangle = new CIVector(10, 10, width - 20, height - 20)
};
return crop.OutputImage;
}