How to calculate the mean of all neighbouring elements in a matrix
16 views (last 30 days)
Show older comments
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?
0 Comments
Answers (2)
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?
conv2(A,K,'same')
So that just adds up all of the elements in the vicinity of any original element in A.
conv2(ones(size(A)),K,'same')
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')
0 Comments
Jan
on 19 Mar 2021
Edited: Jan
on 19 Mar 2021
What about:
M = [3,4,8,6; 1,6,9,0; 2,5,7,1];
Result = movmean(movmean(M, 3, 1), 3, 2);
Or:
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;
Or nicer:
Result = conv2(M, ones(n), 'same') ./ conv2(ones(size(M)), ones(n), 'same')
The approach with nested loops is ugly and slower.
4 Comments
Márton Tamás Birosz
on 16 Aug 2022
Hey, is it possible to use 'movmean' the same way for a 3D (nxnxn) matrix?
See Also
Categories
Find more on Conway's Game of Life in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!