Add centroid to plot - Matlab - matlab

In the following script I get the coordinates of and coins in an image, later they are plotted. How can I also add to the plot the centroid calculated using stat (in red, marked as X)?
Script:
clc;
clear;
I = imread('coins.png');
imshow(I)
BW = im2bw(I);
BW_filled = imfill(BW,'holes');
stat = regionprops(BW_filled,'centroid');
boundaries = bwboundaries(BW_filled);
for k=1:10
b = boundaries{k};
plot(b(:,2),b(:,1),'g','LineWidth',3);
hold on;
end

Add
plot(stat(k).Centroid(1), stat(k).Centroid(2), 'rx');
after
plot(b(:,2), b(:,1), 'g', 'LineWidth', 3);
hold on;
You can also apply any additional customisations to the centroid point like
plot(stat(k).Centroid(1), stat(k).Centroid(2), 'rx', 'LineWidth', 3);
Explanation
stat(k) will get the kth element of stat. stat(k).Centroid will extract the centroid as [x, y] and we can then reference the x coordinate of the centroid as stat(k).Centroid(1) and the y as stat(k).Centroid(2).
Alternative Improvements
Some improvements to your code that I would suggest are
Put close all at the top of the script. This will close all currently open figures
Add figure; hold on; before the for loop and remove hold on from within the for loop. Calling hold on; multiple times is redundant.

Related

Disable axes scale in Matlab

When I use ginput to mark tqo points it is followed by updating the axes scales. How can I stop the "auto-scaling" of the axes in Matlab?
Code:
clc;
clear all;
close all;
grid on;
message = sprintf('Select two points for a line.');
uiwait(helpdlg(message));
[x, y] = ginput(2);
Xorigin1 = x(1);
Xorigin2 = x(2);
Yorigin1 = y(1);
Yorigin2 = y(2);
plot([Xorigin1,Xorigin2],[Yorigin1,Yorigin2], 'b-', 'LineWidth',2);
hold on;
plot([Xorigin1,Xorigin2],[Yorigin1,Yorigin2], 'r+', 'LineWidth',2,'MarkerSize', 12);
Both the x- and y-axis are by default set to automically rescale their limits whenever data in the figure is changed.
You can overwrite this using
xlim manual
ylim manual

Computing the surface between two lines

I have the following figure, where I plotted two surfaces and I wanted to indicate the intersection of both of them. To do that, I did the following:
zdiff = z1-z2;
C = contours(x,y,zdiff,[0 0]);
xL = C(1, 2:end);
yL = C(2, 2:end);
zL = interp2(x, y, z1, xL, yL);
line(xL, yL, zL, 'Color', 'k', 'LineWidth', 2,'Linestyle','--'); hold on;
line(xL, yL, zeros(size(zL)), 'Color', 'k', 'LineWidth', 2); hold off;
Now, I want to plot the vertical surface between the actual intersection (dash line) and its projection over XY (solid line), but I cannot figure out how to do that. Any ideas?
Another really simple option:
dist = (diff(xL).^2+diff(yL).^2).^0.5; %distance between x,y
cdist = [0, cumsum(dist)]; %cumsum of the distance
area = trapz(cdist,zL); %The area
Why not calculating it manually?
Something like (untested):
Area = 0
for i=1:numel(xL)-1
base = sqrt( (xL(i)-xL(i+1))^2 + (yL(i)-yL(i+1))^2);
Area =Area + base * (zL(i) + zL(i+1))/2;
end;
maybe not pretty but its a oneliner it might do the trick. maybe you have to adjust the format as this code is for (1,N) vectors
xL=(1:100); %size 1 100
yL=(1:100) ;%size 1 100
zL=rand(1,100);%size 1 100
line(xL,yL,zL)
line(xL,yL,zeros(size(zL)))
hold on
surf(repmat(xL,100,1),repmat(yL,100,1),cell2mat(arrayfun(#(x,y) linspace(x,y,100)',zL,zeros(size(zL)),'UniformOutput',false)))
xL=sin((1:30)/10); % Data generation for test only. Use your data
yL=cos((1:30)/10); % Data generation for test only. Use your data
zL=2+xL.*yL; % Data generation for test only. Use your data
surf([xL;xL],[yL;yL],[zeros(size(zL));zL]); % plot the surface

Shift the z -value of contour plot in Matlab 2014b

I'm trying to make a figure of a surface plot, and beneath the surface I wish to show the contour lines, but I want the contour to be at z = -1 instead of at the default value 0. I found a previous post about this problem here, but when I try the solution the contour is still at z = 0. Maybe it has something to do with the version of MATLAB I'm using, which is 2014b?
Any ideas on how to make it work?
The code I tried:
%# plot surface and contour
Z = peaks;
surf(Z), hold on
[~,h] = contourf(Z); %# get handle to contourgroup object
%# change the ZData property of the inner patches
hh = get(h,'Children'); %# get handles to patch objects
for i=1:numel(hh)
zdata = ones(size( get(hh(i),'XData') ));
set(hh(i), 'ZData',-10*zdata)
end
So, I couldn't really figure out to do it as proposed in the example I found and posted, but I found a way that works. What I ended up doing was basically this:
figure
hold on
surf(X,Y,Z+1);
contour(X,Y,Z);
zz = get(gca,'ZTick');
set(gca,'ZTickLabel',sprintf('%3.1f\n',zz-1));
This gets me the surf and contour in the same figure, but yields some problems with color mappings.
I figured out how to solve the problem with color mappings the user Kine faced. Note: I've done the following code on MATLAB R2015b:
offset = 0.5;
plotHandle = surfc(X1, Y1, Z1);
hold on;
% This line moves the surface up from its original location and increases the space between the surface and the contour plot
plotHandle(1).ZData = plotHandle.ZData + offset;
% However in doing so the color mappings are changed. So, the line below restores these mappings
plotHandle(1).CData = plotHandle.CData - offset;
% This line changes fills the contour plot
plotHandle(2).Fill = 'on';
grid on;
% The following lines draw critical areas on the contour line, as it was more readable in my case
axisHandle = gca;
ZHeight = axisHandle.ZLim(1);
plot3(X2, Y2, ZHeight, 'o', 'MarkerSize', 10, 'LineWidth', 1, 'Color', 'k', 'MarkerFaceColor', 'm');
plot3(Y2, X2, ZHeight, 'o', 'MarkerSize', 10, 'LineWidth', 1, 'Color', 'k', 'MarkerFaceColor', 'm');
hold off
I got the same problem.
And finally, I got the contourf on plane Z=-10.
My MATLAB version is
MATLAB Version: 8.5.0.197613 (R2015a)
hope the codes work 4 you
clear all
clc
[X,Y,Z] = peaks;
[~,hContour] = contourf(X,Y,Z,20,'edgecolor','none');
hContour.ContourZLevel = -10; % set the contour's Z position
view(44,30)
colormap(jet)

Draw log graph curve on Matlab by clicking?

I'd like to draw a curve on an empty (semilog-y) graph by clicking the points I want it to run through, on the X-Y plane.
Is there a function for this?
edit: I'm trying to do this by obtaining the position of last pointer click -
axis([0 3000 0 1000]);
co=get(gcf, 'CurrentPoint');
It seems to return the cursor position at the time of execution, but it does not change later.
edit2: Here's what works for me. The actual drawing I can do by using the arrays of points collected.
clear
clc
h=plot(0);
grid on;
xlim([0 3000]);
ylim([0 1000]);
datacursormode on;
% Enlarge figure to full screen.
screenSize = get(0,'ScreenSize');
set(gcf, 'units','pixels','outerposition', screenSize);
hold on;
% Print the x,y coordinates - will be in plot coordinates
x=zeros(1,10); y=zeros(1,10);
for p=1:10;
[x(p),y(p)] = ginput(1) ;
% Mark where they clicked with a cross.
plot(x(p),y(p), 'r+', 'MarkerSize', 20, 'LineWidth', 3);
% Print coordinates on the plot.
label = sprintf('(%.1f, %.1f)', x(p), y(p));
text(x(p)+20, y(p), label);
end
Not really, but now there is:
function topLevel
%// parameters
xrange = [0 100];
yrange = [1e-4 1e4];
%// initialize figure, plot
figure, clf, hold on
plot(NaN, NaN);
axis([xrange yrange]);
set(gca, 'YScale', 'log')
t = text(sum(xrange)/2, sum(yrange)/2, ...
'<< Need at least 3 points >>',...
'HorizontalAlignment', 'center');
%// Main loop
xs = []; p = [];
ys = []; P = [];
while true
%// Get new user-input, and collect all of them in a list
[x,y] = ginput(1);
xs = [xs; x]; %#ok<AGROW>
ys = [ys; y]; %#ok<AGROW>
%// Plot the selected points
if ishandle(p)
delete(p); end
p = plot(xs, ys, 'rx');
axis([xrange yrange]);
%// Fit curve through user-injected points
if numel(xs) >= 3
if ishandle(t)
delete(t); end
%// Get parameters of best-fit in a least-squares sense
[A,B,C] = fitExponential(xs,ys);
%// Plot the new curve
xp = linspace(xrange(1), xrange(end), 100);
yp = A + B*exp(C*xp);
if ishandle(P)
delete(P); end
P = plot(xp,yp, 'b');
end
end
%// Fit a model of the form y = A + B·exp(C·x) to data [x,y]
function [A, B, C] = fitExponential(x,y)
options = optimset(...
'maxfunevals', inf);
A = fminsearch(#lsq, 0, options);
[~,B,C] = lsq(A);
function [val, B,C] = lsq(A)
params = [ones(size(x(:))) x(:)] \ log(abs(y-A));
B = exp(params(1));
C = params(2);
val = sum((y - A - B*exp(C*x)).^2);
end
end
end
Note that as always, fitting an exponential curve can be tricky; the square of the difference between model and data is exponentially much greater for higher data values than for lower data values, so there will be a strong bias to fit the higher values better than the lower ones.
I just assumed a simple model and used a simple solution, but this gives a biased curve which might not be "optimal" in the sense that you need it to be. Any decent solution really depends on what you want specifically, and I'll leave that up to you ^_^

How can I specify to which figure a plot should go?

I have multiple figures open, and I want to update them independently during runtime. The following toy example should clarify my intention:
clf;
figure('name', 'a and b'); % a and b should be plotted to this window
hold on;
ylim([-100, 100]);
figure('name', 'c'); % only c should be plotted to this window
a = 0;
b = [];
for i = 1:100
a = a + 1;
b = [b, -i];
c = b;
xlim([0, i]);
plot(i, a, 'o');
plot(i, b(i), '.r');
drawnow;
end
The problem here is that when I open the second figure, I cannot tell the plot functions to plot to the first one instead of the second (and only c should be plotted to the second).
You can use something like
figure(1)
plot(x,y) % this will go on figure 1
figure(2)
plot(z,w) % this will go on another figure
The command will also set the figure visible and on top of everything.
You can switch back and forth between the figures as necessary by issuing the same figure command. Alternatively, you can use the handle to the figure as well:
h=figure(...)
and then issue figure(h) instead of using numeric indices. With this syntax, you can also prevent the figure from popping up on top by using
set(0,'CurrentFigure',h)
You can specify the axes-object in the plot-command. See here:
http://www.mathworks.de/help/techdoc/ref/plot.html
So, open a figure, insert the axes, save the id of the axes object, and then plot into it:
figure
hAx1 = axes;
plot(hAx1, 1, 1, '*r')
hold on
figure
hAx2 = axes;
plot(hAx2, 2, 1, '*r')
hold on
plot(hAx2, 3, 4, '*b')
plot(hAx1, 3, 3, '*b')
Alternatively, you can use gca instead of creating the axes object yourself (because it's automatically created within the actual figure, when it doesn't exist!)
figure
plot(1,1)
hAx1 = gca;
hold on
figure
plot(2,2)
plot(hAx1, 3, 3)
See the following hierarchy representing the relationship between figures and axes
From http://www.mathworks.de/help/techdoc/learn_matlab/f3-15974.html.