11

I have scene composed of one arbitrary quadrilateral. I need to be able to transform that quadrilateral into a rect. Each quad is in 2d coordinates, so they have 4 vertex (x_i, y_i).

The transformation need to have an inverse because the idea is to go back to the original quad after manipulating the rectangle.

What would be the easiest way to perform this operation ? I've heard it's called a perspective transformation, but I've found some small clues that lead me to think this could be quite easy to do.

genpfault
  • 51,148
  • 11
  • 85
  • 139
Goles
  • 11,599
  • 22
  • 79
  • 140

3 Answers3

15

Do you know what the size of the desired rectangle is? You can map any convex quadrilateral to a rectangle with an invertible transformation with a perspective transformation if this is the case. All you have to do is get 4 corresponding points (between the quadrilateral and the rectangle), say, (X1,Y1), (X2,Y2), (X3,Y3), (X4,Y4) for the quadrilateral and correspondingly (x1,y1), (x2,y2), (x3,y3), (x4,y4) for the rectangle. Then plug it into the final equation in Borealid's link and you're set:

alt text

The solution of the above equation (where n = 4) will give you the elements (a,b,c,d,e,...,h) of the invertible perspective transformation matrix,

alt text

This will allow you to transform the points on the rectangle to the points on the quadrilateral. For the reverse transformation, just invert the transformation matrix.

Also note that once you obtain the vector [XW YW W]T of transformed coordinates, you need to normalize it such that W = 1. I.e., your final answer is [XW/W YW/W W/W]T which is equal to [X Y 1]T, the desired answer.

webelo
  • 1,646
  • 1
  • 14
  • 32
Jacob
  • 34,255
  • 14
  • 110
  • 165
  • 1
    Very nice answer, thanks. This is perfectly right. I don't know if I should put this as a new question but, here it goes: When I have obtained the perspective transformation matrix, is there a way to apply this matrix to an OpenGL context ? I have tried to do glPushMatrix() then glMultMatrixf(perspectiveTransformationInverse) then try to draw my quad, and then glPopMatrix(). This approach doesn't work though, what would you recommend me to do ? – Goles Jul 11 '10 at 00:00
  • Since I haven't used OpenGL, I can't really recommend anything. But to transform all the points you just have to multiply it with the transformation matrix and normalize them. – Jacob Jul 11 '10 at 14:50
  • I understand, but what if I would like to draw some points inside the transformed quad (rect). Is it possible to do that and then to apply the same transformation inverse to those points to bring them inside of the original non-rect quad ??? Or the transformation won't apply to those points ? Thanks – Goles Jul 11 '10 at 15:48
  • Can you do this with a 3D quadrilateral as well? – dwitvliet Jul 08 '15 at 19:23
  • 1
    [Here's this thing](https://github.com/9-volt/quadrilateral_translation) implemented in Java/Processing – bumbu Oct 10 '15 at 21:27
  • 2
    Thanks for the links! This works great for smoothly texturing quads. The page those images are from now goes to a 403, but there's a version up on the wayback machine: http://web.archive.org/web/20100327214143/http://alumni.media.mit.edu/~cwren/interpolator – Will Feb 23 '17 at 01:39
1

Not all quadrilaterals are rectangles. There is no invertible transformation from a quad to a rectangle for this reason; there exist more quads than rects, so you cannot produce an invertible mapping from quads to rects.

However, you can generate an invertible transformation for a particular quadrilateral. As you surmise, it's about rotating the perspective so the quadrilateral "appears" as a rectangle in your new coordinate space. See https://web.archive.org/web/20100801071311/http://alumni.media.mit.edu/~cwren/interpolator/ , which contains Matlab source code for this problem.

webelo
  • 1,646
  • 1
  • 14
  • 32
Borealid
  • 95,191
  • 9
  • 106
  • 122
0

This solution use JAI (Java Advance Image) API all the magic is in QuadToQuad method. here is the code sample.

try
     {
      BufferedImage img = UtilImageIO.loadImage(picName);
      ParameterBlock params = new ParameterBlock();
      params.addSource(img); //source is the input image
        int w = img.getWidth(); //Set to the original width of the image
        int h = img.getHeight(); //Set to the original height of image
        Point tl = new Point(x,y); //The new top left corner
        Point tr = new Point((x1,y1); //The new top right corner
        Point bl = new Point(x2,y2); //The new bottom left corner
        Point br = new Point(x3,y3); //The new bottom right corner
        PerspectiveTransform p = PerspectiveTransform.getQuadToQuad(0,0, 0, h, w, h, w, 0, tl.x, tl.y, bl.x, bl.y, br.x, br.y, tr.x, tr.y).createInverse();
        WarpPerspective wa = new WarpPerspective(p);
        params.add(wa);
        params.add(Interpolation.getInstance(Interpolation.INTERP_BICUBIC)); //Change the interpolation if you need more speed
        RenderedOp dest = JAI.create("warp", params); //dest is now the output
        File outputfile = new File(picName);
        ImageIO.write(dest, "jpg", outputfile); 

    }
    catch(Exception e){}

Hopefully it will help you. :)

Muhammad Suleman
  • 2,892
  • 2
  • 25
  • 33