9

I'm using the following code to remove green background from an image.But the edges of the image has green tint to it and some pixels are damaged.How can i smoothen this and make the cut out perfect.

 func chromaKeyFilter(fromHue: CGFloat, toHue: CGFloat) -> CIFilter?
    {
        // 1
        let size = 64
        var cubeRGB = [Float]()

        // 2
        for z in 0 ..< size {
            let blue = CGFloat(z) / CGFloat(size-1)
            for y in 0 ..< size {
                let green = CGFloat(y) / CGFloat(size-1)
                for x in 0 ..< size {
                    let red = CGFloat(x) / CGFloat(size-1)

                    // 3
                    let hue = getHue(red: red, green: green, blue: blue)
                    let alpha: CGFloat = (hue >= fromHue && hue <= toHue) ? 0: 1

                    // 4
                    cubeRGB.append(Float(red * alpha))
                    cubeRGB.append(Float(green * alpha))
                    cubeRGB.append(Float(blue * alpha))
                    cubeRGB.append(Float(alpha))
                }
            }
        }



  @IBAction func clicked(_ sender: Any) {
          let a = URL(fileURLWithPath:"green.png")
          let b = URL(fileURLWithPath:"back.jpg")

        let image1 = CIImage(contentsOf: a)
        let image2 = CIImage(contentsOf: b)





        let chromaCIFilter = self.chromaKeyFilter(fromHue: 0.3, toHue: 0.4)
        chromaCIFilter?.setValue(image1, forKey: kCIInputImageKey)
        let sourceCIImageWithoutBackground = chromaCIFilter?.outputImage

        /*let compositor = CIFilter(name:"CISourceOverCompositing")
        compositor?.setValue(sourceCIImageWithoutBackground, forKey: kCIInputImageKey)
        compositor?.setValue(image2, forKey: kCIInputBackgroundImageKey)
        let compositedCIImage = compositor?.outputImage*/

        var rep: NSCIImageRep = NSCIImageRep(ciImage: sourceCIImageWithoutBackground!)
        var nsImage: NSImage = NSImage(size: rep.size)
        nsImage.addRepresentation(rep)

        let url = URL(fileURLWithPath:"file.png")

        nsImage.pngWrite(to: url)
        super.viewDidLoad()
    }

Input:

enter image description here

Output: enter image description here

Update: enter image description here

Update 2: enter image description here

Update 3: enter image description here

techno
  • 6,100
  • 16
  • 86
  • 192
  • Why did you comment out that code? I see that it is the default code from Apple's example, https://developer.apple.com/documentation/coreimage/applying_a_chroma_key_effect, so what made you change their example if it works? – impression7vx Nov 16 '18 at 03:18
  • @impression7vx As far as i understand that piece of code replaced the background image.But i just need to remove the background.The apple sample code is not for `macOS` , i have used CIImage while the OSX sample uses UIImage. – techno Nov 16 '18 at 03:22
  • Ahh i see. I was just curious. – impression7vx Nov 16 '18 at 03:22
  • Hey @techno, did you solve your problem? I am trying to modify the default sample app from apples doc, but I am facing some issues with removing the color. Would you be so kind and would check my question? Thanks a lot dude https://stackoverflow.com/questions/74230729/chromakey-filter-not-filtering-desired-color-ciimage – Jason Krowl Oct 28 '22 at 15:12

2 Answers2

8

Professional tools for chroma keying usually include what's called a spill suppressor. A spill suppressor finds pixels that contain small amounts of the chroma key color and shifts the color in the opposite direction. So green pixels will move towards magenta. This reduces the green fringing you often see around keyed footage.

The pixels you call damaged are just pixels that had some level of the chroma color in them and are being picked up by your keyer function. Rather than choosing a hard 0 or 1, you might consider a function that returns a value between 0 and 1 based on the color of the pixel. For example, you could find the angular distance of the current pixel's hue to the fromHue and toHue and maybe do something like this:

// Get the distance from the edges of the range, and convert to be between 0 and 1
var distance: CGFloat
if (fromHue <= hue) && (hue <= toHue) {
    distance = min(abs(hue - fromHue), abs(hue - toHue)) / ((toHue - fromHue) / 2.0)
} else {
    distance = 0.0
}
distance = 1.0 - distance
let alpha = sin(.pi * distance - .pi / 2.0) * 0.5 + 0.5

That will give you a smooth variation from the edges of the range to the center of the range. (Note that I've left off dealing with the fact that hue wraps around at 360°. That's something you'll have to handle.) The graph of the falloff looks like this:

A sine function scaled and offset so that it smoothly goes from 0 to 1 over the range 0 to 1

Another thing you can do is limit the keying to only affect pixels where the saturation is above some threshold and the value is above some threshold. For very dark and/or unsaturated colors, you probably don't want to key it out. I think that would help with the issues you're seeing with the model's jacket, for example.

user1118321
  • 25,567
  • 4
  • 55
  • 86
  • seems complex... im just a beginner in swift.. How can i implement this in my code? – techno Nov 16 '18 at 03:45
  • I think he told you .. right after your `let hue = ...` add his code – impression7vx Nov 16 '18 at 03:58
  • Yes, sorry, that's exactly what I meant. Note that this was written off the top of my head, so might need some cleanup to have valid syntax. – user1118321 Nov 16 '18 at 05:09
  • @user1118321 Your code produces wrong results.Please see the update. – techno Nov 18 '18 at 04:08
  • Sorry about that! I forgot to invert the `distance` before calculating the `alpha`. I think I also reversed the `fromHue` and `toHue` in the denominator of the `distance` calculation. I've updated the code. Give it a try and let me know if it works. – user1118321 Nov 18 '18 at 16:19
  • This also results in problems.Please see the update. – techno Nov 19 '18 at 04:25
  • That's actually looking pretty good. Now you just need to adjust the curve so that it takes out the green completely. You could try something like having an and inner from and to value, where everything between those values is full transparent, but between the inner and outer "to" and the inner and outer "from" you use the smooth falloff shown above. – user1118321 Nov 19 '18 at 04:29
  • Since you're using .3-.4 for your range of hue, you probably actually want .333... to be in the center of the range for this. Try making your range be .3 to .366.... Regardless, having a wide inner area that's fully transparent and a smooth fall-off at the edges is still a good idea. – user1118321 Nov 19 '18 at 04:50
  • This also does not solve the core problem i have mentioned.Please see the update. – techno Nov 19 '18 at 12:21
  • Not 100% related to this question. But I am facing issue with the default sample implementation that you find on apple website. Would you be so kind and check my question @user1118321 , please? https://stackoverflow.com/questions/74230729/chromakey-filter-not-filtering-desired-color-ciimage – Jason Krowl Oct 28 '22 at 14:54
3

My (live) keyer works like this (with the enhancements user1118321 describes) and using its analyser I quickly noticed this is most likely not a true green screen image. It's one of many fake ones where the green screen seems to have been replaced with a saturated monochrome green. Though this may look nice, it introduces artefacts where the keyed subject (with fringes of the originally used green) meets the monochrome green. You can see a single green was used by looking at the histogram. Real green screen always have (in)visible shades of green. I was able to get a decent key but had to manually tweak some settings. With a NLE you can probably get much better results, but they will also be a lot more complex. So to get back to your issue, your code probably works as it is now (update #3), you just have to use a proper real life green screen image. enter image description here

Mike Versteeg
  • 1,615
  • 14
  • 28
  • hmm... but applications like PhotoKey8 works fine with the image i have tested. – techno Nov 19 '18 at 17:10
  • If your code does not have to be fast then you either have to buy the technology (the good ones are patented btw) or invest a few years of your life. It's a fascinating technology so enjoy! – Mike Versteeg Nov 20 '18 at 10:30
  • @Summit I use two: one works in YUV domain and one in RGB, they complement each other. The idea is to suppress the key colour in the foreground and at the mask border. The real trick is to automate this so it works on most images and, in my case where live video is involved, make it fast. There's enough public information, like research papers, on the internet to learn this wonderful technology. – Mike Versteeg Aug 01 '20 at 10:19
  • @Summit if your suppressor doesn't work then maybe your keyer doesn't work properly? Sorry but I cannot share my code with you, it is part of my commercial product. – Mike Versteeg Aug 01 '20 at 13:39