# Changing graphics coordinates to the image coordinate frame

The graphics libraries I'm used to using with images, all use image coordinate frames. That is, the origin is in the upper left hand corner.

I'm relatively new to using Mathematica for image processing and the Graphics command.

Having the coordinate frame for graphics overlays in the lower left hand corner is causing me endless headaches, as all annotations/overlays/JSON files etc. from the other products/projects I use, all have upper left hand corner as the origin.

Here is an example generated with this code:

img = ExampleData[{"TestImage", "House"}];
Show[img,
Graphics[{EdgeForm[{Thick, Blue}], FaceForm[],
Rectangle[{10, 20}, {30, 60}]}]]


Is there some function foo I can wrap around my Graphics object such that, if I did this:

img = ExampleData[{"TestImage", "House"}];
Show[img,
foo[Graphics[{EdgeForm[{Thick, Blue}], FaceForm[],
Rectangle[{10, 20}, {30, 60}]}]]]


I'm looking for something operating at the high "graphics" level, not something I have to do for each individual graphics primitive....

• does height = Last@ImageDimensions[img]; Show[img, Graphics[{EdgeForm[{Thick, Blue}], FaceForm[], Rectangle[{10, 1 + height - 20}, {30, 1 + height - 60}]}]] give what you need?
– kglr
Commented May 13, 2021 at 5:01
• Yes, thanks for clarifying. That's the correct transformation.
– John
Commented May 13, 2021 at 5:02
• But, that doesn't get me what I need. How does one map that across all the graphics objects such that they render properly? I can do it by hand for individual cases. I was hoping for a way to do it more automagically.
– John
Commented May 13, 2021 at 5:16

Here's a way that might work, though I'm not sure if it would break in some cases:

flip[height_?NumericQ][g : Graphics[p_, options___]] :=
Graphics[GeometricTransformation[
p, {{{1, 0}, {0, -1}}, {0, height}}], options]


Then flip[imageheight][Graphics[...]] gets you what you want! But we can do better by packaging this into a custom version of Show:

ImageShow[img_Image, graphics___Graphics] :=
Show[img, Sequence @@ (flip[ImageDimensions[img][[2]]] /@ {graphics})]


Then simply using it in place of Show gets you what you want, e.g. ImageShow[img, Graphics[...]]. (By the way, I checked—I was worried about being off-by-one, but I think this puts the origin at the right place!)

EDIT: It was mentioned that Text works...well, undesirably, appearing upside down. So, I created different versions of the above functions that correct text by default, but allow an option to be passed to turn this off.

ClearAll[flip, ImageShow]

correctText[Text[text_, options___Rule]] :=
GeometricTransformation[Text[text, options], {{1, 0}, {0, -1}}]

correctText[Text[text_, {x_, y_}, args___]] :=
GeometricTransformation[Text[text, {x, -y}, args], {{1, 0}, {0, -1}}]

correctText[Text[text_, Scaled[{x_, y_}], args___]] :=
GeometricTransformation[Text[text, Scaled[{x, -y}], args], {{1, 0}, {0, -1}}]

correctText[Text[text_, Scaled[{dx_, dy_}, {x_, y_}], args___]] :=
GeometricTransformation[
Text[text, Scaled[{dx, -dy}, {x, -y}], args], {{1, 0}, {0, -1}}]

SetAttributes[correctText, Listable]

correctText[x_] := x

flip[height_?NumericQ, OptionsPattern[{CorrectText -> True}]][g : Graphics[p_, options___]] :=
Graphics[GeometricTransformation[
If[OptionValue[CorrectText], correctText[p],
p], {{{1, 0}, {0, -1}}, {0, height}}], options]

ImageShow[img_Image, graphics___Graphics, opts : OptionsPattern[{CorrectText -> True}]] :=
Show[img,
Sequence @@ (flip[ImageDimensions[img][[2]], opts] /@ {graphics})]

(* Test: *)

img = ExampleData[{"TestImage", "House"}];
ImageShow[img,
Graphics[{EdgeForm[{Thick, Blue}], FaceForm[],
Text["ABCDEF", {50, 20}], Rectangle[{10, 20}, {30, 60}]}]]

ImageShow[img,
Graphics[{EdgeForm[{Thick, Blue}], FaceForm[],
Text["ABCDEF", {50, 20}], Rectangle[{10, 20}, {30, 60}]}], CorrectText -> False]

• Interestingly, and not surprisingly, the Text graphics directive renders text upside down with this solution....
– John
Commented May 20, 2021 at 3:50
• @John Good point! I updated the answer to correct for that. :) Commented May 21, 2021 at 3:57

Functions to convert back and forth between image coordinates and (pixel )indices:

Clear[ConvertImageCoordinatesToIndices];
ConvertImageCoordinatesToIndices[InPt_List, InIm_Image] := Module[{
IMax, JMax
},
{IMax, JMax} = ImageDimensions[InIm];
{JMax - InPt[[2]], InPt[[1]]}
];

Clear[ConvertImageIndicesToCoordinates];
ConvertImageIndicesToCoordinates[InPt_List, InIm_Image] := Module[{
IMax, JMax
},
{IMax, JMax} = ImageDimensions[InIm];
{InPt[[2]], JMax - InPt[[1]]}
];


An alternative approach to @thorimur's response, is to make use of HighlightImage.

This does essentially what was requested, i.e. provide a way to place arbitrary graphics objects over an image similar to Show, while making use of image coordinates, instead of graphics coordinates.

• HightlightImage has (0,0) in the lower left corner. Commented Dec 23, 2022 at 2:58