2016-07-22 3 views
2

Je travaille sur une application WPF, qui doit permettre à un utilisateur de définir toute forme quadrilatérale pour servir de zone de recadrage pour la trame de la caméra. Ceci est accompli en superposant un flux vidéo avec quatre points connectés avec des lignes pour visualiser quelle zone doit être utilisée dans l'image de sortie.quadrilatérale extensible pour adapter rectangle

Dans un deuxième temps, ce qui me cause des problèmes, je voudrais montrer un résultat de la transformation quadrilatérale sélectionnée dans un rectangle. J'ai réussi à faire cela en utilisant la fonction warpPerspective d'opencv, mais cela s'est avéré lent (processeur lourd).

Je crois que cela peut également être fait avec WPF (GPU accéléré) en utilisant Viewport3D et les transformations 3D. Je l'ai trouvé très utile article qui m'a conduit au code suivant

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

Et

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

Malheureusement, cela fait le contraire de ce que je dois - il déplace les coins de cadre à des points définis dans Corners collection, alors que j'ai besoin de placer les points définis dans les coins Canvas.

Sur une image ci-dessous, dans la région entre le rectangle et quadrilatérale intérieur doit être clipsé, tandis que le contenu de la quadrilatérale sont censés être étiré pour adapter le rectangle extérieur. visualization of my intent

Y at-il une autre transformation que je dois appliquer pour y parvenir? Peut-être y a-t-il une simple transformation que je peux appliquer aux coordonnées des coins du cadre pour y déplacer des points définis?

+0

tout progrès jusqu'à présent? – Nissim

Répondre

1

En utilisant Accord.NET (disponible en paquet NuGet), vous pouvez utiliser les éléments suivants:

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

Bien que cela semble être une bonne solution, je ne peux pas facilement le vérifier maintenant, parce que je ne travaille plus sur cette partie du projet. Merci. – slawekwin