I have a 3D 4-by-4-by-4 matrix and I consider it circular (so a 4th element of row closes with the 1st and same goes for columns and pages).
we know that in 3D each point has exactly 26 neighbors which can be stated as (i, j, k-1) (i, j, k+1) etc. but I am not sure how to make matlab know that a (i,j, k-1) neighbor of a point (1,1,1) is not (1,1,0) but (as it is circular) (1,1,4) as well as that point (2,4,3)'s neighbor (i,j+1,k) is not (2,5,3) but (2,1,3). In other words HOW DO I MAKE IT CIRCULAR?
Thank you
MATLAB does not have built-in facilities for this, but you can use the mod (modulus) function when accessing your matrix to achieve the effect you want. To illustrate this on a vector:
v=[1 2 3];
i=5;
result=v(mod(i-1, length(v))+1);
% assigns 2 to 'result'
You'll probably want to write a function that encapsulates the "circular" matrix access so that you have to do the index computations in only one place.
The idea is to use the MOD function as #MartinB explained. Here some code to efficiently compute the neighbors of each point in your 4x4x4 cube:
%# generate X/Y/Z coordinates of each point of the 4x4x4 cube
sz = [4 4 4]; %# size of the cube along each dimension
[X Y Z] = ndgrid(1:sz(1),1:sz(2),1:sz(3));
coords = [X(:) Y(:) Z(:)];
%# generate increments to get the 26 neighbors around a 3D point
[X Y Z] = ndgrid([-1 0 1], [-1 0 1], [-1 0 1]);
nb = [X(:) Y(:) Z(:)];
nb(ismember(nb,[0 0 0],'rows'),:) = []; %# remove the row [0 0 0]
%# for each 3D point, compute its neighbors
allNeighbors = zeros([size(nb,1) 3 size(coords,1)]);
szMod = repmat(sz, [size(nb,1) 1]);
for i=1:size(coords,1)
cc = bsxfun(#plus, nb, coords(i,:)); %# find 26 neighbors of coords(i,:)
cc = mod(cc-1,szMod)+1; %# wrap around circularly
allNeighbors(:,:,i) = cc; %# save them for later processing
end
The order of the neighbors generated is as follows:
>> nb
nb =
-1 -1 -1 %# read as: (i-1,j-1,k-1)
0 -1 -1 %# read as: (i,j-1,k-1)
1 -1 -1 %# ...
-1 0 -1
0 0 -1
1 0 -1
-1 1 -1
0 1 -1
1 1 -1
-1 -1 0
0 -1 0
1 -1 0
-1 0 0
1 0 0
-1 1 0
0 1 0
1 1 0
-1 -1 1
0 -1 1
1 -1 1
-1 0 1
0 0 1
1 0 1
-1 1 1
0 1 1
1 1 1
Related
Suppose I have a Matlab array PE of size Ex1. The elements of PE are between 0 and 1 and they sum up to one.
Take another array PEY of size ExY. The elements of PEY are one or zero. Moreover, for each row, there exists at least a one. Y=3.
For example
clear
E=4;
Y=3;
PE=[1/2; 1/6; 1/6; 1/6];
PEY=[1 0 0; 0 1 1; 1 1 1; 0 1 1];
Now, consider the simplex with vertices (1,0,0), (0,1,0), and (0,0,1)
patch([0 0 1],[0 1 0],[1 0 0],[0.8 0.8 0.8]);
axis equal
axis([0 1 0 1 0 1])
view(120,30)
I want to draw a convex subset A of such simplex. A is constructed as follows.
STEP 1: We construct an Ex1 cell array PEY_expanded such that, for each e-th row of PEY that has more than a 1, we write down all admissible 1xY vectors containing just a 1 and stack them in PEY_expanded{e}.
PEY_expanded=cell(E,1);
for e=1:E
if isequal(PEY(e,:),[1 1 1])
PEY_expanded{e}=[1 0 0; 0 1 0; 0 0 1];
elseif isequal(PEY(e,:),[1 1 0])
PEY_expanded{e}=[1 0 0; 0 1 0];
elseif isequal(PEY(e,:),[1 0 1])
PEY_expanded{e}=[1 0 0; 0 0 1];
elseif isequal(PEY(e,:),[0 1 1])
PEY_expanded{e}=[0 1 0; 0 0 1];
else
PEY_expanded{e}=PEY(e,:);
end
end
STEP 2: Take Cartesian product PEY_expanded{1} x PEY_expanded{2} x ... PEY_expanded{E} and get PEY_cartesian.
Note: the code below is specific for E=4 and not general
size_PEY_expanded=zeros(E,1);
for e=1:E
size_PEY_expanded(e)=size(PEY_expanded{e},1);
end
[a,b,c,d]=ndgrid(1: size_PEY_expanded(1),1: size_PEY_expanded(2),...
1: size_PEY_expanded(3), 1: size_PEY_expanded(4));
PEY_Cartesian= [PEY_expanded{1}(a,:),PEY_expanded{2}(b,:),...
PEY_expanded{3}(c,:), PEY_expanded{4}(d,:)];
PEY_Cartesian_rearranged=cell(prod(size_PEY_expanded),1);
for i=1:prod(size_PEY_expanded)
PEY_Cartesian_rearranged{i}=[PEY_Cartesian(i,1:3); PEY_Cartesian(i,4:6);...
PEY_Cartesian(i,7:9); PEY_Cartesian(i,10:end)];
end
PEY_Cartesian=PEY_Cartesian_rearranged;
STEP 3: For each possible cell of PEY_Cartesian, for y=1,...,Y, weight PEY_Cartesian{i}(e,y) by PE(e) and then sum across e.
PY=zeros(prod(size_PEY_expanded),Y);
for i=1:prod(size_PEY_expanded)
for y=1:Y
temp=0;
for e=1:E
temp=temp+PE(e)*PEY_Cartesian{i}(e,y);
end
PY(i,y)=temp;
end
end
STEP 4: Draw the region A that is the convex hull of the rows of PY (black region in the picture)
%Need https://fr.mathworks.com/matlabcentral/fileexchange/37004-suite-of-functions-to-perform-uniform-sampling-of-a-sphere
close all
patch([0 0 1],[0 1 0],[1 0 0],[0.8 0.8 0.8]);
axis equal
axis([0 1 0 1 0 1])
view(120,30)
hold on
T = delaunayTriangulation(PY);
K = convexHull(T);
patch('Faces',K,'Vertices',T.Points,'FaceColor','k','edgecolor','k');
hold on
QUESTION:
The algorithm above is unfeasible for large E. In my actual case I have E=216, for example. In particular, step 2 is unfeasible.
Could you suggest an easier way to proceed? Given that A is a convex region, maybe there is some shortcut I'm unable to see.
I have a matrix A which is
A=[1 0 0 1 0;
0 1 1 0 0;
0 0 1 1 0;
1 1 1 0 0]
And a given vector v=[ 0 0 1 1 0] which has two elements one. I have to change the position of element one such that the new vector v is orthogonal to all the rows in the matrix A.
How can I do it in Matlab?
To verify the correct answer, just check gfrank([A;v_new]) is 5 (i.e v_new=[0 1 0 0 1]).
Note that: Two vectors uand v whose dot product is u.v=0 (i.e., the vectors are perpendicular) are said to be orthogonal.
As AVK also mentioned in the comments, v_new = [0 1 0 0 1] is not orthogonal to all rows of A.
Explanation:-
A=[1 0 0 1 0;
0 1 1 0 0;
0 0 1 1 0;
1 1 1 0 0]
For A(1,:).*v = 0 to A(4,:).*v = 0,
0 x x 0 x % elements of v so that it's orthagonal to the 1st row of A
x 0 0 x x % -------------------------------------------- 2nd row of A
x x 0 0 x % -------------------------------------------- 3rd row of A
0 0 0 x x % -------------------------------------------- 4th row of A
where 0 represents the terms which have to be 0 and x represents the terms which can be either 0 or 1.
If you look as a whole, first 4 columns of v have to be zero so that the output is orthagonal to all rows of A. The 5th column can either be zero or 1.
So,
v_new can either be: v_new = [0 0 0 0 1] or v_new = [0 0 0 0 0]
From above explanation, you can also see that [0 1 0 0 1] is not orthagonal to 2nd and 4th row of A
Solution:-
To find v_new, you can use null function as: v_new = null(A).'
which gives: v_new = [0 0 0 0 1] for which gfrank([A;v_new]) also gives 5.
Maybe this will help you see the orthogonality between two vectors in N dimension.
N=100;
B1 = ones(1,N);
B2 = -1*ones(1,N/2);
B2 = [ones(1,N/2) B2];
B2 = transpose(B2);
B3 = dot(B1,B2);
The above code generates two vectors in N dimension. To check for orthogonality just transpose one of the vectors and multiply with the other one. You should get zero if they are Orthogonal.
The example I used makes sure that I get zero indeed.
Consider a set of points arranged on a grid of size N-by-M.
I am trying to build the adjacency matrix such that
neighboring points are connected.
For example, in a 3x3 grid with a graph:
1-2-3
| | |
4-5-6
| | |
7-8-9
We should have the corresponding adjacency matrix:
+---+------------------------------------------------------+
| | 1 2 3 4 5 6 7 8 9 |
+---+------------------------------------------------------+
| 1 | 0 1 0 1 0 0 0 0 0 |
| 2 | 1 0 1 0 1 0 0 0 0 |
| 3 | 0 1 0 0 0 1 0 0 0 |
| 4 | 1 0 0 0 1 0 1 0 0 |
| 5 | 0 1 0 1 0 1 0 1 0 |
| 6 | 0 0 1 0 1 0 0 0 1 |
| 7 | 0 0 0 1 0 0 0 1 0 |
| 8 | 0 0 0 0 1 0 1 0 1 |
| 9 | 0 0 0 0 0 1 0 1 0 |
+---+------------------------------------------------------+
As a bonus, the solution should work for both 4- and 8-connected neighboring points, that is:
o o o o
o X o vs. o X o
o o o o
This the code that I have so far:
N = 3; M = 3;
adj = zeros(N*M);
for i=1:N
for j=1:M
k = sub2ind([N M],i,j);
if i>1
ii=i-1; jj=j;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
if i<N
ii=i+1; jj=j;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
if j>1
ii=i; jj=j-1;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
if j<M
ii=i; jj=j+1;
adj(k,sub2ind([N M],ii,jj)) = 1;
end
end
end
How can this improved to avoid all the looping?
If you notice, there is a distinct pattern to the adjacency matrices you are creating. Specifically, they are symmetric and banded. You can take advantage of this fact to easily create your matrices using the diag function (or the spdiags function if you want to make a sparse matrix). Here is how you can create the adjacency matrix for each case, using your sample matrix above as an example:
4-connected neighbors:
mat = [1 2 3; 4 5 6; 7 8 9]; % Sample matrix
[r, c] = size(mat); % Get the matrix size
diagVec1 = repmat([ones(c-1, 1); 0], r, 1); % Make the first diagonal vector
% (for horizontal connections)
diagVec1 = diagVec1(1:end-1); % Remove the last value
diagVec2 = ones(c*(r-1), 1); % Make the second diagonal vector
% (for vertical connections)
adj = diag(diagVec1, 1)+diag(diagVec2, c); % Add the diagonals to a zero matrix
adj = adj+adj.'; % Add the matrix to a transposed copy of
% itself to make it symmetric
And you'll get the following matrix:
adj =
0 1 0 1 0 0 0 0 0
1 0 1 0 1 0 0 0 0
0 1 0 0 0 1 0 0 0
1 0 0 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 0 0 1
0 0 0 1 0 0 0 1 0
0 0 0 0 1 0 1 0 1
0 0 0 0 0 1 0 1 0
8-connected neighbors:
mat = [1 2 3; 4 5 6; 7 8 9]; % Sample matrix
[r, c] = size(mat); % Get the matrix size
diagVec1 = repmat([ones(c-1, 1); 0], r, 1); % Make the first diagonal vector
% (for horizontal connections)
diagVec1 = diagVec1(1:end-1); % Remove the last value
diagVec2 = [0; diagVec1(1:(c*(r-1)))]; % Make the second diagonal vector
% (for anti-diagonal connections)
diagVec3 = ones(c*(r-1), 1); % Make the third diagonal vector
% (for vertical connections)
diagVec4 = diagVec2(2:end-1); % Make the fourth diagonal vector
% (for diagonal connections)
adj = diag(diagVec1, 1)+... % Add the diagonals to a zero matrix
diag(diagVec2, c-1)+...
diag(diagVec3, c)+...
diag(diagVec4, c+1);
adj = adj+adj.'; % Add the matrix to a transposed copy of
% itself to make it symmetric
And you'll get the following matrix:
adj =
0 1 0 1 1 0 0 0 0
1 0 1 1 1 1 0 0 0
0 1 0 0 1 1 0 0 0
1 1 0 0 1 0 1 1 0
1 1 1 1 0 1 1 1 1
0 1 1 0 1 0 0 1 1
0 0 0 1 1 0 0 1 0
0 0 0 1 1 1 1 0 1
0 0 0 0 1 1 0 1 0
Just for fun, here's a solution to construct the adjacency matrix by computing the distance between all pairs of points on the grid (not the most efficient way obviously)
N = 3; M = 3; %# grid size
CONNECTED = 8; %# 4-/8- connected points
%# which distance function
if CONNECTED == 4, distFunc = 'cityblock';
elseif CONNECTED == 8, distFunc = 'chebychev'; end
%# compute adjacency matrix
[X Y] = meshgrid(1:N,1:M);
X = X(:); Y = Y(:);
adj = squareform( pdist([X Y], distFunc) == 1 );
And here's some code to visualize the adjacency matrix and the graph of connected points:
%# plot adjacency matrix
subplot(121), spy(adj)
%# plot connected points on grid
[xx yy] = gplot(adj, [X Y]);
subplot(122), plot(xx, yy, 'ks-', 'MarkerFaceColor','r')
axis([0 N+1 0 M+1])
%# add labels
[X Y] = meshgrid(1:N,1:M);
X = reshape(X',[],1) + 0.1; Y = reshape(Y',[],1) + 0.1;
text(X, Y(end:-1:1), cellstr(num2str((1:N*M)')) )
I just found this question when searching for the same problem. However, none of the provided solutions worked for me because of the problem size which required the use of sparse matrix types. Here is my solution which works on large scale instances:
function W = getAdjacencyMatrix(I)
[m, n] = size(I);
I_size = m*n;
% 1-off diagonal elements
V = repmat([ones(m-1,1); 0],n, 1);
V = V(1:end-1); % remove last zero
% n-off diagonal elements
U = ones(m*(n-1), 1);
% get the upper triangular part of the matrix
W = sparse(1:(I_size-1), 2:I_size, V, I_size, I_size)...
+ sparse(1:(I_size-m),(m+1):I_size, U, I_size, I_size);
% finally make W symmetric
W = W + W';
Just came across this question. I have a nice working m-function (link: sparse_adj_matrix.m) that is quite general.
It can handle 4-connect grid (radius 1 according to L1 norm), 8-connect grid (radius 1 according to L_infty norm).
It can also support 3D (and arbitrarily higher domensional grids).
The function can also connect nodes further than radius = 1.
Here's the signiture of the function:
% Construct sparse adjacency matrix (provides ii and jj indices into the
% matrix)
%
% Usage:
% [ii jj] = sparse_adj_matrix(sz, r, p)
%
% inputs:
% sz - grid size (determine the number of variables n=prod(sz), and the
% geometry/dimensionality)
% r - the radius around each point for which edges are formed
% p - in what p-norm to measure the r-ball, can be 1,2 or 'inf'
%
% outputs
% ii, jj - linear indices into adjacency matrix (for each pair (m,n)
% there is also the pair (n,m))
%
% How to construct the adjacency matrix?
% >> A = sparse(ii, jj, ones(1,numel(ii)), prod(sz), prod(sz));
%
%
% Example:
% >> [ii jj] = sparse_adj_matrix([10 20], 1, inf);
% construct indices for 200x200 adjacency matrix for 8-connect graph over a
% grid of 10x20 nodes.
% To visualize the graph:
% >> [r c]=ndgrid(1:10,1:20);
% >> A = sparse(ii, jj, 1, 200, 200);;
% >> gplot(A, [r(:) c(:)]);
Your current code doesn't seem so bad. One way or another you need to iterate over all neighbor pairs. If you really need to optimize the code, I would suggest:
loop over node indices i, where 1 <= i <= (N*M)
don't use sub2ind() for efficiency, the neighbors of node i are simpy [i-M, i+1, i+M, i-1] in clockwise order
Notice that to get all neighbor pairs of nodes:
you only have to compute the "right" neighbors (i.e. horizontal edges) for nodes i % M != 0 (since Matlab isn't 0-based but 1-based)
you only have to compute "above" neighbors (i.e. vertical edges) for nodes i > M
there is a similar rule for diagonal edges
This would leed to a single loop (but same number of N*M iterations), doesn't call sub2ind(), and has only two if statements in the loop.
For each node in the graph add a connection to the right and one downwards. Check that you don't overreach your grid. Consider the following function that builds the adjacency matrix.
function adj = AdjMatrixLattice4( N, M )
% Size of adjacency matrix
MN = M*N;
adj = zeros(MN,MN);
% number nodes as such
% [1]---[2]-- .. --[M]
% | | |
% [M+1]-[M+2]- .. -[2*M]
% : : :
% [] [] .. [M*N]
for i=1:N
for j=1:N
A = M*(i-1)+j; %Node # for (i,j) node
if(j<N)
B = M*(i-1)+j+1; %Node # for node to the right
adj(A,B) = 1;
adj(B,A) = 1;
end
if(i<M)
B = M*i+j; %Node # for node below
adj(A,B) = 1;
adj(B,A) = 1;
end
end
end
end
Example as above AdjMatrixLattice4(3,3)=
0 1 0 1 0 0 0 0 0
1 0 1 0 1 0 0 0 0
0 1 0 0 0 1 0 0 0
1 0 0 0 1 0 1 0 0
0 1 0 1 0 1 0 1 0
0 0 1 0 1 0 0 0 1
0 0 0 1 0 0 0 1 0
0 0 0 0 1 0 1 0 1
0 0 0 0 0 1 0 1 0
I have the following 5x5 Matrix A:
1 0 0 0 0
1 1 1 0 0
1 0 1 0 1
0 0 1 1 1
0 0 0 0 1
I am trying to find the centroid in MATLAB so I can find the scatter matrix with:
Scatter = A*Centroid*A'
If you by centroid mean the "center of mass" for the matrix, you need to account for the placement each '1' has in your matrix. I have done this below by using the meshgrid function:
M =[ 1 0 0 0 0;
1 1 1 0 0;
1 0 1 0 1;
0 0 1 1 1;
0 0 0 0 1];
[rows cols] = size(M);
y = 1:rows;
x = 1:cols;
[X Y] = meshgrid(x,y);
cY = mean(Y(M==1))
cX = mean(X(M==1))
Produces cX=3 and cY=3;
For
M = [1 0 0;
0 0 0;
0 0 1];
the result is cX=2;cY=2, as expected.
The centroid is simply the mean average computed separately for each dimension.
To find the centroid of each of the rows of your matrix A, you can call the mean function:
centroid = mean(A);
The above call to mean operates on rows by default. If you want to get the centroid of the columns of A, then you need to call mean as follows:
centroid = mean(A, 2);
Say I have an boundary image in a logical matrix where true means boundary and false means region interior. The image encodes a tessellation of a 2D domain.
I was wondering if there is a compact way in MATLAB to "fix" those pixel neighborhoods where the separation between adjacent regions is only 4-connected and transform them into 8-connected in a manner that preserves the topology of the tessellation.
I believe this can be done with LUTs, but I'm not sure how to proceed. Do I have to, and if so, how do I exactly evaluate all the 3x3 pixel regions where the connectivity is only 4-wise to fill-in the corresponding pixels?
My proposed solution: use BWHITMISS to find the pixels whose neighborhood is at least 4-connected, dilate the result with a rectangular-shaped structuring element to convert those neighborhoods to 8-connected, finally we combine with the original image using logical-OR.
Example:
bw = [
0 0 0 1 0 1 0
0 0 1 1 1 1 1
0 1 1 1 0 1 0
0 0 1 0 1 0 0
0 1 1 0 0 0 0
0 0 1 0 1 1 1
0 0 1 0 0 1 0
];
hm = bwhitmiss(bw, [0 1 0; 1 1 1; 0 1 0]); %# [-1 1 -1; 1 1 1; -1 1 -1]
bw2 = imdilate(hm,ones(3)) | bw;
We can visualize the result:
[r c] = find(hm);
subplot(121), imshow(bw), hold on, plot(c(:),r(:),'o')
subplot(122), imshow(bw2)