Nous avons vu la semaine dernière comment détecter une forme dans une image. L’image que nous avons traitée comportait différentes formes de différentes couleurs. Il est alors normal de parler aujourd’hui de la reconnaissance de couleurs.

Comme vous le savez certainement, une image couleur est composée de pixels. Chaque pixel a un triplet de valeur représentant, dans la majorité des cas, les valeurs de teinte rouge vert et bleu (RGB). On notera que dans le cas d’OpenCV, ce triplet est inversé (BGR).
Cependant l’un des plus gros défausse ce repère, est que les informations de luminance et de chrominance sont complètement mélangées. Cela va poser un problème lorsque l’on cherchera à résoudre un problème indépendant des variations de luminosité, c’est-à-dire de ne s’intéresser qu’aux informations de chrominance. Dans notre cas, la luminance importe peu. En effet, nous travaillons avec une luminosité constante, c’est une image.

Cependant, l’intérêt de passer du repère RGB au repère HSV est qu’il est plus simple de classifier les couleurs. En effet, en RGB, il est nécessaire d’établir les conditions sur le triplet de valeurs alors que dans le cas du HSV, nous pourrons utiliser uniquement les informations de teinte (H – Hue).

Étape 1 : Définition des conditions sur les couleurs.

Afin de pouvoir définir les conditions sur la teinte, nous allons nous servir du cercle chromatique. En effet, la teinte en HSV est codée en fonction de l’angle sur le cercle chromatique. Ainsi le rouge sera proche de l’angle 0° ou 360°, le vert aux alentours des 120° et le bleu vers 240°.

Reconnaissance de couleurs: cercle chromatique
Cercle chromatique

Nous allons donc créer un dictionnaire comprenant l’association couleur – angle min, angle max. Il est à noter que nous définissons deux zones rouges : rouge1 pour la zone 0°- 20° et rouge2 pour 330°-360°.

colors={"Rouge1":[0,20],
"Orange":[20,40],
"Jaune":[40,80],
"Vert":[80,160],
"Cyan":[160,200],
"Bleu":[200,260],
"Magenta":[260,330],
"Rouge2":[330,360]
}

Étape 2 : Création d’une fonction de reconnaissance de couleurs

Maintenant que notre dictionnaire est prêt, nous allons l’utiliser dans une fonction. Cette fonction prendra en entrée l’image d’origine, les contours détectés, ainsi que le dictionnaire.

def detect_couleur(image,cnt,colors):
    image_hsv=cv2.cvtColor(image,cv2.COLOR_BGR2HSV)
    mask = np.zeros(image_hsv.shape[:2], dtype="uint8")
    cv2.drawContours(mask, [cnt], -1, 255, -1)
    mask = cv2.erode(mask, None, iterations=2)

La première étape (ligne 2) consiste à convertir l’image d’origine RGB en image HSV. Nous créons ensuite, ligne 3, une image noire, de taille identique à l’image HSV qui nous servira de masque. Nous dessinons les contours (l.4) de nos formes, tout en spécifiant un chiffre négatif pour l’épaisseur (dernière valeur) permettant ainsi de remplir la forme de la même couleur que le contour (255 – Blanc). Enfin, nous appliquons une érosion ligne 5 sur notre masque afin d’être sûr de ne pas prendre en compte le fond de l’image dans notre calcul de couleurs.

Il ne nous reste plus qu’à retrouver les couleurs:

 mean = cv2.mean(image_hsv, mask=mask)
    if image.dtype=='uint8':
        for color in colors:
            if 2*mean[0] in range(colors[color][0],colors[color][1]):
                return(color)
    else:
        for color in colors:
            if mean[0] in range(colors[color][0],colors[color][1]):
                return(color)

Nous calculons ligne 6, la moyenne des valeurs des pixels de l’image HSV où les pixels de notre masque sont à 255, c’est-à-dire dans la forme étudiée. Enfin nous comparons aux lignes 9 ou 13, la valeur moyenne en H avec tous les seuils du dictionnaire.

Pourquoi deux cas différents en fonction du nombre de bits sur lequel sont codés l’image ? Simplement car dans le cas  où un pixel est codé sur 8 bits, OpenCV attribue une valeur entre 0 et 180 pour H et non entre 0 et 360. En effet, il est difficilement possible de coder 360 sur 8 bits. C’est pourquoi nous multiplions la moyenne par 2 dans le premier cas afin d’avoir des valeurs comprises entre 0 et 360.

Si la moyenne est comprise entre les bornes d’une couleur, nous renvoyons le nom de cette couleur lignes 10 ou 14.

Étape 3: Amélioration de la reconnaissance de formes !

Reprenons le programme de la semaine dernière et modifions le avec ce que nous avons vu aujourd’hui. Commencez par ajouter le dictionnaire et la fonction après les lignes d’import de bibliothèques. Ensuite, on appelle la fonction « detect_couleur » pour chaque contour détecté.

for cnt in contours:
perimetre=cv2.arcLength(cnt,True)    approx = cv2.approxPolyDP(cnt,0.01*perimetre,True)    color = detect_couleur(image,cnt,colors)    M = cv2.moments(cnt)

La dernière étape consistera à la modification du texte que l’on inscrit sur l’image:

 cv2.putText(image, shape + " " + color, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,0.5, (125, 125, 125), 2)

Nous pouvons alors admirer le résultat

Nous avons réussi, nous avons une méthode de reconnaissance de formes et de couleurs ! Cette solution est simpliste et fonctionnelle uniquement dans le cas d’images virtuelles. Cependant il ne sera pas difficile d’utiliser ces méthodes dans un environnement réel.

Le code complet

import numpy as np
import cv2
 
colors={"Rouge1":[0,20],
        "Orange":[20,40],
        "Jaune":[40,80],
        "Vert":[80,160],
        "Cyan":[160,200],
        "Bleu":[200,260],
        "Magenta":[260,330],
        "Rouge2":[330,360]
        }
 
def detect_couleur(image,cnt,colors):
    image_hsv=cv2.cvtColor(image,cv2.COLOR_BGR2HSV)
    mask = np.zeros(image_hsv.shape[:2], dtype="uint8")
    cv2.drawContours(mask, [cnt], -1, 255, -1)
    mask = cv2.erode(mask, None, iterations=2)
    mean = cv2.mean(image_hsv, mask=mask)
    if image.dtype=='uint8':
        for color in colors:
            if 2*mean[0] in range(colors[color][0],colors[color][1]):
                return(color)
    else:
        for color in colors:
            if mean[0] in range(colors[color][0],colors[color][1]):
                return(color)
 
 
image = cv2.imread('image.bmp')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,250,255,cv2.THRESH_BINARY_INV)
img,contours,h = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
 
for cnt in contours:
    perimetre=cv2.arcLength(cnt,True)
    approx = cv2.approxPolyDP(cnt,0.01*perimetre,True)
    color = detect_couleur(image,cnt,colors)
    M = cv2.moments(cnt)
    cX = int(M["m10"] / M["m00"])
    cY = int(M["m01"] / M["m00"])
    cv2.drawContours(image,[cnt],-1,(0,255,0),2)
    if len(approx)==3:
        shape = "triangle"
    elif len(approx)==4:
        (x, y, w, h) = cv2.boundingRect(approx)
        ratio = w / float(h)
        if ratio >= 0.95 and ratio <= 1.05:
            shape = "carre"
        else:
            shape = "rectangle"
 
    elif len(approx)==5:
        shape = "pentagone"
    elif len(approx)==6:
        shape = "hexagone"
    else:
        shape= "cercle"
    cv2.putText(image, shape + " " + color, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,0.5, (125, 125, 125), 2)
cv2.imshow('image',image)
cv2.waitKey(5000)
cv2.imwrite('result.jpg',image)
cv2.destroyAllWindows()
Catégories : BaseOpenCVTutoriel

1 commentaire

Reconnaissance de formes – Pymotion · 11 décembre 2024 à 14h24

[…] Maintenant que nous arrivons à détecter les contours, il peut être intéressant d’en étudier la couleur. […]

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail 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.