Desired Legend Layout:
I am using plot(.) in Matlab to plot following test data
I had 2 cases, each of which is tested for 4 conditions. I am using two markers for represent cases whereas each condition is assigned a distinct color. If I use default legend(.) command in matlab, I end up with 8 entries in legend which I think is a bit redundant.
I would like to have a legend with only 6 entries. Two markers and 4 colors. How can I get that in Matlab? I am open to both single column of double column legend solutions.
MWE:
function MinimalExample
MixedShade = [000 000 205; ... % Medium Blue
255 064 064; ... % Brown
000 233 255; ... % Aqua
255 185 015] ... % Gold
./255;
alpha = [0.1:0.1:0.4];
a = [1*1e0, 1*1e-1, 1*1e-2, 1*1e-3] ;
A = [1*a; 2*a; 3*a; 4*a];
b = [1*1e+1, 1*1e+2, 1*1e+3, 1*1e+4] ;
B = [1*b; 2*b; 3*b; 4*b];
for ii = 1:size(A, 2)
semilogy(alpha, A(ii, :), 'color', MixedShade(ii, :), 'LineStyle', '-', ...
'Marker','o', 'MarkerFaceColor', MixedShade(ii, :));
hold on
end
for ii = 1:size(B, 2)
semilogy(alpha, B(ii, :), 'color', MixedShade(ii, :), 'LineStyle', '-', ...
'Marker', '>', 'MarkerFaceColor', MixedShade(ii, :));
hold on
end
xlabel('$\alpha$', 'Interpreter', 'LaTex');
ylabel('$Cost$', 'Interpreter', 'LaTex');
grid on
xlim([0.1 0.4])
legend('A,Set1', 'A,Set2', 'A,Set3', 'A,Set4', ...
'B,Set1', 'B,Set2', 'B,Set3', 'B,Set4', ...
'location','best');
set(get(gcf,'CurrentAxes'), 'FontName', 'Times', 'FontSize', 14);
Related
See above image, if there are 24 pictures, how to use MATLAB to achieve this output effect. This kind of graph often appears in papers. I define a function, but there is one line of code that cannot be implemented.
function h = op(file_path, pad,m,n)
img_path_list = dir(strcat(file_path,'*.jpg'));
num = length(img_path_list);%
% [m,n]=size(image);
fw=n+(num-1)*pad;
fh=m+(num-1)*pad;
h=figure('position',[0,0,fw+pad,fh+pad]);
for j = 1:num
image_name = img_path_list(j).name;
image = imread(strcat(file_path,image_name));
hold on
pd=(j-1)*pad;
rpl=fw-n-pd;
rpb=fh-m-pd;
%How to specify the location of the output on the image canvas
% set('Position',[rpl rpb n m]);
% axes('position',[rpl rpb n m]);
imshow (image);
end
% h=gcf;
You can use the Xdata, and Ydata in imshow() function to set the axis position of each image to display them on the same axis stacked one upon other shifted to a fixed units for each image.
The code illustrating the procedure is given below.
close all
% read the images in metrices
i1 = imread('onion.png');
i2 = imread('cameraman.tif');
i3 = imread('peppers.png');
i4 = imread('moon.tif');
i5 = imread('trees.tif');
i6 = imread('greens.jpg');
% create a cell array of the images
imgs = {i1, i2, i3, i4, i5, i6};
% variable to shift the position of each image
shift = 0;
% looping from 1 to length of the cell arrays
for i = 1:numel(imgs)
% display image, shifting the position to 2 units
% for each image on the same axis
imshow(imgs{i}, 'XData', [1+shift 10+shift], ...
'YData', [1+shift 10+shift],'InitialMagnification', 400)
% hold on the axis
hold on
% increment the shift value
shift = shift + 2;
end
% set the axis limits
xlim([1 10+shift])
ylim([1 10+shift])
% hide the axis lines
axis off
Sample Output
Here is an attempt of creating a simple animation by generating frames as a sequence of figures, saved in .png format1:
clc
clear
close all
% don't display plot : produces error.
%set(0, 'defaultfigurevisible', 'off');
% number of frames.
N = 2;
% generate data sets.
x = linspace(0, 6 * pi, 1000);
y = sin(x);
% run animation.
for i = 1 : N
% create figure.
clf
% hold on % produces error.
plot(x, y, x(i), y(i), 'ro')
% plot(x(i), y(i), 'ro')
grid on
axis tight
title( sprintf( 'Sine Wave at (%f, %f)', x(i), y(i) ) )
xlabel('x')
ylabel('y')
drawnow
% create frame name: fr00001.png, etc.
framename = sprintf('output/fr%05d.png', i);
% save current figure in file: output.
print(framename);
end
However, the only thing I get is:
Mesa warning: couldn't open dxtn.dll, software DXTn compression/decompression unavailableGL2PS error: Incorrect viewport (x=0, y=240883432, width=0, height=240885832)error: gl2ps_renderer::draw: gl2psBeginPage returned GL2PS_ERRORerror: called fromopengl_print at line 172 column 7print at line 519 column 14sinewave at line 48 column 3
Any recommendation would be appreciated.
Note: the commented lines are intentionally left.
1. Later on to be stitched into a movie in .avi format with the help of an encoder.
Executed on Octave-4.2.1 on Windows 10
What helps1 is to use print() with a graphics handle to the figure and to define the file names (.png) including the absolute path of the directory in which they will be stored. Noticing that sub-directory is denoted with \ rather than /, moreover, it needs an additional \ escape character to be valid. Then the following code:
N = 1000;
x = linspace(0, 6*pi, N);
y = sin(x);
abspath = 'D:\\...\\Project\\output\\';
fh = figure();
for i = 1 : N
clf
hold on
plot(x, y);
plot(x(i), y(i), 'ro')
grid on
axis tight
title( sprintf( 'Sine Wave at (%f, %f)', x(i), y(i) ) )
xlabel('x')
ylabel('y')
drawnow
framename = sprintf('%sfr%04d.png', abspath, i);
print(fh, framename);
end
produces 1000 frames, which when2 stitched together give:
1. It still crashes unexpectedly after few hundred plots.
2. The first 100 .png files are converted to a .gif with the use of imagemagick's command: magick convert -delay 10 -loop 0 *png output.gif.
The matlab code below splits up an image into a number of smaller images. It then counts the number of black pixels in the image and displays it as a percentage of the total number of pixels in the picture. example of image
My question is - instead of counting the black pixels and displaying the percentage, how can I count the white pixels? (essentially the opposite!)
Thanks
% Divide an image up into blocks (non-overlapping tiles).
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
workspace; % Make sure the workspace panel is showing.
fontSize = 20;
% Read the image from disk.
rgbImage = imread('edge-diff.jpg');
% Display image full screen.
imshow(rgbImage);
% Enlarge figure to full screen.
set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
drawnow;
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows columns numberOfColorBands] = size(rgbImage)
%==========================================================================
% The first way to divide an image up into blocks is by using mat2cell().
blockSizeR = 400; % Rows in block.
blockSizeC = 400; % Columns in block.
% Figure out the size of each block in rows.
% Most will be blockSizeR but there may be a remainder amount of less than that.
wholeBlockRows = floor(rows / blockSizeR);
blockVectorR = [blockSizeR * ones(1, wholeBlockRows), rem(rows, blockSizeR)];
% Figure out the size of each block in columns.
wholeBlockCols = floor(columns / blockSizeC);
blockVectorC = [blockSizeC * ones(1, wholeBlockCols), rem(columns, blockSizeC)];
% Create the cell array, ca.
% Each cell (except for the remainder cells at the end of the image)
% in the array contains a blockSizeR by blockSizeC by 3 color array.
% This line is where the image is actually divided up into blocks.
if numberOfColorBands > 1
% It's a color image.
ca = mat2cell(rgbImage, blockVectorR, blockVectorC, numberOfColorBands);
else
ca = mat2cell(rgbImage, blockVectorR, blockVectorC);
end
percentBlack = cellfun(#(x)sum(sum(all(x == 0, 3))) / (numel(x) / size(x,3)), ca);
% Now display all the blocks.
plotIndex = 1;
numPlotsR = size(ca, 1);
numPlotsC = size(ca, 2);
for r = 1 : numPlotsR
for c = 1 : numPlotsC
fprintf('plotindex = %d, c=%d, r=%d\n', plotIndex, c, r);
% Specify the location for display of the image.
subplot(numPlotsR, numPlotsC, plotIndex);
ax2 = subplot(numPlotsR, numPlotsC, plotIndex);
% Extract the numerical array out of the cell
% just for tutorial purposes.
rgbBlock = ca{r,c};
imshow(rgbBlock); % Could call imshow(ca{r,c}) if you wanted to.
[rowsB columnsB numberOfColorBandsB] = size(rgbBlock);
set(ax2, 'box', 'on', 'Visible', 'on', 'xtick', [], 'ytick', []);
% Make the caption the block number.
averageBlack = percentBlack(r,c);
disp(numPlotsR);
disp(averageBlack);
caption = sprintf('Frame #%d of %d\n Percentage information content %0.2f', ...
plotIndex, numPlotsR*numPlotsC, averageBlack*100);
title(caption);
drawnow;
% Increment the subplot to the next location.
plotIndex = plotIndex + 1;
end
end
This line:
percentBlack = cellfun(#(x)sum(sum(all(x == 0, 3))) / (numel(x) / size(x,3)), ca);
specifically the part that says all(x == 0, 3) means "all color channels have value 0". You want to change it to "all color channels have value 1 (or 255 depends on your image)"
So basically, change that 0 to 1 or 255, deependinf if your image is unit8 or double
I have created a big heat map using matlab's imagesc command. It plots the error output for each combination of the values in x and y axes. As can be seen in the figure there are too many axes labels. This might become even denser as I plan to increase the number of points in both x and y axes - which means I will get more outputs on a finer grid.
I want to be flexible with the labels, and skip some of them. I want to do this for both X and Y. I also want to be flexible with the "ticks" and draw either all of them or maybe skip some of them. Keep in mind that both the X and Y values are not increasing in order, at first the increment is 0.01 for 9 points, then 0.1, then 1 or 3 or whatever. I will change these increments too.
I tried to show what I want the graph look like in the second image. I want roughly the labels shown in red boxes only. As I said these are not set values, and I will make the increments smaller which will lead to denser plot.
Thank you for your help.
OS: Windows 7, 8 (64 bit)
Matlab version: Matlab 2014 a
You can manipulate the ticks and labels like this:
ticksarray=[1 33 41 100 ...] % edit these to whatever you want
tickslabels={'1', '33', '41', '100'; ...} % match the size of both arrays
set(gca,'XTick',ticksarray)
set(gca,'XTickLabel',tickslabels)
The same thing applies to the y-axis.
Small working example:
x=1:100;
y=2*x.^2-3*x+2;
plot(x,y)
ticksarray=[1 33 41 100];
tickslabels={'1', '33', '41', '100'};
set(gca,'XTick',ticksarray)
set(gca,'XTickLabel',tickslabels)
Example:
figure(1)
load clown
subplot(211)
imagesc(X);
subplot(212)
imagesc(X);
h = gca;
Now you can either set a maximum number of labels per axis:
%// define maximum number of labels
maxLabel = 3;
h.XTick = linspace(h.xlim(1),h.xlim(2),maxLabel);
h.YTick = linspace(h.ylim(1),h.ylim(2),maxLabel);
or define how many labels should be skipped:
%// define number of labels to skip
skipLabel = 2;
h.XTick = h.XTick(1:skipLabel:end);
h.YTick = h.YTick(1:skipLabel:end)
You can also get a different number of ticks and labels, more complicated though:
maxLabel = 3;
maxTicks = 6;
h.XTick = linspace(h.xlim(1),h.xlim(2),maxTicks);
h.YTick = linspace(h.ylim(1),h.ylim(2),maxTicks);
h.XTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ) ) = repmat({''},1,maxTicks-maxLabel);
h.YTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ) ) = repmat({''},1,maxTicks-maxLabel);
If you use a prior version of Matlab 2014b, then you will need the set command to set all properties:
%// define maximum number of labels
maxLabel = 3;
Xlim = get(h,'Xlim');
Ylim = get(h,'Ylim');
set(h,'XTick', linspace(Xlim(1),Xlim(2),maxLabel));
set(h,'YTick', linspace(Ylim(1),Ylim(2),maxLabel));
%// or define number of labels to skip
skipLabel = 2;
XTick = get(h,'XTick');
YTick = get(h,'YTick');
set(h,'XTick', XTick(1:skipLabel:end));
set(h,'YTick', YTick(1:skipLabel:end));
%// or combined
maxLabel = 3;
maxTicks = 6;
Xlim = get(h,'Xlim');
Ylim = get(h,'Ylim');
set(h,'XTick', linspace(Xlim(1),Xlim(2),maxTicks));
set(h,'YTick', linspace(Ylim(1),Ylim(2),maxTicks));
XTickLabel = cellstr(get(h,'XTickLabel'));
YTickLabel = cellstr(get(h,'YTickLabel'));
XTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ),: ) = repmat({''},1,maxTicks-maxLabel);
YTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ),: ) = repmat({''},1,maxTicks-maxLabel);
set(h,'XTickLabel',XTickLabel);
set(h,'YTickLabel',YTickLabel);
After applying the second method proposed by #thewaywewalk I got the second figure below. Apparently the labels need to be structured as well, because they only take the first so many labels.
Then I tried to manipulate the labels as shown below, and got the third image.
skipLabel = 2;
XTick = get(h,'XTick');
YTick = get(h,'YTick');
set(h,'XTick', XTick(1:skipLabel:end));
set(h,'YTick', YTick(1:skipLabel:end));
XTickLabel = get(h,'XTickLabel');
labelsX = cell( length(1: skipLabel:length(XTick)) , 1);
j = 1;
for i = 1: skipLabel:length(XTick)
labelsX{j} = XTickLabel(i, :);
j = j + 1;
end
set(h,'XTickLabel', labelsX);
YTickLabel = get(h,'YTickLabel');
labelsY = cell( length(1: skipLabel:length(YTick)) , 1);
j = 1;
for i = 1: skipLabel:length(YTick)
labelsY{j} = YTickLabel(i, :);
j = j + 1;
end
set(h,'YTickLabel', labelsY);
The Y axis labels seem to be in place as before (right next to tick), however the X axis labels seem to be shifted to the left a little. How can I correct this?
Another note: How can I change the scientific values into normal numbers? Also, probably there is a better approach at manipulating the labels.
I currently have a big headache to get a small GUI working nicely which isn't being created with GUI editor but programmatically! What I have so far is something like the following:
hFig = figure();
set(hFig, 'Position', [300 200 500 400]);
plot((1:10).^2, '*-r');
% Größe des Plots so anpassen, dass links Platz für Buttons
ap = get(gca, 'TightInset');
fp = get(gcf, 'Position');
set(gca, 'Position', [160/fp(3), 30/fp(4), (fp(3)-180)/fp(3), (fp(4)-60)/fp(4)]);
uicontrol('Style', 'pushbutton', 'String', 'foo', 'Position', [15 fp(4)-60 110 30]);
uicontrol('Style', 'pushbutton', 'String', 'bar', 'Position', [15 fp(4)-100 110 30]);
Try to resize it: It doesn't 'look' the same, which means that the uicontrol boxes don't stay at the same relative position and the margins from the axis to the figure window get bigger. What I want to achieve is:
Have a figure window with a given position (x/y, width and height) with a plot inside. The plot will have a title and labels for x and y. Make the plot as height and width to have the TightInset plus a margin in each direction of a certain px-size (e.g. TightInset + 10px) as big as the figure window; except leave 150px of free space on the left to place some uicontrol buttons, and have them stay in the same position: This would be the same as being able to give the position from top/left (top = 20, left = 10) instead of bottom/left.
Thanks a lot!
Okay finally found a working solution I wanted it to be :-) Hopefully it is helpfull for somebody interested in it:
Main script file:
p = [300 300 1000 600];
fixedMargins = [250 0 0 0]; % [left, top, right, bottom]
f = figure('Position', p, 'Color', [0.9 0.9 0.9]);
plot(-10:10, (-10:10).^3, '*-r');
set(f, 'ResizeFcn', {#resizeCallback, gca, fixedMargins, {#myuiFunc, f, 40, 50}});
title('bla')
xlabel('foooooooooo')
ylabel('barrrrrrr')
Resize Callback Function:
% Need to pass the handle of the axis to modify (hAx) AND to pass the
% desired margins as second extra callback argument:
% [left, top, right, bottom]!
function resizeCallback(hFig, ~, hAx, fixedMargins, func)
% Disable automatic rezising
set(hAx, 'Units', 'pixels');
% Figure-Size
fp = get(hFig, 'Position');
% Calculate Position of the axis
margin = get(hAx, 'TightInset') * [-1 0 1 0; 0 -1 0 1; 0 0 1 0; 0 0 0 1];
% Position to fill the figure minus the TightInset-Margin
newPos = [0 0 fp(3:4)] - margin;
% Change position based on margins
newPos(1) = newPos(1) + fixedMargins(1);
newPos(3) = newPos(3) - fixedMargins(1) - fixedMargins(3);
newPos(2) = newPos(2) + fixedMargins(4);
newPos(4) = newPos(4) - fixedMargins(2) - fixedMargins(4);
% Set new position
set(hAx, 'Position', newPos);
% Call UI-Func
if(nargin == 5)
f = func{1};
args = func(2:end);
f(args{:});
end
end
You can pass whatever function you want to be called when resizing the figure window, e.g. to update something in the figure. In my example it's the myuiFunc(), which is the following:
function myuiFunc(hFig, left, top)
persistent handles;
if(~isempty(handles))
delete(handles);
handles = [];
end
fp = get(hFig, 'Position');
h1 = uicontrol('Style', 'pushbutton', 'String', 'Foo','Position', [left fp(4)-top 100 35]);
h2 = uicontrol('Style', 'pushbutton', 'String', 'Bar','Position', [left fp(4)-top-50 100 35]);
handles = [h1 h2];
end
I like it :) Hopefully you too!
Edit: No need to edit the resizeCallback Function! Should work if you just pass your desired margins to it and if you like, additionally a function handle with arguments which will be called for each resize!
You can also use "Normalized" units.
uicontrol('Style', 'pushbutton', 'String', 'foo', 'Units','normalized','Position', [0.90 0.05 0.08 0.08] );