how to visualize a 3D matrix in MATLAB - matlab

I am trying to visualize the data contained a 3D array in MATLAB.
The array is has the dimension of 20*20*40 which all except some of the elements being zero.
I am looking for a way to plot these non-zero points in a scatter plot so that the non-zero points are connected to each other.
here is what I've done so far:
b=zeros(6,6,3);
a=[1 1 1;2 2 1;2 1 1;1 2 1;
1 1 2;1 2 2;2 1 2;2 2 2;
6 6 3;6 5 3;5 6 3;5 5 3;
6 6 2;6 5 2;5 6 2;5 5 2];
[r c]=size(a);
for i = 1:r
b(a(i,c-2),a(i,c-1),a(i,c)) = 1;
end
[m n o]=size(b);
figure (1)
[x,y,z] = meshgrid(1:m,1:n,1:o);
scatter3(x(:),y(:),z(:),90,b(:),'filled')
So, what I am after is being able to connect each of the eight points two form to cubic lattices. Any thoughts is much appreciated.
EDIT:
Many thanks to all the experts who helped a lot. Now, I am facing another issue with memory.
The b matrix for my real case would be 1000*1000*2000 and I have the "a" matrix of the size 4,4700,000*3 . All the elements of "a" matrix are integer values.
Although I have up to 48GB of memory availabe. However, in the "for loop" above the program bounces back an "out of memory" error.
Any ideas to make this more memory efficient is greatly appreciated.

Consider the following (based on a previous answer):
%# adjacency matrix
adj = false(numel(b));
%# extract first cube, and connect all its points
bb = b;
bb(:,1:3,:) = false;
idx = find(bb);
adj(idx,idx) = true;
%# extract second cube, and connect all its points
bb = b;
bb(:,4:6,:) = false;
idx = find(bb);
adj(idx,idx) = true;
%# points indices
[r c] = find(adj);
p = [r c]';
%# plot
plot3(x(p), y(p), z(p), 'LineWidth',2, 'Color',[.4 .4 1], ...
'Marker','o', 'MarkerSize',6, ...
'MarkerFaceColor','g', 'MarkerEdgeColor','g')
axis equal, axis vis3d, grid on, view(3)
xlabel x, ylabel y, zlabel z
EDIT:
It would be easier to just manually connect the points:
%# edges: connecting points indices
p = [
1 2; 2 8; 8 7; 7 1;
37 38; 38 44; 44 43; 43 37;
1 37; 2 38; 7 43; 8 44;
65 66; 66 72; 72 71; 71 65;
101 102; 102 108; 108 107; 107 101;
65 101; 66 102; 71 107; 72 108
]';
%# plot
figure
plot3(x(p), y(p), z(p), 'LineWidth',2, 'Color',[.4 .4 1], ...
'Marker','o', 'MarkerSize',6, ...
'MarkerFaceColor','g', 'MarkerEdgeColor','g')
axis equal, axis vis3d, grid on, view(3)
xlabel x, ylabel y, zlabel z
%# label points
labels = strtrim(cellstr( num2str((1:numel(b))','%d') ));
idx = find(b);
text(x(idx(:)), y(idx(:)), z(idx(:)), labels(idx(:)), ...
'Color','m', ...
'VerticalAlignment','bottom', 'HorizontalAlignment','left')
EDIT#2: (credit goes to #tmpearce)
You can semi-automate the process of builing the edges list. Just replace the manually constructed matrx p in the above code with the following:
%# compute edges: pairs of vertex indices
yIdx = {1:3 4:6}; %# hack to separate each cube points
p = cell(numel(yIdx),1);
for i=1:numel(yIdx) %# for each cube
%# find indices of vertices in this cube
bb = b;
bb(:,yIdx{i},:) = false;
idx = find(bb);
%# compute L1-distance between all pairs of vertices,
%# and find pairs which are unit length apart
[r,c] = find(triu(squareform(pdist([x(idx) y(idx) z(idx)],'cityblock'))==1));
%# store the edges
p{i} = [idx(r) idx(c)]';
end
p = cat(2,p{:}); %# merge all edges found
The idea is, for each cube, we compute the city-block distance between all vertices. The "side" edges will have a distance of 1, while the diagonals have a distance greater than or equal to 2.
This process still assume that the list of vertices belonging to each cube is provided to us, or easily extracted as we did in the code above...

I thought I'd answer the follow-up to Amro's answer, and show how you can automate the connection of edges along a cubic matrix. This answer is intended to be a supplement to Amro's, and I would recommend incorporating it into that solution so it is easier to read for future viewers.
This starts with idx from Amro's solution, which contains the linear indices of the corners of the cubes. Below I've done it with only one cube so the matrices aren't grossly large and we can see them here.
>> idx' %# transposed to save space in the printed output
ans =
1 2 7 8 37 38 43 44 '
%# now get the subindex of each point in 3D
>> [sx sy sz] = ind2sub([6 6 3],idx);
%# calculate the "edge distance" between each corner (not euclidean distance)
>> dist = abs(bsxfun(#minus,sx,sx')) +...
abs(bsxfun(#minus,sy,sy')) +...
abs(bsxfun(#minus,sz,sz'))
dist =
0 1 1 2 1 2 2 3
1 0 2 1 2 1 3 2
1 2 0 1 2 3 1 2
2 1 1 0 3 2 2 1
1 2 2 3 0 1 1 2
2 1 3 2 1 0 2 1
2 3 1 2 1 2 0 1
3 2 2 1 2 1 1 0 '
%# find row and column index of points one edge apart
>> [r c]=find(triu(dist==1)); %# triu means don't duplicate edges
%# create 'p' matrix like in Amro's answer
>> p = [idx(r) idx(c)]'
p =
1 1 2 7 1 2 37 7 37 8 38 43
2 7 8 8 37 38 38 43 43 44 44 44
To visualize, you can plot exactly like in Amro's answer.

Is this what you need?
b=logical(b);
scatter3(x(b(:)),y(b(:)),z(b(:)),90,'filled')

Related

Applying median filter to data with 2 axes

I have the following code:
x = VarName3;
y = VarName4;
x = (x/6000)/60;
plot(x, y)
Where VarName3 and VarName4 are 3000x1. I would like to apply a median filter to this in MATLAB. However, the problem I am having is that, if I use medfilt1, then I can only enter a single array of variables as the first argument. And for medfilt2, I can only enter a matrix as the first argument. But the data looks very obscured if I convert x and y into a matrix.
The x is time and y is a list of integers. I'd like to be able to filter out spikes and dips. How do I go about doing this? I was thinking of just eliminating the erroneous data points by direct manipulation of the data file. But then, I don't really get the effect of a median filter.
I found a solution using sort.
Median is the center element, so you can sort three elements, and take the middle element as median.
sort function also returns the index of the previous syntaxes.
I used the index information for restoring the matching value of X.
Here is my code sample:
%X - simulates time.
X = [1 2 3 4 5 6 7 8 9 10];
%Y - simulates data
Y = [0 1 2 0 100 1 1 1 2 3];
%Create three vectors:
Y0 = [0, Y(1:end-1)]; %Left elements [0 0 1 2 0 2 1 1 1 2]
Y1 = Y; %Center elements [0 1 2 0 2 1 1 1 2 3]
Y2 = [Y(2:end), 0]; %Right elements [1 2 0 2 1 1 1 2 3 0]
%Concatenate Y0, Y1 and Y2.
YYY = [Y0; Y1; Y2];
%Sort YYY:
%sortedYYY(2, :) equals medfilt1(Y)
%I(2, :) equals the index: value 1 for Y0, 2 for Y1 and 3 for Y2.
[sortedYYY, I] = sort(YYY);
%Median is the center of sorted 3 elements.
medY = sortedYYY(2, :);
%Corrected X index of medY
medX = X + I(2, :) - 2;
%Protect X from exceeding original boundries.
medX = min(max(medX, min(X)), max(X));
Result:
medX =
1 2 2 3 6 7 7 8 9 9
>> medY
medY =
0 1 1 2 1 1 1 1 2 2
Use a sliding window on the data vector centred at a given time. The value of your filtered output at that time is the median value of the data in the sliding window. The size of the sliding window is an odd value, not necessarily fixed to 3.

to find maximum point from the graph/plot

a= [1 2 13 20 10 20 12 1 13 14]
b= [1:10:100]
plot(a,b)
I want to find the maximum('a') from the plot and then take the corresponding point let say 'a3,b3' and store it some where else and remove it from the plot. then I want to subtract 'a3' from every point left in 'a' and plot the graph. and I need to do this again till it reaches an thresh hold point.
This seems like an odd request but if I understand correctly.
%Input data
a = [1 2 13 20 10 20 12 1 13 14];
b = [1:10:100];
%Some threshold (which you didn't specify
lengthA = 4;
%Initialize storage vector
aPrime = a;
bPrime = b;
%While vector
while (length(aPrime) >= lengthA)
%New figure
figure;
%Plot vector
plot(aPrime, bPrime)
%Find index and max value in a'
[aMax, index] = max(aPrime);
%Find max value in b'
bMax = bPrime(index);
%Remove max value from vector and subtract off max value
aPrime = aPrime([1:index - 1, index + 1:end]) - aMax;
bPrime = bPrime([1:index - 1, index + 1:end]) - bMax;
end

Remove internal edges in 3D MATLAB plot using patch function

I am plotting a 3D object, say a cube, in MATLAB.
Node = [0 0 0; 1 0 0; 1 1 0; 0 1 0; 0 0 1; 1 0 1; 1 1 1; 0 1 1];
Elem = cell(1); Elem{1} = 1:8;
figure
for elm = 1:size(Elem,1)
X = Node(Elem{elm},:); K = convhulln(X); hold on;
patch('Faces',K,'Vertices',X,'FaceColor',rand(1,3),'FaceAlpha',1.0);
end
view(3); grid off; axis equal; cameramenu; axis off;
In the plot, how do I remove the internal diagonal lines? The plot should just show edges of cube. I am looking for a general solution which is applicable to any polyhedron.
the output of K=convhulln(X); is causing this, because convex hull will have triangular facets... (that's the default).
if instead you'd define K to be:
K= [1 2 3 4; ...
2 6 7 3; ...
4 3 7 8; ...
1 5 8 4; ...
1 2 6 5; ...
5 6 7 8];
You'll get it right.
Another option is to use geom3D from the FEX.

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.

Getting all pixel coordinates of a vector inside a image

I have an intensity/greyscale image, and I have chosen a pixel inside this image. I want to send vectors starting from this pixel in all directions/angles, and I want to sum all the intensities of the pixels touching one vector, for all vectors.
After this step I would like to plot a histogram with the intensities on one axis and the angle on the other axis. I think I can do this last step on my own, but I don't know how to create these vectors inside my greyscale image and how to get the coordinates of the pixels a vector touches.
I previously did this in C++, which required a lot of code. I am sure this can be done with less effort in MATLAB, but I am quite new to MATLAB, so any help would be appreciated, since I haven't found anything helpful in the documentation.
It might not be the best way to solve it, but you can do it using a bit of algebra, heres how...
We know the Point-Slope formula of a line passing through point (a,b) with angle theta is:
y = tan(theta) * (x-a) + b
Therefore a simple idea is to compute the intersection of this line with y=const for all const, and read the intensity values at the intersection. You would repeat this for all angles...
A sample code to illustrate the concept:
%% input
point = [128 128]; % pixel location
I = imread('cameraman.tif'); % sample grayscale image
%% calculations
[r c] = size(I);
angles = linspace(0, 2*pi, 4) + rand;
angles(end) = [];
clr = lines( length(angles) ); % get some colors
figure(1), imshow(I), hold on
figure(2), hold on
for i=1:length(angles)
% line equation
f = #(x) tan(angles(i))*(x-point(1)) + point(2);
% get intensities along line
x = 1:c;
y = round(f(x));
idx = ( y<1 | y>r ); % indices of outside intersections
vals = diag(I(x(~idx), y(~idx)));
figure(1), plot(x, y, 'Color', clr(i,:)) % plot line
figure(2), plot(vals, 'Color', clr(i,:)) % plot profile
end
hold off
This example will be similar to Amro's, but it is a slightly different implementation that should work for an arbitrary coordinate system assigned to the image...
Let's assume that you have matrices of regularly-spaced x and y coordinates that are the same size as your image, such that the coordinates of pixel (i,j) are given by (x(i,j),y(i,j)). As an example, I'll create a sample 5-by-5 set of integer coordinates using MESHGRID:
>> [xGrid,yGrid] = meshgrid(1:5)
xGrid =
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
yGrid =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
Next we can define a line y = m*(x - a) + b passing through the coordinate system by selecting some values for the constants and computing y using the x coordinates of the grid:
>> a = 0;
>> b = 1;
>> m = rand
m =
0.5469
>> y = m.*(xGrid(1,:)-a)+b
y =
1.5469 2.0938 2.6406 3.1875 3.7344
Finally, we find the y points in the grid that differ from the points computed above by less than the grid size:
>> index = abs(yGrid-repmat(y,size(yGrid,1),1)) <= yGrid(2,1)-yGrid(1,1)
index =
1 0 0 0 0
1 1 1 0 0
0 1 1 1 1
0 0 0 1 1
0 0 0 0 0
and use this index matrix to get the x and y coordinates for the pixels crossed by the line:
>> xCrossed = xGrid(index);
>> yCrossed = yGrid(index);