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
使用 CIColorInvert
和 CIHeightFieldFromMask
根据 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.
类似问题的一些资源
你现在如何子类化 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
使用 CIColorInvert
和 CIHeightFieldFromMask
根据 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)
。以下是描述与