Set pixel aspect ratio?

UPDATE

In version 12.1 they implemented correct handling of the ImageResolution option for Image (but for Image3D ImageResolution is still ignored).


Original answer

Note: I've sent a suggestion for improvement to the tech support with a link to this post ([CASE:4081592]).

Mathematica already basically supports different resolutions in different directions for Image when importing and exporting (what is documented, for example, on the Documentation page for "JPEG" format):

Options[img = Import["ExampleData/coneflower.jpg", 
   ImageResolution -> {72, 144}], ImageResolution]

Options[Import@Export["coneflower_new.jpg", img], ImageResolution]
{ImageResolution -> {72, 144}}

{ImageResolution -> {72, 144}}

So halirutan is right: the developers just overlooked that this should also influence how the image is rendered by the FrontEnd. Other picture viewers respect this setting, for example here is how Windows Photo Viewer renders the image "coneflower_new.jpg":

screenshot

As one can see from the screenshot, the image is scaled up two times in horizontal direction according to the specified ImageResolution which is two times smaller in horizontal direction (72 dpi) than in vertical direction (144 dpi), as expected.

But I think that it is incorrect to set AspectRatio of the whole image equal to pixel aspect ratio (as halirutan did), because we should also take into account actual ImageDimensions when calculating the overall AspectRatio. So here is my fix for Image which makes it respecting the ImageResolution setting (I wrapped changed portions of the original code by (* edit start *) and (* edit end *) tags):

Unprotect[Image];
MakeBoxes[Image`ImageDump`img : Image[_, Image`ImageDump`type_, Image`ImageDump`info___], 
    Image`ImageDump`fmt_] /; Image`ValidImageQHold[Image`ImageDump`img] ^:= With[{
    Image`ImageDump`newarray = 
     Image`InternalImageData[Image`ImageDump`img, Interleaving -> True], 
    Image`ImageDump`tag = 
     BoxForm`ImageTag[Image`ImageDump`type, 
      Sequence @@ 
       FilterRules[FilterRules[{Image`ImageDump`info}, Except[Options["Graphics"]]], 
        Except[ImageMarkers]]], 
    Image`ImageDump`imgdata = 
     ImageData[Image`ImageDump`img, {"Width", "Height", "DataRange", "ColorFunction"}], 
    Image`ImageDump`imgsize = 
     Image`ImageDump`ruleValue[{Image`ImageDump`info}, ImageSize], 
    Image`ImageDump`mag = 
     Image`ImageDump`ruleValue[{Image`ImageDump`info}, Magnification],
    (* edit start *)
    Image`ImageDump`pixelAspectRatio = 
     With[{Image`ImageDump`res = 
        Image`ImageDump`ruleValue[{Image`ImageDump`info}, ImageResolution]},
      If[MatchQ[Image`ImageDump`res, {_?(# > 0 &), _?(# > 0 &)}],
       Divide @@ Image`ImageDump`res, 1, 1]
      ]
    (* edit end *)
    },
   With[{
     (* edit start *)
     Image`ImageDump`width = 
      Image`ImageDump`imgdata[[1]]/
       If[Image`ImageDump`pixelAspectRatio < 1, Image`ImageDump`pixelAspectRatio, 1, 1],
     Image`ImageDump`height = 
      Image`ImageDump`imgdata[[2]]*
       If[Image`ImageDump`pixelAspectRatio > 1, Image`ImageDump`pixelAspectRatio, 1, 1],
     (* edit end *)
     Image`ImageDump`range = Image`ImageDump`imgdata[[3]],
     Image`ImageDump`cfun = Image`ImageDump`imgdata[[4]]},
    With[{Image`ImageDump`options = Sequence @@ Join[{
          DefaultBaseStyle -> "ImageGraphics",
          ImageSizeRaw -> {Image`ImageDump`width, Image`ImageDump`height},
          PlotRange -> {{0, Image`ImageDump`width}, {0, Image`ImageDump`height}}
          }, 
         Image`ImageDump`ConvertImageSizeToFE[Image`ImageDump`imgsize, 
          Image`ImageDump`mag], 
         FilterRules[{Image`ImageDump`info}, Image`ImageDump`$typesetOptions]]}, 
     GraphicsBox[
      TagBox[RasterBox[
        Image`ImageDump`newarray, {{0, Image`ImageDump`height}, {Image`ImageDump`width, 
          0}}, Image`ImageDump`range, ColorFunction -> Image`ImageDump`cfun], 
       Image`ImageDump`tag, Selectable -> False], Image`ImageDump`options]]]];
Protect[Image];

Now

Import["coneflower_new.jpg"]

screenshot

Image[Image3DSlices[ExampleData[{"TestImage3D", "CThead"}], 100, 3], 
 ImageResolution -> {72, 72/2}]

screenshot


Alexey suggested that the following undocumented (but not going away) ImageSize syntax could be used:

img = Image3DSlices[ExampleData[{"TestImage3D","CThead"}],100,3];
Show[img, ImageSize -> 1 -> {1,2}]

enter image description here

I think this does what you want?


This might have been overlooked in the implementation of Image. When I see this right, then the correct way to handle this would be to respect the setting of ImageResolution and acknowledge that it can be different for different directions

enter image description here

and it is not unusual for medical devices like a CT to have a drastic difference in resolution for different directions. For a plain image, that doesn't know its resolution, the displayed size is always determined by assuming pixels are squares and using the ImageDimension-ratio as aspect-ratio.

However, I would have expected that something like this works

img = Image3DSlices[ExampleData[{"TestImage3D", "CThead"}], 100, 3];
Image[img, ImageResolution -> {100, 100}]

Mathematica graphics

Unfortunately it doesn't, although the GraphicsBox would support it. But we could fix MakeBoxes for Image and we would get

enter image description here

Below are the changes I made as an image because it seems not easily possible to copy code from the PrintDefinitions view.

enter image description here