2016-07-22 28 views
2

Ich arbeite an einer WPF-Anwendung, die es einem Benutzer ermöglichen soll, eine beliebige viereckige Form zu definieren, die als Ausschnitt für den Kamerarahmen dient. Dies wird erreicht, indem der Videostrom mit vier mit Linien verbundenen Punkten überlagert wird, um zu visualisieren, welcher Bereich in dem Ausgabebild verwendet werden soll.dehnbares Viereck zum Anpassen des Rechtecks ​​

Als zweiten Schritt, der mir Probleme bereitet, möchte ich ein Ergebnis der Umwandlung ausgewählter Vierecke in ein Rechteck zeigen. Ich habe es geschafft, dies mit Opencv warpPerspective Funktion zu tun, aber das erwies sich als langsam (Prozessor schwer).

Ich glaube, es kann auch mit WPF (GPU beschleunigt) mit Viewport3D und 3D-Transformationen durchgeführt werden. Ich gefunden habe, ein sehr hilfreich article den mich

<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> 

Und

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); 
} 

Leider den folgenden Code geführt, tut dies das Gegenteil von dem, was ich brauche - es die Rahmenecken Punkte bewegt definiert in Corners Sammlung, während ich die definierten Punkte in die Canvas Ecken platzieren muss.

Auf einem Bild unten ist der Bereich zwischen Rechteck und innerem Viereck zu beschneiden, während der Inhalt des Vierecks gestreckt werden soll, um an das äußere Rechteck zu passen. visualization of my intent

Gibt es eine andere Umwandlung, die ich anwenden muss, um dies zu erreichen? Vielleicht gibt es eine einfache Transformation, die ich auf die Koordinaten der Rahmenecken anwenden kann, um dort definierte Punkte zu bewegen?

+0

jeder Fortschritt so weit? – Nissim

Antwort

1

Mit Accord.NET (erhältlich als nuget Paket) Sie Folgendes verwenden:

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

Obwohl dies eine gute Lösung zu sein scheint, kann ich es jetzt nicht einfach überprüfen, da ich an diesem Teil des Projekts nicht mehr arbeite. Vielen Dank. – slawekwin