Avoiding sub2ind and ind2sub - matlab

I need to access several indices around a certain point in 3D.
For example, for point (x1,y1,z1) I need to get all the indices of its 3x3x3 neighborhood such that (x1,y1,z1) is centered. For neighborhood of size 3, I do it with
[x,y,z] = meshgrid(-1:1,-1:1,-1:1);
x_neighbors = bsxfun(#plus,x,x1);
y_neighbors = bsxfun(#plus,y,y1);
z_neighbors = bsxfun(#plus,z,z1);
Here, I center x1,y1,z1 to (0,0,0) by adding the distances from (x1,y1,z1) to any point in the 3x3x3 box.
that gives me the coordinates of (x1,y1,z1) 3x3x3 neighborhood. I then need to turn them into linear indices so I can access them:
lin_ind = sub2ind(size(volume),y_neighbors,x_neighbors,z_neighbors);
that is costly in what I do.
My question is, how to avoid sub2ind. If inx is the linear index of (x1,y1,z1),
inx = sub2ind(size(volume),y1,x1,z1);
how can I find the 3x3x3 neighborhood of the linear index by adding or subtracting or any other simple operation of inx?

As long as you know the dimensions of your 3D array, you can compute the linear offsets of all the elements of the 3x3x3 neighborhood. To illustrate this, consider a 2D example of a 4x5 matrix. The linear indices look like this:
1 5 9 13 17
2 6 10 14 18
3 7 11 15 19
4 8 12 16 20
The 3x3 neighborhood of 10 is [5 6 7 9 10 11 13 14 15]. The 3x3 neighborhood of 15 is [10 11 12 14 15 16 18 19 20]. If we subtract off the index of the central element, in both cases we get [-5 -4 -3 -1 0 1 3 4 5]. More generally, for MxN matrix we will have [-M-1 -M -M+1 -1 0 1 M-1 M M+1], or [(-M+[-1 0 1]) -1 0 1 (M+[-1 0 1])].
Generalizing to three dimensions, if the array is MxNxP, the linear index offsets from the central element will be [(-M*N+[-M-1 -M -M+1 -1 0 1 M-1 M M+1]) [-M-1 -M -M+1 -1 0 1 M-1 M M+1] (M*N+[-M-1 -M -M+1 -1 0 1 M-1 M M+1])]. You can reshape this to 3x3x3 if you wish.
Note that this sort of indexing doesn't deal well with edges; if you want to find the neighbors of an element on the edge of the array you should probably pad the array on all sides first (thereby changing M, N, and P).

Just adding the (generalized) code to #nhowe answer:
This is an example for neighborhood of size 5X5X5, therefore r (the radius) is 2:
ns = 5;
r = 2;
[M,N,D] = size(vol);
rs = (1:ns)-(r+1);
% 2d generic coordinates:
neigh2d = bsxfun(#plus, M*rs,rs');
% 3d generic coordinates:
pages = (M*N)*rs;
pages = reshape(pages,1,1,length(pages));
neigh3d = bsxfun(#plus,neigh2d,pages);
to get any neighborhood of any linear index of vol, just add the linear index to neigh3d:
new_neigh = bxsfun(#plus,neigh3d, lin_index);

Related

Indexing all diagonals of a matrix in MATLAB

I am trying to index (not get) the diagonals of a matrix in matlab.
Say I have a matrix "M", that is n by n. Then I want to obtain all indeces of all possible diagonals in the matrix "M".
I know that the center diagonal is indexed by
M(1:(n+1):end)
and all the following diagonals above it are indexed as:
M((1+1*n):(n+1):end)
M((1+2*n):(n+1):end)...
M((1+n*n):(n+1):end)
Now I also want to get the diagonals below. I cannot for the life of me figure out how to however.
Reproducible example:
rng(1); % set seed
n = 4;
M = rand(n);
yielding
M =
0.562408 0.947364 0.655088 0.181702
0.960604 0.268834 0.469042 0.089167
0.578719 0.657845 0.516215 0.419000
0.226410 0.601666 0.169212 0.378740
where I would like to index the lower diagonals, e.g. the subdiagonal:
0.960604 0.657845 0.169212
That is, I don't need to get the diagonal by e.g. the diags function, but access the index (since I ultimately want to replace the matrix entries diagonal by diagonal).
As you already noted, you can use the diag function to get the main diagonal and other diagonals above or below the main diagonals,
M = magic(4) % Test data
M =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
diag(M, -1)
ans =
5
7
15
but you can not assign values to the diagonal with the diag function:
diag(M, -1) = [3; 2; 1]
Index in position 2 is invalid. Array indices must be positive integers or logical values.
Instead, we can use logical indexing by indexing the array M with a logical matrix of the same size. We can easily create this matrix using the diag function, by creating a diagonal matrix with ones on the specified diagonal:
diag(ones(1, 3), -1)
ans =
0 0 0 0
1 0 0 0
0 1 0 0
0 0 1 0
To use this matrix for logical indexing, we need to convert it from double to logical with the logical function.
M(logical(diag(ones(1, 3), -1)))
ans =
5
7
15
or assign new values to it with
M(logical(diag(ones(1, 3), -1))) = [99, 98, 97]
M =
16 2 3 13
99 11 10 8
9 98 6 12
4 14 97 1
There is a slightly more performant way of using diag to get indices to a diagonal:
n = 5; % matrix size
M = reshape(1:n*n,n,n); % matrix with linear indices
indices = diag(M, ii); % indices to diagonal ii
However, it is much easier to just compute the right indices directly. As discovered by OP, the upper diagonal elements are given by:
indices = (1+ii*n):(n+1):(n*n);
(Note that the parenthesis are not necessary, as the colon operator has the lowest precedence.)
The lower diagonal elements are given by:
indices = (1+ii):(n+1):((n-ii)*n);
Both series are identical for the main diagonal, where ii=0.
We can verify correctness of these calculations by using the first method:
n = 5; % matrix size
M = reshape(1:n*n,n,n); % matrix with linear indices
for ii=1:n-1
indices = (1+ii*n):(n+1):(n*n);
assert(isequal(indices, diag(M, ii).'))
indices = (1+ii):(n+1):((n-ii)*n);
assert(isequal(indices, diag(M, -ii).'))
end

how to select a submatrix from a matrix in Matlab [duplicate]

How to select a submatrix (not in any pattern) in Matlab? For example, for a matrix of size 10 by 10, how to select the submatrix consisting of intersection of the 1st 2nd and 9th rows and the 4th and 6th columns?
Thanks for any helpful answers!
TLDR: Short Answer
As for your question, suppose you have an arbitrary 10-by-10 matrix A. The simplest way to extract the desired sub-matrix would be with an index vector:
B = A([1 2 9], [4 6]);
Indexing in MATLAB
There's an interesting article in the official documentation that comprehensively explains indexing in MATLAB.
Basically, there are several ways to extract a subset of values, I'll summarize them for you:
1. Indexing Vectors
Indexing vectors indicate the indices of the element to be extracted. They can either contain a single index or several, like so:
A = [10 20 30 40 50 60 70 80 90]
%# Extracts the third and the ninth element
B = A([3 9]) %# B = [30 90]
Indexing vectors can be specified for each dimension separately, for instance:
A = [10 20 30; 40 50 60; 70 80 90];
%# Extract the first and third rows, and the first and second columns
B = A([1 3], [1 2]) %# B = [10 30; 40 60]
There are also two special subscripts: end and the colon (:):
end simply indicates the last index in that dimension.
The colon is just a short-hand notation for "1:end".
For example, instead of writing A([1 2 3], [2 3]), you can write A(:, 2:end). This is especially useful for large matrices.
2. Linear Indexing
Linear indexing treats any matrix as if it were a column vector by concatenating the columns into one column vector and assigning indices to the elements respectively. For instance, we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to compute b = A(2). The equivalent column vector is:
A = [10;
40;
70;
20;
50;
80;
30;
60;
90]
and thus b equals 40.
The special colon and end subscripts are also allowed, of course. For that reason, A(:) converts any matrix A into a column vector.
Linear indexing with matrix subscripts:
It is also possible to use another matrix for linear indexing. The subscript matrix is simply converted into a column vector, and used for linear indexing. The resulting matrix is, however always of the same dimensions as the subscript matrix.
For instance, if I = [1 3; 1 2], then A(I) is the same as writing reshape(A(I(:)), size(I)).
Converting from matrix subscripts to linear indices and vice versa:
For that you have sub2ind and ind2sub, respectively. For example, if you want to convert the subscripts [1, 3] in matrix A (corresponding to element 30) into a linear index, you can write sub2ind(size(A), 1, 3) (the result in this case should be 7, of course).
3. Logical Indexing
In logical indexing the subscripts are binary, where a logical 1 indicates that the corresponding element is selected, and 0 means it is not. The subscript vector must be either of the same dimensions as the original matrix or a vector with the same number of elements. For instance, if we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to extract A([1 3], [1 2]) using logical indexing, we can do either this:
Ir = logical([1 1 0]);
Ic = logical([1 0 1]);
B = A(Ir, Ic)
or this:
I = logical([1 0 1; 1 0 1; 0 0 0]);
B = A(I)
or this:
I = logical([1 1 0 0 0 0 1 1 0]);
B = A(I)
Note that in the latter two cases is a one-dimensional vector, and should be reshaped back into a matrix if necessary (for example, using reshape).
Let me explain with an example:
Let's define a 6x6 matrix
A = magic(6)
A =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
From this matrix you want the elements in rows 1, 2 and 5, and in the columns 4 and 6
B = A([1 2 5],[4 6])
B =
26 24
21 25
12 16
Hope this helps.
function f = sub(A,i,j)
[m,n] = size(A);
row = 1:m;
col = 1:n;
x = row;
x(i) = [];
y=col;
y(j) = [];
f= A(x,y);
Returns the matrix A, with the ith row and jth column removed.

How to select a submatrix (not in any particular pattern) in Matlab

How to select a submatrix (not in any pattern) in Matlab? For example, for a matrix of size 10 by 10, how to select the submatrix consisting of intersection of the 1st 2nd and 9th rows and the 4th and 6th columns?
Thanks for any helpful answers!
TLDR: Short Answer
As for your question, suppose you have an arbitrary 10-by-10 matrix A. The simplest way to extract the desired sub-matrix would be with an index vector:
B = A([1 2 9], [4 6]);
Indexing in MATLAB
There's an interesting article in the official documentation that comprehensively explains indexing in MATLAB.
Basically, there are several ways to extract a subset of values, I'll summarize them for you:
1. Indexing Vectors
Indexing vectors indicate the indices of the element to be extracted. They can either contain a single index or several, like so:
A = [10 20 30 40 50 60 70 80 90]
%# Extracts the third and the ninth element
B = A([3 9]) %# B = [30 90]
Indexing vectors can be specified for each dimension separately, for instance:
A = [10 20 30; 40 50 60; 70 80 90];
%# Extract the first and third rows, and the first and second columns
B = A([1 3], [1 2]) %# B = [10 30; 40 60]
There are also two special subscripts: end and the colon (:):
end simply indicates the last index in that dimension.
The colon is just a short-hand notation for "1:end".
For example, instead of writing A([1 2 3], [2 3]), you can write A(:, 2:end). This is especially useful for large matrices.
2. Linear Indexing
Linear indexing treats any matrix as if it were a column vector by concatenating the columns into one column vector and assigning indices to the elements respectively. For instance, we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to compute b = A(2). The equivalent column vector is:
A = [10;
40;
70;
20;
50;
80;
30;
60;
90]
and thus b equals 40.
The special colon and end subscripts are also allowed, of course. For that reason, A(:) converts any matrix A into a column vector.
Linear indexing with matrix subscripts:
It is also possible to use another matrix for linear indexing. The subscript matrix is simply converted into a column vector, and used for linear indexing. The resulting matrix is, however always of the same dimensions as the subscript matrix.
For instance, if I = [1 3; 1 2], then A(I) is the same as writing reshape(A(I(:)), size(I)).
Converting from matrix subscripts to linear indices and vice versa:
For that you have sub2ind and ind2sub, respectively. For example, if you want to convert the subscripts [1, 3] in matrix A (corresponding to element 30) into a linear index, you can write sub2ind(size(A), 1, 3) (the result in this case should be 7, of course).
3. Logical Indexing
In logical indexing the subscripts are binary, where a logical 1 indicates that the corresponding element is selected, and 0 means it is not. The subscript vector must be either of the same dimensions as the original matrix or a vector with the same number of elements. For instance, if we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to extract A([1 3], [1 2]) using logical indexing, we can do either this:
Ir = logical([1 1 0]);
Ic = logical([1 0 1]);
B = A(Ir, Ic)
or this:
I = logical([1 0 1; 1 0 1; 0 0 0]);
B = A(I)
or this:
I = logical([1 1 0 0 0 0 1 1 0]);
B = A(I)
Note that in the latter two cases is a one-dimensional vector, and should be reshaped back into a matrix if necessary (for example, using reshape).
Let me explain with an example:
Let's define a 6x6 matrix
A = magic(6)
A =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
From this matrix you want the elements in rows 1, 2 and 5, and in the columns 4 and 6
B = A([1 2 5],[4 6])
B =
26 24
21 25
12 16
Hope this helps.
function f = sub(A,i,j)
[m,n] = size(A);
row = 1:m;
col = 1:n;
x = row;
x(i) = [];
y=col;
y(j) = [];
f= A(x,y);
Returns the matrix A, with the ith row and jth column removed.

Physical significance of the rotation of the filter matrix in filter2 function

While using MATLAB 2D filter funcion filter2(B,X) and convolution function conv(X,B,''), I see that the filter2 function is essentially 2D convolution but with a rotation by 180 degrees of the filter coefficients matrix. In terms of the outputs of filter2 and conv2, I see that the below relation holds true:
output matrix of filter2 = each element negated of output of conv2
EDIT: I was incorrect; the above relation does not hold true in general, but I saw it for a few cases. In general, the two output matrices are unrelated, due to the fact that 2 entirely different kernels are obtained in both which are used for convolution.
I understand how 2D convolution is performed. What I want to understand is the implication of this in image processing terms. How do I visualize what is happening here? What does it mean to rotate a filter coefficient matrix by 180 degrees?
I'll start with a very brief discussion of convolution, using the following image from Wikipedia:
As illustrated, convolving two 1-D functions involves reflecting one of them (i.e. the convolution kernel), sliding the two functions over one another, and computing the integral of their product.
When convolving 2-D matrices, the convolution kernel is reflected in both dimensions, and then the sum of the products is computed for every unique overlapping combination with the other matrix. This reflection of the kernel's dimensions is an inherent step of the convolution.
However, when performing filtering we like to think of the filtering matrix as though it were a "stencil" that is directly laid as is (i.e. with no reflections) over the matrix to be filtered. In other words, we want to perform an equivalent operation as a convolution, but without reflecting the dimensions of the filtering matrix. In order to cancel the reflection performed during the convolution, we can therefore add an additional reflection of the dimensions of the filter matrix before the convolution is performed.
Now, for any given 2-D matrix A, you can prove to yourself that flipping both dimensions is equivalent to rotating the matrix 180 degrees by using the functions FLIPDIM and ROT90 in MATLAB:
A = rand(5); %# A 5-by-5 matrix of random values
isequal(flipdim(flipdim(A,1),2),rot90(A,2)) %# Will return 1 (i.e. true)
This is why filter2(f,A) is equivalent to conv2(A,rot90(f,2),'same'). To illustrate further how there are different perceptions of filter matrices versus convolution kernels, we can look at what happens when we apply FILTER2 and CONV2 to the same set of matrices f and A, defined as follows:
>> f = [1 0 0; 0 1 0; 1 0 0] %# A 3-by-3 filter/kernel
f =
1 0 0
0 1 0
1 0 0
>> A = magic(5) %# A 5-by-5 matrix
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, when performing B = filter2(f,A); the computation of output element B(2,2) can be visualized by lining up the center element of the filter with A(2,2) and multiplying overlapping elements:
17*1 24*0 1*0 8 15
23*0 5*1 7*0 14 16
4*1 6*0 13*0 20 22
10 12 19 21 3
11 18 25 2 9
Since elements outside the filter matrix are ignored, we can see that the sum of the products will be 17*1 + 4*1 + 5*1 = 26. Notice that here we are simply laying f on top of A like a "stencil", which is how filter matrices are perceived to operate on a matrix.
When we perform B = conv2(A,f,'same');, the computation of output element B(2,2) instead looks like this:
17*0 24*0 1*1 8 15
23*0 5*1 7*0 14 16
4*0 6*0 13*1 20 22
10 12 19 21 3
11 18 25 2 9
and the sum of the products will instead be 5*1 + 1*1 + 13*1 = 19. Notice that when f is taken to be a convolution kernel, we have to flip its dimensions before laying it on top of A.

How to conveniently do cross product of a 3x3 matrix with a 3d vector in matlab?

for example,
magic(3) x [1,2,3] gives:
-9 -18 15
1 -2 1
23 -10 -1
It sounds like what you want to do is compute the cross product of each row of a 3-by-3 matrix with a 1-by-3 vector. In order to use the function CROSS, the two inputs must be the same size, so you will have to replicate your 1-by-3 vector using the function REPMAT so that it has three rows. Then perform the cross product along the columns:
>> A = magic(3);
>> B = [1 2 3];
>> C = cross(A,repmat(B,size(A,1),1),2);
C =
-9 -18 15
1 -2 1
23 -10 -1