teeeeee
teeeeee

Reputation: 737

Ramping values of a matrix smoothly down to zero around its edges

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:

enter image description here

enter image description here

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

Answers (1)

Ander Biguri
Ander Biguri

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

Related Questions