lampo808
lampo808

Reputation: 315

MATLAB custom datatip in images

Following the instructions on other pages, e.g.

http://blogs.mathworks.com/videos/2011/10/19/tutorial-how-to-make-a-custom-data-tip-in-matlab/ http://it.mathworks.com/help/matlab/ref/datacursormode.html http://it.mathworks.com/matlabcentral/answers/68079-how-to-add-additional-info-to-the-data-cursor

i've written a custom callback function for the datatip to show me the index of the points on a x-y graph as well as their x and y coordinates:

function output_txt = customCallback_DataTip(obj,event_obj)
% Display the position of the data cursor
% obj          Currently not used (empty)
% event_obj    Handle to event object
% output_txt   Data cursor text string (string or cell array of strings).

pos = get(event_obj,'Position');
output_txt = {['X: ',num2str(pos(1),4)],...
    ['Y: ',num2str(pos(2),4)]};

% If there is a Z-coordinate in the position, display it as well
if length(pos) > 2
    output_txt{end+1} = ['Z: ',num2str(pos(3),4)];
else % 2D plot: write index of current point
    i = find(event_obj.Target.XData == pos(1), 1);
    output_txt{end+1} = ['i: ',num2str(i)];
end

This code starts from the default callback suggested by MATLAB, and adds a z-coordinate info whenever the plot is a 3D one. Since I very often need to know the array index of a point on a graph, the custom callback function is enabled automatically at MATLAB startup.

Now, whenever I plot an image (e.g. via imagesc) I would like to have the "normal" image datatip:

image datatip

i.e. with Index/RGB information on it. How can I modify the callback function in order to obtain this behavior?

EDIT: i would like to modify my custom callback so that it automatically displays something similar to the default MATLAB default datatip when I'm using the datatip on an image.

Upvotes: 1

Views: 2349

Answers (1)

Suever
Suever

Reputation: 65440

To accomplish this, you can check the type of the event_obj.Target and respond accordingly.

get(event_obj.Target, 'type')

All images (whether imagesc, image, or imshow) will have a Type of image.

isImage = strcmpi(get(event_obj.Target, 'type'), 'image')

You can then extract the image data. If you have an indexed image, you can also get the colormap to determine all the other information to go into the datatip.

cdata = get(event_obj.Target, 'cdata');
cmap = colormap(ancestor(event_obj.Target, 'axes'));

Bringing this all together, I would modify your custom data tip callback to be something like this.

function output_txt = callback(obj, event_obj, clims)
    % Get the cursor location
    pos = get(event_obj, 'Position');

    output_txt = {sprintf('[X,Y]: [%i, %i]', pos(1), pos(2))};

    if strcmpi(get(event_obj.Target, 'type'), 'image')
        % Get the image data
        cdata = get(event_obj.Target, 'CData');

        % Check to ensure pos is in range
        if pos(1) < 1 || pos(1) > size(cdata, 2) || ...
           pos(2) < 1 || pos(2) > size(cdata, 1)
            rgb = {NaN, NaN, NaN};
            newline = sprintf('[R,G,B]: [%0.4f %0.4f %0.4f]', rgb{:});
            output_txt = cat(1, output_txt, newline);
            return
        end

        % If the image is RGB
        if size(cdata, 3) == 3
            rgb = num2cell(cdata(pos(2), pos(1), :));

        % If this is an indexed image
        else
            index = cdata(pos(2), pos(1));

            % Figure out the colormap
            hax = ancestor(event_obj.Target, 'axes');
            cmap = colormap(hax);

            % If the CData is scaled, we need to scale to the colormap
            if strcmpi(get(event_obj.Target, 'CDataMapping'), 'scaled')
                value = (index - clims(1)) * size(cmap, 1) / diff(clims);
            else
                value = index;
            end

            % Determine RGB value from colormap
            rgb = num2cell(ind2rgb(round(value), cmap));

            if round(index) == index
                newline = sprintf('Index: %d', index);
            else
                newline = sprintf('Index: %.4f', index);
            end

            % Generate datatip text
            output_txt = cat(1, output_txt, newline);
        end

        output_txt = cat(1, output_txt, ...
                sprintf('[R,G,B]: [%0.4f %0.4f %0.4f]', rgb{:}));

    % Otherwise we use your custom datatip for plots
    else
        index = find(event_obj.Target.XData == pos(1), 1);

        pos = get(event_obj, 'Position');
        output_txt = { sprintf('X: %0.4f', pos(1));
                       sprintf('Y: %0.4f', pos(2))};

        % If there is a Z-coordinate in the position, display it as well
        if length(pos) > 2
            output_txt{end+1} = sprintf('Z: %0.4f', pos(3));
        else % 2D plot: write index of current point
            output_txt{end+1} = sprintf('i: %d', index);
        end
    end
end

If you notice, I'm passing in an additional variable (clims) to the callback function. This is because some versions won't actually allow me to query axes properties from within the datatip UpdateFcn. So this means that you will have to change your UpdateFcn anonymous function just a little bit.

h = datacursormode(fig);
set(h, 'Enable', 'on')

% Here I have to pass the `clims` because I can't fetch them inside
set(h, 'UpdateFcn', @(dt,e)callback(dt, e, caxis(ancestor(dt.Host, 'axes'))));

Using this, I was able to show the proper display for both plots and images (both indexed and RGB).

enter image description here

Upvotes: 4

Related Questions