La vidéo ci-dessous montre un saut effectué avec un skateboard. On souhaite déterminer la hauteur de ce saut. Elle est égale à l'altitude maximale atteinte par la roue arrière, la roue avant étant levée en premier.
Le défi consiste à écrire un programme en Python qui analyse automatiquement la vidéo. Il s'agit de tracer une courbe donnant la position de la roue arrière en fonction du temps, permettant ainsi de connaître la hauteur du saut. Le travail à effectuer s'appuie sur les données suivantes :
Le programme en Python exploitera les bibliothèques :
En fait, nous verrons que la vidéo se décompose en 76 photos, nommées "image_0.png" à "image_75.png". La roue arrière reste constamment éclairée par le soleil, au moins en partie, sauf pour la trente-septième photo où elle passe complètement dans l'ombre.
La photo "image_36.png" est donc problématique car la roue passe dans l'ombre. Sa couleur change et se distingue difficilement de celle des feuilles d'arbres, en haut à gauche de la vidéo. La position de la roue arrière pour cette photo en particulier est difficile à identifier. Des solutions pourraient être mises en œuvre, mais elles complexifieraient le programme.
OpenCV inverse les couleurs, en indiquant d'abord la quantité de bleu, puis la quantité de vert, enfin la quantité de rouge. La mise en œuvre d'une image avec OpenCV puis son affichage, directement avec Matplotlib, engendre une inversion des couleurs rouge et bleu. Il faut donc enregistrer l'image puis la rouvrir pour l'afficher correctement.
Exemple de code :
#!/usr/bin/python3 import cv2 import numpy as np import matplotlib.pyplot as plt from PIL import Image # Image noire de 4 pixels en largeur par 3 pixels en hauteur img=np.zeros((3,4,3)) # Image entièrement verte for x in range(0,4): for y in range(0,3): img[y][x]=[0,255,0] # Ajout de trois points rouges img[0][0]=[0,0,255] img[1][3]=[0,0,255] img[2][1]=[0,0,255] # Enregistrement de l'image cv2.imwrite("dessin.png",img) # Visualisation de l'image voir=Image.open("dessin.png") plt.imshow(voir) plt.show()
Résultat :
Le Python étant un langage interprété, il est relativement lent. Faire des boucles sur les trois couleurs primaires de chaque pixel pour toutes les photos de la vidéo prendrait un temps considérable. On exploite donc les possibilités de la bibliothèque numpy pour éviter ces boucles.
En reprenant l'image précédente :
#!/usr/bin/python3 import cv2 import numpy as np image=cv2.imread('dessin.png') couleur_cherchee = np.where((image[...,0] == 0) & (image[...,1] == 0) & (image[...,2] == 255) ) print(image.shape) print() print(image) print() print(couleur_cherchee)
Résultat :
(3, 4, 3) [[[ 0 0 255] [ 0 255 0] [ 0 255 0] [ 0 255 0]] [[ 0 255 0] [ 0 255 0] [ 0 255 0] [ 0 0 255]] [[ 0 255 0] [ 0 0 255] [ 0 255 0] [ 0 255 0]]] (array([0, 1, 2]), array([0, 3, 1]))
Le code ci-dessous doit être placé dans un dossier comportant :
#!/usr/bin/python3 import cv2 import numpy as np import matplotlib.pyplot as plt # Vidéo à traiter source="source.mp4" # Intervalles des couleurs pour le vert fluo rouge_M=234 ; rouge_m=120 ; vert_M=234 ; vert_m=170 ; bleu_M=46 ; bleu_m=12 # 1 cm pour 10 px echelle=10.0 # Caractéristiques de la vidéo video=cv2.VideoCapture(source) nb_im=int(video.get(cv2.CAP_PROP_FRAME_COUNT)) h=int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) l=int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) fps=int(video.get(cv2.CAP_PROP_FPS)) duree=float(nb_im)/float(fps) # Images de la vidéo placées dans le dossier dossier_images for num_img in range(nb_im): ret,frame=video.read() nom_image="dossier_images/image_"+str(num_img)+".png" cv2.imwrite(nom_image, frame) video.release() # Traitement des images tableau_hauteur_pixels=[] for num_img in range(nb_im) : image=cv2.imread('dossier_images/image_'+str(num_img)+'.png') couleur_cherchee = np.where((image[...,2]<=rouge_M) & (image[...,2]>=rouge_m) & (image[...,1]<=vert_M) & (image[...,1]>=vert_m) & (image[...,0]<=bleu_M) & (image[...,0]>=bleu_m)) hauteur_pixel_mini=h for i in range(len(couleur_cherchee[0])) : if (couleur_cherchee[0][i]<hauteur_pixel_mini) : hauteur_pixel_mini=couleur_cherchee[0][i] tableau_hauteur_pixels.append(hauteur_pixel_mini) # Tracé de la courbe x=np.linspace(0,duree,nb_im) y=(h-np.array(tableau_hauteur_pixels))/echelle plt.figure("Altitude de la roue en fonction du temps") plt.xlabel("Temps (s)") plt.ylabel("Hauteur (cm)") plt.plot(x, y, marker='o', color='#00ff00') plt.show()
Résultat :
La courbe obtenue comporte trois phases :
La mesure serait plus facile et plus précise si on fixait sur le skateboard une diode électroluminescente, rouge par exemple :