Objective c 正确的CIGaussianBlur作物

Objective c 正确的CIGaussianBlur作物,objective-c,macos,core-image,Objective C,Macos,Core Image,正如我注意到的,当CIGaussianBlur应用于图像时,图像的角变得模糊,看起来比原始图像小。所以我发现我需要正确地裁剪它,以避免图像的透明边缘。但如何根据模糊量计算我需要裁剪多少呢 例如: 原始图像: 50输入的CIGaussianBlur图像(蓝色是一切的背景): 以下面的代码为例 CIContext *context = [CIContext contextWithOptions:nil]; CIImage *inputImage = [[CIImage alloc] initW

正如我注意到的,当CIGaussianBlur应用于图像时,图像的角变得模糊,看起来比原始图像小。所以我发现我需要正确地裁剪它,以避免图像的透明边缘。但如何根据模糊量计算我需要裁剪多少呢


例如:

原始图像:

50输入的CIGaussianBlur图像(蓝色是一切的背景):

以下面的代码为例

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]];

这将生成上面提供的图像。但是如果我改为使用原始图像rect来创建脱离上下文的CGImage,则生成的图像就是所需的大小

CGImageRef cgImage = [context createCGImage:result fromRect:[inputImage extent]];

有两个问题。首先,模糊过滤器对输入图像边缘外的像素进行采样。这些像素是透明的。这就是透明像素的来源。 诀窍是在应用模糊过滤器之前延伸边缘。这可以通过钳形过滤器完成,例如:

CIFilter *affineClampFilter = [CIFilter filterWithName:@"CIAffineClamp"];

CGAffineTransform xform = CGAffineTransformMakeScale(1.0, 1.0);
[affineClampFilter setValue:[NSValue valueWithBytes:&xform
                                           objCType:@encode(CGAffineTransform)]
                     forKey:@"inputTransform"];
CGImageRef cgImage = [context createCGImage:outputImage
                                   fromRect:CGRectOffset([inputImage extend],
                                                         offset, offset)];
此过滤器无限延伸边并消除透明度。下一步是应用模糊过滤器

第二个问题有点奇怪。某些渲染器会为模糊过滤器生成更大的输出图像,您必须通过某些偏移调整结果图像的原点,例如:

CIFilter *affineClampFilter = [CIFilter filterWithName:@"CIAffineClamp"];

CGAffineTransform xform = CGAffineTransformMakeScale(1.0, 1.0);
[affineClampFilter setValue:[NSValue valueWithBytes:&xform
                                           objCType:@encode(CGAffineTransform)]
                     forKey:@"inputTransform"];
CGImageRef cgImage = [context createCGImage:outputImage
                                   fromRect:CGRectOffset([inputImage extend],
                                                         offset, offset)];

iPhone上的软件渲染器需要三倍的模糊半径作为偏移。同一iPhone上的硬件渲染器根本不需要任何偏移。Maybee您可以从输入和输出图像的大小差异中推断偏移量,但我没有尝试…

请参见下面两个Xamarin的实现

1) 适用于iOS 6 2) iOS 7的实施 使用上面所示的方式在iOS 7上不再正常工作(至少在Xamarin 7.0.1上是这样)。所以我决定以另一种方式添加裁剪(度量可能取决于模糊半径)

这对我有用:)


要获得具有硬边的图像的良好模糊版本,首先需要对源图像应用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上测试的。它应该与OSX类似(用NSImage代替UIImage)

以下是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

}

尝试此操作,将输入的范围设为
-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;
}

我看到了一些解决方案,并想根据这里分享的一些想法推荐一个更现代的解决方案:

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
的原因是(如中所述):

由于岩芯图像的坐标系与
UIKit
不匹配,这种过滤方法在带有“contentMode”的
UIImageView
中显示时可能会产生意外的结果。请确保使用
CGImage
对其进行备份,以便它正确处理contentMode


如果你需要一个
CIImage
你可以返回它,但是在这种情况下,如果你正在显示图像,你可能需要小心。

这里是模糊图像的Swift 5版本。将钳制过滤器设置为默认值,这样就不需要进行变换

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)

}

只是顺便说一句,这头狮子是个超级坏蛋。这很好用。我只是想指出,在您的示例中指定的CGAffineTransform如果是过滤器的默认值,那么您可以省略所有代码,或者可能只是调用[affineClampFilter setDefaults],为了获得所需的结果,您需要先应用Clamp filter,然后再应用blur。参见@orjThanks Ben提供的解决方案这是最好的答案问题是针对目标C的-但是嘿…范围从哪里来?我正在当前项目中尝试此解决方案,我用于输入的图像大小为60.33 x 70.0,但生成的范围是149 x 188,这是一个不同的纵横比。也许这与我使用的是方形框架的
UIImageView
有关(使用纵横比拟合),但据我所知,函数对
UIImageView
一无所知。为什么这些纵横比不同?我希望它们都是一样的,也许只是从模糊中延伸出的边缘稍微有点方形。不过,模糊的图像不那么方形。
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)

}