Plot digitization in MATLAB using ginput - matlab

I'm trying to digitize this image using MATLAB:
I have the following script:
%// Get data from plot
clear all; close all;
%// Input
fname = 'Fig15a.PNG';
xvec = [1e3:1:1e8];
yvec = [1e-4:1:1e-1];
xt = [1e3 1e4 1e5 1e6 1e7 1e8];
yt = [1e-4 1e-3 1e-2 1e-1];
%// Read and plot the image
im = imread(fname);
figure(1), clf
im = im(end:-1:1,:,:);
image(xvec,yvec,im)
axis xy;
grid on;
%// Set ticks
set(gca,'xtick',xt,'ytick',yt); %// Match tick marks
%// Collect data
[x,y] = ginput; %// Click on points, and then hit ENTER to finish
%// Plot collected data
hold on; plot(x,y,'r-o'); hold off;
%// Then save data as:
save Fig15a.mat x y
The script works fine
Is there a way I can change the x and y axes to a log scale ?
I have tried adding the following code in different places without luck:
%// Set Log scale on x and y axes
set(gca,'XScale','log','YScale','log');

Below's a proof of concept that should get you on the right track. I have replaced things in your original code with what I consider "good practices".
function q36470836
%% // Definitions:
FIG_NUM = 36470836;
%% // Inputs:
fname = 'http://i.stack.imgur.com/2as4t.png';
xt = logspace(3,8,6);
yt = logspace(-4,-1,4);
%% // Init
figure(FIG_NUM); clf
% Read and plot the image
im = imread(fname);
hIMG = imshow(im); axis image;
%// Set ticks
hDigitizer = axes('Color','none',...
'XLim',[xt(1) xt(end)],'YLim',[yt(1) yt(end)],...
'XScale','log','YScale','log',...
'Position',hIMG.Parent.Position .* [1 1 696/785 (609-64+1)/609]);
uistack(hDigitizer,'top'); %// May be required in some cases
grid on; hold on; grid minor;
%// Collect data:
[x,y] = ginput; %// Click on points, and then hit ENTER to finish
%// Plot collected data:
scatter(x,y,'o','MarkerEdgeColor','r');
%// Save data:
save Fig15a.mat x y
Here's an example of what it looks like:
Few notes:
xt, yt may be created in a cleaner fashion using logspace.
It is difficult (possibly impossible) to align the digitization grid with the image correctly, which would inevitably result in errors in your data. Though this can be helped in the following scenarios (for which you will require a vector graphics editor, such as the freeware InkScape):
If, by any chance, you got this image from a PDF file, where it appears as a vector image (you can test this by zooming in as much as you like without the chart becoming pixelated; this seems to be your case from the way the .png looks), you would be better off saving it as a vector image and then you have two options:
Exporting the image to a bitmap with a greatly increased resolution and then attempting the digitization procedure again.
Saving the vector image as .svg then opening the file using your favorite text editor and getting the exact coordinates of the points.
If the source image is a bitmap (as opposed to vector graphic), you can "trace the bitmap", thus converting it to vectoric, then #GOTO step 1.
This solution doesn't (currently) support resizing of the figure.
The magic numbers appearing in the Position setting are scaling factors explained in the image below (and also size(im) is [609 785 3]). These can technically be found using "primitive image processing" but in this case I just hard-coded them explicitly.

You can plot in double logarithmic scale with
loglog(x,y);
help loglog or the documentation give additional information.
For a single logarithmic scale use
semilogx(x,y);
semilogy(x,y);

Related

Logarithmic x axis in a stackplot MatLab

I'm trying to make a stack plot from a table, to present several variables with the same x-axis. however, I want the x-axis to be logarithmic. I couldn't find any way in stackplot documentation. Does anyone have any suggestions on how I can solve this?
I have tried using subplots instead, however, that way my graphs would not fit all on one page and I would have a lot of white space between the subplots. Therefore, I chose stackplot to make it more nice and less space-consuming.
tbl = readtable('usage.csv');
newYlabels = {'Heating (kWh/year)','Cooling (kWh/year)','Electricity (kWh/year)'};
stackedplot(tbl,[{2:16},{17:27},{28:35}],'XVariable',[1],'DisplayLabels',newYlabels);
Here is the output of the code:
Here is an image of what I'm trying to make, but the x-axis needs to be the real variable (\beta) in logarithmic scale
stackedplot has unfortunately no logarithmic axes option, and since it creates a StackedAxes instead of a normal Axes object, there is no way to changes this.
If the only reason you want to use stackedplot is to have less white-space, you might want to check out tight_subplot on the Matlab FEX. This would allow you to just do: set(ax, 'XScale', 'log').
You can however take the log of your x-data, and add that to the table:
tbl = readtable('outages.csv'); % sample data
tbl = sortrows(tbl, 'OutageTime'); % sort by date
% make x vector; for example just row numbers
x = (1:size(tbl,1)).';
xlog = log10(x);
% add x to table
tbl.Xlog = xlog;
tbl.X = x;
% plot normal x
f = figure(1); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'X');
xlabel('rows');
% plot log(x)
f = figure(2); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'Xlog');
xlabel('log(rows)')
Normal:
Log:

Matlab how to make smooth contour plot?

I want to represent data with 2 variables in 2D format. The value is represented by color and the 2 variables as the 2 axis. I am using the contourf function to plot my data:
clc; clear;
load('dataM.mat')
cMap=jet(256); %set the colomap using the "jet" scale
F2=figure(1);
[c,h]=contourf(xrow,ycol,BDmatrix,50);
set(h, 'edgecolor','none');
xlim([0.0352 0.3872]);
ylim([0.0352 0.3872]);
colormap(cMap);
cb=colorbar;
caxis([0.7 0.96]);
% box on;
hold on;
Both xrow and ycol are 6x6 matrices representing the coordinates. BDmatrix is the 6x6 matrix representing the corresponding data. However, what I get is this:
The following is the xrow and yrow matices:
The following is the BDmatrix matices:
Would it be possible for the contour color to vary smoothly rather than appearing as straight lines joining the data points? The problem of this figure is the coarse-granularity which is not appealing. I have tried to replace contourf with imagec but it seems not working. I am using MATLAB R2015b.
You can interpolate your data.
newpoints = 100;
[xq,yq] = meshgrid(...
linspace(min(min(xrow,[],2)),max(max(xrow,[],2)),newpoints ),...
linspace(min(min(ycol,[],1)),max(max(ycol,[],1)),newpoints )...
);
BDmatrixq = interp2(xrow,ycol,BDmatrix,xq,yq,'cubic');
[c,h]=contourf(xq,yq,BDmatrixq);
Choose the "smoothness" of the new plot via the parameter newpoints.
To reduce the Color edges, you can increase the number of value-steps. By default this is 10. The following code increases the number of value-steps to 50:
[c,h]=contourf(xq,yq,BDmatrixq,50);
A 3D-surf plot would be more suitable for very smooth color-shading. Just rotate it to a top-down view. The surf plot is also much faster than the contour plot with a lot of value-steps.
f = figure;
ax = axes('Parent',f);
h = surf(xq,yq,BDmatrixq,'Parent',ax);
set(h, 'edgecolor','none');
view(ax,[0,90]);
colormap(Jet);
colorbar;
Note 1: Cubic interpolation is not shape-preserving. That means, the interpolated shape can have maxima which are greater than the maximum values of the original BDmatrix (and minima which are less). If BDmatrix has noisy values, the interpolation might be bad.
Note 2: If you generated xrow and yrow by yourself (and know the limits), than you do not need that min-max-extraction what I did.
Note 3: After adding screenshots of your data matrices to your original posting, one can see, that xrow and ycol come from an ndgrid generator. So we also must use this here in order to be consistent. Since interp2 needs meshgrid we have to switch to griddedInterpolant:
[xq,yq] = ndgrid(...
linspace(min(min(xrow,[],1)),max(max(xrow,[],1)),newpoints ),...
linspace(min(min(ycol,[],2)),max(max(ycol,[],2)),newpoints )...
);
F = griddedInterpolant(xrow,ycol,BDmatrix,'cubic');
BDmatrixq = F(xq,yq);

How to animate points on an image in MATLAB?

I have the pixel locations of P points on a -constant- image, for T iterations of an algorithm, so locations = [T x 2*P] double.
Now I want to create an animation where it plots the image, then plots the points, pauses for N seconds and updates their location to the next step. I don't know if there is a standard way to follow. I think I need something like:
figure;
imshow(img);
hold on;
for t=1:T
anim = updatePlot(locations(t,:), anim); % ?
end
How can I implement this updatePlot function?
Thanks for any help!
You can do this a couple of different ways. The first way would be to give the plotted points a handle so that you can delete them before the next iteration:
figure
imshow(img);
hold on;
for t = 1:T
% delete the previous points plotted (skip t = 1 since they won't exist)
if t > 1
delete(hPoints);
end
hPoints = plot(xLocations(t,:),yLocations(t,:),'.');
getframe;
pause(N);
end
(I am not exactly sure how you parse your locations along each row to separate the x and y components, so I've just used xLocations and yLocations to represent those values.)
The second way would be to re-draw the entire image at each iteration:
figure
for t = 1:T
clf;
imshow(img);
hold on;
plot(xLocations(t,:),yLocations(t,:),'.');
getframe;
pause(N);
end
Note that imshow might have its own getframe effect so that you'll see the image flicker before plotting the points -- if that happens just switch from imshow to image.

Create a bitmap from patch object in Matlab

I have a set of polygon vertices in in X, Y matrices and their colors are in RGB values in another matrix C.
I then use fill() function to generate patch objects that is displayed in a Matlab figure.
I want to create a bmp object from this figure. What I mean by a bmp object is the x,y coordinates of the pixels and there RGB values.
If I use the print() function, with '-dbmp' and a file name, matlab can write the bmp to a file. But then I have to read the file with imread() to create the bmp object.
Is there a way to create the bmp object without writing and reading to from a file?
Becauswe I have to perform this operation many times and writing and reading to file is time consuming and will reduce the life time of my disk too I guess.
Edit: code after editing according to answer
N = 5;
Tri = 100;
res = 200; %200 pixles per inch
G = zeros(Tri,9,N);
X = 2*rand(Tri,3,N);
Y = 2*rand(Tri,3,N);
R = randi([0 255],Tri,N)/255;
G = randi([0 255],Tri,N)/255;
B = randi([0 255],Tri,N)/255;
for c1=1:N
G(:,1:3,c1)= X(:,:,c1);
G(:,4:6,c1)= Y(:,:,c1);
G(:,7,c1)= R(:,c1);
G(:,8,c1)= G(:,c1);
G(:,9,c1)= B(:,c1);
end
for c2=1:N;
h = figure('Visible','off');
set(h, 'PaperUnits', 'inches', 'PaperPosition', [0 0 400 400]/res);
for c3 =1:Tri
h1 = fill(G(c3,1:3,c2), G(c3,4:6,c2), [G(c3,7,c2) G(c3,8,c2) G(c3,9,c2)]);
set(h1,'EdgeColor','None');
hold on;
end
%print(h,'-dbmp',['-r' num2str(res)],['file' num2str(c2)]);
F = getframe(h);
[a, b] = frame2im(F);
Tmp_v1 = a;
Tmp_v1 = Tmp_v1(:);
Norm_v1(c2) = norm(single(Tmp_v1));
end
Thank you.
If you have the current figure open, you can try the getframe idiom. Once you have access to this, you can access the frame's image data by looking at the cdata field in the structure. After you have this, you can use the imwrite command from MATLAB's image processing toolbox (let's hope you have it...) to write the image to file.
Here's an example:
x = 1 : 5;
y = 1 : 5;
plot(x,y); %// Plot a line
h = getframe;
img = h.cdata;
imwrite(img, 'testFrame.bmp');
This should be able to grab what is inside the current figure and save it to file. In this case, this will be a straight line from (x,y) = (1,1) to (x,y) = (5,5), with a slope of 1. Bear in mind that this won't save the title of the graph or the axes. This will only grab what is rendered inside the frame.
Edit from comments
Now that I know what you're really after, you want to generate a bunch of random polygons, then extract just the core image, without any axes or tick marks and so on. You'll have to modify your for loop where you're creating the images so that you want the figure to fill the entire window without any gray padding You'll also want to turn off the axis too. Also, when you're generating each image, you'll need to turn off the ticks. This is done with a combination of not writing any tick labels, as well as setting the tick length to 0. In other words, you'll need to modify your code so that it looks like this. You'll see where I inserted code by seeing the %// NEW statements in your code:
for c2=1:N;
h = figure('Visible','off');
subplot('position', [0 0 1 1]); %// NEW
axis off; %// NEW
set(h, 'PaperUnits', 'inches', 'PaperPosition', [0 0 400 400]/res);
for c3 =1:Tri
h1 = fill(G(c3,1:3,c2), G(c3,4:6,c2), [G(c3,7,c2) G(c3,8,c2) G(c3,9,c2)]);
set(h1,'EdgeColor','None');
set(gca,'xcolor','w','ycolor','w','xtick',[],'ytick',[]) %// NEW
set(gca,'Ticklength',[0 0]); %// NEW
hold on;
end
%print(h,'-dbmp',['-r' num2str(res)],['file' num2str(c2)]);
F = getframe(h);
close all; %// NEW
[a, b] = frame2im(F);
a = imresize(a, [400 400], 'nearest'); %// NEW
Tmp_v1 = a;
Tmp_v1 = Tmp_v1(:);
Norm_v1(c2) = norm(single(Tmp_v1));
end
This will still show you the frames being popped up for each image you're creating, but you should be able to get just pure image data at this point. Note that the images are still coming out as a bit larger than 400 x 400. This is due to the fact that once we have removed the borders and the tick marks and so on, the figure will stretch to fill the entire figure. To get around this, I use imresize and shrink the images back down to 400 x 400 as per your desired size.
Also, take note that every time you generate a new image, a new figure is spawned. Every time you call getframe, the figure with this randomly generated polygon image pops up, and getframe takes a snapshot of that current frame. There isn't a way to prevent this from happening, as you won't be able to take a snapshot of that figure. One way to get around this would be to close the figure after you grab the image. You can do a close all; after each call to getframe. That way, only one figure gets shown at any one time, but this still won't prevent the figure from showing up.
If I do find a solution to this, I'll let you know!

Matlab plot of several digital signals

I'm trying to find a way to nicely plot my measurement data of digital signals.
So I have my data available as csv and mat file, exported from an Agilent Oscilloscope. The reason I'm not just taking a screen shot of the Oscilloscope screen is that I need to be more flexible (make several plots with one set of data, only showing some of the lines). Also I need to be able to change the plot in a month or two so my only option is creating a plot from the data with a computer.
What I'm trying to achieve is something similar to this picture:
The only thing missing on that pic is a yaxis with 0 and 1 lines.
My first try was to make a similar plot with Matlab. Here's what I got:
What's definitely missing is that the signal names are right next to the actual line and also 0 and 1 ticks on the y-axis.
I'm not even sure if Matlab is the right tool for this and I hope you guys can give me some hints/a solution on how to make my plots :-)
Here's my Matlab code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*1.5, 'color', cm(i-1,:));
end
hold off;
legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
set(gca, 'ytick', []); % hide y axis
Thank you guys for your help!
Dan
EDIT:
Here's a pic what I basically want. I added the signal names via text now the only thing that's missing are the 0, 1 ticks. They are correct for the init done signal. Now I just need them repeated instead of the other numbers on the y axis (sorry, kinda hard to explain :-)
So as written in my comment to the question. For appending Names to each signal I would recommend searching the documentation of how to append text to graph. There you get many different ways how to do it. You can change the position (above, below) and the exact point of data. As an example you could use:
text(x_data, y_data, Var_Name,'VerticalAlignment','top');
Here (x_data, y_data) is the data point where you want to append the text and Var_Name is the name you want to append.
For the second question of how to get a y-data which contains 0 and 1 values for each signal. I would do it by creating your signal the way, that your first signal has values of 0 and 1. The next signal is drawn about 2 higher. Thus it changes from 2 to 3 and so on. That way when you turn on y-axis (grid on) you get values at each integer (obviously you can change that to other values if you prefer less distance between 2 signals). Then you can relabel the y-axis using the documentation of axes (check the last part, because the documentation is quite long) and the set() function:
set(gca, 'YTick',0:1:last_entry, 'YTickLabel',new_y_label(0:1:last_entry))
Here last_entry is 2*No_Signals-1 and new_y_label is an array which is constructed of 0,1,0,1,0,....
For viewing y axis, you can turn the grid('on') option. However, you cannot chage the way the legends appear unless you resize it in the matlab figure. If you really want you can insert separate textboxes below each of the signal plots by using the insert ->Textbox option and then change the property (linestyle) of the textbox to none to get the exact same plot as above.
This is the end result and all my code, in case anybody else wants to use the good old ctrl-v ;-)
Code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*2, 'color', cm(i-1,:));
text(MD.D(2,1), (i-2)*2+.5, MD.N(i-1));
end
hold off;
%legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
% make y axis and grid the way I want it
set(gca, 'ytick', 0:size(MD.D,2)*2-3);
grid off;
set(gca,'ygrid','on');
set(gca, 'YTickLabel', {'0'; '1'});
ylim([-1,(size(MD.D,2)-1)*2]);