Search code examples
swiftxcode9core-imagecifiltercikernel

Custom Filter of Core Image and "sig abrt" in Xcode 9.x


I'm writing a code. Here is my full source code.

I confirmed that my code runs successfully in Xcode 8.3.3.

However, my code crashed in Xcode 9.x (9.1 and 9.2).

Exactly, my code crashed in "filter.setValue(inputCIImage, forKey: kCIInputImageKey)" with "NSException (sig abrt)"

I did not find the cause even though I used the debugger.

result of Xcode 8.3.3

import UIKit
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!

let inputImage = UIImage(named: "testImage.png")

override func viewDidLoad() {
    super.viewDidLoad()
    let inputCIImage = CIImage(image: inputImage!)

    let filter = CustomFilter()
    filter.setValue(inputCIImage, forKey: kCIInputImageKey)
    let outputImage = filter.outputImage!
    let context = CIContext()
    let outputCGImage = context.createCGImage(outputImage, from: outputImage.extent)
    imageView.image = UIImage(cgImage: outputCGImage!)
}
}

class CustomFilter: CIFilter {
var inputImage: CIImage?

override public var outputImage: CIImage! {
    get {
        if let inputImage = self.inputImage {
            let args = [inputImage as AnyObject]
            return createCustomKernel().apply(extent: inputImage.extent, arguments: args)
        } else {
            return nil
        }
    }
}

func createCustomKernel() -> CIColorKernel {
    let kernelString =
        "kernel vec4 chromaKey(__sample s){\n" +
            "vec4 newPixel = s.rgba;\n" +
            "newPixel[0] = 0.0;\n" +
            "newPixel[2] = newPixel[2] / 2.0;\n" +
            "return newPixel;\n" + "}"
    return CIColorKernel(source: kernelString)!
}
}

Solution

  • The only way I've been able to use setValue(forKey:) is to actually register my custom filter. Granted, I've only been doing this in a Swift 4 app, but I'm rather surprised this worked in Swift 3.

    Keep in mind, while `setValue(forKey:) is syntactically valid - meaning it will build - it's when CoreImage tried to execute your filter that any errors will arise.

    I think you have two options:

    (1) The easy one is to change how you pass the input image in (this is how I've done things since Swift 2). Try replacing that line with this:

    filter.inputImage = inputCIImage
    

    (2) Register your filter. In your CustomFilter class, add this:

    var inputImage: CIImage!
    
    override var attributes: [String : Any] {
        return [
            kCIAttributeFilterDisplayName: "My custom filter",
    
            "inputImage": [kCIAttributeIdentity: 0,
                           kCIAttributeClass: "CIImage",
                           kCIAttributeDisplayName: "Image",
                           kCIAttributeType: kCIAttributeTypeImage]
        ]
    }
    

    Next, subclass CIFilterConstructor:

    class CustomFilters: NSObject, CIFilterConstructor {
        static func registerFilters() {
            CIFilter.registerName(
                "My custom filter",
                constructor: CustomFilters(),
                classAttributes: [
                    kCIAttributeFilterCategories: [CategoryCustom]
                ])
        }
        func filter(withName name: String) -> CIFilter? {
            switch name {
            case "My custom filter":
                return CustomFilter()
            default:
                return nil
            }
        }
    }
    

    Finally, in your app code, do these two things:

    (A) Register your filter. I typically do this in viewDidLoad:

    CustomFilters.RegisterFilters()
    

    (B) Instantiate your filter, calling it by name:

    let filter = CIFilter(name: "My custom filter")