I'm building a photo editor and to keep a good performance I filter a small version of the image first and when the user wants to export it, then I filter the higher resolution image.
I'm using CIGaussianBlur filter but I can't achieve same results for different images resolutions.
This is my code:
class ViewController : UIViewController {
var originalImage = UIImage()
var previewImageView = UIImageView()
var previewCIImage: CIImage!
var scaleFactor = CGFloat()
let blurFilter = CIFilter.gaussianBlur()
var blurSlider = UISlider()
var blurRadius = Float()
override func viewDidLoad() {
previewImageView.image = originalImage.scalePreservingAspectRatio(targetSize: previewImageView.frame.size)
previewCIImage = CIImage(image: previewImageView.image!)
// Get the scale factor
scaleFactor = originalImage.getScaleFactor(targetSize: previewImageView.frame.size)
blurSlider.addTarget(self, action: #selector(blurChanged(slider:)), for: .valueChanged)
}
@objc func blurChanged(slider: UISlider) {
blurRadius = slider.value
let scaledRadius = blurRadius * Float(scaleFactor)
blurFilter.radius = scaledRadius
MTKView.setNeedsDisplay()
}
func exportFullSizeImage() -> UIImage {
let inputImage = CIImage(image: originalImage)!
blurFilter.inputImage = inputImage.clampedToExtent()
// Assuming scaleFactor is 1.0 for the unscaled image
let scaledRadius = blurRadius * 1.0
blurFilter.radius = scaledRadius
let output = (blurFilter.outputImage)!
let outputCGImage = context.createCGImage(output, from: output.extent)
return UIImage(cgImage: outputCGImage!)
}
}
extension UIImage {
func scalePreservingAspectRatio(targetSize: CGSize) -> UIImage {
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let scaleFactor = min(widthRatio, heightRatio)
let scaledImageSize = CGSize(
width: size.width * scaleFactor,
height: size.height * scaleFactor
)
let renderer = UIGraphicsImageRenderer(
size: scaledImageSize
)
let scaledImage = renderer.image { _ in
self.draw(in: CGRect(
origin: .zero,
size: scaledImageSize
))
}
return scaledImage
}
func getScaleFactor(targetSize: CGSize) -> CGFloat {
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let scaleFactor = min(widthRatio, heightRatio)
return scaleFactor
}
}
Here's the output of the small version of the image (preview image):
And here's the output of the full size image (unscaled image):
The results are clearly different, the full size/unscaled image has more blur. I need to achieve the same blur effect on both images.
I've found two similar questions: Output of CIFilter has different effect for different sizes of same image and How to achieve same CIFilter effect on multiple sizes of same image
I know the scale factor of the resized image, that's maybe useful to get an answer.
The parameter scaling from the linked answer should work for all images, regardless of their aspect ratio. The important part is that you apply the scale factor to both images, the preview and the export.
Alternatively, since you have the scale factor of the resized image, you can use that to scale the parameter (instead of using the image size):
// assuming scaleFactor is 1.0 for the unscaled image
let scaledRadius = radius * scaleFactor
filter.setValue(scaledRadius, forKey: "inputRadius")
Please also note that not every parameter of every filter needs scaling to achieve consistency across different image sizes. Usually, only parameters that describe some kind of effect radius or size need scaling.