Подтвердить что ты не робот

Правильный урожай CIGaussianBlur

Как я заметил, когда CIGaussianBlur применяется к изображению, углы изображения становятся размытыми, так что он выглядит как меньше оригинала. Поэтому я понял, что мне нужно правильно обрезать его, чтобы не было прозрачных краев изображения. Но как рассчитать, сколько мне нужно обрезать в зависимости от размера размытия?


Пример:

Исходное изображение:
enter image description here

Изображение с 50 inputRadius из CIGaussianBlur (синий цвет - фон всего):
enter image description here

4b9b3361

Ответ 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)

}