%The following code creates a spatiotemporal 3d Gabor filter that prefers a given speed and direction of motion 

%created by: Nicolai Petkov and Easwar Subramanian
%Department of Mathematics and Computer Science
%University of Groningen
%The Netherlands

function result = GaborKernel3d(v,theta,xi,opt,bandwidth)

%Input : v     = preferred speed of the filter (in pixels per frame)
%	 theta = preferred direction of motion, The convention is that 0 is rightward motion with 
%		pi/2 and pi radians denoting upward and leftward motion.
%	 xi    = phase of the filter (symmteric or anti-symmteric)
%  	 opt   = If 0 the spatial Gaussian Envelope is stationary else the spatial Gaussian Envelope moves with the
%		 same speed as the harmonic (cosine) factor
%        bandwidth = bandwidth of the filter
	 

%output result : A three dimensional matrix that defines a normalized 3D Spatiotemporal Gabor filter

%usage : result = GaborKernel3d(1,0,0,1,1);

%setting of constants
%For the interpretation of the parameters, please refer to the relevant webpages 

%spatiotemporal period
lambda0 = 2;

%aspect ratio 
gamma = 0.5;


%mean of the temporal Gaussian
t0 = 1.75;

%standard deviation of the temporal Gaussian
tau = 2.75;

%calculate the spatial wavelength using the following relation
lambda = lambda0*sqrt(1+v^2);

%calculate the size 'sigma' of the receptive field 
slratio = (1/pi) * sqrt( (log(2)/2) ) * ( (2^bandwidth + 1) / (2^bandwidth - 1) );
sigma = slratio * lambda;

%determine the matrix size
n = ceil(7*sigma)

%do meshgrid to create the three dimensional matrix
[x,y,t] = meshgrid(-n:n+1,-n:n+1,-n:n+1);

%theta is set to pi - theta for conventional purposes (for ex, a value of 0 would mean rightward motion)
theta = pi - theta; 

%rotation matrix
xt = x * cos(theta) + y * sin(theta);
yt = -x * sin(theta) + y * cos(theta);
tt = t;

%Fix vf depending on whether the kernel has a stationary envelope or moving envelope
if(opt == 0)
    vf = 0;
else 
    vf = v;
end

%precompute few constants

%components of the cosine factor
xp = xt + v*t;
f1 = 2*pi/lambda;  
 
b = 1 / (2*sigma*sigma);  
tau2 = 1/(2*tau*tau);
gamma2 = gamma*gamma;

a = gamma / (2*sigma*sigma*pi);  %normalization for spatial gaussian
t1 = 1/(sqrt(2*pi)*tau);        %normalization for temporal gaussian 

%compute the kernel itself
result = (a*exp(-b*( (xt + vf*(tt)).*(xt + vf*(tt)) +gamma2* (yt.*yt)))).*(t1*(exp(-tau2*((tt - t0).*(tt-t0))))) .* cos(f1*xp +  xi); 

%For causality we do the following
result(:,:,1:n) = 0;

%The following part concerns normalization of the kernel. It is primarily done so that the filter does not respond for 
%constant input. The goal is make sure the integral of the kernel (sum of positive and negative values is zero)

% NORMALIZATION of positive and negative values to ensure that the integral of the kernel is 0.

ppos = find(result > 0); %pointer list to indices of elements of result which are positive
pneg = find(result < 0); %pointer list to indices of elements of result which are negative 

pos =     sum(result(ppos));  % sum of the positive elements of result
neg = abs(sum(result(pneg))); % abs value of sum of the negative elements of result
meansum = (pos+neg)/2;
if (meansum > 0) 
   pos = pos / meansum; % normalization coefficient for negative values of result
   neg = neg / meansum; % normalization coefficient for psoitive values of result
end

result(pneg) = pos*result(pneg);
result(ppos) = neg*result(ppos);

%The kernel can be viewed with the help of functions such as imagesc in matlab. One can
%view only one frame at a time. If the kernel is of size m*m*m, because of causality the first m/2 - 1 frames are set to zero. 
%Therefore, meaningful viewing is possible only from m/2 th frame. Further, one has to find the maximum value 
%and the minimum value of the kernel (say, maxker and minker, respectively) and add them as options in imagesc function in %order to see the change of intensity of the excitatory and inhibitory lobes of the kernel over different frames. 
%For example if the kernel is of size 16 * 16 * 16, one could use imagesc(result(:,:,9),[minker,maxker]); to view 9th %frame. 


