Asked by Perturabo
on 13 Feb 2019

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.

Answer by John D'Errico
on 13 Feb 2019

Edited by John D'Errico
on 13 Feb 2019

Accepted Answer

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.

Opportunities for recent engineering grads.

Apply Today
## 3 Comments

## madhan ravi (view profile)

Direct link to this comment:https://de.mathworks.com/matlabcentral/answers/444683-averaging-every-element-with-nearby-elements#comment_670713

## Perturabo (view profile)

Direct link to this comment:https://de.mathworks.com/matlabcentral/answers/444683-averaging-every-element-with-nearby-elements#comment_670719

## John D'Errico (view profile)

Direct link to this comment:https://de.mathworks.com/matlabcentral/answers/444683-averaging-every-element-with-nearby-elements#comment_670844

Sign in to comment.