2016-07-22 3 views
2

Я работаю над приложением WPF, которое должно позволить пользователю определять любую четырехстороннюю форму, которая будет служить средой для рамки камеры. Это достигается путем наложения видеопотока с четырьмя точками, связанными с линиями, для визуализации, какая область должна использоваться в выходном изображении.растягивать четырехугольник, чтобы соответствовать прямоугольнику

Как второй шаг, который вызывает у меня проблемы, я хотел бы показать результат преобразования выбранного четырехугольника в прямоугольник. Мне удалось это сделать, используя opencv's 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