## Averaging every element with nearby elements

### Perturabo (view profile)

on 13 Feb 2019
Latest activity Commented on by Perturabo

### Perturabo (view profile)

on 15 Feb 2019
Accepted Answer by John D'Errico

### John D'Errico (view profile)

I want to average each element of an m by n matrix with surrounding w elements, for example, if w = 1, the element would be the mean of the 3x3 submatrix with the element in the middle, i.e, if the matrix is
a = [1:10;2:11;3:12;4:13;5:14;6:15;7:16]
and w = 1, each element would be average of nearby eight elements and itself.
I tried using 3 loops, i = 1:w, 1 = w+1:end-w, i = end-w+1:end, but there must be a quicker way to do this.

### madhan ravi (view profile)

on 13 Feb 2019
Write your expected output explicitly.
Perturabo

### Perturabo (view profile)

on 13 Feb 2019
for a smaller matrix
a = [1:4;2:5;3:6]
output = [2,2.53.5,4;3,2.67,4,5;3,3.5,4.5,5]
John D'Errico

### John D'Errico (view profile)

on 13 Feb 2019
I don't think you were that careful in how you computed your example. Or if you were, then you need to explain far better what you want.

### Release

R2018a ### John D'Errico (view profile)

Answer by John D'Errico

### John D'Errico (view profile)

on 13 Feb 2019
Edited by John D'Errico

### John D'Errico (view profile)

on 13 Feb 2019

A simple solution is to use conv2. It works quite well inside the domain. But then you will need to be careful around the edges. However, that is quite easily fixed. Even trivial, once you see the trick. All it requires is TWO calls to conv2. Lets see how this trick works. First, I'll do it the wrong way, and we can see what happens.
A = magic(5)
A =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
Now lets use w = 1. So 1 element in each direction. That means we need to use a convolution kernel that is 3x3.
w = 1;
K = ones(2*w+1)/(2*w+1)^2;
K
K =
0.11111 0.11111 0.11111
0.11111 0.11111 0.11111
0.11111 0.11111 0.11111
So the convolution kernel is just an array that forms anaverage at every point. If w were 2, then each element would be 1/25, or 0.04.
Aave = conv2(A,K,'same')
ans =
7.6667 8.5556 6.5556 6.7778 5.8889
8.7778 11.111 10.889 12.889 10.556
6.6667 11 13 15 10.667
6.7778 13.111 15.111 14.889 8.5556
5.6667 10.556 10.778 8.7778 3.8889
If we look at the inside elements of the array Aave, say the (2,2) element, it should be 11.111, thus
mean(mean(A(1:3,1:3)))
ans =
11.111
So that worked. If I had used the valid option for conv, then every element computed would have been correct.
But what is Aave(1,1)? For that, we need to do it as:
mean(mean(A(1:2,1:2)))
ans =
17.25
and we got 7.6667 instead. The problem is, conv2 substitues 0 in for the elements that fell outside. So all of the edge elements in the average will be wrong. You COULD fix it, by going back afterwards, then patching those edge elements that were wrongly computed. But then you need to be careful with the elements at or near the corner. It gets worse if w is 2 or 3, because then when what is near the edge gets more complex. There is a trick however that works VERY nicely.
Are you seeing the trick now that I will use? I said you need to do TWO calls to conv2.
What would happen if you wrote the convolution kernel as
K = ones(2*w+1);
Aave = conv2(A,K,'same')./conv2(ones(size(A)),K,'same')
Aave =
17.25 12.833 9.8333 10.167 13.25
13.167 11.111 10.889 12.889 15.833
10 11 13 15 16
10.167 13.111 15.111 14.889 12.833
12.75 15.833 16.167 13.167 8.75
Think about why this works. It is a neat trick that is applicable in multiple circumstances. The first call to conv2 is a SUM. Then the second call to conv counts how many elements were involved with that sum. When you divide them, you get a mean, thus the average as desired.
It clearly applies for larger values of w too.
It even works for a 1-dimensional array, although then it may make more sense to use two calls to conv instead of conv2.

Perturabo

### Perturabo (view profile)

on 15 Feb 2019
That's a neat trick. Thanks for the help.