iosswiftciimagegaussianblur

How to remove blur effect from image in iOS swift


I have an image on which I applied blur effect using the following code:

@IBAction func blurSliderSlides(_ sender: UISlider) {
    
    let currentValue = Int(sender.value)

    let currentFilter = CIFilter(name: "CIGaussianBlur")
    currentFilter!.setValue(CIImage(image: mainImage), forKey: kCIInputImageKey)
    currentFilter!.setValue(currentValue, forKey: kCIInputRadiusKey)

    let cropFilter = CIFilter(name: "CICrop")
    cropFilter!.setValue(currentFilter!.outputImage, forKey: kCIInputImageKey)
    cropFilter!.setValue(CIVector(cgRect: (CIImage(image: mainImage)?.extent)!), forKey: "inputRectangle")

    let output = cropFilter!.outputImage
    let cgimg = context.createCGImage(output!, from: output!.extent)
    processedImage = UIImage(cgImage: cgimg!)
    backgroundImage.image = processedImage
}

Now I have three buttons square circle and rectangle. when ever use tap any of button that type of view is created on mid of blurred image and user can make that view large or small using slider. but all I want when subview is added of any shape the background image should be unblur from that point where view is created.

subview created on background image:

@IBAction func squareButtonTapped(_ sender: Any) {
 

    squareView.removeFromSuperview()
    squareView.frame = CGRect(x: backgroundImage.bounds.midX, y: backgroundImage.bounds.midY, width: CGFloat(backgroundImage.frame.size.height * 20 / 100), height: CGFloat(backgroundImage.frame.size.width * 10 / 100))

    backgroundImage.addSubview(squareView)
}

using slider subview can be changed:

@IBAction func blurTypeSliderSlides(_ sender: UISlider) {

    squareView.transform = CGAffineTransform(scaleX: CGFloat(sender.value / 10), y: CGFloat(sender.value / 10))
}

how to remove blur effect from background image at point where subview is added. I have searched a lot but nothing find helpful as my requirement. can someone please help. Thanks in advance.


Solution

  • It looks like you have a UIImage and a UIImageView declared as a property of your controller... and in viewDidLoad() you're loading that image, something like:

    mainImage = UIImage(named: "background")
    

    I'm guessing that, because in your func blurSliderSlides(_ sender: UISlider) you have this line:

    currentFilter!.setValue(CIImage(image: mainImage), forKey: kCIInputImageKey)
    

    and at the end:

    backgroundImage.image = processedImage
    

    So, when you add the "shape" subview, set the .image to the original:

    @IBAction func squareButtonTapped(_ sender: Any) {
     
        squareView.removeFromSuperview()
        squareView.frame = CGRect(x: backgroundImage.bounds.midX, y: backgroundImage.bounds.midY, width: CGFloat(backgroundImage.frame.size.height * 20 / 100), height: CGFloat(backgroundImage.frame.size.width * 10 / 100))
    
        backgroundImage.addSubview(squareView)
    
        // replace the "blur" image with the original
        backgroundImage.image = mainImage
    
    }
    

    Edit - after clarification in comments...

    You don't want to think in terms of "adding subviews."

    Instead, use two image views... one containing the original image, and another containing the blurred image, overlaid on top. Then use a layer mask (with a "hole cut") on the blurred image view to let the original "show through."

    So,

    enter image description here

    And it can look like this at run-time:

    enter image description here

    enter image description here

    enter image description here

    Here is some example code you can try out. It has one slider which controls the "cut-out oval" as a percentage of the image view width:

    class BlurMaskVC: UIViewController {
        
        var mainImage: UIImage!
        var originalImageView: UIImageView!
        var blurredImageView: UIImageView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // make sure we can load the image
            guard let img = UIImage(named: "bkg640x360") else {
                fatalError("Could not load image!!!")
            }
            mainImage = img
            
            originalImageView = UIImageView()
            blurredImageView = UIImageView()
            originalImageView.image = mainImage
            blurredImageView.image = mainImage
            
            originalImageView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(originalImageView)
            blurredImageView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(blurredImageView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                
                // constrain original image view Top / Leading / Trailing
                originalImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
                originalImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
                originalImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
                // let's use the image's aspect ratio
                originalImageView.heightAnchor.constraint(equalTo: originalImageView.widthAnchor, multiplier: mainImage.size.height / mainImage.size.width),
                
                // constrain blurred image view to match the original image view
                //  so it's overlaid directly on top
                blurredImageView.topAnchor.constraint(equalTo: originalImageView.topAnchor, constant: 0.0),
                blurredImageView.leadingAnchor.constraint(equalTo: originalImageView.leadingAnchor, constant: 0.0),
                blurredImageView.trailingAnchor.constraint(equalTo: originalImageView.trailingAnchor, constant: 0.0),
                blurredImageView.bottomAnchor.constraint(equalTo: originalImageView.bottomAnchor, constant: 0.0),
                
            ])
        
            // a slider to set the "percentage" of the image view to "un-blur"
            let areaSlider = UISlider()
            areaSlider.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(areaSlider)
            NSLayoutConstraint.activate([
                areaSlider.topAnchor.constraint(equalTo: originalImageView.bottomAnchor, constant: 20.0),
                areaSlider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                areaSlider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            ])
    
            areaSlider.addTarget(self, action: #selector(updateBlurMask(_:)), for: .valueChanged)
            
            // since this example does not have a "blur" slider,
            //  let's set the blur to 20
            doBlur(20)
            
        }
        
        func doBlur(_ currentValue: Int) {
            
            let context = CIContext(options: nil)
            guard let inputImage = CIImage(image: mainImage) else {
                fatalError("Could not get CIImage from mainImage!!!")
            }
            let originalOrientation = mainImage.imageOrientation
            let originalScale = mainImage.scale
            
            if let filter = CIFilter(name: "CIGaussianBlur") {
                filter.setValue(inputImage, forKey: kCIInputImageKey)
                filter.setValue(currentValue, forKey: kCIInputRadiusKey)
    
                guard let outputImage = filter.outputImage,
                      let cgImage = context.createCGImage(outputImage, from: inputImage.extent)
                else {
                    fatalError("Could not generate Processed Image!!!")
                }
                let processedImage = UIImage(cgImage: cgImage, scale: originalScale, orientation: originalOrientation)
                blurredImageView.image = processedImage
            }
            
        }
        
        @objc func updateBlurMask(_ sender: UISlider) {
            
            let b: CGRect = blurredImageView.bounds
            
            // let's make a "square" rect with the max of blurImageView's width, height
            let m: CGFloat = max(b.width, b.height)
            let maxR: CGRect = CGRect(x: 0.0, y: 0.0, width: m, height: m)
    
            // use the value of the slider - 0.0. to 1.0
            //  as a percentage of the width
            //  to scale the max rect
            let v: CGFloat = CGFloat(sender.value)
            let tr = CGAffineTransform(scaleX: v, y: v)
            var r: CGRect = maxR.applying(tr)
    
            // center it
            r.origin.x = (b.width - r.width) * 0.5
            r.origin.y = (b.height - r.height) * 0.5
    
            // a path for the full image view rect
            let fullPath = UIBezierPath(rect: blurredImageView.bounds)
            // a path for the oval in a percentage of the full rect
            let pth = UIBezierPath(ovalIn: r)
            // append the paths
            fullPath.append(pth)
            // this "cuts a hole" in the path
            fullPath.usesEvenOddFillRule = true
    
            // shape layer to use as a mask
            let maskLayer = CAShapeLayer()
            // again, we're going to "cut a hole" in the path
            maskLayer.fillRule = .evenOdd
            // set the path
            maskLayer.path = fullPath.cgPath
            // can be any opaque color
            maskLayer.fillColor = UIColor.white.cgColor
            // set the layer mask
            blurredImageView.layer.mask = maskLayer
            
        }
        
    }
    

    You should have no trouble using the updateBlurMask() func as a basis for your other shapes.