Initiation Matlab 3 : introduction `a la Psychtoolbox

Transcription

Initiation Matlab 3 : introduction `a la Psychtoolbox
SCALab
Initiation Matlab 3
Laurent Ott
Initiation Matlab 3 : introduction à la Psychtoolbox
La Psychtoolbox est un ensemble de fonctions et d’exécutables Matlab (fichier .mex) écrits en C/C++
dédiés à la réalisation d’expériences en psychologie. Cette boı̂te à outil n’est pas développée par The Mathworks
(l’éditeur de Matlab) mais par des chercheurs en psychologie. Elle est disponible à l’adresse suivante
http://psychtoolbox.org/PsychtoolboxDownload.
Elle offre une interface de contrôle aisée de la couche graphique et permet la maı̂trise des aspects temporels
quant à la mise à jour de l’affichage. De même, elle facilite la collecte précise des réponses du sujet par le biais
de différents matériels (clavier, souris, eye tracker, ...).
De nombreux scripts de démonstration sont fournis avec la toolbox. Ces scripts sont accessibles dans le
répertoire PsychDemos situé dans le répertoire d’installation de la Psychtoolbox. Pour y accéder sous Matlab :
>> cd([PsychtoolboxRoot,’/PsychDemos’])
Ces scripts de démonstration sont une bonne base de départ pour la mise en place d’expériences personnalisées. Ils montrent la mise en oeuvre des fonctionnalités de la Psychtoolbox.
La psychtoolbox n’intègre pas dans l’environnement Matlab une documentation aussi facilement accessible
et fournie que pour les fonctions Matlab de base. Si une connexion internet est disponible à l’utilisateur, il est
conseillé d’utiliser l’aide en ligne à l’adresse suivante :
→ http ://docs.psychtoolbox.org/Psychtoolbox
Cependant, si aucune connexion internet n’est disponible, il est possible d’obtenir de l’aide pour les fonctions
de la Psychtoolbox en ligne de commande (par ex : >>help Screen pour une description générale de la fonction
Screen, >>Screen(’Flip?’) pour une aide détaillée de l’option ’Flip’).
1
La fonction Screen
La fonction Screen est à la base de la Psychtoolbox. C’est la fonction qui permet la gestion de l’affichage
des stimuli. Dans cette introduction, nous passerons en revue quelques options de cette fonction.
la syntaxe de cette fonction est toujours de la forme :
Screen(’fonction’,pointeur_fenetre,options obligatoires et facultatives)
1.1
Ouverture / Fermeture d’un ’window’
Exécuter le script first screen.m décrit ci-dessous.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% first_screen.m
clear all
close all
%initialise some variables that will be used to call ’OpenWindow’
whichScreen = 0; %allow to choose the display if there’s more than one
wPtr = Screen( ’OpenWindow’, whichScreen);
%Open a window and
%returns a pointer to the window
white = 255; %pixel value for white
black = 0; %pixel value for black
1
SCALab
Initiation Matlab 3
Laurent Ott
gray = (white+black) / 2; %pixel value for middle gray
Screen(’FillRect’, wPtr , gray);
%fill the buffer image in the
%graphic card memory with grey
Screen(’Flip’, wPtr );
%displays the content of the buffer
%in the window
WaitSecs(1); %pause for 1 sec
Screen(’FillRect’, wPtr , white);
Screen(’Flip’, wPtr );
WaitSecs(1);
Screen(’FillRect’, wPtr , black);
Screen(’Flip’, wPtr );
WaitSecs(1);
Screen(’CloseAll’); %closes the window
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
On fait appel en début de script à la fonction ’OpenWindow’, cette fonction permet à la Psychtoolbox de
prendre la main sur la gestion d’un écran. Pour que le système d’exploitation reprenne la main sur l’affichage,
il faudra faire appel à la fonction Screen(’CloseAll’), comme dans l’exemple en fin de script.
1.2
Reprendre la main en cas de blocage
Il peut arriver lors de la phase d’écriture/test d’un programme de perdre la main alors que la Psychtoolbox
est en cours d’utilisation et recouvre la barre des tâches et la fenêtre de commandes de Matlab. Ceci peut se
produire si votre programme s’arrête (certainement à cause d’une erreur dans votre programme) avant de fermer
le window avec Screen(’CloseAll’). Le clavier semblera ne plus fonctionner car il sera vu uniquement par la
fonction Screen et non plus par Matlab.
Pour reprendre la main, appuyez simultanément sur Ctrl+C. Ceci aura pour effet de stopper votre programme si ce n’est pas déjà le cas. Utiliser ensuite la combinaison Alt+TAB pour ramener la fenêtre de commande Matlab au premier plan. Ceci devrait permettre l’utilisation du clavier. Tapez ensuite sca puis appuyer
sur la touche ENTER. sca est un raccourci vers la fonction Screen(’CloseAll’). La fenêtre de la Psychtoolbox
devrait alors se fermer.
Faites le test avec le script suivant.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% test_erreur.m
clear all
close all
whichScreen = 0;
wPtr = Screen( ’OpenWindow’, whichScreen );
a = b %cette instruction va générer une erreur
%car b n’a pas été défini
%L’instruction suivante ne sera jamais exécutée
Screen(’CloseAll’); %fermeture du window
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Pour se prémunir de ces blocages pendant la phase d’écriture et de test d’un programme avec la psychtoolbox.
Il est possible d’utiliser les instructions try, catch de la manière suivante.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% test_try_catch.m
clear all
close all
try
whichScreen = 0;
2
SCALab
Initiation Matlab 3
Laurent Ott
wPtr = Screen( ’OpenWindow’, whichScreen );
a = b %this statement yields an error
%indeed, b is undefined
Screen(’CloseAll’); %close the window
catch ERR
%if an error occurs between the try / catch statements
%the error informations are stored in the structure ERR
%(ERR is an arbritary variable name), and the following code
%is then executed
Screen(’CloseAll’);
rethrow( ERR ); %now that the window is closed,
%we propagate the error so that
%the message error is nicely displayed
%at the prompt
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Si une erreur se produit entre les instructions try et catch, plutôt que d’arrêter immédiatement l’exécution
du script, Matlab va exécuter les instructions situées entre le catch et le end suivant.
Nous pouvons donc nous assurer de l’exécution du Screen(’CloseAll’) pour récupérer la main sur le
clavier et la command window. L’instruction rethrow( ERR ) va propager l’erreur ayant eu lieu entre le try
et catch, nous retrouverons donc la trace de cette éventuelle erreur sur la command window, ce qui facilitera
son débuggage.
1.3
Stimuli à partir de primitives visuelles simples
Screen(’DrawLine’, windowPtr [,color], fromH, fromV, toH, toV [,penWidth]);
Screen(’DrawArc’,windowPtr,[color],[rect],startAngle,arcAngle)
Screen(’FrameArc’,windowPtr,[color],[rect],startAngle,arcAngle[,penWidth] [,penHeight] [,penMode])
Screen(’FillArc’,windowPtr,[color],[rect],startAngle,arcAngle)
Screen(’FillRect’, windowPtr [,color] [,rect] );
Screen(’FrameRect’, windowPtr [,color] [,rect] [,penWidth]);
Screen(’FillOval’, windowPtr [,color] [,rect] [,perfectUpToMaxDiameter]);
Screen(’FrameOval’, windowPtr [,color] [,rect] [,penWidth] [,penHeight] [,penMode]);
Screen(’FramePoly’, windowPtr [,color], pointList [,penWidth]);
Screen(’FillPoly’, windowPtr [,color], pointList [, isConvex]);
Voici la liste des options à la fonction Screen pour l’affichage de primitves simples. Pour rappel, vous pouvez
accéder à l’aide de chacune de ces options depuis la ligne de commande (par ex : Screen(’DrawLine ?’).
L’unité de base de l’écran est le pixel. Un point sur l’écran est définie par ses coordonnées (x,y) en pixels
sur un repère à 2 dimensions. L’origine (0,0) de ce repère se trouve dans le coin en haut à gauche de l’écran,
l’axe horizontal X est dirigé vers la droite et l’axe vertical Y vers le bas (voir Fig. 1-(a) .
(a)
(b)
(c)
(d)
Figure 1 – (a) système de coordonnée de écran. (b) un point (2 coordonnées - horizontal, vertical). (c) une
ligne (2 points : début, fin). (d) un rectangle (2 points : coin supérieur gauche, coin inférieur droit)
Le paramètre rect, utile pour la majorité des primitives visuelles, permet de définir un rectangle sur l’écran
dans lequel on veut placer notre primitive. C’est un vecteur ligne de 4 éléments. Les 1er et 2ème éléments
3
SCALab
Initiation Matlab 3
Laurent Ott
sont les coordonnées du coin supérieur gauche. Les 3ème et 4ème les coordonnées du coin inférieur droit. Par
exemple, le rectangle de la Figure 1-(d) serait rect_1d = [2,3,7,7].
Le paramètre color permet de spécifier la couleur souhaitée de la primitive visuelle. On la définie par
un vecteur ligne des 3 composantes RGB (pour Red Green Blue) avec R,G,B codés entre 0 et 255 (par ex
bleu = [0,0,255]) . On trouvera facilement sur internet les correspondances entre couleur et composantes RGB
(recherche google ’RGB color picker’). Il existe un cas spécial pour définir les couleurs en niveau de gris. Dans
ce cas, il suffit de définir la couleur comme un entier compris entre 0 (du noir) et 255 (au blanc).
Préparation d’un stimulus complexe et raffraichissement de l’écran : L’appel au fonction ci-dessous
pour dessiner des primitves ne met pas à jour l’affichage présent à l’écran. Ces fonction vont modifier une image
présente dans la mémoire de la carte graphique. On peut ainsi combiner plusieurs primitves avant d’en afficher
le résultat à l’écran. Pour mettre à jour l’écran avec l’image présente dans la mémoire de la carte graphique, on
fait appel à la fonction Screen(’Flip’, wPtr);.
Exercice : Créer un script ouvrant un window pour y afficher sur fond blanc les primitives suivantes :
— un rectangle plein de couleur jaune avec son coin supérieur gauche en (30,50) et son coin inférieur
droit en (130,150)
— un cercle de couleur verte placé au milieu de l’écran de diamètre 100 pixels. La taille de l’écran en
pixels peut être obtenue à l’aide de la 2ème valeur de retour de la fonction Screen(’OpenWindow’,...)
(pour voir l’aide >>Screen(’OpenWindow?’))
— une ligne reliant le centre du rectangle au centre du cercle
Laisser s’écouler 2 secondes avant de fermer le window. Puis réafficher les même primitives mais sur fond
noir cette fois-ci.
1.4
Affichage d’images (MakeTexture et DrawTexture)
La fonction Screen(’MakeTexture’’) permet de convertir une matrice d’image 2D ou 3D en une texture
OpenGL et retourne un pointeur vers cette texture. Ce pointeur sera utilisé pour mettre à jour le buffer avant
affichage à l’aide de la fonction ’DrawTexture’.
texturePointer=Screen(’MakeTexture’, windowPointer, imageMatrix, ...)
Screen(’DrawTexture’, windowPointer, texturePointer [,sourceRect] [,destinationRect],...)
sourceRect est un paramètre optionel, un rect définissant une partie de l’image à présenter, si il n’est pas
spécifié ou laissé vide ( [] ), par défaut toute l’image est utilisée. destinationRect est un rect pouvant être
utilisé pour spécifier une partie de l’écran où l’on souhaite présenter l’image (par défaut l’image sera présenté
au centre de l’écran sans mise à l’échelle), si le ratio entre la source et la destination n’est pas le même, l’image
sera distordue pour épouser le rect de destination.
La fonction imread issue de la bibliothèque de fonctions de Matlab permet de créer une matrice d’image à
partir d’un fichier image (ex : BMP, JPEG).
Le script d’exemple ci-dessous montre l’utilisation de 4 textures pour afficher des images. Nous utilisons ici
des images se trouvant incluses dans la distribution de la Psychtoolbox dans le sous-répertoire PsychHardware/EyelinkToo
ResearchDemo/AntiSaccade.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ex_texture.m
clear all
close all
basedir = fullfile( PsychtoolboxRoot , ’PsychHardware’ , ’EyelinkToolbox’ , ’EyelinkDemos’ , ’SR-ResearchDemo’ , ’AntiSaccade’ );
imMatrix_V1
imMatrix_V2
imMatrix_R1
imMatrix_R2
=
=
=
=
imread(fullfile(
imread(fullfile(
imread(fullfile(
imread(fullfile(
basedir
basedir
basedir
basedir
,
,
,
,
’img51.jpg’ ));
’img52.jpg’ ));
’img101.jpg’ ));
’img102.jpg’ ));
4
SCALab
Initiation Matlab 3
Laurent Ott
%initialise some variables that will be used to call ’OpenWindow’
whichScreen = 0; %allow to choose the display if there’s more than one
try
wPtr = Screen( ’OpenWindow’, whichScreen);
%Open a window and
%returns a pointer to the window
white = 255; %pixel value for white
black = 0; %pixel value for black
gray = (white+black) / 2; %pixel value for middle gray
%create a openGL texture for each image
imTexture_V1 = Screen(’MakeTexture’,wPtr,imMatrix_V1);
imTexture_V2 = Screen(’MakeTexture’,wPtr,imMatrix_V2);
imTexture_R1 = Screen(’MakeTexture’,wPtr,imMatrix_R1);
imTexture_R2 = Screen(’MakeTexture’,wPtr,imMatrix_R2);
Screen(’DrawTexture’, wPtr , imTexture_V1); %Fill the buffer with the first texture
Screen(’Flip’, wPtr ); %update the display with the buffer content
WaitSecs(1);
Screen(’DrawTexture’, wPtr , imTexture_V2);
Screen(’Flip’, wPtr );
WaitSecs(1);
Screen(’DrawTexture’, wPtr , imTexture_R1);
Screen(’Flip’, wPtr );
WaitSecs(1);
Screen(’DrawTexture’, wPtr , imTexture_R2);
Screen(’Flip’, wPtr );
WaitSecs(1);
Screen(’CloseAll’); %close the window
catch ERR
Screen(’CloseAll’);
rethrow(ERR);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%
Exercice : Compléter le script suivant dont le but est d’afficher 3 images (konijntjes1024x768.jpg, konijntjes1024x768blur.jpg, konijntjes1024x768gray.jpg) sur le même écran comme sur la figure Fig. 2.
La taille de l’écran en pixels peut être obtenue à l’aide de la 2ème valeur de retour de la fonction
Screen(’OpenWindow’,...) (pour voir l’aide >>Screen(’OpenWindow?’)). De même, la taille d’une image
peut être obtenue en utilisant la fonction size() sur la matrice d’image. Par exemple, pour une matrice
d’image IM, la taille horizontale est donnée par size(IM,2) et la taille verticale par size(IM,1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ex_texture_placement.m
clear all
close all
basedir = fullfile( PsychtoolboxRoot , ’PsychDemos’ );
imMatrix_sharp = imread(fullfile( basedir , ’konijntjes1024x768.jpg’ ));
imMatrix_blur = imread(fullfile( basedir , ’konijntjes1024x768blur.jpg’ ));
imMatrix_gray = imread(fullfile( basedir , ’konijntjes1024x768gray.jpg’ ));
%initialise some variables that will be used to call ’OpenWindow’
whichScreen = 0; %allow to choose the display if there’s more than one
try
[wPtr , wRect] = Screen( ’OpenWindow’, whichScreen );
%Open a window and
%returns a pointer to the window
white = 255; %pixel value for white
black = 0; %pixel value for black
gray = (white+black) / 2; %pixel value for middle gray
5
SCALab
Initiation Matlab 3
Laurent Ott
%create a openGL texture for each image
imTexture_sharp = Screen(’MakeTexture’,wPtr,imMatrix_sharp);
imTexture_blur = Screen(’MakeTexture’,wPtr,imMatrix_blur);
imTexture_gray = Screen(’MakeTexture’,wPtr,imMatrix_gray);
%%% ADD YOUR CODE HERE %%%
%wait for a key stroke
while ~KbCheck
end
Screen(’CloseAll’);
catch ERR
Screen(’CloseAll’);
rethrow(ERR);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%
Figure 2 – Placement souhaité des 3 images.
1.5
Gestion précise de la mise à jour de l’affichage
Comme vous avez pu le constater dans les exemples précédents, la mise à jour effective de l’affichage se fait
au moyen de la fonction ’Flip’. Cette fonction peut être appelée avec différentes options et peut renvoyer des
informations importantes quant à la gestion du temps.
[VBLTimestamp StimulusOnsetTime] = Screen(’Flip’, windowPtr [, when] )
l’argument d’entrée optionnel ’when’ permet de spécifier le temps machine auquel on souhaite mettre à jour
l’affichage. La fonction ’Flip’ bloquera l’exécution du programe jusqu’à la mise à jour de l’affichage. La valeur
de retour ’StimulusOnsetTime’ permet d’obtenir une estimation précise du temps auquel a effectivement eu lieu
l’affichage.
Il faut cependant être conscient des limites de l’écran en terme de fréquence de raffraichissment. Un écran
ayant une fréquence de raffraichissement de 100Hz permet d’afficher une image toutes les 10ms. Pour un écran
à 60Hz, on tombe à une image toutes les 16,67ms.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% synchro_flip.m
clear all
close all
duree_stim = 0.5; %variable to specify the stim presentation duration (here 500ms)
basedir = fullfile( PsychtoolboxRoot , ’PsychHardware’ , ’EyelinkToolbox’ , ’EyelinkDemos’ , ’SR-ResearchDemo’ , ’AntiSaccade’ );
imMatrix_V1
imMatrix_V2
imMatrix_R1
imMatrix_R2
=
=
=
=
imread(fullfile(
imread(fullfile(
imread(fullfile(
imread(fullfile(
basedir
basedir
basedir
basedir
,
,
,
,
’img51.jpg’ ));
’img52.jpg’ ));
’img101.jpg’ ));
’img102.jpg’ ));
%initialise some variables that will be used to call ’OpenWindow’
6
SCALab
Initiation Matlab 3
Laurent Ott
whichScreen = 0; %allow to choose the display if there’s more than one
try
wPtr = Screen( ’OpenWindow’, whichScreen);
%Open a window and
%returns a pointer to the window
white = 255; %pixel value for white
black = 0; %pixel value for black
gray = (white+black) / 2; %pixel value for middle gray
%create a openGL texture for each image
imTexture_V1 = Screen(’MakeTexture’,wPtr,imMatrix_V1);
imTexture_V2 = Screen(’MakeTexture’,wPtr,imMatrix_V2);
imTexture_R1 = Screen(’MakeTexture’,wPtr,imMatrix_R1);
imTexture_R2 = Screen(’MakeTexture’,wPtr,imMatrix_R2);
Screen(’FillRect’, wPtr , gray); %Fill the buffer in grey
[VBL , stimOnsetTime] = Screen(’Flip’, wPtr ); %immediately update the display and store the timestamp of
%the effective update in stimOnsetTime
Screen(’DrawTexture’, wPtr , imTexture_V1); %Fill the buffer with the first texture
[VBL , stimOnsetTime] = Screen(’Flip’, wPtr, stimOnsetTime+duree_stim );
%update the display 500 ms after the last ’Flip’
Screen(’DrawTexture’, wPtr , imTexture_V2);
[VBL , stimOnsetTime] = Screen(’Flip’, wPtr , stimOnsetTime+duree_stim );
Screen(’DrawTexture’, wPtr , imTexture_R1);
[VBL , stimOnsetTime] = Screen(’Flip’, wPtr , stimOnsetTime+duree_stim );
Screen(’DrawTexture’, wPtr , imTexture_R2);
Screen(’Flip’, wPtr , stimOnsetTime+duree_stim );
WaitSecs(0.5);
Screen(’CloseAll’); %close the windows
catch ERR
Screen(’CloseAll’);
rethrow(ERR);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%
2
Interaction avec le sujet
Dans la plupart des expériences de psychologie, les réponses du sujet sont obtenues par le biais du clavier
ou de la souris. En plus de la touche ou de la position de la souris, le temps de réponse (temps écoulé entre
l’apparition du stimulus et la réponse du sujet) est bien souvent une variable que les chercheurs en psychologie
souhaitent mesurer de manière précise. Les fonctions d’accès au clavier et à la souris offertes par la Psychtoolbox
permettent d’obtenir une précision temporelle de l’ordre de la milliseconde.
2.1
Evènement clavier
La réponse d’un sujet au clavier est obtenue par le biais de la fonction KbCheck. Cette fonction n’est pas
bloquante (elle ne bloque pas l’exécution du programme en attente d’un évènement au clavier). Elle renvoie
”instantanément” l’état des touches du clavier de la manière suivante :
[keyIsDown, secs, keyCode] = KbCheck
La variable de retour ’KeyIsDown’ est un booléen qui passe à 1 lorsque n’importe quelle touche est enfoncée.
’secs’ renvoie le temps machine de l’appui et ’keyCode’ est un tableau de 256 booléens. Chaque booléen réprésente
l’état d’une touche du clavier.
7
SCALab
Initiation Matlab 3
Laurent Ott
Pour trouver quel booléen du tableau ’keyCode’ correspond à une touche, vous pouvez exécuter l’instruction
suivante ds la Command window en appuyant sur la touche désirée pendant son exécution :
>>for i = 1:10, WaitSecs(0.5); [keyIsDown, secs, keyCode] = KbCheck; find(keyCode), end
Pour observer et attendre un évènement sur la souris ou le clavier, il faut mettre en place une boucle de
scrutation. Par ex, pour attendre un appui quelconque au clavier :
while ~KbCheck
end
Pour attendre un appui sur la touche Ctrl gauche :
[keyIsDown, secs, keyCode] = KbCheck;
while ~keyCode( 38 ) % le élément 38 de keyCode correspond à la touche Ctrl sur mon système mais peut varier
% d’un système à l’autre
[keyIsDown, secs, keyCode] = KbCheck;
end
2.2
Evènement souris
La position de la souris et l’état de ses boutons peuvent être obtenus à l’aide de la fonction :
[x,y,buttons] = GetMouse
Les valeurs de retour ’x’ et ’y’ sont la position du curseur en coordonnées ecran (pixels). ’buttons’ est un
tableau de N booléens où N est le nombre de boutons de la souris. Chaque élément du tableau représente un
bouton de la souris. Un booléen à (1) signifie que le bouton correspond est appuyé et vaut (0) sinon.
Le script suivant donne un exemple d’utilisation des fonctions KbCheck et GetMouse.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% mesure_clavier_souris.m
clear all
close all
try
whichScreen = 0;
wPtr = Screen( ’OpenWindow’, whichScreen );
white = 255; %pixel value for white
black = 0; %pixel value for black
gray = (white+black) / 2; %pixel value for middle gray
nb_trial = 5;
for i = 1:nb_trial
Screen(’FillRect’, wPtr , gray);
DrawFormattedText(wPtr, ...
’Measure of the time difference between \na key stroke and a mouse click\n\nStart by hitting a key’ ...
,’center’ , ’center’, black);
Screen(’Flip’, wPtr );
%wait for a key stroke
while ~KbCheck
end
tkeyboard = GetSecs; %we store the timestamp of the key stroke event
Screen(’FillRect’, wPtr , white);
DrawFormattedText(wPtr, ’Waiting for a mouse click’ ,’center’ , ’center’, black);
Screen(’Flip’, wPtr );
%wait for a mouse click
[x,y,buttons] = GetMouse;
while ~sum(buttons)
[x,y,buttons] = GetMouse;
end
8
SCALab
Initiation Matlab 3
Laurent Ott
tmouse = GetSecs; %we store the timestamp of the mouse event
Screen(’FillRect’, wPtr , black);
DrawFormattedText(wPtr, ’Please relase the keyboard and the mouse’ ,’center’ , ’center’, white);
Screen(’Flip’, wPtr );
%wait for the subject to release the mouse and the keyboard
[x,y,buttons] = GetMouse;
while KbCheck | sum(buttons)
[x,y,buttons] = GetMouse;
end
deltaT(i) = tmouse-tkeyboard;
Screen(’FillRect’, wPtr , gray);
DrawFormattedText(wPtr, [’deltaT = ’,num2str(deltaT(i),’%.3f’), ’ s’] ,’center’ , ’center’, black);
Screen(’Flip’, wPtr );
WaitSecs(0.5);
end
Screen(’CloseAll’); %close the window
%graph the measure
figure,
plot(deltaT,’*’), xlim([0 length(deltaT)+1]), ylim([0 max(deltaT)]);
xlabel(’Trial num’), ylabel(’deltaT (s)’);
catch ERR
Screen(’CloseAll’);
rethrow(ERR);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%
Exercice : Programmer une tâche pour évaluer l’effet de l’illusion de Muller-Lyer
L’illusion de Muller-Lyer est une illusion d’optique. Une de ces manifestations les plus connues est pour
un segment de droite d’apparaı̂tre comme plus ou moins long selon qu’il ait attaché à ses extrémités des
flèches pointant vers l’intérieur ou vers l’extérieur (voir Fig 3).
On peut imaginer la tâche d’évaluation suivante :
- Afficher un segment de droite horizontale de longueur fixe (par exemple 300 px) avec des flêches pointant
vers l’extérieur placé au centre de l’écran.
- Afficher un second segment de droite horizontale de longueur aléatoire (variant par exemple de 260 px à
340 px) avec des flêches pointant vers l’intérieur placé sous la première ligne.
- En utilisant 2 touches du clavier de votre choix pour récupérer les réponses du sujet, demander au sujet
de choisir si la ligne du dessous paraı̂t comme étant plus grande ou plus petite que la ligne du dessus.
Réaliser le programme de cette tâche pour 50 essais en faisant variant la longueur de la ligne du bas de
-40 à +40 pixels par rapport à la longueur de la ligne du dessus. Pour chaque essai, on souhaite garder un
historique des réponses du sujet associés à la différence de taille des lignes.
3
Animations
L’illusion du mouvement est obtenue en changeant rapidement la position ou la forme de ce qui est affiché à
l’écran. Typiquement, la fréquence de raffraı̂chissement d’un écran est de 60 Hz (i.e. 60 fois par seconde, certains
écrans peuvent avoir des fréquences supérieures), nous pouvons donc afficher une nouvelle image ou changer la
position d’un stimulus toutes les 16.67 ms
Plus la distance entre les positions d’un stimulus dans 2 images successives est grande, plus le mouvement
apparent sera important.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% animation1.m
9
SCALab
Initiation Matlab 3
Laurent Ott
Figure 3 – Illustration of the Muller-Lyer illusion. The line in the bottom arrow seems to be longer than the
one from the top arrow.
clear all
close all
try
whichScreen = 0;
[wPtr , wRect] = Screen( ’OpenWindow’, whichScreen );
white = 255; %pixel value for white
black = 0; %pixel value for black
gray = (white+black) / 2; %pixel value for middle gray
red = [255,0,0];
Vx = 5; % velocity on X axis in pixel / frame
Px = wRect(3)/2; %initial position of the circle on X axis
Py = wRect(4)/2; %initial position of the circle on Y axis
R = 50; %radius of the circle
while ~KbCheck
% compute new position
Px=Px+Vx;
% if the circle goes outside the right side of the screen
% we change its position in order to make it appear on
% the left side
if Px-R > wRect(3)
Px = -R;
end
% draw circle in new position
Screen(’FillOval’,wPtr, red ,[Px-R,Py-R, Px+R, Py+R]);
Screen(’Flip’,wPtr);
end
Screen(’CloseAll’);
catch ERR
Screen(’CloseAll’);
rethrow(ERR);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%
10
SCALab
Initiation Matlab 3
Laurent Ott
Exercices :
- Observer l’effet de la valeur de la vitesse Vx en l’augmentant ou en la diminuant. Modifiez la vitesse Vx
pour une valeur negative. Que ce passe-t-il ? Résolvez le bug.
- modifez le code pour ajouter une composante verticale au mouvement.
- Modifiez le code pour contrôler le mouvement du cercle à partir du clavier.
- Créez un script pour que le cercle suive le pointeur de la souris
- Créez un script affichant et mettant à jour un montre analogique simplifiée (voir Fig. 4), utilisez les lignes
de code suivantes pour récupérer l’heure en temps réel
time = now;
hour = str2num( datestr( time , ’HH’ ) );
minute = str2num( datestr( time , ’MM’ ) );
second = str2num( datestr( time , ’SS’ ) );
Figure 4 – Montre analogique simplifiée à 13h58m35s.
4
Programmation d’un clone du jeu Snake/Nibbles
Vous avez sûrement tous déjà eu l’occasion de jouer à une variante du jeu Snake. Ce jeu consiste à piloter les
déplacements d’un serpent pour collecter des objets et gagner ainsi des points. Le serpent se déplace en continu
à vitesse constante, on ne peut que changer sa direction. A chaque objet collecté, le serpent s’agrandit un peu.
On perd la partie dès lors que le serpent sort de l’écran ou qu’il rencontre un obstacle (un mur ou lui même en
se mangeant la queue). Je vous propose de programmer une version simplifiée de ce jeu culte, sans obstacle et
avec des graphismes très limités (voir Fig 5 ).
4.1
Modélisation du jeu
Nous allons subdiviser l’écran en une matrice de blocs décrivant les positions possibles des éléments du jeu
(objet à collecter, la tête du serpent et sa queue). Le taille horizontale et verticale de la matrice de bloc sont
des paramètres que nous fixerons dans le code (par ex Nx = 100 et Ny=75). Comme le montre la Figure 6, nous
pouvons calculer le rectangle d’un bloc situé à la colonne i et à la ligne j dans la matrice de blocs à partir de la
formule suivante.
rectBloc=[ (i-1)*(scrSizeX/Nx) , i*(scrSizeX/Nx) , (j-1)*(scrSizeY/Ny) , j*(scrSizeY/Ny) ];
Exercice : - Ecrivez le script qui ouvre un windows en prenant soin de récupérer la taille de l’écran, puis
sur une matrice de blocs de taille Nx=100, Ny=75 remplie les blocs (i=2,j=2) en noir, (i=50,j=37) en rouge
11
SCALab
Initiation Matlab 3
Laurent Ott
Figure 5 – Version du jeu Snake à programmer.
scrSizeX
1
1
ΔY
2
3
Nx-1
Nx
ΔX
( j−1)Δ Y
=2 Δ Y
j ΔY
=3 Δ Y
2
3
(i−1) Δ X
=1 Δ X
i=2
j=3
i ΔX
=2 Δ X
scrSizeY
Ny-1
Ny
with Δ X =
scrSizeX
scrSizeY
, Δ Y=
NX
NY
Figure 6 – Discretisation de l’espace en blocs. Calcul des rectangles des blocs.
et (i=99,j=74) en bleu
4.1.1
Direction de déplacement du serpent
Le serpent peut suivre 4 directions (haut, droite, bas, gauche). Nous représenterons la direction du serpent
à l’aide d’une variable direction pouvant prendre les valeurs 1,2,3 ou 4 pour représenter respectivement les
directions précédemment mentionnées.
Au début du jeu, on choisira aléatoirement la direction initiale.
direction = randi(4);
Le joueur pourra par la suite modifier la direction à l’aide des flèches du clavier. Voici par exemple comment
on pourrait scruter le clavier pendant 0.5 secondes.
tStart = GetSecs;
while GetSecs - tStart < 0.5
12
SCALab
Initiation Matlab 3
Laurent Ott
[keyIsDown, secs, keyCode] = KbCheck;
if keyCode( UpArrowCode )
direction = 1;
elsif keyCode( RightArrowCode )
direction = 2;
elseif keyCode( DownArrowCode )
direction = 3;
elseif keyCode( LeftArrowCode )
direction = 4;
end
end
Pour utiliser ce bout de code, il vous faut d’abord initialiser les variables *ArrowCode avec les codes correspondant aux flèches du clavier.
4.1.2
Cible
Les position des objets et du serpent peuvent à présent simplement être décrit par les positions i,j des blocs
associés. Nous définissons donc une variable target vecteur ligne de 2 éléments pour représenter la position de
la cible. On initalisera la position de la cible aléatoirement et lorsque la cible sera mangé par le serpent on la
replacera à nouveau aléatoirement à une nouvelle position.
target = [ randi(Nx) , randi(Ny) ];
4.1.3
Serpent
Le serpent a quant à lui une longueur variable et peut occuper plusieurs blocs. Il sera représenté par une
matrice snake de taille (Nsx2) où Ns est la longueur du serpent. Chaque ligne de snake contient les coordonnées
i,j d’un bloc constituant le serpent. Au début du jeu Ns=1, et augmentera à chaque objet collecté. On fera en
sorte de garder la tête du serpent à la première ligne de snake
On initialisera au début du jeu le serpent au centre de l’écran.
snake = [ round(Nx/2) , round(Ny/2) ];
Pour faire se déplacer le serpent, il faudra dans un premier temps agrandir la matrice snake en rajoutant
la nouvelle position de la tête du serpent en première ligne de la matrice. Pour ce faire, nous utilisons la
concaténation de matrices. Par exemple pour placer la tête du serpent à la gauche de sa position précédente,
cela donne :
snake = [ [ snake(1,1)-1 , snake(1,2) ] ; snake]; %deplacement de la t^
ete vers la gauche
En l’état, le serpent a grandi d’un bloc, si le serpent vient de manger une cible tout est pour le mieux, sinon
il faut retirer le dernier bloc pour simuler la traine du serpent. On peut faire cela en affectant la matrice vide
[] à la dernière ligne de la variable snake comme suit
snake(end,:) = [];
4.2
Boucle du jeu
Contrairement à un programme ou script simple qui effectue son traitement puis s’arrête. Un jeu video
peut fonctionner indéfiniment. Et contrairement à un programme avec une interface graphique qui attend une
intéraction de l’utilisateur pour réaliser l’action demandée, l’environnement du jeu continue souvent d’évoluer
même sans intéraction de l’utilisateur.
Nous allons donc utiliser une boucle while dont on ne sortira que si le joueur souhaite quitter la partie
(par exemple en appuyant sur Echap) ou si il a perdu la partie (le serpent sort de l’écran ou se mord la
queue pour notre jeu). Dans cette boucle, nous allons à chaque itération regarder si le joueur souhaite changer
de direction, calculer les nouvelles positions de nos objets dans l’environnment et mettre jour l’affichage de
l’environnement. Dans notre jeu, le mouvement apparent du serpent est réalisé en décalant periodiquement la
13
SCALab
Initiation Matlab 3
Laurent Ott
position du serpent d’un block dans la direction souhaitée. Comme nous l’avons vu précédemment dans la partie
concernant les animations, la vitesse du serpent dépend de l’intervalle de temps entre 2 positions consecutives.
Nous spécifirons cet intervalle de temps dans la variable updateInterval que nous utiliserons pour limiter la
cadence des itérations de notre boucle de jeu. Ce paramètre permettra également de modifier la difficulté du
jeu.
Description du programme du jeu :
— initialiser les paramètres du jeu (interval de raffraichissement par ex updateInterval=0.1, taille de
la matrice de blocs Nx, Ny)
— Initialiser les variables d’état du jeu (position de la cible, du serpent, direction, score)
— Présenter un écran de bienvenue, attendre du joueur un appui sur une touche pour signaler qu’il est
prêt
— tant que le jeu n’est pas fini (boucle de jeu)
— Afficher la cible et le serpent à leur position courante.
— Scruter l’état du clavier pendant updateInterval, mettre à jour la variable direction si une
flèche du clavier a été enfoncée.
— Mettre à jour la variable snake en fonction de la direction (déplacement d’un bloc), i.e. agrandir
la matrice pour rajouter en première position la nouvelle position de la tête du serpent
— Vérifier si la cible a été mangée.
— Si oui, on calcule une nouvelle position de cible et on incrémente le score
— Si non, on raccourci le serpent en supprimant la dernière ligne de la matrice snake
— Vérifier que le serpent ne rencontre pas d’obstacle à sa nouvelle position. Sinon, le joueur a perdu
— Présenter un écran de fin du jeu
Il ne vous reste plus qu’à implémenter tout ça.
5
Pour aller plus loin
La Psychtoolbox comprend également des abstractions facilitant la présentation de stimuli sonores ainsi
que des vidéos. Nous ne détaillerons pas ici ces aspects. Mais j’invite les curieux à parcourir les scripts de
démonstrations fournies avec la Psychtoolbox.
Pour l’aspect audio, la librairie portaudio est utilisée via la fonction d’abstraction PsychPortAudio(), les
démonstrations intéressantes sont les suivantes :
— BasicSoundOutputDemo - Demonstrate basic usage of PsychPortAudio() for sound playback.
— BasicSoundInputDemo - Demonstrate basic usage of PsychPortAudio() for sound capture.
— SimpleSoundScheduleDemo - Simple demo for basic use of sound schedules with PsychPortAudio.
— SimpleVoiceTriggerDemo - Demo of a simple voice trigger with PsychPortAudio.
— BasicSoundFeedbackDemo - Demonstrates a audio feedback loop via PsychPortAudio(). See DelayedSoundFeedbackDemo for a more research grade approach.
— BasicSoundScheduleDemo - Demonstrate basic usage of sound schedules and buffers with PsychPortAudio().
— DelayedSoundFeedbackDemo - Demonstrates a audio feedback loop via PsychPortAudio() with exactly
controlled latency.
Pour l’aspect lecture de videos, la librairie Gstreamer est utilisée via la fonction d’abstraction Screen(), les
démonstrations intéressantes sont les suivantes :
— SimpleMovieDemo - Most simplistic demo on how to play a movie.
— PlayMoviesDemo - Show simple playback of one movie with sound at a time.
— PlayDualMoviesDemo - Same as PlayMoviesDemo, but play two movies in parallel.
— PlayMoviesWithoutGapDemo2 - Play one movie while opening another one to reduce gaps between
movies.
— DetectionRTInVideoDemo - How to collect reaction times in response to detection of some event in a
presented movie file. Takes care to get timing right.
14
SCALab
Initiation Matlab 3
Laurent Ott
— LoadMovieIntoTexturesDemo - Quickly load a movie into a stack of textures for quick playback with
arbitrary speed and order.
Pour voir le code source des démos, entrez >>edit DemoName à l’invite de commmande.
15

Documents pareils