c# - 拉伸(stretch)四边形以适合矩形

标签 c# wpf transformation perspective viewport3d

我正在开发一个 WPF 应用程序,它必须允许用户定义任何四边形形状以用作相机框架的裁剪区域。这是通过用用线连接的四个点叠加视频流来实现的,以可视化输出图像中应使用哪个区域。

作为给我带来麻烦的第二步,我想展示将选定的四边形转换为矩形的结果。我已经设法使用 opencv 的 warpPerspective 做到了这一点功能,但这被证明很慢(处理器很重)。

我相信它也可以通过使用 Viewport3D 和 3D 转换的 WPF(GPU 加速)来完成。我找到了一个非常有用的 article这让我得到了以下代码

<Viewport3D x:Name="canvasViewPort">
    <Viewport3D.Camera>
        <OrthographicCamera Position="0.5 0.5 1" LookDirection="0 0 -1" UpDirection="0 1 0" Width="1" />
    </Viewport3D.Camera>
    <ModelVisual3D>
        <ModelVisual3D.Content>
            <DirectionalLight Color="White" Direction="0,0,-1"/>
        </ModelVisual3D.Content>
    </ModelVisual3D>
    <Viewport2DVisual3D>
        <Viewport2DVisual3D.Transform>
            <MatrixTransform3D x:Name="canvas3dTransform" />
        </Viewport2DVisual3D.Transform>
        <Viewport2DVisual3D.Geometry>
            <MeshGeometry3D Positions="0 0 0, 0 1 0, 1 0 0, 1 1 0" TextureCoordinates="0 1, 0 0, 1 1, 1 0" TriangleIndices="0 2 1, 2 3 1"/>
        </Viewport2DVisual3D.Geometry>
        <Viewport2DVisual3D.Material>
            <DiffuseMaterial Brush="White" Viewport2DVisual3D.IsVisualHostMaterial="True"/>
        </Viewport2DVisual3D.Material>
        <Canvas x:Name="mainCanvas" Margin="0" Width="{Binding ResolutionX}" Height="{Binding ResolutionY}">
            <Canvas.Background>
                <ImageBrush ImageSource="{Binding BackgroundImg}"  />
            </Canvas.Background>
        </Canvas>
    </Viewport2DVisual3D>
</Viewport3D>

protected void Transform()
{
    double targetWidth  = canvasViewPort.ActualWidth,  xScale = targetWidth  / ResolutionX,
           targetHeight = canvasViewPort.ActualHeight, yScale = targetHeight / ResolutionY;

    var points3d = new Point3D[4];
    if (EditorMode || Corners == null || Corners.Count < 4)
    {   //fit canvas in parent container without warping to allow Corners edition
        points3d[0] = Point2dTo3d(canvasViewPort, new Point(0, 0));
        points3d[1] = Point2dTo3d(canvasViewPort, new Point(0, targetHeight));
        points3d[2] = Point2dTo3d(canvasViewPort, new Point(targetWidth, 0));
        points3d[3] = Point2dTo3d(canvasViewPort, new Point(targetWidth, targetHeight));
    }
    else
    {   //get warped points, Corners indices order is to reflect convention used in the linked blog post
        points3d[0] = Point2dTo3d(canvasViewPort, new Point(Corners[0].X * xScale, Corners[0].Y * yScale));
        points3d[1] = Point2dTo3d(canvasViewPort, new Point(Corners[3].X * xScale, Corners[3].Y * yScale));
        points3d[2] = Point2dTo3d(canvasViewPort, new Point(Corners[1].X * xScale, Corners[1].Y * yScale));
        points3d[3] = Point2dTo3d(canvasViewPort, new Point(Corners[2].X * xScale, Corners[2].Y * yScale));
    }

    var A = new Matrix3D();
    A.M11 = points3d[2].X - points3d[0].X;
    A.M12 = points3d[2].Y - points3d[0].Y;
    A.M21 = points3d[1].X - points3d[0].X;
    A.M22 = points3d[1].Y - points3d[0].Y;
    A.OffsetX = points3d[0].X;
    A.OffsetY = points3d[0].Y;

    double den = A.M11 * A.M22 - A.M12 * A.M21;
    double a =  (A.M22 * points3d[3].X - A.M21 * points3d[3].Y +
                 A.M21 * A.OffsetY - A.M22 * A.OffsetX) / den;
    double b =  (A.M11 * points3d[3].Y - A.M12 * points3d[3].X +
                 A.M12 * A.OffsetX - A.M11 * A.OffsetY) / den;

    var B = new Matrix3D();
    B.M11 = a / (a + b - 1);
    B.M22 = b / (a + b - 1);
    B.M14 = B.M11 - 1;
    B.M24 = B.M22 - 1;

    canvas3dTransform.Matrix = B * A;
}

Point3D Point2dTo3d(Viewport3D vp, Point pt)
{
    var cam = (OrthographicCamera)canvasViewPort.Camera;
    double x = cam.Width / vp.ActualWidth  * (pt.X - vp.ActualWidth / 2)  + cam.Position.X;
    double y = cam.Width / vp.ActualWidth * (pt.Y - vp.ActualHeight / 2) + cam.Position.Y;

    return new Point3D(x, y, 0);
}

不幸的是,这与我需要的相反 - 它将框架角移动到 Corners 集合中定义的点,而我需要将定义的点放在 Canvas' 角落。

在下图中,矩形和内部四边形之间的区域将被剪裁,而四边形的内容应该被拉伸(stretch)以适应外部矩形。 visualization of my intent

我是否必须应用其他转换才能实现此目的?也许有一个简单的变换我可以应用于框架角坐标以将定义的点移动到那里?

最佳答案

使用 Accord.NET(作为 nuget 包提供),您可以使用以下内容:

Bitmap origin = // this is your quadrilateral bitmap
var corners = new List<IntPoint>(new IntPoint[]
{
    new IntPoint(topLeftPoint), new IntPoint(topRightPoint), new IntPoint(bottomRightPoint), new IntPoint(bottomLeftPoint)
});
var filter = new QuadrilateralTransformation(corners, rect.Width, rect.Height); // rect = your target rectangle size
Bitmap result = filter.Apply(origin); // voila!

关于c# - 拉伸(stretch)四边形以适合矩形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38524712/

相关文章:

c# - 绑定(bind)到多个窗口上的相同属性不起作用

c# - WPF 用户控件父级

javascript - Underscore.js:将对象数组转换为多级嵌套对象?

java - 需要一个基于 xml 映射的将 POJO 转换为 XML 和 JSON 的工具

c# - Windows 窗体中 TopMost 属性的真正烦人的错误

c# - 使用 Web 浏览器控件提供 jar 装脱机 Web 内容

c# - 有没有办法强制派生类实现抽象类或嵌套在基类中的接口(interface)?

c# - 在 telerik RadSpreadSheet 中,如何从单元格索引或选择中获取单元格名称?

java - 模型转换为平台特定 (NC) 代码

c# - (DataGridView + Binding)如何根据绑定(bind)的对象给线条上色?