Utilisation de la binarisation pour détecter les contours d'une image en niveaux de gris. Toutes les images dont vous aurez besoin sont dans le dossier : commun/travail/infoPCSI
Les instructions de cette première partie sont à tester dans un shell Python. Il est fortement conseillé de prendre un peu de temps pour réaliser les tests et faire éventuellement les réglages nécessaires sur votre distribution Python (Anaconda) pour que l'image s'affiche avec l'instruction show() et comprendre la notion de chemin relatif et absolu.
from PIL import Image
chemin_absolu = "/chemin_depuis_la_racine/"
src = Image.open(chemin_absolu + "chaplin.png")
dest = Image.new("L",src.size)
src.show()
Dans cet exemple nous avons créé l'objet image dest, une image en niveau de gris (mode='L'), de dimension src.size (tuple indiquant la largeur et la hauteur de l'image). L'objet dest est une image vide
src_data = list(src.getdata())
print src_data[20:30]#slicing on affiche les valeurs dans l'intervalle [20; 30[
La liste contient toutes les intensités de niveaux de gris des pixels de l'image. Pratique il n'y a plus qu'à les modifier pour effectuer un traitement sur l'image.
width, height = src.size
print width, height
Dans cet exemple nous allons modifier le contraste de l'image
dest_data = [] #création d'une liste vide destinée à recevoir les intensités de niveaux de gris de l'image finale
for i in src_data:#on parcourt la liste des niveaux de gris de l'image source
dest_data.append(i * 2 ) #on stocke les modifications
dest.putdata(dest_data)#De la liste vers l'image
dest.show()#On affiche
dest.save(chemin_absolu + "chaplin_nb.png")
Le but de la détection de contours est de repérer les points d'une image numérique qui correspondent à un changement brutal de l'intensité lumineuse. Ces changements de propriétés de l'image traduisent en général des événements importants ou des changements dans les propriétés du monde. Ils incluent des discontinuités dans la profondeur, dans l'orientation d'une surface, dans les propriétés d'un matériau et dans l'éclairage d'une scène. La détection de contours est un champ de la recherche qui appartient au traitement d'image et à la vision par ordinateur, particulièrement dans le domaine de l'extraction de caractéristiques. La détection des contours d'une image réduit de manière significative la quantité de données et élimine les informations qu'on peut juger moins pertinentes, tout en préservant les propriétés structurelles importantes de l'image.
Il existe un grand nombre de méthodes de détection de contours, nous allons nous intéresser à la recherche de contours après binarisation d'une image en niveau de gris. Rien n'empêche d'étendre cette méthode à des images couleurs.
Commençons avec une image binaire comme ci-dessous : contoursV.png. Elle présente une seule discontinuité de l'intensité. Les bords noirs ne sont pas présents sur le fichier image, ils ont été ajoutés uniquement pour délimiter les bords de l'image sur le fond blanc de la page. Les contours cherchés se réduisent dans ce cas à une ligne verticale, frontière entre le noir et le blanc. Notre objectif est d'obtenir une nouvelle image dans laquelle seule cette frontière sera conservée.
Les coordonnées (i,j) dans l'image 2-dimensions permettent d'obtenir l'index d'un pixel dans une liste à 1 dimension à l'aide de la relation suivante :\[index = i + j* WIDTH\]
Dans l'exemple suivant le pixel de coordonnées (0, 1) a pour valeur d'intensité 0 (noir), dans notre liste l'index de ce pixel est \[index = 0 + 1 * 4 = 4\]
Écrire une fonction contoursV(img_data, size) qui prend en paramètre une image sous la forme d'une liste de pixels, un tuple avec la largeur et la hauteur de l'image et renvoyant une nouvelle liste de même dimension mais dont chaque pixel a pour valeur la différence entre la valeur du pixel (i,j) et celle du pixel (i-1, j). Pour commencer vous pourrez tester votre fonction avec la liste de l'exemple ci-dessus L = [0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255] qui doit fournir le résultat suivant:
\[\begin{array}{|c|c|c|c|}\hline 0 & 255 & 0 & 0 \\\hline 0 & 255 & 0 & 0 \\\hline 0 & 255 & 0 & 0 \\\hline 0 & 255 & 0 & 0 \\\hline \end{array}\qquad \longrightarrow \qquad \begin{array}{|c|c|c|c|c|c|c|c|}\hline 0 & 255 & 0 & 0 & 0 & 255 & \dots & 0\\\hline \end{array} \]
Écrire une fonction newImage(img_data, size) qui prend en argument une liste d'intensité de pixels, un tuple (largeur, hauteur) de l'image et qui renvoie une image prête à visualiser.
Pour finir nous allons écrire la première version de la fonction contoursImage(img_file) suivante qui renvoie une image ne conservant que les contours verticaux de l'image source:
def contoursImage(img_file):
data = imgData(img_file) #liste des intensités de pixels
size = imgInfo(img_file)[0] # informations sur l'image
dest_data = contoursV(data, size) # Détection contours verticaux
dest = newImage(dest_data, size) # création de la nouvelle image
return dest
Appliquer la fonction contoursImage avec l'image : contoursV.png
Appliquer la fonction contourV(img_data) avec l'image suivante (contoursH.png). Que se passe-t-il ? Expliquez pourquoi
Écrire la fonction contourH(img_data). Modifier la fonction contoursImage pour que l'utilisateur puisse choisir entre les contours verticaux ou horizontaux de l'image source. Tester cette nouvelle fonction avec l'image : contoursH.png
Tester également la fonction contoursImage sur l'image : formes.png
À l'aide des fonctions contoursV et contoursH écrire une fonction contoursNaifImageBin(img_file) renvoyant une image des contours de toutes les formes. Évaluer la complexité de la fonction contoursNaifImageBin
Les changements d'intensité entre deux pixels voisins en niveaux de gris ne sont pas forcément significatifs d'un contour, il est donc nécessaire d'effectuer un pré-traitement avant la détection des contours
La binarisation ou comment transformer une image en niveaux de gris en image binaire. Pour cela nous allons utiliser une technique très simple appelée seuillage. Le seuillage d'image est une méthode permettant de rassembler entre eux les pixels d'une région à partir d'un critère précis. Dans notre exemple à partir d'une image en niveaux de gris, le seuillage d'image peut être utilisé pour créer une image comportant uniquement deux couleurs, noir et blanc, d'où la notion d'image binaire. Ces deux couleurs sont définies par les niveaux d'intensité de gris 0 pour le noir et 255 pour le blanc. Le seuillage d'image permet donc de remplacer un à un les pixels d'une image en comparant avec une valeur seuil, les pixels de la nouvelle image prennent la valeur 0 en dessous du seuil et la valeur 255 sinon.
#exemple de code pour seuillage
dest_data=[] #liste contenant les valeurs d'intensité finales
seuil = 126
for i in range(len(src_data)): #src_data : liste des valeurs d'intensité initiales
if src_data[i] < seuil:
dest_data.append(0)
else:
dest_data.append(255)
dest.putdata(dest_data) #construction de l'image finale dest
dest.show() #Affichage de l'image à l'écran