This code can be used to manipulate an image into any space defined by four corners. Unlike an affine-transform (3 point transformation) which uses a parallelogram shaped space, this 4 point transformation can use absolutely any generic shape that can be defined by four points. Below is this code, split into the two files that I ended up using.
Code for DPointType.bas
Code:
Public Type DPOINT
X As Double
Y As Double
End Type
Code for FourPointTransform.cls
Code:
'Requires DPointType.bas
'The Points array holds 4 points. Below is an explanation of each point.
'Points(0) is the position that a rectangle's upper left point is mapped to.
'Points(1) is the position that a rectangle's upper right point is mapped to.
'Points(2) is the position that a rectangle's lower left point is mapped to.
'Points(3) is the position that a rectangle's lower right point is mapped to.
Friend Function X2(ByVal X As Double, ByVal Y As Double, ByVal ImgWidth As Double, ByVal ImgHeight As Double, _
ByRef Points() As DPOINT) As Double
Dim a As Double
Dim b As Double
Dim c As Double
Dim d As Double
b = (Points(1).X - Points(0).X) / (ImgWidth - 1)
d = Points(0).X
c = (Points(2).X - Points(0).X) / (ImgHeight - 1)
a = (Points(3).X - (ImgHeight - 1) * c - d - (ImgWidth - 1) * b) / ((ImgWidth - 1) * (ImgHeight - 1))
X2 = X * (Y * a + b) + Y * c + d
End Function
Friend Function Y2(ByVal X As Double, ByVal Y As Double, ByVal ImgWidth As Double, ByVal ImgHeight As Double, _
ByRef Points() As DPOINT) As Double
Dim a As Double
Dim b As Double
Dim c As Double
Dim d As Double
b = (Points(2).Y - Points(0).Y) / (ImgHeight - 1)
d = Points(0).Y
c = (Points(1).Y - Points(0).Y) / (ImgWidth - 1)
a = (Points(3).Y - (ImgHeight - 1) * b - (ImgWidth - 1) * c - d) / ((ImgHeight - 1) * (ImgWidth - 1))
Y2 = Y * (X * a + b) + X * c + d
End Function
Use the above class to transform an image in one picture box into a random shape in a second picture box, using the sample code below:
Code:
Private Sub TransformImage()
dim Xfrm as new FourPointTransform
dim Points(3) as DPOINT
Points(0).X=100 : Points(0).Y=20
Points(1).X=300 : Points(1).Y=45
Points(2).X=115 : Points(2).Y=200
Points(3).X=290 : Points(3).Y=230
for y = 0 to Picture1.Height-1
for x = 0 to Picture1.Width-1
u = Xfrm.X2(X, Y, Picture1.Width, Picture1.Height, Points)
v = Xfrm.Y2(X, Y, Picture1.Width, Picture1.Height, Points)
Picture2.pset(u,v),Picture1.point(x,y)
next x
next y
End Sub
Or use the above class to transform a portion of an image defined by any random 4-point shape in one picture box to fit correctly into a second picture box. This is the inverse of the above transformation. The code is very similar to the above, with just a few changes. See the code below
Code:
Private Sub InverseTransformImage()
dim Xfrm as new FourPointTransform
dim Points(3) as DPOINT
Points(0).X=100 : Points(0).Y=20
Points(1).X=300 : Points(1).Y=45
Points(2).X=115 : Points(2).Y=200
Points(3).X=290 : Points(3).Y=230
for y = 0 to Picture2.Height-1
for x = 0 to Picture2.Width-1
u = Xfrm.X2(X, Y, Picture2.Width, Picture2.Height, Points)
v = Xfrm.Y2(X, Y, Picture2.Width, Picture2.Height, Points)
Picture2.pset(x,y),Picture1.point(u,v)
next x
next y
End Sub
Re: Code for a four point transformation of an image
Nice job Ben. I think this is what MS Paint calls Skew.
A few recommendations:
Your TransformImage routine uses arbitrary points on an unknown picture so it is
hard to see what is happening. Write a little demo that allows the user to
select 4 points on the destination picture, then do the transformation.
I would change all the doubles to singles, since your code mixes & matches them.
Replace the Point & PSet calls with SetPixelV & GetPixel APIs for speed.
Change all the Widths/Heights to ScaleWidths/ScaleHeights.
Finally, you don't really need a class for this. Your 2 workhorse functions
(X2 & Y2) can go in a module along with the Point UDT & the APIs.
Re: Code for a four point transformation of an image
Originally Posted by VBClassicRocks
Nice job Ben. I think this is what MS Paint calls Skew.
A few recommendations:
Your TransformImage routine uses arbitrary points on an unknown picture so it is
hard to see what is happening. Write a little demo that allows the user to
select 4 points on the destination picture, then do the transformation.
I would change all the doubles to singles, since your code mixes & matches them.
Replace the Point & PSet calls with SetPixelV & GetPixel APIs for speed.
Change all the Widths/Heights to ScaleWidths/ScaleHeights.
Finally, you don't really need a class for this. Your 2 workhorse functions
(X2 & Y2) can go in a module along with the Point UDT & the APIs.
Let me address some of your points
MSPaint's skew is not like this. It is based on a transform defined by only 3 points called an affine transform. Strangest shape you can get out of that is a parallelogram. Opposing sides are always parallel with an affine transform. With my transform (which I believe is called a "projection transform") it uses 4 points so each of the 4 points of the output shape (or input shape if it's being used as an inverse transform) defines a corner of the quadrangle. This means that you can make the transform shape be ANY 4 sided shape, including a shape in which none of the sides are parallel to any of the other sides. That can NOT be done with an affine transform (such as MSPaint's "skew").
I am currently workng on a test program that allows you to set the 4 points of the transform shape by just clicking on the image. I'll send post the complete code for it here when it's finished.
I'm not sure where the SINGLEs are in my code. I've only DIMed DOUBLEs. If I use SINGLEs I'll get more artifacts for loss of precision.
The test program I am working on is indeed using SetPixel and GetPixel.
Also for compiling the EXE file I've set some of the special optimizations:
Remove Array Bounds Checks (because I don't have any array indexes that go over the bounds of an array, so I don't need error checking for this and such checks just slow down the program)
Remove Integer Overflow Checks (because I don't have any operations that would produce an overflow in any condition, so I don't need error checking on this, and it would just slow down my program)
Remove Floating Point Error Checks (I'm not expecting to get any INF or NAN or other overflow problems out of the operations in this program, so once again it is just an error check that can only serve to slow down my program)
Remove Safe Pentium FDIV Checks (completely unnececary as this only applies to Pentium computers, not Pentium Pro, Pentium II, Pentium III, Pentium IIII, or any newer Intel CPUs or any other brand of CPU, and most people now days are not using that ancient CPU that was just called "Pentium", so these Safe Pentium checks are useless for modern computers and could only serve to slow down my program)
By putting these functions in a class, it allows them to be easily implemented in other programs that use the same algorithm. Also since X2 or Y2 may be defined elsewhere as something else putting them in a class so that you need to call the class, guaranties that there are no conflicts with other possible definitions of X2 and Y2 (maybe in some context these would be variables like Dim X2 as Double, instead of the name of a function). This way you can use X2 as both a variable and a function even, like this:
Code:
Dim A As New FourPointTransform
Dim X2 As Double
X2 = A.X2(X,Y,Picture1.Width,Picture1.Height,Points)
If I put these functions into a module or in the Declarations part of a form, they may conflict with variables of the same name. Thus a class is needed to separate them from the "main code" of the program.
As for ScaleHeight and ScaleWidth, I already have set my pictureboxes to have a scalemode of pixels, and not to have any border (thus the upperleft pixel has coordinates x=0 y=0 and the scalewidth=width and scaleheight=height for my pictureboxes, and this simplifies a LOT of code later on so I have less to type so it physically is quicker to write my program). I don't need to explicitly refer to scalewidth and scaleheight in my pictureboxes.
Last edited by Ben321; Nov 25th, 2012 at 01:11 AM.
Re: Code for a four point transformation of an image
I've finally completed my program. I've attached a zip file with containing my program to this message. It has all the source code and the compiled EXE file. I've included a minor improvement in my code in the class (the implementation in the CLS file in the attached ZIP file now differs slightly from the above pasted code). It now uses the forth point as the lower left corner instead of the lower right corner. This is great because it allows lines to be drawn in the image between the points (not just the corner points) to better help one visualize where the pixels are being drawn to (for a forward transformation) or taken from (for an inverse transformation).
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Re: Code for a four point transformation of an image
Your program works fine! No for small picrutes...so your code must handle the minimum size that no interpolation needed.
I run it in VB5. You need a memory bitmap for better performance. Maybe I can do it.
Re: Code for a four point transformation of an image
An interesting project.
Code seems to be tight enough. However, apart from a speed problem, user interface definitely needs some enhancement. Below are a few small suggestions:
-- For a demo project in a code bank, a picture should be readily available there, rather than being availed through a Clipboard button.
-- Have diagonal lines on the picture for user to see that the original center point remains relatively in the center after each operation, as this is critical in the transformation.
-- Provide nodes at four corners for user to drag.
Re: Code for a four point transformation of an image
Some other examples here appear to take perspective into account (trapazoid transform makes parts of the image look farther away). My code simply stretches the image to fit the arbitrarily defined shape, but no perspective effect is imparted.
Re: Code for a four point transformation of an image
Hi Ben,
Yes, very nice work.
Also, off the top of my head, I'm not sure this is something that can be done with the GDI+, so nice addition.
It is important to realize though that this isn't a 3D transform. It's a true 2D transform.
It might be fun though to get into 3D transforms, with all the transformation matrices, linear algebra, and manipulation of quaternions that goes along with it. However, I'm concerned about whether or not VB6 is close enough to the CPU (and GPU) to get it done with the speed that's necessary. To a large degree, that's what the GDI+ and extending into the DirectX libraries is all about, with the DirectX connections to the GPU that just isn't possible with VB6.
Just for fun, I've attached a VB6 and DirectX project I was playing around with a while back. Basically, it's a Block sphere that can be rotated by dragging it around with the mouse. However, to make it run, you will need DirectX-8 for VB6 installed on your machine (a registered copy of dx8vb.dll from Microsoft). I'll leave you on your own to get that if you're interested.
Keep up the excellent coding,
Elroy
EDIT: Also, even though it won't be fast, I've never found anything wrong with coding this stuff up in straight-up VB6, just for the experience of it if nothing else. I've still got my pic rotation code around somewhere from before the GDI was available.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: Code for a four point transformation of an image
Hi Ben321,
Just wondering if you could re-post the link/attached files for your program - the link doesn't work anymore - I've been looking at EmguCV for a similar function, but the library is massive and a little bit buggy based on target platforms (x86, x64, any cpu) in vb.net. I'm also using Aforge.NET, however that doesn't have anything for 4 point transformations, but is pretty good at shape recognition (which I was wanting to use to identify a shapes points and then perform a transform). I'm really just after something pretty simple and straight forward for the transform.
Re: Code for a four point transformation of an image
Originally Posted by Ben321
I've finally completed my program. I've attached a zip file with containing my program to this message. It has all the source code and the compiled EXE file. I've included a minor improvement in my code in the class (the implementation in the CLS file in the attached ZIP file now differs slightly from the above pasted code). It now uses the forth point as the lower left corner instead of the lower right corner. This is great because it allows lines to be drawn in the image between the points (not just the corner points) to better help one visualize where the pixels are being drawn to (for a forward transformation) or taken from (for an inverse transformation).
Hi Ben I'm keen to test your 4 point image transform code. The zip attachment appears broken. Do you mind reposting it if you still have it? Cheers.