1
function BlurredMipmapDemo(deviceIndexOrMoviename, gazeRadius, customMipmap)
2
% BlurredMipmapDemo - Show blurring of live video or movies via adaptive mipmapping.
4
% This demo shows how to selectively blur regions of a live video stream
5
% captured from an attached camera, or a live playing movie. The method
6
% employed here uses a GLSL shader to select an individual blur level for
7
% each output pixel. Pixels close to the mouse cursor (which simulates gaze
8
% position, e.g., as aquired by an eyetracker) will be drawn sharp, whereas
9
% pixels at larger radial distance will be drawn increasingly unsharp by
10
% low-pass filtering them.
12
% The method of blurring here is computationally efficient and fast,
13
% because it generates and uses a image resolution pyramid to precompute
14
% different versions of the video image at different resolutions. Then the
15
% shader just looks up color information for each individual output pixel
16
% from the proper "level of detail" in the pyramid of low pass filtered
17
% images. The resolution pyramid is auto-generated in a fast, GPU
18
% accelerated way for each image by use of OpenGL "mip-mapping". This
19
% method uses a fixed low-pass filter for downsampling, usually a box
20
% filter, but the exact type of filter used is system dependent and can
21
% vary across different graphics cards, graphics driver versions or
24
% The demo requires recent graphics hardware and won't work on old graphics
29
% BlurredMipmapDemo([deviceIndexOrMoviename=0][, gazeRadius=25][, customMipmap=0])
31
% 'gazeRadius' Some falloff radius in pixels. Controls how quickly the
32
% resolution degrades with increasing distance from the cursorposition.
33
% Smaller values give a smaller simulated "foveal area".
35
% 'deviceIndexOrMoviename' = Optional: Either the index of the video
36
% capture device, or the full path to a movie file. Defaults to
37
% auto-selected default video source.
39
% 'customMipmap' Optional: If non-zero, use a custom shader for
40
% downsampling during mipmap building, instead of the relatively simple
41
% builtin filter of the GPU.
43
% Press any key to finish the demo.
47
% 28.08.2012 mk Written.
52
% Select maximum display id for output:
53
screenId = max(Screen('Screens'));
55
if nargin < 1 || isempty(deviceIndexOrMoviename)
56
deviceIndexOrMoviename = [];
59
if nargin < 2 || isempty(gazeRadius)
63
if nargin < 3 || isempty(customMipmap)
67
% Is it a movie file name?
68
if ischar(deviceIndexOrMoviename)
69
% Yes: Playback the movie.
70
moviename = deviceIndexOrMoviename;
73
% No: Nothing or a numeric video capture deviceindex. Do videocapture.
78
% Use automatic mipmap generation by GPU driver builtin method:
81
% Don't generate mipmaps automatically, roll our own downsampling:
83
% This is the correct flag for 'GetMovieImage':
86
% This is the correct flag for 'GetCapturedImage':
92
% Open onscreen window with black background color:
93
win = Screen('OpenWindow', screenId, 0);
94
[w, h] = Screen('WindowSize', win); %#ok<*ASGLU>
96
% Load & Create a GLSL shader for adaptive mipmap lookup:
97
shaderpath = fileparts(mfilename('fullpath'));
98
shader = LoadGLSLProgramFromFiles([shaderpath filesep 'BlurredMipmapDemoShader'], 1);
100
% Bind texture unit 0 to shader as input source for the mip-mapped video image:
101
glUseProgram(shader);
102
glUniform1i(glGetUniformLocation(shader, 'Image'), 0);
105
% Load & Create a GLSL shader for downsampling during MipMap pyramid
106
% creation. This specific example shader uses a 3-by-3 gaussian filter
107
% kernel with a standard deviation of 1.0:
108
mipmapshader = LoadGLSLProgramFromFiles([shaderpath filesep 'MipMapDownsamplingShader.frag.txt'], 1);
111
% Open movie and start its playback:
112
movie = Screen('OpenMovie', win, moviename);
113
Screen('PlayMovie', movie, 1, 1, 1);
115
% Open video capture device 'deviceIndex':
116
grabber = Screen('OpenVideoCapture', win, deviceIndexOrMoviename);
118
% Start video capture:
119
Screen('StartVideoCapture', grabber, realmax, 1);
122
% Change cursor to a cross-hair:
123
ShowCursor('CrossHair');
129
% Repeat until keypress or timeout of 10 minutes:
130
while ((GetSecs - t) < 600) && ~KbCheck
131
% Wait for next video image or movie frame. The first '1' asks to
132
% wait blocking for the next image, instead of polling. 'tex' is
133
% returned for recycling to increase efficiency. The specialMode
134
% flag '1' asks Screen() to return the texture as a GL_TEXTURE_2D
135
% texture. This is needed for the automatic mip-mapping for
136
% low-pass filtering to work:
138
% Get next movie frame:
139
tex = Screen('GetMovieImage', win, movie, 1, [], 1 + dontautomip);
141
% Get next video frame:
142
tex = Screen('GetCapturedImage', win, grabber, 1, tex, 1 + dontautomip);
145
% Valid 'tex'ture with new videoframe returned?
147
% Yes. Get current "center of gaze" as simulated by the current
148
% mouse cursor position:
149
[gazeX, gazeY] = GetMouse(win);
151
% Flip y-axis direction -- Shader has origin bottom-left, not
152
% top-left as GetMouse():
155
% Need to create mipmaps ourselves?
157
% Yes: Use PTB's utility function for shader-based mipmap
158
% generation with more control, but less performance. We
159
% use 'mipmapshader' as downsampling shader, and '1' enable
160
% bilinear filtering for the shader:
161
CreateResolutionPyramid(tex, mipmapshader, 1);
164
% Draw new video texture from framegrabber. Apply GLSL 'shader'
165
% during drawing. Use filterMode 3: This will automatically
166
% generate the mipmap image resolution pyramid, then use the
167
% 'shader' to adaptively lookup lowpass filtered pixels from
168
% the different blur-levels of the image pyramid to simulate
169
% typical foveation effect of decreasing resolution with
170
% increasing distance to center of fixation. We pass gaze
171
% center (gazeX, gazeY) and radius of foveation 'gazeRadius' as
172
% auxParameters. auxParameters must always have a multiple of 4
173
% components, so we add a zero value for padding to length 4:
174
Screen('DrawTexture', win, tex, [], [], [], 3, [], [], shader, [], [gazeX, gazeY, gazeRadius, 0]);
176
% Need to close texture if movie is played back:
178
Screen('Close', tex);
181
% Show it at next video refresh:
185
% Increase frame counter:
189
% Display some timing stats:
190
telapsed = GetSecs - t;
191
avgfps = count / telapsed;
192
fprintf('Average frames per second: %f.\n', avgfps);
195
% Stop and close movie:
196
Screen('PlayMovie', movie, 0);
197
Screen('CloseMovie', movie);
199
% Stop and close video source:
200
Screen('StopVideoCapture', grabber);
201
Screen('CloseVideoCapture', grabber);
204
% Close down everything else:
208
% Error handling, emergency shutdown:
210
psychrethrow(psychlasterror);