2016-07-22 88 views
2

我正在研究WPF應用程序,該應用程序必須允許用戶定義任何四邊形形狀以充當相機框架的裁剪區域。這是通過將四個點連接在一起的視頻流疊加來實現的,從而可視化輸出圖像中應該使用哪個區域。拉伸四邊形以適合矩形

作爲第二步,這給我造成麻煩,我想顯示將選定的四邊形轉換爲矩形的結果。我設法使用opencv的warpPerspective函數來做到這一點,但事實證明這很慢(處理器很重)。

我相信它也可以用WPF(GPU加速)使用Viewport3D和3D轉換完成。我發現一個非常有用的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的角落。

在下面的圖片中,矩形和內部四邊形之間的區域將被剪裁,而四邊形的內容應該被拉伸以適應外部矩形。 visualization of my intent

是否有另一個轉變,我必須申請實現這一目標?也許有一個簡單的轉換,我可以應用到框架邊角座標來移動定義的點嗎?

+0

任何進展如何呢? – Nissim

回答

1

使用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! 
+0

雖然這似乎是一個很好的解決方案,但我現在無法輕鬆檢查它,因爲我不再在這部分項目上工作。謝謝。 – slawekwin