Partial fft of multidimensional array - matlab

I have a 3D array of dimension (NX,NY,NZ) which represents a variable in physical space, let's say velocities, taken from a simulation in a 3D domain.
1) I want to Fourier-transform only the dimensions X and Z, how should I use the built-in function fft in this case? At some point I want to also get back to the physical space, but only on X, so the same question applies.
2) I read that FFTW uses only 2*N/3 points, should I specify NX and NZ as the number of retained modes or fewer?
3) When using the FFTW package, is there any issue with the coefficient in front of the integral defining the Fourier transformation? Does this package assume that my domain is 2pix2pix2pi?

1°) The function for 2D FFT is fft2, and it will by default apply to the two first dimensions of the array. That is, fft2(velocities) will give you a 3D array with NZ Fourier transforms along dimensions X and Y
In order to do the FFT along other dimensions, you have to manually decompose the 2D FFT as two 1D FFTs. fft will work by default along dimension 1 and produce as many samples as there were in the input. fft(X[],n) does the same, but along dimension n.
Thus, you may compute a 2D FFT of your 3D array, along dimensions X and Z with the command:
my_FFT = fft(fft(velocities),[],3);
2°) There will be as many samples out as samples in.
3°) I believe the normalization by the size of the array is fully applied on the reverse transform, and not at all on the direct transform.
fft([1 0 0 0 0 0])
ans =
1 1 1 1 1 1
To maintain normalization, a coefficient sqrt(NX*NZ) should be applied (multiply when doing FFT, divide when doing an IFFT).

Related

Principle of FFT in multidimensional array

How can one explain the principle of fft in a multidimensional array?
Y = fft(X)
If X is a multidimensional array, then fft(X) treats the values along the first array dimension whose size does not equal 1 as vectors and returns the Fourier transform of each vector.
I don't understand it very well.
X=[1 2 ; 3 4];
X(:,:,2)=[5 6 ; 7 8]
Let's take the 2D case for simplicity.
Given a 2D matrix of data, X, one can treat each row vector individually and perform a one-dimensional discrete Fourier transform. This will decompose each row into one dimensional frequency components. No attention is given to whether the rows of the matrix are correlated, and so every row of the output matrix will have some non-zero components.
Alternatively, using fft2, one can decompose X into its constituent two-dimensional frequency components. I find this easiest to think about in analogy with the DCT, where you can visualize the 2D basis easily
With the 2D FFT, correlations between rows would affect the output of the FFT. It's possible a single 2D frequency component (which is more like a surface than a wave) could contain all the energy of the 2D FFT, and so the output matrix could be much sparser than when treating the rows individually.
This analogy can be continued in to n-dimensions, with higher dimensional frequency components potentially capturing the higher dimensional structure in your data.

Generate random samples from arbitrary discrete probability density function in Matlab

I've got an arbitrary probability density function discretized as a matrix in Matlab, that means that for every pair x,y the probability is stored in the matrix:
A(x,y) = probability
This is a 100x100 matrix, and I would like to be able to generate random samples of two dimensions (x,y) out of this matrix and also, if possible, to be able to calculate the mean and other moments of the PDF. I want to do this because after resampling, I want to fit the samples to an approximated Gaussian Mixture Model.
I've been looking everywhere but I haven't found anything as specific as this. I hope you may be able to help me.
Thank you.
If you really have a discrete probably density function defined by A (as opposed to a continuous probability density function that is merely described by A), you can "cheat" by turning your 2D problem into a 1D problem.
%define the possible values for the (x,y) pair
row_vals = [1:size(A,1)]'*ones(1,size(A,2)); %all x values
col_vals = ones(size(A,1),1)*[1:size(A,2)]; %all y values
%convert your 2D problem into a 1D problem
A = A(:);
row_vals = row_vals(:);
col_vals = col_vals(:);
%calculate your fake 1D CDF, assumes sum(A(:))==1
CDF = cumsum(A); %remember, first term out of of cumsum is not zero
%because of the operation we're doing below (interp1 followed by ceil)
%we need the CDF to start at zero
CDF = [0; CDF(:)];
%generate random values
N_vals = 1000; %give me 1000 values
rand_vals = rand(N_vals,1); %spans zero to one
%look into CDF to see which index the rand val corresponds to
out_val = interp1(CDF,[0:1/(length(CDF)-1):1],rand_vals); %spans zero to one
ind = ceil(out_val*length(A));
%using the inds, you can lookup each pair of values
xy_values = [row_vals(ind) col_vals(ind)];
I hope that this helps!
Chip
I don't believe matlab has built-in functionality for generating multivariate random variables with arbitrary distribution. As a matter of fact, the same is true for univariate random numbers. But while the latter can be easily generated based on the cumulative distribution function, the CDF does not exist for multivariate distributions, so generating such numbers is much more messy (the main problem is the fact that 2 or more variables have correlation). So this part of your question is far beyond the scope of this site.
Since half an answer is better than no answer, here's how you can compute the mean and higher moments numerically using matlab:
%generate some dummy input
xv=linspace(-50,50,101);
yv=linspace(-30,30,100);
[x y]=meshgrid(xv,yv);
%define a discretized two-hump Gaussian distribution
A=floor(15*exp(-((x-10).^2+y.^2)/100)+15*exp(-((x+25).^2+y.^2)/100));
A=A/sum(A(:)); %normalized to sum to 1
%plot it if you like
%figure;
%surf(x,y,A)
%actual half-answer starts here
%get normalized pdf
weight=trapz(xv,trapz(yv,A));
A=A/weight; %A normalized to 1 according to trapz^2
%mean
mean_x=trapz(xv,trapz(yv,A.*x));
mean_y=trapz(xv,trapz(yv,A.*y));
So, the point is that you can perform a double integral on a rectangular mesh using two consecutive calls to trapz. This allows you to compute the integral of any quantity that has the same shape as your mesh, but a drawback is that vector components have to be computed independently. If you only wish to compute things which can be parametrized with x and y (which are naturally the same size as you mesh), then you can get along without having to do any additional thinking.
You could also define a function for the integration:
function res=trapz2(xv,yv,A,arg)
if ~isscalar(arg) && any(size(arg)~=size(A))
error('Size of A and var must be the same!')
end
res=trapz(xv,trapz(yv,A.*arg));
end
This way you can compute stuff like
weight=trapz2(xv,yv,A,1);
mean_x=trapz2(xv,yv,A,x);
NOTE: the reason I used a 101x100 mesh in the example is that the double call to trapz should be performed in the proper order. If you interchange xv and yv in the calls, you get the wrong answer due to inconsistency with the definition of A, but this will not be evident if A is square. I suggest avoiding symmetric quantities during the development stage.

Fast computation of joint histogram of two images [duplicate]

I have two black and white images and I need to calculate the mutual information.
Image 1 = X
Image 2 = Y
I know that the mutual information can be defined as:
MI = entropy(X) + entropy(Y) - JointEntropy(X,Y)
MATLAB already has built-in functions to calculate the entropy but not to calculate the joint entropy. I guess the true question is: How do I calculate the joint entropy of two images?
Here is an example of the images I'd like to find the joint entropy of:
X =
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Y =
0 0 0 0 0 0
0 0 0.38 0.82 0.38 0.04
0 0 0.32 0.82 0.68 0.17
0 0 0.04 0.14 0.11 0
0 0 0 0 0 0
To calculate the joint entropy, you need to calculate the joint histogram between two images. The joint histogram is essentially the same as a normal 1D histogram but the first dimension logs intensities for the first image and the second dimension logs intensities for the second image. This is very similar to what is commonly referred to as a co-occurrence matrix. At location (i,j) in the joint histogram, it tells you how many intensity values we have encountered that have intensity i in the first image and intensity j in the second image.
What is important is that this logs how many times we have seen this pair of intensities at the same corresponding locations. For example, if we have a joint histogram count of (7,3) = 2, this means that when we were scanning both images, when we encountered the intensity of 7, at the same corresponding location in the second image, we encountered the intensity of 3 for a total of 2 times.
Constructing a joint histogram is very simple to do.
First, create a 256 x 256 matrix (assuming your image is unsigned 8-bit integer) and initialize them to all zeroes. Also, you need to make sure that both of your images are the same size (width and height).
Once you do that, take a look at the first pixel of each image, which we will denote as the top left corner. Specifically, take a look at the intensities for the first and second image at this location. The intensity of the first image will serve as the row while the intensity of the second image will serve as the column.
Find this location in the matrix and increment this spot in the matrix by 1.
Repeat this for the rest of the locations in your image.
After you're done, divide all entries by the total number of elements in either image (remember they should be the same size). This will give us the joint probability distribution between both images.
One would be inclined to do this with for loops, but as it is commonly known, for loops are notoriously slow and should be avoided if at all possible. However, you can easily do this in MATLAB in the following way without loops. Let's assume that im1 and im2 are the first and second images you want to compare to. What we can do is convert im1 and im2 into vectors. We can then use accumarray to help us compute the joint histogram. accumarray is one of the most powerful functions in MATLAB. You can think of it as a miniature MapReduce paradigm. Simply put, each data input has a key and an associated value. The goal of accumarray is to bin all of the values that belong to the same key and do some operation on all of these values. In our case, the "key" would be the intensity values, and the values themselves are the value of 1 for every intensity value. We would then want to add up all of the values of 1 that map to the same bin, which is exactly how we'd compute a histogram. The default behaviour for accumarray is to add all of these values. Specifically, the output of accumarray would be an array where each position computes the sum of all values that mapped to that key. For example, the first position would be the summation of all values that mapped to the key of 1, the second position would be the summation of all values that mapped to the key of 2 and so on.
However, for the joint histogram, you want to figure out which values map to the same intensity pair of (i,j), and so the keys here would be a pair of 2D coordinates. As such, any intensities that have an intensity of i in the first image and j in the second image in the same spatial location shared between the two images go to the same key. Therefore in the 2D case, the output of accumarray would be a 2D matrix where each element (i,j) contains the summation of all values that mapped to key (i,j), similar to the 1D case that was mentioned previously which is exactly what we are after.
In other words:
indrow = double(im1(:)) + 1;
indcol = double(im2(:)) + 1; %// Should be the same size as indrow
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
With accumarray, the first input are the keys and the second input are the values. A note with accumarray is that if each key has the same value, you can simply assign a constant to the second input, which is what I've done and it's 1. In general, this is an array with the same number of rows as the first input. Also, take special note of the first two lines. There will inevitably be an intensity of 0 in your image, but because MATLAB starts indexing at 1, we need to offset both arrays by 1.
Now that we have the joint histogram, it's really simple to calculate the joint entropy. It is similar to the entropy in 1D, except now we are just summing over the entire joint probability matrix. Bear in mind that it will be very likely that your joint histogram will have many 0 entries. We need to make sure that we skip those or the log2 operation will be undefined. Let's get rid of any zero entries now:
indNoZero = jointHistogram ~= 0;
jointProb1DNoZero = jointProb(indNoZero);
Take notice that I searched the joint histogram instead of the joint probability matrix. This is because the joint histogram consists of whole numbers while the joint probability matrix will lie between 0 and 1. Because of the division, I want to avoid comparing any entries in this matrix with 0 due to numerical roundoff and instability. The above will also convert our joint probability matrix into a stacked 1D vector, which is fine.
As such, the joint entropy can be calculated as:
jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero));
If my understanding of calculating entropy for an image in MATLAB is correct, it should calculate the histogram / probability distribution over 256 bins, so you can certainly use that function here with the joint entropy that was just calculated.
What if we have floating-point data instead?
So far, we have assumed that the images that you have dealt with have intensities that are integer-valued. What if we have floating point data? accumarray assumes that you are trying to index into the output array using integers, but we can still certainly accomplish what we want with this small bump in the road. What you would do is simply assign each floating point value in both images to have a unique ID. You would thus use accumarray with these IDs instead. To facilitate this ID assigning, use unique - specifically the third output from the function. You would take each of the images, put them into unique and make these the indices to be input into accumarray. In other words, do this instead:
[~,~,indrow] = unique(im1(:)); %// Change here
[~,~,indcol] = unique(im2(:)); %// Change here
%// Same code
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
indNoZero = jointHistogram ~= 0;
jointProb1DNoZero = jointProb(indNoZero);
jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero));
Note that with indrow and indcol, we are directly assigning the third output of unique to these variables and then using the same joint entropy code that we computed earlier. We also don't have to offset the variables by 1 as we did previously because unique will assign IDs starting at 1.
Aside
You can actually calculate the histograms or probability distributions for each image individually using the joint probability matrix. If you wanted to calculate the histograms / probability distributions for the first image, you would simply accumulate all of the columns for each row. To do it for the second image, you would simply accumulate all of the rows for each column. As such, you can do:
histogramImage1 = sum(jointHistogram, 1);
histogramImage2 = sum(jointHistogram, 2);
After, you can calculate the entropy of both of these by yourself. To double check, make sure you turn both of these into PDFs, then compute the entropy using the standard equation (like above).
How do I finally compute Mutual Information?
To finally compute Mutual Information, you're going to need the entropy of the two images. You can use MATLAB's built-in entropy function, but this assumes that there are 256 unique levels. You probably want to apply this for the case of there being N distinct levels instead of 256, and so you can use what we did above with the joint histogram, then computing the histograms for each image in the aside code above, and then computing the entropy for each image. You would simply repeat the entropy calculation that was used jointly, but apply it to each image individually:
%// Find non-zero elements for first image's histogram
indNoZero = histogramImage1 ~= 0;
%// Extract them out and get the probabilities
prob1NoZero = histogramImage1(indNoZero);
prob1NoZero = prob1NoZero / sum(prob1NoZero);
%// Compute the entropy
entropy1 = -sum(prob1NoZero.*log2(prob1NoZero));
%// Repeat for the second image
indNoZero = histogramImage2 ~= 0;
prob2NoZero = histogramImage2(indNoZero);
prob2NoZero = prob2NoZero / sum(prob2NoZero);
entropy2 = -sum(prob2NoZero.*log2(prob2NoZero));
%// Now compute mutual information
mutualInformation = entropy1 + entropy2 - jointEntropy;
Hope this helps!

How do I get the spectrum of 1D and 2D spatial filters used in images?

I would like to know how to obtain the spectrum of a spatial 1 dimensional and 2 dimensional sharpening filter in image processing.
The sharpening filters are:
[-1, 3, -1];
[-1, -1, -1; -1, 8, -1; -1, -1, -1];
In MATLAB, what should I do to get the spectrum of the filters, or how do I get the frequency components of these filters?
You can use Luis Mendo's post to determine the spectrum of your filter right away. Use either freqz for a 1D filter, or freqz2 for a 2D filter. Note that the plot using freqz and freqz2 are in terms of normalized frequency that spans between [-1,1].
However, I'd like to write this post to compliment Luis Mendo's and show you how the spectrum is derived.
Let's start with your first filter:
h1 = [-1,3,-1];
If you recall, the 1D Discrete-Time Fourier Transform (DTFT) is defined as:
Aside: Why the DTFT and not the Discrete Fourier Transform (DFT)?
Take special care that this is different from the Discrete Fourier Transform (DFT). The difference between them is that the DTFT is the traditional Fourier Transform applied to a discrete signal. From the continuous viewpoint, we apply the normal Fourier Transform where the spectrum is continuous in terms of frequency. It is essentially the same thing here for a discrete signal, but the transform is applied to a discrete signal. The output is also continuous in frequency, with the additional constraint that the spectrum is 2*pi periodic. For the DFT, it is essentially a sampled version of the DTFT in frequency domain. Therefore, the core difference between them both is that in the frequency domain, one is continuous while the other is a sampled version of the continuous counterpart. The reason why we need to sample the DTFT is so that you can actually store the spectra on computers and let programs process the spectra (i.e. MATLAB).
Your question asked to determine what the spectrum is, and in terms of a theoretical perspective, I will present the DTFT to you. When we actually plot the spectra, we are actually sampling the DTFT anyway, so when we see the spectra, you would be visualizing the DFT (more or less!).
A very good explanation about the differences between them both can be found on DSP StackExchange: https://dsp.stackexchange.com/questions/16586/difference-between-discrete-time-fourier-transform-and-discrete-fourier-transfor
Back to your question
For the definition of the DTFT, h[k] is a 1D signal, and omega is the angular frequency defined in radians. Therefore, you can consider your filter to be this 1D signal and when you filter in the spatial domain, it is the same as taking this signal, transforming it into the frequency domain and performing multiplication with another input signal in the frequency domain.
Therefore, if we consider that your filter is symmetric, the value of 3 is defined as the centre point. As such, you can think of h[k] as:
h = [-1 3 -1]
^ ^ ^
k = -1 0 1
Therefore, the frequency domain representation is simply a weighted sum of the filter coefficients with complex exponential terms. Substituting h[k] into the Fourier Transform formula, we get:
If you recall Euler's formula, we have:
Similarly:
If we add the two equations together, we can rearrange and solve for cos(x):
Going back to our transform of our 1D filter, we can do some factoring:
Note that the domain is between [-pi,pi], as the 1D Fourier Transform is 2*pi symmetric. As such, if you want to show the spectrum, simply plot using the above domain and use the equation that I just created, plotting the absolute value, as the magnitude of the spectrum is always positive:
w = linspace(-pi,pi);
h = abs(3 - 2*cos(w));
plot(w,h);
title('Magnitude of 1D spectrum');
axis([-pi, pi, 0, 5]);
linspace generates a linearly increasing array from -pi to pi with 100 points in between the ranges. You can override this behaviour by specifying a third parameter that manually tells linspace how many points you want to generate, but the default is 100 if you omit this parameter.
Note that I've made the y-axis extend from 0 so you can see where the curve starts at and it going up to 5 as this is the maximum value that is ever possible for h. This is what we get:
Indeed the above spectrum certainly looks like a high-pass filter, and because the magnitude at the DC frequency (w = 0) is 1, you are essentially adding the original signal on top of the high-pass filtering results, which thus "sharpens" your signal.
You can do the same process with the 2D case, though it will be a bit more involved. In the 2D case, the Discrete-Time Fourier Transform is defined to be:
We have two independent variables to consider, and we will have 2D spatial frequencies. w1 and k1 will operate along the rows of the 2D signal and w2 and k2 will operate along the columns of the 2D signal.
Given your mask:
h2 = [-1 -1 -1; -1 8 -1; -1 -1 -1]
As the shape of h2 is symmetric, the value of 8 will be location (w1,w2) = (0,0). Therefore, when we calculate the spectrum with the above equation, we get:
I'll save you the trouble with the simplification, but doing some rearranging and making use of Euler's formula, we get:
Note that:
I used the above property to make the simplifications possible. For the 2D case, we will define a meshgrid of coordinates that both range between [-pi,pi] for both dimensions, and we will plot this on a surface plot with surf. Remember to take the absolute value of the filter to show the magnitude:
[w1,w2] = meshgrid(linspace(-pi,pi));
h = abs(8 - 2*cos(w1+w2) - 2*cos(w1) - 2*cos(w2) - 2*cos(w1-w2));
surf(w1,w2,h);
title('2D spectrum of filter');
w1 and w2 provide unique combinations of the frequencies defined horizontally and vertically at each respective spatial location of the arrays. w1 and w2 will in fact be two-dimensional. For each unique combination of w1 and w2, we determine what the magnitude of the spectra will be. Once we calculate these, we can put all of this information together in a nice three-dimensional surface plot.
We thus get:
Take note that both dimensions span from [-pi,pi]. If you examine the spectrum, you will see that the DC component gets nulled to 0, which is actually a high-pass filter, and not a sharpening filter. See my note below.
Minor note
BTW, your 2D filter definition is not a 2D sharpening filter. It is simply a 2D Laplacian, which is an edge detection and thus a high-pass filter. It only detects edges. If you want to properly sharpen the image, make sure you add 1 to the centre of the kernel, and so what you're really after is:
h2 = [-1 -1 -1; -1 9 -1; -1 -1 -1];
The offset of 1 will ensure that you are leaving the original signal untouched, but also adding the high-pass results on top of the original signal to thus sharpen your input.
To plot the frequency response of a 1D FIR filter, use freqz:
h = [-1, 3, -1]; %// FIR impulse response
N = 128; %// number of frequency samples
freqz(h,1,N); %// "1" because the filter is not recursive
The frequency axis is normalized to half the sample frequency, as usual.
See the documentation of freqz for further options.
To plot the frequency response of a 2D FIR filter, use freqz2:
h = [-1, -1, -1; -1, 8, -1; -1, -1, -1]; %// FIR impulse response (filter mask)
N = 128; %// number of frequency samples
freqz2(h,N,N)
The frequency axes are normalized to half the sample frequency, as in the 1D case.
See the documentation of freqz2 for further options.

Variable levels of smoothing within the same Matlab matrix

I currently have a large matrix M (~100x100x50 elements) containing both positive and negative values. At the moment, if I want to smooth this matrix, I use the smooth3 function to apply a gaussian kernel over the entire 3-D matrix.
What I want to achieve is a variable level of smoothing within this matrix - i.e.. different parts of the matrix M are smoothed to different levels of sigma depending of the value in a similar 3-D matrix, d (with values ranging from 0 to 1). Where d is 0, no smoothing occurs, where d is 1 a maximum level of smoothing occurs.
The fact that the matrix is 3-D is trivial. Smoothing in 3 dimensions is nice, but not essential, and my current code (performing various other manipulations) handles each of the 50 slices of M separately anyway. I am happy to replace smooth3 with a convolution of M with a gaussian function, and perform this convolution over each slice individually. What I can't figure out is how to vary the sigma level of this gaussian function (based on d) given its location in M and output the result accordingly.
An alternative approach may be to use matrix d as a mask for a very smooth version of matrix Ms and somehow manipulate M and Ms to give an equivalent result, however I'm not convinced that this will work as I can't think of a function to combine M and Md that won't give artefacts of each of M or Ms when 0 < d < 1...any thoughts?
[I'm using 2009b, and only have access to the Signal Processing toolbox.]
You should have a look at the Guided Image Filter. It is a computationally efficient generalization of the bilateral filter.
http://research.microsoft.com/en-us/um/people/jiansun/papers/guidedfilter_eccv10.pdf
It will allow you to do proper smoothing based on your guidance matrix.