Analyse temps-réel de flux vidéo automotive - GIPSA-lab

Commentaires

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

Documents pareils