Traitement d'images et appels de fonctions coûteux

Traitement d'images et appels de fonctions coûteux - C++ - Programmation

Marsh Posté le 15-01-2006 à 17:42:21    

Bonjour à tous,
j'ai un petit problème avec un programme en c++.
 
Voilà j'ai une application qui récupère le flot d'images d'une webcam à 30 images secondes.
La webcam me fournit un tableau en yuv, que je convertis en rgb avant affichage.
 
Avec une conversion "naïve" et un appel pour chaque pixel d'une fonction "yuv_to_rgb" je consomme en gros 70% de cpu
Avec une conversion naïve mais pas d'appel de fonction (donc le code de la fonction est dans la boucle qui parcourt les pixels) je consomme en gros 40% de cpu
Avec une conversion "intelligente" (en entier et pas en flottant, avec lookup table et tout), je tombe à un peu moins de 20% de cpu.
C'est donc sur cette base que je travaille.
 
 
Mon problème est le suivant : je dois appliquer différents "filtres" à l'image, comme par exemple des modifications de contrastes, de luminosité, une détection de zones par agglomérations de composantes connexes et encore d'autres trucs que je rajouterai prochainement. Et le tout toujours sur la video à 30fps.
 
Pour le moment je complexifie petit à petit la boucle principale (celle qui parcourt les pixels), ce qui fait que le code est de plus en plus complexe à chaque étape.
De plus, comme je parcours les pixels 4 par 4 (car la structure en yuv est en 4:2:2 pour les connaisseurs), j'ai le code dupliqué 4 fois !
 
Pour des raisons évidentes de compréhension du code et de "maintenance", j'aimerai avoir un truc du genre :
Pour un bloc de 4 pixel {
    traitement n°1
    traitement n°2
}
 
Mais les appels de fonctions sont  coûteux et les  fonctions trop grosses pour être "inline"
 
Existe-il une autre solution pour garder de bonnes perfs tout en appelant  des fonctions ? Un mécanisme allégé d'appel de fonctions en gros ?


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 15-01-2006 à 17:42:21   

Reply

Marsh Posté le 15-01-2006 à 22:04:18    

Tu devrais
essayer de
mijoter un
pré-processeur
libre, optimisant
assez rapide
turing complet et
entièrement
standardisé !

Reply

Marsh Posté le 15-01-2006 à 23:32:41    

Xavier_OM a écrit :


Mais les appels de fonctions sont  coûteux et les  fonctions trop grosses pour être "inline"


Si tes fonctions sont "grosses" et gourmandes, le cout de l'appel sera surement négligeable. N'utilise pas les fonctions inline sauf si ton profiler te dit le contraire. Au fait, tu en utilises un ?
 

Reply

Marsh Posté le 15-01-2006 à 23:42:59    

deja comme dit ++fab, profiel ton code pour sortir le vrai bottleneck. Ensuite : inlining aprtiel, unrolling, template, SIMD (altivec ou SSE)

Reply

Marsh Posté le 16-01-2006 à 03:08:19    

 
tu affiches comment au fait ?
 
tu es sûr que la conversion en RGB s'impose ?
 
en tout cas opères un maximum sur la composante Y, par ligne de scan
et n'utilises U & V, que lorsque c'est nécessaire (et dans leur résolution d'origine)
 
sinon quelle est la résolution de ton image ?
 
pour les perfs disons qu'il faut prévoir une projection 3D par pixel,
c'est rien... mais c'est beaucoup plus que n'importe quel jeu vidéo,
 
peut être que l'acquisition (ou le blit) pompe un max aussi de son côté
bref...
 
 
 
 
 

Reply

Marsh Posté le 16-01-2006 à 07:24:50    

++fab a écrit :

Si tes fonctions sont "grosses" et gourmandes, le cout de l'appel sera surement négligeable. N'utilise pas les fonctions inline sauf si ton profiler te dit le contraire. Au fait, tu en utilises un ?


 
euh j'utilise gprof mais je débute avec lui, je suis à la recherche d'une bonne méthode de profiling en effet.
 
Pour le template je ne comprends pas trop, si qqun a de la doc ca m'intéresse (ca a un rapport avec les classes Templates ? parce que moi à part les int et les float, je touche à rien...)
 
fra0: oui je bosse en RGB, mais avec une conversion en entier + lookup table ca va assez vite pour le moment, mon but est de détecter des diodes de couleurs qui bougent face à une webcam, et actuellement je perds plus de temps à détecter les zones de couleurs qu'à faire les autres traitements genre la conversion.
 
L'image c'est du 320*240.


Message édité par Xavier_OM le 16-01-2006 à 07:26:06

---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 16-01-2006 à 14:10:43    

Ben prends un (bon) bouquin sur les templates, et n'effectue pas des traitements 4 pixels/4 pixels, mais sur des blocs plus gros, 40x60 par exemple.


Message édité par el muchacho le 16-01-2006 à 14:11:18

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 16-01-2006 à 17:27:56    

les diodes qui bougent vraiment ou elles changent de couleur / luminosité ?

Reply

Marsh Posté le 16-01-2006 à 18:11:52    

Les diodes bougent vraiment (je m'intéresse aux mouvements).
Pour les templates je vais me renseigner de ce pas, je ne conçois pas comment on peut traiter des blocs aussi gros d'un coup  :ouch:


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 16-01-2006 à 18:18:11    

40x60 gros ??? Euh, je ne vois pas bien où est le pb, ou alors les traitements ne sont pas ceux que j'imagine ??


---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 16-01-2006 à 18:18:11   

Reply

Marsh Posté le 16-01-2006 à 18:27:17    

el muchacho a écrit :

40x60 gros ??? Euh, je ne vois pas bien où est le pb, ou alors les traitements ne sont pas ceux que j'imagine ??


 
 
Je détaille :
j'ai un tableau de YUV, appelé yuv_buff, de taille width*height*1.5  (car sur l'image, pour un carré de 4 pixels, on a 4 y mais un seul u et un seul v)
j'ai un tableau de RGB, appelé rgb_buff. de taille width*height*4 (car les composantes sont b g r alpha)
 
Je fais un read sur la webcam, ca me sort le tableau de yuv.
Je fais alors :
pour tous les pixels, par bloc de 4 (lus comme il faut avec des pointeurs qui vont où il faut dans yuv_buff) {
    lire y u v du premier pixel
    les convertir en r g b par une conversion en entier
    les modifier pour augmenter leur contraste et leur luminosité
 
    lire y u v du second pixel
    idem
 
    idem pour le 3ème pixel
 
    idem pour le 4ème pixel
}
Mon tableau de rgb a été rempli et modifié en une passe, je peux l'afficher.
 
NB : si je passe les pixels un par un, je constate une baisse importante des perfs, la structure du tableau de yuv s'adapte bien à ce traitement en bloc de 4.
 
Si je veux en plus savoir si le pixel appartient à un groupe plutôt rouge, vert ou bleu, je me fais par exemple un tableau du genre  
if (pixel rouge) { zones[num_pixel] = rouge }
Mais je dois copier coller ce "if" dans les 4 sections de la boucle for ci-dessus
 
Evidement je peux faire une méthode du genre "traiter_pixel_rouge" que j'appelerai quatre fois, mais cet appel est couteux je trouve (cf les chiffres dans mon premier post)
 
 
Je ne vois pas :
1-ce que les templates peuvent m'apporter
2-comment traiter un bloc de 40*60 "en une fois"
 
 :??:


Message édité par Xavier_OM le 16-01-2006 à 18:29:39

---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 16-01-2006 à 18:33:45    

j'ai pas tout lu mais
 
    lire y u v du premier pixel
    les convertir en r g b par une conversion en entier
    les modifier pour augmenter leur contraste et leur luminosité  
 
 pourquoi faire ça après ?
 d'un coté tu as une addition sur Y, de l'autre tu as addition, mutltiplication etc sur R et G et B ?

Reply

Marsh Posté le 16-01-2006 à 20:04:33    

Remarque pertinente ! A force de tester différentes possibilités pour détecter les diodes je me suis même pas rendu compte que certaines étapes pouvaient être plus rapide avant conversion  :pt1cable:  
 
Mais bon imagine que je veuille faire une détection de zones par un algo d'étiquetage en composantes connexes (ce genre de truc, je sais pas si cette page est la plus claire.... http://telesun.insa-lyon.fr/~teles [...] etail.html ).
 
J'ai un algo qui marche bien, mais qui se base sur le tableau en rgb déjà rempli pour le moment. En gros c'est "convertit toute l'image en rgb" puis "repart au début de l'image et run l'algo". C'est pratique pour développer, mais évidemment il y a une passe supplémentaire par algo à appliquer. Donc pour le code final je veux optimiser.
 
Là par exemple, pour économiser cette passe "inutile", il faut que je numérote "au fur et à mesure" du remplissage du tableau en rgb, vu que j'ai seulement besoin du pixel immédiatement à gauche et immédiatement supérieur du pixel courant pour ce genre de truc. Mais ce qui me gêne c'est que si j'adapte ca dans ma boucle, j'obtiens un code moins lisible et surtout chiant à maintenir le jour où je veux rajouter / retirer cet algo (ou un autre du même genre, surtout que celui-ci avec des ensembles de tarjan pour faciliter les unions d'ensemble, c'est pas le plus lisible...).
 
Hélas je ne pense pas qu'il y ait de solutions à ce genre de truc, pour limiter le nb de passes ben j'ai forcément des passes plus complexes :/
 
Un conseil de profiler sous linux ?


Message édité par Xavier_OM le 16-01-2006 à 20:08:17

---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 17-01-2006 à 16:19:23    

Valgrind ?


---------------
Viendez vous battre à Prologin \o/
Reply

Marsh Posté le 17-01-2006 à 17:11:09    

Je ne sais pas à quoi ressemble ton code, mais 1), que tu inlines tes fonctions ou que tu les recopies à la main à l'intérieur de la boucle, c'est exactement la même chose, ou alors, change de compilateur, et 2), le principe des templates, si tu l'utilises bien, ce qui n'est pas si facile, va te permettre, en quelque sorte, de forcer l'inlining et d'avoir un programme plus lisible (modulo le fait qu'on sache lire la programmation template en C++, ce qui n'est pas si facile non plus). Cherche un article de Todd Veldhuizen, tu verras ce que ça permet de faire, la programmation template dans toute sa splendeur. N'oublie pas d'utiliser les options d'optimisation de ton compilateur, ça change souvent la vie. Je ne sais pas si tu es expert dans l'optimisation de code, mais j'imagine que tu sais qu'il ne faut pas utiliser de variables dynamiques, limiter les appels systèmes en général, les interdire dans les boucles, et qu'une boucle bien nestée est ultra-optimisable automatiquement par ton compilateur.

Reply

Marsh Posté le 17-01-2006 à 21:07:50    

ZoUmMo a écrit :

Je ne sais pas à quoi ressemble ton code, mais 1), que tu inlines tes fonctions ou que tu les recopies à la main à l'intérieur de la boucle, c'est exactement la même chose, ou alors, change de compilateur, et 2), le principe des templates, si tu l'utilises bien, ce qui n'est pas si facile, va te permettre, en quelque sorte, de forcer l'inlining et d'avoir un programme plus lisible (modulo le fait qu'on sache lire la programmation template en C++, ce qui n'est pas si facile non plus). Cherche un article de Todd Veldhuizen, tu verras ce que ça permet de faire, la programmation template dans toute sa splendeur. N'oublie pas d'utiliser les options d'optimisation de ton compilateur, ça change souvent la vie. Je ne sais pas si tu es expert dans l'optimisation de code, mais j'imagine que tu sais qu'il ne faut pas utiliser de variables dynamiques, limiter les appels systèmes en général, les interdire dans les boucles, et qu'une boucle bien nestée est ultra-optimisable automatiquement par ton compilateur.


 
Ben mon compilo c'est g++, et il tente d'inliner les fonctions possédant le mot clé inline, mais ce n'est pas garanti (lu dans la man page), d'où le copier coller actuel.
Je vais me renseigner sur les templates, merci pour les références  :jap:


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 18-01-2006 à 00:30:32    

Avant de te lancer dans l'utilisation avancée des templates, prend ton profiler, gprof par exemple il n'est pas difficle à utiliser. Lis le man gprof et repère les goulots d'étranglement. Ensuite, tu joues avec les options de compilations, et tu essaye d'optimiser les portions critiques.
 
-Winline devrait t'indiquer pourquoi il n'inline pas. (Et il inline meme certaines fonctions non déclarées inline.)

Reply

Marsh Posté le 18-01-2006 à 22:21:47    

Comme vous le suggériez tous un peu, je suis plutôt parti sur le fignolage (économie du nb de tests, de déclaration...) mais pas sur les templates : il faudrait refaire toute la conception APRES avoir compris l'esprit du truc.
 
 :jap:  :hello:


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 19-01-2006 à 00:40:00    

c'est dommage, ça doit être assez rapide de tout refaire avec la Bonne Méthode(tm)
regarde sur de vulgaires générateurs de nombres aléatoires par exemple :
 

Code :
  1. // avec deux types de base
  2. typedef unsigned long K ;
  3. typedef __int64 I64;
  4. // je décris un premier générateur (à très courte période) tout se fait dans l'opérateur () qui renvoie 32bits "aléatoires"
  5. template <K Multiply=1754349821ul> struct random
  6. {
  7.     K multiply,current,carry;
  8.            
  9.     random(K seed0 = 12345678ul, K seedc = 1ul, K multiplier = Multiply) : multiply(multiplier), current(seed0), carry(seedc) {}
  10.     K operator()()
  11.     {
  12.         I64 digit = I64(multiply) * current + carry ;
  13.         current = digit & 0xFFFFFFFF ;
  14.         carry   = digit >> 32 ;
  15.         return current ;
  16.     }
  17. };
  18. // je décris un second générateur (moins aléatoire)
  19. template<K R=43,K S=22> struct subtract
  20. {
  21.     K alea[R],n,c;
  22.     random<1929682203ul> r;  // juste pour initialiser l'état
  23.      
  24.     subtract() : n(0), c(r()%2), r()
  25.     {
  26.         std::generate(alea,alea+R,r);
  27.     }
  28.    
  29.     K operator()()
  30.     {
  31.         I64 digit=alea[(n-S)%R]-alea[(n-R)%R]-c;
  32.         c=digit<=0ll;digit+=4294967291ll*c;
  33.         alea[n]=digit;++n%=R;
  34.         return digit;
  35.     }
  36. };
  37. // et trois classes pour combiner INFINIMENT les résultats
  38. // c'est ma grammaire en quelque sorte
  39. template<class GeneratorA,class Operation,class GeneratorB> struct combine : std::pair<GeneratorA,GeneratorB>
  40. {
  41.     K operator()()
  42.     {
  43.         return Operation()(this->first(),this->second());
  44.     }
  45. };
  46. struct XOR
  47. {
  48.       K operator()(const K&A,const K&B) const
  49.       {
  50.           return A^B;
  51.       }
  52. };
  53. struct PLUS
  54. {
  55.       K operator()(const K&A,const K&B) const
  56.       {
  57.           return A+B;
  58.       }
  59. };
  60. int main(int argc, char *argv[])
  61. {
  62.   // maintenant je peux déclarer un troisième générateur comme ça :
  63.     combine < subtract<>,PLUS,random<> > gen;  // sa période est d'environ 2^1400bits  (!)
  64.     int de6=gen()%6+1;
  65. // quelle est la période de c'ui-ci ?  
  66. // combine<subtract<0xFFFF,0x8000>,XOR,combine<subtract<>,PLUS,random<2131995751> > > q;
  67. // [ETC]


 
merci
pardon


Message édité par fra0 le 19-01-2006 à 03:16:40
Reply

Marsh Posté le 19-01-2006 à 19:15:47    

Merci pour le bout de code, ca pourra toujours être utile d'avoir ça dans un coin  :)


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed