Interfacer Quartz Composer et Cocoa

ChachaChacha Membre
04:36 modifié dans API AppKit #1
Salut,

Au vu du beau projet Fenêtres volantes, ce post est un peu destiné à  Bru, Boris ou Vincent. En fait j'aurais aimé quelques éclaircissements sur QuartzComposer avant de me plonger dans la doc qui de loin ne répond pas aux premières questions que je me pose.
Je n'ai jamais vraiment bien compris ce qu'était QuartzComposer, et comment ça se positionnait par rapport à  du code normal. En gros, je vois QuartzComposer comme un LabView destiné à  produire un "visuel", c'est à  dire permettant de créer des sortes de fonction de transfert spécialisées dans un traitement image/vidéo, mais je comprend assez mal comment on s'en sert. En lisant la doc de A à  Z et en faisant quelques essais, j'aurais sans doute la réponse aux questions que je me pose ci-dessous, mais j'aurais juste besoin de quelques éclaircissement très informels pour bien orienter ma lecture.

1)QuartzComposer produit un .qtz, mais comment un .qtz s'intègre-t-il à  MacOS ? J'ai cru comprendre qu'il suffit de positionner un qtz dans une QCView pour le faire tourner, mais y a-t-il d'autres interactions avec Quartz ?
2)Pour ce qui est des interactions, comment fait-on pour connecter les entrées/sorties à  un code Cocoa ? Y a-t-il de la part du .qtz une scrutation continuelle des entrées pour communiquer en temps réel avec ? De quel "type" sont les entrées ? On récupère des NSImage, des CVBuffer ?
3)À l'inverse, peut-on créer facilement ses propres éléments "boà®tes" à  importer dans QuartzComposer ? Quelle forme cela a-t-il d'un point de vue algorithmique (classe, Bundle..., que sais-je.... ?)
4)Tout projet QuartzComposer peut-il être facilement écrit en C/CoreGraphics, ou QuartzComposer apporte-t-il vraiment un plus indéniable ? Je me demande ça parce qu'en regardant le projet "Fenêtres volantes", je trouve que le schéma d'animation est assez compliqué et je me demande si le même code en C/CoreGraphics serait plus simple ?

Le but inavoué de tout cela est de créer une application pour effectuer un certain traitement vidéo. Pour tout ce qui est capture vidéo et traitements standards, j'aurais a priori juste besoin de 1), mais pour insérer un traitement algorithmique ne pouvant être uniquement représenté par des "patchs" QC, j'ai besoin de 2) pour dérouter le flot vers du bon vieux code C. Et si au final ça marche bien, je peux extraire de l'appli la partie traitement pour créer avec 3) un élément QC réutilisable.

+
Chacha

Réponses

  • boris cargoboris cargo Membre
    04:36 modifié #2
    hum... beaucoup de questions. Je connais pas bien les imbrications au niveau technique (développement) mais en je t'engage à  lire ceci :
    http://developer.apple.com/documentation/GraphicsImaging/Conceptual/QuartzComposer/index.html?http://developer.apple.com/documentation/GraphicsImaging/Conceptual/QuartzComposer/qc_intro/chapter_1_section_1.html

    C'est pas très long et suffisament clair pour répondre à  tes interrogations.
    QC fait du compositing temps réel, pour ça il passe par la carte graphique. Pour des gros traitements c'est plus tellement temps réel, ça dépend pas mal de la carte  graphique bien sur.
    L'avantage je pense est de pas avoir à  se palucher la partie graphique, tu te concentre sur ton code et quelq'un d'autre peut intervenir sur le graphisme, sans etre forcement développeur.

    mais comment un .qtz s'intègre-t-il à  MacOS

    Il est intégré en profondeur, par défaut tu peux voir l'économiseur qui affiche le flux rss apple; la fonction diaporama quand tu fais un ctrl+click/diaporama sur un ensemble d'image c'est aussi un qtz, les derniers effects dans Imovie et certainement d'autres dont je ne me souviens plus.

    j'ai cru comprendre qu'il suffit de positionner un qtz dans une QCView pour le faire tourner, mais y a-t-il d'autres interactions avec Quartz ?


    Oui c'est c'est assez simple à  positionner (j'y suis arrivé alors...) après les connections et le traitement logiciel c'est autre chose. Pour les interactions avec Quartz tu peux créer du Kernel Code dans QC, donc du filtrage. Si j'ai bien compris... voir ici:

    http://developer.apple.com/documentation/GraphicsImaging/Conceptual/CoreImaging/ci_custom_filters/chapter_4_section_3.html#//apple_ref/doc/uid/TP30001185-CH207-CJBEDHHH

    tu peux regarder également ici il fait du traitement d'image via QC:
    http://www.samkass.com/blog/

    À l'inverse, peut-on créer facilement ses propres éléments "boà®tes" à  importer dans QuartzComposer ? Quelle forme cela a-t-il d'un point de vue algorithmique (classe, Bundle..., que sais-je.... ?)

    Facilement je sais pas et j'ai pas de lien à  te proposer la maintenant mais oui on peut, j'en vu un ou deux de traitement du signal et un autre qui traitait l'audio.

    Tout projet QuartzComposer peut-il être facilement écrit en C/CoreGraphics, ou QuartzComposer apporte-t-il vraiment un plus indéniable ? Je me demande ça parce qu'en regardant le projet "Fenêtres volantes", je trouve que le schéma d'animation est assez compliqué et je me demande si le même code en C/CoreGraphics serait plus simple ?


    Le plus c'est d'avoir un moteur graphique fonctionnant, bien-sur par code tu aurais plus de souplesse mais aussi plus de boulot il me semble. C'est sur que les soft de composting  fonctionnants avec  des boites et des connections, comme QC, ça devient tout de suite très touffu et pas facilement lisible pour qq'un qui déboule dans le projet mais ça apporte souplesse et puissance. Tu peux créer des boites noires dont tu te fiches de savoir comment elles fonctionne, tu as une entrée, un sortie et voilà .

    Le but inavoué de tout cela est de créer une application pour effectuer un certain traitement vidéo. Pour tout ce qui est capture vidéo et traitements standards


    QC a un patch d'entrée video, ca fonctionne avec la Isight, une caméra, derriere tu peux faire ton traitement. Tu peux ouvrir un qtz avec Quicktime, tu verras il sait le lire.
    Tu veux faire quoi?? :0)

    PS: ah si pour écrire ces propres patch...
    http://www.clockskew.com/blog/?p=15
  • BruBru Membre
    04:36 modifié #3
    dans 1153904470:

    2)Pour ce qui est des interactions, comment fait-on pour connecter les entrées/sorties à  un code Cocoa ? Y a-t-il de la part du .qtz une scrutation continuelle des entrées pour communiquer en temps réel avec ? De quel "type" sont les entrées ? On récupère des NSImage, des CVBuffer ?


    Un .qtz permet de mettre à  disposition des inputs et des outputs, qui sont les éléments de communication (des ports en terminologie Apple) entre le programme et l'animation QC.

    Dans un prog cocoa, une fois la QCView instanciée et le .qtz chargé, ces inputs/outputs sont accessibles via les méthodes setValue:forInputKey:, valueForInputKey: et valueForOutputKey:.
    Les types de donnée acceptés sont très variés  : Fenêtres volantes envoie des NSArray, NSNumber et NSImage, mais NSDictionary, NSString et NSColor sont aussi acceptés.

    Pour ce que j'en sais, les valeurs des inputs/outputs sont dynamiques et influent sur le déroulement de l'animation en temps réèl.
    Dans Fenêtres volantes, c'est par un input que l'animation est démarrée (en y mettant un NSNumber booléen YES).
    Ensuite, sur détection d'un événement (qui doit mettre fin au screen saver), ce même input reçoit un NSNumber booléen à  NO, ce qui déclenche immédiatement la partie de l'animation qui fait revenir les fenêtres en position normale.

    .
  • ChachaChacha Membre
    04:36 modifié #4
    Salut,

    Merci pour vos réponses.
    J'ai fait le tutoriel QC (je me suis acquitté de mon glowing cube, et des bindings pour les published inputs), ce qui m'a permis de mieux comprendre le tout.
    Dites-moi si je me trompe :
    -un QTZ est vraiment atomique, si on s'en sert dans un code, on ne contrôle rien de ce qui peut se passer entre les entrées et les sorties publiées.
    -Un QTZ, au plus bas niveau, s'exécute dans un QCRenderer. Celui-ci est associé à  un contexte OpenGL (qui peut , j'espère, être hors écran avec des NSOpenGLPFAOffScreen...). Une QCView, c'est juste un truc bien pratique qui embarque un QCRenderer mais a priori on pourrait s'en passer.
    -Les entrées/sorties des QTZ sont de certains types (listés dans la doc), et une conversion automatique est effectuée, si c'est possible dans le cas où on ne fournit pas tout à  fait ce qu'il faut. Ce que je trouve bizarre, c'est que question video on ne travaille qu'avec des NSImage ou des CIImage... La CIImage est-elle suffisante pour assurer un flot temps réel ?
    -Il n'est officiellement documenté nulle part comment créer ses propres patches QC, (macro mise à  part); le lien vers clockskew est de la rétro-ingénierie.
    -Si je veux, par exemple, créer une appli qui affiche deux vues : à  gauche ce que capte ma webcam, et à  droite le résultat d'un filtre QC, je peux faire les choses suivantes :
       -créer un qtz qui fait input video, publier une CIImage en sortie, et la récupérer dans mon code à  intervalles réguliers (par exemple avec un timer)
       -créer un QCRenderer associé à  un contexte OpenGL hors écran, qui applique un certain filtre à  une CIImage en entrée, et me produit une CIImage en sortie
       -afficher la CIImage dans la vue de droite

    Alors, tout ceci vous semble correct ?
    J'ai bien conscience que je peux aussi essayer pour avoir les réponses, mais finalement une telle discussion peut servir à  plusieurs personnes.

    +
    Chacha
  • ChachaChacha Membre
    juillet 2006 modifié #5
    Alors j'ai fait ce que je disais dans le truc précédent, ça marche plutôt bien.
    1)Mon code (un bête NSDocument) est connecté à  une QCView qui fait tourner une compo QC, laquelle est un simple "video input" de la webcam.
    2)En appuyant sur un bouton, je peux récupérer une NSImage extraite du flux vidéo (valueForOutputKey: sur la QCView)
    3)Je peux ensuite injecter cette NSImage (setValue:forInputKey:) dans un QCRenderer qui me fait, disons, une détection de contours. Ce QCRenderer est initialisé à  un contexte OpenGL que j'ai fabriqué à  la main. Il n'y a pas de vue associée.
    4)Je récupère une image en sortie de mon QCRenderer (valueForOutputKey:), et je peux l'afficher, par exemple, dans une NSImageView.
    Par contre, j'ai deux grosses interrogations :
    -Pourquoi est-ce que je récupère des NSImage et pas des CIImage ? Ce n'est pas très efficace, non ?
    -Pourquoi, en initialisant mon contexte OpenGL, n'ai-je pas eu besoin de spécifier NSOpenGLPFAOffScreen (et qu'en plus, si je le fais ça ne marche pas!) ?

    Et si le code vous intéresse, je peux vous le fournir.

    +
    Chacha
  • boris cargoboris cargo Membre
    04:36 modifié #6
    hélas je peux répondre à  rien,  B)
    mais il me vient un autre exemple en tête, Photobooth aussi passe par QC.
  • ChachaChacha Membre
    04:36 modifié #7
    Alors j'ai maintenant un souci, mais je ne sais pas s'il vient d'OpenGL ou quoi.
    Comme expliqué dans les précédents posts, je me sers d'une composition QC comme d'input pour récupérer des images, et ensuite je peux réinjecter ces images dans une autre composition QC exécutée hors écran.
    Comme je veux pouvoir appliquer un traitement écrit en C aux images fournies par les compositions, j'ai besoin de récupérer un tableau bitmap de ces images. Plutôt que de convertir des NSCIImageRep en NSBitmapRep (coûteux), j'ai trouvé un code d'Apple qui utilise un pbuffer. Le pBuffer est un pixelBuffer, c'est à  dire un tableau dans lequel OpenGL fait son rendu. Pour l'utiliser, il suffit donc de faire en sorte que le contexte openGL associé à  la composition QC hors écran soir configuré pour le pbuffer. Ensuite, avec un glReadPixels, on récupère le contenu du pbuffer. Un exemple est donné à  l'adresse http://developer.apple.com/samplecode/QuartzComposerOffline/listing1.html.

    Ensuite, j'ai réalisé l'exemple simple suivant :
    -ma composition QC d'entrée génère une image RGB(255,44,0) de taille 16x16.
    -je récupère cette image et je l'injecte dans la deuxième composition, qui ne fait rien (la sortie est une recopie de l'entrée)
    -je récupère l'image de sortie de la compo et je l'affiche : c'ets bien un carré rouge

    Par contre, si j'essaye de récupérer le pbuffer de la deuxième compo, j'obtiens n'importe quoi ! ça ne ressemble pas du tout à  une suite de (255, 44, 0, 0) (rgba)

    De deux choses l'une :
    -soit il y a un problème d'OpenGL "pur", mais c'est bizarre puisque l'exemple d'Apple fait comme ça
    (cela dit, c'est vraiment la merdouille les glReadPixel, glFortmat glType... galère)
    -soit il y a un souci dans l'interaction pbuffer/QC/openGL... mais je me casse la tête dessus !

    Si quelqu'un s'y connaà®t un peu en OpenGL, voici le p'tit projet expliqué ci-dessus... je sèche grave !

    +
    Chacha

    [Fichier joint supprimé par l'administrateur]
  • AliGatorAliGator Membre, Modérateur
    04:36 modifié #8
    J'ai déjà  eu des problèmes avec glReadPixels qui étaient dûs :
    - Soit au GraphicContext qui n'était pas défini (si tu commences à  dessiner sans avoir fourni de contexte dans lequel dessiner)
    - Soit à  cause de l'obfuscating (dessin dans une fenêtre qui était masqué par une autre fenêtre, et selon les implémentations d'OpenGL sur les cartes graphiques, certaines ne dessinent pas dans les zones masquées (obfuscated), ce qui fait que quand tu lis la zone avec glReadPixels le PBuffer n'ayant pas été écrit il contient n'importe quoi.

    Sinon vérifie que ton PBuffer est bien en lecture et écriture et pas lecture only (je ne suis plus sûr qu'on puisse régler ça pour le PBuffer, c'est p'tet que pour le Z-Buffer mais je suis plus trop sûr), que les formats choisis sont les bons (RGBA, GRAY, ...) ainsi que le GL_UNPACK_BITS ou un machin comme ça (pour dire si les pixels sont alignés dans la mémoire ou pas)

    Dernière chose, est-tu sûr que l'image que tu injectes (et qui est donc bêtement recopiée) n'est pas compressée d'une certaine manière, convertie en couleurs indexées, ou un truc du genre ? Je doute que ça joue puisque c'est le rendu qui écrit sur le PBuffer donc il écrit les valeurs des pixels, mais quand même.

    Tu as bien initialisé ton PBuffer comme il faut (en indiquant la profondeur de couleurs que tu voulais pour ce PBuffer, etc) t'es sûr ?
    Vérifie aussi que le glReadPixels lit bien depuis le PBuffer (avec un glReadBuffer si ma mémoire est bonne)...


    Voilà  pour quelques pistes.
  • ChachaChacha Membre
    juillet 2006 modifié #9
    dans 1154006004:

    J'ai déjà  eu des problèmes avec glReadPixels qui étaient dûs :
    - Soit au GraphicContext qui n'était pas défini
    - Soit à  cause de l'obfuscating

    Ici, le contexte est hors écran et bien initialisé (enfin, moi j'ai recopié les paramètres qu'utilise le code d'Apple)


    Sinon vérifie que ton PBuffer est bien en lecture et écriture et pas lecture only

    ls NSOpenGLPixelBuffer ne dispose pas d'une telle option, je suppose donc que ce problème n'existe pas vraiment


    les formats choisis sont les bons (RGBA, GRAY, ...) ainsi que le GL_UNPACK_BITS

    Là  encore, j'ai recopié ce que faisait apple, au niveau des ROW_LENGTH, GL_RGBA, GL_UNSIGNED_INT_8_8_8...


    Dernière chose, est-tu sûr que l'image que tu injectes (et qui est donc bêtement recopiée) n'est pas compressée d'une certaine manière, convertie en couleurs indexées, ou un truc du genre ?

    En téhorie ça ne devrait pas avoir d'incidence : quelle que soit sa représentation interne, je lui donne le format que moi je veux récupérer


    Tu as bien initialisé ton PBuffer comme il faut (en indiquant la profondeur de couleurs que tu voulais pour ce PBuffer, etc) t'es sûr ?

    Ben j'ai relu des centaines de fois, je crois pas avoir fait de faute de recopie


    Vérifie aussi que le glReadPixels lit bien depuis le PBuffer (avec un glReadBuffer si ma mémoire est bonne)...

    J'va essayer !



    Voilà  pour quelques pistes.

    Merci, c'est vraiment pas un problème facile.

    +
    Chacha
  • ChachaChacha Membre
    juillet 2006 modifié #10
    J'ai trouvé !
    Pfou, j'ai mis le temps.
    En fait, ce n'était pas directement un problème d'OpenGL. Tout était dans mon filtre "qui fait rien".
    Mon filtre "qui fait rien" recopiait (très bien au passage) l'image d'entrée sur sa sortie. Or, cela ne correspond pas à  un rendu ! Pour le forcer à  écrire dans le buffer, il fallait rajouter un Patch "Billboard" dans cette composition. De fait, mon pbuffer contenait ce qu'il voulait, à  savoir n'importe quoi.

    C'est marrant, ça veut dire qu'une compo QC peut produire une image correcte (puisque j'en récupérais une via valueForOutputKey) sans effectuer de rendu dans le buffer openGL... étrange.

    Sympa comme problème non ?

    +
    Chacha
  • AliGatorAliGator Membre, Modérateur
    04:36 modifié #11
    Pas si étrange que ça quand on y pense, tu peux très bien créer un filtre de traitement d'une image en QC, sans pour autant vouloir que ton image s'affiche ou soit rendue, onscreen ou offscreen.
    L'opération de rendu (blitting & co) est une des opérations les plus coûteuses dans ce domaine. Tu peux parfois faire des gros traitements sur des images bien rapides, par contre au moment du rendu (blitting, swapping, ...) ça prend du temps (bon surtout quand c'est onscreen). Et puis c'est bien une étape à  part dans la création d'un environnement graphique ou vidéo, qui a ses contraintes, spécificités, caractéristiques...

    C'est pour ça qu'il existe l'objet "Billboard" d'ailleurs, c'est pour effectuer la dernière opération que l'on fait en général, le rendu des fragments (éléments graphiques qui ne sont pas encore rendus) vers des pixels.

    (Plus d'infos sur l'ordre des opérations d'une chaà®ne OpenGL, pour ta culture perso dans le RedBook)

    Ceci dit je te dis ça, mais je n'y aurais pas pensé du moins sans voir la composition de ton "filtre qui fait rien" (n'ayant jamais fait de QC en dehors d'avec l'appli du même nom, sans l'interfacer avec du code et juste pour faire mumuse), donc bon, bien débogué :)
  • ChachaChacha Membre
    04:36 modifié #12
    dans 1154030572:

    (Plus d'infos sur l'ordre des opérations d'une chaà®ne OpenGL, pour ta culture perso dans le RedBook)

    Merci; j'ai déjà  consulté le RedBook et fait pas mal d'OpenGL, ce qui ne m'empêche pas de le trouver très peu pratique à  l'usage (qui a parlé des textures ?). De ce fait, j'apprécie beaucoup le travail fait par Apple, qui base MacOS X sur OpenGL mais se casse la nénette pour fournir des outils formidables qui masquent l'aspect compliqué !

    Enfin, j'ai encore du boulot. En ce moment, je suis en train de me demander si on peut écrire des Vertex Shaders en Core Image, j'ai l'impression que c'est plutôt limité aux Fragment Shaders. Comme je n'ai encore jamais fait de glsl, je ne sais pas non plus à  quel point on peut utiliser en Core Image les gl_position, uniform, varying, etc.
    Déjà , quand j'aurai compris les interactions entre CISampler et CIKernel, et que j'aurais aussi compris si un CISampler peut être utilisé dans QC, là  j'aurais fait des progrès.


    (n'ayant jamais fait de QC en dehors d'avec l'appli du même nom, sans l'interfacer avec du code et juste pour faire mumuse), donc bon, bien débogué :)

    En fait je te conseille d'essayer comme moi, parce que ça révèle bien la puissance du truc. Avant, je voyais QC comme un gadget, mais depuis que j'ai compris qu'on peut s'en servir comme boà®tes  dans du code Cocoa, histoire de rendre OpenGL pratique, alors je vois à  quel point c'est grandiose.

    +
    Chacha
  • AliGatorAliGator Membre, Modérateur
    04:36 modifié #13
    Wow hé bé tu m'as l'air bien avancé dans ce domaine, à  mon avis c'est moi qui vais te demander des infos un de ces jours :D

    Je suis encore loin de vouloir me mettre au GLSL même si ça peut fournir des effets vraiment sympas, ceci dit dans /Developer/Examples/OpenGL/Shaders tu as apparament des exemples (dans ARBVertexProgram j'imagine ?) assez sympas et qui semblent toucher aussi aux Vertex Shaders.

    Tu as aussi l'application "OpenGL Shader Builder" fournie avec les Dev Tools qui trône aux côtés de Quartz Composer dans "/Developer/Applications/Graphic Tools" qui peut être utile pour travailler tes ".shdr" :)
  • ChachaChacha Membre
    juillet 2006 modifié #14
    dans 1154084351:

    Wow hé bé tu m'as l'air bien avancé dans ce domaine, à  mon avis c'est moi qui vais te demander des infos un de ces jours :D

    Si tu as des question d'OpenGL, je peux effectivement répondre à  quelques une, mais pas tout.
    -je connais le principe d'OpenGL et le pipelining vertex->rasterization->rendu
    -j'ai déjà  réussi à  plaquer une texture (super) mais je ne maà®trise pas vraiment le code qui va avec (au mieux je le comprends, mais pour le reproduire...) cela dit je comprends les problématique qu'il y a derrière
    -pour tout ce qui est des raffinement sur les utilisations des buffers (notamment le stencil buffer), je sais que ça existe (tracé d'ombres), je vois à  peu près comment ça marche, mais de loin. Les techniques algoritmiques associées ne m'aident d'ailleurs pas à  aimer OpenGL !
    -pour les shaders, je découvre. Je sais maintenant ce que sont des vertex et fragment shaders, je vois à  peu près comment ça s'utilise, je fais la différence entre CG, GLSL, HLSL. J'en ai testé quelques-uns. La plupart sont des algos cons comme la pluie (genre la fourrure, c'est misérable), mais ça reste très dur à  lire

    Bref, j'ai plutôt une connaissance de principe que d'application, mais c'est déjà  pas mal - ça t'aidera à  voir si je suis la bonne personne pour tes questions :-D


    Tu as aussi l'application "OpenGL Shader Builder"

    Un peu plantogène mais ça marche. On ne peut cependant pas tester les shaders qui s'appliquent en plusieurs passes.

    +
    Chacha
Connectez-vous ou Inscrivez-vous pour répondre.