MATLAB Answers

1

Duplicating an imshow image into a new figure, without using imshow

Asked by davidnhutch on 31 Dec 2015
Latest activity Edited by Walter Roberson
on 11 Jun 2019
I use imshow to show the 'coins.png' built-in image. I'm trying to duplicate that image perfectly into another figure. The below code ALMOST works, but there are still differences:
  • The colormap is not copied over, despite my effort on the last line to do so
  • The new image isn't the same size, despite copying over the 'Position' property
  • The new image is arbitrarily stretchable, unlike the old image (try it and see)
  • The new image has the thick black axis UNDER the image, while the old image has it on top of the image
  • The image is flipped
How can I fix the above?
In case you are wondering, no I can't just call imshow('coins.png') on the second figure. Because, in my case I have a more complicated situation (I have an existing .fig file with an image inside it that I am recalling into a subplot, and I need to replicate the axis in the subplot axis. If I just call imshow on the second figure's subplot I lose all the associated formatting such as axis labels and axis titles. I have many such images to do, so can't do it manually. Anyway if I can solve the above problem I could do this.)
Here's the code:
clear;
close all;
% Old figure
f1 = figure;
imshow('coins.png')
axis on;
colormap(jet);
old_ax = gca;
set(old_ax,'LineWidth',2);
% New figure -- should be a perfect copy of the old
f2 = figure;
new_ax = gca;
copyobj(get(old_ax,'children'),new_ax);
% Copy over a bunch of properties
properties = {'Title','Box','BoxStyle','LineWidth','XLabel','YLabel',...
'XLim','YLim','CLim','CLimMode','XTick','YTick','TickDir',...
'FontName','FontSize','Position'};
for i = 1:numel(properties)
prop_str = char(properties(i));
set(new_ax,prop_str,get(old_ax,prop_str));
end
% Copy over the colormap too
cm = colormap(old_ax);
colormap(new_ax,cm);

  0 Comments

Sign in to comment.

2 Answers

Answer by Image Analyst
on 31 Dec 2015
 Accepted Answer

This seems to work.
% Create the Old figure
f1 = figure;
imshow('coins.png')
axis on;
colormap(jet);
oldColorMap = colormap();
xlabel('x');
ylabel('y');
title('Coins Figure');
old_ax = gca;
set(old_ax,'LineWidth',2);
hold on;
% New figure -- should be a perfect copy of the old
f2 = figure;
new_ax = gca;
copyobj(get(old_ax, 'children'), new_ax);
% Copy over the figure size
fig1Position = get(f1, 'Position');
set(f2, 'Position', fig1Position');
% Copy most of the properties
oldProperties = get(old_ax);
figureFieldNames = fieldnames(oldProperties)
for k = 1:length(figureFieldNames)
thisProperty = figureFieldNames{k};
if ~isempty(strfind(thisProperty, 'Fcn'))
continue;
end
if strcmpi(thisProperty, 'Tag') || strcmpi(thisProperty, 'Parent') || strcmpi(thisProperty, 'Children')
continue; % Skip the tag, parent, and children properties
end
fprintf('About to try to set %s\n', thisProperty);
try
new_ax.(thisProperty) = old_ax.(thisProperty);
catch
% Some properties are readonly so this will skip them
end
end
% Copy over the colormap too
colormap(new_ax, oldColorMap);
The only thing I can't figure out is why it removes the xLabel, yLabel, and title from the old figure when it applies them to the new figure. It should just add them to the new figure, not remove them from the old figure. Maybe Walter knows.

  3 Comments

Also, I would add: I found that to copy it to a subplot (which was my final goal although not the core question) you need to add
if ~isempty(strfind(thisProperty,'Position'))
continue;
end
This skips any Positioning action, allowing you to add it as a regular subplot.
This does not handle legend, at least not in R2014a or earlier. It is not copying a figure into a subplot, or even a figure into a panel, it is copying an axes into a new figure. Renderer mode, colormap, and numerous other properties will not be copied. uicontrol and other such objects will not be copied either.
If this is the solution to the problem then the problem was badly described.

Sign in to comment.


Answer by Walter Roberson
on 31 Dec 2015

You can apply hold() to a specific axes, and supply the axes as the Parent for imshow()
hold(subplot_axes, 'on');
imshow(TheImage, 'Parent', subplot_axes);

  2 Comments

Thanks. However, unfortunately this doesn't solve the problem because it doesn't bring along the formatting (title, axes titles, ticks, etc) in TheImage.fig file.
So, instead, I want to load the .fig file and then copy it, along with all its formatting, into a new figure or a new subplot. For starters I'm just trying to copy it in to a new figure. So, we can forget the subplot thing for now.
You cannot generally copy a figure into a subplot. figures can include menu items, uicontrol, uitable, uipanel, and various other items that are not permitted to be parented to an axes. Also in R2014a and earlier, legend are implemented as axes with linked properties.
You probably do not need to copy the fig into a new figure. You can openfig() it and specify 'new' (which is the default), store the resulting figure handle, and modify the contents of the result.
What might make sense is to create a uipanel in a new figure and copy the figure contents in to it, without the menus or toolbar, if you are trying to put the figure content "inside" a GUI.
pan2 = uipanel('parent', f2, 'Position', ....);
copyobj(findall(allchild(f1),'-depth', 0, '-not', 'type', 'uimenu', '-not', 'type', 'uitoolbar'), pan2)
This does not get all of the properties of the fig; for example it does not copy over the renderer mode or the current colormap (R2014a and earlier has only a single colormap per figure). But you need to ask yourself whether it is meaningful to copy those over if you are putting the content inside a container -- what if the other things you want to put into the new figure disagree?
But if you must...
%get names of settable properties
hu = get(0,'HideUndocumented');
set(0, 'HideUndocumented', 'off');
settable = set(f1);
set(0, 'HideUndocumented', hu);
%need to exclude some
fn = setdiff(fieldnames(pq),{'Children','CurrentAxes','CurrentCharacter','CurrentObject'});
for K = 1 : length(fn)
set(f2, fn{K}, get(f1, fn{K}));
end
We do not simply get(f1) to get all of the properties because not all of them can be set(); instead we set(f1) because if you set() without passing in at least parameter/value pair then what is returned is a struct with one field per parameter name (with the field content illustrating the possible values). But you need to weed out the Current* fields anyhow.
The bit about HideUndocumented has to do with copying properties whose existence is normally hidden from view... since we want all of the properties copied.

Sign in to comment.