2017-09-22 4 views
1

Comment sous-classez-vous CIFilter maintenant? En Swift 3 que je pouvais faire cela comme un exemple simple:Swift 4, sous-classe CIFilter se bloque uniquement avec des variables d'instance "entrée"

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

Mais Swift 4 je reçois un NSException. Si je supprime "entrée" de chaque variable cela fonctionne bien. Je pourrais juste faire ça. Mais j'ai l'impression qu'il me manque quelque chose d'important et je n'arrive pas à trouver quoi que ce soit expliquant ce comportement.

Cette compile bien dans Swift4:

class CustomFilter: CIFilter { 
    var image: CIImage? 
    var origin: CIVector? 
    var anotherVar: String? 
} 

est ici l'erreur dans un terrain de jeu:

enter image description here

+0

Il me semble qu'il vous manque du code. Pourquoi exactement 'inputImage' est-il un' CIVector' et * pas * une sorte d'image (probablement 'CIImage')? Et si ce n'est pas le problème, peut-être pourriez-vous fournir plus de code sur ce qui fait 'CustomFilter'? – dfd

+0

Merci d'avoir jeté un coup d'oeil! Vous avez raison, le nom était trompeur. Mais j'ai réédité la question pour, espérons-le, clarifier ce que je veux dire. –

+0

Je suis peut-être un peu confus, mais j'utilise * un "CIFilter" ou un "CIKernel" de trois façons - dont une seule nécessite le sous-classement 'CIFilter'. J'ai vérifié mon code (Swift 3 et 4) et - par exemple - 'public var inputImage: CIImage?' Les deux compilations et fonctionne très bien pour moi. Au-delà de déclarer ma classe «publique» (cela fait partie d'une cible-cadre), je ne vois pas pourquoi vous avez un problème. Obtenez-vous une erreur de construction? Une erreur d'exécution? – dfd

Répondre

1

Sur la base des commentaires, voici un code Swift 4 (inchangé par rapport à Swift 3) qui construit et exécute à la fois. Je ne vois pas où votre problème est, donc si cela ne vous aide pas, commentez-le et je vais le supprimer. (Si elle ne aide, je vais modifier ma réponse pour être plus précis!)

Le premier CIFilter utilise CIColorInvert et CIHeightFieldFromMask pour créer un « masque de texte » basé sur le texte dans un UILabel. Il a également overrides la propriété outputImage de CIFilter. La deuxième CIFilter est en fait un «wrapper» autour d'un CIKernel, en utilisant un CIImage comme inputImage, le masque (du premier filtre) comme inputMask et remplace également outputImage comme le premier fait.

Pratiquement tout ce code a été levé de Core Image for Swift par Simon Gladman, aujourd'hui disponible comme un iBook gratuitement. Alors que le livre a été écrit dans Swift 2, je trouve que c'est une ressource précieuse pour travailler avec Core Image.

(Note:. Le livre combine tout cela je partage des choses que je travaillais sur l'adaptation cela dans une application existante en filigrane j'ai fini par un itinéraire différent.!)

Mask. rapide

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);" + 
     "}" 
    ) 
} 

Utilisation

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 

Hope this helps!

3

J'ai rencontré ce problème (même "erreur: l'exécution a été interrompue, raison: EXC_BAD_INSTRUCTION ...") dans Swift 4 en expérimentant avec "Core Image for Swift" de Simon Gladman. J'ai également essayé d'exécuter du code exemple dans une application au lieu d'un terrain de jeu.La solution pour moi était d'ajouter un @objc dynamic devant var inputImage: CIImage? Dans votre code, il ressemblerait à ceci:

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

Si je comprends bien, c'est parce que Swift 4 par défaut minimise l'inférence de manière à réduire la taille du code binaire . En revanche, Swift 3 déduit implicitement les attributs Objc. Ce que cela signifie en pratique, c'est que je dois ajouter le @objc dynamic à certaines variables qui tireront parti de la répartition dynamique d'Objective-C, comme lors de la définition d'un filtre CoreImage: filter.setValue(inputImage, forKey: kCIInputImageKey). Voici quelques ressources qui décrivent des problèmes similaires avec Swift's static dispatch using Swift4 when dealing with Obj-C API's that rely on dynamic dispatch et comment gérer le dispatch lorsque vous migrate from Swift 3 to 4.

+0

J'ai dû mettre '@objc dynamic' devant chaque variable pour qu'il soit compilé dans une cour de récréation. Dans une application, je n'ai même pas besoin d'utiliser '@objc dynamic' .... bizarre. –