【Matlab】向图像中添加帮助读者理解的额外标注信息

有些时候,为了更加清晰地表示数据,我们需要对绘好的图像添加一些其他标注信息,以帮助读者更好地理解自己的意思,本文将向大家介绍几种常用的添加额外信息的方法。注:下述实现均参考自MathWorks,在此表示感谢,如有侵权请留言联系。

目录

1. 添加线段

2. 添加Latex公式

3. 数据放大镜

4.添加图标志


1. 添加线段

常见的两个需求:

  • 观察某个特定点的数据的值;
  • 观察数据的趋势,判断数据收敛的上界或下界。

我们可以通过添加一些必要的线段来帮助读者理解上述信息。

【Matlab】向图像中添加帮助读者理解的额外标注信息

 具体的实现代码如下所示:

% 数据
step_data =[
         0    0.0113
   10.0000    0.0162
   20.0000    0.1947
   30.0000    0.5591
   40.0000    0.7050
   50.0000    0.7744
   60.0000    0.9218
   70.0000    0.9208
   80.0000    0.9852
   90.0000    0.9575
  100.0000    1.0546
  110.0000    0.9873
  120.0000    1.0258
  130.0000    0.9930
  140.0000    1.0203
  150.0000    1.0605
  160.0000    0.9637
  170.0000    1.0051
  180.0000    0.9878
  190.0000    0.9876
  200.0000    1.0349];
figure
plot(step_data(:,1), step_data(:,2), 'r*-')
% 设置坐标轴显示范围
axis([0 200 0 1.2])
% 添加标题和标签
title('Closed loop response')
xlabel('Time')
ylabel('Tank temperature (normalized)')
% 画一条横线
line('XData', [0 200], 'YData', [1 1], 'LineStyle', '-', ...
    'LineWidth', 2, 'Color','m')

% 在x=25处添加一条横线和一条竖线
x = 25;
y = (step_data(4,2) - step_data(3,2));
line('XData', [x x 0], 'YData', [0 y y], 'LineWidth', 2, ...
    'LineStyle', '-.', 'Color', [0 0.6 0])

% 在x=50处添加一条横线和一条竖线
x = 50;
y = step_data(6,2);
line('XData', [x x 0], 'YData', [0 y y], 'LineWidth', 2, ...
    'LineStyle', '-.', 'Color', [0.2 0.4 1.0])

2. 添加Latex公式

另一个常见的需求:

我们绘制了一条曲线,为了读者一眼可以认出这条曲线表达的含义,我们需要把公式放在图中;

【Matlab】向图像中添加帮助读者理解的额外标注信息

具体实现代码如下所示: 

%%
% *This is an example of how to add LaTeX equations to plots in MATLAB®* .
% 
% You can open this example in the <https://www.mathworks.com/products/matlab/live-editor.html 
% Live Editor> with MATLAB version 2016a or higher.
%
% Read about the <http://www.mathworks.com/help/matlab/ref/text.html |text|> function in the MATLAB documentation.
% For more examples, go to <http://www.mathworks.com/discovery/gallery.html MATLAB Plot Gallery>
%
% Copyright 2012-2018 The MathWorks, Inc.

% Calculate the Fibonacci numbers from 1 to 12
fib = zeros(1, 12);
for i = 1:12
    fib(i) = (((1+sqrt(5))/2)^i - ((1-sqrt(5))/2)^i)/sqrt(5);
end

% Plot the first 12 Fibonacci numbers
figure
plot(1:12, fib, 'k^-')

% Add a title and axis labels
title('Fibonacci Numbers from 1-12')
xlabel('n')
ylabel('F_n')

% Build a string that contains the Latex expression
eqtext = '$$F_n={1 \over \sqrt{5}}';
eqtext = [eqtext '\left[\left({1+\sqrt{5}\over 2}\right)^n -'];
eqtext = [eqtext '\left({1-\sqrt{5}\over 2}\right)^n\right]$$'];

% Add the string containing the Latex expression to the plot
text(0.5, 125, eqtext, 'Interpreter', 'Latex', 'FontSize', 12, 'Color', 'k')

3. 数据放大镜

有时候,我们需要动态地观察整个周期的某个细节的数据,这个需求有点类似淘宝里面的放大镜功能。

【Matlab】向图像中添加帮助读者理解的额外标注信息

 上图中,左图为放大镜中看到的细节,右图为原始图像,通过上下左右键可以控制右图窗口的移动,实现放大,具体实现如下:

classdef Orienter < handle
%Zoom and pan plotted data (referencePlot) using keypresses (arrow keys, shift, and command).
%Keypresses adjust the limits of the axes (Ax2Adjust) where the data are plotted. 
%A separate axes, TrackerAx, shows the user which portion of the referencePlot is visible in Ax2Adjust. 
%TrackerAx contains copy of referencePlot (miniPlot) such that all its X and Y data are visible. 
%A semi-opaque yellow square (TrackerPatch) highlights the portion of data currently visible in Ax2Adjust.
%See MATLAB documentation on Patch to can change the color etc. of TrackerPatch

%Orienter automatically responds to events.
%If referencePlot data or Ax2Adjust limits changes, listeners automatically 
%adjust miniPlot and TrackerAx accordingly. These allows you link axes with Ax2Adjust and to 
%edit referencePlot data without having to alter Orienter.

%User can adjust the speed/precision (aka moveSpeed) of zooming and panning.
%More specifically, user can adjust how much the axes limits changes with
%each key press by clicking the patch object.

%Orienter should function in standalone applications/GUIs.

%TrackerPatch appearance can be altered by accessing Patch properties 
%(e.g. ?FaceColor? for color, ?FaceAlpha? for transparency).

%% limitations
% Only changes in referencePlot XData and YData properties will cause miniPlot to update.
% If properties of referencePlot change BESIDES X-Data and Y-Data (e.g. color)
% these changes may not transfer automically transfer to miniPlot. 
% you can always ensure miniplot matches referencePlot by using
% update_miniPlot method. When referencePlot X/Y Data do change, ALL
% properties of referencePlot are transferred to miniPlot.

%Ellen Zakreski (2016), efzakreski@mac.com
    %% KEY PRESS 
    % || Key       || Description
    % ||(+modifier)||
    %----------------------------------------------------------------
 %PANING-------------------------------------------------------------
    % || >         || pan right  (moves both left and right sides of patch)
    % || <         || pan left
    % || ^         || pan up     (moves both top and bottom sides of patch)
    % || v         || pan down
 %ZOOMING (horizontally)---------------------------------------------
    % || > shift   || zoom in horizontally (moves only left side of patch right)
    % || < shift   || zoom out horizontally (moves only left side left)
    % || > command || zoom out horizontally  (moves right side right)
    % || < command || zoom in horizontally (moves right side left) 
 %ZOOMING (vertically)-----------------------------------------------
    % || v shift   || zoom in vertically (moves only top side of patch down)
    % || ^ shift   || zoom out vertically (moves top side of patch up)
    % || v command || zoom out vertically  (move bottom side of patch down)
    % || ^ command || zoom in vertically (moves bottom side up) 

    properties
        referencePlot %plot handle or any graphics object with XData and YData
        Ax2Adjust %will become the axes whose limits will be adjusted (by default, is the first axes that is ancestor to the reference plot).
        
        keypressCatcher %figure that when clicked will detect keypresses that will move the patch (by default, is the parent of the TrackerAx).
        
        listeners = struct('plotXData',[],'plotYData',[],'axXLim',[],'axYLim',[]); %see addAllListeners method below
        
        XmoveSpeed = 2; %percentage by which X-axis limits change with each keypress.
        YmoveSpeed = 10; %percentage by which Y-axis limits change with each keypress.
        
        %% TRACKER
        miniPlot %copy of referencePlot (plotted in TrackerAx);
        %properties below are initially structs but become objects 
        %TrackerFig = struct('MenuBar','none','ToolBar','none','Position',[100 100 600 200]); 
        TrackerFig = struct('Position',[100 100 600 200]); 
        TrackerAx = struct('NextPlot','add',...%TrackerAx (by default plotted on TrackerFig) shows the entire reference plot
            'XLimMode','manual','XLim',[1 2],...
            'YLimMode','manual','YLim',[1 2]);
        TrackerPatch 
    end %end properties
    
    properties (Dependent)
        totalXLim % X limits of reference plot 
        totalYLim % Y limits of reference plot
    end
    
    methods
        function obj = Orienter(referencePlot)
            %referencePlot can be plot or any graphics object with XData and YData
            if nargin ~=0
                obj.referencePlot = referencePlot;
                %get axes to adjust
                p = referencePlot.Parent;
                while ~strcmpi(p.Type,'axes')
                    p = p.Parent;
                end
                obj.Ax2Adjust = p; clear p
                % set to default Ax2Adjust axes limits 
                defaultview(obj);
                
                % Add key press function to tracker fig(so that key presses trigger patch movement when trackerfig is in focus.
                obj.TrackerFig = figure(obj.TrackerFig);
               
                % TrackerAx shows the entire reference plot
                obj.TrackerAx = axes(obj.TrackerAx,'parent',obj.TrackerFig);
                
                obj.TrackerPatch = patch('parent',obj.TrackerAx,...
                    'XData',[1 1 2 2 1],...%TrackerPatch is square showing the portion of the total data in  view.
                    'YData',[1 2 2 1 1],...
                    'FaceColor',[1 1 0],...%yellow
                    'FaceAlpha',0.5,... %opacity = 50%.
                    'EdgeColor','none',...
                    'buttondownfcn',@(src,ev)adjust_moveSpeed(obj));
                
                
                % make miniPlot if reference plot changes
                obj.miniPlot = copyobj(obj.referencePlot,obj.TrackerAx);
                
                addAllListeners(obj)
                
                
                 %make miniPlot reflect referencePlot
                 update_plotData(obj,'X');  update_plotData(obj,'Y');
                 %make patch reflect portion of data currently in view
                 update_axLim(obj,'X');  update_axLim(obj,'Y');
                
            end
        end
        %default view
        function defaultview(obj)
            %by default show 20% of the total x data,
            obj.Ax2Adjust.XLim = [obj.totalXLim(1), obj.totalXLim(1) + diff(obj.totalXLim)*0.20];
            % and 80% of the total y data
            obj.Ax2Adjust.YLim = [obj.totalYLim(1), obj.totalYLim(1) + diff(obj.totalYLim)*0.80];
        end
        
        
        %% listeners
        function addAllListeners(obj)
            structfun(@delete,obj.listeners);
            % listen to changes in reference plot X/YData
            
            obj.listeners.plotXData = addlistener(obj.referencePlot,'XData','PostSet',@(src,ev)update_plotData(obj,'X'));
            obj.listeners.plotYData  = addlistener(obj.referencePlot,'YData','PostSet',@(src,ev)update_plotData(obj,'Y'));
           
            %listen to changes in Ax2Adjust
            obj.listeners.axXLim = addlistener(obj.Ax2Adjust,'XLim','PostSet',@(src,ev)update_axLim(obj,'X'));
            obj.listeners.axYLim = addlistener(obj.Ax2Adjust,'YLim','PostSet',@(src,ev)update_axLim(obj,'Y'));
        end
        
        % listern callbacks
        function update_plotData(obj,xory)
            %if referencePlot X/YData changes
            xory=upper(xory);
            %% update minplot only X/YData
            %set(obj.miniPlot,[xory,'Data'],obj.referencePlot.([xory,'Data']));
            %% just copy miniplot again.
            update_miniPlot(obj);
            
            %change the axes limits of TrackerAx
            newlim =obj.(['total',xory,'Lim']);
            if isempty(newlim) || any(isnan(newlim))
                warning('Could not reset tracker axes limits because plot %s data are all empty or NaN''s.', xory)
            else
                set(obj.TrackerAx,[xory,'Lim'],newlim);
            end
        end
        
        function update_axLim(obj,xory) 
            %if Ax2Adjust axes limits change update patch X/YData
            %accordingly so it overlaps with the portion of referencePlot that is currently
            %visible in Ax2Adjust.
            xory=upper(xory);
            % get indexes for converting axes limits to patch X/YData/
            switch xory
                case 'Y'
                    ind = [1 2 2 1 1]; %B T T B B (B=bottom, T=top)
                case 'X'
                    ind = [1 1 2 2 1]; %L L R R L  (L=left, R=right)
            end
            L = obj.Ax2Adjust.([xory,'Lim']);
            %update patch position.
            obj.TrackerPatch.([xory,'Data']) = L(ind);
        end
        
        %% Get
        function l=get.totalXLim(obj)
            l=feval(@(d)[nanmin(d),nanmax(d)],obj.referencePlot.XData);
        end
        function l=get.totalYLim(obj)
            l=feval(@(d)[nanmin(d),nanmax(d)],obj.referencePlot.YData);
        end
        
        
        
        %% Set
        function set.referencePlot(obj,newval)
            %validate
            if ~ishandle(newval) || ~isvalid(newval)
                error('referencePlot must be valid handle')
            end
            assert(all(ismember({'XData','YData'},fieldnames(newval))),'referencePlot must have properties called XData and YData');
            
            obj.referencePlot=newval;
        end
        function set.Ax2Adjust(obj,newval)
            if isstruct(newval)
                 newval = axes(newval);
             end
            if ~isa(newval,'matlab.graphics.axis.Axes') || ~isvalid(newval)
                error('Ax2Adjust must be valid axes handle');
            end
            set(newval,'NextPlot','add','XLimMode','manual','YLimMode','manual');
            obj.Ax2Adjust = newval;
            
        end
        function set.TrackerFig(obj,newval)
            if isstruct(newval)
                newval = figure(newval);
            end
            if ~isa(newval,'matlab.ui.Figure') || ~isvalid(newval)
                error('TrackerFig must be valid figure handle');
            end
            % Add key press function to tracker fig(so that key presses trigger patch movement when trackerfig is in focus.
            newval.KeyPressFcn=@(src,ev)keypress(obj,ev);
            obj.TrackerFig = newval;
        end
        function set.TrackerAx(obj,newval)
             if isstruct(newval)
                 newval = axes(newval);
             end
             if ~isa(newval,'matlab.graphics.axis.Axes') || ~isvalid(newval)
                error('TrackerAx must be valid axes handle');
            end
            set(newval,'NextPlot','add',...
                'XLimMode','manual','XLim',[1 2],'YLimMode','manual','YLim',[1 2]);
            obj.TrackerAx = newval;
        end
        
        function update_miniPlot(obj)
            delete(obj.miniPlot);
            obj.miniPlot = copyobj(obj.referencePlot,obj.TrackerAx);
            uistack(obj.TrackerPatch,'top'); %put TrackerPatch on top of all other plots in TrackerAx.
        end
        
        
        %% moveSpeed
        function adjust_moveSpeed(obj)
            xyls = {'X','Y'};
            prompt = cellfun(@(xy)sprintf('%% of %s-axis that changes per key press:',xy),xyls,'uniformoutput',0);
            defval = cellfun(@(xy)num2str(obj.([xy,'moveSpeed'])),xyls,'uniformoutput',0);
            answer = inputdlg(prompt,...
                'Adjust pan/zoom speed/precision.',...%title
                1,...%numlines
                defval);
            if isempty(answer); return; end
            for n=1:numel(xyls)
                val = str2double(answer{n});
                xy = xyls{n};
                if isnan(val)
                    uiwait(errordlg('%input for must be numeric. %s-axis was not changed.',xy));
                    continue
                end
                obj.([xy,'moveSpeed']) = val;
            end
        end
        
        function keypress(obj,ev)
            TL = ismember('shift',ev.Modifier); %shift moves top/left side only
            BR = ismember('command',ev.Modifier); %command moves bottom/right side only
            %This is because on the left side of the keyboard,
            %shift is above and to the left of the command key.
            
            ispan = ~xor(TL,BR); %ispan if either TL or BR, or not TL nor BR.
            %panning moves both sides move together, if zoom, only one side moves.
            if ispan; TL = true; BR = true; end;
            coef = [0,0]; % determines how much and in what direction Ax2Adjust axes limits change by.
            switch ev.Key
                case 'uparrow'
                    xory = 'Y'; 
                    coef([BR,TL]) = 1; %since for Y-axis limits [ymin,ymax], ymin is Bottom, ymax is Top [B,T]
                case 'downarrow'
                    xory = 'Y';
                    coef([BR,TL]) = -1; %since for Y-axis limits [ymin,ymax], ymin is Bottom, ymax is Top [B,T]
                case 'leftarrow'
                    xory  ='X';
                    coef([TL,BR]) = -1; %since for X-axis limits [xmin,xmax], xmin is Left, xmax is Right [L,R]
                case 'rightarrow'
                    xory  ='X'; 
                    coef([TL,BR]) = 1; %since for X-axis limits [xmin,xmax], xmin is Left, xmax is Right [L,R]
                otherwise
                    return
            end
            movepatch(obj,xory,coef);
        end
        
        function movepatch(obj,xory,coef)
            xory = upper(xory);
            mspeed = obj.([xory,'moveSpeed']);
            span = diff(obj.(sprintf('total%sLim',xory)));
            moveby = span*coef*mspeed/100;
            %coeff elements indicate which sides of axis limits (min, max or both) and whether to increase or decrease
            %is -1 (decrease along axis) or 1 (increase along axis)
            %set new axes position
            obj.Ax2Adjust.([xory,'Lim']) = obj.Ax2Adjust.([xory,'Lim']) + moveby; %will trigger listener to update patch position
        end

    end
end
function OrienterObj = example
    %example of Orienter. 
    XX= unique(randi(300,[100,1]),'sorted');
    YY = rand(size(XX));
    f=figure; a=axes('parent',f); refPlot = plot(XX,YY,'-r*','parent',a);
    title(a,'Ax2Adjust: Pan using arrowkeys. Use shift/command to zoom in and out.');
    xlabel(a,'Visible X-Data');
    ylabel(a,'Visible Y-Data');
    legend({'referencePlot'});
    
    OrienterObj = Orienter(refPlot);
    
    title(OrienterObj.TrackerAx,'TrackerAx: Click patch(yellow square) to adjust pan/zoom speed');
    xlabel(OrienterObj.TrackerAx,'Total X-Data');
    ylabel(OrienterObj.TrackerAx,'Total Y-Data');
end

4.添加图标志

有些时候,我们在生成图像时,由于图像的数据个数不一致,或者数据过多时,我们不能将每个图像中的数据点都显示为特定的标志,此时,就需要一种工具,能够生成图标在我们已经绘出的图像中。

【Matlab】向图像中添加帮助读者理解的额外标注信息

实现代码如下: 

function ho=addmarkers(hi,n)
% ADDMARKERS    Add a pre-defined number of markers to a plot instead of
% one at each datapoint which is default in Matlab.
%
% Usage:
% ADDMARKERS(HI) adds 5 markers to the input handle HI, J=1..length(HI)
% ADDMARKERS(HI,N) adds N markers to the input handle HI, J=1..length(HI)
% HO=ADDMARKERS(...) returns the output handle HO for future usage such as
% changing colors, markers, etc.
%
% Hint: use the output handle when adding legends, e.g.
% LEGEND(HO,'legend 1',...,'legend N')
%
% The markers will be added in the following order:
%
% o     Circle
% s     Square
% d     Diamond
% x     Cross
% +     Plus sign
% *     Asterisk
% .     Point
% ^     Upward-pointing triangle
% v     Downward-pointing triangle
% >     Right-pointing triangle
% <     Left-pointing triangle
% p     Five-pointed star (pentagram)
% h     Six-pointed start (hexagram)
%
% By Matthijs Klomp at Saab Automobile AB, 2009-10-11
% E-mail: matthijs.klomp@gm.com
%
if nargin<2, n=5; end % default number of markers
if nargin<1, error('Supply an input handle as input argument.'), end
figure(get(get(hi(1),'parent'),'parent'))% plot in the figure of the handle
subplot(get(hi(1),'parent'))            % plot in the subplot of the handle
hold on                                 % do not overwrite the current plot
markers = {'o','s','d','^','v','x','+','*','.','>','<','p','h'};
ho = hi;                                % initialize output handle
for i = 1:length(hi)
    x = get(hi(i),'xdata');             % get the independent variable
    y = get(hi(i),'ydata');             % get the dependent variable data
    s = linspace(1,n,length(x));        % sampling independent variable
    sn = [1 (2:n-1)+randn(1,n-2)/n n];  % add some noise to avoid overlap
    xrs = interp1(s,x,sn,'nearest');    % downsample to n datapoints
    yrs = interp1(s,y,sn,'nearest');    % downsample to n datapoints
    plot(xrs,yrs,...                    % Plot the markers
        'Marker',markers{i},'LineStyle','None','Color',get(hi(i),'Color'));
    ho(i) = plot([0 1],[1 1]*NaN,...    % Create the output handle
        'Marker',markers{i},...
        'LineStyle',get(hi(i),'Linestyle'),...
        'Color',get(hi(i),'Color'));
end
if nargout==0, clear ho, end
x=(linspace(1,10,100));
y=x+1;
plot(x,y)
h=plot(x,y);
h0=addmarkers(h,4);%返回h0可以改变格式
legend(h0,'legend 1');

实现效果如下: 

【Matlab】向图像中添加帮助读者理解的额外标注信息

上一篇:怎样创建并插入一个属性节点


下一篇:Vue中computed(计算属性)、methods、watch的区别