Graphic sizing in Matlab R2015b

I would like to introduce Daniel Dolan of Sandia National Laboratories. Dan works on a variety of data analysis projects in Matlab, and is an active lurker on MATLAB Central. Dan has a habit of finding interesting bugs for the Mac version of Matlab. Today he will discuss graphic sizing in Matlab and important changes that occurred in release R2015b.

Matlab-generated graphics are often not displayed at their requested size. This problem has been known for some time and has a well-known solution: setting the root object’s ScreenPixelsPerInch property to the display’s actual DPI (dots per inch) value. Release R2015b no longer supports this solution, creating problems for publication graphics and general readability.

Physical sizing in R2015a vs. R2015b (click for full-size)

Physical sizing in R2015a vs. R2015b (click for full-size)


Physical sizing

Matlab supports graphic sizing in various physical units: inches, centimeters, and points. For example:

figure; axes('Box','on', 'Units','inches','Position',[0.3 0.3 4 4]);

requests to display an axes having square sizes measuring exactly 4″ (101.6 mm) each. It is evident, however, that the displayed axes is smaller than 4″. The mismatch between requested and physical size depends on the display and operating system — go ahead, try it on your system. The problem is particularly severe on Mac laptops, presumably even worse for those with Retina displays.

The problem is that Matlab cannot determine pixel size, which varies from one display to the other. Generating a figure spanning a particular number of pixels (e.g., 1024 x 768) is easy, but absolute physical units requires a conversion factor called ScreenPixelsPerInch, which is a root property (see related post on setting/getting default graphics property values):

DPI = 110;                             % dots per inch for my 27" Apple Cinema Display
set(0,    'ScreenPixelsPerInch',DPI);  % all releases prior to R2015b
set(groot,'ScreenPixelsPerInch',DPI);  % R2014b through R2015a

DPI values tend to be higher for laptops, usually in the 120-130 range. Retina displays are supposed to be >300 DPI, but I have not been able to test that myself.

There are several ways to determine the correct DPI setting for a particular display. It may be available in the hardware specifications, and it can be calculated from the diagonal size and the number of pixels. Unfortunately these methods are not always reliable. If you really care about physical sizing, the best approach is to actually calibrate your display. There are tools for doing this at Matlab Central, but it’s not hard to do manually:

  • Create a figure.
  • Manually resize the figure to match a convenient width. I often use a piece of US letter paper as 8.5″ guide on the display.
  • Determine the width of the figure in pixels:
    set(gcf,'Units','pixels');
    pos = get(gcf,'Position');
    width = 8.5; % inches
    DPI = pos(3) / width;

I usually apply the DPI settings in my startup file so that Matlab begins with a calibrated display.

What changed in 2015b?

ScreenPixelsPerInch is a read-only property in R2015b, so display calibration no longer works. The following sequence of commands:

figure('Units','inches', 'PaperPositionMode','auto', 'Position',[0 0 4 4]);
set(gcf, 'MenuBar','none', 'ToolBar','none', 'DockControls','off', 'NumberTitle','off');
axes('FontUnits','points', 'FontSize',10);
image

now renders differently in R2015b than does for a calibrated display in R2015a. Differences between the two outputs are shown in the screenshot at the top of this post. The grid behind the figures was rendered at 8.5″ x 8.5″ inches on my display; if your browser’s zoom level isn’t 100%, it may appear larger or smaller.

A side effect of improper graphic sizing is that text is difficult to read — the uncalibrated axes labels are clearly smaller than 10 points. These examples were rendered on ~110 DPI display. Matlab assumes that Macs use 72 DPI (96 DPI on Windows), so graphics appear at 65% of the request size.

The loss of ScreenPixelsPerInch as an adjustable setting strongly affects anyone using Matlab for publication graphics. Scientific and engineering journals are extremly strict about figure widths. With a calibrated screen, figure appear exactly as they will when printed to a file (usually EPS or PDF). Figures are often made as small as possible to and densely packed to save journal space, and accurate sized display helps the author determine legibility. Displaying accurately sized graphics is very difficult in R2015b, which is unfortunate given the many enhancements in this release.

Developers who create graphical interfaces for other users should also care about this change. A common complaint I get is that text and control labels is too small to easily read. Screen calibration deals with this problem, but this option is no longer available.

Where do we go from here?

I reported the above issues to the Mathworks several months ago. It does not appear as a formal bug, but technical support is aware of the problem. The change is part of the “DPI aware” nature of release R2015b. So far I have found no evidence this release is any more aware of pixel size than previous releases, but my experience is limited to non-Retina Macs. I welcome input from users on other operating systems, particularly those with high-resolution displays.

To be fair, correct physical sizing is not an easy across the many platforms that Matlab runs on. Display resolution is particularly tricky when it changes during a Matlab session, such as when computer is connector to projector/television or a laptop is connected to a docking station.

Thankfully, printed graphic sizes are rendered correctly when a figure’s PaperPositionMode property is 'auto'. Many users can (and will) ignore the display problem if they aren’t dealing with strict size requirements and text legibility isn’t too bad. Some users may be willing to periodically print publication figures to externally verify sizing, but this breaks the interactive nature of Matlab figures.

A potential work around is the creating of a new figure class that oversizes figures (as needed) to account for a particular display. I started working on such a class, but the problem is more complicated than one might think:

  • Child objects (axes, uicontrols, etc.) also must be resized if they are based on physical units.
  • Resized objects must be temporarily restored to their original size for printing, and new objects must be tracked whenever they are added.
  • Figure resolution may need to be changed when moving to different computer systems.

These capabilities are quite possible to implement, but this is a complicated solution to problem that was once easy to fix.

Retina displays don’t suffer as badly as one might think from the DPI mismatch. Even though the display specification may be greater than 200 DPI, OS X and/or Matlab must perform some intermediate size transformations. The effective DPI in R2015a is 110-120 for 13-15″ MacBook Pro laptops (at the default resolution). Objected sized with physical units still appear smaller than they should (~72/110), but not as small as I expected (<72/200).Effect pixel size can also be changed by switching between different monitor scalings. This isn't entirely surprising, but it can lead to some interesting results because Matlab only reads these settings at startup. Changing the display scaling during a session can cause square figures to appear rectangular. Also, the effective DPI changes for setting: I could reach values of ~60-110 DPI on an Apple Cinema Display.So where does this leave us? Display calibration was always a finicky matter, but at least in principle one could make graphics appear exactly the same size on two different displays. Now it seems that sizing is completely variable between operation systems, displays, and display settings. For publication graphics, there will almost always be a disconnect between figure size on the screen and the printed output; some iteration may be needed to ensure everything looks right in the finished output. For graphical interfaces, font sizes may need to generated in normalized units and then converted to pixels (to avoid resizing).Physical accuracy may not be important for non-publication figures, but the issue of text legibility remains. Some text objects--such as axes and tick labels--can easily be resized because the parent axes automatically adjusts itself as needed. Free floating text objects and uincontrols are much more difficult to deal with. Controls are often sized around the extent of their text label, so changing font sizes may require changes to the control position; adjacent controls may overlap after resizing for text clarity. Normalized units partially solve this problem, but their effect on uicontrols is not always desirable: do you really want push buttons to get larger/smaller when the figure is resized?Can you think of a better workaround to this problem? If so, then please post a comment below. I will be very happy to hear your ideas, as I'm sure others who have high resolution displays would as well.(cross-reference: CSSM newsgroup post)

Categories: Figure window, Guest bloggers, GUI, Handle graphics, Low risk of breaking in future versions, Stock Matlab function, Undocumented feature

Tags: , , ,

Bookmark and SharePrint Print

7 Responses to Graphic sizing in Matlab R2015b

  1. Alain Barraud says:

    hello yair
    Since 2 years I use a windows laptop with a high definition screen 3200×1600.
    I have many problems with it low level error crash. I have contacted mathworks without any positive response. ScreenSize value was excatly the half of the reality (1600×900) for exemple. Testing R2016a I observe that a very few number of bugs have been suppressed. The best way for me was to set >>OpenGL software in my startup file for some applications.
    Thanks a lot for your expertise
    Alain

  2. The following may be an undocumented workaround to force Matlab to use a non-standard DPI for axes children (plot lines, text labels etc.):

    warning off MATLAB:graphics:axeslayoutmanager:InconsistentState
     
    % you can use any axes handle below, instead of gca:
    hAxManager = matlab.graphics.shape.internal.AxesLayoutManager.getManager(gca);
    hAxManager.Parent.ScreenPixelsPerInch = 192;  % for example

    Unfortunately, this only affects axes children, not GUI components (figure, uicontrols etc.). Still, it’s better than nothing I guess…

  3. Dan says:

    Looks like the property is still not modifiable in 2016a. The error message informs us to simply change the resolution in our OS. Right.

    >> set(0, 'ScreenPixelsPerInch',100);
    Warning: The ScreenPixelsPerInch property became read-only in R2015b.
    To change the size of text and other elements on your screen, adjust
    the screen resolution in your operating system settings. 
    For more information, see Root ScreenPixelsPerInch Property.
    • Dan Dolan says:

      I’m also puzzled by this message. I can tweak resolution settings, but not precisely enough to actually calibrate this screen.

  4. Dan OShea says:

    Thanks for writing this up Yair. A related issue occurs when using Matlab with external monitors (at least on MacOS). I end up with figure windows where everything is 2x the size it should be and seems to be cropped to the upper left corner of the full figure. I haven’t gotten very far with support on this:

    “Currently, MATLAB is not tested to work on external monitors and so issues such as this may occur. Graphics components in MATLAB do not detect changes in such settings as system DPI/screen size, etc. Our developers are aware of this limitation and may address it in a future release.”

    Ugh.

  5. Steven Lauwereins says:

    On a macbook pro 13″ retina the DPI awareness of matlab works only when the screen is in default mode (and then it looks indeed like any retina aware app). Whenever you scale the screen to have more real estate the axes get cropped to the upper left corner. The size of this is dependent on the scaling you have applied on your windows.

    A functional work around (although annoying) is the following: Before starting matlab put your screen back in the default mode (where every rendered pixel is 4 physical pixels). After matlab has started you can set your display back to the preferred scaling, from then on the figures are shown in the correct size.

Leave a Reply


Your email address will not be published. Required fields are marked *