2

I am relatively new to matlab and image processing, so please bear with me.
What I am trying to do is characterize noise within an image, specifically by averaging the fft of an area where this noise occurs with high probability.

img = img(1:40,1:100)
imshow(img);
ffts = blockproc(img, [20 20], @(block_struct) fftshift(fft2(block_struct.data)));
// fft = imresize(ffts, [40 100], 'nearest');

Essentially, this code takes the upper left hand 40 x 100 portion of the image and then performs a block-process on each 20 x 20 subsection of that area calculating fft2. Hopefully, my logic sounds alright so far.

What I am wondering though, is if there is any way to perform the average fft2 of the 20 x 20 subsections of the 40 x 100 fft matrix with the built-in matlab functionality. I know that this could be completed relatively easy with loops, but I'd like to keep the solution in my code as compact as possible.

I've read through the manual a little and it is apparent that there are a couple of matlab functions that may perform this; However, I am not entirely confident in my application so far. Any directions are welcomed!

m.n
  • 47
  • 4
  • Do you want to compute the average `20 x 20` block now that you have computed all `20 x 20` 2D FFTs of the image? – rayryeng Aug 28 '14 at 19:03
  • Yes, I would like to compute the average 20 x 20 block over all 20 x 20 2D FFTs of the image. Sorry for the lack of clarity on my part. – m.n Aug 28 '14 at 19:04
  • OK, I'll provide an answer. Give me 5 minutes – rayryeng Aug 28 '14 at 19:06

1 Answers1

2

This can easily be done in 3 lines of code.


Line #1

First use im2col to reshape each distinct block neighbourhood of 20 x 20 into a single column. As such, the output of this will be a 400 x N matrix, where each column denotes a unique block neighbourhood that has been reshaped into a column. Each column will have 400 rows, as each neighbourhood has 400 elements (20 x 20). N would be the total number of unique blocks we have in your 40 x 100 image. This would amount to 10, as we can fit 2 blocks horizontally and 5 blocks vertically given the 20 x 20 requirement.

Line #2

What is great about the output of im2col is that the ith row of im2col tells you the ith element for every block in your image. As such, all you have to do next is take each row and average over all of the columns. The output will be a 400 x 1 vector that denotes your average FFT for all of the blocks. This can be achieved using mean and specifying that we want to average over the second dimension (second parameter is 2), which is the columns.

Line #3

We then need to reshape this back into a 20 x 20 matrix, so use reshape to do this. We specify that the output matrix is 20 x 20, given the 400 x 1 element vector.

One question that you may ask is whether or not this re-ordering is guaranteed to reorder our FFT block correctly. This is guaranteed because when im2col constructed each block into a column, it progresses in a column-major order. This means that for one column of blocks, we construct them on a row-by-row basis. Once we get our 20 x 20 set of distinct blocks, these blocks are arranged so that the are sampled in column major order. This means that a single 20 x 20 block gets constructed into a 400 x 1 column vector, where columns of the 20 x 20 block are stacked on top of each other from left to right. Therefore, by doing mean and reshape, the spatial locations for each block do correspond to each other and will thus produce the right answer.


Without further ado, here's the code:

colBlocks = im2col(ffts, [20 20], 'distinct'); %// Line 1
meanCol = mean(colBlocks, 2); %// Line 2
fftBlockAverage = reshape(meanCol, [20 20]); %// Line 3

Minor Side Effect

Because the FFT is complex valued in nature, by doing the average, you would average the real and imaginary components separately. This is how MATLAB handles the average of complex valued data. I'm not sure what analysis you'll be performing after you calculate the average 2D FFT block, but bear this in mind before you proceed any further with your analysis.


Sidenote

Divakar in an earlier answer created a more efficient implementation of im2col. This is especially useful if you don't have the Image Processing Toolbox installed. You can check out that implementation here. It has been shown that the timing between this function and MATLAB's im2col are magnitudes faster.

Benchmarking

As a bonus, here is a benchmark using his code. Timing results were taken using a 40 x 100 matrix where the im2col built-in function was timed, and Divakar's custom function after. Results show that his method is faster. This may be very useful when considering larger size images. However, if you are looking for succinctness, use what I have written. If you want something fast, use his method.

Benchmarking Code

%// Input Parameters
nrows = 20;
ncols = 20;
A = rand(40,100);

disp('------------------------- With IM2COL');
tic
B1 = im2col(A,[nrows ncols],'distinct');
toc,clear B1

disp('----------------- With CUSTOM-BUILT IM2COL');
tic
B2 = im2col_distinct(A,[nrows ncols]);
toc,clear B2

Results

------------------------- With IM2COL
Elapsed time is 0.026914 seconds.
----------------- With CUSTOM-BUILT IM2COL
Elapsed time is 0.004186 seconds.
Community
  • 1
  • 1
rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • If this works for OP and if `im2col` is to be used and if runtime performance is essential, allow me to suggest an alternative to it - http://stackoverflow.com/a/25454746/3293881, which seemed faster on few tests that I was able to perform. – Divakar Aug 28 '14 at 19:19
  • @Divakar - That's funny. I actually favourited that question you linked to and upvoted you lol. I should have known better. Thanks for the link Divakar! – rayryeng Aug 28 '14 at 19:20
  • Guess I paid back ;) Well now that we have an actual case, OP can test it out with the in-built one and the custom one! Would love to see some benchmarks from OP on this! – Divakar Aug 28 '14 at 19:21
  • @Divakar - hehe. I don't know when I did it, but when I checked out your answer, I already upvoted it and I favourited it. It must have been a while ago. In terms of code writing, this would be the shortest solution. If efficiency is required, then the OP should definitely use your answer. – rayryeng Aug 28 '14 at 19:22
  • 1
    @Divakar - My guess is that for small matrices, the speed tests will be comparable. When the matrices are of larger sizes, I suspect that your method will be faster. – rayryeng Aug 28 '14 at 19:24
  • Thanks you two!! If I have to perform these calculations on a larger matrix size, I'll be sure to do some speed tests between the two recommended implementations and go form there. – m.n Aug 28 '14 at 19:36
  • @m.n - You're very welcome. Welcome to StackOverflow! Just to be clear, Divakar's code only replaces the first line of my code. You will still need to do Lines #2 and Lines #3 to complete what you want. You would only be benchmarking the first line of the code I wrote as Divakar's post is a replacement of `im2col` with the `distinct` flag - not the entire averaging and reshaping process. – rayryeng Aug 28 '14 at 19:40
  • @rayryeng Damn! I totally forgot you didn't even mention about the custom function! Edit the benchmark if you would like! I was just eager to post out, as that ~4x speedup got me excited and I have used the sizes as used by OP here. – Divakar Aug 28 '14 at 19:43
  • @Divakar - There is no need to edit :) The code is already just benchmarking what is required! – rayryeng Aug 28 '14 at 19:44