Perspective Transform a picture

Is it possible to transform the perspective of a picture?

When I take a picture of a, lets say, a business card. But I the resulting image is slanted and distorted because I took the picture from an angle. How would I transform this area to a normal “straight” image?

What I try to do is

  • showing the image on a canvas
  • show 4 corner points
  • show lines between the points, so I have some kind of guide rectangle
  • I can drag the corer points to the corners of my “business card”
  • I click a button to undistort the image.
  • The canvas updates with the new image.

I actually want to put this all in a class, where I store the original image, and the perspective points.

I did find some solution on THIS REALBASIC tutorial. But works the wrong way. This example transforms a straight image to a perspective projection. It is nice, but not the way I want.
The problem is that I can’t wrap my head around to change this RB project to fulfill my needs.

Has anybody have an idea?

A built in, cross platform solution is OpenGL for distorting the picture. Use it texture a quad which has its texture coordinates set to your 4 corner points.

There’s also platform specific declares that can transform pictures that way.

I’m sure there will be libraries for this kind of thing.
But let’s talk about sticky tape and string solution first.

Assume the rotation is correct and that you have a flat lower side.
Assume (at first) that the perspective is even… both sides slope in the same manner.

Your perspective image can be considered as a series of rows of pixels.

…xxxx…
…xxxxx…
…xxxxxx…

The trick is to stretch the short ones so that they are the same length as the long ones.
If your points mark the 4 corners, then you already know the relative sizes of the top row and the bottom row.

Lets say the bottom row is 600 pixels wide, and the top row is 400 pixels wide.
Take the top row of 1 pixel high by 400 wide, and paint it as an image 1 pixel high but 600 wide.
The next row down will probably need 401 pixels stretched into this 600 pixel wide area.
The amount of pixels you take and stretch will be proportional to the distance from the top versus the difference between top and bottom.
For example, if the image is 300 pixels high and the difference between top and bottom is 200 pixels, each row will vary in size by
200/300 = 0.66 pixels.
or in other words, the source slice will be 400 + (row -1)* 0.66 pixels wide.
First row 400 pixels, last but one row would be (in theory) 599.33 pixels

…such as that… :slight_smile:

I have been looking for examples to get me started with the OpenGL way of dealing with the distortion.
This is what I want to achieve:

@Jeff Tullin, as you might notice, the bottom side is not flat. I think I would have to find the rotation by comparing the lower two points, to make the bottom side flat. Then do the rest of shaping the image

Actually, I tried out what I said and it doesn’t work as I thought. Using a single quad the picture is transformed, but in ‘2 triangles’. You can see a discontinuity along the diagonal of the quad.

I’m sure there’s a way to transform it, just not what that’d be :slight_smile: Maybe texture the whole quad from 0 to 1 and move the vertices.

What about @Jeff Tullin 's solution. After I rotate the image by comparing the lower two points?

Besides… I have no experience with OpenGL whatsoever. And their website didn’t make me much smarter…

Yes, that will work well when there’s a single tilt. Use DrawPicture to draw rows stretched as Jeff described.

For an arbitrary perspective the math is more involved. I’m sure it’s doable in OpenGL by setting the right transform, I’m just not sure how to calculate it. (OpenGL isn’t that bad, you just have to use a framework.)

It might also be done by scanning the pictures RGBSurface and calculating each pixel. I think it’d be just interpolating vectors to find the source color. (Or maybe not :stuck_out_tongue: )

I’ve seen OSX functions for this very purpose but don’t remember where they are or how involved they are.

Is speed a big factor?

This is what my ABXVision library (pure Xojo/RB) does Edwin with the QuadrilateralTransform method. I used it to do my augmented reality demo. http://gorgeousapps.com/ABXVision/ABXVTransform.html#QuadrilateralTransform_1

Is there’s (free or not) available software that allows that (from a user point of view) ?

I bounced around Alains comment about QuadrilateralTransform and got it implemented in OpenGL before really looking at that original link and seeing that Alains project is already doing the QuadrilateralTransform.

This is how to modify Alains project to go the other way

In mPerspective.ABBackwardQuadrilateralTransformation swap the parameters, so srcRect is first.

dim matrix(2,2) as Double = MapQuadToQuad(srcRect, destinationQuadrilateral)
srcRect is the 4 corners of the unmolested picture. destination is the 4 corners of the arbitrary quad that is dragged around. Instead of mapping src to dest we flip them so the matrix maps dest to src (map arbitrary quad to the rectangular picture)

Also, in this same method we need to change the values on these lines. These are the output range of pixels and want the whole picture scanned.

dim startX as integer = 0 dim startY as integer = 0 dim stopX as integer = srcPic.Width - 1 dim stopY as integer = srcPic.Height - 1

You may also comment out these lines

[code]'Dim minXY as ABPoint
'Dim maxXY as ABPoint

'get bounding rectangle of the quadrilateral
'GetBoundingRectangle destinationQuadrilateral, minXY, maxXY[/code]

That’s it. Now the region the rect is placed around is drawn rectangularly below. It’s difficult to see the correspondence though so add this line which draws the image underneath the quad.

In Main.DrawPerspectivePoints add the marked line

[code]tmpBuffer.Graphics.ForeColor = &c000000
tmpBuffer.Graphics.FillRect 0,0,tmpBuffer.Width, tmpBuffer.Height

tmpBuffer.Graphics.DrawPicture(OrigPic, 0, 0) //<<<<<<<<<<<<<<< add

dim a as integer[/code]

Maybe that’s enough for you to wrap up in a class.

My OpenGL test redraws effortlessly but I only know how to do it with an OpenGLSurface control. An offscreen OpenGL Context is needed to do it in the background.
download distorted.zip (readme Note in Window)
Trying it on realworld pictures is fun but I wonder if the curved distortion in the output on the right comes from OpenGLs math or the optics of the camera.