Home > Software engineering >  How to remove blur effect from image in iOS swift
How to remove blur effect from image in iOS swift

Time:09-13

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.

CodePudding user response:

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.

  • Related