« Pygame/Introduction au module Surfarray » : différence entre les versions
Contenu supprimé Contenu ajouté
m a renommé Pygame - Introduction au module surfarray en Pygame/Introduction au module Surfarray: Restructuration du wiki Pygame |
Relecture |
||
Ligne 1 :
{{Pygame}}
Traduit de l'anglais, original par ''Pete Shinners'' :<br/>
http://www.pygame.org/docs/tut/surfarray/SurfarrayIntro.html
{{Remarqueampoule|1=''Note du traducteur'' :<br/>
Le projet [http://sourceforge.net/project/showfiles.php?group_id=1369&package_id=1351 Numeric], auquel il est fait référence dans ce tutoriel, a engendré le projet [http://sourceforge.net/project/showfiles.php?group_id=1369&package_id=32367 Numarray], qui lui-même a engendré le projet [http://numpy.scipy.org/ NumPy], lequel est maintenant intégré au projet [http://www.scipy.org/ SciPy] (ouf!). D'après quelques lectures ([http://wiki.python.org/moin/NumericAndScientific 1], [http://numpy.scipy.org//#older_array 2], [http://sourceforge.net/project/showfiles.php?group_id=1369 3]), NumPy '''''devrait''''' être entièrement compatible avec les 2 projets précédents. A vérifier également la compatibilité de Pygame avec NumPy.}}
== Introduction ==
Ce tutoriel a pour objectif d'introduire les utilisateurs à Numeric et au module Surfarray de Pygame. Pour les débutants, le code utilisé par Surfarray peut être légèrement intimidant. Mais ici, il y a seulement quelques concepts à comprendre et vous serez opérationnel. En utilisant le module Surfarray, il devient possible de réaliser des opérations au niveau du pixel en utilisant du code python pur. Les compétences requises pour faire cela en C sont d'un niveau beaucoup plus difficilement accessible.
Vous pouvez avoir envie d'aller directement voir à la section [[Pygame/Introduction au module Surfarray#Exemples|Exemples]] pour vous faire une idée sur ce qu'il est possible de faire avec ce module, ensuite nous commençerons par le début pour vous montrer la manière d'y arriver.
Maintenant, je ne vais pas essayer de vous flouer en vous faisant penser que tout est simple. L'obtention d'effets puissants en modifiant les valeurs de chaque pixels est très complexe. Commencer à maîtriser Numeric constitue déjà un apprentissage ardu. Dans ce tutoriel, je serai rapide avec ce qui est facile et je vais utiliser beaucoup d'exemples avec pour objectif de semer les graines de la connaissance. Après avoir fini la lecture de ce tutoriel, vous devriez comprendre les bases du fonctionnement de Surfarray.
== Numeric Python ==
Si la paquet python Numeric n'est pas installé, il est préférable de le faire maintenant. Vous pouvez télécharger le paquet depuis [http://sourceforge.net/project/showfiles.php?group_id=1369 cette adresse]. Pour être certain que Numeric fonctionne chez vous, vous devriez obtenir quelque chose de ce genre à partir du mode interactif de Python.
<source lang="python">
>>> from Numeric import * #Importer Numeric
>>> a = array((1,2,3,4,5)) #Créer un tableau
>>> a #Afficher le tableau
array([1, 2, 3, 4, 5])
>>> a[2] #Un index dans le tableau
3
>>> a*2 #Un nouveau tableau avec des valeurs doublées
array([ 2, 4, 6, 8, 10])
</source>
Comme vous pouvez le voir, le module Numeric nous fournit un nouveau type de données, le ''array''. Cet objet contient un tableau de taille fixe, et toutes les valeurs qu'il contient sont du même type. Les tableaux peuvent aussi être multidimensionnels, et c'est de cette manière nous les utiliserons avec les images. Il y aurait un peu plus à dire à leur sujet, mais c'est suffisant pour commencer.
Si vous observez la dernière commande ci-dessus, vous verrez que les opérations mathématiques sur les tableaux du module Numeric s'appliquent à toutes les valeurs du tableau. Ce fonctionnement est appelé ''elementwise operations''. Ces tableaux peuvent également être ''slicés'' (découpés) à la façon des listes normales. La syntaxe du découpage en slice est la même que celle utilisée avec les objets python standards ''(donc révisez-la si besoin)''. Voici quelques exemples de plus sur le fonctionnement des tableaux :
<source lang="python">
>>> len(a) #Obtenir la taille du tableau
5
>>> a[2:] #Les éléments [2] et supérieurs
array([3, 4, 5])
>>> a[:-2] #Tous exceptés les 2 derniers
array([1, 2, 3])
>>> a[2:] + a[:-2] #Ajout le début et la fin
array([4, 6, 8])
>>> array((1,2,3)) + array((3,4)) #Ajout de tableau de tailles différentes
Traceback (innermost last):
File "<interactive input>", line 1, in ?
ValueError: frames are not aligned
</source>
On obtient une erreur avec la dernière commande, en essayant d'ajouter deux tableaux de tailles différentes. Pour réaliser des opérations impliquant deux tableaux (incluant les comparaisons et les assignations) les deux tableaux doivent avoir les mêmes dimensions. Il est très important de savoir que les valeurs contenues dans un tableau créé depuis le slice d'un original possède les mêmes références que les valeurs du tableau de départ. Donc modifier une valeur dans un slice issue d'un tableau original, modifiera la valeur correspondante du tableau original. Cette propriété des tableaux est très importante à retenir.
<source lang="python">
>>> a #Afficher le tableau de départ
array([1, 2, 3, 4, 5])
>>> aa = a[1:3] #Slicer 2 éléments intermédiaires
>>> aa #Afficher le slice
array([2, 3])
>>> aa[1] = 13 #Modifier une valeur dans le slice
>>> a #Afficher le changement dans l'original
array([ 1, 2, 13, 4, 5])
>>> aaa = array(a) #Faire une copie du tableau
>>> aaa #Afficher la copie
array([ 1, 2, 13, 4, 5])
>>> aaa[1:4] = 0 #Définir à 0 des valeurs intermédiaires
>>> aaa #Afficher la copie
array([1, 0, 0, 0, 5])
>>> a #Afficher l'original
array([ 1, 2, 13, 4, 5])
</source>
Maintenant, nous jetterons un coup d'œil à de petits tableau à deux dimensions. Ne soyez pas trop inquiets, c'est la même chose que d'avoir un tuple à deux dimensions (un tuple dans un tuple). Commençons avec des tableaux à deux dimensions.
<source lang="python">
>>> row1 = (1,2,3) #Créer un tuple
>>> row2 = (3,4,5) #Créer un second tuple
>>> (row1,row2) #Afficher comme un tuple 2D
((1, 2, 3), (3, 4, 5))
>>> b = array((row1, row2)) #Créer un tableau 2D
>>> b #Afficher le tableau
array([[1, 2, 3],
[3, 4, 5]])
>>> array(((1,2),(3,4),(5,6))) #Afficher un nouveau tableau 2D
array([[1, 2],
[3, 4],
[5, 6]])
</source>
Maintenant, avec ce tableau à deux dimensions (que l'on appellera à partir de maintenant un '''2D'''), nous pouvons récupérer des valeurs spécifiques par leur index et slicer dans les deux dimensions. L'utilisation d'une virgule pour séparer les indices, nous permet de chercher/slicer dans plusieurs dimensions. L'utilisation de "''':'''" comme un index (afin de ne pas fournir tous les indices) nous renvoie toutes les valeurs contenues sur cette dimension. Voyons son fonctionnement :
<source lang="python">
>>> b #Afficher la tableau précédent
array([[1, 2, 3],
[3, 4, 5]])
>>> b[0,1] #Indexer une valeur unique
2
>>> b[1,:] #Slicer la seconde rangée
array([3, 4, 5])
>>> b[1] #Slicer la seconde rangée (idem ci-dessus)
array([3, 4, 5])
>>> b[:,2] #Slicer la dernière colonne
array([3, 5])
>>> b[:,:2] #Slicer en un tableau 2x2
array([[1, 2],
[3, 4]])
</source>
Bon, restez avec moi, c'est à peu près aussi dur que ça. En utilisant Numeric, il existe une fonctionnalité supplémentaire pour effectuer des slices. Le slice de tableau nous permet de spécifier un ''incrément de slice''. La syntaxe pour un slice avec incrément est <tt>index_debut : index_fin : increment</tt>.
<source lang="python">
>>> c = arange(10) #Comme range(), mais pour faire un tableau
>>> c #Afficher le tableau
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> c[1:6:2] #Slicer les valuers impaires entre 1 et 6
array([1, 3, 5])
>>> c[4::4] #Slicer toutes les 4 valeurs en démarrant à l'index 4
array([4, 8])
>>> c[8:1:-1] #Slice de 1 à 8 inversé
array([8, 7, 6, 5, 4, 3, 2])
</source>
Voilà. Vous en savez suffisamment pour vous permettre de commencer à utiliser Numeric avec le module Surfarray. Les propriétés du module Numeric sont certainement plus consistantes, mais il ne s'agit que d'une introduction. Par ailleurs, on veut seulement faire des trucs marrants, pas vrai ?
== Importer le module Surfarray ==
Pour utiliser le module Surfarray, nous avons besoin de l'importer. Les modules Surfarray et Numeric étant des composants optionnels de Pygame, il est judicieux de s'assurer de les importer correctement avant de les utiliser. Dans ces exemples, j'importerai le module Numeric dans une variable nommée N. Vous verrez ainsi quelles fonctions utilisées provient du module Numeric (et de plus c'est légèrement plus court que de taper Numeric devant chaque fonction).
<source lang="python">
try:
import Numeric as N
import pygame.surfarray as surfarray
except ImportError:
raise ImportError, "Numeric and Surfarray are required."
</source>
== Introduction à Surfarray ==
Il y a deux principaux types de fonctions dans Surfarray. Un des jeu de fonctions concerne la création d'un tableau qui est une copie des données de pixels d'une surface. L'autre jeu de fonctions crée une copie par référence d'un tableau de pixels, donc changer le tableau modifie directement la surface originale. Il y a d'autres fonctions qui vous permettent d'accéder aux valeurs du canal alpha de chaque pixel à l'aide de tableaux et de plusieurs autres fonctions très utiles. Nous étudierons ces fonctions plus tard.
En utilisant ces tableaux de surface, il y a deux moyens de représenter les valeurs des pixels. La première, peut être de les représenter comme un carte de nombre entiers. Ce type de tableau est un simple tableau 2D avec un unique entier qui représente la couleur du pixel correspondant. Ce type de tableau est pratique pour déplacer des parties d'une image. L'autre type de tableaux utilise trois valeurs pour représenter chaque pixel en codage RGB. Ce type de tableau rend extrêmement simple la réalisation d'effets qui modifie la couleur de chaque pixel. Ce type de matrice est également un peu délicat à manipuler, puisqu'il s'agit en fait d'un tableau à 3 dimensions. Si vous parvenez malgré tout à comprendre le truc, ce n'est pas plus difficile que d'utiliser un tableau 2D normal.
Le module Numeric utilise une machine de nombres naturels pour représenter les données numériques, donc un tableau Numeric peut être constitué d'entier de 8bits, 16bits, et 32bits. (les tableaux peuvent également utiliser d'autres types comme des flottants et des doubles, mais pour notre manipulation d'image nous n'utilisons partiquement que des entiers). Du fait de la limitation en taille de certains entiers, vous devez veiller à ce que les tableaux contenant les données des pixels soient des tableaux dont les données sont du type adéquat. Les fonctions fabriquant ces tableaux à partir de surfaces sont :
<source lang="python">
surfarray.pixels2d(surface)
</source>
:Crée un tableau 2D (valeur des pixels entière) qui '''référence''' les données originales de la surface. Ceci fonctionnera pour tous les formats de surface excepté celles en 24 bits.
<source lang="python">
surfarray.array2d(surface)
</source>
:Crée un tableau 2D (valeur des pixels entière) '''copié''' depuis n'importe quel type de surface.
<source lang="python">
surfarray.pixels3d(surface)
</source>
:Crée un tableau 3D (valeur des pixels codé en RGB) qui '''référence''' les données originales d'une surface. Cela va fonctionner exclusivement avec des surfaces sur 24 bits ou 32 bits qui ont un formatage RGB et BGR.
<source lang="python">
surfarray.array3d(surface)
</source>
:Crée un tableau 3D (valeurs des pixels codé en RGB) '''copié''' depuis n'importe quel type de surface.
Voici un petit résumé qui devrait mieux illustrer quels types de fonctions devraient être utilisés sur quelles surfaces. Comme vous pouvez le voir, les fonctions de type arrayXD vont fonctionner avec tous les types de surface.
{| class="wikitable"
Ligne 168 ⟶ 196 :
| yes
|}
== Exemples ==
Avec ces informations, nous sommes parés pour commencer à essayer diverses choses avec les tableaux de surface. Les petites démonstrations suivantes créent un tableau Numeric et l'affichent dans pygame. Ces différents tests sont issus des exemples contenus dans le fichier <tt>arraydemo.py</tt>. Il y a une fonction simple nommée <tt>surfdemo_show()</tt> qui affiche un tableau à l'écran.
==
<source lang="python">
allblack = N.zeros((128, 128))
</source>
[http://www.pygame.org/docs/tut/surfarray/allblack.jpg Image exemple : allblack.jpg]
Dans notre premier exemple, nous créons un tableau entièrement noir de 128 lignes sur 128 colonnes. Pour créer un tableau numérique avec un taille déterminée, il est préférable d'utiliser la fonction <tt>N.zeros()</tt>. Ici, le tableau de zéro forme une surface noire.
<br style="clear:both;" />
=== Tableaux de 3 dimensions (séparation des composantes RGB) ===
<source lang="python">
striped = N.zeros((128, 128, 3))
striped[:] = (255, 0, 0)
striped[:,::3] = (0, 255, 255)
surfdemo_show(striped, 'striped')
</source>
[http://www.pygame.org/docs/tut/surfarray/striped.jpg Image exemple : striped.jpg]
Ici nous manipulons un tableau à 3 dimensions. On commence par créer une image rouge. Ensuite nous extrayons une ligne sur trois et nous lui donnons la couleur bleu/vert. Comme vous pouvez le constater, nous pouvons traiter les tableaux à trois dimensions presque comme un tableau à deux dimensions, seulement on lui assigne des 3-uplets au lieu de valeurs uniques (scalaires).
<br style="clear:both;" />
=== Extraction des données d'une image depuis un fichier ===
<source lang="python">
imgsurface = pygame.image.load('surfarray.jpg')
imgarray = surfarray.array2d(imgsurface)
surfdemo_show(imgarray, 'imgarray')
</source>
[http://www.pygame.org/docs/tut/surfarray/imgarray.jpg Image exemple : imgarray.jpg]
Ici nous chargeons une image avec la fonction <tt>image.load()</tt> qui la convertit en un tableau 2D d'entiers. Nous utiliserons cette image comme base dans le reste de nos exemples.
<br style="clear:both;" />
=== Retournement ''miroir'' vertical ===
<source lang="python">
flipped = imgarray[:,::-1]
surfdemo_show(flipped, 'flipped')
</source>
[http://www.pygame.org/docs/tut/surfarray/flipped.jpg Image exemple : flipped.jpg]
Voici un retournement vertical de l'image, réalisé en utilisant la notation en ''slices'' à l'aide d'un incrément négatif pour l'indice des colonnes.
<br style="clear:both;" />
=== Miniaturisation d'une image ===
<source lang="python">
scaledown = imgarray[::2,::2]
surfdemo_show(scaledown, 'scaledown')
</source>
[http://www.pygame.org/docs/tut/surfarray/scaledown.jpg Image exemple : scaledown.jpg]
Diminuer une image repose sur le même principe que l'exemple précédent. Ici, la notation en slices est utilisée pour conserver seulement un pixel sur deux à la fois verticalement et horizontalement.
<br style="clear:both;" />
=== Augmentation de la taille d'une image ===
<source lang="python">
size = N.array(imgarray.shape)*2
scaleup = N.zeros(size)
scaleup[::2,::2] = imgarray
scaleup[1::2,::2] = imgarray
scaleup[:,1::2] = scaleup[:,::2]
surfdemo_show(scaleup, 'scaleup')
</source>
[http://www.pygame.org/docs/tut/surfarray/scaleup.jpg Image exemple : scaleup.jpg]
Augmenter la taille d'une image n'est pas aussi radicalement simple, mais s'inspire de la diminution que nous avons réalisé en utilisant les slices. D'abord, nous créons un tableau qui est de deux fois la taille de l'original. On réalise une copie du tableau original, pixel par pixel, en écrivant seulement sur les colonnes paires du tableau de destination, puis on réalise à nouveau l'opération en écrivant seulement sur les colonnes impaires du tableau de destination. A ce stade, nous avons image redimensionnée correctement, mais toutes les lignes impaires sont noires. Il nous suffit alors de recopier chaque ligne paire sur la ligne du dessous. On obtient ainsi une image dont la taille a doublé.
<br style="clear:both;" />
=== Filtrage de canaux ===
<source lang="python">
rgbarray = surfarray.array3d(imgsurface)
redimg = N.array(rgbarray)
redimg[:,:,1:] = 0
surfdemo_show(redimg, 'redimg')
</source>
[http://www.pygame.org/docs/tut/surfarray/redimg.jpg Image exemple : redimg.jpg]
Retour vers les tableaux 3D, on utilisera le codage RGB pour modifier les couleurs. On fait un simple tableau 3D à partir de l'image originale, en utilisant la méthode <tt>surfarray.array3D()</tt>, puis toutes les valeurs pour le bleu et le vert sont mises à zéro. Il nous reste alors, uniquement le canal rouge.
<br style="clear:both;" />
=== Filtrage par convolution ===
<source lang="python">
soften = N.array(rgbarray)
soften[1:,:] += rgbarray[:-1,:]*8
soften[:-1,:] += rgbarray[1:,:]*8
soften[:,1:] += rgbarray[:,:-1]*8
soften[:,:-1] += rgbarray[:,1:]*8
soften /= 33
surfdemo_show(soften, 'soften')
</source>
[
On réalise ici une convolution à l'aide d'un filtre 3x3 qui va adoucir les reliefs de l'image. Cela paraît lourd en calculs, mais ce qui est fait est en fait de décaler l'image de 1 pixel dans toutes les directions, et de sommer toutes ces images (en multipliant par un certain coefficient de poids). Alors, on moyenne toutes les valeurs obtenues. Ce n'est pas un filtre gaussien, mais c'est rapide.
<br style="clear:both;" />
=== Décoloration ===
<source lang="python">
src = N.array(rgbarray)
dest = N.zeros(rgbarray.shape)
dest[:] = 20, 50, 100
diff = (dest - src) * 0.50
xfade = src + diff.astype(N.Int)
surfdemo_show(xfade, 'xfade')
</source>
[http://www.pygame.org/docs/tut/surfarray/xfade.jpg Image exemple : xfade.jpg]
Enfin, Nous réalisons une décoloration croisée entre l'image originale et une fond entièrement en bleu. Ce n'est pas très folichon, mais l'image de destination peut être n'importe quoi, et en modifiant le coefficient multiplicateur (0.50 dans l'exemple), vous pouvez choisir chaque étape pour un fondu linéaire entre deux images.
<br style="clear:both;" />
=== Conclusion ===
J'espère qu'à partir de maintenant vous commencez à voir comment le module Surfarray peut-être utilisé pour réaliser des effets spéciaux et/ou transformations qui ne sont possibles qu'à partir d'une manipulation de pixels. Au minimum, vous pouvez utiliser Surfarray pour faire un grand nombre d'opérations très rapides de type <tt>Surface.set_at()</tt> et <tt>Surface.get_at()</tt>. Mais ne pensez pas que vous en ayez terminé avec ce module, il vous reste encore beaucoup à apprendre.
== Verrouillage de surface ==
Comme le reste de Pygame, Surfarray va verrouiller tout objet de type Surface lors de l'accès aux données de pixels. C'est une chose dont il faut être conscient dans tout ce que vous faites. En créant un tableau de données de pixels, la surface originale sera verrouillée pendant le temps d'existence du tableau de donnée. Il est important de s'en rappeler. Soyez certain d'avoir supprimé le tableau de pixels soit explicitement avec l'instruction Python : <tt>del</tt>, soit implicitement en sortant de l'espace de nom et ainsi faire intervenir le ''garbage collector'' (comme par exemple après un retour de fonction).
Faites attention à ne pas accéder directement à des surfaces en hardware (''HWSURFACE''). Car les données de ces surfaces résident dans la mémoire de la carte graphique, et le transfert de modifications de pixels à travers le bus PCI/AGP n'est pas des plus rapide.
== Transparence ==
Le module Surfarray possède plusieurs méthodes pour accéder aux valeurs du canal alpha/couleur clé d'une surface. Aucune des fonctions qui gèrent le canal alpha, n'a d'effet sur le reste des données de la surface, uniquement sur les valeurs du canal alpha des pixels. Voici la liste de ces fonctions :
;surfarray.pixels_alpha(surface)
:Crée un tableau 2D de valeurs entières qui
;surfarray.array_alpha(surface)
:Crée un tableau 2D de valeurs entières qui
;surfarray.array_colorkey(surface)
:Creates a 2D array (integer pixel values) that is set to transparent (0) wherever that pixel color matches the Surface colorkey. Crée un tableau 2D de valeurs entières qui met la transparence à 0 (valeur maximale de transparence) pour chaque pixel de la surface dont la couleur correspond à la couleur
== Autres fonctions du module Surfarray ==
Il existe quelques autres fonctions disponibles dans le module Surfarray. Vous pouvez en obtenir une liste exhaustive ainsi qu'une description plus complète sur la page de [http://pygame.seul.org/docs/ref/surfarray.html référence]. Notez malgré tout cette fonction très utile :
;surfarray.blit_array(surface, array)
:This will transfer any type of 2D or 3D surface array onto a Surface of the same dimensions. This surfarray blit will generally be faster than assigning an array to a referenced pixel array. Still, it should not be as fast as normal Surface blitting, since those are very optimized.
Ceci va
== Utilisation plus avancée de Numeric ==
Voici deux dernière choses qu'il est bon de connaître à propos des tableaux de Numeric. En manipulant des
En manipulant les tableaux avec des valeurs de pixel codés sur 16 bits, Numeric n'utilise pas des entiers non signés sur 16 bits, donc certaines de vos valeurs seront des nombres négatifs signés. Heureusement ça ne pose pas de problème.
Une autre chose à laquelle il faut veiller en utilisant des tableaux est le type de données manipulé. Certains tableaux (particulièrement les surfaces de pixels ''mappées'', en codage RGB) retourne des tableaux avec des valeurs sur 8 bits non signés. Ces tableaux peuvent facilement provoquer un dépassement de capacité si vous n'êtes pas très attentifs.Le module Numeric possède les mêmes contraintes que vous trouverez dans le langage C, c'est à dire qu'une opération avec un nombre en 8 bits et un nombre en 32 bits va renvoyer un nombre en 32 bits. Vous pouvez toujours convertir le type de donnée d'un tableau, mais soyez toujours certain du type que contienne les tableaux que vous manipulez. S'il arrive une situation dans laquelle un dépassement de capacité est provoqué, Numeric va lever une exception.
Enfin, vous devez faire attention lorsque vous assignez des valeurs dans un tableau à trois dimensions, celles-ci doivent être comprises entre 0 et 255, sinon vous obtiendrez des erreurs de troncatures indéfinies.
== Remise du Diplôme ==
Ok, vous l'avez, ma formation rapide sur Numeric python et Surfarray. Espérons que maintenant vous voyez ce qu'il est possible de faire, et que si vous ne l'avez jamais utilisé vous-même, vous ne serez pas effrayé à la vue de ces codes. Regardez dans l'exemples <tt>vgrade.py</tt> pour plus d'actions sur les tableaux Numeric. Il existe également quelques démonstrations "enflammées" qui utilisent Surfarray pour créer un effet de liquide en temps réel.
Le mieux est toujours d'essayer des choses par vous même. Allez-y tranquillement au début, et construisez au fur et à mesure. J'ai vu des choses très intéressantes faites avec Surfarray comme des gradients radiaux et d'autres choses dans le genre. Bonne Chance.
|