6

I need to transform bitmap images with their 4 corner points moved from one location to another.

Any code that can run on Windows, C#/VB.NET preferably, even help how to use scriptable programs like Paint.NET or Photoshop would be accepted. The Java Advanced Imaging API sounds hopeful.

I need it for a screenshot manipulation system, which allows you to get such effects:

alt text
(source: wholetomato.com)

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Robin Rodricks
  • 110,798
  • 141
  • 398
  • 607

4 Answers4

4

Check out the Perspective warping examples from ImageMagick. It is available for most mainstream platforms.

unwind
  • 391,730
  • 64
  • 469
  • 606
2

The keyword here is homography. Manolis Lourakis has written a GPL'ed homography implementation in C that is available here; however, this will not be able to be ported very easily because it relies on some external libraries such as LAPACK.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
2

Disclaimer: I work at Atalasoft

If you are willing to go commercial, DotImage Photo can do this with the QuadrilateralWarpCommand. Sample C# Code

// Load an image.
AtalaImage image = new AtalaImage("test-image.jpg");

// Prepare the warp positions.
Point bottomLeft = new Point(100, image.Height - 80);
Point topLeft = new Point(130, 45);
Point topRight = new Point(image.Width - 60, 140);
Point bottomRight = new Point(image.Width - 20, image.Height);

// Warp the image.
QuadrilateralWarpCommand cmd = new QuadrilateralWarpCommand(bottomLeft,
   topLeft, topRight, bottomRight, InterpolationMode.BiLinear, Color.White);
AtalaImage result = cmd.Apply(image).Image;

http://www.atalasoft.com/products/dotimage

Lou Franco
  • 87,846
  • 14
  • 132
  • 192
  • Is this an affine warp or a perspective warp? – Mr Fooz Mar 19 '09 at 12:53
  • I don't believe you can get this with an Affine warp (at least not 2d) We support several common affine transformations, and you can make your own 2D matrix. We don't support 3D transformations (which is what I think you would need to do this kind of transformation using a matrix). – Lou Franco Mar 19 '09 at 15:30
1

Easier than simulating a perspective distort using image manipulation, you could use OpenGL or DirectX (XNA) to actually perform the perspective display.

Render a simple quad with your image as the texture map. Setup your scene, render to a buffer, and you have your image.

Update It turns out that XNA is a ridiculous library (geared toward making games and nothing else, yawn). Managed DirectX requires a brain lobotomy. OpenGL is easy to use, but lacks image loading code. That leaves us with WPF:

alt text http://praeclarum.org/so/persp.png

The image could be improved by forcing WPF into anti-alias mode (why oh why Microsoft are you so short-sighted?), and by not using Aero glass which forces that 1 pixel black border on all screenshots (or by removing that 1 pixel border).

(Sorry for the length of this code, but WPF is a chatty API.)

public partial class Window1 : Window {
    const float ANGLE = 30;
    const float WIDTH = 8;
    public Window1() {
        InitializeComponent();

        var group = new Model3DGroup();
        group.Children.Add(Create3DImage(@"C:\Users\fak\Pictures\so2.png"));
        group.Children.Add(new AmbientLight(Colors.White));

        ModelVisual3D visual = new ModelVisual3D();
        visual.Content = group;
        viewport.Children.Add(visual);
    }

    private GeometryModel3D Create3DImage(string imgFilename) {
        var image = LoadImage(imgFilename);

        var mesh = new MeshGeometry3D();
        var height = (WIDTH * image.PixelHeight) / image.PixelWidth;
        var w2 = WIDTH / 2.0;
        var h2 = height / 2.0;
        mesh.Positions.Add(new Point3D(-w2, -h2, 0));
        mesh.Positions.Add(new Point3D(w2, -h2, 0));
        mesh.Positions.Add(new Point3D(w2, h2, 0));
        mesh.Positions.Add(new Point3D(-w2, h2, 0));
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(2);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(2);
        mesh.TriangleIndices.Add(3);
        mesh.TextureCoordinates.Add(new Point(0, 1)); // 0, 0
        mesh.TextureCoordinates.Add(new Point(1, 1));
        mesh.TextureCoordinates.Add(new Point(1, 0));
        mesh.TextureCoordinates.Add(new Point(0, 0));

        var mat = new DiffuseMaterial(new ImageBrush(image));
        mat.AmbientColor = Colors.White;

        var geometry = new GeometryModel3D();
        geometry.Geometry = mesh;
        geometry.Material = mat;
        geometry.BackMaterial = mat;

        geometry.Transform = new RotateTransform3D(
            new AxisAngleRotation3D(new Vector3D(0,1,0), ANGLE),
            new Point3D(0, 0, 0));

        return geometry;
    }

    public static BitmapSource LoadImage(string filename) {
        return BitmapDecoder.Create(new Uri(filename, UriKind.RelativeOrAbsolute),
            BitmapCreateOptions.None, BitmapCacheOption.Default).Frames[0];
    }
}

And the required XAML:

<Window x:Class="Persp.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Perspective Window" Height="480" Width="640">
<Grid>
    <Viewport3D x:Name="viewport">
        <Viewport3D.Resources>
        </Viewport3D.Resources>
        <Viewport3D.Camera>
            <PerspectiveCamera x:Name="cam"
                  FarPlaneDistance="100"
                  LookDirection="0,0,-1"
                  UpDirection="0,1,0"
                  NearPlaneDistance="1"
                  Position="0,0,10"
                  FieldOfView="60" />
        </Viewport3D.Camera>
    </Viewport3D>
</Grid>
</Window>
Frank Krueger
  • 69,552
  • 46
  • 163
  • 208