[Résolu] Peut-on écrire son kernel CIFilter sous iOS?

HerveHerve Membre
septembre 2016 modifié dans API UIKit #1

Bonjour,


 


J'ai essayé d'adapter à  iOS mes classes de création personnelle de filtres CIFilter. Je ne trouve pas les bonnes méthodes.


Est-ce possible aujourd'hui d'écrire ses propres filtres CIFilter (ce qui, d'après certaines forums était impossible avant), ou est-ce que je fais fausse route?


 


Si c'est possible, quel code permet d'utiliser le .kernel avec le filtre? Comment écrit-on le constructeur, et la méthode "outputImage" en clair? (Le compilateur rejette mon code MacOS)


 


Apparemment, je fais fausse route, mais sait-on jamais? Merci par avance pour vos posts.


 


Réponses

  • CéroceCéroce Membre, Modérateur

    D'après ce que je vois, CIKernel existe depuis iOS 8. Ce qui correspond à  mon souvenir: dans le temps, ce n'était pas possible, mais dans les versions récentes d'iOS, ça l'est.


     


    Voir la rubrique "Creating Custom Filters" de Core Image Programming Guide.


  • HerveHerve Membre
    septembre 2016 modifié #3

    Merci Céroce.


    Mais les méthodes suivantes posent problème (voir commentaires):



    NSBundle *bundle = [NSBundle bundleForClass: [self class]];
    NSString *code = [NSString stringWithContentsOfFile: [bundle
    pathForResource: @KernelFiltre
    ofType: @cikernel]];
    ​//le compilateur dit que cette méthode est dépréciée depuis iOS2!!
    NSArray *kernels = [CIKernel kernelsWithString: code];

    Celle-ci est rejetée tout simplement :



    CISampler *src = [CISampler samplerWithImage: inputImage];

    return [self apply: hazeRemovalKernel, src, couleurA, couleurB, couleurC, mixVal, fadeVal, transpVal, kCIApplyOptionDefinition, [src definition], nil];//Là , le compilateur rejette la méthode, inconnue de iOS dit-il


    le compilateur refuse "apply".


     


    Qu'utilisez-vous pour :


    - bien faire prendre en compte votre kernel par le CIFilter?


    - en sortir l'image traitée?


     


    Merci par avance.


  • CéroceCéroce Membre, Modérateur
    septembre 2016 modifié #4

    Pour le premier warning, il faut juste utiliser la méthode de NSString qui va bien (par ex. +stringWithContentsOfFile:encoding:error).


     


     


    Alors, du coup j'ai cherché... et la doc concerne OS X, sans avoir été mise à  jour pour iOS, on dirait.


     


    C'est expliqué dans la session 515 de la WWDC 2014.


    La transcription:


    http://asciiwwdc.com/2014/sessions/515


     


    Les diapos:


    http://devstreaming.apple.com/videos/wwdc/2014/515xxv01d9tcg3o/515/515_developing_core_image_filters_for_ios.pdf


     


    Sur une des diapos, on a cet extrait de code: (dans une sous-classe de CIFilter)



    - (CIKernel *) myKernel { 
    static CIColorKernel *kernel = nil;   
    static dispatch_once_t once;  
    dispatch_once(&once, ^{  
    kernel = [CIColorKernel kernelWithString: 
    @kernel vec4 swapRedAndGreenAmount ( __sample s, float amount )  
    { return mix(s.rgba, s.grba, amount); }" ]; 
    });
    return kernel;  
    }
     
    - (CIImage *)outputImage   { 
    CGRect dod = inputImage.extent ; 
    return [ [self myKernel] applyWithExtent : dod  arguments : @[;inputImage, inputAmount]]; 
    }

  • Merci Céroce pour tes posts toujours hyper bien informés!


     


    J'ai un problème pour le CIKernel : je préfèrerais utiliser un fichier .cikernel comme d'habitude.


    En plus, lorsque je copie mon .cikernel dans la méthode "kernelWithString:", le compilateur "croit" que la string s'arrête à  la première accolade (après la parenthèse contenant les paramètres), et ignore donc le code (le code cesse d'être écrit en rouge après l'accolade). En fait, il rejette l'ensemble par conséquent.


     


    Je progresse mais n'y suis pas encore...


  • Bon, le compilateur met un warning pour "NSString stringWithContentOfFile" ou "initWithContentOfFile", mais cela a l'air de marcher quand même...


     


    Par contre, le filtre ne marche pas. j'ai ceci dans la console (le code n'est pas exécuté) :



    2016-09-02 13:36:36.067 TestBitmapPad[734:88583] Note: CIColorKernel applyWithExtent:roiCallback:arguments: ignores callback and is not recomended. Use applyWithExtent:arguments: instead.

    or la méthode sans roiCallback est inconnue du compilateur. 


     


    ma méthode :



    - (CIImage *)outputImage {
    CGRect dod = inputImage.extent ;
    CIKernelROICallback callback = ^(int index, CGRect rect) {
    return CGRectInset(dod, -1.0, -1.0);
    };

    return [ [self myKernel] applyWithExtent: dod roiCallback:callback arguments : [NSArray arrayWithObjects: inputImage, couleurA, couleurB, couleurC, mixVal, fadeVal,transpVal,nil]];
    }

    Je ne sais pas ce que veut dire cet accent circonflexe devant les arguments de "CIKernelROICallback". Le savez-vous? (encore une subtilité du C que j'ignore sans doute...)


  • CéroceCéroce Membre, Modérateur


    Je ne sais pas ce que veut dire cet accent circonflexe devant les arguments de "CIKernelROICallback". Le savez-vous? (encore une subtilité du C que j'ignore sans doute...)




     


    Ben c'est une closure (un "block"), quoi. C'est normal, c'est une callback, le code sera exécuté plus tard.

  • HerveHerve Membre
    septembre 2016 modifié #8

    Oui, j'ai lu la doc, mais ne l'ai que moyennement comprise... Du coup j'oubliais de recopier les "dispatch_once_t", que j'ai fini par recopier bêtement, je l'avoue.


     


    Mon filtre marche :



    static CIKernel *kernel = nil;
    static dispatch_once_t once;
    NSBundle *bundle = [NSBundle bundleForClass: [self class]];
    NSString *code = [[NSString alloc]initWithContentsOfFile: [bundle
    pathForResource: @KernelFiltre
    ofType: @cikernel]];
    //le compilateur n'est toujours pas content, mais la doc de NSString n'indique aucune dépréciation de la méthode.
    dispatch_once(&once, ^{kernel = [CIKernel kernelWithString:code];});

    return kernel;

    Par rapport à  MacOS, on simplifie beaucoup. On ne garde que deux méthodes, celle du CIKernel, et celle de imageOutput. Bon, on va pouvoir s'amuser!!


     


    Merci infiniment Céroce!


     


    (Comment met-on "résolu" dans le titre du sujet déjà ?)


  • HerveHerve Membre
    septembre 2016 modifié #9

    Entre nous soit dit, le pps/pdf Apple mis en lien par Céroce est passionnant, je vous le conseille vivement. Je le conserve dans mes docs perso.


     


    Je suis en train de faire une routine de type valeurs rgb > hsb car je travaille à  partir du hsb.


    Deux questions : 


    - je n'ai pas vu dans la doc OpenGL de méthode de conversion automatique. Y en t-il une?


    - sinon, est-il possible d'utiliser à  chaque fois le même fichier .cikernel dans différents filtres, en chaà®nant par exemple : kernelRgbToHsb > kernelMonEffet > kernel HsbToRgb dans un CIFilter, ou bien en appelant des CIFilter "conversion" dans un CIFilter?? Cela me semble être hasardeux... Sans doute vaut-il mieux recopier le code d'un .cikernel à  l'autre?


     


    PS : la doc précitée m'apporte un début de réponse avec l'exemple du kaléidoscope. Ceci dit, il me faudrait les valeurs hsb de l'image entrante et de une ou deux CIColors avant l'effet...


  • CéroceCéroce Membre, Modérateur

    - je n'ai pas vu dans la doc OpenGL de méthode de conversion automatique. Y en t-il une?

    Il me semble qu'OpenGL fonctionne uniquement en RGB. Et GLSL est un langage assez simple; il n'y a pas de bibliothèque standard. C'est donc ton code qui doit implémenter la conversion. (Mais bon, cherche sur le web, tu vas le trouver tout fait).

    - sinon, est-il possible d'utiliser à  chaque fois le même fichier .cikernel dans différents filtres, en chaà®nant par exemple : kernelRgbToHsb > kernelMonEffet > kernel HsbToRgb dans un CIFilter

    Le code que tu mets dans ton fichier .cikernel " qui est un simple fichier texte " est compilé à  la volée.
    Tu peux:
    - créer une nouvelle instance de CIKernel avec le même code
    - sans doute partager l'instance de CIKernel (ce qui évite de compiler à  nouveau)

    Dans l'univers GLSL, certains vont jusqu'à  assembler plusieurs fichiers texte pour créer le shader final. ça évite le code conditionnel " les conditions rendent le code GPU très lent.



  • Dans l'univers GLSL, certains vont jusqu'à  assembler plusieurs fichiers texte pour créer le shader final. ça évite le code conditionnel " les conditions rendent le code GPU très lent.




    Effectivement, la conversion suppose de multiples comparaisons. La doc CIKernel explique que les if, etc. peuvent ne pas être pris en compte :



     


     


    Other flow control statements (if, for, while, do while) are supported only when the loop condition can be inferred at the time the code compiles.

    J'ai l'impression que si je veux travailler en hsb, j'ai intérêt à  passer par CGImage, en utilisant par exemple ton cours "BitmapEffets" et en le complétant... Comme cela, on code en C, ce qui sera peut-être aussi bien. 


     


    Je porte sous iOS mon soft MacOS "Harmony of Colors", qui utilisait NSBitmapImageRep et les valeurs hsb de NSColor. Je gagnerai du temps en restant en C je pense... (même si j'aime beaucoup les filtres CoreImage).


     


    Code source de conversion, ou plutôt formules à  employer ici :


    http://www.rapidtables.com/convert/color/rgb-to-hsv.htm

  • CéroceCéroce Membre, Modérateur
    septembre 2016 modifié #12
    Je ne comprends pas trop tes propos. De toute façon, Core Graphics ne gère que des espaces colorimétriques RGB, gris et CMYK (sauf sous iOS).
    On ne peut donc pas lire ou écrire directement dans la bitmap en HSL, et tu devais sans doute déjà  travailler en RVB. On peut créer une UIColor avec des composants HSL, comme pour NSColor.

    Core Image a ses défauts:
    - ne peut pas travailler sur un thread secondaire (à  cause d'OpenGL).
    - lent.

    Mais il a deux grandes qualités:
    - relativement facile à  mettre en oe“uvre
    - les effets de base sont de qualité
    - GLSL est mieux adapté au traitement d'image que le C.

    Si tu ne fais que des effets à  l'écran, qui ne sont pas trop lourds, alors Core Image s'impose.


  •  


    (Comment met-on "résolu" dans le titre du sujet déjà ?)




     


     


    Tu édites ton premier post avec l'éditeur complet et çà  te permet de modifier le titre...

  •  

    Je suis finalement en train de bosser à  partir de CGImage. Je n'ai pas testé, mais vu le code de hsb2rgb qui utilise un switch, j'ai préféré renoncer pour ce projet à  CIFilter. Les premiers tests sont corrects, il y a peu de traitement d'images à  appliquer, juste une modification des couleurs, et le résultat est immédiat. 


     


    Je garde mon test CIImage par contre, il me servira je n'en doute pas. Merci beaucoup en tous les cas.


     


    Un code C "qui marche" pour rgb > et l'inverse (en C++) :


    http://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both


    if it can help...


     


    Merci à  tous.


  • CéroceCéroce Membre, Modérateur
    septembre 2016 modifié #15
    Quand on code en GLSL, il faut penser différemment du C, parce que oui, les branchements conditionnels sont lents, mais par contre, un tas d'opérations d'algèbre linéaire sont câblées (une seule instruction). On peut souvent remplacer un if par la fonction min() ou max(), un switch par une table de correspondance, etc.

    On trouve du code tout fait:
    http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
    Justement sans branchement.

    Pour ceux que ça intéresse en dehors de moi, jetez un oe“il sur ce que certains parviennent à  faire avec seulement des fragment shaders:
    https://www.shadertoy.com
    (il n'y a pas de polygones envoyés à  la GPU, et tous les calculs sont effectués par la GPU).

    Je ne cherche pas à  te convaincre que c'est la bonne solution dans ton cas, seulement qu'il y a un univers entier à  explorer !
Connectez-vous ou Inscrivez-vous pour répondre.