MATLAB Answers

0

Averaging every element with nearby elements

Asked by Perturabo on 13 Feb 2019
Latest activity Commented on by Perturabo on 15 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.

  3 Comments

Write your expected output explicitly.
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]
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.

Sign in to comment.

Products


Release

R2018a

2 Answers

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.

  1 Comment

That's a neat trick. Thanks for the help.

Sign in to comment.


Answer by KSSV
on 13 Feb 2019

Read about knnsearch. With this you can get your desired number of nearest neighbors indices.....with the indices you can get your mean.

  0 Comments

Sign in to comment.