How to calculate the mean of all neighbouring elements in a matrix
33 views (last 30 days)
Hello, I have a m x n matrix and I want tot calculate the mean for every element and all its neighbouring values (horizontal, vertical, diagonal). Here I also want to consider the number of neighbours an element has dependent on its position. With matrix M = [3,4,8,6;1,6,9,0;2,5,7,1], M(1,1) should only have 3 neighbours, M(1,2) 5 neigbours and M(2,2) 8 neighbours.
How can I implement a function to calculate the mean of all neighbouring elements with using the matrix positions i,j which represents the rows and column values of the matrix. So M(i,j-1) should be the left element, M(i,j+1) the right element, M(i+1,j) the upper element, M(i-1) the lower element, M(i-1,j-1) the south-west element and so on.
The result should be saved in a new matrix. Should I use a nested for-loop to achieve this?
John D'Errico on 16 Aug 2022
Edited: John D'Errico on 16 Aug 2022
This is actually trivially easy to solve, using a nice trick that is well worth remembering. (Note that this is a great trick that has worked for me in multiple places. For example, you can use it to write a simple code for the game of life.) I'll use my own matrix for the example, so we can see how it works.
A = magic(7)
Now we wish to compute the mean of 3x3 blocks of the matrix. But near the edges, we have fewer neighbors. In a corner, even fewer neighbors yet. So what can we do? First, we will use convolution, but we do so in a tricky way. I'll do so here for the 2-dimensional problem, but higher dimensional problems would work just as nicely, because we can use convn.
K = ones(3,3); % the convolution kernel, indicating a 3x3 moving mean
Amean = conv2(A,K,'same')./conv2(ones(size(A)),K,'same')
Why did this work? Perhaps we need to look at the pieces. What did this do?
So that just adds up all of the elements in the vicinity of any original element in A.
Do you see how this works? It counted the number of terms in the sum. Essentially, it counts up the number of elements that were added to form the sum in the first call to conv2. And what is a mean, except the sum of a list of elements, divided by the number of elements in the list?
Now do you see how to make this work for a higher dimensional matrix? Easy, peasy.
A = randi(9,[4,4,4])
K = ones(3,3,3); % the convolution kernel, indicating a 3x3x3 moving mean
Amean = convn(A,K,'same')./convn(ones(size(A)),K,'same')
Jan on 19 Mar 2021
Edited: Jan on 19 Mar 2021
M = [3,4,8,6; 1,6,9,0; 2,5,7,1];
Result = movmean(movmean(M, 3, 1), 3, 2);
n = 3; % Window size
[s1, s2] = size(M);
D = repmat(9, size(M));
D(1, 1) = 4;
D(1, s2) = 4;
D(s1, 1) = 4;
D(s1, s2) = 4;
D(1, 2:s2-1) = 6;
D(s1, 2:s2-1) = 6;
D(2:s1-1, 1) = 6;
D(2:s1-1, s2) = 6;
Result = conv2(M, ones(n), 'same') ./ D;
Result = conv2(M, ones(n), 'same') ./ conv2(ones(size(M)), ones(n), 'same')
The approach with nested loops is ugly and slower.