Introduction à MATLAB MATrix LABoratory : Ce tour d’horizon fournit une familiarisation rapide mais néanmoins rigoureuse de la programmation MATLAB. En effet, MATLAB n’est pas une application "pousse-bouton", mais un langage de programmation interprété (i.e. dont les instructions sont exécutées à la volée). Ce langage a été conçu pour soulager l’utilisateur scientifique de la plupart des difficultés informatiques inhérentes aux langages à usage universel comme C/C++ ou Java. Sa syntaxe, compacte et élégante, est bien adaptée pour le calcul matriciel. Sa plate-forme permet d’acquérir, de traiter, d’analyser et de visualiser pratiquement n’importe quelles données. Elle est constituée d’un noyau de routines compilées (i.e. traduites, une fois pour toute, en code machine après analyse lexicale, syntaxique et contextuelle de leur code source), particulièrement robustes et optimisées, auquel peuvent se greffer des bibliothèques spécialisées, appelées "toolboxes". À cela s’ajoute un IDE (s.s. un environnement de développement intégré) complet qui permet d’implémenter efficacement de nouveaux algorithmes et de construire des interfaces graphiques conviviales, le tout, directement utilisable sous Linux, MacOS, UNIX et Windows !
Lancement de MATLAB : Matlab version 6 pour Windows est installé sur les PCs des salles de TP. L’application s’ouvre sous la forme d’une fenêtre cadre hébergeant (de haut en bas) une barre de titre, une barre de menu, une barre d’outils, trois fenêtres vue et une barre d’état. La barre de menu et la barre d’outils offrent les commandes habituelles comme la création de document, l’ouverture de fichier, l’édition de texte, le réglage des préférences, et l’accès à l’aide interactive via le menu «Help» et la commande «MATLAB Help». Écrite en HTML, (s.s. Hyper Text Markup Language) cette dernière est classée thématiquement et est largement illustrée. Dans la barre d’outils, le combo (s.s. la liste combinée) intitulé «Current Directory» (s.s. dossier courant) permet d’éditer le chemin du répertoire de travail. Par défaut, MATLAB recherche les programmes et les données de l’utilisateur dans «'C:\MATLAB…\work'». Le bouton «…» ouvre un explorateur qui permet de sélectionner un nouvel emplacement dans l’arborescence du système de fichiers. Pour les TPs, il est judicieux de créer un dossier sur le bureau (i.e. dans le profil personnel et itinérant de l’utilisateur) puis de le rajouter définitivement à la fin de la hiérarchie de recherche de MATLAB, via le menu «File» et la commande «Set Path…». La vue intitulée «Command Window» est la console de MATLAB. Les commandes en ligne doivent êtres saisies à la suite du prompt (i.e. l’invite de commande de la forme «>>»), et sont exécutées par l’interpréteur MATLAB après validation sur la touche entrée. Il est recommandé de suivre la démo qui s’obtient avec la commande « demo », puis de lancer des commandes d’aide en ligne comme « help », « help ops », « help elfun », « help sqrt » et de faire des recherches par mot clé comme « lookfor 'square root' ». Il est possible d’économiser les frappes au clavier grâce à la touche tabulation qui appelle la complétion (s.s. le complément) du lexème en cours de frappe. De même, les touches haut et bas rappèlent, avec ou sans complétion, les commandes passées. 1
La page intitulée «Workspace» contient une table détaillée des variables présente dans l’espace de travail (i.e. stockées en mémoire). La barre d’outils permet de charger, sauvegarder, éditer et effacer les variables sans avoir à taper de commande en ligne. La vue intitulée «Command History» affiche une liste chronologique des commandes passées. Un double-clic suffit pour relancer la commande sélectionnée.
Valeurs et variables : La console peut être utilisée comme une simple calculatrice. Les résultats sont automatiquement affectés à la variable « ans » (s.s. un diminutif de "answer"). >> 1 + 1 ans = 2
Une variable est un identificateur (i.e. un nom symbolique, de préférence évocateur et non ambigu) désignant l’allocation mémoire d’une valeur. Cette dernière est évidemment modifiable, contrairement à celle d’une constante (e.g. « pi », « true », « eps », …). Pour conserver un calcul intermédiaire, il suffit donc de déclarer une nouvelle variable et de l’initialiser avec l’expression souhaitée. L’identificateur est une chaîne de caractères alphanumériques (i.e. composés de lettres et de chiffres) non accentués et sensibles à la casse (i.e. aux majuscules et aux minuscules), qui ne doit pas contenir d’espace et dont le premier caractère doit être purement alphabétique. >> MaVariable = ans + 1 MaVariable = 3 >> MaVariable * ans ans = 6
L’attribut principal d’une variable est son type, c’est-à-dire la longueur et la signification du code binaire sérialisé en mémoire physique à partir de l’adresse indiquée par la variable. C’est lui qui détermine les opérations possibles avec la variable. La fonction « whos » montre que MATLAB stocke (quasiment) tout, sous la forme de tableaux multidimensionnels. Par exemple, l’entier naturel 42 est devenu un «double array» de taille 1x1, c’est-à-dire un tableau bidimensionnel de nombre décimaux codés en virgule flottante et en double précision, ne contenant qu’un seul élément. En fait, les tableaux dynamiques (i.e. dont la taille et donc l’occupation mémoire peuvent varier librement) bidimensionnels de décimaux constituent la structure de donnée la plus naturelle pour manipuler des matrices (i.e. le fond de commerce de MATLAB). >> >> >> >> >> >> >>
clear Var1 = Var2 = Var3 = Var4 = Var5 = whos Name Var1 Var2 Var3 Var4 Var5
42; 3.14; 1 + 2i; [1 2; 2 3; 3 4]; 'Hello World';
Size 1x1 1x1 1x1 3x2 1x10
% % % % % %
Efface les variables de l'espace de travail. Un nombre entier. Un nombre décimal. Un nombre complexe. Une matrice entière 3x2. Une chaîne de caractères.
Bytes 8 8 16 48 20
Class double array double array double array (complex) double array char array
Grand total is 19 elements using 100 bytes
2
MATLAB est un langage faiblement typé, ce qui masque une bonne part de la complexité des types informatiques sous-jacents (e.g. entiers, flottants, caractères, booléens, pointeurs, références, tableaux, structures, unions, ...). C’est ainsi que les opérations de transtypage (s.s. les conversions de type) sont généralement implicites. Toutefois, autant de souplesse sémantique n’est pas forcément une qualité puisque cela autorise le meilleur comme le pire... >> 'ABC' + 0 ans = 65 66 67
% Une chaîne de caractères additionnée à un entier. % Un tableau d'entiers (les codes ASCII de 'ABC').
Variables numériques et symboliques: Un «double» est une valeur décimale codée en virgule flottante et en double précision. Cette chaîne de 64 bits (e.q. 8 bytes ou octets) représente, une fois traduite en base 10, une mantisse signée à 16 chiffres significatifs, et un exposant signé à trois chiffres. Les constantes «realmax», «realmin», «eps», «Inf» et «NaN» sont spécifiques au calcul numérique. >> format long e >> realmax ans = 1.797693134862316e+308 >> eps ans = 2.220446049250313e-016 >> 1/0 ans = Inf >> 0/0 ans = NaN
% Notation scientifique étendue. % Plus grande valeur positive représentable. % e.g. "2 * realmax == Inf". % Plus petite nombre "n" tel que "n + 1 > 1". % e.g. "(eps/2 + 1) == 1".
% Infini.
% Not-a-Number.
Les erreurs d’arrondis rendent plus ou moins rapidement le résultat imprécis. Si l’inverse de l’inverse de «49» ne vaut pas exactement«49», c’est parce que l’expression «1/49» n’est qu’une valeur arrondie à environ 16 chiffres significatifs de la vraie constante mathématique (rationnelle). >> VarNum = 1/49 VarNum = 2.040816326530612e-002 >> 49 - 1/VarNum ans = -7.105427357601002e-015
Une solution élégante, consiste à travailler avec la «Symbolic Math Toolbox», qui sait manipuler et résoudre les expressions symboliquement (e.g. résolution d’équation différentielle, …). >> syms x y >> y = 1/x; >> x - 1/y ans = 0
% Déclaration des variables symboliques x et y. % Initialisation de la variable y.
Si une expression symbolique doit finalement être évaluée numériquement, le calcul peut être effectué avec une précision arithmétique variable, réglée par défaut à 32 chiffres significatifs. >> format >> vpa('pi', 80) ans =
% Notation par défaut. % Approximation à 80 chiffres significatifs de la % constante (irrationnelle et transcendante).
3.1415926535897932384626433832795028841971693993751058209749445923078164062862090
3
Expressions et opérateurs : Les expressions représentent le niveau supérieur dans la structure d’un programme MATLAB. L’interpréteur évalue une expression pour calculer sa valeur. Les expressions les plus simples sont les littéraux, les constantes et les variables. >> 3.14 ans = 3.1400
% Un littéral.
>> pi ans =
% Une constante. 3.1416
Les expressions plus complexes sont construites à l’aide d’opérateurs. L’expression suivante utilise l’opérateur « * » pour combiner un littéral et une constante au sein d’une expression de multiplication. >> 2 * pi ans = 6.2832
La commande d’aide « help ops » fournit la liste des opérateurs et des caractères spéciaux (i.e. ponctuation, parenthèsage, …). Une page d’aide existe pour décrire la fonction ainsi que le nombre, l’ordre et le type des opérandes de chaque opérateur. C’est ainsi que « help uminus » décrit le "moins monadique" comme un opérateur unaire (i.e. agissant sur un opérande unique) qui change le signe des éléments du tableau qui lui est passé par la droite. De même, «help punct» indique que la virgule sépare les instructions multiples et que le point-virgule permet en plus d’empêcher l’affichage du résultat. >> Tab = [1 -2]; -Tab ans = -1 2
La commande d’aide « help precedence » fournit la hiérarchie de priorités des opérateurs. Une expression doit être composée en prenant garde à la précédence des opérateurs et des caractères spéciaux utilisés. >> 1 + 2 * 3, (1 + 2) * 3 ans = 7 ans = 9
Lorsqu’une expression implique des opérations possédant une précédence équivalente, l’associativité (i.e. l’ordre d’évaluation des opérandes) des opérateurs définit l’ordre dans lequel les opérations sont exécutées. Mis à part l’affectation et les opérateurs monadiques, les opérateurs possèdent une associativité de la gauche vers la droite. >> 3 * (5 \ 15) ans = 9
% Equivalent à "3 * (15 / 5)".
>> 3 * 5 \ 15 ans = 1
% Equivalent à "15 / (3 * 5)".
En langage MATLAB, l’expression d’affectation est particulière puisqu’elle ne possède qu’un effet de bord (i.e. son évaluation modifie l’état du programme mais ne retourne pas de valeur).
4
Tableaux et matrices : La façon la plus naturelle d’initialiser une matrice est d’utiliser un agrégat de littéraux placés entre crochets. Les colonnes sont séparées par des espaces ou des virgules et les lignes par des point-virgules. >> RVect = [1 3 5 7] RVect = 1 3 5 >> size(RVect) ans = 1 4
% Un vecteur ligne. 7
>> CVEct = [1; 3; 5; 7] CVEct = 1 3 5 7 >> size(CVect) ans = 4 1
% Un vecteur colonne.
>> Mat34 = [11 12 13 14; ... 21 22 23 24; ... 31 32 33 34] Mat34 = 11 12 13 14 21 22 23 24 31 32 33 34 >> size(Mat34) ans = 3 4
% Une matrice 3x4.
Il existe une syntaxe intuitive pour initialiser un vecteur sous la forme d’une suite arithmétique bornée de raison entière ou réelle. >> -2:1 ans = -2
% Incrément de 1, équivalent à "-2:1:1".
>> 7:-2:1 ans = 7
-1
0
1 % Incrément de -2.
5
3
1
Par défaut, les fonctions «linspace() » et «logspace() » créent une suite de 100 éléments repartis linéairement ou logarithmiquement entre deux bornes. >> x = linspace(0, 2*pi); >> y = sin(x); >> plot(x,y)
% Affichage de sin(x) sur l'intervalle [0, 2].
>> logspace(1, 5, 3) ans = 10 1000 100000
Des fonctions existent pour créer des matricesaux propriétés remarquablescomme «ones()», «zeros()», «eye()», «rand()»,«magic()», «pascal() », «hadamard()», etc. >> rand(3,3) ans = 0.9501 0.2311 0.6068
% Création d'une matrice 3x3 pseudo-aléatoire.
0.4860 0.8913 0.7621
0.4565 0.0185 0.8214
5
Les éléments d’une matrice sont indexés séquentiellement, à partir de 1. MATLAB respecte la convention surnommée "licol" (s.s. ligne-colonne), puisque la première dimension représente les lignes et la deuxième dimension les colonnes. Il est possible d’utiliser des tableaux d’indices ainsi que le mot réservé «end». >> Mat34(8) ans = 23
% Indexage façon FORTRAN, BLAS, etc.
>> Mat34(2, 3) ans = 23
% "I = L + NL*(C-1)" donne "8 = 2 + 3(3-1)".
>> Mat34([1 3], [1 4]) ans = 11 14 31 34
% Extraction d'une sous matrice.
>> Mat34(:, end) = 0 Mat34 = 11 12 13 21 22 23 31 32 33
% Equivalent à "Mat34(1:3, 4) = 0". 0 0 0
>> Mat34(:, end) = [] Mat34 = 11 12 13 21 22 23 31 32 33
% Suppression de la dernière colonne.
>> Mat34(2, 4) = 24 Mat34 = 11 12 13 21 22 23 31 32 33
% Construction par agrégation successive. 0 24 0
Il est aussi possible de construire des matrices par concaténation horizontale ou verticale. >> a = 0:4; b = 5:9; c = [a b; b a] c = 0 1 2 3 4 5 5 6 7 8 9 0
6 1
7 2
8 3
9 4
L’opérateur de transposition permet de changer l’orientation d’une matrice. Toutefois, il sert aussi à calculer le conjugué d’un nombre complexe, et possède une précédence élevée. >> T = 1:2; >> T * T' ans = 5 >> T' * T ans = 1 2 2 4 >> T = 1:3' ans = 1 2
% Equivalent à "1:(3')" avec "3' == 3". 3
Les opérateurs «.*», «./», «.\», et «.^» se distinguent des opérateurs matriciels traditionnels puisqu’ils agissent élément par élément, sur des tableaux de tailles identiques. >> [1 2] .\ [2 4] ans = 2 2
% Division gauche A\B == "inv(A)*B"
6
Persistance des variables : Lorsque que l’application MATLAB quitte, l’espace de travail est définitivement perdu. Pour reprendre ultérieurement une session, il faut avoir préalablement sauvegardé les variables utiles, dans un ou plusieurs fichiers enregistrés sur un support persistant (e.g. disque dur, CD-ROM...). >> save MesVariables V1 V2 V3 >> dir *.mat MesVariables.mat
% Sauvegarde les variables. % Liste du dossier de travail filtrée % par l'expression régulière "*.mat".
>> clear V1 V2 V3 >> load MesVariables
% Efface les variables. % Recharge les variables.
MATLAB sérialise les données dans un format binaire propriétaire, puis écrit le flux d’octets dans un fichier MAT (i.e. avec une extension ".mat"). MATLAB propose des fonctionnalités de sérialisation et de désérialisation analogues à celles du langage C (e.g. « fprintf », « fscanf », ...), mais la solution la plus simple pour échanger des données avec d’autres applications est de passer par un fichier texte au format ASCII 8 bits (i.e. un jeu de caractère limité à 256 codes alphanumériques), avec retours chariot et tabulations pour délimiter les éléments. >> V4 = [1 2; 2 3; 3 4]; >> save MaMatrice.dat V4 -ascii –tabs >> type MaMatrice.dat 1.0000000e+000 2.0000000e+000 3.0000000e+000
% Format ASCII avec tabulation. % Lecture du fichier dans la console.
2.0000000e+000 3.0000000e+000 4.0000000e+000
>> V6 = load('MaMatrice.dat') ;
% Chargement dans une variable.
Scripts et fonctions : Il est pratique de pouvoir exécuter les instructions (i.e. les unités syntaxiques du langage, comme les expressions), manuellement, les unes à la suite des autres, directement depuis la console. Mais il est encore plus intéressant de pouvoir répartir ces mêmes instructions dans un ou plusieurs scripts (s.s. fichiers de commandes) de manière à disposer de procédures (s.s. sousprogrammes) réutilisables. Un traitement de texte capable de sauvegarder en ASCII (i.e. dans un format textuel pur) suffit pour créer un fichier M (i.e. avec une extension ".m") mais l’éditeur intégré de MATLAB offre des facilitées d’édition comme la syntaxe colorée ou l’indentation automatique ainsi que des fonctionnalités de déboguage comme l’insertion de breakpoint, etc. %EPURATION Purge l'espace de travail et efface la console. % EPURATION détruit les variables de l'espace de travail, % efface la console et réinitialise la position du curseur. % % Voir aussi CLEAR, CLC, HOME clear clc
% Purge l'espace de travail. % Efface la console.
Si un fichier M est présent dans le répertoire de travail ou la hiérarchie de recherche, il suffit de saisir son nom de base (i.e. sans chemin et sans extension) pour qu’il soit trouvé. Toutefois, ce nom ne doit pas être surchargé (i.e. désigner plusieurs objets à la fois). En effet, le langage MATLAB ne dispose pas de mécanisme pour résoudre ce genre d’ambiguïté. C’est ainsi que la résolution de nom s’arrête au premier homonyme trouvé parmi les variables, puis les fonctions internes, le répertoire de travail, et enfin la hiérarchie de recherche. 7
>> exist epuration ans = 2
% Test d'existence de l'identificateur.
>> which epuration
% Recherche le nom complet du fichier.
C:\Documents and Settings\Virtual\Bureau\TP MATLAB\epuration.m
Un fichier M supporte l’aide en ligne si son code source (i.e. l’ensemble des instructions) est précédé par un en-tête composé d’un continuum de lignes de commentaire. La première de ces lignes est aussi utilisée pour la recherche par mot clé. >> help epuration EPURATION Purge l'espace de travail et efface la console. EPURATION détruit les variables de l'espace de travail, efface la console et réinitialise la position du curseur. Voir aussi CLEAR, CLC, HOME >> lookfor purge EPURATION
Purge l'espace de travail et efface la console.
Les variables déclarées à l’intérieur d’un script appartiennent à l’espace de travail. Elles sont donc partagées avec les autres scripts et, à moins que le programmeur ne les efface (e.g. par un appel à «clear()»), elles perdurent jusqu’à la fin de la session MATLAB. Les scripts sont donc très pratiques comme procédures à effet de bord sur les variables d’environnement de MATLAB, mais sont inadaptés pour la programmation modulaire (i.e. le partitionnement des programmes de telle façon que les données soient cachées dans des procédures indépendantes). Pour cela, il faut rédiger des fonctions MATLAB, c’est-à-dire des fichiers M qui commencent par une ligne définissant le nom de la fonction ainsi que ses paramètres d’appel et de retour. Le nom du fichier doit correspondre à celui de la fonction. function outTab = isqrt(inTab) %ISQRT calcule l'inverse de la racine carrée de inTab. % isqrt est "vectorisée" (i.e. sait gérer plusieurs éléments en parallèle). % % Voir aussi SQRT outTab
= 1./sqrt(inTab);
>> isqrt(1:4) ans = 1.0000 0.7071
0.5774
0.5000
Par défaut, les variables internes ont une portée locale à la fonction et n’existent (en mémoire) que le temps de l’appel de cette dernière. Si une variable doit être visible par plusieurs fonctions, elle doit être déclarée globale dans chacune d’elles. function tick %TICK démarre le chronomètre. Voir aussi TIC. global g_Clock; g_Clock = clock;
function tock %TOCK arrête le chronomètre.
% Déclaration d'une globale. % Horaire et Date.
Voir aussi TOC.
global g_Clock; disp(sprintf('Temps écoulé: %f sec.', ... etime(clock, g_Clock))); clear global g_Clock;
8
% Formatage de type "C/C++". % Destruction de la globale.
Une fonction peut s’appeler directement ou indirectement de façon récursive. Chaque niveau d’appel possède son propre espace de travail, indépendant du précédent. Comme il faut stocker une pile de valeurs intermédiaires, la récursion n’est pas synonyme d’économie de mémoire, mais les algorithmes de ce type sont souvent très compacts et élégants. function outInt = factorielle(inInt) %FACTORIELLE calcule récursivement la factorielle de inInt, qui doit être % un entier positif ou nul. Compte tenu de la précision du type double, % le résultat n'est correct que si inInt est inférieur ou égal à 21. % % Voir aussi FACTORIAL if (inInt <= 1) outInt = 1; else outInt = inInt * factorielle(inInt - 1); end
% Cas trivial. % Cas général.
>> factorielle(10) ans = 3628800
Les fonctions MATLAB les plus simples ressemblent un peu à des équations algébriques de la forme "y = f(x)", où l’argument "x" fait référence à une valeur inconnue et où "y" prend la valeur de l’image de "x" par "f()". Mais les fonctions MATLAB les plus complexes peuvent posséder plusieurs arguments tant en entrée qu’en sortie et même en utiliser un nombre variable. La page d’aide «matlab/lang» décrit les variables spécialisées dans la transmission de paramètre comme «nargin», «nargout », «varargin » et «varargout ». En principe, une fonction MATLAB retourne ses valeurs de sortie au programme appelant, lorsque la fin de son code source est atteinte. Toutefois, à n’importe quel moment, l’instruction «return » permet de sortir normalement de la fonction alors que la fonction «error()» sert à provoquer la fin anormale du programme complet. Il est aussi possible de forcer l’arrêt d’un programme en cours d’exécution (e.g. en cas de récursion ou de boucle infini), en appliquant le raccourci clavier «Ctrl-C» sur la console. Le cas échéant, il faut essayer de tuer le processus MATLAB en appelant le gestionnaire de tache grâce à la combinaison «Ctrl-Alt-Delete». function [outMin, outMax] = minmax(inTab, inDim) %MINMAX est une encapsulation simplifiée des fonctions MIN et MAX. if nargin == 0, error('Pas le bon nombre d''argumentsen entrée !'), end if isempty(inTab), outMin = []; outMax = []; return, end if nargin == 1 outMin = min(inTab); outMax = max(inTab); else outMin = min(inTab, [], inDim); outMax = max(inTab, [], inDim); end >> [min, max] = minmax([ 1 2 5 3; 4 8 2 3], 2) min = 1 2 max = 5 8
La première fois que MATLAB exécute une fonction, celle-ci est analysée puis compilée dans un byte-code (i.e. traduite dans une représentation interne plus efficace) qui est directement stocké en mémoire. Cette étape permet de déclarer plusieurs fonctions dans un même fichier-M. Dans ce cas, les sous-fonctions ont une porté de fichier (i.e. sont invisibles depuis les autres fichiers).
9
Tests et instructions de contrôle : MATLAB gère les booléens (i.e. les valeurs logiques) comme des nombres. C’est ainsi que «0» est interprété comme «false» et toute les autres valeurs comme «true». >> if pi, disp('c''est vrai'), end c'est vrai >> ~false ans = 1
L’instruction «if-elseif-else-end» permet d’exprimer des prises de décision basées sur des tests construits avec des opérateurs relationnels «<», «<=», «>», «>=», «==», «~=», ou logiques «&», «|», «~», et des fonctions comme «isreal()», «ischar()», «isequal()», etc. if ~isreal(inInt), elseif isempty(inInt), elseif (isinf(inInt) | isnan(inInt)), elseif fix(inInt) ~= n, elseif inInt < 0, else, end
error('inInt n''est pas un nombre!') error('inInt n''est pas initialisé !') error('inInt n''est pas déterminable!') error('inInt n''est pas un entier!') error('inInt n''est pas positif !') disp 'Test factorielle réussi!'
L’instruction «swicth-case-otherwise-end » permet d’exprimer des prises de décision basées sur des choix multiples comme pour ce convertisseur d’unité: switch inUnit case 'cm' y = x / 100; case { 'millimeter', 'mm' } y = x / 1000; otherwise disp(['Unité inconnue: ' inUnit]) y = NaN; end
% Convertit x en mètres.
L’instruction «for-end» permet de répéter un bloc d’instruction un nombre de fois fixe et prédéterminé. Il faut initialiser la boucle avec une variable et un tableau de valeurs que prendra successivement la variable. outInt = 1; if (inInt <= 1), return, end for Cpt = 2:inInt outInt = outInt * Cpt; end
% Factorielle, cas trivial. % Factorielle, cas général.
L’instruction «while-end» permet de répéter un bloc d’instruction un nombre de fois variable et indéterminé, en se basant sur l’évaluation d’une expression conditionnelle. outInt = 1; if (inInt <= 1), return, end Cpt = inInt; while Cpt > 1 outInt = outInt * Cpt; Cpt = Cpt - 1; end
% Factorielle, cas trivial. % Factorielle, cas général.
L’instruction «break» provoque la sortie d’une boucle «for» ou «while» avant sa fin logique. En cas de boucle imbriquée, seule la boucle courante est interrompue, laissant la boucle immédiatement supérieure se poursuivre normalement. L’instruction «continue» permet de sauter une itération pour passer directement à la suivante. 10
Figures et graphiques : MATLAB dispose de nombreuses fonctions graphiques aux usages plus ou moins complexes. Le programmeur aura tout intérêt à découvrir les exemples illustrés de l’aide interactive avant de parcourir l’inventaire "à la Prévert" proposé par les pages de l’aide en ligne comme «graph2d», «graph3d», «specgraph » et «graphics ». Le rendu se fait de manière vectorielle et dans une figure MATLAB, c’est-à-dire une fenêtre vue comportant une barre de menu et une barre d’outils aux fonctionnalités similaires à celle d’un logiciel de PAO (cf. figure 1).
Figure 1: Tracés de courbes 2D dans une figure MATLAB et édition manuelle du graphique.
MATLAB attribue automatiquement un handle (s.s. un descripteur) aux objets graphiques. Utilisé avec les fonctions «get() » et «set() » cet identifiant numérique permet de consulter et de modifier les propriétés des objets graphiques directement depuis un programme. Par exemple, l’instruction «get(gcf) » fournit la liste complète des propriétés de la figure active alors que l’instruction «set(gca) » se limite aux propriétés modifiables du graphe actif. Toutes ces propriétés sont décrites dans l’aide interactive à la page «Handle Graphics Property Browser». >> >> >> >> >> >> >>
thePoly = [-1 1 0 0]; x = linspace(-1, 2, 200); y = polyval(thePoly, x); thePolyH = plot(x, y); theWidth = get(thePolyH, 'LineWidth'); set(thePolyH, 'LineWidth', theWidth + 1); set(gcf, 'Name', 'Exemple of 2D plot');
% % % % % % %
Définition du polynôme "-x^3 +x^2". Définition de l'intervalle [-1, 2]. Evaluation du polynôme. Tracé 2D. Epaisseur du tracé. Modification de l'épaisseur. Définition du titre de la figure.
Par défaut, le graphe actif est effacé (e.q. «cla()») avant chaque nouveau tracé. Pour que les tracés se superposent, il faut préalablement le spécifier avec la fonction «hold()» ou une instruction tel que «set(gca, 'NextPlot', 'add')». >> >> >> >> >>
theDer = polyder(thePoly) y = polyval(theDer, x); hold on plot(x, y, 'r-'); hold off
% % % % %
11
Dérivée du polynôme. Evaluation de la dérivée. Superposition des tracés. Tracé 2D avec chaîne de formatage. Fin de la superposition des tracés.
Par défaut, MATLAB adapte l’échelle des axes en fonction de l’étendue des valeurs à afficher. La fonction «axis()» permet de spécifier beaucoup d’autres comportements. Il est aussi possible d’associer des légendes aux axes, d’afficher une grille, etc. >> >> >> >> >> >>
axis([-2 2 -2 2]) grid on xlabel('Abscisses') ylabel('Ordonnées') title('Dérivation de polynômes') theDer = polyder(thePoly)
% % % % % %
Etendue des axes. Affichage de la grille. Légende de l'axe des abscisses. Légende de l'axe des ordonnées. Titre du graphique. Dérivée du polynôme.
Une figure peut héberger plusieurs graphes. Par exemple, la fonction «subplot()» subdivise une figure en mosaïque et spécifie le graphe actif. Les graphes sont numérotés depuis le coin supérieur gauche et de gauche à droite. La figure 2 a été obtenue en sauvegardant au format EPS (i.e. sous une forme vectorielle) le contenu de la figure MATLAB via son propre menu «File» et la commande «Exporte…». >> >> >> >> >> >> >>
clear all, close all x = linspace(-1, 1, 200); y = exp((-x.^2)); subplot(2,2,1), plot(x, y), subplot(2,2,2), stem(x, y), subplot(2,2,3), stairs(x, y), subplot(2,2,4), bar(x, y),
% Ferme toutes les figures.
title('Courbe pleine'), title('Diagramme en bâton'), title('Diagramme en escalier'), title('Diagramme en barre'),
Courbe pleine 1
0.8
0.8
0.6
0.6
0.4
0.4
0.2
0.2
ï1
0
1
0 ï2
2
Diagramme en barre 1
0.8
0.8
0.6
0.6
0.4
0.4
0.2
0.2
ï1
0
0 0 0 0
1]) 1]) 1]) 1])
1
ï1
0
1
2
Diagramme en escalier
1
0 ï2
2 2 2 2
Diagramme en bâton
1
0 ï2
axis([-2 axis([-2 axis([-2 axis([-2
0 ï2
2
ï1
0
1
2
Figure 2: Export au format EPS d’une figure MATLAB contenant quatre sous-graphes.
Pour créer ou activer une figure il suffit de donner son numéro en argument à la fonction «figure()». Ensuite l’instruction «close(gcf)» permet de fermer la figure courante.
12
Construction d’une interface graphique : Dans le cadre de cette introduction, nous nous limiterons à l’étude d’un cas concret: la création d’une GUI (s.s. Graphical User Interface) pour un algorithme de compression d’image basé sur la DCT (s.s. Discrete Cosinus Transform). La DCT est une variante de la transformation de Fourier qui est couramment utilisée en traitement du signal (e.g. JPEG, MP3, MPEG) car contrairement à cette dernière, elle transforme un signal réel en un signal réel (et non complexe). Un signal discret de longueur N est ainsi décomposé en une somme de N termes sinusoïdaux aux fréquences croissantes et multiples de 1/2N. Ces sinusoïdes sont appelées les harmoniques du signal et l’ensemble des termes de la décomposition harmonique forme le spectre du signal. La transformation est inversible et le spectre en amplitude (i.e. l’ensemble des amplitudes signées des harmoniques) suffit à décrire le signal. En fait, le signal et son spectre correspondent à deux signatures duales de la même réalité: l’une spatiale ou temporelle et l’autre fréquentielle. Les fonctions «dct()» et «idct()» ainsi que leurs homologues bidimensionnelles «dct2()» et «idct2()» sont décrites dans l’aide interactive de la «Signal Processing Toolbox». Il faut noter que MATLAB indices les harmoniques sur [1, N] et non sur [0, N-1]. Voici un script qui utilise les capacités graphiques de MATLAB pour illustrer les principales propriétés de la DCT (cf. figure 3). %DECOMPOSITION Montre un exemple de décomposition de signal par DCT. N = 1024; n = 1:N; t = linspace(0, 2*pi, N+1); t(end) = [];
% % % %
Nombre d'échantillons. Indices des échantillons. Définition d'un domaine temporel sur [0, 2[.
y2 y3 y4 y
% % % %
Harmonique de rang 3. Harmonique de rang 86. Harmonique de rang 92. Reconstruction du signal.
% % % %
Calcul le spectre du signal. Indices des harmoniques. Rang des harmoniques. Amplitudes des harmoniques.
theFigH = figure; plot(t, y, '.- ') axis equal grid on xlabel('variable t'), ylabel('variable y') title('Exemple de décomposition par DCT') gtext('S(t)')
% % % % % % %
Création d'une figure vierge. Affichage par points reliés. Echelles des axes identiques. Affichage de la grille. Labels des axes. Titre du graphique. Annotation manuelle.
hold on plot(t, y2 t, y3 t, y4 text(2*pi, text(2*pi, text(2*pi, axis off
% % % % % % % %
Superposition des tracés . Affichage de H03 en magenta. Affichage de H86 en vert. Affichage de H92 en jaune. Annotation du graphique. Annotation du graphique. Annotation du graphique. Cache les axes.
= = = =
sqrt(2/N)*6*cos(pi*(2*n-1)*(04-1)/(2*N)); sqrt(2/N)*8*cos(pi*(2*n-1)*(87-1)/(2*N)); sqrt(2/N)*7*cos(pi*(2*n-1)*(93-1)/(2*N)); y2 + y3 + y4;
theSpc thePos theHar theMag
= = = =
dct(y); find( abs(theSpc) > 10^(-13) ); thePos -1 theSpc(thePos)
+ 2, 'm.-', ... + 3, 'g.-', ... + 4, 'y.-') 2, ' H03') 3, ' H86') 4, ' H92')
waitforbuttonpress close(theFigH)
>> decomposition theHar = 3 86 92 theMag = 6.0000 8.0000
% Pause utilisateur. % Fermeture de la fenêtre.
7.0000
13
Exemple de décomposition par DCT H92
H86
H03
S(t)
Figure 3: Décomposition harmonique par transformation en cosinus d’un signal discret de longueur 1024. Le signal S est en fait la somme des contributions de ses harmoniques de rang 3, 86 et 92.
Dans cet exemple, six nombres (i.e. trois harmoniques caractérisés par leur fréquence et leur amplitude) suffisent pour décrire un signal (en apparence) compliquée. Dans le cas général, les données ne sont pas compressibles de manière aussi triviale. L’exemple suivant montre qu’elles sont néanmoins encodées sous une forme favorable. Ce script ouvre une image en niveaux de gris et au format TIFF puis enregistre une version rehaussée (i.e. visuellement analysable) de son image spectrale (cf. figure 4). Comme les pixels de l’image TIFF sont de type «uint8» (i.e. des entiers non signés et codés sur 8 bits, soit de 0 à 255), l’image doit être convertie en une matrice réelle avant de pouvoir être utilisée par les fonctions MATLAB traditionnelles. %MAGNITUDE enregistre l'image spectrale d'une image TIFF en niveaux de gris. [theName, thePath] = uigetfile('*.tiff'); if isequal(theName, thePath, 0), return, end; theFullName = fullfile(thePath, theName);
% Sélection de l'image. % Annulation. % Chemin complet.
theImage = double(imread(theFullName)); [theNbR, theNbC] = size(theImage); theS = theNbR*theNbC;
% Conversion uint8 -> double. % Taille de l'image. % Surface de l'image.
theMag = abs(dct2(theImage)); theSpc = sqrt(theMag); theMax = max(max(theSpc)); theSpc = 255*theSpc/theMax; theSpc = theSpc * sqrt(theS)/20 theSpc(find(theSpc > 255)) = 255;
% % % % % %
theFullName = fullfile(thePath, 'mag.tiff'); imwrite(uint8(g_Mag), theFullName, 'tiff');
% Chemin complet. % Conversion double -> uint8.
14
Spectre en amplitude. Compression de l'échelle de dynamique du signal. Etirement du contraste de manière ad hoc et avec seuillage sur [0 255].
Figure 4: Transformée en cosinus d’une image en niveaux de gris (Source: NASA mission sts-97). Le coin supérieur gauche de l’image spectrale renferme l’essentiel de l’énergie de l’image.
La figure 4 montre que l’énergie du signal (i.e. le carré de l’amplitude) se concentre principalement dans le coin supérieur gauche de l’image spectrale et décroît très rapidement en fonction de la fréquence. De plus comme les basses fréquences correspondent aux parties uniformes de l’image, on peut espérer pouvoir compresser l’image avec un filtre passe-bas (i.e. qui coupe les hautes fréquences) bidimensionnel simplifié tout en conservant un résultat visuellement acceptable. C’est l’objectif de l’application «ImgDCT» présentée en figure 5.
Figure 5: Compression d’image par filtrage fréquentiel simpl(ist)e. Avec une image de taille 256x256 et une fréquence de coupure de rang 57, le taux de compression est de (57/256)^2 = 1/20.
15
Figure 6: Le designer de GUI fonctionne comme un logiciel de PAO (s.s. Publication Assistée par Ordinateur).
La commande «guide» lance l’interface du designer de GUI. Par défaut, elle s’ouvre sur une figure MATLAB (i.e. une fenêtre vue) vierge qui apparaît sous la forme d’un canevas quadrillé. On peut déjà enregistrer cette figure sous le nom qui servira à lancer l’application depuis la console. MATLAB génère alors un fichier M et un fichier FIG du même nom. Le fichier M contient le code source nécessaire au fonctionnement de l’interface ainsi que de nombreux commentaires explicatifs invitant le programmeur à compléter les fonctions réflexes (e.q. callback functions) des contrôles (i.e. les composants de l’interface qui réagissent aux actions de l’utilisateur). De son côté, le fichier FIG contient les ressources graphiques de l’interface. Ce format binaire est éditable par le designer en lançant la commande «guide» avec pour argument le nom du fichier. La palette d’outils permet de placer des contrôles sur le canevas. La figure «ImgDCT» héberge quatre axes, un cadre (e.q. frame), deux boutons, une zone de texte non éditable, et une barre de défilement (e.q. slider). La fenêtre flottante intitulée «Property Inspector» s’obtient en doublecliquant sur le contrôle désiré ou via son menu contextuel. Les propriétés propres aux contrôles sont décrites dans l’aide interactive à la page «MATLAB: Handle Graphics Property Browser». En pratique, seule une poignée de champs nécessite l’intervention du programmeur. C’est ainsi que le champ «Callback» est remplis automatiquement par l’IDE. Ce champ définit le nom de la sousfonction du fichier M qui est appelée lorsque que le contrôle est sollicité par l’utilisateur. Pour définir précisément l’emplacement et la taille d’un contrôle il est conseillé de régler son champ «Units» sur «pixels » avant de modifier son vecteur «Position ». Il faut noter que l’origine du système de coordonnée est située dans le coin inférieur gauche de l’objet parent (i.e. l’écran s’il s’agit d’une figure MATLAB) et que l’axe des «y» est orienté vers le haut. Les outils de la palette flottante intitulée «Align Objects» servent à disposer les objets, relativement les uns par rapport aux autres. Pour finir, le bouton «Run» situé à droite de la barre d’outils permet de contrôler l’apparence de l’interface en lançant directement l’application.
16
Le champ «Tag» doit être unique pour chaque contrôle. En effet, c’est grâce à ce nom symbolique que l’on peut facilement obtenir le handle du contrôle et donc accéder à ses propriétés. Cet exemple montre le mécanisme de feedback (s.s. la boucle de retour) qui met à jour la zone de texte lorsque l’utilisateur règle le taux de compression depuis la barre de défilement. theSliderH = findobj('Tag','SliderRatio'); theValue = get(theSliderH, 'Value'); theString = sprintf('%2.1f %%', theValue); theTextH = findobj('Tag','TextRatio'); set(theTextH, 'String', theString);
% % % % %
Handle de la barre de défilement. Consulte la propriété 'Value'. Formatage de type C/C++. Handle la zone de texte. Modifie la propriété 'String'.
Les champs «Max», «Min» et «SliderStep:x» de la barre de défilement ont été fixés à «100», «0» et «0.001 » ce qui revient à régler l’incrément des valeurs sur «(100-0)*0.001 == 0,1». Les champs «Position:height» et «SliderStep:y» valent «300» et«0.04» ce qui donne une taille de «300*0.04 == 12» pixels à l’ascenseur. Le champ «Value » initialise la position de l’ascenseur sur la valeur «5.0 », en accord avec la chaîne de caractère «'5.0 %'» du champ «String» de la zone de texte. Afin que les propriétés des axes (e.g. leur «Tag») ne soient pas réinitialisées à chaque nouveau tracé, leur champ «NextPlot» a été réglé sur «replacechildren». Pour finir voici un récapitulatif des contrôles et de leurs propriétés qui ont été modifiéesainsi que le code source des sous-fonctions qui ont été complétées ou rajoutées: Tag: figure1 Name: ImgDCT Position: [0 32 720 640]
Tag : frame1 BackgroundColor : gris foncé Position : [0 0 80 640]
Tag: AxesGrayscale NextPlot : replacechildren Position: [123 347 256 256]
Tag: AxesSpectral NextPlot : replacechildren Position: [441 347 256 256]
Tag: AxesCompressed NextPlot : replacechildren Position: [123 23 256 256]
Tag: AxesClipped NextPlot : replacechildren Position: [441 23 256 256]
Tag: PushLoad FontSize : 10.0 Fontweight : bold Position: [8 560 64 32] String: 'LOAD'
Tag: PushStuff FontSize : 10.0 Fontweight : bold Position: [8 150 64 32] String: 'STUFF'
Tag: SliderRatio Max: 100.0 Min: 0.0 Position: [30 210 18 300] SliderStep: [0.001 0.04] Value: 5.0
Tag: TextRatio Position: [8 515 64 16] String: [0.001 0.04]
function PushLoad_Callback(hObject, eventdata, handles) if ImgLoad, ImgClip, ImgStuff, end
function SliderRatio_Callback(hObject, eventdata, handles) ImgClip
function PushStuff_Callback(hObject, eventdata, handles) ImgStuff
17
function outExit = ImgLoad global g_NbR g_NbC global g_DCT g_Mag
% Taille de l’image. % Image spectrale et image spectrale rehaussée.
[theName, thePath] = uigetfile('*.tiff', '__ Open a grayscale TIFF image __'); if isequal(theName, thePath, 0) outExit = 0; return; end; outExit = 1; theFullName = fullfile(thePath, theName); theImage = double(imread(theFullName)); [g_NbR, g_NbC] = size(theImage); theGrayScale = theImage/255; % 0.0 <=> noir, 1.0 <=> blanc. axes(findobj('Tag','AxesGrayscale')); title('Grayscale Image'); image(cat(3, theGrayScale, theGrayScale, theGrayScale)); axis image; axis ij; g_DCT = dct2(theImage); g_Mag = sqrt(abs(g_DCT)); g_Mag = 255*g_Mag/max(max(g_Mag)); g_Mag = g_Mag * sqrt(g_NbR*g_NbC)/20; g_Mag(find(g_Mag > 255)) = 255; axes(findobj('Tag','AxesSpectral')); title('Spectral Image'); image(g_Mag); colormap(jet(256)); axis image; axis ij;
function ImgClip global g_NbR g_NbC global g_Mag global g_MaxR g_MaxC
% Taille de l’image. % Image spectrale rehaussée. % Taille du clip.
theValue = get( findobj('Tag','SliderRatio'), 'Value'); set(findobj('Tag','TextRatio'), 'String', sprintf('%2.1f %%', theValue)); theRatio = 10*sqrt(theValue); g_MaxR = round(g_NbR * theRatio/100); g_MaxC = round(g_NbC * theRatio/100); theCMag = zeros(g_NbR, g_NbC); theCMag(1:g_MaxR, 1:g_MaxC) = g_Mag(1:g_MaxR, 1:g_MaxC); axes(findobj('Tag','AxesClipped')); title('Clipped Spectral Image'); image(theCMag); colormap(jet(256)); axis image; axis ij; cla(findobj('Tag','AxesCompressed'));
function ImgStuff global g_NbR g_NbC global g_DCT global g_MaxR g_MaxC
% Taille de l’image. % Image spectrale. % Taille du clip.
theCDCT = zeros(g_NbR, g_NbC); theCDCT(1:g_MaxR, 1:g_MaxC) = g_DCT(1:g_MaxR, 1:g_MaxC); theIDCT = idct2(theCDCT); theCGray(find(theCGray > 255)) = 255; theCGray(find(theCGray < 0.0)) = 0.0;
% correction numérique. % correction numérique.
theCGrayScale = theIDCT/255; % 0.0 <=> noir, 1.0 <=> blanc. axes(findobj('Tag','AxesCompressed')); title('Compressed Image'); image(cat(3, theCGray, theCGray, theCGray)); axis image; axis ij;
18
Le standard de la compression d’image JPEG utilise un algorithme nettement plus subtil pour sélectionner et coder les fréquences et décompose l’image est en tuiles de taille modérée (e.g. 8x8 ou 16x16). Ainsi le contrôle de la qualité visuelle finale et le rapport qualité/efficacité se révèlent être excellent alors que dans le cas de «ImgDCT», la qualité dépend non seulement du taux de compression mais aussi de la taille de l’image. Et pour ne rien arranger la complexité de l’algorithme (i.e. l'incidence de l'augmentation du nombre de donnée sur la durée d'exécution ou l’occupation mémoire du programme) est de type «O(N^4)». C’est ainsi que le temps de calcul est multiplié par seize chaque fois que la définition de l’image est doublée. Ce qui devient très vite dissuasif. «ImgDCT» n’est pas vraiment une application professionnelle. Pour cela il faudrait commencer par améliorer la robustesse du programme (e.g. en contrôlant le format et la taille de l’image). Néanmoins, cet exemple montre qu’une application MATLAB comportant à la fois du calcul, de l’affichage, et une interface graphique, nécessite finalement très peu de lignes de code !
Conclusion : Cette (courte) introduction se concentre essentiellement sur les principes fondamentaux de la programmation MATLAB et ne présente donc qu’une infime partie des fonctionnalités de la plateforme. Le lecteur qui se serait laissé convaincre par le potentiel de cet outil est donc invité à poursuivre son apprentissage par la lecture de la bibliographie spécialisée, et surtout par l’expérimentation personnelle. Une expérience qui a personnellement été très gratifiante puisque MATLAB m’aide considérablement dans mes travaux de recherche.
19