Message Boards Message Boards

GROUPS:

Perspective Anamorphosis in a Square Trihedral Corner

Posted 25 days ago
354 Views
|
1 Reply
|
8 Total Likes
|

Perspective anamorphosis is when you need a specific viewpoint to observe an image correctly and any other viewpoint will give a deformed image. Inspired by Ewan Gedge's Inspiration on Pinterest, (left) I made a project of creating 3D perspective anamorphosis images using Mathematica. Here is "Lena from ExampleData " in a "square trihedral corner" (right).

introduction pics

To create a similar perspective anamorphism for viewing purposes only is quite simple in Mathematica using PlotStyle with Texture and a TextureCoordinateFunction. Some time ago, I uploaded the Wolfram Demonstration Perspective Anamorphosis of Photographic Images which can be used to make GIFs like these when adapted to a trihedron:

lena trihedralenter image description here

However, these GIFs make nice images but are hardly usable to make a real, printable object. What follows is how I made a printable and "make-able" trihedral perspective anamorphism. We want to look from infinity toward the 3 faces of a trihedron in the direction of its main diagonal and see an image undeformed.

We first create a vertically upright trihedron. Then, find the necessary rotation angles [Alpha] and [Beta] around the x- and y-axes, and the derived rotation matrix to set the diagonal vector (1,1,1) of a unit cube vertically upright at (0,0,Sqrt(3))

Solve[RotationMatrix[\[Alpha], {1, 0, 0}].RotationMatrix[\[Beta], {0, 1, 0}].{1, 1, 1} == {0, 0, Sqrt[3]}, {\[Alpha], \[Beta]}] /. 
   C[1] -> 0 /. C[2] -> 0;
rMat = Simplify[
   Last[RotationMatrix[\[Alpha], {1, 0, 
        0}].RotationMatrix[\[Beta], {0, 1, 0}] /. %]];
(PolygonCoordinates[#1] + 1/2 &) /@ PolyhedronData["Cube", "Polygons"];
triHedron = 
  Map[rMat.#1 &, 
   Polygon /@ %[[Position[%, {0, 0, 0}][[All, 1]]]], {3}];

We first will make a setup similar to the rectangle in a corner of Ewan Gredge above. To simulate our viewing of the rectangle, we intersect the trihedron with a cuboid that has the desired rectangle as a base:

is = RegionIntersection[Cuboid[{-4, -5, 0}, {4, 4, 100}], #] & /@ 
   triHedron;
Graphics3D[{FaceForm[], EdgeForm[Thick], is}, 
 PlotRange -> .65 {{-10, 10}, {-10, 10}, {0, 16}}, Boxed -> False]

trihedron

To proceed, we need a 2D drawing of the three faces by selecting view directions perpendicular to the faces of the trihedron:

(*3 viewvectors*)
Take[SortBy[DeleteDuplicates[Flatten[PolygonCoordinates /@ is, 1]], 
   EuclideanDistance[{0, 0, 0}, #] &], {2, 4}];
GraphicsRow@
 MapThread[
  Graphics3D[{FaceForm[], EdgeForm[AbsoluteThickness[4]], is[[#1]]}, 
    Boxed -> False, ViewPoint -> %[[#2]]] &, {Range[3], {2, 1, 3}}]

3 faces printout

This is a makeshift setup of this using masking tape in a rectangular trihedral corner (of the kitchen):

taped trihedron

To get into more complex projects, we need a function that will map a point P in the x-y plane to the intersection Q of the vertical through P with the trihedron and then rotated around axis r, s or t back to the x-y plane as R.

geometry

From P to Q: computing the z-values of Q for each trihedral face:

Flatten[Solve[{{x, y, z} \[Element] 
       Line[{{x, y, 0}, {x, y, z}}], {x, y, z} \[Element] 
       RegionUnion @@ triHedron}, z] /. 
    Rule[z, ConditionalExpression[value_, region_]] :> value] // 
  FullSimplify;
(*{-Sqrt[2] y,(-Sqrt[3] x+y)/Sqrt[2],(Sqrt[3] x+y)/Sqrt[2]}*)

From Q to R: rotation around the appropriate axis in the x-y plane:

sector[pt : {x_, y_}] := Module[{\[Alpha]}, \[Alpha] = ArcTan @@ pt;
  Which[-\[Pi]/6 <= \[Alpha] < \[Pi]/2, 
   1, -5 \[Pi]/6 <= \[Alpha] < -\[Pi]/6, 3, True, 2]]
trihedronPerspectiveMap[pt : {x_, y_}, \[Phi]_: - ArcSec[Sqrt[3]]] := 
 Module[{z}, 
  z = Switch[sector@pt, 1, (Sqrt[3] x + y)/Sqrt[2], 
    2, (-Sqrt[3] x + y)/Sqrt[2], 3, -Sqrt[2] y, True, 0];
  Chop@Switch[sector@pt, 3, 
    RotationMatrix[\[Phi], {-1, 0, 0.}].{x, y, z}, 2, 
    RotationMatrix[\[Phi], {1, Sqrt[3], 0.}].{x, y, z}, 1, 
    RotationMatrix[\[Phi], {1, -Sqrt[3], 0.}].{x, y, z}, _, {0, 0}]]

We can try out the function trihedronPerspectiveMap on a GraphicsComplex from one of the many "Popular Curves":

The parametric curve of Bugs Bunny is obtained using Interpreter. We extract the coordinates and apply the function trihedronPerspectiveMap to them. This is used to create a printable set of the 3 faces. When glued together, they make up a trihedral image.

flow bunnies

Interpreter[
  Restricted["PopularCurve", 
   EntityClass["PopularCurve", "LooneyTunes"]]]["bugs bunny curve"]
bugsbunnyPrimitives = 
  First[ParametricPlot[%["ParametricEquations"][t], {t, 0, 
      40 \[Pi]}]] /. {x_?NumericQ, y_?NumericQ} :> {x + 189, y + 150}/
     650;
allPts = Cases[
   bugsbunnyPrimitives /. Line -> Identity, {x_?NumericQ, 
    y_?NumericQ}, \[Infinity]];
Animate[Module[{tbl, pt}, pt = AngleVector[{0, -.1}, {.2, \[Phi]}]; 
  tbl = (pt + #1 &) /@ (+allPts); 
  Grid[{{"original", "2D printout view", 
     "3D perspectiveview"}, {Graphics[{Blue, AbsolutePointSize[1.], 
       Point /@ tbl}, Axes -> True, Ticks -> None, PlotRange -> .75], 
     Graphics[{{Blue, AbsolutePointSize[1.], 
        Point[ParallelMap[Most[trihedronPerspectiveMap[#1]] &, 
          tbl, {1}]]}, {FaceForm[{White, Opacity[.85]}], 
        EdgeForm[
         Thin], (Triangle[{{0, 0}, AngleVector[{10, #1 - \[Pi]/12}], 
             AngleVector[{10, #1 + \[Pi]/12}]}] &) /@ {-(\[Pi]/
           6), \[Pi]/2, (7 \[Pi])/6}}}, PlotRange -> 1.185], 
     Graphics3D[{triHedron, {Blue, AbsolutePointSize[2], 
        Point[ParallelMap[trihedronPerspectiveMap[#1, 0.] &, 
          tbl, {1}]]}}, PlotRange -> .7 {{-1, 1}, {-1, 1}, {0, 2}}, 
      ViewAngle -> 0.175, ViewCenter -> {0.65, 0.5`, 1}, 
      ViewPoint -> {1.434, 0.34, 5.94}, 
      ViewVertical -> {.2, 0.24, 0.95}, Boxed -> False, 
      Lighting -> {{"Ambient", White}}]}}]], {\[Phi], 0, 2 \[Pi]}]

bunny open-close

bunny 3D

Finally, we want to test our function on a photographic image. Mapping the function pixel by pixel would be a solution. However, it is easier to create 3 perspective transformation functions and apply them to the complete image. This is "Lena" with the projection of the triHedron on top, dividing the image in 3 sectors.

ImageCrop[
  ImageAdjust@
   ColorConvert[
    ImageResize[ExampleData[{"TestImage", "Lena"}], {300}], 
    "Grayscale"], {200, 242}];
data = ImageData[%]; img = 
 Image[Show[{ListDensityPlot[Reverse@data, 
     DataRange -> {{-.7, .7}, {-.85, .85}}, 
     ColorFunction -> GrayLevel, AspectRatio -> Automatic], 
    Graphics[{FaceForm[], EdgeForm[Black], 
      triHedron /. {x_?NumericQ, y_?NumericQ, z_} :> {x, y}}]}, 
   FrameStyle -> Thick, FrameTicks -> None], ImageResolution -> 200, 
  ImageSize -> 200]

enter image description here

We take 3 reference (base) points in each of the 3 sectors and compute their perspective map with the function trihedronPerspectiveMap. We then find the necessary transformation function that converts the base points into the perspective points and apply the 3 transformation functions to our image.

basePoints = 
  Table[Map[RotationMatrix[\[Alpha]].# &, 
    CirclePoints[{.05, -.35}, .2, 3]], {\[Alpha], {0, 2 \[Pi]/3, 
     4 \[Pi]/3}}];
perspectivePoints = 
  Table[Map[Most@trihedronPerspectiveMap[#] &, p], {p, basePoints}];
transfoFunctions = 
  MapThread[
   Chop@Last@FindGeometricTransform[#1, #2] &, {perspectivePoints, 
    basePoints}];
ImagePerspectiveTransformation[ima, #, 300, DataRange -> Full, 
   PlotRange -> All, Resampling -> "Nearest"] & /@ {tf1, tf2, tf3}

enter image description here

After cropping out the necessary parts and rotating:

enter image description here

After assembling into a trihedron, the result looks like this:

enter image description here

This is the same method used with another image, showing the 3 trihedron faces in 2D and the assembled trihedron.

enter image description here enter image description here

enter image description here - Congratulations! This post is now featured in our Staff Pick column as distinguished by a badge on your profile of a Featured Contributor! Thank you, keep it coming, and consider contributing your work to the The Notebook Archive!

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract