Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get CIColorCube Filter Working In Swift

I am trying to get the CIColorCube filter working. However the Apple documents only provide a poorly explained reference example here:

// Allocate memory
const unsigned int size = 64;
float *cubeData = (float *)malloc (size * size * size * sizeof (float) * 4);
float rgb[3], hsv[3], *c = cubeData;

// Populate cube with a simple gradient going from 0 to 1
for (int z = 0; z < size; z++){
    rgb[2] = ((double)z)/(size-1); // Blue value
    for (int y = 0; y < size; y++){
        rgb[1] = ((double)y)/(size-1); // Green value
        for (int x = 0; x < size; x ++){
            rgb[0] = ((double)x)/(size-1); // Red value
            // Convert RGB to HSV
            // You can find publicly available rgbToHSV functions on the Internet
            rgbToHSV(rgb, hsv);
            // Use the hue value to determine which to make transparent
            // The minimum and maximum hue angle depends on
            // the color you want to remove
            float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f: 1.0f;
            // Calculate premultiplied alpha values for the cube
            c[0] = rgb[0] * alpha;
            c[1] = rgb[1] * alpha;
            c[2] = rgb[2] * alpha;
            c[3] = alpha;
            c += 4; // advance our pointer into memory for the next color value
        }
    }
}
// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
                       length:cubeDataSize
                       freeWhenDone:YES];
CIColorCube *colorCube = [CIFilter filterWithName:@"CIColorCube"];
[colorCube setValue:@(size) forKey:@"inputCubeDimension"];
// Set data for cube
[colorCube setValue:data forKey:@"inputCubeData"];

So I have attempted to translate this over to Swift with the following:

var filter = CIFilter(name: "CIColorCube")
    filter.setValue(ciImage, forKey: kCIInputImageKey)
    filter.setDefaults()

    var size: UInt = 64
    var floatSize = UInt(sizeof(Float))
    var cubeDataSize:size_t = size * size * size * floatSize * 4
    var colorCubeData:Array<Float> = [
    0,0,0,1,
    0,0,0,1,
    0,0,0,1,
    0,0,0,1,
    0,0,0,1,
    0,0,0,1,
    0,0,0,1,
    0,0,0,1
    ]

    var cubeData:NSData = NSData(bytesNoCopy: colorCubeData, length: cubeDataSize)

However I get an error when trying to create the cube data:

"Extra argument 'bytesNoCopy' in call"

Basically I am creating the cubeData wrong. Can you advise me on how to properly create the cubeData object in Swift?

Thanks!

like image 335
Aggressor Avatar asked Dec 29 '14 19:12

Aggressor


2 Answers

Looks like you are after the chroma key filter recipe described here. Here's some code that works. You get a filter for the color you want to make transparent, described by its HSV angle:

func RGBtoHSV(r : Float, g : Float, b : Float) -> (h : Float, s : Float, v : Float) {
    var h : CGFloat = 0
    var s : CGFloat = 0
    var v : CGFloat = 0
    let col = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1.0)
    col.getHue(&h, saturation: &s, brightness: &v, alpha: nil)
    return (Float(h), Float(s), Float(v))
}

func colorCubeFilterForChromaKey(hueAngle: Float) -> CIFilter {

    let hueRange: Float = 60 // degrees size pie shape that we want to replace
    let minHueAngle: Float = (hueAngle - hueRange/2.0) / 360
    let maxHueAngle: Float = (hueAngle + hueRange/2.0) / 360

    let size = 64
    var cubeData = [Float](repeating: 0, count: size * size * size * 4)
    var rgb: [Float] = [0, 0, 0]
    var hsv: (h : Float, s : Float, v : Float)
    var offset = 0

    for z in 0 ..< size {
        rgb[2] = Float(z) / Float(size) // blue value
        for y in 0 ..< size {
            rgb[1] = Float(y) / Float(size) // green value
            for x in 0 ..< size {

                rgb[0] = Float(x) / Float(size) // red value
                hsv = RGBtoHSV(r: rgb[0], g: rgb[1], b: rgb[2])
                // the condition checking hsv.s may need to be removed for your use-case
                let alpha: Float = (hsv.h > minHueAngle && hsv.h < maxHueAngle && hsv.s > 0.5) ? 0 : 1.0 

                cubeData[offset] = rgb[0] * alpha
                cubeData[offset + 1] = rgb[1] * alpha
                cubeData[offset + 2] = rgb[2] * alpha
                cubeData[offset + 3] = alpha
                offset += 4
            }
        }
    }
    let b = cubeData.withUnsafeBufferPointer { Data(buffer: $0) }
    let data = b as NSData

    let colorCube = CIFilter(name: "CIColorCube", withInputParameters: [
        "inputCubeDimension": size,
        "inputCubeData": data
    ])
    return colorCube!
}

Then to get your filter call

let chromaKeyFilter = colorCubeFilterForChromaKey(hueAngle: 120)

I used 120 for your standard green screen.

like image 140
inorganik Avatar answered Sep 19 '22 07:09

inorganik


I believe you want to use NSData(bytes: UnsafePointer<Void>, length: Int) instead of NSData(bytesNoCopy: UnsafeMutablePointer<Void>, length: Int). Make that change and calculate the length in the following way and you should be up and running.

let colorCubeData: [Float] = [
    0, 0, 0, 1,
    1, 0, 0, 1,
    0, 1, 0, 1,
    1, 1, 0, 1,
    0, 0, 1, 1,
    1, 0, 1, 1,
    0, 1, 1, 1,
    1, 1, 1, 1
]
let cubeData = NSData(bytes: colorCubeData, length: colorCubeData.count * sizeof(Float))
like image 43
embinder Avatar answered Sep 20 '22 07:09

embinder