Loading [MathJax]/jax/output/HTML-CSS/fonts/TeX/fontdata.js
rotation quart de tour d'une image

Coder une image numérique

Qu’est ce qu’une image numériques?

Rappels de SNT: Une image numérique est constituée de pixels colorés (ou noirs et blanc). Le fichier comportant l’image numérique contient alors une succession de valeurs qui codent la couleur de chaque pixels: lire le cours de SNT, paragraphe codage de l'image

Librairie PIL

Pour manipuler les images, nous pouvons utiliser le module PIL de Python. Celui-ci permet de traiter les pixels d’une image, un par un. Souvent, il faudra parcourir l’image pixel par pixel, sur toute la largeur et toute la hauteur. Cela est possible avec deux boucles imbriquées, à condition de connaitre ses dimensions largeur, hauteur:

from PIL import Image
imageSource=Image.open("crabePortrait.bmp")
largeur,hauteur=imageSource.size

for x in range(largeur): # x varie de 0 à largeur - 1
    for y in range(hauteur): # x varie de 0 à hauteur - 1
      # traitement pixel (x,y)

imageSource est une instance de la classe Image et possède une methode getpixel((x,y)) qui renvoie la valeur du pixel pour l’argument (x,y). S’utilise de la manière suivante:

imageSource.getpixel((x,y))

Nous allons travailler sur l’image suivante: crabePortrait.bmp

image de dimension 247 * 330 en format bitmap, RGB 3 octets

image de dimension 247 * 330 en format bitmap, RGB 3 octets

Dans un premier temps, explorons cette structure de données:

A vous de jouer: une fois l’image téléchargée, ouvrir un notebook dans le même dossier que l’image, puis, à partir du script précédent:

  1. Le script à compléter donné plus haut va créer une double boucle sur x (position en largeur qui varie de 0 à largeur) et sur y (position en hauteur qui varie de 0 à hauteur). Le pixel (0,0) est en haut sur le coin gauche. Copier-coller ce script
  2. dans la boucle: stocker dans p la valeur RGB du pixel de coordonnée (x,y) de imageSource: p=imageSource.getpixel((x,y))
  3. Vérifier le contenu des données pour quelques une des points de l’image: afficher la valeur de p si x < largeur//10 et y < hauteur //10

Laquelle des structures de données représente le mieux le format des données:

  • [124,90,120]
  • (124,90,120)
  • #7C5A78?

On souhaite maintenant diminuer le contraste, en divisant par 2 chacune de composantes RGB de chacun des pixels de l’image. L’image sera egalement plus sombre. On va créer cette fois une nouvelle instance, planPixels, de la classe Image, de la même dimension que celle d’origine, pour y copier les pixels un-à-un:

planPixels=Image.new("RGB",(largeur,hauteur))

On va placer les pixels de imageSource (lecture) dans la nouvelle image planPixels (écriture).

Debut du 2e programme:

from PIL import Image
imageSource=Image.open("crabePortrait.bmp")
largeur,hauteur=imageSource.size
planPixels=Image.new("RGB",(largeur,hauteur))

for x ...
  for y ...
  # traitement pixel (x,y)
  # a completer ...

planPixels.save("crabe2.jpg")
planPixels.show()

On remplira cette image avec les pixels de l’image d’origine de la manière suivante:

A vous de jouer: Compléter le script pour…

  1. parcourir tous les pixels de l’image, largeur * hauteur.
  2. lire la valeur RGB de chaque pixel (getpixel) et modifier cette valeur, en diminuant le contraste de couleur: p=(p[0]//2, p[1]//2, p[2]//2)
  3. placer ce pixel sur la nouvelle image en construction planPixels. Utiliser pour cela la methode putpixel((x,y),p) de l’objet planPixels où x et y sont les nouvelles coordonnées du pixel p que l’on veut dessiner: planPixels.putpixel((x,y),p)

Rotation d’une image: permutation de pixels

On va cherche à écrire un script Python qui réalise la rotation d’un quart de tour dans le sens horaire, comme sur l’image ci-dessous:

principe de la rotation horaire d'un quart de tour

principe de la rotation horaire d'un quart de tour

Questions:

  1. Quelles sont les coordonnées des points A et B avant transformation?
  2. Quelles sont les nouvelles coordonnées des points A' et B' après transformation?
  3. pour un pixel situé initialement en (x,y), quelles sont ses coodonnées (x',y') après transformation? (la reponse est donnée ci-dessous. Cherchez avant de la lire…)
  4. Ecrire le script du programme qui devra réaliser la transformation de crabePortrait.bmp, d’un quart de tour, dans le sens horaire.
  5. Donner la complexité de cet algorithme.

Réponse à la question 3: x=y+hauteur1, y=x.

Rotation par la méthode Diviser pour Régner

On cherche maintenant à effectuer cette transformation, SANS utiliser de nouvelle image planPixels comme précédemment.

On utilisera l’image suivante (carrée) pour cette méthode: woody.jpg

image woody.jpg

image woody.jpg

Compléter la fonction echange_pix suivante

def echange_pix(image,x0,y0,x1,y1):
    """procedure qui echange les pixels d'une image entre une position 
    de depart start et d'arrivée end
    Params:
    ------
    image : objet de la classe Image
    x0,y0: int, int: coordonnées du pixel de depart
    x1,y1: int, int: coordonnées du pixel d'arrivée
    
    Exemple: echange du pixel (0,0) avec celui (120,120)
    --------
    >>> echange_pix(imageSource,0,0,120,120)
    """
    start = image.getpixel((x0,y0))
    end = image.getpixel((x1,y1))
    image.putpixel((x0,y0),end)
    image.putpixel((x1,y1),...)


Compléter la fonction echange_quadrant suivante

Cette fonction permet d’echanger les pixels de 2 zones carrées de même dimensions.

exemple: echange des blocs A et B

exemple: echange des blocs A et B

def echange_quadrant(image,x0,y0,x1,y1,n):
    """procedure qui echange tous les pixels du bloc de pixels A
    avec ceux du bloc B, de même dimension n*n.
    L'image doit être carrée, de largeur et hauteur égaux à n
    A et B occupent une position quelconque parmi les 4 quarts de l'image
    Params:
    -------
    image : objet de la classe Image
    x0,y0: int, int: coordonnées du pixel du coin superieur gauche de A
    x1,y1: int, int: coordonnées du pixel du coin superieur gauche de B
    n : int : largeur ou hauteur de l'image, en nombre de pixels
    Example: echange du quart d'image en haut à gauche (A) avec celui 
    ------------ en haut à droite (B) sur une image de largeur 120
     
   
    >>> echange_quadrant(imageSource,0,0,120,0,120)
    """
    for x in range(n):
        for y in range(n):
            echange_pix(image, x0+x,...# à compléter

Questions:

  • On veut echanger les blocs A et D, qui font chacun 120*120 pixels. Quelle instruction faut-il écrire, utilisant la procedure echange_quadrant.
  • Même question pour echanger les blocs A et C.

analyser la fonction rotate

La fonction rotate permet de faire tourner l’image d’un quart de tour par une méthode de type diviser pour régner:

Tourner l’image d’un quart de tour, c’est permuter les 4 parties d’une image dont on a permuté l’interieur de ses 4 parties…

Une fois la partie divisée executée (appels recursifs), lorsque les subdivisions de l’image sont constituées d’un seul pixel, les pixels sont déplacés (règne) à l’aide de 3 permutations successives, selon le schéma suivant:

trois permutations réalisées sur les subdivisions de l'image

trois permutations réalisées sur les subdivisions de l'image

Il sont alors recombinés pour reformer l’image, tout en suivant les mêmes permutations, mais avec des blocs de pixels plus gros (fusion).

def rotate(image,x0,y0,n):
    """procedure recursive qui tourne d'un quart de tour un carré
    de l'image de dimension n.
    à chaque appel recursif, la taille de l'image est divisée par 2.
    Si l'image fait plus d'un seul pixel, la rotation se fait par 
    permutation des (zones de) pixels A<=>B, B<=>D, D<=>C
    Params:
    -------
    image
    x0,y0: int, int: coordonnées du pixel du coin superieur gauche du carré
    n: dimansion du carré
    Example:
    --------
    rotate(imageSource,0,0,420)
    """
    if n>=2:
        m = n//2
        rotate(image,x0,y0,m)
        rotate(image,x0,y0+m,m)
        rotate(image,x0+m,y0,m)
        rotate(image,x0+m,y0+m,m)
        echange_quadrant(image,x0,y0,x0+m,y0,m)
        echange_quadrant(image,x0,y0,x0+m,y0+m,m)
        echange_quadrant(image,x0,y0,x0,y0+m,m)

Analysez la procedure: On considère l’image de woody, quadrillée de la manière suivante. Cette image fait 512×512 pixels

image d'origine

image d'origine

  1. Lors du premier appel de la fonction: que vaut m?

A la fin de la remontée, lorsque m vaut 256, l’image, si on l’affiche est celle-ci:

image finale

image finale

  1. Compléter la grille avec les lettres A, B, C, D pour montrer la permutation des blocs sur le dessin.

On peut suivre la construction de l’image à différents niveaux de la pile d’appels recursifs, lors de la remontée. Pour faire ceci, ajouter les instructions suivantes en fin de la fonction rotate:

        if m > 100:
            image.show()

Puis ajouter et executer les lignes suivantes:

from PIL import Image

def quart_tour(image):
    largeur,hauteur=image.size
    assert largeur == hauteur
    rotate(image,0,0,largeur)
    
    
img = Image.open("woody.jpg")
quart_tour(img)
img.save("woody_endroit.jpg")
img.show()

Pour suivre la construction de l’image, nous allons placer les repères suivants sur l’image d’origine:

image quadrillée

image quadrillée

  1. Pour chacune des images suivantes:
  • quel est la valeur de m lorsque l’image est affichée?
  • compléter les quadrants montrant la permutation dans chaque sous-partie
  • dans quel ordre ces images sont elles affichées?
image a

image a

image b

image b

image c

image c

image d

image d

efficacité de la méthode

Evaluons l’efficacité de cet algorithme

  • Est-il plus rapide ou plus lent que votre premier algorithme?
  • Le nombre d’opérations significatives suit une loi de recurence: T(n)=4×T(n2)+C×n2 Cela confirme t-il votre reponse à la question pécédente?

On rappelle que l’intérêt de la méthode est surtout de ne PAS utiliser de nouvelle image planPixels comme précédemment. Il s’agit d’une transformation en place.

Liens et aides

  • En cas de difficulté, consulter la page et les videos du site: monlyceenumerique.fr, aller à la partie sur la rotation des images.