Taking images of 3D model using STLRead - matlab

I'm trying to take "images" of a 3D model. I am able to load an STL file through this code. What I need to do is be able rotate the object, and take "images" of it as it rotates.
stlread outputs a face and vertex structure that is compatible with patch(), so I can display the object, but I'm not sure how to actually store that image into a matrix.

I think what you are after is the function getframe, which captures the content of an axes and store the data as a regular "image" matrix.
I made a simple GUI to illustrate the concept. Basically I took the code from the link you provided (stldemo.m) and added a few features to take snapshots. In this case I added the command rotate3d on to rotate the femur inside the axes and a pushbutton whose callback calls getframe and captures the content of the axes. Basically you rotate the femur//patch object as you wish and you press the button to get a snapshot. In the code I make a new figure pop up to convince you it works, but you can of course do whatever you want with the data.
Note that using getframe outputs a structure with a field called cdata...that's what you are after in order to get the content of the axis as a matrix.
So here is the code and a few snapshots to illustrate:
function SnapShotSTL(~)
%// Taken from the stldemo.m file.
%% 3D Model Demo
% This is short demo that loads and renders a 3D model of a human femur. It
% showcases some of MATLAB's advanced graphics features, including lighting and
% specular reflectance.
% Copyright 2011 The MathWorks, Inc.
%% Load STL mesh
% Stereolithography (STL) files are a common format for storing mesh data. STL
% meshes are simply a collection of triangular faces. This type of model is very
% suitable for use with MATLAB's PATCH graphics object.
% Import an STL mesh, returning a PATCH-compatible face-vertex structure
handles.fv = stlread('femur.stl');
%% Render
% The model is rendered with a PATCH graphics object. We also add some dynamic
% lighting, and adjust the material properties to change the specular
% highlighting.
%// Create figure, axes and a pushbutton.
hFigure = figure('Units','Pixels','Position',[200 200 500 600]);
hAxes1 = axes('Units','pixels','Position',[50 50 450 400]);
hSnapShot = uicontrol('Style','push','Position',[50 500 60 30],'String','SnapShot','Callback',#(s,e) GetSnapshot);
patch(handles.fv,'FaceColor', [0.8 0.8 1.0], ...
'EdgeColor', 'none', ...
'FaceLighting', 'gouraud', ...
'AmbientStrength', 0.15);
% Add a camera light, and tone down the specular highlighting
camlight('headlight');
material('dull');
% Fix the axes scaling, and set a nice view angle
axis('image');
view([-135 35]);
rotate3d on %// Enable to rotate the object in the axes
guidata(hFigure,handles);
function GetSnapshot
CurrentImage = getframe(gca);
ImageData = CurrentImage.cdata; %// Access the data. I create a variable but that's not strictly necessary.
%// Here a new figure is created and the image displayed in it... you can store it and do as you like with it.
figure;
imshow(ImageData);
guidata(hFigure,handles);
end
end
So the GUI looks like this when opened:
Then after a rotation of the object/femur:
and finally after pressing the pushbutton, a snapshot is taken (i.e. getframe is executed) and the resulting image matrix displayed in a new figure:
Note that I used imshow to actually display the image but the data can be manipulated as well.
Moreover, you can also use the following calling syntax for getframe in case you want to retrieve the colormap associated with your data:
F = getframe;
[X,map] = frame2im(F);
In this case, X would be equivalent to G when using this code:
G = F.cdata;
Finally, you could set up a loop in which the actual patch object is rotated by itself and frames are taken automatically. That should not be too hard to implement :)
Hope that helps!

Related

How to update a scatter3 plot (in a loop) in Matlab

Quite a simple question but just couldn't find the answer online... I want to visualise a point cloud gathered from a lidar. I can plot the individual frames but wanted to loop them to create a "animation". I know how to do it for normal plots with drawnow but can't get it working with a scatter3. If I simply call scatter3 again like I have done in the commented code then the frame that I am viewing in the scatter plot jumps around with every update (Very uncomfortable). How do i get the scatter3 plot to update to the new points without changing the UI of the scatter ie. Still be able to pan and zoom around the visualised point cloud while it loops through.
EDIT: The file is a rosbag file, I cannot attach it because it is 170MB. The problem doesn't happen when using scatter3 in a loop with a normal array seems to be something with using scatter3 to call a PointCloud2 type file using frame = readMessages(rawBag, i).
EDIT: The problem does not seem to be with the axis limits but rather with the view of the axis within the figure window. When the scatter is initialised it is viewed with the positive x to the right side, positive y out of the screen and positive z upwards, as shown in view 1. Then after a short while it jumps to the second view, where the axis have changed, positive x is now out of the screen, positive y to the right and positive z upwards (both views shown in figures). This makes it not possible to view in a loop as it is constantly switching. So basically how to update the plot without calling scatter3(pointCloudData)?
rawBag = rosbag('jackwalking.bag');
frame = readMessages(rawBag, 1);
scatter3(frame{1});
hold on
for i = 1:length(readMessages(rawBag))
disp(i)
frame = readMessages(rawBag, i);
% UPDATE the 3D Scatter %
% drawnow does not work?
% Currently using:
scatter3(frame{1})
pause(.01)
end
The trick is to not use functions such as scatter or plot in an animation, but instead modify the data in the plot that is already there. These functions always reset axes properties, which is why you see the view reset. When modifying the existing plot, the axes are not affected.
The function scatter3 (as do all plotting functions) returns a handle to the graphics object that renders the plot. In the case of scatter3, this handle has three properties of interest here: XData, YData, and ZData. You can update these properties to change the location of the points:
N = 100;
data = randn(N,3) * 40;
h = scatter3(data(:,1),data(:,2),data(:,3));
for ii = 1:500
data = data + randn(N,3);
set(h,'XData',data(:,1),'YData',data(:,2),'ZData',data(:,3));
drawnow
pause(1/5)
end
The new data can be totally different too, it doesn't even need to contain the same number of points.
But when modifying these three properties, you will see the XLim, YLim and ZLim properties of the axes change. That is, the axes will rescale to accommodate all the data. If you need to prevent this, set the axes' XLimMode, YLimMode and ZLimMode to 'manual':
set(gca,'XLimMode','manual','YLimMode','manual','ZLimMode','manual')
When manually setting the limits, the limit mode is always set to manual.
As far as I understood what you describe as "plots jumpying around", the reason for this are the automatically adjusted x,y,z limits of the scatter3 plot. You can change the XLimMode, YLimMode, ZLimMode behaviour to manual to force the axis to stay fixed. You have to provide initial axes limits, though.
% Mock data, since you haven't provided a data sample
x = randn(200,50);
y = randn(200,50);
z = randn(200,50);
% Plot first frame before loop
HS = scatter3(x(:,1), y(:,1), z(:,1));
hold on
% Provide initial axes limits (adjust to your data)
xlim([-5,5])
ylim([-5,5])
zlim([-5,5])
% Set 'LimModes' to 'manual' to prevent auto resaling of the plot
set(gca, 'XLimMode', 'manual', 'YLimMode', 'manual', 'ZLimMode', 'manual')
for i=2:len(x,2)
scatter3(x(:,i), y(:,i), z(:,i))
pause(1)
end
This yields an "animation" of plots, where you can pan and zoom into the data while continuous points are added in the loop

Undo Markers in reopened fig fieldtrip

some time ago I have done topoplots of my MEG Data using ft_topoplotTFR and saved them as fig files. I have set the cfg.marker = 'on'.
Now I need the files but without the markers, because they just make the picture black and i cannot identify a lot. The markers are the Sensors. See in this picture:
TopoplotTFR
The black little circles that cover the surface are the markers I am talking about.
ft_topoplotTFR belongs to the fieldtrip toolbox and makes topoplots of Brain Data.
http://www.fieldtriptoolbox.org/reference/ft_topoplottfr
Can I somehow change the marker settings in the figure if i just have the fig file?
While we have no idea what ft_topoplotTFR is, the answer is going to follow more or less the same general steps: Load the figure, identify the plot objects, and modify their properties.
For example, we can generate a busy sample plot:
x = 0:0.05:10;
y1 = 5*rand(size(x));
y2 = 5*abs(sin(x));
plot(x, y1, '-d', x, y2, '-p');
savefig('test.fig');
close
We can then use openfig to load the figure so we can access the plotted data:
myfig = openfig('test.fig');
% Child of a basic figure window with a plot is the axes object
myaxes = myfig.Children;
% Child(ren) of a generic axes with plotted data are the plot object(s)
myls = myaxes.Children;
set(myls, 'Marker', 'none'); % Turn off markers
Which gives us:

Plot within a plot in MATLAB

I am trying to create a smaller plot within a plot in MATLAB, for example like the image of this MATLAB File Exchange Upload.
There, two figures are created and then both of them are plotted in one figure.
My problem however is that I already have two MATLAB figures from earlier simulations and I need to embed one figure into the other one, i.e., one would be small and the other plot would be big but in the same graph. Could someone suggest an easy way to do this?
This can be done using the copyobj function. You'll need to copy the Axes object from one figure to the other:
f(1) = openfig('fig1.fig');
f(2) = openfig('fig2.fig');
ax(1) = get(f(1),'CurrentAxes'); % Save first axes handle
ax(2) = copyobj(get(f(2),'CurrentAxes'),f(1)); % Copy axes and save handle
Then you can move and resize both axes as you like, e.g.
set(ax(2),'Position', [0.6, 0.6, 0.2, 0.2]);

Movie in Matlab is cropped and doesn't show the full figure

I am learning to make a movie in Matlab. I coped this piece of code from internet to make the movie. I am using OS X Mavericks and Matlab 2013a. I usually dock the figure windows. When I run the code I can see animation perfectly. When when I save the movie either by using movie2avi or VideoWriter command. It doesn't record the movie with full frame of figure. Instead I see a little bit of the figure window and my Matlab desktop view. Can anyone help me with this?
clear;
close all;
% Creates a 2D Mesh to plot surface
x=linspace(0,1,100);
[X,Y] = meshgrid(x,x);
N=100; % Number of frames
for i = 1:N
% Example of plot
Z = sin(2*pi*(X-i/N)).*sin(2*pi*(Y-i/N));
surf(X,Y,Z)
% Store the frame
M(i)=getframe(gca); % leaving gcf out crops the frame in the movie.
end
% myVideo = VideoWriter('wavemotion.avi');
% open(myVideo);
% writeVideo(myVideo,M)
% close(myVideo)
% Output the movie as an avi file
%movie2avi(M,'WaveMovie.avi');
Based on the documentation of both VideoWriter and movie2avi, you need to add a few more instructions to your code.
First, you need to set the renderer for a nice display and set the axes that the next plot will reset only the children and not the entire axes:
set(gca,'nextplot','replacechildren');
set(gcf,'Renderer','zbuffer');
There is also an advice to preallocate the frame buffer:
M(N) = struct('cdata',[],'colormap',[]);
Finally, you just need to call getframe without any argmuent, as gca is the default argument.
On my computer (with Matlab 2013b), with these modifications and using VideoWriter, it works fine. With movie2avi, it does not work properly; I think I need to specify a compression codec to fix that.

How to automatically spin a 3d hsitogram in matlab?

I have a 3d histogram in matlab. Is it possible to automatically spin it i.e to get the 3d effects. I want to show it as a video in PowerPoint where the 3d histogram swivels.
thanks
A somewhat cumbersome way to do this would be to rotate the chart manually using the view command. You can update azimuth and elevation views of any 3D plot using this command.
Creating a video of it requires capturing the plot window using a command sequence like this (note, you're going to get the gray background, so you might want to change the background color):
% create figure and get handle to it (store handle in hf)
hf = figure(1);
% [create 3d plot]
% Create file to hold the animation
aviobj = avifile('mymovie.avi', 'compression', 'Cinepak');
% loop with some criteria for rotation
while(...)
% update view using view command
view(az, el);
% get Matlab to flush the drawing buffer (effectively forces a plot update)
drawnow;
% capture frame and write to the avi file
aviobj = addframe(aviobj, hf);
end
% end loop
% Close movie (flushes write buffer and finishes the video)
aviobj = close(aviobj);
You can use the same tactic without the avifile stuff to rotate the plot using a script in Matlab, though you might want to use a pause command to slow down the frame change.
Let us make sure we are talking about the same thing here. A 2-D histogram would have bins of a specified X and Y range and the count would be shown on a Z axis. A 3-D histogram would have bins in the X and Y and Z ranges with the count shown in some other way (color?) You would need slicing for a 3-D histogram to make sense.I am going to assume that you mean a 2-D histogram that looks like a bunch of blocks coming out of the ground.
I think you will be better served to make this 2-D histogram into an image, where each pixel represents a specific bin in X and Y and the color specifies the count in that bin. I think the motion will only confuse the data. Use image or imagesc or imshow for this. Also, I would recommend an intensity based colormap (>>colormap), like spring or winter. This make the differences more apparent. Turn on the colorbar (>>colorbar)