CIRadialGradient reduces image size

Viewed 574

After applying CIRadialGradient to my image it gets reduced in width by about 20%.

guard let image = bgImage.image, let cgimg = image.cgImage else {
    print("imageView doesn't have an image!")
    return
}
let coreImage = CIImage(cgImage:cgimg)

guard let radialMask = CIFilter(name:"CIRadialGradient") else {
    return
}

guard let maskedVariableBlur = CIFilter(name:"CIMaskedVariableBlur") else {
    print("CIMaskedVariableBlur does not exist")
    return 
}
maskedVariableBlur.setValue(coreImage, forKey: kCIInputImageKey)

maskedVariableBlur.setValue(radialMask.outputImage, forKey: "inputMask")
guard let selectivelyFocusedCIImage = maskedVariableBlur.outputImage else {
    print("Setting maskedVariableBlur failed")
    return
}


bgImage.image = UIImage(ciImage: selectivelyFocusedCIImage)

To clarify, bgImage is a UIImageView.

Why does this happen and how do I fix it?

Without RadialMask:

enter image description here

With RadialMask:

enter image description here

With the difference that on my physical iPhone the smaller image is aligned to the left.

3 Answers

I tend to explicitly state how big the image is by using a CIContext and creating a specifically sized CGImage instead of simply using UIImage(ciImage:). Try this, assuming your inputImage is called coreGraphics:

let ciCtx = CIContext()
let cgiig = ctx.createCGImage(selectivelyFocusedCIImage, from: coreImage.extent)
let uiImage = UIImage(cgImage: cgIMG!)

A few notes....

(1) I pulled this code out from an app I'm wrapping up. This is untested code (including the forced-unwrap), but the concept of what I'm doing is solid.

(2) You don't explain a lot of what you are trying to do, but when I see a variable named selectivelyFocusedCIImage I get concerned that you may be trying to use CoreImage in a more interactive way than "just" creating one image. If you want "near real-time" performance, render the CIImage in either a (deprecated as of iOS 12) GLKView or an MTKView instead of a UIImageView. The latter only uses the CPU where the two former use the GPU.

(3) Finally, a word of warning on CIContexts - they are expensive to create! Usually you can code it such that there's only one context that can be shared by everything n your app.

Look up the documentation, it's a mask that being applied to the image: enter image description here

Docs: CIRadialGradient

The different sizes are caused by the kernel size of the blur filter:

The blur filter needs to sample a region around each pixel. Since there are no pixels beyond the image bounds, Core Image reduces the extend of the result image by half the kernel size (blur radius) to signal that for those pixels there is not enough information for a proper blur.

However, you can tell Core Image to treat the border pixels as extending infinitely in all directions so that the blur filter gets enough information even on the edges of the image. Afterwards you can crop the result back to the original dimension.

In your code, just change the following two lines:

maskedVariableBlur.setValue(coreImage.clampedToExtent(), forKey: kCIInputImageKey)

bgImage.image = UIImage(ciImage: selectivelyFocusedCIImage.cropped(to:coreImage.extend))
Related