//(...)
//l'idée est de créer les images par groupe de 10, puis de laisser reposer un peu le système...
int nbPlans = planFin - planInit;
int nbDizaines = (int) floorf(nbPlans / 10.0);
for (int i = 0; i < nbDizaines; i++){
int aP = (i * 10) + planInit;
int bP = ((i + 1) * 10) + planInit;
[leDessin creeLesImages:aP etFin:bP etNomFichier:nomFichier];//méthode qui retrouve l'@autoreleasepool
sleep(3);//testé à 2, nécessaire ici je pense pour je ne sais quelle raison d'ailleurs...
}
int aC = (nbDizaines * 10) + planInit;
[leDessin creeLesImages:aC etFin:planFin etNomFichier:nomFichier];//les dernier plans
[self finaliseFilm];//méthode qui utilise QTMovie. Elle est stable.
}
Du coup, un test à 270 images s'est bien déroulé. Pas de plantage. J'essaierai avec plus bientôt...
Ayant repéré qu'en dessous d'un certain nombre d'images, l'export d'images se passait bien, j'ai pensé essayer cela.
Qu'en pensez-vous? (J'avoue être dans "l'intuit" mais ne pas vraiment comprendre...)
J'ai relu ma doc sur les threads : j'en gardais un petit souvenir de l'époque où je faisais du Java. C'est bien le souvenir que j'en avais. Ceci dit, où sont les thread dans mon processus?
NSOperation/Queue créent des threads. C'est même à ça que ça sert. Tu peux tout faire dans un seul thread (thread principal = thread interface utilisateur), mais: - ça bloque l'interface utilisateur, alors pas de mise à jour de l'aperçu, pas possible d'arrêter. - tu n'utilises qu'un seul coe“ur du CPU, alors que tous les CPU en ont au moins deux depuis des années déjà .
Et "thread safe", ça veut dire qu'on peut partager entre threads sans que ça se casse la gueule. (Relis dans ta doc le chapitre sur les problèmes des accès concurrents).
Ceci dit, j'aimerais tout de même comprendre si le problème actuel vient du "multi-thread", car je ne vois pas où de multiples instructions sont lancées. Il n'y a qu'une seule boucle! Et je ne comprends vraiment rien au "crash report", j'avoue!
Par contre, tant qu'on parle de threads, la petite carte graphique de mon iMac (5770M), dispose de 480 unités de traitement. Même contre un processeurs à 4 cores, soit 8 threads , elle fait plus que le poids.
Je n'ai pas les chiffres sous la main, mais le nombre d'unités de traitement est nettement moins flatteur sur un MacBookPro sans carte graphique dédiée.
"J'ai confirmation que le problème vient de "@autoreleasepool" qui, si je vous entend bien, crée un thread qui cause les crash à répétition. Je viens de faire un test à 300 images sans @autoreleasepool, et sans crash.
Le problème alors est la conso RAM : 1,8 Go pour 300 images. C'est pourquoi d'ailleurs j'en étais venu par le passé à utiliser NSOperation etc.
J'ai tenté de tout mettre en variable de classe (la NSSize, la NSBitmapImageRep, la NSData, les NSString, etc.), rien n'y fait, la taille de l'image s'ajoute inexorablement à la conso RAM (3,1 Mo à chaque image).
Auriez-vous une meilleure idée que la mienne? Connaitriez-vous par exemple une méthode qui force le ramasse-miette à travailler (puisque les images sont crées par paquets de 10)? "
Mais un second test a crashé encore. Rapport en PJ. Ah là là , où donc est la solution? (si possible en conservant le travail graphique déjà effectué)
Merci en tous les cas pour vos nombreux posts qui m'ont permis de progresser.
Si tu regardes les premiers messages, nous t'avons donné des solutions pour réduire l'empreinte mémoire de ton appli, mais dès le début, nous t'avons bien prévenu que c'était un pis-aller.
On dirait que tu connais la solution puisque nous te l'avons exposé trois fois, mais que parce qu'elle t'impose de casser ton travail, tu cherches un raccourci. Ben, y'en a pas.
Comme Céroce, je sais pas trop quoi ajouter non plus. Il y a un gros problème structurel dans ta conception. Et quand je te lis cela vient d'une mauvaise assimilation de concepts fondamentaux (thread, gestion mémoire, et n'y ajoutons pas OpenCL svp. ).
Par exemple, quand je lis des choses comme ça...
Le problème alors est la conso RAM : 1,8 Go pour 300 images. C'est pourquoi d'ailleurs j'en étais venu par le passé à utiliser NSOperation etc.
Comment dire? Ca me choque et pas qu'un peu. NSOperation, GCD ou autre ça n'a rien à voir avec la gestion de la conso mémoire. Ce sont des API pour effectuer des tâches similaires en parallèle afin d'exploiter tous les coeurs de la machine et donc in fine d'aller plus vite. Mais encore faut-il que les tâches à effectuer le permettent (thread safe pour rabâcher).
Il faut que tu remettes à plat ton code (fait gaffe aussi au nommage: laVue qui est en fait une NSImage quand on regarde ton code c'est très moyen. Dans le modèle MVC, NSImage c'est du métier donc model et en aucun cas une vue) et ça on peut pas le faire à ta place. Commence donc par recoder tout en mono-thread (je bouge mon slider et lorsque je relache le calcul s'exécute direct et s'il faut ça freeze l'interface le temps du calcul) et tu le fais marcher correctement. Après seulement, si cela le justifie en terme de performances, tu passes en multi-thread en contrôlant bien que (lire la doc des apis que tu utilises) ce que tu fais dans tes threads est thread-safe (et j'en doute vu ce qu'on a discuté avant).
PS: le multi-thread peut très bien être la cause d'un problème d'autorelease pool. Chaque thread ayant son propre pool, il suffit q'une ressource partagée soit libérée par un thread pour faire partir en carafe son petit frère à coté. Même si tu utilises apparemment ARC, il y a peut être des cas de figure où cela peut arriver.
Ouaip! J'aimais bien pourtant mes filtres... Je vais essayer de comprendre. Pour la petite histoire, si j'utilise peu d'images et de transformations (donc peu de filtres CIFilter), je peux passer plus de 700 images sans plantage. Enfin, c'est comme cela (en me cassant les dents) que j'ai appris pour le son aussi, et maintenant, je sais faire...
Pour info, avec Core Image, je n'ai jamais réussi à faire du rendu avec le GPU sur un thread secondaire. ça plantait et corrompait la mémoire vidéo des autres applis. Par contre, ça fonctionnait en forçant le rendu avec le CPU, mais les performances étaient ridicules.
Ouaip... Les performances avec NSBitmapImageRep étaient si mauvaises que j'étais ravi de celles de CoreImage (pour faire la même chose, voir une autre discussion sur ce forum). Sniff!
Ouaip... Les performances avec NSBitmapImageRep étaient si mauvaises que j'étais ravi de celles de CoreImage (pour faire la même chose, voir une autre discussion sur ce forum). Sniff!
CoreImage est a des années lumières des performances qu'on peut atteindre avec NSBitmapImageRep. Via la méthode bitmapData, on accède directement (ou presque) au buffer des pixels d'une image. Il n'y pas plus performant à ce jour avec les API d'Apple que ce soit en terme de mémoire, de ressources ou de sécurité (tout se fait côté CPU donc pas de risque de kernel panic en cas de fausse manip).
Alors là ! Vous m'épatez! Effectivement, j'utilisais "setColor/getColor at point...". je ne connaissais pas "bitmapData". Je vais éclaircir cela, car du coup les bonnes vieilles méthodes de NSAffineTransform (au lieu de filtres CoreImage pour les rotations, etc.) pourraient refaire surface...
Je vois ce que je peux faire par là . Je pensais que CoreImage était plus performant que tout le reste! Manifestement non. Merci Céroce et Mala.
Ceci dit, je pense que vous voulez parler de CGImage, et non de NSBitmapImageRep?
For this reason, you should not use an NSBitmapImageRep object if you want to manipulate image data. To work with data that is not premultiplied, use the Core Graphics framework instead. (Specifically, create images using the CGImageCreate function and kCGImageAlphaLast parameter.)
Personnellement, je travaille plutôt avec un CGBitmapContext, mais c'est pour ne pas me traà®ner NSImage, sa complexité et ses comportements douteux. Mais ça ne veut pas dire que c'est infaisable avec NSBitmapImageRep, et ça peut même être pratique pour créer la vidéo.
À nouveau, je te conseille de te reporter à ma présentation et ses programmes d'exemple pour voir comment je crée la bitmap. On peut obtenir une CGImage avec CGBitmapContextCreateImage() et créer une NSImage à partir de la CGImage.
Il est très bien ton code, Céroce, tu permets que je t'en prenne un bout? (Le bon code est du code réutilisable dit-on...) Les classes CEEffect et CEEffectChain en particulier me semblent parfaites...
Cependant, le code a été écrit pour être le plus lisible possible, pas forcément le plus efficace. Par exemple, par principe CEEffectChain applique les effets les uns après les autres, c'est moins efficace que de faire tout d'un seul tenant.
Et c'est du code qui date, mes compétences et mon style ont évolué !
Il me faudrait un peu de temps pour écrire une générateur de l'ensemble de Mandelbrot en Haskell...
Ceci dit, je pense que vous voulez parler de CGImage, et non de NSBitmapImageRep?
Non, quand j'écris NSBitmapImageRep je pense NSBitmapImageRep.
Lorsque tu cites la doc, prends soin de ne pas l'extraire du contexte par ce que sinon c'est la porte ouverte à toutes les fenêtres...
Alpha Premultiplication and Bitmap Formats
When creating a bitmap using a premultiplied format, if a coverage (alpha) plane exists, the bitmap's color components are premultiplied with it. In this case, if you modify the contents of the bitmap, you are therefore responsible for premultiplying the data. Note that premultiplying generally has negligible effect on output quality. For floating-point image data, premultiplying color components is a lossless operation, but for fixed-point image data, premultiplication can introduce small rounding errors. In either case, more rounding errors may appear when compositing many premultiplied images; however, such errors are generally not readily visible.
For this reason, you should not use an NSBitmapImageRep object if you want to manipulate image data. To work with data that is not premultiplied, use the Core Graphics framework instead. (Specifically, create images using the CGImageCreate function and kCGImageAlphaLast parameter.) Alternatively, include the NSAlphaNonpremultipliedBitmapFormat flag when creating the bitmap.
Core Graphics reste une API de haut niveau. C'est pratique pour manipuler des images dans leur globalité (transfomation, tracès en surimpression) car il y a déjà presque tout ce qu'il faut côté API mais cela reste très en retrait en terme de performances par rapport à un accès buffer avec NSBitmapImageRep.
Personnellement, j'ignorais que NSBitmapImageRep était limité aux images utilisant un alpha prémultiplié. Pour générer des images fractales, ce n'est justement pas bien pratique, puisqu'on ne veut pas d'alpha.
On peut aussi accéder à la bitmap directement avec CGBitmapContext. Et là , Core Graphics est plus intéressant puisqu'on peut choisir précisément le format de la bitmap.
Je compte bien utiliser le canal alpha, la transparence faisant partie avec la couleur des calculs dans l'IFS. Je veux, à partir de photos, faire des IFS transformations affines + modulation de la couleur et de la transparence.
Je commence à m'y perdre avec tous ces frameworks et formats image.
What a "premultiplied format" is please? J'ai compris en lisant des tutos qu'il s'agit de ce avec quoi je travaille depuis toujours avec NSColor, d'une couleur codée en RGB + valeur alpha de transparence.
En quoi NSBitmapImageRep va être plus performant que CGImage? Comment fonctionnent "bitmapData" et "getBitmapData" ? Je lis dans la doc que cela renvoie à un unsigned char . J'ai cru comprendre qu'il s'agissait d'une structure, est-ce que je me trompe? J'ai déjà expérimenté "setColor:atX:y:" qui n'est pas du tout productif dans mon projet. Comment peut-on faire mieux avec NSBitmapImageRep?
Pour le reste, travailler le code en fonction de la position des pixels (x/y) me semble plus évident et intéressant que faire les kernel de CIFilter. Cela me semble plus intuitif. En plus, on reste en C/ObjectivC .
Personnellement, j'ignorais que NSBitmapImageRep était limité aux images utilisant un alpha prémultiplié. Pour générer des images fractales, ce n'est justement pas bien pratique, puisqu'on ne veut pas d'alpha.
Non, ce n'est pas le cas mais j'avoue ne pas trop comprendre où veut en venir le gars d'Apple qui a écrit ce bout de doc bien merdique.
Par exemple lorsqu'il écrit:
you are therefore responsible for premultiplying the data
A partir du moment où on créer une image avec un canal alpha c'est qu'on veut gérer l'alpha (couche prémultipliée). C'est con d'écrire ça. Et si on veut pas d'alpha et bien on créé un NSBitmapImageRep sans couche alpha. Y a tout ce qu'il faut pour.
Le gars nous fait ensuite une pseudo démonstration avec des images en précisions flottantes. Mais de quoi parle-t-on exactement? Coordonnées dans l'espace ou résolution d'encodage des pixels? Sachant qu'au passage on peut lire dans ce paragraphe:
Note that premultiplying generally has negligible effect on output quality.
donc c'est pas grave en fait? Et encore pour enfoncer le clou...
however, such errors are generally not readily visible.
Donc c'est toujours grave pas grave docteur?
Et bien oui apparemment vu qu'il déconseille son usage au regard de cette brillante démonstration fumeuse... Lol
En m'amusant avec le code de Céroce, je me rends compte que l'on n'a accès qu'au pixel un par un. Je voulais faire un test du genre élargissement/distorsion de l'image :
avec une fonction int posX = (int) floorf(powf(x/width, 2.0) * width), je pensais faire une image de destination qui utiliserai le pixel en (posX, y) de l'image source. Je pense que vous me comprenez. Avec toutes les données, cela me semble possible, mais comment feriez-vous en traitant les pixels un par un? ??? Mais ce n'est certainement pas la bonne méthode aussi?
Pourtant l'effet "convolution" me semble bien utiliser les pixels "avant" ou "après" dans l'image source. J'ai essayé :
destPixel = sourcePixel - posXsans succès (ne riez pas trop fort, merci! )
J'ai vu des codes sources qui utilisent setPixel: at: (...) au lieu de setColor. Mais cette méthode ne me semble pas plus efficace ou rapide. Est-ce que je me trompe?
What a "premultiplied format" is please? J'ai compris en lisant des tutos qu'il s'agit de ce avec quoi je travaille depuis toujours avec NSColor, d'une couleur codée en RGB + valeur alpha de transparence.
Ce dont tu parles est le codage RGBA. Souvent, on prémultiplie les composantes RGB par l'alpha, ce qui permet de gagner du temps quand on dessine l'image. Voir https://fr.wikipedia.org/wiki/Alpha_blending
En quoi NSBitmapImageRep va être plus performant que CGImage?
Il ne le sera pas. C'est très exactement la même façon de faire: on écrit directement les octets dans la bitmap.
Comment fonctionnent "bitmapData" et "getBitmapData" ? Je lis dans la doc que cela renvoie à un unsigned char. J'ai cru comprendre qu'il s'agissait d'une structure, est-ce que je me trompe?
En fait, c'est assez bizarre de renvoyer un unsigned char *, j'aurais plutôt renvoyé un void *. En gros, la façon dont est agencée la mémoire dépend du format de la bitmap. Par exemple, si tu as une image en 256 niveaux de gris, tu auras un octet par pixel. Par contre, si tu as une image RGBA 32 bits, tu auras un octet par composante, et il est intéressant de créer une structure pour accéder à chaque composante:
Tu peux alors caster le pointeur renvoyé par getBitmapData en un PixelsRGBA *. Encore une fois, voir ma présentation.
Pour le reste, travailler le code en fonction de la position des pixels (x/y) me semble plus évident et intéressant que faire les kernel de CIFilter. Cela me semble plus intuitif. En plus, on reste en C/ObjectivC .
ça dépend. Par exemple, l'un des problèmes d'accéder directement à la bitmap, c'est qu'il ne faut pas essayer de lire ou d'écrire en dehors, sous peine de plantage. Par exemple, pour un filtre de convolution, il faut accéder aux pixels adjacents. Or, que se passe-t-il quand on est sur le bord de l'image? Il faut gérer ce cas. Un autre problème: avec Core Image, on travaille en flottants, sachant qu'ils sont écrêtés à 0 ou 1. Avec le code en C, il faut s'assurer de bien écrire des valeurs entre 0 et 255.
Contrairement à toi, je pense qu'OpenGL Shading Language (sur lequel est basé Core Image Kernel) est plus pratique. Notamment, tout ce qui concerne les opérations sur les vecteurs ou les matrices.
En m'amusant avec le code de Céroce, je me rends compte que l'on n'a accès qu'au pixel un par un.
Non, c'est faux, tu as accès à toute la bitmap.
Je voulais faire un test du genre élargissement/distorsion de l'image : avec une fonction int posX = (int) floorf(powf(x/width, 2.0) * width), je pensais faire une image de destination qui utiliserai le pixel en (posX, y) de l'image source. Je pense que vous me comprenez. Avec toutes les données, cela me semble possible, mais comment feriez-vous en traitant les pixels un par un? ???
Tu connais la position du pixel "courant". Utilise ta formule pour connaà®tre les coordonnées du pixels source. Note qu'il te faudra ta propre fonction d'interpolation bi-linéaire si tu veux des coordonnées qui ne tombent pas sur des pixels entiers.
J'ai vu des codes sources qui utilisent setPixel: at: (...) au lieu de setColor. Mais cette méthode ne me semble pas plus efficace ou rapide. Est-ce que je me trompe?
Elle est un poil plus rapide, puisque tu fixes le pixel dans le bon format, donc il n'y a pas de conversion à faire. Mais elle est quand même très inefficace parce que les appels de méthodes prennent beaucoup de temps, particulièrement en Objective-C. T'as qu'à faire un coup d'Instruments, tu verras que le code passe son temps dans objc_msg_send().
Merci Céroce, je viens de faire de nouveaux tests, le problème venait d'erreurs de calcul, mais on accède bien à toute l'image d'origine avec ton code effectivement. Pour l'instant, le résultat que j'obtiens n'est pas exactement celui souhaité, mais je vais y arriver.
Elle est un poil plus rapide, puisque tu fixes le pixel dans le bon format, donc il n'y a pas de conversion à faire. Mais elle est quand même très inefficace parce que les appels de méthodes prennent beaucoup de temps, particulièrement en Objective-C. T'as qu'à faire un coup d'Instruments, tu verras que le code passe son temps dans objc_msg_send().
Est-il possible d'écrire les routines critiques en Swift pour éviter les appels de messages, et laisser le reste en Objective-C ?
Est-il possible d'écrire les routines critiques en Swift pour éviter les appels de messages, et laisser le reste en Objective-C ?
On peut appeler du code Swift depuis le code Objective-C, donc oui, ça me semble possible. Par contre, le pont ObjC->Swift a forcément un impact sur les performances.
Où je voulais en venir est qu'il faut absolument éviter tout appel à une méthode, mais même un appel de fonction est trop lent. Il faut au minimum la déclarer inline.
Nous sommes ici dans des problématiques où on fait des millions de fois la même simple opération et la moindre différence est notable. Par exemple, il faut aussi faire en sorte de bien utiliser la mémoire cache en accédant à des adresses successives et éviter les conditions qui empêchent la parallélisation sur les différentes pipelines du CPU.
Dans tous les cas, même en programmant de façon inefficace, Herve devrait voir une grosse amélioration des performances, surtout s'il s'arrange pour travailler avec plusieurs cores.
Il ne le sera pas. C'est très exactement la même façon de faire: on écrit directement les octets dans la bitmap.
Avec NSBitmapImageRep on est à la source (on tape le vrai bitmap) et dans le cas de CIImage on est à l'arrivée au niveau du contexte graphique côté GPU (on tape sur une projection). C'est une différence fondamentale qui a un impact important sur les perfs.
Réponses
Le sujet me passionne, pardon!
J'ai fait ceci :
Du coup, un test à 270 images s'est bien déroulé. Pas de plantage. J'essaierai avec plus bientôt...
Ayant repéré qu'en dessous d'un certain nombre d'images, l'export d'images se passait bien, j'ai pensé essayer cela.
Qu'en pensez-vous? (J'avoue être dans "l'intuit" mais ne pas vraiment comprendre...)
Tu peux tout faire dans un seul thread (thread principal = thread interface utilisateur), mais:
- ça bloque l'interface utilisateur, alors pas de mise à jour de l'aperçu, pas possible d'arrêter.
- tu n'utilises qu'un seul coe“ur du CPU, alors que tous les CPU en ont au moins deux depuis des années déjà .
Et "thread safe", ça veut dire qu'on peut partager entre threads sans que ça se casse la gueule.
(Relis dans ta doc le chapitre sur les problèmes des accès concurrents).
Merci Céroce.
Juste pour info, mon "astuce" précédente ne vaut pas mieux : gros crash passé 270 images (très petit léger progrès ) ).
Céroce, où est cette doc? ??? Je ne comprends pas encore ce qu'il faut chercher.
PJ : rapport du crash.
Vi, je comprends.
Par contre, tant qu'on parle de threads, la petite carte graphique de mon iMac (5770M), dispose de 480 unités de traitement. Même contre un processeurs à 4 cores, soit 8 threads , elle fait plus que le poids.
Une Geforce 970 GTX en dispose de plus de 1600.
Je n'ai pas les chiffres sous la main, mais le nombre d'unités de traitement est nettement moins flatteur sur un MacBookPro sans carte graphique dédiée.
Bonjour, c'est lundi!
Je viens d'écrire ceci :
"J'ai confirmation que le problème vient de "@autoreleasepool" qui, si je vous entend bien, crée un thread qui cause les crash à répétition. Je viens de faire un test à 300 images sans @autoreleasepool, et sans crash.
Le problème alors est la conso RAM : 1,8 Go pour 300 images. C'est pourquoi d'ailleurs j'en étais venu par le passé à utiliser NSOperation etc.
J'ai tenté de tout mettre en variable de classe (la NSSize, la NSBitmapImageRep, la NSData, les NSString, etc.), rien n'y fait, la taille de l'image s'ajoute inexorablement à la conso RAM (3,1 Mo à chaque image).
Auriez-vous une meilleure idée que la mienne? Connaitriez-vous par exemple une méthode qui force le ramasse-miette à travailler (puisque les images sont crées par paquets de 10)? "
Mais un second test a crashé encore. Rapport en PJ. Ah là là , où donc est la solution? (si possible en conservant le travail graphique déjà effectué)
Merci en tous les cas pour vos nombreux posts qui m'ont permis de progresser.
@autoreleasepool n'a jamais créé de thread...
Je ne sais pas trop quoi te répondre.
Si tu regardes les premiers messages, nous t'avons donné des solutions pour réduire l'empreinte mémoire de ton appli, mais dès le début, nous t'avons bien prévenu que c'était un pis-aller.
On dirait que tu connais la solution puisque nous te l'avons exposé trois fois, mais que parce qu'elle t'impose de casser ton travail, tu cherches un raccourci. Ben, y'en a pas.
Comme Céroce, je sais pas trop quoi ajouter non plus. Il y a un gros problème structurel dans ta conception. Et quand je te lis cela vient d'une mauvaise assimilation de concepts fondamentaux (thread, gestion mémoire, et n'y ajoutons pas OpenCL svp. ).
Par exemple, quand je lis des choses comme ça...
Comment dire? Ca me choque et pas qu'un peu. NSOperation, GCD ou autre ça n'a rien à voir avec la gestion de la conso mémoire. Ce sont des API pour effectuer des tâches similaires en parallèle afin d'exploiter tous les coeurs de la machine et donc in fine d'aller plus vite. Mais encore faut-il que les tâches à effectuer le permettent (thread safe pour rabâcher).
Il faut que tu remettes à plat ton code (fait gaffe aussi au nommage: laVue qui est en fait une NSImage quand on regarde ton code c'est très moyen. Dans le modèle MVC, NSImage c'est du métier donc model et en aucun cas une vue) et ça on peut pas le faire à ta place. Commence donc par recoder tout en mono-thread (je bouge mon slider et lorsque je relache le calcul s'exécute direct et s'il faut ça freeze l'interface le temps du calcul) et tu le fais marcher correctement. Après seulement, si cela le justifie en terme de performances, tu passes en multi-thread en contrôlant bien que (lire la doc des apis que tu utilises) ce que tu fais dans tes threads est thread-safe (et j'en doute vu ce qu'on a discuté avant).
PS: le multi-thread peut très bien être la cause d'un problème d'autorelease pool. Chaque thread ayant son propre pool, il suffit q'une ressource partagée soit libérée par un thread pour faire partir en carafe son petit frère à coté. Même si tu utilises apparemment ARC, il y a peut être des cas de figure où cela peut arriver.
Ouaip! J'aimais bien pourtant mes filtres... Je vais essayer de comprendre. Pour la petite histoire, si j'utilise peu d'images et de transformations (donc peu de filtres CIFilter), je peux passer plus de 700 images sans plantage. Enfin, c'est comme cela (en me cassant les dents) que j'ai appris pour le son aussi, et maintenant, je sais faire...
Je vous tiendrai au courant. Merci à tous!
Pour info, avec Core Image, je n'ai jamais réussi à faire du rendu avec le GPU sur un thread secondaire. ça plantait et corrompait la mémoire vidéo des autres applis. Par contre, ça fonctionnait en forçant le rendu avec le CPU, mais les performances étaient ridicules.
Ouaip... Les performances avec NSBitmapImageRep étaient si mauvaises que j'étais ravi de celles de CoreImage (pour faire la même chose, voir une autre discussion sur ce forum). Sniff!
Les méthodes -[NSBitmapImageRep setColor/Pixel:atX:y:] sont très inefficaces. Rien que l'appel de méthode rend cette technique inutilisable.
CoreImage est a des années lumières des performances qu'on peut atteindre avec NSBitmapImageRep. Via la méthode bitmapData, on accède directement (ou presque) au buffer des pixels d'une image. Il n'y pas plus performant à ce jour avec les API d'Apple que ce soit en terme de mémoire, de ressources ou de sécurité (tout se fait côté CPU donc pas de risque de kernel panic en cas de fausse manip).
Alors là ! Vous m'épatez! Effectivement, j'utilisais "setColor/getColor at point...". je ne connaissais pas "bitmapData". Je vais éclaircir cela, car du coup les bonnes vieilles méthodes de NSAffineTransform (au lieu de filtres CoreImage pour les rotations, etc.) pourraient refaire surface...
Je vois ce que je peux faire par là . Je pensais que CoreImage était plus performant que tout le reste! Manifestement non. Merci Céroce et Mala.
Ceci dit, je pense que vous voulez parler de CGImage, et non de NSBitmapImageRep?
De ceci :
https://developer.apple.com/library/prerelease/ios/documentation/CoreGraphics/Reference/CoreGraphics_Framework/index.html
En effet, dans la doc, je lis :
À nouveau, je te conseille de te reporter à ma présentation et ses programmes d'exemple pour voir comment je crée la bitmap.
On peut obtenir une CGImage avec CGBitmapContextCreateImage() et créer une NSImage à partir de la CGImage.
OK, il est vrai que je n'avais pas pu lancer ces petits programmes à cause de ce message :
"The run destination My Mac is not valid for Running the scheme 'BitmapEffets'." j'ai trouvé depuis la solution.
Je vais en relire le code. Merci.
Il est très bien ton code, Céroce, tu permets que je t'en prenne un bout? (Le bon code est du code réutilisable dit-on...) Les classes CEEffect et CEEffectChain en particulier me semblent parfaites...
Oui, oui, bien sûr, c'est fait pour.
Cependant, le code a été écrit pour être le plus lisible possible, pas forcément le plus efficace. Par exemple, par principe CEEffectChain applique les effets les uns après les autres, c'est moins efficace que de faire tout d'un seul tenant.
Et c'est du code qui date, mes compétences et mon style ont évolué !
Il me faudrait un peu de temps pour écrire une générateur de l'ensemble de Mandelbrot en Haskell...
Non, quand j'écris NSBitmapImageRep je pense NSBitmapImageRep.
Lorsque tu cites la doc, prends soin de ne pas l'extraire du contexte par ce que sinon c'est la porte ouverte à toutes les fenêtres...
Core Graphics reste une API de haut niveau. C'est pratique pour manipuler des images dans leur globalité (transfomation, tracès en surimpression) car il y a déjà presque tout ce qu'il faut côté API mais cela reste très en retrait en terme de performances par rapport à un accès buffer avec NSBitmapImageRep.
Personnellement, j'ignorais que NSBitmapImageRep était limité aux images utilisant un alpha prémultiplié. Pour générer des images fractales, ce n'est justement pas bien pratique, puisqu'on ne veut pas d'alpha.
On peut aussi accéder à la bitmap directement avec CGBitmapContext. Et là , Core Graphics est plus intéressant puisqu'on peut choisir précisément le format de la bitmap.
Je compte bien utiliser le canal alpha, la transparence faisant partie avec la couleur des calculs dans l'IFS. Je veux, à partir de photos, faire des IFS transformations affines + modulation de la couleur et de la transparence.
Je commence à m'y perdre avec tous ces frameworks et formats image.
What a "premultiplied format" is please? J'ai compris en lisant des tutos qu'il s'agit de ce avec quoi je travaille depuis toujours avec NSColor, d'une couleur codée en RGB + valeur alpha de transparence.
En quoi NSBitmapImageRep va être plus performant que CGImage? Comment fonctionnent "bitmapData" et "getBitmapData" ? Je lis dans la doc que cela renvoie à un unsigned char . J'ai cru comprendre qu'il s'agissait d'une structure, est-ce que je me trompe? J'ai déjà expérimenté "setColor:atX:y:" qui n'est pas du tout productif dans mon projet. Comment peut-on faire mieux avec NSBitmapImageRep?
Pour le reste, travailler le code en fonction de la position des pixels (x/y) me semble plus évident et intéressant que faire les kernel de CIFilter. Cela me semble plus intuitif. En plus, on reste en C/ObjectivC .
Non, ce n'est pas le cas mais j'avoue ne pas trop comprendre où veut en venir le gars d'Apple qui a écrit ce bout de doc bien merdique.
Par exemple lorsqu'il écrit:
A partir du moment où on créer une image avec un canal alpha c'est qu'on veut gérer l'alpha (couche prémultipliée). C'est con d'écrire ça. Et si on veut pas d'alpha et bien on créé un NSBitmapImageRep sans couche alpha. Y a tout ce qu'il faut pour.
Le gars nous fait ensuite une pseudo démonstration avec des images en précisions flottantes. Mais de quoi parle-t-on exactement? Coordonnées dans l'espace ou résolution d'encodage des pixels? Sachant qu'au passage on peut lire dans ce paragraphe:
donc c'est pas grave en fait? Et encore pour enfoncer le clou...
Donc c'est toujours grave pas grave docteur?
Et bien oui apparemment vu qu'il déconseille son usage au regard de cette brillante démonstration fumeuse... Lol
Hum!...
En m'amusant avec le code de Céroce, je me rends compte que l'on n'a accès qu'au pixel un par un. Je voulais faire un test du genre élargissement/distorsion de l'image :
avec une fonction int posX = (int) floorf(powf(x/width, 2.0) * width), je pensais faire une image de destination qui utiliserai le pixel en (posX, y) de l'image source. Je pense que vous me comprenez. Avec toutes les données, cela me semble possible, mais comment feriez-vous en traitant les pixels un par un? ??? Mais ce n'est certainement pas la bonne méthode aussi?
Pourtant l'effet "convolution" me semble bien utiliser les pixels "avant" ou "après" dans l'image source. J'ai essayé :
destPixel = sourcePixel - posX sans succès (ne riez pas trop fort, merci! )
J'ai vu des codes sources qui utilisent setPixel: at: (...) au lieu de setColor. Mais cette méthode ne me semble pas plus efficace ou rapide. Est-ce que je me trompe?
Souvent, on prémultiplie les composantes RGB par l'alpha, ce qui permet de gagner du temps quand on dessine l'image. Voir https://fr.wikipedia.org/wiki/Alpha_blending
Il ne le sera pas. C'est très exactement la même façon de faire: on écrit directement les octets dans la bitmap.
En fait, c'est assez bizarre de renvoyer un unsigned char *, j'aurais plutôt renvoyé un void *.
En gros, la façon dont est agencée la mémoire dépend du format de la bitmap. Par exemple, si tu as une image en 256 niveaux de gris, tu auras un octet par pixel. Par contre, si tu as une image RGBA 32 bits, tu auras un octet par composante, et il est intéressant de créer une structure pour accéder à chaque composante:
Tu peux alors caster le pointeur renvoyé par getBitmapData en un PixelsRGBA *.
Encore une fois, voir ma présentation.
ça dépend. Par exemple, l'un des problèmes d'accéder directement à la bitmap, c'est qu'il ne faut pas essayer de lire ou d'écrire en dehors, sous peine de plantage. Par exemple, pour un filtre de convolution, il faut accéder aux pixels adjacents. Or, que se passe-t-il quand on est sur le bord de l'image? Il faut gérer ce cas.
Un autre problème: avec Core Image, on travaille en flottants, sachant qu'ils sont écrêtés à 0 ou 1. Avec le code en C, il faut s'assurer de bien écrire des valeurs entre 0 et 255.
Contrairement à toi, je pense qu'OpenGL Shading Language (sur lequel est basé Core Image Kernel) est plus pratique. Notamment, tout ce qui concerne les opérations sur les vecteurs ou les matrices.
Tu connais la position du pixel "courant". Utilise ta formule pour connaà®tre les coordonnées du pixels source. Note qu'il te faudra ta propre fonction d'interpolation bi-linéaire si tu veux des coordonnées qui ne tombent pas sur des pixels entiers.
Elle est un poil plus rapide, puisque tu fixes le pixel dans le bon format, donc il n'y a pas de conversion à faire. Mais elle est quand même très inefficace parce que les appels de méthodes prennent beaucoup de temps, particulièrement en Objective-C. T'as qu'à faire un coup d'Instruments, tu verras que le code passe son temps dans objc_msg_send().
Merci Céroce, je viens de faire de nouveaux tests, le problème venait d'erreurs de calcul, mais on accède bien à toute l'image d'origine avec ton code effectivement. Pour l'instant, le résultat que j'obtiens n'est pas exactement celui souhaité, mais je vais y arriver.
Je fais :
et cela me fait une parfaite symétrie avec la gauche de l'image. Super! Comment récupérer le pixel à la position (x,y) ??? J'y retourne!
Merci encore.
Est-il possible d'écrire les routines critiques en Swift pour éviter les appels de messages, et laisser le reste en Objective-C ?
Par contre, le pont ObjC->Swift a forcément un impact sur les performances.
Où je voulais en venir est qu'il faut absolument éviter tout appel à une méthode, mais même un appel de fonction est trop lent. Il faut au minimum la déclarer inline.
Nous sommes ici dans des problématiques où on fait des millions de fois la même simple opération et la moindre différence est notable. Par exemple, il faut aussi faire en sorte de bien utiliser la mémoire cache en accédant à des adresses successives et éviter les conditions qui empêchent la parallélisation sur les différentes pipelines du CPU.
Dans tous les cas, même en programmant de façon inefficace, Herve devrait voir une grosse amélioration des performances, surtout s'il s'arrange pour travailler avec plusieurs cores.
Avec NSBitmapImageRep on est à la source (on tape le vrai bitmap) et dans le cas de CIImage on est à l'arrivée au niveau du contexte graphique côté GPU (on tape sur une projection). C'est une différence fondamentale qui a un impact important sur les perfs.