MATLAB: Print contents of uipanel to PNG image - matlab

I don't know how to accomplish the following in MATLAB. I have a figure which looks like this:
In the figure, I have a panel with many subplots and a scrollbar that allows me to view a portion of the panel.
I want to save the whole contents of the panel to a PNG image file (not just the visible portion), i.e. I want to have a file which is a tall rectangle, and doesn't require scrolling.
The code for generating the figure is as follows:
function draw(obj)
figure;
panel1 = uipanel('Parent',1);
panel2 = uipanel('Parent',panel1);
panelheight = obj.nIterations / 2;
set(panel1,'Position',[0 0 0.97 1]);
set(panel2,'Position',[0 1-panelheight 1 panelheight]); %%
nPlot = 1;
for i=1:obj.nIterations
models = obj.iterations{i};
for nModel=1:length(models)
subplot(obj.nIterations,length(models)*2,nPlot);
nPlot = nPlot + 1;
drawTransitions(models{nModel});
set(gca,'Parent',panel2);
subplot(obj.nIterations,length(models)*2,nPlot);
nPlot = nPlot + 1;
drawRewards(models{nModel});
set(gca,'Parent',panel2);
end
end
s = uicontrol('Style','Slider','Parent',1,...
'Units','normalized','Position',[0.97 0 0.03 1],...
'Value',1,'Callback',{#slider_callback1,panel2,panelheight});
end
I have tried the following, without success.
The saveas funstion saves the whole figure, not just the panel. Also, it clips the invisible portion of the panel.
export_fig(panel2.'file.png') gives just a solid gray image.

Why don't you just scroll your panel and grab the frames and concatenate them all together? Here's some code that will basically do that. I would have posted am image, but I guess I don't have enough points for that. You may need to fiddle with the scrolling, and maybe making the slider invisible, but it works.
function printPanel(pnl,filename)
fig = figure(ancestor(pnl,'figure'));
pnl_units = get(pnl,'units');
fig_units = get(fig,'units');
set(pnl,'units','pixels')
set(fig,'units','pixels')
pnl_rect = getpixelposition(pnl);
fig_rect = getpixelposition(fig);
pnl_height = pnl_rect(4);
fig_height = fig_rect(4);
pnl_rect(2) = -pnl_height;
set(pnl,'position',pnl_rect)
N = ceil(pnl_height/fig_height);
CDATA = cell(N,1);
for i = 1:N
F = getframe(fig);
CDATA{i} = F.cdata;
pnl_rect(2) = pnl_rect(2)+fig_height;
set(pnl,'position',pnl_rect)
drawnow
end
set(pnl,'units',pnl_units)
set(fig,'units',fig_units)
imwrite(cat(1,CDATA{:}),filename)
end

You could get rid of the ui elements and just make a figure with all the subplots, and then export that one, using e.g. print -dpng ....
saveas takes a handle as a first argument. Maybe this does not have to be a figure or model handle, but could be a reference to the contents of the panel.

Related

How to plot a "moving" graph for real time values along the x-axis (using psychtoolbox)?

I am writing a code for a real time experiment using psychtoolbox to present the stimulus. In my experiment, I need to show the subject a graph that indicates his performance. I have plotted the graph using this simple code:
% Draw the graph
figure('visible','off','color',[0 0 0]);
pcolor([0 Num_timepoint+2],[-10 0],ones(2,2));
hold on;
pcolor([0 Num_timepoint+2],[0 10],2*ones(2,2));
colormap([79 167 255;255 187 221]/256);
plot(1:subloop,value0,'*-',...
'color',[0,0,0],...
'LineWidth',1,...
'MarkerSize',5,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[0.5,0.5,0.5]);
axis([0,Num_timepoint+2,-10,10]);
saveas(gcf,'line_chart.png'); %save it
close(figure);
line_chart=imread('line_chart.png'); %read it
resized_plot=imresize(line_chart,0.5);
imageSize = size(resized_plot);
[imageHeight,imageWidth,colorChannels]=size(resized_plot);
bottomRect = [xCenter-imageWidth/1.5, yCenter+gapdown, xCenter+imageWidth/1.5, yCenter+gapdown+imageHeight];
imageDisplay=Screen('MakeTexture', win0, resized_plot);
Screen('DrawTexture', win0, imageDisplay, [], bottomRect);
Unfortunately, this simple code is very slow. In addition, I couldn't make the graph moving along x axis, as soon as the new value comes.
Any help would be Awesome. Thanks in advance for your efforts.
Why are you saving the figure and redisplaying as an image? Maybe I'm missing something but you should be able to accomplish what you need by updating the existing plot with the fresh data using the handles properties of the plot:
.... First time through we need the initial plot ....
% Set up figure with colormaps and such but leave as 'visible','on'
hPlot = plot(plot(1:subloop,value0,'*-',...
'color',[0,0,0],...
'LineWidth',1,...
'MarkerSize',5,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[0.5,0.5,0.5]);
hAxes = gca;
.... Loop over your real time updates ....
% Assuming value0 has subject's results from t=0 to t=now
hPlot.XData = value0; hPlot.YData = [1:subloop];
hAxes.XLim = [0 numTimePoints+2];
hAxes.YLim = [-10 10]
.... Continue test and update value0 ....
I think that should keep your plots current without having to save the figure as image to file then reopen the image to display to subject.
If you want to move your data one sample, you can use the circshift function. For example, if you want your new values to appear on the left hand side, you can shift all values 1 sample rightward, then add your new value in the first position.
For converting a MATLAB figure to a Psychtoolbox texture, you don't need to save, then load the temporary images. You can instead use the getframe function to capture the MATLAB figure data, which can then be given to MakeTexture to turn it into a Psychtoolbox texture.
I'm not sure what values you're actually using for subloop, value0, etc. but there is an example that I think might be close to what you want. In this example, 30 frames of figures are plotted, with each figure being on screen for 1 second. New data points are generated randomly and appear from the left hand side of the figure.
Depending on the details of your experiment, you may find that this approach is still too slow. You could also create the figure directly via Psychtoolbox drawing methods like DrawLines, etc. though that would require more effort.
try
win0 = Screen('OpenWindow', 0, 0);
Num_timepoint = 100;
subloop = 100;
value0 = zeros(1,100);
num_demo_frames = 30;
% Draw the graph
fig_h = figure('visible','off','color',[0 0 0]);
pcolor([0 Num_timepoint+2],[-10 0],ones(2,2));
hold on;
pcolor([0 Num_timepoint+2],[0 10],2*ones(2,2));
colormap([79 167 255;255 187 221]/256);
plot_h = plot(1:subloop,value0,'*-',...
'color',[0,0,0],...
'LineWidth',1,...
'MarkerSize',5,...
'MarkerEdgeColor','k',...
'MarkerFaceColor',[0.5,0.5,0.5]);
axis([0,Num_timepoint+2,-10,10]);
for f = 1:num_demo_frames
new_value = randn(1,1);
data_values = plot_h.YData;
data_values = circshift(data_values, 1);
data_values(1) = new_value;
plot_h.YData = data_values;
plot_values = getframe(fig_h);
imageDisplay=Screen('MakeTexture', win0, plot_values.cdata);
Screen('DrawTexture', win0, imageDisplay);
Screen('Flip', win0);
WaitSecs(1);
end
sca;
catch e
sca;
rethrow(e);
end

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.

MATLAB combine two avi. together

I created two avi. file and they have the same length.
I wanted to combine them on a same background and one on top another one on the bottom.
The code I used for one of the animation is below, the other one is similar.
Is there any way I can do that?
load Results.mat;
I = imread('hex08.jpg');
[rows,columns,numberOfColorChannels] = size(I);
if numberOfColorChannels >1
I = I(:,:,2);
end
background = imresize(background, [rows,columns]);
figure(1);
hold on;
for i=1:500
A=angle(i,:);
J = imrotate(I,A,'crop');
mask = J == 0;
mask = bwareafilt(mask,4);
J(mask) = background(mask);
pause(0.01)
imshow(J);
imwrite(J,[num2str(i),'.png']);
end
imageNames = dir(fullfile('*.png'));
imageNames = {imageNames.name}';
outputVideo = VideoWriter(fullfile('hex08.avi'));
outputVideo.FrameRate = 15;
open(outputVideo)
for ii = 1:length(imageNames)
img = imread(fullfile(imageNames{ii}));
writeVideo(outputVideo,img)
end
close(outputVideo)
Here is one way this can be done:
Just like you read the image into img, you can read a second image (from the second video) into img2. Then, you can create a combined image of the two: imgCombined = [img ; img2]; which will contain the first image on top and the second on bottom (This will work if both images are the same width, otherwise you will need to resize/crop). When you write to the video file, use writeVideo(outputVideo,imgCombined).

Matlab get vector of specific pixels

I am pretty new to Matlab and encountered a problem when working with images.
I want to get a pixel that is in a specific colour (blue) in the following image:
image
My current code looks something like this:
function p = mark(image)
%// display image I in figure
imshow(image);
%// first detect all blue values higher 60
high_blue = find(image(:,:,3)>60);
%cross elements is needed as an array later on, have to initialize it with 0
cross_elements = 0;
%// in this iteration the marked values are reduced to the ones
%where the statement R+G < B+70 applies
for i = 1:length(high_blue)
%// my image has the size 1024*768, so to access the red/green/blue values
%// i have to call the i-th, i+1024*768-th or i+1024*768*2-th position of the "array"
if ((image(high_blue(i))+image(high_blue(i)+768*1024))<...
image(high_blue(i)+2*768*1024)+70)
%add it to the array
cross_elements(end+1) = high_blue(i);
end
end
%// delete the zero element, it was only needed as a filler
cross_elements = cross_elements(cross_elements~=0);
high_vector = zeros(length(cross_elements),2);
for i = 1:length(cross_elements)
high_vector(i,1) = ceil(cross_elements(i)/768);
high_vector(i,2) = mod(cross_elements(i), 768);
end
black = zeros(768 ,1024);
for i = 1:length(high_vector)
black(high_vector(i,2), high_vector(i,1)) = 1;
end
cc = bwconncomp(black);
a = regionprops(cc, 'Centroid');
p = cat(1, a.Centroid);
%// considering the detection of the crosses:
%// RGB with B>100, R+G < 100 for B<150
%// consider detection in HSV?
%// close the figure
%// find(I(:,:,3)>150)
close;
end
but it is not optimized for Matlab, obviously.
So i was wondering if there was a way to search for pixels with specific values,
where the blue value is larger than 60 (not hard with the find command,
but at the same time the values in the red and green area not too high.
Is there a command I am missing?
Since English isn't my native language, it might even help if you gave me some suitable keywords for googling ;)
Thanks in advance
Based on your question at the end of the code, you could get what you want in a single line:
NewImage = OldImage(:,:,1) < SomeValue & OldImage(:,:,2) < SomeValue & OldImage(:,:,3) > 60;
imshow(NewImage);
for example, where as you see you provide a restriction for each channel using logical operators, that you can customize of course (eg. using | as logical OR). Is this what you are looking for? According to your code you seem to be looking for specific regions in the image like crosses or coins is that the case? Please provide more details if the code I gave you is completely off the track :)
Simple example:
A = imread('peppers.png');
B = A(:,:,3)>60 & A(:,:,2)<150 & A(:,:,1) < 100;
figure;
subplot(1,2,1);
imshow(A);
subplot(1,2,2)
imshow(B);
Giving this:

Matlab while loop screenshot reload

I want to take a screenshot of my screen every second and then save it.
I want to use this to play a game, where there is a counter. I thought a while loop would keep refreshing the imshow(screenshot) however, it does not. Here is my code:
i = 1;
while i > 0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Screenshot
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
imshow(screenshot)
robo = java.awt.Robot;
t = java.awt.Toolkit.getDefaultToolkit();
%# Set the capture area as the size for the screen
rectangle = java.awt.Rectangle(t.getScreenSize());
%# Get the capture
image = robo.createScreenCapture(rectangle);
%# Save it to file
filehandle = java.io.File(sprintf('capture%d.jpg', 1));
javax.imageio.ImageIO.write(image,'jpg',filehandle);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Load screen
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
screenshot = imread('capture1.jpg'); % is using as a test
screenshot = imcrop(screenshot,[360 205 520 423]);
end
Have you tried using drawnow to update the current figure: http://www.mathworks.com/help/matlab/ref/drawnow.html?refresh=true