background image

Chapter 4 - 1 - 4/18/2007 

Chapter 4 - Beginning the first experiment 

 
In this chapter we are actually going to begin writing real code for a real experiment. We 
are going to do a version of a very cool illusion first demonstrated by Mike Webster 
(Webster, Kaping, Mizokami, and Duhamel, 2004, Adaptation to natural facial 
categories. 
Nature 428, 558-561.) 
 
The illusion works like this – after staring at a series of male faces for a long time, a face 
that previously appeared gender neutral will look female. Similar aftereffects are very 
famous in other domains (look on the web for color adaptation effects and the waterfall 
illusion) but it’s still pretty surprising to see such strong adaptation for human faces.  
 
What we are going to do is write a program where subjects adapt for 2 minutes to a set of 
male or female images, and then carry out a series of trials where they respond whether a 
morph image (morphed between male and female) is perceived as being male or female. 
Between each trial will be a 5 second adaptation top-up period. The goal is to measure 
how adaptation to male or female images influences your perception of the morphed 
images.  
 
You should have a website where you can collect the necessary images. Make a new 
folder in your MatlabClass/ folder called ‘Face Experiment’. Then download the images 
into that folder. You should have a folder called ‘male’, a folder called ‘female’ and a 
folder called ‘morph’. In the male and female folders you should have 10 images: 
male01, male02 … etc. In the morph folder you should have morphs going from morph00 
to morph49.   
 
So let’s think about the structure of the program. You can think of it having 5 parts, as 
shown below.  
 

Decide if adapting to m or f

Pre-adapt for X seconds 
to series of m or f images

Present test morph image
& get response

Top-up adaptation for X 
seconds to series of m or 
f images

Repeat X times

Analyze Data

Decide if adapting to m or f

Pre-adapt for X seconds 
to series of m or f images

Present test morph image
& get response

Top-up adaptation for X 
seconds to series of m or 
f images

Repeat X times

Analyze Data

 

  
 

background image

Chapter 4 - 2 - 4/18/2007 

(I’ve put the Analyze Data part in gray since we’re not going to worry about that right 
now.) 
 
Each sub-part of this program can also be diagrammed.  
 

Pre-adapt for X seconds to series of m or f images

Load new m or f image

Display image for X seconds

Repeat X times

Number of repeats =  total pre-adapt time

display time per image

Pre-adapt for X seconds to series of m or f images

Load new m or f image

Display image for X seconds

Repeat X times

Number of repeats =  total pre-adapt time

display time per image

 

 
 
Whenever you create a program with multiple parts you should make the effort to 
diagram it in this way. Trust me – you will end up writing better code and it will save you 
time in the long run. Now create a program in your MatlabClass/Face Experiment folder 
called FaceAdaptation.m. Now away we go.  
 
We are actually going to start by programming each sub-part of the program as a separate 
little chunk. 
 
 










10 
11 
12 
13 
14 
15 
16 
17 
18 
19 

% Face adaptation experiment

 

 

 

% A program where subjects adapt for 1 minute to

 

% a set of male or female images.

 

% then carry out a series of trials where they respond

 

% whether they perceive a morph image (morphed between male and female)

 

% as being male or female.

 

% Between each trial will be a 5 second adaptation

 

% top-up period.

 

% Measures how adaptation to male or female images

 

% influences your perception of morphs.

 

%

 

% written IF Mar 2007

 

 

 

cd(

'C:\WORK\Matlab class & book\MatlabBookOrganization\Spring2007\MatlabClass\Face 

addpath(pwd);

 

addpath(

'morph'

);

 

 

 

% adapting variables

 

background image

Chapter 4 - 3 - 4/18/2007 

20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 

stim.preadapttime=10; 

% pre-adapt time (in seconds)

 

stim.adaptpresentationrate=2; 

% presentation time (secs) for each adaptation image

stim.numadaptimages=10; 

% there are 10 images in each adaptation set of images

 

stim.preadaptstoptimes=stim.adaptpresentationrate:stim.adaptpresentationrate:stim.p

 

 

adapttype=input(

'Do you want to adapt to males (m) or females (f)? ... '

's'

);

 

if

 adapttype == 

'f'

 

    addpath(

'female'

);

 

    adaptstr=

'female'

;

 

elseif

 adapttype == 

'm'

 

    addpath(

'male'

);

 

    adaptstr=

'male'

;

 

else

 

    disp(

'Error 1. Sorry, response about adapt type is not correct'

);

 

end

 

 

 

pause 

% waits for a keypress before going to the next stage of the program

 

%%%%%%%%%%%%%%%%%%%%

 

% Preadapting interval

 

%%%%%%%%%%%%%%%%%%%%

 

adaptcount=1; 

% keeps track of which adaptation image is being presented

 

tic 

% starts a stopwatch

 

for

 a=1:stim.preadapttime/stim.adaptpresentationrate

 

    figure(1)

 

    

% create a string that matches the filename of the image you want to

 

    

% read

 

    

if

 adaptcount<10

 

        imname=[adaptstr, 

'0'

, num2str(adaptcount)];

 

    

else

 

        imname=[adaptstr, num2str(adaptcount)];

 

    

end

 

    img=imread(imname, 

'JPG'

);

 

    image(img);

 

    axis 

off

; axis 

equal

;

 

    colormap(gray(256));

 

    title(

'pre-adapt'

);

 

    drawnow

 

    

while

 toc<stim.preadaptstoptimes(1)

 

        ;

 

    

end

 

    stim.preadaptstoptimes=stim.preadaptstoptimes(2:end);

 

    

% move on to the next image, and keep cycling through the images, so

 

    

% adaptcount goes between 1-10

 

    adaptcount=adaptcount+1;

 

    adaptcount=mod(adaptcount, stim.numadaptimages)+1;

 

end

 

 

 

 

 

pause 

% waits for a keypress before going to the next stage of the program

 

%%%%%%%%%%%%%%%%%%%%

 

% Test image

 

%%%%%%%%%%%%%%%%%%%%

 

% test variables

 

 

 

ntrials=10;

 

stim.testindices=linspace(0,49,8);

 

background image

Chapter 4 - 4 - 4/18/2007 

77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 

stim.numtestimages=length(stim.testindices);

 

beep=sin(1:0.5:100);

 

trialcount=1; 

% keeps track of which test image is being presented

 

if

 stim.testindices(trialcount)<10

 

    imname=[

'morph0'

, num2str(stim.testindices(trialcount))];

 

else

 

    imname=[

'morph'

, num2str(stim.testindices(trialcount))];

 

end

 

img=imread(imname, 

'JPG'

);

 

figure(1)

 

image(img);

 

title(

'test'

)

 

axis 

off

; axis 

equal

;

 

colormap(gray(256)); drawnow; 

 

% get response

 

sound(beep);

 

FlushEvents;

 

response.char=input(

'Press m for male, f for female'

's'

);

 

title([

'response was '

, response.char]);

 

 

 

pause 

% waits for a keypress before going to the next stage of the program

 

 

 

%%%%%%%%%%%%%%%%%%%%

 

% Top Up interval

 

%%%%%%%%%%%%%%%%%%%%

 

stim.topuptime=6; 

% seconds for each top up interval

 

stim.topupstoptimes=stim.adaptpresentationrate:stim.adaptpresentationrate:stim.topu
adaptcount=1;

 

 

 

tic

 

for

 a=1:stim.topuptime/stim.adaptpresentationrate

 

    

if

 adaptcount<10

 

        imname=[adaptstr, 

'0'

, num2str(adaptcount)];

 

    

else

 

        imname=[adaptstr, num2str(adaptcount)];

 

    

end

 

    img=imread(imname, 

'JPG'

);

 

    image(img);

 

    axis 

off

; axis 

equal

;

 

    figure(1)

 

    colormap(gray(256));

 

    title(

'top-up'

)

 

    drawnow

 

    

while

 toc<stim.topupstoptimes(1)

 

        ;

 

    

end

 

    stim.topupstoptimes=stim.topupstoptimes(2:end);

 

    

% move on to the next image, and keep cycling through the images, so

 

    

% adaptcount goes between 1-10

 

    adaptcount=adaptcount+1;

 

    adaptcount=mod(adaptcount, stim.numadaptimages)+1;

 

end

 

 

 

pause 

% waits for a keypress before going to the next stage of the program

 

 

 

 

 

background image

Chapter 4 - 5 - 4/18/2007 

134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 

 

%%%%%%%%%%%%%%%%%%%%%

 

% blank interval

 

%%%%%%%%%%%%%%%%%%%%%%

 

stim.shortpause=2;

 

blankcmap=ones(256, 3);

 

colormap(blankcmap); 

 

title(

'small pause'

);

 

drawnow;

 

tic

 

while

 toc<stim.shortpause

 

    ;

 

end

 

 

 
The pre-adapt section of the experiment 

Line 20-23. Here we have bundled all the parameters we are going to use for the pre-adapt part of the 
experiment into a structure called stim. Structures are like little shopping baskets – they allow you to collect 
a bunch of variables that are related to each other into a little bundle. The reason you want to do this will be 
clearer later. Here we’ve bundled all the stimulus parameters together under the structure 

stim

Line 23. Look at 

stim.preadaptstoptime

. It basically is a list of the times at which we want to 

move to the next adapting image. We want to move onto the next image every 2 seconds. 
Lines 25-34. Here we are checking what response the subject gave. Based on whether the subject 
responded ‘m’ or ‘f’, we are setting the path and the name of the string that will be used to pull up jpg image 
files. Note that if the subject gives a meaningless response we put in an Error. Good error management will 
make your programs much more robust against the random behavior of undergraduate subjects. 
Line 41. 

tic

 starts a stopwatch. We’ll find out how much time has gone by since we started the stopwatch 

using 

toc 

Line 42. We want to adapt for 10 seconds (

stim.preadapttime

), and we want to present a new 

image every 2 seconds (

stim.adaptpresentationrate

). So we will need to present a total of 5 

images (

stim.preadapttime/stim.adaptpresentationrate). 

We’re therefore going 

to go through this loop 5 times, each time presenting a face image.  
Lines 46-51. Here we are creating a string that represents the filename of the image we want to present on 
the screen. We want to present the image with number 

adaptcount

. The first time we run through this 

loop, 

adaptcount

 will equal 1 (see Line 40). 

Line 51. Here we load in the image. The image is saved as a jpg. When loaded in, it becomes a matrix, like 
the ones we have been using. But if you 

whos

 your workspace you will notice something odd about img -

it’s described as a uint8 rather than a double. The reason for this is that jpg often saves images as unsigned 
integers (whole numbers between 0-255) in order to save memory (depending on the particular settings that 
were chosen when the jpg image was saved). When Matlab reads in the jpg file it actually reads from the 
header of the jpg file that the values inside the file are unsigned integers, and it therefore loads the matrix as 
unsigned integers.  

> whos 
  Name             Size                    Bytes  Class 
 
  a                1x1                         8  double array 
  adaptcount       1x1                         8  double array 
  stim.adaptstr         1x6                        12  char array 
  adapttype        1x1                         2  char array 
  img            540x720                  388800  uint8 array 
  imname           1x8                        16  char array 
  stim             1x1                       520  struct array 

 

If you look at img, you will see that the values in img range between 0 and 255. 

>max(img) 
>min(img) 

background image

Chapter 4 - 6 - 4/18/2007 

 

Line 52. Here we use image to look at the image. But wait a moment – when we used image before, to look 
at gratings and Gabors, the values in the matrix varied between 1-256. But here the values range between 
0-255. What’s going on?  
One peculiarity about image is that when you are using 2D matrices for 

image 

the way it indexes into 

colormaps depends on whether the input matrix consists of doubles or uint8. If the matrix consists of 
doubles then image uses 1-256 to reference into the colormap. i.e. 1 refers to first paintpot, 2 refers to 
second paintpot etc. But if you are using unsigned integers (uint8 or uint16) then image actually uses 0-255 
to reference into the colormap, so 0 refers to the first paintpot, 1 refers to the second paintpot and so on. 
When we get to 3D matrices (where the third dimension holds separate information for the red, green and 
blue monitor gun) things get even weirder. 
This is always something to think about when dealing with colormap issues (Psychtoolbox, which we will be 
dealing with later) has similar ambiguities). The main reason for weirdnesses like these is historical – C/C++ 
generally uses 0-based referencing whereas Matlab generally uses 1-based referencing. But sometimes 
Matlab uses routines that were actually written in C or were originally written for C programs (or other 0-
based reference programs). In those cases Matlab moves to a 0-based system. So be wary of this issue. 
Line 56. Normally drawing things in the figure window is a pretty low priority for Matlab. 

drawnow

 tells 

Matlab to prioritize updating the window. 
Line 57-59. Here we use another loop command. 

while

 checks to see if the statement after the 

while

  

(

toc<stim.preadaptstoptimes(1))

 is true. If it is true then it does what’s inside the loop. In 

this case there is just a semi-colon inside the loop, which doesn’t do anything, so the program simply 
repeats the loop (doing nothing) until 

toc

 is greater or equal to 

stim.preadaptstoptimes(1). 

toc 

is a measure of how much time has gone by since 

tic

, which we called on line 41. So this line 

basically pauses the program until 2 seconds go by. 
Line 60. Here we remove the stim.preadaptstoptime that has just passed, so we have a list of the remaining 
times on the clock   
Line 63. Here we add 1 onto adaptcount, so the next time we run through the loop we will present adapt 
image number 2, and then 3 and so on. Of course eventually we are going to want to present adapting 
images in a random order, but we’ll worry about that later. 
Line 64. Of course we only have 10 different adapting images. So if adapt count goes above 10 the program 
will try to read a jpg image that doesn’t exist. Here we use the command 

mod

. We find the remainder after 

dividing 

adaptcount

 by the number of adapting images. So the remainder will vary between 0-9. We 

then add 1 to that and 

adaptcount

 will now vary between 1-10, as do our jpg images.  

 
 

The test section of the experiment 

Line 76. The labels for the morph jpg images range from 0-49. We don’t want to test every single image (if 
you look at them, each image is barely distinguishable from each other) so we are only going to test 8 

images evenly spaced between 0-49

.  

Line 78. Here we are creating the sound wave for a beep. You can plot beep to see what it looks like 

>plot(beep).

 Because it is a simple sine wave it should sound like a pure tone. 

 

Line 79.  

trialcount

 is going to be used to update which test image is presented. At the moment we 

are only going to present a single image, but that will change. 
Line 80-90. Read in the morph jpg images and put them up in the figure window. 
Line 92. Then we play our 

beep

Line 93.  We are going to collect a key-press (an event). Before we do that we want to make sure that no 
events are stored in the computer memory, using 

FlushEvents

 

Line 94. We then collect the subjects’ response using 

input

 –which basically pauses the program, waits 

for the subject to respond with a keypress, and saves that response as 

response.char 

 

The top-up section of the experiment 

This is almost identical to the pre-adapt part of the experiment and you should be able to work it out for 
yourself. 
 

The blank interval section of the experiment 

Line 137. Define how long the blank interval will last  
Line 138-139. Here we create a colormap that is all ones and use it to blank out the screen.  

background image

Chapter 4 - 7 - 4/18/2007 

Lines 143-144. Again we use the 

while

 command to pause the program until 

toc

 is 2 seconds 

(

stim.shortpause

) later than 

tic

 
 
 

What’s left? 

 
There are only a few things left to do to make this into a proper experiment: 
 
1. At the moment the adaptation faces during the pre-adaptation period and the top-up 
adaptation period are always presented in the same order instead of in random order 
 
2. We are always presenting the same test face instead of all 8 possible test faces in 
random order 
 
3. We need to retain a list of the key-press for every single trial 
 
4. We need to retain a list of which test face was presented on each trial 
 
5. We don’t want to present data in the figure window – we want a proper display screen. 
 
6. We want to save the data 
 
But before we do this we are going to learn about functions