linkprop objects with hgtransform - matlab

Literately figured out the last problem as soon as I posted. Was able to fix many of the issues I was having. Now the problem revolves around hgtransform and linkprop. How does one copy the object location and transformation to additional figures. The code below will copy objects from the first axes to the next and animate making all of them move. However it doesn't copy the transformation.
fig = figure();
% create subplots for stim system 3 plate setup
for aa = 1:3
Stimsubfigures{aa} = axes(...
'Position',[((aa*.21)-.2),.2,.2,.2],'color','none');
set(Stimsubfigures{aa},'xLim',[-320,320])
set(Stimsubfigures{aa},'YLim',[-240,240])
set(Stimsubfigures{aa},'Visible','off')
end
axes(Stimsubfigures{1});
for aa = 1:10
Xdata = [1+aa*50,10+aa*50,10+aa*50,1+aa*50];
ObjectTransformation{aa,1} = hgtransform; % Add object to end of transformation list
ObjectList{aa,1} = patch(... % Add object to end of Object list, bind to transformation list
'Parent', ObjectTransformation{aa}, ...
'XData',Xdata, 'YData',[1,1,20,20],...
'Facecolor', [1,0,0], 'EdgeColor', [1,0,0], ...
'visible','on');
ObjectTransformation{aa,1}.Matrix = makehgtform('zrotate',50);
NextStepX{aa,1} = Xdata;
end
tmp = transpose([ObjectList{:}]);
tmptrans = transpose([ObjectTransformation{:}]);
TrialLength = 10;
% copy objects to other figures
copyobj(tmp,Stimsubfigures{2})
copyobj(tmp,Stimsubfigures{3})
property_names = {'XData', 'YData', 'ZData'};
for aa = 1:10
linked_objects = [tmp(aa),...
Stimsubfigures{2}.Children(aa),...
Stimsubfigures{3}.Children(aa)];
hlink{aa} = linkprop(linked_objects,property_names);
end
timer = tic();
while true
t1 = toc(timer);
if t1 >= TrialLength, break;end % break loop once time trial ends
NextStepX = cellfun(#(x) x+1,NextStepX,'UniformOutput',false);
[tmp.XData] = NextStepX{:};
drawnow;
pause(0.1);
step = NextStepX;
end
for aa = 1:3
delete(Stimsubfigures{aa}.Children)
end
When I change this section to copy the transformation, the objects transform correctly but lose the animation.
% copy objects to other figures
copyobj(tmptrans,Stimsubfigures{2})
copyobj(tmptrans,Stimsubfigures{3})
property_names = {'XData', 'YData', 'ZData'};
trans_names = {'zrotate'};
for aa = 1:10
linked_objects = [tmp(aa),...
Stimsubfigures{2}.Children(aa),...
Stimsubfigures{3}.Children(aa)];
Trlink_objects = [tmptrans(aa),...
Stimsubfigures{2}.Children(aa),...
Stimsubfigures{3}.Children(aa)];
hlink{aa} = linkprop(linked_objects,trans_names);
Trhlink{aa} = linkprop(Trlink_objects,trans_names);
end
I tried to perform an copyobj on both handles but it just results in two sets of objects. How can one link all three together so I can perform rotation change Xdata?

Figured it out. Will post answer for those who had similar questions.
The hgtransform is a parent of the objects which are being rotated. because of this when I copy the hgtransform, the children are also copied, hence why the objects appear in the proper orientation in the other windows. From here, I need to link the children from the copied parents to generate the animation.
% copy objects to other figures
copyobj(tmptrans,Stimsubfigures{2})
copyobj(tmptrans,Stimsubfigures{3})
property_names = {'XData', 'YData', 'ZData'};
for aa = 1:10
linked_objects = [tmptrans(aa).Children(1),...
Stimsubfigures{2}.Children(aa).Children(1),...
Stimsubfigures{3}.Children(aa).Children(1)];
hlink{aa} = linkprop(linked_objects,trans_names);
end
By replacing the part of code with the section above. One can transform the objects and animate.

Related

duplicate axes in a figure

Sorry for a somewhat simple question. I am trying to generate a figure which displays the same animation but in different subplots. I'm starting simple and primarily focusing on duplicating the plot first.
I was originally thinking of attaching the handle for the subplot to the other plots
afig = figure;
a1{1} = axes('Position',[.01,.2,.2,.2], 'color','none','Xlim',[-10,10],'Ylim',[-10,10]);
a1{2} = a1{1};
a1{2}.Position = [.3,.2,.2,.2];
a1{3} = a1{1};
a1{3}.Position = [.6,.2,.2,.2];
obj = patch('Parent',a1{1},'XData',[1,3,1],'YData',[1,1,3]);
But this just moves the existing plot around as opposed to duplicating it. (on account that I'm still referring to the same object even though it has different names)
I next thought of just recreating the same setup 3 times and then update the animation, looping through the three, but this feels inefficient and computationally intensive.
afig = figure;
a1{1} = axes('Position',[.01,.2,.2,.2], 'color','none','Xlim',[-10,10],'Ylim',[-10,10]);
a1{2} = axes('Position',[.3,.2,.2,.2], 'color','none','Xlim',[-10,10],'Ylim',[-10,10]);
a1{3} = axes('Position',[.6,.2,.2,.2], 'color','none','Xlim',[-10,10],'Ylim',[-10,10]);
obj{1} = patch('Parent',a1{1},'XData',[1,3,1],'YData',[1,1,3]);
obj{2} = patch('Parent',a1{2},'XData',[1,3,1],'YData',[1,1,3]);
obj{3} = patch('Parent',a1{3},'XData',[1,3,1],'YData',[1,1,3]);
Is there a way to call 1 subplot, update that 1 subplot but have it propagate to other subplots?
It really depends on what you want to do in the end, how complex the animations are, and if you can prepare your plots in advance.
First, if there are only a few objects, you could use the linkprop function to link graphics objects' properties:
afig = figure;
a1{1} = axes('Position',[.01,.2,.2,.2], 'color','none','Xlim',[-10,10],'Ylim',[-10,10]);
obj = patch('Parent',a1{1},'XData',[1,3,1],'YData',[1,1,3]);
a1{2} = copyobj(a1{1}, afig);
a1{2}.Position = [.3,.2,.2,.2];
a1{3} = copyobj(a1{1}, afig);
a1{3}.Position = [.6,.2,.2,.2];
linked_objects = [ a1{1}.Children, a1{2}.Children, a1{3}.Children];
property_names = {'XData', 'YData', 'ZData'};
hlink = linkprop(linked_objects, property_names);
for ii = 1:10
obj.XData(1) = ii;
drawnow
pause(0.01)
end
Here, we first create the base plot, then we copy the axes (note that children objects get copied, too, but not callbacks and other properties, see copyboy). We then link properties we may want to change during the animation (note that you could also link the axes' view properties), and then change them in a loop.
Another approach would be to change the object's properties in a main axes in every loop iteration, and copy the main axes' children to the other axes afterwards. This approach may be more costly – as a lot of objects get copied and rendered – but on the other hand, individual properties do not have to be tracked. Here is an example:
afig = figure;
a1{1} = axes('Position',[.01,.2,.2,.2], 'color','none','Xlim',[-10,10],'Ylim',[-10,10]);
obj = patch('Parent',a1{1},'XData',[1,3,1],'YData',[1,1,3]);
a1{2} = copyobj(a1{1}, afig);
a1{2}.Position = [.3,.2,.2,.2];
a1{3} = copyobj(a1{1}, afig);
a1{3}.Position = [.6,.2,.2,.2];
for ii = 1:10
obj.XData(1) = ii;
delete(a1{2}.Children);
delete(a1{3}.Children);
copyobj(a1{1}.Children, a1{2});
copyobj(a1{1}.Children, a1{3});
drawnow
pause(0.01)
end
Finally, it could be an option to use getframe to just capture the rendered image and display it in the copy axes.

How to add standrad deviation and moving average

What I want to is:
I got folder with 32 txt files and 1 excle file, each file contain some data in two columns: time, level.
I already managed to pull the data from the folder and open each file in Matlab and get the data from it. What I need to do is create plot for each data file.
each of the 32 plots should have:
Change in average over time
Standard deviation
With both of this things I am straggling can't make it work.
also I need to make another plot this time the plot should have the average over each minute from all the 32 files.
here is my code until now:
clc,clear;
myDir = 'my path';
dirInfo = dir([myDir,'*.txt']);
filenames = {dirInfo.name};
N = numel(filenames);
data=cell(N,1);
for i=1:N
fid = fopen([myDir,filenames{i}] );
data{i} = textscan(fid,'%f %f','headerlines',2);
fclose(fid);
temp1=data{i,1};
time=temp1{1};
level=temp1{2};
Average(i)=mean(level(1:find(time>60)));
AverageVec=ones(length(time),1).*Average(i);
Standard=std(level);
figure(i);
plot(time,level);
xlim([0 60]);
hold on
plot(time, AverageVec);
hold on
plot(time, Standard);
legend('Level','Average','Standard Deviation')
end
the main problam with this code is that i get only average over all the 60 sec not moving average, and the standard deviation returns nothing.
few things you need to know:
*temp1 is 1x2 cell
*time and level are 22973x1 double.
Apperently you need an alternative to movmean and movstd since they where introduced in 2016a. I combined the suggestion from #bla with two loops that correct for the edge effects.
function [movmean,movstd] = moving_ms(vec,k)
if mod(k,2)==0,k=k+1;end
L = length(vec);
movmean=conv(vec,ones(k,1)./k,'same');
% correct edges
n=(k-1)/2;
movmean(1) = mean(vec(1:n+1));
N=n;
for ct = 2:n
movmean(ct) = movmean(ct-1) + (vec(ct+n) - movmean(ct-1))/N;
N=N+1;
end
movmean(L) = mean(vec((L-n):L));
N=n;
for ct = (L-1):-1:(L-n)
movmean(ct) = movmean(ct+1) + (vec(ct-n) - movmean(ct+1))/N;
N=N+1;
end
%mov variance
movstd = nan(size(vec));
for ct = 1:n
movstd(ct) = sum((vec(1:n+ct)-movmean(ct)).^2);
movstd(ct) = movstd(ct)/(n+ct-1);
end
for ct = n+1:(L-n)
movstd(ct) = sum((vec((ct-n):(ct+n))-movmean(ct)).^2);
movstd(ct) = movstd(ct)/(k-1);
end
for ct = (L-n):L
movstd(ct) = sum((vec((ct-n):L)-movmean(ct)).^2);
movstd(ct) = movstd(ct)/(L-ct+n);
end
movstd=sqrt(movstd);
Someone with matlab >=2016a can compare them using:
v=rand(1,1E3);m1 = movmean(v,101);s1=movstd(v,101);
[m2,s2] = moving_ms(v,101);
x=1:1E3;figure(1);clf;
subplot(1,2,1);plot(x,m1,x,m2);
subplot(1,2,2);plot(x,s1,x,s2);
It should show a single red line since the blue line is overlapped.

Updating data cursor position within a loop

I have a function updating a plot given a new point (it appends the last point to the line). I want to have the possibility to update the cursor such that it automatically appears on the last point. Currently, I do
for i = 1 : numel(dataObjs)
X{i}(end+1) = newX{i};
Y{i}(end+1) = newY{i};
set(dataObjs(i), 'XData', X{i});
set(dataObjs(i), 'YData', Y{i});
set(cursorMode, 'enable', 'on');
% Create a new data tip
hDatatip = cursorMode.createDatatip(dataObjs(i));
% Update annotation position
hDatatip.Cursor.Position = [newX{i}, newY{i} 0];
end
However, it is slow since it always creates a new cursor. I cannot find where the old one is stored such that I don't have to create new ones.
Rather than creating a new cursor object every time you add new data, you could create it once (per plot object) and save it to a variable. Then inside of the loop you can update the position.
set(cursorMode, 'Enable', 'on')
%// Create all of your data cursors once
for k = 1:numel(dataObjs)
datacursors(k) = cursorMode.createDatatip(dataObjs(k));
end
%// Now when you add new data
for k = 1 : numel(dataObjs)
X{k}(end+1) = newX{k};
Y{k}(end+1) = newY{k};
set(dataObjs(k), 'XData', X{k});
set(dataObjs(k), 'YData', Y{k});
%// Update annotation position
datacursors(k).Cursor.Position = [newX{k}, newY{k} 0];
end
And for the sake of a complete example:
hfig = figure();
data = rand(5,4);
hplots = plot(data);
cursorMode = datacursormode(hfig);
for k = 1:numel(hplots)
datacursors(k) = cursorMode.createDatatip(hplots(k));
end
for k = 1:size(data, 1)
for m = 1:numel(hplots)
set(datacursors(m), 'Position', [k, data(k,m)])
end
end
Update
As an alternative you could use findall to actually locate the data cursor for a given plot. The only downside of this is that it adds the overhead of having to find the data cursor every time that you want to update it.
datacursor = findall(ancestor(hplots(k), 'axes'), 'DataSource', hplots(k));
Another alternative
Rather than storing everything within a variable, you could store the data cursor in the UserData property of the plot objects themselves.
for k = 1:numel(hplots)
set(hplots(k), 'UserData', cursorMode.createDatatip(hplots(k)))
end
Then to use it:
set(get(hplots(k), 'UserData'), 'Position', [x, y, 0])

How to force MATLAB function area to hold on in figure

I'm working on this function which gets axis handler and data, and is supposed to plot it correctly in the axis. The function is called in for loop. It's supposed to draw the multiple data in one figure. My resulted figure is shown below.
There are only two correctly plotted graphs (those with four colors). Others miss areas plotted before the final area (red area is the last plotted area in each graph). But the script is same for every axis. So where can be the mistake? The whole function is written below.
function [] = powerSpectrumSmooth(axis,signal,fs)
N= length(signal);
samplesPer1Hz = N/fs;
delta = int16(3.5*samplesPer1Hz); %last sample of delta frequncies
theta = int16(7.5*samplesPer1Hz); %last sample of theta frequncies
alpha = int16(13*samplesPer1Hz); %last sample of alpha frequncies
beta = int16(30*samplesPer1Hz); %last sample of beta frequncies
x=fft(double(signal));
powerSpectrum = 20*log10(abs(real(x)));
smoothPS=smooth(powerSpectrum,51);
PSmin=min(powerSpectrum(1:beta));
y1=[(smoothPS(1:delta)); zeros(beta-delta,1)+PSmin];
y2=[zeros(delta-1,1)+PSmin; (smoothPS(delta:theta)); zeros(beta-theta,1)+PSmin];
y3=[zeros(theta-1,1)+PSmin; (smoothPS(theta:alpha)); zeros(beta-alpha,1)+PSmin];
y4=[zeros(alpha-1,1)+PSmin; (smoothPS(alpha:beta))];
a1=area(axis,1:beta,y1);
set(a1,'FaceColor','yellow')
hold on
a2=area(axis,1:beta,y2);
set(a2,'FaceColor','blue')
a3=area(axis,1:beta,y3);
set(a3,'FaceColor','green')
a4=area(axis,1:beta,y4);
set(a4,'FaceColor','red')
ADDED
And here is the function which calls the function above.
function [] = drawPowerSpectrum(axesContainer,dataContainer,fs)
size = length(axesContainer);
for l=1:size
powerSpectrumSmooth(axesContainer{l},dataContainer{l},fs)
set(axesContainer{l},'XTickLabel','')
set(axesContainer{l},'YTickLabel','')
uistack(axesContainer{l}, 'top');
end
ADDED 29th July
Here is a script which reproduces the error, so you can run it in your computer. Before running it again you might need to clear variables.
len = 9;
axesContainer = cell(len,1);
x = [0.1,0.4,0.7,0.1,0.4,0.7,0.1,0.4,0.7];
y = [0.1,0.1,0.1,0.4,0.4,0.4,0.7,0.7,0.7];
figure(1)
for i=1:len
axesContainer{i} = axes('Position',[x(i),y(i),0.2,0.2]);
end
dataContainer = cell(len,1);
N = 1500;
for i=1:len
dataContainer{i} = rand(1,N)*100;
end
for l=1:len
y1=[(dataContainer{l}(1:N/4)) zeros(1,3*N/4)];
y2=[zeros(1,N/4) (dataContainer{l}(N/4+1:(2*N/4))) zeros(1,2*N/4)];
y3=[zeros(1,2*N/4) (dataContainer{l}(2*N/4+1:3*N/4)) zeros(1,N/4)];
y4=[zeros(1,3*N/4) (dataContainer{l}(3*N/4+1:N))];
axes=axesContainer{l};
a1=area(axes,1:N,y1);
set(a1,'FaceColor','yellow')
hold on
a2=area(axes,1:N,y2);
set(a2,'FaceColor','blue')
hold on
a3=area(axes,1:N,y3);
set(a3,'FaceColor','green')
hold on
a4=area(axes,1:N,y4);
set(a4,'FaceColor','red')
set(axes,'XTickLabel','')
set(axes,'YTickLabel','')
end
My result of this script is plotted below:
Again only one picture contains all areas.
It looks like that every call to plot(axes,data) deletes whatever was written in axes.
Important note: Do not use a variable name the same as a function. Do not call something sin ,plot or axes!! I changed it to axs.
To solve the problem I just used the classic subplot instead of creating the axes as you did:
len = 9;
axesContainer = cell(len,1);
x = [0.1,0.4,0.7,0.1,0.4,0.7,0.1,0.4,0.7];
y = [0.1,0.1,0.1,0.4,0.4,0.4,0.7,0.7,0.7];
figure(1)
dataContainer = cell(len,1);
N = 1500;
for i=1:len
dataContainer{i} = rand(1,N)*100;
end
for l=1:len
y1=[(dataContainer{l}(1:N/4)) zeros(1,3*N/4)];
y2=[zeros(1,N/4) (dataContainer{l}(N/4+1:(2*N/4))) zeros(1,2*N/4)];
y3=[zeros(1,2*N/4) (dataContainer{l}(2*N/4+1:3*N/4)) zeros(1,N/4)];
y4=[zeros(1,3*N/4) (dataContainer{l}(3*N/4+1:N))];
axs=subplot(3,3,l);
a1=area(axs,1:N,y1);
set(a1,'FaceColor','yellow')
hold on
a2=area(axs,1:N,y2);
set(a2,'FaceColor','blue')
hold on
a3=area(axs,1:N,y3);
set(a3,'FaceColor','green')
hold on
a4=area(axs,1:N,y4);
set(a4,'FaceColor','red')
set(axs,'XTickLabel','')
set(axs,'YTickLabel','')
axis tight % this is to beautify it.
end
As far as I know, you can still save the axs variable in an axescontainer and then modify the properties you want (like location).
I found out how to do what I needed.
len = 8;
axesContainer = cell(len,1);
x = [0.1,0.4,0.7,0.1,0.4,0.7,0.1,0.4];
y = [0.1,0.1,0.1,0.4,0.4,0.4,0.7,0.7];
figure(1)
for i=1:len
axesContainer{i} = axes('Position',[x(i),y(i),0.2,0.2]);
end
dataContainer = cell(len,1);
N = 1500;
for i=1:len
dataContainer{i} = rand(1,N)*100;
end
for l=1:len
y1=[(dataContainer{l}(1:N/4)) zeros(1,3*N/4)];
y2=[zeros(1,N/4) (dataContainer{l}(N/4+1:(2*N/4))) zeros(1,2*N/4)];
y3=[zeros(1,2*N/4) (dataContainer{l}(2*N/4+1:3*N/4)) zeros(1,N/4)];
y4=[zeros(1,3*N/4) (dataContainer{l}(3*N/4+1:N))];
axes=axesContainer{l};
Y=[y1',y2',y3',y4'];
a=area(axes,Y);
set(axes,'XTickLabel','')
set(axes,'YTickLabel','')
end
The area is supposed to work with matrices like this. The tricky part is, that the signal in every next column is not plotted absolutely, but relatively to the data in previous column. That means, if at time 1 the data in first column has value 1 and data in second column has value 4, the second column data is ploted at value 5. Source: http://www.mathworks.com/help/matlab/ref/area.html

Matlab GUI for array of spots

I need to create a GUI in Matlab. It requires me to identify the spots for two images, and calculate the distance between them.
I have obtained the code for finding and encircling a single spot. It is as follows:
function [meanx,meany] = centroid(pic)
[x,y,z] = size(pic);
if(z==1)
;
else
pic = rgb2gray(pic);
end
% N=2;
% image = interp2(double(pic),N,'spline');
image = sort(sort(pic,1),2);
image =reshape(image,1,numel(image));
i=0;
while(i<3)
if(image(end)==image(end-1))
image(end)=[];
else
image(end)=[];
i=i+1;
end
end
threshold = image(end);
pic2 =(pic>=threshold);
pic=(pic-threshold).*uint8(pic2);
% % image=(pic-threshold+1).*uint8(image); %minus threshold
[rows,cols] = size(pic);
x = ones(rows,1)*[1:cols];
y = [1:rows]'*ones(1,cols);
area = sum(sum(pic));
if area ~= 0
meanx = sum(sum(double(pic).*x))/area;
meany = sum(sum(double(pic).*y))/area;
else
meanx = cols/2;
meany = rows/2;
end
However, I need it to work for multiple spots as shown below :
http://imgur.com/oEe0mRV,UAnbH5y#0
http://imgur.com/oEe0mRV,UAnbH5y#1
So far, I have come up with this, but it only circles separate spots and not all together.
PLEASE HELP - I need to encircle at least 10X10 spots and store their values, and do this for two images as shown above, and find the distance between them!
img1 = imread('r0.bmp');
centroidmat=zeros(10,10,2);
for numx=1:2
for numy=1:2
single_spot=img1((numx*220+780):((numx+1)*220+780),(numy*220+1272):((numy+1)*220+1272),:);
figure
imshow(single_spot);
figure
[cx,cy] = centroid(single_spot);
centroidmat(numx,numy,1)=cx;
centroidmat(numx,numy,2)=cy;
imshow(single_spot);
hold on;
plot(cx,cy,'og')
end
end
Please HELP GUYS! Any help is appreciated!
Would this work ? -
centroidmat=zeros(10,10,2);
for numx=1:2
for numy=1:2
single_spot=img1((numx*220+780):((numx+1)*220+780),(numy*220+1272):((numy+1)*220+1272),:);
[cx,cy] = centroid(single_spot);
centroidmat(numx,numy,1)=cx;
centroidmat(numx,numy,2)=cy;
figure,
imshow(single_spot);
hold on;
plot(cx,cy,'og')
end
end
I have only removed the redundant figure and imshow(single_spot); at the start of the loop, as they appear again later on inside the same loop.