Académique Documents
Professionnel Documents
Culture Documents
2 : PHOTOMOSAÏQUE
CORRIGÉ
1. Nommer un type de conteneur indexé mais pas ordonné en Python. Donner une instruction Python
permettant de créer un tel conteneur, avec deux éléments quelconques de votre choix.
Un dictionnaire est un conteneur indexé mais pas ordonné en Python. On le crée ainsi :
dictionnaire = {'prenom': 'Benjamin', 'age': 18}
Chaque composante peut prendre 256 valeurs différentes (en n’oubliant pas de compter la valeur 0). Un pixel
peut donc prendre 2563 = 16 777 216 de couleurs différentes.
3. Donner une instruction permettant de créer un vecteur correspondant à un pixel rouge, puis un pixel
blanc.
source.shape renvoie la taille du tableau numpy associé à l’image surfer.jpg suivant ses différentes
dimensions. Il s’agit donc d’un de 3 000 lignes, 4 000 colonnes et comportant 3 informations pour chaque
élément, c’est-à-dire une image de 3 000 pixels de haut et 4 000 pixels de large, chaque pixel contenant les
informations RGB.
source[0,0] renvoie les valeurs de l’élément situé aux coordonnées (0,0), c’est-à-dire en haut à gauche du
tableau. Le pixel à ces coordonnées possède une intensité de rouge de 144, de vert de 191 et de bleu de 221.
return image
TSI 1 p.1/7
7. Quelle taille de vignette (𝒘 × 𝒉, en pixels) faut-il choisir ? Quelle sera alors la taille en pixels de la
photomosaïque ?
𝑙𝑎𝑟𝑔𝑒𝑢𝑟_𝑡𝑜𝑡𝑎𝑙𝑒 = 2 000 𝑚𝑚
𝑙𝑎𝑟𝑔𝑒𝑢𝑟_𝑡𝑜𝑡𝑎𝑙𝑒 ⋅ 𝑟é𝑠𝑜𝑙𝑢𝑡𝑖𝑜𝑛
𝑤= = 500 𝑝𝑥 𝑎𝑣𝑒𝑐 { 𝑟é𝑠𝑜𝑙𝑢𝑡𝑖𝑜𝑛 = 10 𝑝𝑥 ⁄𝑚𝑚
𝑛𝑜𝑚𝑏𝑟𝑒_𝑣𝑖𝑔𝑛𝑒𝑡𝑡𝑒𝑠
𝑛𝑜𝑚𝑏𝑟𝑒_𝑣𝑖𝑔𝑛𝑒𝑡𝑡𝑒𝑠 = 40
𝑤 4 3
Pour avoir un ratio 4:3, on doit donc avoir = et donc ℎ = 𝑤 = 375 𝑝𝑥 . La photomosaïque dans son
ℎ 3 4
ensemble aura alors une taille 𝑊 = 40 ⋅ 𝑤 = 20 000 𝑝𝑥 et 𝐻 = 40 ⋅ ℎ = 15 000 𝑝𝑥.
return image
9. Sur le code de la question précédente, entourer les opérations élémentaires effectuées par
l’algorithme. En déduire le nombre total d’opérations élémentaires en fonction de 𝒘 et de 𝒉, puis la
complexité temporelle en fonction de 𝒏.
On suppose que np.empty() est une opération élémentaire, et on néglige l’accès à un élément d’un tableau :
return image
L’instruction à l’intérieur des doubles s’exécutant ℎ ⋅ 𝑤 fois, on a un total de 4 + 7 ⋅ ℎ ⋅ 𝑤 = 4 + 7 ⋅ 𝑛 opérations
élémentaires, et donc un algorithme de complexité 𝑂(𝑛).
10. Expliquer en quelques lignes son principe de fonctionnement, et en particulier en quoi cette fonction
diffère de la fonction proche_voisin.
Comme la fonction proche_voisin, cette fonction prend comme argument une image initiale et les
dimensions de l’image réduite que l’on souhaite obtenir. Contrairement à proche_voisin qui se contente de
prendre en compte la valeur d’un unique pixel de l’image initiale pour obtenir un pixel de l’image finale, celle-ci
fait la moyenne de tout un bloc de pixels.
TSI 1 p.2/7
Figure 1 : principe de la fonction proche_voisin (à gauche) et moyenne_locale (à droite).
Figure 2 : comparaison entre l'image d'origine (à gauche), l'image réduite par proche_voisin (au centre) et l’image réduite par
moyenne_locale (à droite).
11. Sur le code de la question précédente, entourer les opérations élémentaires effectuées par
l’algorithme. En déduire le nombre total d’opérations élémentaires en fonction de 𝒘 et de 𝒉, puis la
complexité temporelle en fonction de 𝒏.
On suppose que np.empty() est une opération élémentaire, et on néglige l’accès à un élément d’un tableau.
En revanche, la fonction np.mean(A) nécessite en revanche approximativement A.size opérations. Dans ce
cas, cela correspond à 𝑝ℎ ⋅ 𝑝𝑤 opérations :
𝐻 𝑊
L’instruction à l’intérieur des doubles s’exécutant ⋅ = ℎ ⋅ 𝑤 fois, on a un total de :
𝑝ℎ 𝑝𝑤
TSI 1 p.3/7
A. OPTIMISATION DE LA RÉDUCTION PAR MOYENNE LOCALE
0 0 0 0
0 7 3 7+3
⟶ 0 0 7
= 10
9 2 5
7+9+2 7+3+9+2+5
0 9
= 18 = 26
13. Quel est le plus grand nombre possible qui pourrait apparaître dans la table de sommation d’une
image de 𝟓𝟎 millions de pixels ?
Le plus grand nombre de la table de sommation apparait en bas à droite : il correspond à la somme de tous les
éléments de l’image. Chaque élément pouvant prendre une valeur jusqu’à 255, le plus grand nombre de la table
de sommation est alors 255 ⋅ 50 ⋅ 106 = 12.75 ⋅ 109 .
Attention, on ne souhaite ni utiliser np.sum(), ni utiliser deux boucles supplémentaires pour calculer chaque
valeur de 𝑆, car l’algorithme serait alors de complexité 𝑂(𝑁 2 ).
return S
Cette moyenne est réalisée à l’aide de la table de sommation qui est manipulée de sorte à obtenir X : la somme
des valeurs comprises dans la zone (de 𝑝ℎ × 𝑝𝑤 pixels de l’image initiale) à réduire en un pixel de l’image finale.
Cette somme est finalement moyennée (en la divisant par nbp : le nombre de pixel compris dans cette zone)
puis affectée à la nouvelle image réduite.
On suppose que np.empty() est une opération élémentaire, et on néglige l’accès à un élément d’un tableau :
TSI 1 p.4/7
nbp = ph * pw
for I in range(0, H, ph):
for J in range(0, W, pw):
X = (S[I+ph, J+pw] - S[I+ph, J]) - (S[I, J+pw] - S[I, J])
a[I // ph, J // pw] = round(X / nbp)
return a
𝐻 𝑊
L’instruction à l’intérieur des doubles s’exécutant ⋅ = ℎ𝑤 fois, on a un total de 9 + 13 ⋅ ℎ𝑤 = 9 + 13 ⋅ 𝑛
𝑝ℎ 𝑝𝑤
opérations élémentaires, et donc un algorithme de complexité 𝑂(𝑛).
17. Discuter des avantages et des cas d’usage respectifs de proche_voisin, moyenne_locale et
réduction_sommation pour redimensionner une image.
La fonction proche_voisin perd de l’information (on ne prend que quelques pixels parmi l’image initiale A).
La fonction moyenne_locale ne fonctionne que si les dimensions de l’image a divisent celles de l’image A.
Finalement, la fonction réduction_sommation permet de réduire la taille de l’image initiale en prenant en
compte l’intégralité de ses éléments, tout fonctionnant même si les dimensions de l’image a ne divisent pas
celles de l’image A.
# Possibilité 2
h = a.shape[0]
w = a.shape[1]
resultat = 0
return resultat
TSI 1 p.5/7
20. Sans utiliser les fonctions min et max, écrire une fonction d’en-tête
def index_minimum(liste:list[int]) -> int:
qui prend en paramètre une liste de nombres et qui renvoie l’index du plus petit nombre de cette liste
(ou de l’un d’entre eux si plusieurs conviennent).
return index_min
return index_minimum(liste_valeurs)
for i in range(0,h*p,h):
for j in range(0,w*p,w):
mosaique_finale[i:i+h, j:j+w] =
vignettes[choix_vignette(mosaique_initiale[i:i+h,j:j+w], vignettes)]
return mosaique_finale
Finalement, on a :
TSI 1 p.6/7
24. Proposer une stratégie de construction de photomosaïque permettant de sélectionner un maximum
de vignettes différentes et, au cas où une vignette serait réutilisée, d’éviter que les différentes
apparitions de la même vignette se retrouvent trop proches.
Pour sélectionner un maximum de vignettes différentes, on peut définir des groupes de vignettes qui minimisent
la fonction L qui définit la distance entre deux images. Lorsque l’on doit affecter une vignette sur la
photomosaïque, on prend alors la vignette minimisant L sauf si un des proches voisins possède la même
vignette ; dans ce cas, on pioche une autre vignette dans le groupe qui minimisant L.
On pourrait également supprimer de la liste la vignette après l’avoir affectée dans la matrice de la mosaïque.
On pourrait enfin modifier une autre vignette que celle minimisant la fonction L (en augmentant la transparence,
en accentuant la luminosité, …) dans le cas où un des proches voisins possède la même vignette.
25. Implanter cette stratégie sous la forme d’une fonction belle_mosaïque, version améliorée de la
fonction construire_mosaïque, dont on définira les éventuels paramètres supplémentaires.
L’implémentation la plus simple est de supprimer de la liste la vignette utilisée dans la matrice finale.
for i in range(0,h*p,h):
for j in range(0,w*p,w):
i_vignette = choix_vignette(mosaique_initiale[i:i+h,j:j+w],
vignettes)
mosaique_finale[i:i+h,j:j+w] = vignettes[i_vignette]
vignettes = vignettes.pop(i_vignette)
return mosaique_finale
TSI 1 p.7/7