I want to find out how nearest neighbor interpolation works in MATLAB. I have input data :
A = [1 4 7 4 3 6] % 6 digit vector
I use the following MATLAB code :
B = imresize(A,[1 9],'nearest');
I get the following result :
[1,4,4,7,4,4,3,6,6]
Solving by hand, I get this result :
[1 4 4 7 4 4 3 3 6]
Can you please guide me? Am I going wrong somewhere?
If you apply regular interpolation using interp1, it will give you the result you computed by hand:
>> N = 9;
>> B = interp1(linspace(0,1,numel(A)), A, linspace(0,1,N), 'nearest')
B =
1 4 4 7 4 4 3 3 6
Some time ago, I went through the source code of IMRESIZE trying to understand how it works. See this post for a summary. At some point the code will call an private MEX-function (no corresponding source code available), but the comments are enough to understand the implementation.
For what it's worth, there an also a function imresize_old which provides an older implementation of imresize (used in version R2006b and earlier). It gave yet another different result:
>> B = imresize(A, [1 N], 'nearest')
B =
1 4 4 7 4 4 3 6 6
>> B = imresize_old(A, [1 N], 'nearest')
B =
1 4 4 7 7 4 3 6 6
What's more it was previously observed that the implementation between MATLAB and Octave also differed in some cases.
EDIT:
As you noted, in some cases you have to be mindful about floating-point limitations when working with interp1. So we could do the interpolation by choosing x-numbers to be between [0,1] range, or a more stable range like [1,numel(A)]. Because of rounding errors in edge cases, this might give different results.
For example compare the two codes below:
% interpolation in [0,1]
N = 11;
y = [1 4 7 4 3 6];
x = linspace(0,1,numel(y));
xi = linspace(0,1,N);
yi = interp1(x, y, xi, 'nearest');
% print numbers with extended precision
fprintf('%.17f %g\n',[x;y])
fprintf('%.17f %g\n',[xi;yi])
against:
% interpolation in [1,k]
N = 11;
y = [1 4 7 4 3 6];
x = 1:numel(y);
xi = linspace(1,numel(y),N);
yi = interp1(x, y, xi, 'nearest');
% print numbers with extended precision
fprintf('%.17f %g\n',[x;y])
fprintf('%.17f %g\n',[xi;yi])
Here is the output nicely formatted:
--------------------------------------------------------
[0,1] RANGE | [1,k] RANGE
--------------------------------------------------------
xi yi | xi yi
--------------------------------------------------------
0.00000000000000000 1 | 1.00000000000000000 1 |
0.20000000000000001 4 | 2.00000000000000000 4 |
0.40000000000000002 7 | 3.00000000000000000 7 |
0.59999999999999998 4 | 4.00000000000000000 4 | INPUT
0.80000000000000004 3 | 5.00000000000000000 3 |
1.00000000000000000 6 | 6.00000000000000000 6 |
--------------------------------------------------------
0.00000000000000000 1 | 1.00000000000000000 1 |
0.10000000000000001 4 | 1.50000000000000000 4 |
0.20000000000000001 4 | 2.00000000000000000 4 |
0.29999999999999999 4 | 2.50000000000000000 7 |
0.40000000000000002 7 | 3.00000000000000000 7 |
0.50000000000000000 4 | 3.50000000000000000 4 | OUTPUT
0.59999999999999998 4 | 4.00000000000000000 4 |
0.69999999999999996 4 | 4.50000000000000000 3 |
0.80000000000000004 3 | 5.00000000000000000 3 |
0.90000000000000002 6 | 5.50000000000000000 6 |
1.00000000000000000 6 | 6.00000000000000000 6 |
--------------------------------------------------------
So you can see that some numbers are not exactly representable in double-precision when working in the [0,1] range. So 0.3 which is supposed to be in the middle [0.2, 0.4], turns to be closer to the lower end 0.2 than 0.4 because of roundoff error. While on the other side, 2.5 is exactly in the middle of [2,3] (all numbers exactly represented), and is assigned to the upper end 3 using nearest neighbor.
Also be aware that colon and linspace can produce different outputs sometimes:
>> (0:0.1:1)' - linspace(0,1,11)'
ans =
0
0
0
5.5511e-17
0
0
0
0
0
0
0
NN is the simplest form of interpolation. It has the following recipe: use the value at the nearest sample location. The NN interpolation in MATLAB is computationally efficient but if you need more accuracy, I recommend you to use the bilinear or the bicubic interpolation. You can also check interp1() instead.
Here provides an explanation with an example: http://www.mathworks.com/help/vision/ug/interpolation-methods.html
I have no reference for this so I suggest you test it against other examples using imresize but I can recover Mat;ab's values like this.
Assume that A represents y values and the positions of elements in A represent x values. so now
n = length(A);
N = 9;
x = 1:n %// i.e. 1:6
now we need to find interpolating position i.e. xi points. I would have done it like this:
xi = round((1:N)/N)*n
which gives
xi =
1 1 2 3 3 4 5 5 6
which results in a yi of
yi = A(xi)
yi =
1 1 4 7 7 4 3 3 6
which differs from both yours and Matlab's answers (how did you get yours?)
So then I tried:
xi = round(((0:N-1)/N)*n)+1
yi = A(xi)
which makes just as much sense and gets me Matlab's result of
yi =
1 4 4 7 4 4 3 6 6
So I'm guessing that's how they do it. But I don't have imresize to test other cases
Related
Let
M = | 1 2 3 |
| 4 5 6 |
| 7 8 9 |
and
V = | 1 1 1 |
I want to subtract V from every row of M so that M should look like
M = | 0 1 2 |
| 3 4 5 |
| 6 7 8 |
How can I do that without using a for, is there any straightforward command?
You can also use bsxfun.
M = [1 2 3 ; 4 5 6 ; 7 8 9] ;
V = [1 1 1] ;
iwant = bsxfun(#minus,M,V)
>> M = [1 2 3; 4 5 6; 7 8 9];
>> V = [1 1 1];
>> MV = M-repmat(V,size(M,1),1)
MV =
0 1 2
3 4 5
6 7 8
The call to repmat repeats the vector V by the number of rows in M.
User beaker pointed out that an even simpler (though a bit obscure) syntax works in recent versions of MATLAB. If you subtract a vector from a matrix, MATLAB will extend the vector to match the size of the matrix as long as one dimension of vector matches the matrix dimensions. See Compatible Array Sizes for Basic Operations.
>> M-V
ans =
0 1 2
3 4 5
6 7 8
Of course, if you know that V will contain all 1s, the solution is even simpler:
>> MV = M-1
MV =
0 1 2
3 4 5
6 7 8
I would like to align and count vectors with different time stamps to count the corresponding bins.
Let's assume I have 3 matrix from [N,edges] = histcounts in the following structure. The first row represents the edges, so the bins. The second row represents the values. I would like to sum all values with the same bin.
A = [0 1 2 3 4 5;
5 5 6 7 8 5]
B = [1 2 3 4 5 6;
2 5 7 8 5 4]
C = [2 3 4 5 6 7 8;
1 2 6 7 4 3 2]
Now I want to sum all the same bins. My final result should be:
result = [0 1 2 3 4 5 6 7 8;
5 7 12 16 ...]
I could loop over all numbers, but I would like to have it fast.
You can use accumarray:
H = [A B C].'; %//' Concatenate the histograms and make them column vectors
V = [unique(H(:,1)) accumarray(H(:,1)+1, H(:,2))].'; %//' Find unique values and accumulate
V =
0 1 2 3 4 5 6 7 8
5 7 12 16 22 17 8 3 2
Note: The H(:,1)+1 is to force the bin values to be positive, otherwise MATLAB will complain. We still use the actual bins in the output V. To avoid this, as #Daniel says in the comments, use the third output of unique (See: https://stackoverflow.com/a/27783568/2732801):
H = [A B C].'; %//' stupid syntax highlighting :/
[U, ~, IU] = unique(H(:,1));
V = [U accumarray(IU, H(:,2))].';
If you're only doing it with 3 variables as you've shown then there likely aren't going to be any performance hits with looping it.
But if you are really averse to the looping idea, then you can do it using arrayfun.
rng = 0:8;
output = arrayfun(#(x)sum([A(2,A(1,:) == x), B(2,B(1,:) == x), C(2,C(1,:) == x)]), rng);
output = cat(1, rng, output);
output =
0 1 2 3 4 5 6 7 8
5 7 12 16 22 17 8 3 2
This can be beneficial for particularly large A, B, and C variables as there is no copying of data.
let us consider following matrix
A=[1 2 3 4;2 4 6 7;3 1 9 8]
A =
1 2 3 4
2 4 6 7
3 1 9 8
size of which can easily be calculated using
n,m]=size(A)
n =
3
m =
4
let us consider that we want to get following vector v
v(1)=A(1,1);
v(2)=(A(2,1)+A(1,2)/2;
v(3)=(A(3,1)+A(2,2)+A(1,3))/3;
v(4)=(A(3,2)+A(2,3)+A(1,4))/3;
v(5)=(A(3,3)+A(2,4))/2;
v(6)=A(3,4);
definitely we need vector with size
v=zeros(n+m-1,1);
i have calculated first two element which seems trivial
v(1)=A(1,1);
v(2)=(A(2,1)+A(1,2))/2;
but all others i need to implement using cycles,please pay attention that i need it for general matrix using same principle,not exact for such matrix
my starting code is following
function [v]=dehankel(A);
%convert matrix A to vector using diagonal averaging
[n,m]=size(A);
v=zeros(n+m-1,1);
v(1)=A(1,1);
v(2)=(A(2,1)+A(1,2))/2;
for i=2:3
for j=2:3
please help me how to continue
A =
1 2 3 4
2 4 6 7
3 1 9 8
[n,m]=size(A);
v=zeros(n+m-1,1);
i = 1;
for d = -(n-1):(m-1)
v(i) = mean(diag(flipud(A),d));
i = i+1;
end
It can be done without loops (not the most readable code, I admit):
B = zeros(size(A,1)+size(A,2)-1, size(A,2));
B(bsxfun(#plus, (1:size(A,1)).', (0:size(A,2)-1)*(size(A,1)+size(A,1)+1))) = A;
v = sum(B.')./[1:size(A,1) size(A,1):-1:1];
What is a Matlab-efficient way (no loop) to do the following operation: transform an input vector input into an output vector output such as output(i) is the number of integers in input that are less or equal than input(i).
For example:
input = [5 3 3 2 4 4 4]
would give:
output = [7 3 3 1 6 6 6]
First of all, don't use input for a variable name, it's a reserved keyword. I'll use X here instead.
An alternative way to obtain your desired result would be:
[U, V] = meshgrid(1:numel(X), 1:numel(X));
Y = sum(X(U) >= X(V))
and here's a one-liner:
Y = sum(bsxfun(#ge, X, X'))
EDIT:
If X has multiple rows and you want to apply this operation on each row, this is a little bit trickier. Here's what you can do:
[U, V] = meshgrid(1:numel(X), 1:size(X, 2));
V = V + size(X, 2) * idivide(U - 1, size(X, 2));
Xt = X';
Y = reshape(sum(Xt(U) >= Xt(V))', size(Xt))'
Example:
X =
5 3 3 2 4 4 4
3 9 7 7 1 2 2
Y =
7 3 3 1 6 6 6
4 7 6 6 1 3 3
I have found a possible answer:
output = arrayfun(#(x) sum(x>=input),input)
but it doesn't take advantage of vectorization.
A = [1 2 3; 7 6 5]
B = [3 7];
A-B = [1-3 2-3 3-3; 7-7 6-7 5-7];
ans =[-2 -1 0; 0 -1 -2]
This is the operation I want to have done. How could I do it by matrix functions other than the iterative solutions?
You do this most conveniently with bsxfun, which automatically expands the arrays to match in size (so that you don't need to use repmat). Note that I need to transpose B so that it's a 2-by-1 array.
A = [1 2 3; 7 6 5]
B = [3 7];
result = bsxfun(#minus,A,B')
result =
-2 -1 0
0 -1 -2
I think that Jonas answer is the best. But just for the record, here is the solution using an explicit repmat:
A = [1 2 3; 7 6 5];
B = [3 7];
sz = size(A);
C = A - repmat(B', [1 sz(2:end)]);
Not only is Jonas' answer simpler, it is actually faster by a factor of 2 for large matrices on my machine.
It's also interesting to note that in the case where A is an n-d array, both these solutions do something quite reasonable. The matrix C will have the following property:
C(k,:,...,:) == A(k,:,...,:) - B(k)
In fact, Jonas' answer will run, and very likely do what you want, in the case where B is m-d, as long as the initial dimensions of A and B' have the same size. You can change the repmat solution to mimic this ... at which point you are starting to reimplement bsxfun!
Normally you can't. Iterative solutions will be necessary, because the problem is poorly defined. Matrix addition/subtraction is only defined for matrices of the same dimensions.
ie:
A = | 1 2 3 |
| 7 6 5 |
B = | 3 7 |
It makes no sense to subtract a 1x2 matrix from a 2x3 matrix.
However, if you multiplied B by some intermediate matrix to make the result a 2x3 matrix, that would work, ie:
B' * Y = | 3 3 3 |
| 7 7 7 |
eg:
B' = diag(B)
= | 3 0 |
| 0 7 |
B' * Y = | 3 3 3 |
| 7 7 7 |
Y = | 1 1 1 |
| 1 1 1 |
Therefore, A-B'*Y gives a valid, non-iterative solution.
A-(B'*Y) = | 1 2 3 | - | 3 3 3 |
| 7 6 5 | | 7 7 7 |
= A - (diag(B) * Y )
The only "cheat" here is the use of the diag() function, which converts a vector to a strictly-diagonal-matrix. There is a way to manually decompose a set of matrix/vector multiplication operations to manually re-create the diag() function, but that would be more work than my solution above itself.
Good luck!