Search code examples
c#win2d

Win2D Keystone Correction


I'm trying to use Win2D/C# to project an image using a overhead projector and I need to use a Win2D effect to do Keystone Correction (pre-warp the image) as the final step.

Basically I'm drawing a rectangle, then trying to use a Transform3DEffect to warp it before rendering. I can't figure out what Matrix transformation combination to use to get it to work. Doing a full camera projection seems like overkill since I only need warping in one direction (see image below). What transforms should I use?

enter image description here


Solution

  • Using an Image like following, can get you a similar effect.

    https://i.sstatic.net/5QnEm.png

    enter image description here

    I am unsure what results in the "bending".

    Code for creating the displacement map (with GDI+, because you can set pixels fast). The LockBitmap you can find here

    static void DrawDisplacement(int width, int height, LockBitmap lbmp)
        {
            for (int x = 0; x < width; x++)
                for (int y = 0; y < height; y++)
                {
                    int roff = (int)((((width >> 1) - x) / (float)(width >> 1)) * ((height - y) / (float)height) * 127);
                    int goff = 0;
    
                    lbmp.SetPixel(x, y, Color.FromArgb(127 - roff, 127 - goff, 0));
                }
        }
    

    Drawing in Win2D looks something like this, where displacementImage is the loaded file and offscreen, is a 'CanvasRenderTarget' on which I drew the grid.

    //Scaling for fitting the image to the content
    ICanvasImage scaledDisplacement = new Transform2DEffect
    {
        BorderMode = EffectBorderMode.Hard,
        Source = displacementImage,
        TransformMatrix = Matrix3x2.CreateScale((float) (sender.Size.Width / displacementImage.Bounds.Width), (float) (sender.Size.Height / displacementImage.Bounds.Height)),
        Sharpness = 1f,
        BufferPrecision = CanvasBufferPrecision.Precision32Float,
        InterpolationMode = CanvasImageInterpolation.HighQualityCubic,
    };
    
    //Blurring, for a better result
    ICanvasImage displacement = new GaussianBlurEffect
    {
        BorderMode = EffectBorderMode.Hard,
        Source = scaledDisplacement,
        BufferPrecision = CanvasBufferPrecision.Precision32Float,
        BlurAmount = 2,
        Optimization = EffectOptimization.Quality,
    };
    
    ICanvasImage graphicsEffect = new DisplacementMapEffect
    {
        Source = offscreen,
        Displacement = displacement,
        XChannelSelect = EffectChannelSelect.Red,
        YChannelSelect = EffectChannelSelect.Green,
        Amount = 800,//change for more or less displacement
        BufferPrecision = CanvasBufferPrecision.Precision32Float,
    };