Can I override imshow InitialMagnification default behavior?
9 views (last 30 days)
Show older comments
Problem:
I want to use imshow with a uiaxes in a matlab app to show images at exactly 100% magnification. The imshow function has a name/value argument InitialMagnification for this purpose. However, the documentation (linked below) states the following:
"Initially, imshow attempts to display the entire image at the specified magnification. If the magnification value is so large that the image is too big to display on the screen, imshow displays the image at the largest magnification that fits on the screen."
I want to override this behavior and force the imshow function to display at 100% magnification even in cases where the image is larger than the axes it is being displayed on.
Context:
I have a mlapp that I use to view images from a dataset that sometimes have data that is aliased. I need the user to be able to view the data at exactly 100% magification (1 image pixel maps to 1 screen pixel) so the user can see for themself if the data is aliased without aliasing from the monitor interfering. The images are always much larger than the uiaxes that I am displaying the image on, so the default behavior to reduce the magnification until the image fits kicks in, overriding the "InitialMagnification", 100 argument I give to imshow.
0 Comments
Answers (2)
DGM
on 3 Sep 2024
Edited: DGM
on 3 Sep 2024
This can be set manually from the preferences dialog, under Image Processing Toolbox > IMSHOW Display.
This can also be done programmatically; see:
That said, I don't think that will guarantee that an image is displayed 1:1 if it's larger than the axes. Whenever an image (at the specified magnification) is larger than the axes, I'm pretty sure it will be downscaled to fit.
In general, getting an exact 1:1 scaling is difficult, and truesize() does not work on uifigures.
I might have to come back to this, but right now I'm too tired to think.
4 Comments
DGM
on 4 Sep 2024
In between other work, I've been beating my head against trying to get a uifigure>uipanel>uiaxes>image layout to work at 1:1, and I've got a lot of nowhere. I can't get the scaling consistently correct, and whenever I add an image() object to the uiaxes(), the uipanel object loses its scrollbars, despite being scrollable and containing a descendant object larger than itself.
As much as I hate the bad ideas that are rolled into uiimage(), its zoom behavior is what you're looking for. The comments I gave on Jaimin's answer should at least provide a feasible method for making sure uiimage() maintains fidelity.
Jaimin
on 4 Sep 2024
Hi @Jeff
According to the issue description and context provided, I understand that you want to display the image without any automatic aliasing.
To address this, I have a workaround that might help you. You can use the "image" component in App Designer with the "scaleMethod" set to "none."
I have attached a sample code below for better understanding.
function startupFcn(app)
% Load an image
img = imread(<PATH>); % Replace with your image path
% Display the image in the UIImage component
app.Image.ImageSource = img;
app.Image.ScaleMethod = 'none';
% Optionally adjust the size of UIImage to be larger than the panel
app.Image.Position = [1, 1, size(img, 2), size(img, 1)]; % [x, y, width, height]
end
For more information, please refer this MathWorks Documentation
I hope this will be helpful.
2 Comments
DGM
on 4 Sep 2024
Edited: DGM
on 4 Sep 2024
Note that using uiimage() comes with significant problems.
See my answer here and the given link:
TL;DR: If you're passing raster image content directly to uiimage(), it cannot support indexed images, transparency, grayscale images, or binary images. Every other form of image which it accepts will be destroyed by converting it to a low-quality downsampled JPG. It actually uses a 4:2:0 JPG temp file as part of the image display pipeline. If you have a raster image in memory and you want to display it exactly as in memory, uiimage() cannot do the job.
... unless you want to jump through a bunch of hoops; again, see the link.
DGM
on 4 Sep 2024
Edited: DGM
on 4 Sep 2024
For example, this is combining the linked answer with your suggestion (which does work quite easily!)
% this may either be a raster image or a filename
inpict = '1pxcb.png'; % just a filename
% but let's say we have data in the workspace
[inpict,CT,alpha] = imread(inpict);
inpict = joinalpha(inpict,alpha); % attach any alpha if it exists
% throw together a GUI with a tiny scrollable panel in one corner
app.UIFigure = uifigure();
app.UIPanel = uipanel(app.UIFigure);
app.UIImg = uiimage(app.UIPanel);
app.UIFigure.Position = [100 100 800 400];
app.UIPanel.Position = [10 10 400 200];
app.UIPanel.Scrollable = 'on';
app.UIImg.ScaleMethod = 'none';
% babysit uiimage() so that it can actually handle images
if ischar(inpict)
% uiimage() can handle images if it's allowed to read them from disk
app.UIImg.ImageSource = inpict;
% but we need to know what the image size is
% and we can't rely on querying the uiimage() object to do that
S = imfinfo(inpict);
szimg = [S.Width S.Height]; % [x y]
app.UIImg.Position = [1 1 szimg];
else
% uiimage() cannot correctly handle raster image data from the workspace
% if inpict isn't a filename, we _must_ create a temp file
% this should be able to handle I/IA/RGB/RGBA/indexed
% though any transparency must be attached (use MIMT joinalpha() or cat())
% and the CT associated with an indexed image must be provided
tempfname = createtempfile(inpict,CT,'discardalpha'); % see options
app.UIImg.ImageSource = tempfname;
app.UIImg.Position = [1 1 size(inpict,[2 1])];
end
function tempfname = createtempfile(inpict,CT,alphaoption)
% TEMPFILENAME = CREATETEMPFILE(INPICT,[],ALPHAOPTION)
% TEMPFILENAME = CREATETEMPFILE(INPICT,CT,ALPHAOPTION)
%
% INPICT is an I/IA/RGB/RGBA or indexed-color image of any
% standard image class. Multiframe inputs are not supported.
% CT is a Mx3 unit-scale color table associated with
% indexed-color image inputs. When CT is non-empty, INPICT
% must be a single-channel integer-valued index array.
% ALPHAOPTION specifies how IA/RGBA images are handled
% 'keepalpha' will preserve the given alpha and let uiimage()
% handle the compositing with the figure background or
% whatever is beneath it in the uistack.
% 'discardalpha' will flatten the image by composing it
% with a gray checkerboard background.
% create ImageComp directory under tempdir
tmpDir = fullfile(tempdir, 'ImageComp');
[~,~,~] = mkdir(tmpDir);
% create tempfile for image writing
tempfname = fullfile([tempname(tmpDir), '.png']);
% prepare and write temp file
if isempty(CT)
switch lower(alphaoption)
case 'keepalpha'
% keep any attached alpha and let uiimage() handle the compositing
[cdata adata] = splitalpha(inpict);
if isempty(adata)
imwrite(cdata,tempfname);
else
imwrite(cdata,tempfname,'alpha',adata);
end
case 'discardalpha'
% discard alpha by doing checkerboard matting composition
cdata = alphasafe(inpict);
imwrite(cdata,tempfname);
end
else
imwrite(inpict,CT,tempfname);
end
end
As before, this relies on MIMT joinalpha(),splitalpha() and alphasafe() to deal with handling images with transparency. If no transparency is ever used, those can be simplified away. Similarly, if indexed images are never used, some simplifications can be made. This is a simplified version of the above:
% this may either be a raster image or a filename
inpict = '1pxcb.png';
% let's say we have data in the workspace
% inpict can only be I or RGB
% no alpha, no indexed color
inpict = imread(inpict);
% throw together a GUI with a tiny scrollable panel in one corner
app.UIFigure = uifigure();
app.UIPanel = uipanel(app.UIFigure);
app.UIImg = uiimage(app.UIPanel);
app.UIFigure.Position = [100 100 800 400];
app.UIPanel.Position = [10 10 400 200];
app.UIPanel.Scrollable = 'on';
app.UIImg.ScaleMethod = 'none';
% babysit uiimage() so that it can actually handle images
if ischar(inpict)
% uiimage() can handle images if it's allowed to read them from disk
app.UIImg.ImageSource = inpict;
% but we need to know what the image size is
% and we can't rely on querying the uiimage() object to do that
S = imfinfo(inpict);
szimg = [S.Width S.Height]; % [x y]
app.UIImg.Position = [1 1 szimg];
else
% uiimage() cannot correctly handle raster image data from the workspace
% if inpict isn't a filename, we _must_ create a temp file
% this should be able to handle I/RGB
tempfname = createtempfile(inpict); % see options
app.UIImg.ImageSource = tempfname;
app.UIImg.Position = [1 1 size(inpict,[2 1])];
end
function tempfname = createtempfile(inpict)
% TEMPFILENAME = CREATETEMPFILE(INPICT)
%
% INPICT is an I/RGB image of any standard image class.
% IA/RGBA/indexed inputs are not supported.
% Multiframe inputs are not supported.
% create ImageComp directory under tempdir
tmpDir = fullfile(tempdir, 'ImageComp');
[~,~,~] = mkdir(tmpDir);
% create tempfile for image writing
tempfname = fullfile([tempname(tmpDir), '.png']);
% prepare and write temp file
imwrite(inpict,tempfname);
end
The main point here is that uiimage() cannot be allowed to touch raster image data. You have to create tempfiles for uiimage(), because uiimage() can't create acceptable temp files on its own. The alternative is to modify uiimage(), but that might cause other issues with app portability. Not sure.
Attached is a fine checkerboard image which can be used to verify that no aliasing is occurring due to the display scaling.
If you want to check my claims that the above workaround is necessary for fidelity, you can load the checkerboard image into memory and feed it directly to uiimage() as the documentation suggests:
inpict = imread('1pxcb.png'); % this is a 1-ch logical image
inpict = repmat(double(inpict),[1 1 3]); % but uiimage() can't handle either of those things
% ... all the same gui setup ...
app.UIFigure = uifigure();
app.UIPanel = uipanel(app.UIFigure);
app.UIImg = uiimage(app.UIPanel);
app.UIFigure.Position = [100 100 800 400];
app.UIPanel.Position = [10 10 400 200];
app.UIPanel.Scrollable = 'on';
app.UIImg.ScaleMethod = 'none';
app.UIImg.ImageSource = inpict; % let uiimage() destroy the image
app.UIImg.Position = [1 1 size(inpict,[2 1])]; % the same scaling
You will notice a subtle and unavoidable banding pattern caused by the compression damage.
See Also
Categories
Find more on Develop uifigure-Based Apps in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!