Nous avons vu la semaine dernière comment reconnaître les chiffres présents sur un panneau de score. Cependant, nous avions un peu triché : l’image que nous traitions ne contenait que le tableau d’affichage. Dans cet article, nous allons corriger le tir en proposant une méthode pour détecter et extraire le tableau dans une image, afin de pouvoir faciliter l’extraction des chiffres.

Note : cet article utilise de nombreuses techniques que nous avons vues dans l’article sur la détection de formes. Si vous ne l’avez pas encore lu, il est disponible ici.

La méthode que nous proposer aujourd’hui consiste en la détection et la localisation du panneau d’affichage des scores dans une image. Une fois les coordonnées extraites, nous corrigerons l’image afin d’obtenir une vue « de face » du panneau, similaire à celle que nous avons utilisée la semaine dernière.

Panneau score basket
Image 1: Panneau score basket

Étape 1: Localisation du panneau

La première étape de notre méthode consiste à localiser le panneau. Plus précisément les 4 exterminées du panneau. Pour ce faire, nous allons seuiller l’image (l. 5) afin de mettre en évidence toutes les parties noires de l’image. Nous utiliserons quelques transformations morphologiques afin de ne pas prendre en compte les chiffres lors de la localisation. De plus, ces transformations d’ouverture (l. 9) et de fermeture (l. 8) nous permettent d’améliorer l’extraction du tableau. Pour réaliser ces opérations, nous avons besoin d’utiliser des éléments structurants que nous déclarons lignes 6 et 7.

import cv2
import numpy as np

image=cv2.imread('basket.jpg')
image_gris=cv2.inRange(image,(0,0,0), (75,75,75))
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,9))
kernel2=cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
image_traitee=cv2.morphologyEx(image_gris, cv2.MORPH_CLOSE, kernel)
image_traitee=cv2.morphologyEx(image_traitee, cv2.MORPH_OPEN, kernel)

im,cnt,hier=cv2.findContours(image_traitee,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for i in cnt:
    peri=cv2.arcLength(i,True)
    approx=cv2.approxPolyDP(i, 0.02*peri,True)
    if len(approx)==4 and peri >=100:
        contours=approx
Image de la scène seuilée
Image 2: Image de la scène seuilée

Maintenant que notre image est seuillée et nettoyée (image ci-dessus), nous pouvons passer à l’extraction de ses coordonnées. Nous commençons par utiliser la fonction cv2.findcontours()  ligne 11 afin de d’extraire tous les contours. Puis nous calculons le périmètre des contours (l. 14) et leurs approximations (l. 15) afin de ne garder que les contours ayant 4 sommets et un périmètre assez grand, correspondant à celui du panneau (l. 16).

Étape 2: Extraction et correction du panneau

Notre panneau étant identifié dans l’image, nous pouvons maintenant procéder à son extraction. Mais avant cela, une petite chose reste à prendre en compte : la déformation liée à la prise de vue. En effet, dans certains cas, le panneau ne sera pas en face de la caméra, causant ainsi une déformation de l’image. Il sera donc nécessaire de corriger la perspective, comme nous pouvons le voir dans l’image suivante.

Correction de la perspective
Image 3: Correction de la perspective

Pour ce faire, nous devons trier les coordonnées du contour afin de pouvoir calculer facilement les coordonnées dans notre espace de destination.

sel=np.array(contours.sum(axis=1))
rect=np.zeros((4,2),dtype='float32')

s = sel.sum(axis = 1)
rect[0] = sel[np.argmin(s)] #Point A
rect[2] = sel[np.argmax(s)] #Point C

diff = np.diff(sel, axis = 1)
rect[1] = sel[np.argmin(diff)] #Point B
rect[3] = sel[np.argmax(diff)] #Point D

largeurAB=np.sqrt(((rect[1][0]-rect[0][0])**2)+((rect[1][1] - rect[0][1])**2))
largeurCD=np.sqrt(((rect[2][0]-rect[3][0])**2)+((rect[2][1] - rect[3][1])**2))
largeur=max(int(largeurAB),int(largeurCD))

hauteurAD=np.sqrt(((rect[3][0]-rect[0][0])**2)+((rect[3][1] - rect[0][1])**2))
hauteurBC=np.sqrt(((rect[2][0]-rect[1][0])**2)+((rect[2][1] - rect[1][1])**2))
hauteur=max(int(hauteurAD),int(hauteurBC))

dst=np.array([[0,0],[largeur-1,0], [largeur-1,hauteur-1],[0,hauteur-1]],dtype='float32')

M=cv2.getPerspectiveTransform(rect,dst)
img_cor=cv2.warpPerspective(image,M,(largeur,hauteur))

Nous commençons par convertir les points du contour en array (l. 19) et nous définissons la variable rect qui nous permettra de stocker les valeurs des coordonnées du panneau d’origine. Puis nous rangeons les points du contour comme illustré dans la figure précédente (lignes 22-28).

Maintenant que nos points sont correctement identifiés, nous pouvons calculer les points de notre image de destination. Pour le point d’origine, le point A, il est logique qu’il se trouve aux coordonnées [0,0]. Pour les autres coordonnées, ils seront placés en fonction de la largeur et la hauteur de notre image d’origine.

Pour connaître la largeur de notre image, nous prenons la largeur la plus grande (l. 32) du panneau d’origine. Nous calculons donc la distance Cartésienne entre les points A et B (l. 30) et celle entre C et D (l. 31). Pour la hauteur, nous réalisons la même opération, mais cette fois pour le couple A-D (l. 34) et B-C (l. 35), puis nous récupérons la hauteur la plus grande (l. 36).

Finalement, nous stockons, dans l’array dst (l. 38),  les valeurs des coordonnées de destinations dans le même ordre que celles d’origine: dst= [A, B, C, D].

Il ne nous reste plus qu’à estimer la transformation de perspective (l. 40) et à effectuer la correction (l. 41).

Image après la correction de perspective
Image 4: Image corrigée

Enfin, nous pouvons récupérer l’image après correction de la perspective. Nous pouvons donc maintenant, extraire les chiffres de cette image facilement.


0 commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.