How to find neighbors in 4D array in MATLAB? - matlab

I am a bit confused and would greatly appreciate some help.
I have read many posts about finding neighboring pixels, with this being extremely helpful:
http://blogs.mathworks.com/steve/2008/02/25/neighbor-indexing-2/
However I have trouble applying it on a 4D matrix (A) with size(A)=[8 340 340 15]. It represents 8 groups of 3D images (15 slices each) of which I want to get the neighbors.
I am not sure which size to use in order to calculate the offsets. This is the code I tried, but I think it is not working because the offsets should be adapted for 4 dimensions? How can I do it without a loop?
%A is a 4D matrix with 0 or 1 values
Aidx = find(A);
% loop here?
[~,M,~,~] =size(A);
neighbor_offsets = [-1, M, 1, -M]';
neighbors_idx = bsxfun(#plus, Aidx', neighbor_offsets(:));
neighbors = B(neighbors_idx);
Thanks,
ziggy

Have you considered using convn?
msk = [0 1 0; 1 0 1; 0 1 0];
msk4d = permute( msk, [3 1 2 4] ); % make it 1-3-3-1 mask
neighbors_idx = find( convn( A, msk4d, 'same' ) > 0 );
You might find conndef useful for defining the basic msk in a general way.

Not sure if I've understood your question but what about this sort of approach:
if you matrix is 1D:
M = rand(10,1);
N = M(k-1:k+1); %//immediate neighbours of k
However this could error if k is at the boundary. This is easy to fix using max and min:
N = M(max(k-1,1):min(k+1,size(M,1))
Now lets add a dimenion:
M = rand(10,10);
N = M(max(k1-1,1):min(k1+1,size(M,1), max(k2-1,1):min(k2+1,size(M,2))
That was easy, all you had to do was repeat the same index making the minor change of using size(M,2) for the boundary (and also I changed k to k1 and k2, you might find using an array for k instead of separate k1 and k2 variables works better i.e. k(1) and k(2))
OK so now lets skip to 4 dimensions:
M = rand(10,10,10,10);
N = M(max(k(1)-1,1):min(k(1)+1,size(M,1)), ...
max(k(2)-1,1):min(k(2)+1,size(M,2)), ...
max(k(3)-1,1):min(k(3)+1,size(M,3)), ...
max(k(4)-1,1):min(k(4)+1,size(M,4))); %// Also you can replace all the `size(M,i)` with `end` if you like
I know you said you didn't want a loop, but what about a really short loop just to refactor a bit and also make it generalized:
n=ndims(M);
ind{n} = 0;
for dim = 1:n
ind{dim} = max(k(dim)-1,1):min(k(dim)+1,size(M,dim));
end
N = M(ind{:});

Here's how to get the neighbors along the second dimension
sz = size( A );
ndims = numel(sz); % number of dimensions
[d{1:ndims}] = ind2sub( sz, find( A ) );
alongD = 2; % work along this dim
np = d{alongD} + 1;
sel = np <= sz( alongD ); % discard neighbors that fall outside image boundary
nm = d{alongD} - 1;
sel = sel & nm > 0; % discard neighbors that fall outside image boundary
d = cellfun( #(x) x(sel), d, 'uni', 0 );
neighbors = cat( 1, ...
ind2sub( sz, d{1:alongD-1}, np(sel), d{alongD+1:end} ),...
ind2sub( sz, d{1:alongD-1}, nm(sel), d{alongD+1:end} ) );

Related

Smarter way to generate a matrix of zeros and ones in Matlab

I would like to generate all the possible adjacency matrices (zero diagonale) of an undirected graph of n nodes.
For example, with no relabeling for n=3 we get 23(3-1)/2 = 8 possible network configurations (or adjacency matrices).
One solution that works for n = 3 (and which I think is quite stupid) would be the following:
n = 3;
A = [];
for k = 0:1
for j = 0:1
for i = 0:1
m = [0 , i , j ; i , 0 , k ; j , k , 0 ];
A = [A, m];
end
end
end
Also I though of the following which seems to be faster but something is wrong with my indexing since 2 matrices are missing:
n = 3
C = [];
E = [];
A = zeros(n);
for i = 1:n
for j = i+1:n
A(i,j) = 1;
A(j,i) = 1;
C = [C,A];
end
end
B = ones(n);
B = B- diag(diag(ones(n)));
for i = 1:n
for j = i+1:n
B(i,j) = 0;
B(j,i) = 0;
E = [E,B];
end
end
D = [C,E]
Is there a faster way of doing this?
I would definitely generate the off-diagonal elements of the adjacency matrices with binary encoding:
n = 4; %// number of nodes
m = n*(n-1)/2;
offdiags = dec2bin(0:2^m-1,m)-48; %//every 2^m-1 possible configurations
If you have the Statistics and Machine Learning Toolbox, then squareform will easily create the matrices for you, one by one:
%// this is basically a for loop
tmpcell = arrayfun(#(k) squareform(offdiags(k,:)),1:size(offdiags,1),...
'uniformoutput',false);
A = cat(2,tmpcell{:}); %// concatenate the matrices in tmpcell
Although I'd consider concatenating along dimension 3, then you can see each matrix individually and conveniently.
Alternatively, you can do the array synthesis yourself in a vectorized way, it's probably even quicker (at the cost of more memory):
A = zeros(n,n,2^m);
%// lazy person's indexing scheme:
[ind_i,ind_j,ind_k] = meshgrid(1:n,1:n,1:2^m);
A(ind_i>ind_j) = offdiags.'; %'// watch out for the transpose
%// copy to upper diagonal:
A = A + permute(A,[2 1 3]); %// n x n x 2^m matrix
%// reshape to n*[] matrix if you wish
A = reshape(A,n,[]); %// n x (n*2^m) matrix

How to get a 3D-matrix or cell array efficiently by using vectorized code?

Here is what I want, a 3-D matrix:
K = 2:2.5:10;
den = zeros(1,4,4);
for i = 1:1:4
den(:,:,i) = [1, 5, K(i)-6, K(i)];
end
Or, a cell array is also acceptable:
K = 2:2.5:10;
for i = 1:1:4
den{i} = [1, 5, K(i)-6, K(i)];
end
But I want to know if there is a more efficient way of doing this using vectorized code like:
K = 2:2.5:10;
den = [1, 5, K-6, K];
I know the last code will not get what I wanted. But, like I can use:
v = [1 2 3];
v2 = v.^2;
instead of:
v = [1 2 3];
for i = 1:length(v)
v(i) = v(i)^2;
end
to get the matrix I want. Is there a similar way of doing this so that I can get the 3-D matrix or cell array I mentioned at the beginning more efficiently?
You need to "broadcast" the scalar values in columns so they are of the same length as your K vector. MATLAB does not do this broadcasting automatically, so you need to repeat the scalars and create vectors of the appropriate size. You can use repmat() for this.
K = 2:2.5:10;
%% // transpose K to a column vector:
K = transpose(K);
%% // helper function that calls repmat:
f = #(v) repmat(v, length(K), 1);
%% // your matrix:
den = [f(1) f(5) K-6 K];
This should be more optimized for speed but requires a bit more intermediary memory than the loop does.
Just use reshape with a 1*3 size:
den = reshape([ones(1,length(K));ones(1,length(K))*5; K-6; K],[1 4 length(K)]);
I think the used extra memory by reshape should be low and constant (dependent only on the length of the vector of new sizes).
You can use the classic line equation y=a*x+b, extended to the matrix form:
k = 2:2.5:10 ;
fa = [0 0 1 1].' ; %' // "a" coefficients
fb = [1 5 -6 0].' ; %' // "b" coefficients
d(1,:,:) = fa*k + fb*ones(1,4) ;
The above is better for clarity, but if you're not bothered you can also pack everything in one line:
d(1,:,:) = [0 0 1 1].' * (2:2.5:10) + [1 5 -6 0].' * ones(1,4) ;
If you need to re-use the principle for many different values of k, then you can use an anonymous function to help:
fden = #(k) [0 0 1 1].' * k + [1 5 -6 0].' * ones(1,4) ; %// define anonymous function
k = 2:2.5:10 ;
d(1,:,:) = fden(k) ; %// use it for any value of "k"

How to write this matrix in matlab,

I want to write this matrix in matlab,
s=[0 ..... 0
B 0 .... 0
AB B .... 0
. . .
. . .
. . . 0 ....
A^(n-1)*B ... AB B ]
I have tried this below code but giving error,
N = 50;
A=[2 3;4 1];
B=[3 ;2];
[nx,ny] = size(A);
s(nx,ny,N) = 0;
for n=1:1:N
s(:,:,n)=A.^n;
end
s_x=cat(3, eye(size(A)) ,s);
for ii=1:1:N-1
su(:,:,ii)=(A.^ii).*B ;
end
z= zeros(1,60,1);
su1 = [z;su] ;
s_u=repmat(su1,N);
seems like the concatenation of matrix is not being done.
I am a beginner so having serious troubles,please help.
Use cell arrays and the answer to your previous question
A = [2 3; 4 1];
B = [3 ;2 ];
N = 60;
[cs{1:(N+1),1:N}] = deal( zeros(size(B)) ); %// allocate space, setting top triangle to zero
%// work on diagonals
x = B;
for ii=2:(N+1)
[cs{ii:(N+2):((N+1)*(N+2-ii))}] = deal(x); %//deal to diagonal
x = A*x;
end
s = cell2mat(cs); %// convert cells to a single matrix
For more information you can read about deal and cell2mat.
Important note about the difference between matrix operations and element-wise operations
In your question (and in your previous one) you confuse between matrix power: A^2 and element-wise operation A.^2:
matrix power A^2 = [16 9;12 13] is the matrix product of A*A
element-wise power A.^2 takes each element separately and computes its square: A.^2 = [4 9; 16 1]
In yor question you ask about matrix product A*b, but the code you write is A.*b which is an element-by-element product. This gives you an error since the size of A and the size of b are not the same.
I often find that Matlab gives itself to a coding approach of "write what it says in the equation". That also leads to code that is easy to read...
A = [2 3; 4 1];
B = [3; 2];
Q = 4;
%// don't need to...
s = [];
%// ...but better to pre-allocate s for performance
s = zeros((Q+1)*2, Q);
X = B;
for m = 2:Q+1
for n = m:Q+1
s(n*2+(-1:0), n-m+1) = X;
end
X = A * X;
end

Replicate vector and shift each copy by 1 row down without for-loop

I would like replicate a vector N times to create a matrix with each copy shifted 1 row down. See image (first column is the vector 1 to 5). It would be great if this can be achieved without using for loop.
So far was able to to do this repmat(my_vector, 1, 5) to create an N x 5 matrix.
You can do it with toeplitz and tril;
a = [1 2 3 4 5]
out = tril( toeplitz(a) )
or
out = toeplitz(a, a*0)
%// out = toeplitz(a, zeros(size(a)) ) %// for large arrays
or if you don't mind some happy flipping:
out = flipud( hankel( flipud(a(:)) ) )
Solution Code
This seems to be a fast approach based on repmat and bsxfun as the benchmarks listed in the next section might convince us -
%// Concatenate one zero at the end of a column vector version of the input vector.
%// Then, replicate the whole vector along columns to have a 2D matrix.
%// Then "progressively" set elements from each column as zeros corresponding
%// to the starting zeros of the desired output.
val = repmat([A(:);0],1,N).*bsxfun(#le,[1:N+1]',N:-1:1); %//'
%// Chop-off at N x N length and reshape to have the final output
out = reshape(val(1:N*N),N,N);
Benchmarking
In this section we will cover runtime benchmarking for the various approaches listed on this page for the stated problem.
Benchmarking Code -
%datasizes = [10 20 50 70 100 200 500 700 1000]; %// Set -1
datasizes = [1000 2000 5000 7000 10000]; %// Set -2
fcns = {'repvecshiftdown_flipud_hankel','repvecshiftdown_toeplitz',...
'repvecshiftdown_repmat_bsxfun','repvecshiftdown_tril_toeplitz'};%//approaches
tsec = zeros(numel(fcns),numel(datasizes));
for k1 = 1:numel(datasizes),
A = randi(9,1,datasizes(k1)); %// Creare random input vector
for k2 = 1:numel(fcns), %// Time approaches
tsec(k2,k1) = timeit(#() feval(fcns{k2}, A), 1);
fprintf('\tFunction: %s (%3.2f sec)\n',fcns{k2},tsec(k2,k1));
end
end
figure; %% Plot Runtimes
plot(datasizes,tsec(1,:),'-rx'), hold on
plot(datasizes,tsec(2,:),'-bo')
plot(datasizes,tsec(3,:),'-k+')
plot(datasizes,tsec(4,:),'-g.')
set(gca,'xgrid','on'),set(gca,'ygrid','on'),
xlabel('Datasize (# elements)'), ylabel('Runtime (s)')
legend(upper(strrep(fcns,'_',' '))),title('Runtime')
Associated function codes (all approaches) -
function out = repvecshiftdown_repmat_bsxfun(A)
N = numel(A);
val = repmat([A(:);0],1,N).*bsxfun(#le,[1:N+1]',[N:-1:1]); %//'
out = reshape(val(1:N*N),N,N);
return;
function out = repvecshiftdown_tril_toeplitz(A)
out = tril( toeplitz(A) );
return;
function out = repvecshiftdown_toeplitz(A)
out = toeplitz(A, zeros(size(A)));
return;
function out = repvecshiftdown_flipud_hankel(A)
out = flipud( hankel( flipud(A(:)) ) );
return;
Runtime plots -
Set #1 [From 10 till 1000 datasizes]:
Set #2 [From 1000 till 10000 datasizes]:

Vectorizing sums of different diagonals in a matrix

I want to vectorize the following MATLAB code. I think it must be simple but I'm finding it confusing nevertheless.
r = some constant less than m or n
[m,n] = size(C);
S = zeros(m-r,n-r);
for i=1:m-r+1
for j=1:n-r+1
S(i,j) = sum(diag(C(i:i+r-1,j:j+r-1)));
end
end
The code calculates a table of scores, S, for a dynamic programming algorithm, from another score table, C.
The diagonal summing is to generate scores for individual pieces of the data used to generate C, for all possible pieces (of size r).
Thanks in advance for any answers! Sorry if this one should be obvious...
Note
The built-in conv2 turned out to be faster than convnfft, because my eye(r) is quite small ( 5 <= r <= 20 ). convnfft.m states that r should be > 20 for any benefit to manifest.
If I understand correctly, you're trying to calculate the diagonal sum of every subarray of C, where you have removed the last row and column of C (if you should not remove the row/col, you need to loop to m-r+1, and you need to pass the entire array C to the function in my solution below).
You can do this operation via a convolution, like so:
S = conv2(C(1:end-1,1:end-1),eye(r),'valid');
If C and r are large, you may want to have a look at CONVNFFT from the Matlab File Exchange to speed up calculations.
Based on the idea of JS, and as Jonas pointed out in the comments, this can be done in two lines using IM2COL with some array manipulation:
B = im2col(C, [r r], 'sliding');
S = reshape( sum(B(1:r+1:end,:)), size(C)-r+1 );
Basically B contains the elements of all sliding blocks of size r-by-r over the matrix C. Then we take the elements on the diagonal of each of these blocks B(1:r+1:end,:), compute their sum, and reshape the result to the expected size.
Comparing this to the convolution-based solution by Jonas, this does not perform any matrix multiplication, only indexing...
I would think you might need to rearrange C into a 3D matrix before summing it along one of the dimensions. I'll post with an answer shortly.
EDIT
I didn't manage to find a way to vectorise it cleanly, but I did find the function accumarray, which might be of some help. I'll look at it in more detail when I am home.
EDIT#2
Found a simpler solution by using linear indexing, but this could be memory-intensive.
At C(1,1), the indexes we want to sum are 1+[0, m+1, 2*m+2, 3*m+3, 4*m+4, ... ], or (0:r-1)+(0:m:(r-1)*m)
sum_ind = (0:r-1)+(0:m:(r-1)*m);
create S_offset, an (m-r) by (n-r) by r matrix, such that S_offset(:,:,1) = 0, S_offset(:,:,2) = m+1, S_offset(:,:,3) = 2*m+2, and so on.
S_offset = permute(repmat( sum_ind, [m-r, 1, n-r] ), [1, 3, 2]);
create S_base, a matrix of base array addresses from which the offset will be calculated.
S_base = reshape(1:m*n,[m n]);
S_base = repmat(S_base(1:m-r,1:n-r), [1, 1, r]);
Finally, use S_base+S_offset to address the values of C.
S = sum(C(S_base+S_offset), 3);
You can, of course, use bsxfun and other methods to make it more efficient; here I chose to lay it out for clarity. I have yet to benchmark this to see how it compares with the double-loop method though; I need to head home for dinner first!
Is this what you're looking for? This function adds the diagonals and puts them into a vector similar to how the function 'sum' adds up all of the columns in a matrix and puts them into a vector.
function [diagSum] = diagSumCalc(squareMatrix, LLUR0_ULLR1)
%
% Input: squareMatrix: A square matrix.
% LLUR0_ULLR1: LowerLeft to UpperRight addition = 0
% UpperLeft to LowerRight addition = 1
%
% Output: diagSum: A vector of the sum of the diagnols of the matrix.
%
% Example:
%
% >> squareMatrix = [1 2 3;
% 4 5 6;
% 7 8 9];
%
% >> diagSum = diagSumCalc(squareMatrix, 0);
%
% diagSum =
%
% 1 6 15 14 9
%
% >> diagSum = diagSumCalc(squareMatrix, 1);
%
% diagSum =
%
% 7 12 15 8 3
%
% Written by M. Phillips
% Oct. 16th, 2013
% MIT Open Source Copywrite
% Contact mphillips#hmc.edu fmi.
%
if (nargin < 2)
disp('Error on input. Needs two inputs.');
return;
end
if (LLUR0_ULLR1 ~= 0 && LLUR0_ULLR1~= 1)
disp('Error on input. Only accepts 0 or 1 as input for second condition.');
return;
end
[M, N] = size(squareMatrix);
if (M ~= N)
disp('Error on input. Only accepts a square matrix as input.');
return;
end
diagSum = zeros(1, M+N-1);
if LLUR0_ULLR1 == 1
squareMatrix = rot90(squareMatrix, -1);
end
for i = 1:length(diagSum)
if i <= M
countUp = 1;
countDown = i;
while countDown ~= 0
diagSum(i) = squareMatrix(countUp, countDown) + diagSum(i);
countUp = countUp+1;
countDown = countDown-1;
end
end
if i > M
countUp = i-M+1;
countDown = M;
while countUp ~= M+1
diagSum(i) = squareMatrix(countUp, countDown) + diagSum(i);
countUp = countUp+1;
countDown = countDown-1;
end
end
end
Cheers