Search code examples
iosswiftcore-graphicscore-imagecifilter

How to change Brightness, Contrast and Saturation using CIColorControls via UISlider with Swift


I'm working on a Photo Filter App and as you can see I added a feature to adjust contrast, brightness, saturation, and noise. But the problem is they work independently, which means when I'm adjusting let's say brightness as soon as I start editing contrast, it returns to the original brightness.

Interface

Here is a preview when I'm putting the brightness to the maximum (the image gets white) and then trying to adjust its contrast and the slider changes the contrast of an original image.

Filters work independetly!

Here I was able to catch the moment of releasing the slider and putting its value on the original image and as you can see it works, on a demo I put saturation 0 and then change the contrast of the same saturated to 0 picture.

Works!.

The problem is that now when I just click on the slider without changing its value it doubled the current value. For example, if I put the brightness 5 and the put saturation 10 and decided to adjust the brightness as soon as I click on brightness it is doubling its value I don't know why. Here is an example. I'm just clicking on slider without changing its value.

enter image description here

And here is the code

  @objc func sliderValueDidChange(_ sender: UISlider!) {

    if sender.tag == 0 {

        let displayinPercentage: Int = Int((sender.value/200) * 10000)
        brightnessValueLabel.text = ("\(displayinPercentage)")
        selectedPictureImageView.image = originalImage
        let beginImage = CIImage(image: selectedPictureImageView.image!)
        self.filter = CIFilter(name: "CIColorControls")
        self.filter?.setValue(beginImage, forKey: kCIInputImageKey)
        self.filter.setValue(sender.value, forKey: kCIInputBrightnessKey)
        self.filteredImage = self.filter?.outputImage
        selectedPictureImageView.image = UIImage(cgImage: self.context.createCGImage(self.filteredImage!, from: (self.filteredImage?.extent)!)!)
        sliderValue = sender.value


    } else if sender.tag == 1 {

        let displayinPercentage: Int = Int((sender.value/200) * 10000)
        contrastValueLabel.text = ("\(displayinPercentage)")
        self.selectedPictureImageView.image = originalImage
        let beginImage = CIImage(image: self.selectedPictureImageView.image!)
        self.filter = CIFilter(name: "CIColorControls")
        self.filter?.setValue(beginImage, forKey: kCIInputImageKey)
        self.filter.setValue(sender.value, forKey: kCIInputContrastKey)
        self.filteredImage = self.filter?.outputImage
        self.selectedPictureImageView.image = UIImage(cgImage: self.context.createCGImage(self.filteredImage!, from: (self.filteredImage?.extent)!)!)
        sliderValue = sender.value

    } else if sender.tag == 2 {

        let displayinPercentage: Int = Int((sender.value/200) * 10000)
        saturationValueLabel.text = ("\(displayinPercentage)")
        self.selectedPictureImageView.image = originalImage
        let beginImage = CIImage(image: self.selectedPictureImageView.image!)
        self.filter = CIFilter(name: "CIColorControls")
        self.filter?.setValue(beginImage, forKey: kCIInputImageKey)
        self.filter.setValue(sender.value, forKey: kCIInputSaturationKey)
        self.filteredImage = self.filter?.outputImage
        self.selectedPictureImageView.image = UIImage(cgImage: self.context.createCGImage(self.filteredImage!, from: (self.filteredImage?.extent)!)!)
        sliderValue = sender.value

    } else if sender.tag == 3 {

        let displayinPercentage: Int = Int((sender.value/200) * 10000)
        sharpenValueLabel.text = ("\(displayinPercentage)")
        self.selectedPictureImageView.image = originalImage
        let beginImage = CIImage(image: self.selectedPictureImageView.image!)
        self.filter = CIFilter(name: "CIUnsharpMask")
        self.filter?.setValue(beginImage, forKey: kCIInputImageKey)
        self.filter.setValue(7, forKey: kCIInputRadiusKey)
        self.filter.setValue(sender.value, forKey: kCIInputIntensityKey)
        self.filteredImage = self.filter?.outputImage
        self.selectedPictureImageView.image = UIImage(cgImage: self.context.createCGImage(self.filteredImage!, from: (self.filteredImage?.extent)!)!)
        sliderValue = sender.value

    }
}

@objc func sliderValueDidEnd(_ sender: UISlider!) {

    if sender.tag == 0 {
        originalImage = selectedPictureImageView.image
        sender.value = sliderValue
        print("Unpressed button slider value is \(sender.value)")
    } else if sender.tag == 1 {
        originalImage = selectedPictureImageView.image
        sender.value = sliderValue
        print("Unpressed button slider value is \(sender.value)")
    } else if sender.tag == 2 {
        originalImage = selectedPictureImageView.image
        sender.value = sliderValue
        print("Unpressed button slider value is \(sender.value)")
    } else if sender.tag == 3 {
        originalImage = selectedPictureImageView.image
        sender.value = sliderValue
        print("Unpressed button slider value is \(sender.value)")
    }
}

I think that everything is working fine, I just did wrong calculations on filters values.


Solution

  • You problem is that every time you slide the slider you are applying the filter to the resulting image of the previous filter instead of applying it to the original image. Btw you can make your filter a lot more responsive when sliding the UISlider if you just create a CGImage once the user is finished. Your code should look like this:

    let displayinPercentage = Int((sender.value/200) * 10000)
    brightnessValueLabel.text = String(displayinPercentage)
    let beginImage = CIImage(image: originalImage)
    filter.setValue(beginImage, forKey: kCIInputImageKey)
    filter.setValue(sender.value, forKey: kCIInputBrightnessKey)
    if let ciimage = filter.outputImage {
        filteredImage = ciimage
        selectedPictureImageView.image = UIImage(ciImage: filteredImage)
    }