How to zoom out further (using image/imshow)

27 views (last 30 days)
I have been working on a set of GUI tools that incorporate one or more axes for image viewing. One frustration has always been the inefficient view control behavior of IMSHOW when zooming. If my axes and image have contrary aspect ratios (one is short and wide and the other is tall and narrow), zooming in on an image doesn't allow the user any better use of the space defined by the parent axes. The effective viewport aspect ratio (abs(diff(xlim))/abs(diff(ylim)) remains the same no matter the zoom level. In any other image editing application, zooming such an image will fill the unused space in the axes until the displayed region and axes have the same aspect ratio.
So far, my approach has been to use a callback function (actionpostcallback) on each zoom event. If there is a mismatch of aspect ratios between the viewport and axes, i can scale the viewport in accordingly. This only changes anything when zooming in the first time. After this, the two aspect ratios are the same, and the ability to zoom out to the initial zoom level is unpredictable.
In R2009b, the normal gui zoom tools (zoom in/out or scroll wheel) can't zoom out further than the initial zoom level. In normal use, this works fine because the viewport aspect ratio never changes. Whichever dimension it's using to limit the scaling doesn't matter. But since I'm changing the viewport and i can never seem to predict whether it's Xlim or Ylim which will be clamped by the plot/zoom/graphics routines, I often can't zoom back out.
I can see a couple of ways which I could solve this issue:
1: If my callback function were able to know what zoom level was attempted but disallowed, I could make it zoom out manually. I don't know how I'd get that information in a callback that occurs after the zoom event. All I can hope to detect is that geometry stops changing between calls.
2: If I were able to find a way to change the "can't zoom further out" behavior, this could all be managed easily. I'm pretty sure that's not a trivial thing. In R2015b, one can easily zoom out further than the initial zoom factor, but I imagine that's a consequence of a lot of changes to the graphics handling routines.
3: Pad every image such that it has the same aspect ratio as the axes.
4: Do something entirely different.
I'm not sure this has been a clear problem description. I feel I almost need a video to show what happens, and I didn't want to post a mess of code. If anyone has any insight, it would be appreciated. If anyone wants further details, I can add them.

Accepted Answer

Image Analyst
Image Analyst on 18 Jul 2016
You're not using api calls. See the Mathworks image zooming demo. You'll see you can change the aspect ratio. You're not stuck using the aspect ratio of the original image forever.
  1 Comment
DGM
DGM on 19 Jul 2016
Edited: DGM on 19 Jul 2016
You're right; I didn't even know that existed. While this certainly opens up a lot of possibilities (for this and for simplifying other things), I'm not sure how I could apply that to the mouse-wheel zoom functionality unless simply via a windowscrollwheelfcn. I may give that a try.
It's a minor concern at this point, but I'd also rather avoid adding more IPT dependencies than I need to.
EDIT: With this suggestion in mind, my searching was a bit more fruitful; however, I still haven't found anything that succinctly provides the desired behavior without essentially reinventing all of the view control tools. Between trying to use WindowScrollWheelFcn in a manner that works with multiple axes and the mutually exclusive choices of custom methods and built-in zoom/pan controls, I could never get anything that worked completely and consistently, without causing conflicts or issues in different versions.
I know there are most certainly workarounds for some of the things I got stuck on, but at this point, I know enough to know that some of it is over my head.
In the vein of "reinventing view controls", I simply chose to use akZoom from the FEX for more intuitive mouse controls, and I avoided the aspect ratio/viewport issue by forcing the viewport to fill the parent axes immediately after calling image/imshow, instead of trying to manage it when a zoom event occurred.
function padimshow(h)
set(h,'units','pixels');
axpos=get(h,'position');
set(h,'units','normalized');
axaspect=axpos(3)/axpos(4);
ze=[get(h,'ylim'); get(h,'xlim')];
zeaspect=abs(ze(2,1)-ze(2,2))/abs(ze(1,1)-ze(1,2));
center=mean(ze,2);
if zeaspect<axaspect
% if viewport is taller & skinnier than axes
w=abs(ze(2,1)-ze(2,2))/zeaspect*axaspect;
newlimit=[center(2)-(w/2) center(2)+(w/2)]
set(h,'xlim',newlimit)
else
% if viewport is shorter & fatter than axes
w=abs(ze(1,1)-ze(1,2))/axaspect*zeaspect;
newlimit=[center(1)-(w/2) center(1)+(w/2)]
set(h,'ylim',newlimit)
end
end
The combination of this and akZoom works smoothly, without flickering or glitchiness.
If I had chosen to roll my own comprehensive view controls replacement, I'd basically be reinventing akZoom with a few extra bits. In time, or when I am more comfortable with what I've been learning, I may come back to trying to do this better, but it works for now.
Thanks for the insight, on this question and all the others.

Sign in to comment.

More Answers (0)

Categories

Find more on Visual Exploration 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!