Reputation: 737
Hi I am looking for some help to optimize some Matlab code. I have an input matrix M
, which is Ny
rows by Nx
columns. I would like to define a boundary all the way round the edge of the matrix where the values should be attenuated smoothly down to zero. To do this, I attempted to define a masking function MSK
and multiply it point by point to get an output N
, shown below:
My attempt above was created using the code below, where I use a cosine squared function to get the smooth drop off from 1 to zero:
%%% Set up some grids %%%
Nx = 128;
Ny = 64;
dx = 0.1;
dy = 0.1;
x = -(Nx*dx/2):dx:(Nx*dx/2-dx);
y = -(Ny*dy/2):dy:(Ny*dy/2-dy);
x = permute(x, [1 2 3]); % Creates a [1 x Nx x 1] vector (saves memory)
y = permute(y, [2 1 3]); % Creates a [Ny x 1 x 1] vector
M = complex( double( rand(Ny,Nx)+10 ) ); % Create some input data
bnd_frac = 0.7; % The fraction of the grid to begin masking boundary, i.e 70% of the grid max
MSK = create_mask_function(x,y,Nx,Ny,bnd_frac); % Create masking function
N = M.*MSK; % Apply mask to input data, to smoothly ramp edges to zero
figure;
subplot(1,3,1);imagesc(x,y, abs(M) )
subplot(1,3,2);imagesc(x,y, abs(MSK) )
subplot(1,3,3);imagesc(x,y, abs(N) )
%%%%%%%%%%%%%%%%%%%%%%%%
%%% Masking Function %%%
%%%%%%%%%%%%%%%%%%%%%%%%
function MSK = create_mask_function(x,y,Nx,Ny,bnd_frac)
[~,bndind_x] = min( abs(x - bnd_frac*x(Nx)) ); % Find the index to begin the masking
x_bound = x(bndind_x); % Find the grid coordinate to begin masking
[~,bndind_y] = min( abs(y - bnd_frac*y(Ny)) );
y_bound = y(bndind_y);
dbnd_xp = x(Nx) - x_bound; % Find the width of the boundary region at each edge
dbnd_xm = x(1) + x_bound;
dbnd_yp = y(Ny) - y_bound;
dbnd_ym = y(1) + y_bound;
MSK = ones(Ny,Nx); % Initialise the mask matrix as ones
% Set the values within each boundary region at the edge to ramp smoothly to 0 from 1
MSK( : , x > x_bound ) = repmat( ( cos( ( x(x>+x_bound) - x_bound )/dbnd_xp * pi/2 ) ).^2 , Ny , 1);
MSK( : , x < -x_bound ) = repmat( ( cos( ( x(x<-x_bound) + x_bound )/dbnd_xm * pi/2 ) ).^2 , Ny , 1);
MSK( y > y_bound , : ) = repmat( ( cos( ( y(y>+y_bound) - y_bound )/dbnd_yp * pi/2 ) ).^2 , 1 , Nx);
MSK( y < -y_bound , : ) = repmat( ( cos( ( y(y<-y_bound) + y_bound )/dbnd_ym * pi/2 ) ).^2 , 1 , Nx);
MSK(:,Nx,:) = 0;
MSK(:,1,:) = 0;
MSK(Ny,:,:) = 0;
MSK(1,:,:) = 0;
end
The problem with the code above is that it allocates too much memory to MSK
.
(In the real version of my code, all of of the matrices are 3D, and the sizes can be more like 1024x512x512 in reality, but I have made a simplifed 2D example here to help explain.)
Note that this is the reason that I am using permute()
on the x
and y
vectors above, because other parts of my full code use Matlab's implicit expansion. This is to avoid storing 3D versions of x and y that have been generated using meshgrid()
.
It is clear in the above image that MSK
contains mostly values of 1, and the ramping values only occur near the edges. So this seems like large waste of memory to store those 1's because their purpose is to just make sure the input matrix is not modified in the centre. Can anyone help me understand a way in Matlab to implement what I want but in a way that will use less RAM?
Upvotes: 3
Views: 100
Reputation: 35525
If you can't store, then you need to recompute the weights each time and use them on the fly.
The short answer then is that instead of some precomputing of MSK
and then usage later, as:
MSK( : , x > x_bound ) = repmat( ( cos( ( x(x>+x_bound) - x_bound )/dbnd_xp * pi/2 ) ).^2 , Ny , 1);
etc()
...
N = M.*MSK
You want to just, on the fly:
N( : , x > x_bound ) = M( : , x > x_bound ).*repmat( ( cos( ( x(x>+x_bound) - x_bound )/dbnd_xp * pi/2 ) ).^2 , Ny , 1);
etc()
I'll leave how you restructure the code to you, but I'd basically just add the matrix M
as input to your function, and rename it to "apply_mask" or something like that. There will likely be ways to optimize this further if needed, but "early optimization is the root of all evil".
Upvotes: 1