Правильный урожай CIGaussianBlur
Как я заметил, когда CIGaussianBlur применяется к изображению, углы изображения становятся размытыми, так что он выглядит как меньше оригинала. Поэтому я понял, что мне нужно правильно обрезать его, чтобы не было прозрачных краев изображения. Но как рассчитать, сколько мне нужно обрезать в зависимости от размера размытия?
Пример:
Исходное изображение:
![enter image description here]()
Изображение с 50 inputRadius из CIGaussianBlur (синий цвет - фон всего):
![enter image description here]()
Ответы
Ответ 1
В качестве примера возьмем следующий код...
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]];
Это приводит к изображениям, которые вы указали выше. Но если вместо этого использовать исходные изображения для создания CGImage вне контекста, получившееся изображение будет желательным.
CGImageRef cgImage = [context createCGImage:result fromRect:[inputImage extent]];
Ответ 2
Есть два вопроса. Во-первых, фильтр размытия выбирает пиксели за пределами краев входного изображения. Эти пиксели прозрачны. То, откуда появляются прозрачные пиксели.
Хитрость заключается в том, чтобы протянуть края, прежде чем применять фильтр размытия. Это может быть сделано с помощью зажимного фильтра, например. например:
CIFilter *affineClampFilter = [CIFilter filterWithName:@"CIAffineClamp"];
CGAffineTransform xform = CGAffineTransformMakeScale(1.0, 1.0);
[affineClampFilter setValue:[NSValue valueWithBytes:&xform
objCType:@encode(CGAffineTransform)]
forKey:@"inputTransform"];
Этот фильтр бесконечно расширяет края и устраняет прозрачность. Следующим шагом будет применение фильтра размытия.
Вторая проблема немного странная. Некоторые рендереры создают большее выходное изображение для фильтра размытия, и вы должны приспособить источник полученного CIImage к некоторому смещению, например. например:
CGImageRef cgImage = [context createCGImage:outputImage
fromRect:CGRectOffset([inputImage extend],
offset, offset)];
Средство рендеринга программного обеспечения на моем iPhone требует в три раза радиуса размытия как смещения. Средство аппаратного рендеринга на одном и том же iPhone вообще не нуждается в каком-либо смещении. Maybee вы могли бы вывести смещение от разницы в размерах входных и выходных изображений, но я не пробовал...
Ответ 3
Чтобы получить красивую размытую версию изображения с жесткими краями, вам сначала нужно применить CIAffineClamp к исходному изображению, расширяя его края, а затем вам нужно убедиться, что вы используете экстенты входного изображения при создании выходного изображения.
Код выглядит следующим образом:
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);
Обратите внимание, что этот код был протестирован в iOS. Он должен быть аналогичным для OS X (заменяя NSImage для UIImage).
Ответ 4
Это работает для меня:)
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);
Ответ 5
Я видел некоторые из решений и хотел порекомендовать более современное, основанное на некоторых идеях, которыми здесь поделились:
private lazy var coreImageContext = CIContext() // Re-use this.
func blurredImage(image: CIImage, radius: CGFloat) -> CGImage? {
let blurredImage = image
.clampedToExtent()
.applyingFilter(
"CIGaussianBlur",
parameters: [
kCIInputRadiusKey: radius,
]
)
.cropped(to: image.extent)
return coreImageContext.createCGImage(blurredImage, from: blurredImage.extent)
}
Если после этого вам понадобится UIImage
, вы, конечно, можете получить его так:
let image = UIImage(cgImage: cgImage)
... Для тех, кто интересуется, причина возврата CGImage
(как отмечено в документации Apple):
Из-за несоответствия системы координат Core Image с UIKit
, этот подход фильтрации может дать неожиданные результаты при отображении в UIImageView
с "contentMode". Обязательно поддержите его CGImage
чтобы он правильно обрабатывал contentMode.
Если вам нужен CIImage
вы можете вернуть его, но в этом случае, если вы отображаете изображение, вы, вероятно, захотите быть осторожным.
Ответ 6
Ниже приведены две версии для Xamarin (С#).
1) Работает для 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) Реализация для iOS 7
Использование вышеописанного способа не работает должным образом на iOS 7 (по крайней мере в данный момент с Xamarin 7.0.1). Поэтому я решил добавить кадрирование другим способом (меры могут зависеть от радиуса размытия).
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;
}
Ответ 7
Вот версия 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
}
Ответ 8
Попробуйте это, пусть размер ввода будет -createCGImage:fromRect:
:
-(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;
}
Ответ 9
Вот Swift 5 версия размытия изображения. Установите фильтр Clamp на значения по умолчанию, чтобы вам не нужно было выполнять преобразование.
func applyBlurEffect() -> UIImage? {
let context = CIContext(options: nil)
let imageToBlur = CIImage(image: self)
let clampFilter = CIFilter(name: "CIAffineClamp")!
clampFilter.setDefaults()
clampFilter.setValue(imageToBlur, forKey: kCIInputImageKey)
//The CIAffineClamp filter is setting your extent as infinite, which then confounds your context. Try saving off the pre-clamp extent CGRect, and then supplying that to the context initializer.
let inputImageExtent = imageToBlur!.extent
guard let currentFilter = CIFilter(name: "CIGaussianBlur") else {
return nil
}
currentFilter.setValue(clampFilter.outputImage, forKey: kCIInputImageKey)
currentFilter.setValue(10, forKey: "inputRadius")
guard let output = currentFilter.outputImage, let cgimg = context.createCGImage(output, from: inputImageExtent) else {
return nil
}
return UIImage(cgImage: cgimg)
}