Get points which are within a given distance in two different matrices - matlab

I have two matrices A and B, in which the number of rows can vary. A and B do not necessarily have the same number of rows.
For example:
A = [ 110 90
130 140
230 50
370 210 ];
B = [ 321 95
102 35
303 200 ];
Now matrix A and B have 'corresponding points'. Corresponding points are rows where the values in the 2nd column of both matrices are within +/-20.
For example:
A(1,2) = 90 and B(1,2) = 95, the difference is within +/-20 so A(1,:) and B(1,:) are corresponding points.
A(2,2) = 140 and B(2,2) = 35, the difference is not within +/-20 so A(2,:) and B(2,:) are not corresponding points.
A(3,2) = 50 and B(2,2) = 35, the difference is within +/-20 so A(3,:) and B(2,:) are corresponding points.
Using this I want to store the corresponding points of A and B in C and D respectively. For the above example, the final matrices should look like this:
C = [ 110 90
230 50
370 210 ]
D = [ 321 95
102 35
303 200 ]

You can get all of the distances using pdist2
dists = pdist2( A(:,2), B(:,2) )
>> dists = [ 5 55 110
45 105 60
45 15 150
115 175 10 ]
Then get the indices of all 'corresponding points', as defined by a threshold of 20.
% Get combinations within tolerance
idx = dists < 20;
% Get indices
[iA, iB] = find(idx);
Then you can create the final matrices
C = A(iA, :);
D = B(iB, :);
Edit: One way to ensure each pairing is unique (i.e. A(1,:) cannot be paired with multiple rows from B) would be to get the minimum dists for each row/column. Note: this would still give you duplicate matches if the distances are exactly the same, you haven't defined how this should be handled.
dists = pdist2( A(:,2), B(:,2) );
% Set values which are greater than the row/column minima to be infinity.
% This means they will never be within the tolerance of 20 (or whatever else)
dists ( bsxfun(#gt, dists, min(dists,[],1)) | bsxfun(#gt, dists, min(dists,[],2)) ) = Inf;
% In MATLAB versions > 2016b, you can use implicit expansion to replace bsxfun
% That would be: dists( dists > min(dists,[],1) | dists > min(dists,[],2) )
% Now continue as before
[iA, iB] = find( dists < 20 );
C = A(iA, :);
D = B(iB, :);

Related

Find Array around Maximum Values of an Array

i have a quite complicated question here which i am working on. It's extremely difficult to describe in words, so i will try to explain it with an example.
Assume i have a matrix of values:
A =
[31 85 36 71 51]
[12 33 74 39 12]
[67 11 13 14 18]
[35 36 84 33 57]
Now, i want to first find a maximum vector in the first dimension, which is easy:
[max_vector,~] = max(A,[],1);
max_vector=[67,85, 84, 71,57]
Now i want to get a "slimmed" matrix with values around the maxima (periodical indices):
Desired_Matrix =
[12 36 36 33 18]
[67 85 84 71 57]
[35 33 13 39 51]
This is the matrix with the vectors around the maximum values of matrix A. Can someone tell me how to do this without using a double for loop?
Thank you!
% Input.
A = [31 85 36 71 51; 12 33 74 39 12; 67 11 13 14 18; 35 36 84 33 57]
% Dimensions needed.
nRows = size(A, 1);
nCols = size(A, 2);
% Get maxima and corresponding indices in input.
[max_vector, ind] = max(A);
% Get neighbouring indices.
ind = [ind - 1; ind; ind + 1];
% Modulo indices to prevent dimension overflow.
ind = mod(ind, nRows);
% Correct zero indices.
ind(ind == 0) = nRows;
% Calculate correct indices in A.
temp = repmat(0:nRows:nRows*(nCols-1), 3, 1);
ind = ind + temp;
% Output.
B = A(ind)
Since we have max indices per column, but later want to access these elements in the original array A, we need proper linear indices for A. Here, the trick is to add the number of rows multiplied by the column index (starting by 0). The easiest way to understand might be to remove the semicolons, and inspect the intermediate values of ind.
#HansHirse's answer is more efficient, as it does not create an intermediate matrix.
Try this:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = bsxfun(#plus, bsxfun(#plus, ind_max, (0:2).'), (0:size(A_ext,2)-1)*size(A_ext,1));
result = reshape(A_ext(ind_lin), 3, []);
For Matlab R2016b or newer, you can simplify the third line:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = ind_max + (0:2).' + (0:size(A_ext,2)-1)*size(A_ext,1);
result = reshape(A_ext(ind_lin), 3, []);
Here is another solution. This is similar to HansHirse's answer, with two improvements:
Slightly more elegantly handles the modular indexing
Is more flexible for specifying which neighbours your want
Code:
% Input
A = [31 85 36 71 51;
12 33 74 39 12;
67 11 13 14 18;
35 36 84 33 57];
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc] = size(A);
% Get max indices
[~,idx] = max( A, [], 1 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + p.' - 1, nr ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
B = A(idx + (0:nr:nr*nc-1));
You can use the Image Processing Toolbox to generate the result, though is less efficient than other solutions.
[~,idx] = max(A, [], 1);
d = imdilate( idx == (1:size(A,1) ).', [1;1;1], 'full');
p = padarray(A, 1, 'circular');
Desired_Matrix = reshape(p(d), 3, []);
just for your information, here is the generalized form for the 3D-Case:
A = zeros(3,5,5);
for id = 1: 20
A(:,:,id) = id;
if id == 10
A(:,:,id) = 100;
end
end
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc, nz] = size(A);
% Get max indices
[~,idx] = max( A, [], 3 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + reshape(p,1,1,3) - 1, nz ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
INDICES = ((idx-1) * (nr*nc)+1 )+ reshape(0:1:nc*nr-1,nr,nc);
B = A(INDICES);

Find the indexes of duplicate values and replace their indexes in Matlab

I have a matrix B 1631x5.Download matfile
Columns 2 and 3 represent X and Y coordinates respectively.
I want to identify the indexes where B(i+k,2)==B(i+j,2)&B(i+k,3)==B(i+j,3). Note that there can be more than one duplicate.
Below is the script of identifying the duplicates:
%% X coordinate
[~, indX] = unique(B(:, 2), 'rows');
% duplicate indices
duplicate_indX = setdiff(1:size(B, 1), indX);
% duplicate values
duplicate_valueX = B(duplicate_indX, 2);
%% Y coordinate
[~, indY] = unique(B(:, 3), 'rows');
% duplicate indices
duplicate_indY = setdiff(1:size(B, 1), indY);
% duplicate values
duplicate_valueY = B(duplicate_indY, 3);
%% Both coordinates
duplicate_ind=intersect(duplicate_indX,duplicate_indY);
duplicate_value = B(duplicate_ind, 2:3);
When the code is executed, we get 2 matrix: duplicate_ind(1x149) and duplicate_value(149x2).
Let's consider first 4 values of duplicate_ind as an example:
>> duplicate_ind(1:4)
ans =
61 77 106 111
The corresponding values for these indexes are
>> duplicate_value(1:4,:)
ans =
355.3035 176.9755
364.7316 182.2644
354.4987 202.1553
350.5895 226.7602
Now I can find the original and the duplicate:
find(B(:,2)==duplicate_value(1,1))
ans =
1
61
>> find(B(:,2)==duplicate_value(2,1))
ans =
57
77
In this case, the index of the original value is 1 and the index of duplicate is 61. In other case: original: 57 and duplicate:77.
Now, I want to replace the indexes of the duplicates by the original ones. In our case 61 will be replaced by 1 (and 77 will be replaced by 57). Considering above, I want to build a matrix which has the size 1631x3 (must have the same number of rows as matrix B), and looks like following:
1 1 2
2 2 3
3 3 4
...
57 57 58
...
61 1 62
...
77 57 78
78 78 79
...
Solved:
%% X coordinate
[~, indX] = unique(B(:, 2), 'rows');
% duplicate indices
duplicate_indX = setdiff(1:size(B, 1), indX);
% duplicate values
duplicate_valueX = B(duplicate_indX, 2);
%% Y coordinate
[~, indY] = unique(B(:, 3), 'rows');
% duplicate indices
duplicate_indY = setdiff(1:size(B, 1), indY);
% duplicate values
duplicate_valueY = B(duplicate_indY, 3);
%% Both coordinates
duplicate_ind=intersect(duplicate_indX,duplicate_indY);
duplicate_value = B(duplicate_ind, 2:3);
indexes=zeros(3,size(duplicate_value,1));
for i=1:size(duplicate_value,1)
if size(find(B(:,2)==duplicate_value(i,1)&B(:,3)==duplicate_value(i,2)),1)==2
indexes(1:2,i)=find(B(:,2)==duplicate_value(i,1)&B(:,3)==duplicate_value(i,2));
end
if size(find(B(:,2)==duplicate_value(i,1)&B(:,3)==duplicate_value(i,2)),1)==3
indexes(1:3,i)=find(B(:,2)==duplicate_value(i,1)&B(:,3)==duplicate_value(i,2));
end
end
for j=1:size(B,1)-1
lines(j,1:2)=j;
lines(j,3)=j+1;
end
for j=1:size(lines,1)
for i=1:1:size(indexes,2)
if indexes(3,i)==0
if lines(j,2)==indexes(2,i)
lines(j,2)=indexes(1,i);
end
if lines(j,3)==indexes(2,i)
lines(j,3)=indexes(1,i);
end
end
if indexes(3,i)~=0
if lines(j,2)==indexes(3,i)
lines(j,2)=indexes(1,i);
end
if lines(j,3)==indexes(3,i)
lines(j,3)=indexes(1,i);
end
if lines(j,2)==indexes(2,i)
lines(j,2)=indexes(1,i);
end
if lines(j,3)==indexes(2,i)
lines(j,3)=indexes(1,i);
end
end
end
end

Fast way to find the neighboor of pixel

I am programming for task that finds the neighbor of a given pixel x in image Dthat can formula as:
The formula shown pixels y which satisfy the distance to pixel x is 1, then they are neighbor of pixel x. This is my matlab code. However, it still takes long time to find. Could you suggest a faster way to do it. Thank you so much
%-- Find the neighborhood of one pixel
% x is pixel coordinate
% nrow, ncol is size of image
function N = find_neighbor(x,nrow,ncol)
i = x(1);
j = x(2);
I1 = i+1;
if (I1 > nrow)
I1 = nrow;
end
I2 = i-1;
if (I2 < 1)
I2 = 1;
end
J1 = j+1;
if (J1 > ncol)
J1 = ncol;
end
J2 = j-1;
if (J2 < 1)
J2 = 1;
end
N = [I1, I2, i, i; j, j, J1, J2];
For example: ncol=128; nrow=128; x =[30;110] then output
N =31 29 30 30; 110 110 111 109]
For calling the function in loop
x=[30 31 32 33; 110 123 122 124]
for i=1:length(x)
N = find_neighbor(x(:,i),nrow,ncol);
end
Here's a vectorized approach using bsxfun:
% define four neighbors as coordinate differences
d = [-1 0 ; 1 0 ; 0 -1 ; 0 1]';
% add to pixel coordinates
N = bsxfun(#plus, x, permute(d, [1 3 2]));
% make one long list for the neighbors of all pixels together
N = reshape(N, 2, []);
% identify out-of-bounds coordinates
ind = (N(1, :) < 1) | (N(1, :) > nrow) | (N(2, :) < 1) | (N(2, :) > ncol);
% and remove those "neighbors"
N(:, ind) = [];
The permute is there to move the "dimension" of four different neighbors into the 3rd array index. This way, using bsxfun, we get the combination of every pair of original pixel coordinates with every pair of relative neighbor coordinates. The out-of-bounds check assumes that nrow belongs to the first coordinate and ncol to the second coordinate.
With
ncol=128;
nrow=128;
x = [30 31 32 33; 110 123 122 124];
the result is
N =
29 30 31 32 31 32 33 34 30 31 32 33 30 31 32 33
110 123 122 124 110 123 122 124 109 122 121 123 111 124 123 125
Different neighbors of different pixels can end up to be the same pixel, so there can be duplicates in the list. If you only want each resulting pixel once, use
% remove duplicates?
N = unique(N', 'rows')';
to get
N =
29 30 30 30 31 31 31 32 32 32 33 33 33 34
110 109 111 123 110 122 124 121 123 124 122 123 125 124
Matlab's performance is horrible when calling small functions many time. The Matlab approach is to do vectorize as much as possible. A vectorized version of your code:
function N = find_neighbor(x,nrow,ncol)
N = [min(x(1,:)+1,nrow), max(x(1,:)-1,1), x(1,:), x(1,:); x(2,:), x(2,:),min(x(2,:)+1,ncol), max(x(2,:)-1,1)];
end
and usage
x=[30 31 32 33; 110 123 122 124]
N = find_neighbor(x,nrow,ncol);
BTW, for pixels on the border , your solution always gives 4 neighbors. This is wrong. the neighbors of (1,1) for examples should be only (2,1) and (1,2), while you add two extra (1,1).
The solution to this is quite simple - delete all neighbors that are outside the image
function N = find_neighbor(x,nrow,ncol)
N = [x(1,:)+1, x(1,:)-1, x(1,:), x(1,:); x(2,:), x(2,:),x(2,:)+1, x(2,:)-1];
N(:,N(1,:)<1 | N(1,:)> nrow | N(2,:)<1 | N(2,:)>ncol)=[];
end

MATLAB: get equally spaced entries of Vector

How would it be possible to get equally spaced entries from a Vector in MATLAB, e.g. I have the following vector:
0 25 50 75 100 125 150
When I chose 2 I want to get:
0 150
When I chose 3 I want to get:
0 75 150
When I chose 4 I want to get:
0 50 100 150
Chosing 1, 5 or 6 shouldn't work and I even need a check for an if-clause for that but I can't figure this out.
You can generate the indices with linspace and round:
vector = [0 25 50 75 100 125 150]; % // data
n = 4; % // desired number of equally spaced entries
ind = round(linspace(1,length(vector),n)); %// get rounded equally spaced indices
result = vector(ind) % // apply indices to the data vector
If you want to force that values 1, 5 or 6 for n don't work: test if n-1 divides length(vector)-1). If you do that you don't need round to obtain the indices:
if rem((length(vector)-1)/(n-1), 1) ~= 0
error('Value of n not allowed')
end
ind = linspace(1,length(vector),n); %// get equally spaced indices
result = vector(ind) % // apply indices to the data vector
Use linspace:
>> a
a =
0 25 50 75 100 125 150
>> a(linspace(1,length(a),4))
ans =
0 50 100 150
>> a(linspace(1,length(a),3))
ans =
0 75 150
>> a(linspace(1,length(a),2))
ans =
0 150
Note that, except for 1, the invalid values raise an error:
>> a(linspace(1,length(a),5))
error: subscript indices must be either positive integers or logicals
>> a(linspace(1,length(a),6))
error: subscript indices must be either positive integers or logicals

Matrix "Zigzag" Reordering

I have an NxM matrix in MATLAB that I would like to reorder in similar fashion to the way JPEG reorders its subblock pixels:
(image from Wikipedia)
I would like the algorithm to be generic such that I can pass in a 2D matrix with any dimensions. I am a C++ programmer by trade and am very tempted to write an old school loop to accomplish this, but I suspect there is a better way to do it in MATLAB.
I'd be rather want an algorithm that worked on an NxN matrix and go from there.
Example:
1 2 3
4 5 6 --> 1 2 4 7 5 3 6 8 9
7 8 9
Consider the code:
M = randi(100, [3 4]); %# input matrix
ind = reshape(1:numel(M), size(M)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals
ind(:,1:2:end) = flipud( ind(:,1:2:end) ); %# reverse order of odd columns
ind(ind==0) = []; %# keep non-zero indices
M(ind) %# get elements in zigzag order
An example with a 4x4 matrix:
» M
M =
17 35 26 96
12 59 51 55
50 23 70 14
96 76 90 15
» M(ind)
ans =
17 35 12 50 59 26 96 51 23 96 76 70 55 14 90 15
and an example with a non-square matrix:
M =
69 9 16 100
75 23 83 8
46 92 54 45
ans =
69 9 75 46 23 16 100 83 92 54 8 45
This approach is pretty fast:
X = randn(500,2000); %// example input matrix
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).'; %'// output row vector
Benchmarking
The following code compares running time with that of Amro's excellent answer, using timeit. It tests different combinations of matrix size (number of entries) and matrix shape (number of rows to number of columns ratio).
%// Amro's approach
function y = zigzag_Amro(M)
ind = reshape(1:numel(M), size(M));
ind = fliplr( spdiags( fliplr(ind) ) );
ind(:,1:2:end) = flipud( ind(:,1:2:end) );
ind(ind==0) = [];
y = M(ind);
%// Luis' approach
function y = zigzag_Luis(X)
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).';
%// Benchmarking code:
S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
%// f = [0.5 2]; %// plotted with '--'
%// f = [2 0.5]; %// plotted with ':'
t_Amro = NaN(size(S));
t_Luis = NaN(size(S));
for n = 1:numel(S)
X = rand(f(1)*S(n), f(2)*S(n));
f_Amro = #() zigzag_Amro(X);
f_Luis = #() zigzag_Luis(X);
t_Amro(n) = timeit(f_Amro);
t_Luis(n) = timeit(f_Luis);
end
loglog(S.^2*prod(f), t_Amro, '.b-');
hold on
loglog(S.^2*prod(f), t_Luis, '.r-');
xlabel('number of matrix entries')
ylabel('time')
The figure below has been obtained with Matlab R2014b on Windows 7 64 bits. Results in R2010b are very similar. It is seen that the new approach reduces running time by a factor between 2.5 (for small matrices) and 1.4 (for large matrices). Results are seen to be almost insensitive to matrix shape, given a total number of entries.
Here's a non-loop solution zig_zag.m. It looks ugly but it works!:
function [M,index] = zig_zag(M)
[r,c] = size(M);
checker = rem(hankel(1:r,r-1+(1:c)),2);
[rEven,cEven] = find(checker);
[cOdd,rOdd] = find(~checker.'); %'#
rTotal = [rEven; rOdd];
cTotal = [cEven; cOdd];
[junk,sortIndex] = sort(rTotal+cTotal);
rSort = rTotal(sortIndex);
cSort = cTotal(sortIndex);
index = sub2ind([r c],rSort,cSort);
M = M(index);
end
And a test matrix:
>> M = [magic(4) zeros(4,1)];
M =
16 2 3 13 0
5 11 10 8 0
9 7 6 12 0
4 14 15 1 0
>> newM = zig_zag(M) %# Zig-zag sampled elements
newM =
16
2
5
9
11
3
13
10
7
4
14
6
8
0
0
12
15
1
0
0
Here's a way how to do this. Basically, your array is a hankel matrix plus vectors of 1:m, where m is the number of elements in each diagonal. Maybe someone else has a neat idea on how to create the diagonal arrays that have to be added to the flipped hankel array without a loop.
I think this should be generalizeable to a non-square array.
% for a 3x3 array
n=3;
numElementsPerDiagonal = [1:n,n-1:-1:1];
hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
% loop through the hankel array and add numbers counting either up or down
% if they are even or odd
for d = 1:(2*n-1)
if floor(d/2)==d/2
% even, count down
array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
else
% odd, count up
array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
end
end
% now flip to get the result
indexMatrix = fliplr(array2add)
result =
1 2 6
3 5 7
4 8 9
Afterward, you just call reshape(image(indexMatrix),[],1) to get the vector of reordered elements.
EDIT
Ok, from your comment it looks like you need to use sort like Marc suggested.
indexMatrixT = indexMatrix'; % ' SO formatting
[dummy,sortedIdx] = sort(indexMatrixT(:));
sortedIdx =
1 2 4 7 5 3 6 8 9
Note that you'd need to transpose your input matrix first before you index, because Matlab counts first down, then right.
Assuming X to be the input 2D matrix and that is square or landscape-shaped, this seems to be pretty efficient -
[m,n] = size(X);
nlim = m*n;
n = n+mod(n-m,2);
mask = bsxfun(#le,[1:m]',[n:-1:1]);
start_vec = m:m-1:m*(m-1)+1;
a = bsxfun(#plus,start_vec',[0:n-1]*m);
offset_startcol = 2- mod(m+1,2);
[~,idx] = min(mask,[],1);
idx = idx - 1;
idx(idx==0) = m;
end_ind = a([0:n-1]*m + idx);
offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
a(:,offset_startcol:2:end) = bsxfun(#minus,offsets,a(:,offset_startcol:2:end));
out = a(mask);
out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
result = X([out2 ; out(out<=nlim)]);
Quick runtime tests against Luis's approach -
Datasize: 500 x 2000
------------------------------------- With Proposed Approach
Elapsed time is 0.037145 seconds.
------------------------------------- With Luis Approach
Elapsed time is 0.045900 seconds.
Datasize: 5000 x 20000
------------------------------------- With Proposed Approach
Elapsed time is 3.947325 seconds.
------------------------------------- With Luis Approach
Elapsed time is 6.370463 seconds.
Let's assume for a moment that you have a 2-D matrix that's the same size as your image specifying the correct index. Call this array idx; then the matlab commands to reorder your image would be
[~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
reorderedim = im(I);
I don't see an obvious solution to generate idx without using for loops or recursion, but I'll think some more.