Analyse temps-réel de flux vidéo automotive - GIPSA-lab
Transcription
Analyse temps-réel de flux vidéo automotive - GIPSA-lab
Analyse temps-réel de flux vidéo automotive AXIS / IEF - Université PARIS SUD Marius Vasiliu Quelle place pour les GPU dans l’automotive ? Le nombre d’applications embarquées l’automotive est en pleine croissance dans reconnaissance de panneaux routiers détection / suivi de voie / lignes blanches / autres voitures détection / évitement de pié piétons aide à la conduite dans des conditions extrêmes (neige, brouillard, nuit, pluie torrentielle etc.) vision panoramique (360° (360°), aide au parking surveillance nonnon-intrusive du conducteur geogeo-localisation "intelligente" (GPS / GSM / vidé vidéo) Les capteurs optiques (caméras, IR, rétines) subissent des baisses de prix spectaculaires Demande d'une puissance de calcul embarquée de 10000 à 1000000 supérieure à un simple contrôle de trajectoire ... Evolution des applications automotives R 1000 F R Collision avoidance F F SS R F SS I I Pre-crash system Traffic sign recognition 10 SS Light assistant Driver monitoring Driver monitoring F Entire drive Entire drive environment environment detection detection 20 13 Blind spot F 1 R R SS SS FF F R 360° display reconstruction 0.1 Night vision display Wide angle display Information F Lane departure warning 20 15 Alarm F Driver assistant Semi-auto drive Pedestrian recognition R Lane keep assistant I SS R Road keep assistant 100 SS F F trol Co n R Rear parking camera 20 06 20 07 20 09 20 11 courtesy of NEC Quelle place pour les GPU dans l’automotive ? Aujourd'hui : une multitude de dispositifs type "boîte noire" connectés sur un bus automotive (CAN / FlexRay) chaque capteur à son module spé spécifique de traitement une architecture type µC, DSP ou FPGA / ASIC l'inté l'intégrateur les achè achète sans avoir un contrôle fin sur le fonctionnement ( juste un dialogue "normé "normé" ) sorties événementielles à remonter dans le calculateur central consommation moyenne ( 55-20W par dispositif ) Demain : une architecture unique, souple et puissante capteurs "raw " et actionneurs en communication directe avec "raw" une unité unité centrale qui peut consommer 6060-120W souplesse : activation sé sélective des briques logicielles accè accès fin aux donné données et aux algorithmes : fusion et filtrage de donné données, tolé tolérance aux pannes, robustesse Exemple d'application automotive : le projet LOVe Le projet LOVe est destiné a trouver une solution pour la détection et l'évitement des vulnérables (piétons) : La scène (urbaine) surveillée a une profondeur de 5 à 40-50 m (devant la voiture) Voiture Renault équipée d'un LIDAR et d'une paire stéréo de caméras en niveaux de gris Une solution fiable n'a de sens que dans le temps réel: temps réaction < 100 ms (inférieur à l'humain) période du LIDAR : ≈90ms (10(10-11 Hz) période des images : ≈33ms (25(25-30 Hz) Nécessite : algorithmes rapides, fiables et de la fusion de données (trajectoire, vidéo & LIDAR) Approches et méthodologie Analyse des algorithmes par rapport à l'implantation HW ciblée Sélection des meilleurs algorithmes en terme de: résultats (fiabilité (fiabilité), régularité gularité (accè (accès mé mémoire etc.) potentiel / gain d'accé d'accélération (parallé (parallélisme) Analyse de chaque chaîne de décomposition en briques Décision de codage HW en fonction : traitement de la complexité complexité et du parallé parallélisme fin de l'adaptation à la cible HW de l'interaction avec les autres briques Obtention du meilleur système embarqué possible et Architecture typique des traitements Acquisition Rectification Calcul, détection de disparité st. Histogramme de V-disparité Détection du plan de la route caméras paramètres, carte de rectification Projection sur le plan de la route LIDAR Histogramme de U-disparité caractéristiques morpho-métriques et cinématiques des piétons Détection d'objets vulnérables 2D Paramètres voiture : vitesse, direction Détection d'objets vulnérables 2/3D Fusion et filtrage spatio-temporel Analyse des "listes" détections & décisions commande voiture Distribution de la puissance de calcul Puissance demandée par brique (en %) Rectification Projection route Disparité stéréo U et V disparité Position de la route Autres La disparité stéréo demande le plus (≈ 60%) Potentiel d'accélération (parallélisme et régularité) En fonction de la nature de chaque algorithme on peut apprécier le potentiel d'accélération : Rectification stéréo Parallélisme 10 Projection plan route 10 Disparité stéréo Régularité 7 7 8 Histogramme U/V disp 9 6 Détection plan route 10 8 Détection vulnérables 9 5 Fusion, décision 3 3 2 Choix des candidats pour l'accélération et l'implémentation HW Acquisition Rectification Calcul, détection de disparité st. Histogramme de V-disparité Détection du plan de la route caméras paramètres, carte de rectification Projection sur le plan de la route LIDAR Histogramme de U-disparité caractéristiques morpho-métriques et cinématiques des piétons Détection d'objets vulnérables 2D Paramètres voiture : vitesse, direction 90% de puissance demandée Détection d'objets vulnérables 2/3D Fusion et filtrage spatio-temporel Analyse des "listes" détections & décisions commande voiture Implémentation HW Plusieurs architectures ont été envisagées et testées : PC seul utilisant le parallélisme multi-coeur (2/4/8), multi-thread et de registre (SIMD, MMX, SSE 2-4) 0.5 img/s img/s pour programme correct mais nonnon-optimisé optimisé gain important puis saturation due aux accè accès mé mémoire (18 Hz) PC + carte PCI/USB ImapCAR (NEC) gain en puissance (pontuellement (pontuellement x50) et moins de consommation programmation trè très difficile et limitation de la taille d'image à des multiples de 128 (512, 640, 768) saturation de la bande passante entre PC et la carte PC + carte DSP DaVinci (TI) utilisation de l'assembleur VLIW ou d'intrinsè d'intrinsèques pour espé espérer un vrai gain en puissance programmation difficile : transferts DMA, source sté stéréo, etc. saturation de la bande passante entre PC et la carte Implémentation sous Cuda-GPU (NVidia) Les processeurs GPU offrent la bande passante la plus importante avec l'UC (de 200 Mo/s à 3 Go/s) NVidia offre une API portable, gratuite en C/C++ Facilité à paralléliser les algorithmes si l'on respecte les restrictions de l'architecture (nombre de registres, mémoire locale, mémoire globale, textures etc.) Compatibilité GPU ascendante : gain en puissance, quantité de mémoire et bande passante On a utilisé 2 architectures cibles pour les tests : PC portable : CPU = CoreDuo x2 et GPU = QuadroFX 3600M PC fixe : CPU = CoreDuo x4 et GPU = GTX 285 Architecture cible finale : PC portable, CoreExtreme x4, double carte graphique GTX 280M Architecture embarquée ciblée PC portable qui doit assurer l'ensemble des traitements (bas, moyen et haut niveau) en exploitant par "asynchronie" 100% des cœurs CPU et GPU Core2 Extreme µP µP µP µP chip-set NVidia DDR3 8Go bus PCI Express DDR3 1Go GTX 280M 128 PE ( 16 x 8 ) GPUs GTX 280M 128 PE ( 16 x 8 ) GPUs DDR3 1Go DVIDVI-D Utilisation de l'architecture GPU NVidia Potentiellement le facteur d'accélération peut attendre plus de x1000 mais on est limité par : le nombre de registres la vitesse d'accè d'accès de la mémoire globale (pas de cache) la quantité quantité de mé mémoire partagé partagée Chaque brique LOVe a été réécrite complètement pour optimiser son fonctionnement sur les processeurs NVidia. Les performances ont été "profilé "profilées" puis ré ré-optimisé optimisées Résultats de l'implémentation GPU Cuda Rectification stéréo: moins de 1 ms (utilisation des textures) Disparité stéréo: entre 18 et 40 ms (en fct. de la carte) Histogrammes de U et V disparité : entre 3-5 ms Détection du plan de la route : entre 4-7 ms Transferts GPU-CPU : entre 5-8 ms Affichages : entre 10 et 16 ms (synchro vidéo) Lecture RtMaps ou acquisition : 2-10 ms Les temps ne s'additionnent pas forcement, car le système est fortement parallèle ! Résultats d'implémentation HW : Comparaison Processeur UC Proc. spé spécialisé cialisé Charge UC Vitesse max. Consommation CoreDuo x4 aucun 100% 0.50.5-18 img/s img/s 300 - 400 W CoreDuo x2 ImapCAR 30% <10 img/s img/s 220 - 300 W CoreDuo x4 DSP DaVinci 25% 5-14 img/s img/s 250 - 300 W CoreDuo x2 QuadroFX 3600M 8% 35 img/s img/s 120 - 150 W CoreDuo x4 GTX 285 6% 50 img/s img/s 300300- 400 W CoreExtreme x4 GTX 280M x 2 4% 5050-70 img/s img/s 80 - 160 W En utilisant l'implantation NVidia/Cuda NVidia/Cuda on atteint >200% de la vitesse tempstemps-réel tout en laissant de la place sur l'UC aux algorithmes de "haut niveau" type fusion de donné données, suivi de d'objets etc. Analyse de flux en temps-réel : data-flow Transfert hôte - GPU Raw img L Raw img L Raw img R Raw img R Capture/lecture de flux vidé vidéo Analyse de flux en temps-réel : data-flow Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R Rectification épipolaire Analyse de flux en temps-réel : data-flow Cartes de rectification Map L dXdY Map R dXdY Map L dXdY Map R dXdY Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R Rectification épipolaire Analyse de flux en temps-réel : data-flow Map L dXdY Map R dXdY Map L dXdY Map R dXdY Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R Disp L-R Disparité Disparité leftleft-right Analyse de flux en temps-réel : data-flow Map L dXdY Map R dXdY Map L dXdY Map R dXdY U-Disparité Disparité U-Disp Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R Disp L-R Disparité Disparité leftleft-right V-Disp V-Disparité Disparité Analyse de flux en temps-réel : data-flow Map L dXdY Map R dXdY Map L dXdY Map R dXdY U-Disparité Disparité Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R U-Disp Disp L-R Disparité Disparité leftleft-right Plan de la route V-Disp Road plane V-Disparité Disparité Analyse de flux en temps-réel : data-flow Road param V-Disp U-Disp Disp L-R Map L dXdY Map R dXdY Map L dXdY Map R dXdY U-Disp Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R Disp L-R V-Disp Road plane Analyse de flux en temps-réel : data-flow Road V-Disp param U-Disp Disp L-R Map L dXdY Map R dXdY Map L dXdY Map R dXdY U-Disp Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R Affichage D3D9/10 sur GT8x, GT92 Out Img backbuffer Disp L-R Disp L-R backbuffer V-Disp Img backbuffer V-Disp U-Disp Img backbuffer Screen (linear frame buffer) Road plane Road Plane backbuffer Analyse de flux en temps-réel : data-flow Road param V-Disp U-Disp Disp L-R Map L dXdY Map L dXdY Map R dXdY Map R dXdY U-Disp Raw img L Raw img L Rect img L Raw img R Raw img R Rect img R Affichage D3D9/10 sur GT200 Disp L-R V-Disp Road plane Out Img Disp L-R V-Disp Img U-Disp Img Road Plane RGBXtexture RGBXtexture RGBXtexture RGBXtexture RGBXtexture Out Img backbuffer Disp L-R backbuffer V-Disp Img backbuffer U-Disp Img backbuffer Road Plane backbuffer Screen (linear frame buffer) Distribution sur plusieurs devices Cuda HOST GPU 0 FIFO pour GPU0 img R img L img R img L imgimg R R imgimg L L img R img L img R imgL R img img L rect R rect L V-disp road disp U-disp GPU 1 imgimg R R imgimg L L FIFO pour GPU1 img R img L img R imgL R img img L rect R rect L V-disp road disp U-disp Distribution sur plusieurs devices Cuda On décide de distribuer les images entre les devices GPU0 traite les images paires, GPU1 les images impaires Application multi-thread spécialisés : un thread pour l'acquisition / lecture du flux, mise en FIFO un thread pour gé gérer l'ensemble des opé opérations sur GPU0 un thread pour gé gérer l'ensemble des opé opérations sur GPU1 Assigner chaque thread à un cœur CPU dédié : au dé début du processus (gpu_nb (gpu_nb est 2) : ::SetProcessAffinityMask(::GetCurrentProcess(), (1<<(gpu_nb+1))(1<<(gpu_nb+1))-1); pour chaque thread (id est 0, 1 ou 2) : ::SetThreadAffinityMask(::GetCurrentThread(), 1<<id); pour chaque thread dé dédié dié GPU (id est 0 ou 1): cudaSetDevice(id); Attention : Cuda RunTime API cantonne les ressources cuda (host / device) à la durée de vie du thread ! Distribution des threads CPU thread 3 HOST thread 1 FIFO pour GPU0 img R img L img R img L imgimg R R imgimg L L GPU 0 img R img L img R imgL R img img L rect R rect L FIFO pour GPU1 cpu 2 road disp U-disp cpu 0 thread 2 imgimg R R imgimg L L V-disp img R img L img R imgL R img img L rect R rect L GPU 1 V-disp road disp U-disp cpu 1 Comment gagner du temps : parallélisme et asynchronisme Identifier les sous-systèmes qui peuvent fonctionner en parallèle (9): les 4 cœ cœurs CPU la lecture / acquisition par transfert DMA la communication device - hôte l'exé l'exécution des kernels par les 2 GPU Mettre tout en place pour assurer le maximum d'asynchronisme entre les sous-systèmes Paradigme producteur - consommateur Assurer soi-même la synchronisation (si nécessaire) Libérer au maximum les CPU pour d'autres tâches de "haut niveau" Si tout va bien : Timg = (ΣTkernel) / NbGpu Diagramme des tâches parallèles temps Acq/Lecture 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CPU 0 CPU 1 CPU 2 CPU 3 Host-GPU0 Host-GPU1 0 2 1 4 3 6 5 8 7 10 9 GPU 0 GPU 1 Amorçage Régime 11 Comment libérer les CPU ? Définir le comportement de synchronisation du thread dédié GPU (la valeur par défaut ne convient pas) : cudaSetDeviceFlags(cudaDeviceBlockingSync); cudaSetDeviceFlags(cudaDeviceBlockingSync); cudaDeviceScheduleAuto vaut (NbCxt> NbCxt>NbCpu) NbCpu) ? Yield : Spin cudaDeviceScheduleSpin : attente active (poolling (poolling)) cudaDeviceScheduleYield : donne du temps aux autres threads cudaDeviceBlockingSync : attente/blocage sur événement Allouer toujours la mémoire host avec cudaHostAlloc car elle est prête pour les transferts asynchrones DMA Utiliser la mémoire host pour la capture / lecture directe du flux vidéo (::ReadFile(...) ou appel au driver FireWire) Utiliser que des appels Cuda asynchrones et un stream de synchronisation Comment libérer les CPU ? Création d'un stream de synchronisation : cudaStream_t str; str; cudaStreamCreate(&str); cudaStreamCreate(&str); Insertion d'une tâche kernel dans le stream : kernel1<<grid, block, 0, str>> str>> (...); Insertion d'une tâche de copie dans le stream : cudaMemcpyAsync(h_data, cudaMemcpyAsync(h_data, d_data, d_data, taille, taille, cudaMemcpyHostToDevice, cudaMemcpyHostToDevice, str); str); cudaMemcpyAsync(d_data, cudaMemcpyAsync(d_data, h_data, h_data, taille, taille, cudaMemcpyDeviceToHost, cudaMemcpyDeviceToHost, str); str); Interrogation sur la fin des tâches : cudaStreamQuery(str)== cudaSuccess cudaStreamQuery(str)==cudaSuccess Attente bloquante jusqu'à la fin des tâches : cudaStreamSynchronize(str); cudaStreamSynchronize(str); Comment libérer les CPU ? Création d'un événement de synchronisation : cudaEvent_t evt; evt; cudaEventCreate(&evt); cudaEventCreate(&evt); Insertion d'un événement de synchro dans le stream : cudaEventRecord(evt, cudaEventRecord(evt, str); str); Interrogation sur l'activation de l'événement : cudaEventQuery(evt)== cudaSuccess cudaEventQuery(evt)==cudaSuccess Attente bloquante jusqu'à l'activation de l'événement : cudaEventSynchronize(evt); cudaEventSynchronize(evt); Destruction de l'événement de synchronisation : cudaEventDestroy(evt); cudaEventDestroy(evt); Destruction du stream de synchronisation : cudaStreamDestroy(str); cudaStreamDestroy(str); Autres astuces et optimisations CUDA Utiliser, quand cela est possible, des kernels template, sinon, passer des paramètres par la mémoire const Attention, le compilateur n'est pas super-futé : Une variable de type uchar4 est parfois placé placée en LocalMem car pour lui c'est une structure, même si elle n'a que 32 bits, utiliser à la place un DWORD avec typecast "sauvage" Utiliser des types plus petits que 32 bits ne produit pas toujours une économie de registres, il faut les regrouper Utiliser le dé déroulement des boucles, mais attention il ne marche plus à partir de la version 2.3 s'il y a des intrinsics Utiliser Decuda pour décompiler et analyser le code produit, pour comprendre vous erreurs ou celles du compilateur Par défaut l'alignement des variables dans la shared memory se fait à l'octet : utiliser __builtin_align__(N) Comment afficher "proprement" plusieurs fenêtres ? Le Glut/Glew d'OpenGL est orienté C (mono-callback sans contexte) et mono-fenêtre Seule solution fiable : passer par Direct3D9/10 cré créer une classe C++ qui va gé gérer une fenêtre par instance de classe pré prévoir une mé méthode statique à appeler une seule fois, pour allouer et initialiser le Direct3DDevice9 et l'associer à un écran et à un contexte Cuda pour chaque nouvelle fenêtre allouer un nouveau Direct3DSwapChain9 à partir du Direct3DDevice9 associer la surface du backbuffer au Cuda (GT8x et GT92) allouer une texture de même taille et l'associer au Cuda (GT200) pré prévoir une mé méthode pour Map, Map, Unmap et Display pendant l'affichage utiliser la mé Present() avec le 5è 5ème méthode Present() paramè paramètre : D3DPRESENT_DONOTWAIT Comment afficher avec 2 GPU ? On a 3 solutions à notre disposition : Afficher chaque image (si Fimg<Fecran ) les images paires sont dé déjà sur GPU 0 : utiliser D3D9 comme avant : texture backbuffer écran les images impaires sont sur GPU 1 : il faut les transfé transférer en passant par host (il n'y a pas encore de transfert GPUGPU-GPU) Afficher que les images paires (surtout si Fimg>Fecran ) les images paires sont dé déjà sur GPU 0 : utiliser D3D9 comme avant : texture backbuffer écran Envoyer les images du GPU1 directement dans la mémoire vidéo (écran) du GPU0 à tester cas par cas, elle n'est pas documenté documentée ! utiliser DirectX 7 (DirectDraw (DirectDraw)) pour avoir accè accès direct à la mémoire écran de GPU 0 (primary (primary surface) locker en mé mémoire CPU cette surface, puis l'utiliser comme destination GPU1 host Qu'est-ce qui nous manque ... (wish list @ NVidia) Des API pour transférer directement des données entre plusieurs devices (cartes) Cuda Des API pour accéder directement (au moins en écriture) à l'écran (linear frame buffer) Des API pour contrôler finement les fréquences de fonctionnement et la consommation des chipset NVidia et des GPU NVidia Des API pour utiliser la mémoire des cartes NVidia comme destination des transferts DMA issues d'un support de stockage ou un bus FireWire ... Des intégrations mobiles encore plus compactes et moins gourmandes en énergie