2

What I Have:

Referencing Apple's Chroma Key Code, it states that we can create a Chroma Key Filter Cube via

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))
            }
        }
    }

    let data = Data(buffer: UnsafeBufferPointer(start: &cubeRGB, count: cubeRGB.count))

    // 5
    let colorCubeFilter = CIFilter(name: "CIColorCube", withInputParameters: ["inputCubeDimension": size, "inputCubeData": data])
    return colorCubeFilter
}

I then created a function to be able to insert any image into this filter and return the filtered image.

public func filteredImage(ciimage: CIImage) -> CIImage? {
    let filter = chromaKeyFilter(fromHue: 110/360, toHue: 130/360)! //green screen effect colors
    filter.setValue(ciimage, forKey: kCIInputImageKey)
    return RealtimeDepthMaskViewController.filter.outputImage
}

I can then execute this function on any image and obtain a chroma key'd image.

if let maskedImage = filteredImage(ciimage: ciimage) {
    //Do something
}
else {
    print("Not filtered image")
}

Update Issues:

let data = Data(buffer: UnsafeBufferPointer(start: &cubeRGB, count: cubeRGB.count))

However, once I updated Xcode to v11.6, I obtain the warning Initialization of 'UnsafeBufferPointer<Float>' results in a dangling buffer pointer as well as a runtime error Thread 1: EXC_BAD_ACCESS (code=1, address=0x13c600020) on the line of code above.

I tried addressing this issue with this answer to correct Swift's new UnsafeBufferPointer warning. The warning is then corrected and I no longer have a runtime error.

Problem

Now, although the warning doesn't appear and I don't experience a runtime error, I still get the print statement Not filtered image. I assume that the issue stems from the way the data is being handled, or deleted, not entirely sure how to correctly handle UnsafeBufferPointers alongside Data.

What is the appropriate way to correctly obtain the Data for the Chroma Key?

impression7vx
  • 1,728
  • 1
  • 20
  • 50

1 Answers1

5

I wasn't sure what RealtimeDepthMaskViewController was in this context, so just returned the filter output instead. Apologies if this was meant to be left as-is. Also added a guard statement with the possibility of returning nil - which matches your optional return type for the function.

public func filteredImage(ciImage: CIImage) -> CIImage? {
    guard let filter = chromaKeyFilter(fromHue: 110/360, toHue: 130/360) else { return nil }
    filter.setValue(ciImage, forKey: "inputImage")
    return filter.outputImage // instead of RealtimeDepthMaskViewController.filter.outputImage
}

For the dangling pointer compiler warning, I found a couple approaches:

// approach #1
var data = Data()
cubeRGB.withUnsafeBufferPointer { ptr in
    data = Data(buffer: ptr)
}

// approach #2
let byteCount = MemoryLayout<Float>.size * cubeRGB.count
let data = Data(bytes: &cubeRGB, count: byteCount)

One caveat: looked at this with Xcode 11.6 rather than 11.5

Cœur
  • 37,241
  • 25
  • 195
  • 267
lentil
  • 664
  • 5
  • 5
  • I ended up coming up with the solution prior to your answer and completely forgot about the post! However, I'll give it to you since you did answer :) Couple things that were different for me however; `var data = Data()` needs to be of type `[UInt8]`. `data.append(contentsOf: $0)` instead of `data = Data(buffer: ptr)` Lastly, the `CIFilter` needs to take in `NSData` so you need to do something like `let finalData = NSData(bytes: data, length: data.count`. Thanks for answering, though! – impression7vx Aug 05 '20 at 01:40
  • This answer is so helpful @lentil. But do you have any idea why the process will block the main thread? Until the final image is returned, all UI activities on the main thread are frozen. – Umit Kaya Apr 26 '21 at 08:56
  • 1
    Hi @UmitKaya. Glad you found this useful! On your thread question - the original post doesn't specify the context of where the method is called, but if you call the method from main it would block until return. Consider invoking the method in some sort of callback - or submit the work to a dispatch queue. – lentil Apr 26 '21 at 14:32
  • Seems, CoreML is using GPU on main thread. Thats a heavy work which causes main thread to freeze for a second or two. They previously discussed about this here as well: https://developer.apple.com/forums/thread/92996 Anyway, i was able to move CoreML to background thread and sync to main thread after result is returned. Thanks for your advices. – Umit Kaya Apr 26 '21 at 16:46