Swift 4,子类化 CIFilter 仅在 "input" 个实例变量时崩溃

Swift 4, Subclassing CIFilter crashes only with "input" instance variables

你现在如何子类化 CIFilter?在 Swift 3 中,我可以将其作为一个简单的示例:

class CustomFilter: CIFilter {
   var inputImage: CIImage?
   var inputOrigin: CIVector?
   var inputAnotherVar: String?
}

但是在 Swift 4 中我得到一个 NSException。如果我从每个变量中删除“输入”,它就可以正常工作。我可以那样做。但我觉得我遗漏了一些重要的东西,而且我似乎找不到任何可以解释这种行为的东西。

这在 Swift4:

中编译得很好
class CustomFilter: CIFilter {
   var image: CIImage?
   var origin: CIVector?
   var anotherVar: String?
}

这是 Playground 中的错误:

根据评论,这里有一些构建和执行的 Swift 4 代码(与 Swift 3 相同)。我没有看到您的问题出在哪里,所以如果这对您没有帮助,请发表评论,我会删除它。 (如果确实有帮助,我将编辑我的答案以使其更具体!)

第一个 CIFilter 使用 CIColorInvertCIHeightFieldFromMask 根据 UILabel 中的文本创建 "text mask"。它还 overrides outputImage 属性 的 CIFilter。第二个 CIFilter 实际上是围绕 CIKernel 的 "wrapper",使用 CIImage 作为 inputImage,掩码(来自第一个过滤器)作为 inputMask 并且也像第一个一样覆盖 outputImage

几乎所有这些代码都是由 Simon Gladman 从 Core Image for Swift 中提取的,现在可以作为 iBook 免费使用。虽然这本书是在 Swift 2 中编写的,但我发现它是使用 Core Image 的宝贵资源。

(旁注:这本书结合了所有这些。我在努力将其作为水印改编到现有应用程序中时将其拆分。我最终走了一条不同的路!)

Mask.swift

public class Mask: CIFilter {
    public var inputExtent:CGRect?
    var inputRadius: Float = 15 {
        didSet {
            if oldValue != inputRadius {
                refractingImage = nil
            }
        }
    }
    private var refractingImage: CIImage?
    private var rawTextImage: CIImage?

    override public var outputImage: CIImage! {
        if refractingImage == nil {
            generateRefractingImage()
        }
        let mask = refractingImage?.applyingFilter("CIColorInvert", parameters: [:])
        return mask
    }

    func generateRefractingImage() {
        let label = UILabel(frame: inputExtent!)
        label.text = "grand canyon"
        label.font = UIFont.boldSystemFont(ofSize: 300)
        label.adjustsFontSizeToFitWidth = true
        label.textColor = UIColor.white

        UIGraphicsBeginImageContextWithOptions(
            CGSize(width: label.frame.width,
                   height: label.frame.height), true, 1)
        label.layer.render(in: UIGraphicsGetCurrentContext()!)
        let textImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        rawTextImage = CIImage(image: textImage!)!
        refractingImage = CIFilter(name: "CIHeightFieldFromMask",
                                   withInputParameters: [
                                    kCIInputRadiusKey: inputRadius,
                                    kCIInputImageKey: rawTextImage!])?.outputImage?
            .cropped(to: inputExtent!)
    }
}

Refraction.swift

public class Refraction: CIFilter {
    public var inputImage: CIImage?
    public var inputMask:CIImage?

    var inputRefractiveIndex: Float = 4.0
    var inputLensScale: Float = 50
    public var inputLightingAmount: Float = 1.5

    var inputLensBlur: CGFloat = 0
    public var inputBackgroundBlur: CGFloat = 2

    var inputRadius: Float = 15

    override public func setDefaults()
    {
        inputRefractiveIndex = 4.0
        inputLensScale = 50
        inputLightingAmount = 1.5
        inputRadius = 15
        inputLensBlur = 0
        inputBackgroundBlur = 2
    }

    override public var outputImage: CIImage! {
        guard let inputImage = inputImage, let refractingKernel = refractingKernel else {
            return nil
        }

        let extent = inputImage.extent
        let arguments = [inputImage,
                         inputMask!,
                         inputRefractiveIndex,
                         inputLensScale,
                         inputLightingAmount] as [Any]
        return refractingKernel.apply(extent: extent,
                                      roiCallback: {
                                        (index, rect) in
                                        return rect
        },
                                      arguments: arguments)!
    }

    let refractingKernel = CIKernel(source:
        "float lumaAtOffset(sampler source, vec2 origin, vec2 offset)" +
            "{" +
            " vec3 pixel = sample(source, samplerTransform(source, origin + offset)).rgb;" +
            " float luma = dot(pixel, vec3(0.2126, 0.7152, 0.0722));" +
            " return luma;" +
            "}" +


            "kernel vec4 lumaBasedRefract(sampler image, sampler refractingImage, float refractiveIndex, float lensScale, float lightingAmount) \n" +
            "{ " +
            " vec2 d = destCoord();" +

            " float northLuma = lumaAtOffset(refractingImage, d, vec2(0.0, -1.0));" +
            " float southLuma = lumaAtOffset(refractingImage, d, vec2(0.0, 1.0));" +
            " float westLuma = lumaAtOffset(refractingImage, d, vec2(-1.0, 0.0));" +
            " float eastLuma = lumaAtOffset(refractingImage, d, vec2(1.0, 0.0));" +

            " vec3 lensNormal = normalize(vec3((eastLuma - westLuma), (southLuma - northLuma), 1.0));" +

            " vec3 refractVector = refract(vec3(0.0, 0.0, 1.0), lensNormal, refractiveIndex) * lensScale; " +

            " vec3 outputPixel = sample(image, samplerTransform(image, d + refractVector.xy)).rgb;" +

            " outputPixel += (northLuma - southLuma) * lightingAmount ;" +
            " outputPixel += (eastLuma - westLuma) * lightingAmount ;" +

            " return vec4(outputPixel, 1.0);" +
        "}"
    )
}

用法

let filterMask = Mask()
let filter = Refraction()
var imgOriginal:CIImage!
var imgMask:CIImage!
var imgEdited:CIImage!

// I have a set of sliders that update a tuple and send an action that executes the following code

filterMask.inputRadius = sliders.valuePCP.3
imgMask = filterMask.outputImage
filter.inputMask = imgMask
filter.inputRefractiveIndex = sliders.valuePCP.0
filter.inputLensScale = sliders.valuePCP.1
filter.inputLightingAmount = sliders.valuePCP.2
imgEdited = filter.outputImage

希望对您有所帮助!

我 运行 在 Swift 4 中对这个问题(相同 "error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION...")进行了试验,同时尝试了 Simon Gladman 的 "Core Image for Swift." 我还尝试了 运行 中的示例代码一个应用程序而不是游乐场。我的解决方案是在 var inputImage: CIImage? 前面添加一个 @objc dynamic 在您的代码中它看起来像这样:

class CustomFilter: CIFilter {
    @objc dynamic var inputImage: CIImage?
    var inputOrigin: CIVector?
    var inputAnotherVar: String?
}

据我了解,这是因为 Swift 4 默认情况下会最小化推理以减少二进制代码大小。相反,Swift 3 隐式推断 Objc 属性。这在实践中意味着我必须将 @objc dynamic 添加到某些变量中,这些变量将利用 Objective-C 的动态调度,例如在设置 CoreImage 过滤器时:filter.setValue(inputImage, forKey: kCIInputImageKey)。以下是描述与 and how to deal with dispatch when you migrate from Swift 3 to 4.

类似问题的一些资源