5
$\begingroup$

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}]}]]

enter image description here

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....

enter image description here

$\endgroup$
3
  • $\begingroup$ 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? $\endgroup$
    – kglr
    Commented May 13, 2021 at 5:01
  • $\begingroup$ Yes, thanks for clarifying. That's the correct transformation. $\endgroup$
    – John
    Commented May 13, 2021 at 5:02
  • $\begingroup$ 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. $\endgroup$
    – John
    Commented May 13, 2021 at 5:16

3 Answers 3

2
$\begingroup$

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]
$\endgroup$
2
  • 1
    $\begingroup$ Interestingly, and not surprisingly, the Text graphics directive renders text upside down with this solution.... $\endgroup$
    – John
    Commented May 20, 2021 at 3:50
  • 1
    $\begingroup$ @John Good point! I updated the answer to correct for that. :) $\endgroup$
    – thorimur
    Commented May 21, 2021 at 3:57
1
$\begingroup$

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]]}
   ];
$\endgroup$
0
$\begingroup$

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.

$\endgroup$
1
  • $\begingroup$ HightlightImage has (0,0) in the lower left corner. $\endgroup$
    – Danvil
    Commented Dec 23, 2022 at 2:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.