5.7. Composition d’images

Les primitives de dessin de la page précédente peignent des marques géométriques sur une image – une ligne, un rectangle, un fragment de texte. Cela couvre la plupart des annotations qu’un algorithme a besoin de rendre visibles, mais pas toutes. Parfois, l’annotation est elle-même une image : une trame de référence capturée à afficher côte à côte avec la trame courante, une miniature d’une capture précédente affichée dans un coin de l’aperçu, un gabarit stocké précédemment visualisé par-dessus une trame en direct pour le calibrage. Le mécanisme permettant de dessiner une image sur une autre est une méthode unique – draw_image() – dotée de suffisamment de paramètres pour gérer la position, la mise à l’échelle, la palette de couleurs et la transparence qu’exige une composition réelle.

5.7.1. L’appel de base

Dans sa forme la plus simple, draw_image prend une autre Image et une position où la dessiner :

reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)

La destination est img ; la source est reference ; le pixel en haut à gauche de la source se place en (10, 10) de img, et le reste des pixels de la source suit vers la droite et vers le bas à partir de là. Les pixels de la destination que la source recouvre sont écrasés par les pixels correspondants de la source ; les pixels en dehors de l’emprise de la source restent intacts.

Si la source dépasse le bord de la destination, les parties qui débordent sont silencieusement rognées – le même comportement indulgent que set_pixel affiche pour les positions hors plage. Le code applicatif n’a pas à contraindre la position aux dimensions de l’image au préalable ; il peut transmettre la position souhaitée et laisser la méthode gérer le rognage.

5.7.2. Charger un fichier directement

draw_image accepte un chemin de fichier à la place de l’argument Image et charge le fichier avant de le composer :

img.draw_image("/sdcard/reference.bmp", x=10, y=10)

Cela ressemble à une commodité – une ligne au lieu de deux – et c’en est une, mais la différence va au-delà de la syntaxe. Construire une Image à partir d’un fichier alloue un tampon pour contenir les pixels décodés, et ce tampon subsiste jusqu’à ce que le ramasse-miettes le libère. Transmettre le chemin directement à draw_image permet au module de décoder le fichier dans un tampon temporaire, de composer à partir de celui-ci et de libérer le tampon au retour de l’appel, sans que le code applicatif ait à conserver une référence vers une Image distincte entre les trames.

5.7.3. Mise à l’échelle

Lorsque la source et la destination ont des tailles différentes – une capture en basse résolution composée sur un canevas en plus haute résolution, ou une miniature qui doit être dimensionnée à une fraction particulière de la trame – deux paramètres d’échelle se chargent de redimensionner la source au moment où elle est dessinée :

img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)

x_scale et y_scale sont des flottants indépendants ; transmettre la même valeur pour les deux applique une mise à l’échelle uniforme, tandis que transmettre des valeurs différentes étire ou rétrécit la source selon un axe. La mise à l’échelle se produit au moment du dessin ; la source reference n’est pas modifiée.

Un masque de bits d’indicateurs d”indication détermine comment la mise à l’échelle interpole réellement entre les pixels. image.BILINEAR produit des résultats plus lisses au prix de davantage de calculs ; image.BICUBIC produit des résultats encore plus lisses et coûte encore davantage ; la valeur par défaut utilise le plus proche voisin, qui est la moins coûteuse et le bon choix lorsque la source est déjà à la résolution en pixels de la destination. Les indicateurs de gestion du rapport d’aspect – SCALE_ASPECT_KEEP, SCALE_ASPECT_EXPAND, SCALE_ASPECT_IGNORE – décident de ce qu’il faut faire lorsque le rapport d’aspect de la source ne correspond pas au rectangle dans lequel elle est dessinée.

5.7.4. Fusion alpha

Par défaut, draw_image remplace les pixels de destination par les pixels de la source. Lorsque l’objectif est une superposition translucide – de sorte que la destination transparaisse à travers la source – le paramètre alpha contrôle la manière dont les deux sont mélangés. alpha=0 n’affiche que la destination (pas de source) ; alpha=255 est la valeur par défaut et n’affiche que la source (remplacement complet) ; les valeurs intermédiaires mélangent les deux proportionnellement :

img.draw_image(overlay, x=0, y=0, alpha=128)

Un argument distinct alpha_palette est le seul mécanisme d’alpha par pixel du module. Il prend une image GRAYSCALE dont les valeurs sont utilisées comme alpha à la position correspondante dans la source – une carte de chaleur dont l’alpha varie avec son intensité, par exemple. L’alpha doit être fourni sous la forme de cet argument distinct en niveaux de gris ; une image source qui porte son propre canal alpha (un PNG avec transparence, par exemple) ne le transmet pas automatiquement.

5.7.5. ROI source et palette

Deux paramètres supplémentaires complètent le mécanisme de composition :

  • roi=(x, y, w, h) restreint la source à un sous-rectangle d’elle-même, de sorte que seul ce rectangle soit composé sur la destination. Utile pour recadrer au sein du même appel, sans préparer d’intermédiaire recadré.

  • color_palette substitue la valeur de chaque pixel source au travers d’une table de correspondance avant le dessin – le même mécanisme qu’utilisent to_rainbow() et to_ironbow(), exposé ici pour qu’une superposition puisse être palettisée sur son chemin vers la destination sans passe de conversion distincte.

Les deux se composent avec tout le reste de l’appel : la mise à l’échelle, l’alpha, l’argument mask côté destination, et le paramètre roi côté destination qui restreint l”écriture à un rectangle de la destination.