I have a plot of some data (simple 2-d line) and I would like to add circles around some more interesting spots on it. Surprisingly matlab seems to have no simple way to create a physically round circles. I looked on the internet and most answers i found was to either use rectangle('Curvature',[1 1]) or pts = linspace(0,2*pi, 100); plot(sin(pts), cos(pts)); and fixing the aspect ration of a plot to 1. In my case axes have scales that differ by several orders of magnitude so fixing the aspect ration is no option.
I was trying different ways to get the right x/y scale factor but it still seems I'm missing something. My current attempt is:
function hc = circle(x, y, xr)
gca_ylim = get(gca, 'ylim');
gca_xlim = get(gca, 'xlim');
gca_pos = get(gca, 'Position');
gcf_pos = get(gcf, 'Position');
gcf_ar = get(gca, 'DataAspectRatio');
%mod = gca_pos(4)/gca_pos(3) *abs(gca_ylim(2)-gca_ylim(1))/abs(gca_xlim(2)-gca_xlim(1))*gcf_pos(3)/gcf_pos(4);
mod = gca_pos(4)/gca_pos(3)*gcf_ar(2)/gcf_ar(1)*gcf_pos(3)/gcf_pos(4);
yr = xr*mod;
rectangle('Position',[x-xr,y-yr,xr*2,xr*mod*2], 'Curvature',[1,1]);
end
The circles that i got that way are still a bit elongated and I have no idea why. If there is any simple method to get circles in a plot - please share.
PS I know that if I resize plot or add some more stuff to it and change scaling the circles will re-scale with the entire plot. This is not a problem in my case - Figure gets printed out without manual manipulation (no window resizing) and I can add them as the last objects.
Another option:
>> h = plot(rand(1,5),rand(1,5),'o');
>> set(h, 'MarkerSize', 100);
If you want scale invariant circles, you could use scatter command. You can also set the size smaller or larger.
scatter(X,Y,S) draws each circle with the size specified by S. To plot
each circle with equal size, specify S as a scalar. To plot each
circle with a specific size, specify S as a vector with length equal
to the length of X and Y.
http://www.mathworks.com/help/matlab/ref/scatter.html
Related
I have fitted a bounding box on image which have concerned area is human silhouette in BW, and found centroid according to concerned human silhouette?
now i have to plot red line by 45 degree angle from centroid.
from centroid to vertical, horizental and diagonal logic? Need code in MATLAB?
I have fitted a bounding box on image and found centroid according to concerned human silhouette?
% bounding box
labeledImage = bwlabel(Ibw);
blobMeasurements = regionprops(labeledImage, 'BoundingBox');
thisBlobsBoundingBox = blobMeasurements.BoundingBox;
subImage = imcrop(Ibw, thisBlobsBoundingBox);
figure, imshow(subImage);
imwrite(subImage,fullfile(cd, strcat('Croped By BoundingBox','.png')));
%centroid
Ibw = imread('Croped By BoundingBox.png');
Ibw = imfill(Ibw,'holes');
Ilabel = bwlabel(Ibw);
stat = regionprops(Ilabel,'centroid');
imshow(Ibw),hold on;
for x = 1: numel(stat)
plot(stat(x).Centroid(1),stat(x).Centroid(2),'ro');
[rows, cols] =ndgrid(1:size(Ibw, 1), 1:size(Ibw, 2));
centroidrowcol = mean([rows(:) .* Ibw(:), cols(:) .* Ibw(:)]);
hold on
end
figure, imshow(Ibw);
imwrite(Ibw,fullfile(cd, strcat('Centroid','.png')));
plot lines from centroid to vercally horizentally and diagonally by 45 degree from centroid.
i have to obtaon these results
enter image description here
I'll start with assuming centroid, a length-2 vector, is the coordinates of a centroid -- since you already have them.
As a reminder, 45 degree lines centered at (0,0) are the images of the functions f(x)=x and f(x)=-x. In order to shift the lines to intercept points of your choice, say (x0, y0), you need f(x-x0)+y0. In other words, f(x)=x-x0+y0 and f(x)=-x+x0+y0 respectively.
Knowing the functions (whose images are) the lines of your desire, there are a number of things you can do in order to complete the task. You can define Matlab functions as such and plot the usual way with plot, or for a quick display, with ezplot:
ezplot(#(x)x-centroid(1)+centroid(2));
line() in Matlab allows you to draw a line segment between two points in 2D, among other things. With line(), you actually don't have to know the equations explicitly. You just need two points. So, one possibility is that you use [centroid(1)+1, centroid(2)+1] as your first point and [centroid(1)-1, centroid(2)-1] as your second point for the shifted f(x)=x line.
Just read the documentation for the correct syntax. In the above example, you would put in
line([centroid(1)+1;centroid(1)-1], [centroid(2)+1;centroid(2)-1]);
This draws a 45 degree line segment alright. You also want the length of the line segment to be visually sensible. You can either autoscale the length, ie. change the 1 into a formula, based on the size of your image. OR, you can make sure your segment is longer than the extent of the image but confine your plot to a certain size by changing xlim and ylim.
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
I've been plotting some figures and I want to save them multiple times zoomed in certain areas. All my views have pivot-point at y=0 except one, the last view. I've been using 'XLim' to change the view on the X axis. Now I need to pan the view on the Y axis. When using YLim, the aspect ratio of the image changes.
Is it possible to set some pivot point on the Y-axis?
Here is a minimum working code where my problem can be seen. Pauses 2 times.
f1 = figure(10);
hold on
axis equal
x = linspace(1,2*pi);
y = sin(x);
plot(x,y,'*')
xlims=[0 2*pi; 1 2*pi; 0.5 1.5];
for i = 1:size(xlims,1)
set(gca,'XLim',xlims(i,:))
disp('Here I am saving this view! (Paused)')
pause
end
% Now I want to PAN the Y axis and set the view around Y = 1.
% But still keep last set Xlims AND keep the aspect ratio of figure.
% When using YLim, the aspect ratio changes.
set(gca,'YLim',[0.9 1.1]) % Not what I have in mind.
% Just need to pivot Y=1...
There's no "panning" command as such. (there is a pan command but this just toggles the ability to pan using the mouse on or off).
But, there is the ability to set your axis limits dictating which portion of the plot to show, using the axis function, with manually specified limits.
e.g. axis([ xmin, xmax, ymin, ymax ]);
Furthermore, if you apply this after any other axis calls that affect shape / aspect ratio (e.g. axis square or axis equal) then these will be preserved.
Note that in your particular example, the use of axis equal therefore necessarily means that for a smaller range of Y your aspect ratio will be smaller. Therefore you might to opt against that.
Alternatively, you can also control the aspect ratio manually with the daspect function. e.g., in your example:
>> axis equal % your initial axis / aspect ratio situation
>> A = axis;
>> axis([0.5,1.5,0.9,1.1]); % your new values. aspect ratio will change to preserve equality
>> B = axis;
>> Y_factor = (A(4)-A(3))/(B(4)-B(3));
>> daspect([Y_factor, 1, 1]); % back to the old "aspect ratio"
However, this clearly means that the two axes are now not "equal", and the comparison with previous graphs might be misleading. This may or may not be a factor to take into consideration.
Finally, if what you're really after is simply to make sure your axes object does not change size, you can simply set the size of your axes object manually (e.g. set(gca, 'position', [x,y,x_width, y_width]) ) and ensure axis is on normal mode (i.e. adapting to the axes size).
The answer given in a comment by #jodag is perfect. Since comments have a tendency to disappear I thought I would reproduce it here.
To "pan", you need to change both limits without changing their interval. Since you can read the current interval with
get(gca, 'YLim')
you can simply pan the y axis by a distance dy with the command:
set(gca, 'YLim', get(gca, 'YLim')+dy)
I struggle a little bit with overlapping axis numbers of the y and x axis like it is shown in the image. I'd like to keep the size of the numbers and therefore think that simply shifting the numbers away from the axis itself would be an appropriate way to handle this issue.
Is there a possibility to do that?
Thanks in advance,
Joe
Here is a little workaround using text annotations. Basically you clear the current XTick labels and replace them with similar labels, but you can specify the distance from the axis:
clc
clear
close all
x = 1:20;
hPlot = plot(x,sin(x));
set(gca,'xaxisLocation','top');
set(gca,'XTickLabel',[]); %// Clear current XTickLabel
ylim = get(gca,'YLim'); %// Get y limit of the plot to place your text annotations.
for k = 2:2:20
text(k,ylim(2)+0.1,num2str(k),'HorizontalAlignment','Center') %// Play with the 'ylim(1) -0.1' to place the label as you wish.
end
Giving this:
Of course now it's exaggerated and you can do the same for the y axis if you want (using the 'XLim' property of the current axis,gca).
Suppose that I have 2 figures in MATLAB both of which plot data of size (512x512), however one figure is being plotted by an external program which is sets the axis parameters. The other is being plotted by me (using imagesc). Currently the figures, or rather, the axes are different sizes and my question is, how do I make them equal?.
The reason for my question, is that I would like to export them to pdf format for inclusion in a latex document, and I would like to have them be the same size without further processing.
Thanks in Advance, N
Edit: link to figures
figure 1: (big)
link to smaller figure (i.e. the one whose properties I would like to copy and apply to figure 1)
For this purpose use linkaxes():
% Load some data included with MATLAB
load clown
% Plot a histogram in the first subplot
figure
ax(1) = subplot(211);
hist(X(:),100)
% Create second subplot
ax(2) = subplot(212);
Now link the axes of the two subplots:
linkaxes(ax)
By plotting on the second subplot, the first one will adapt
imagesc(X)
First, you have the following:
Then:
Extending the example to images only:
load clown
figure
imagesc(X)
h(1) = gca;
I = imread('eight.tif');
figure
imagesc(I)
h(2) = gca;
Note that the configurations of the the first handle prevail:
linkaxes(h)
1.Get the handle of your figure and the axes, like this:
%perhaps the easiest way, if you have just this one figure:
myFigHandle=gcf;
myAxHandle=gca;
%if not possible, you have to search for the handles:
myFigHandle=findobj('PropertyName',PropertyValue,...)
%you have to know some property to identify it of course...
%same for the axes!
2.Set the properties, like this:
%set units to pixels (or whatever you prefer to make it easier to compare to the other plot)
set(myFigHandle, 'Units','pixels')
set(myAxHandle, 'Units','pixels')
%set the size:
set(myFigHandle,'Position',[x_0 y_0 width height]) %coordinates on screen!
%set the size of the axes:
set(myAxHandle,'Position',[x_0 y_0 width height]) %coordinates within the figure!
Ok, based on the answer of #Lucius Domitius Ahenoba here is what I came up with:
hgload('fig1.fig'); % figure whose axis properties I would like to copy
hgload('fig2.fig');
figHandles = get(0,'Children');
figHandles = sort(figHandles,1);
ax(1) = findobj(figHandles(1),'type','axes','-not','Tag','legend','-not','Tag','Colorbar');
ax(2) = findobj(figHandles(2),'type','axes','-not','Tag','legend','-not','Tag','Colorbar');
screen_pos1 = get(figHandles(1),'Position');
axis_pos1 = get(ax(1),'Position');
set(figHandles(2),'Position',screen_pos1);
set(ax(2),'Position',axis_pos1);
This is the 'before' result:
and this is the 'after' result:
Almost correct, except that the aspect ratios are still off. Does anybody know how to equalize everything related to the axes? (I realize that I'm not supposed to ask questions when posting answers, however adding the above as a comment was proving a little unwieldy!)