Alex0xff
Alex0xff

Reputation: 66

How to properly align Canvas with the underlying Image?

My application detects a foreign object (blob, cluster etc) in the live webcam image and displays object's outline on top of the image. To achieve that I employ Image and Canvas elements as follows:

<Border x:Name="ViewportBorder" Grid.Column="0" Grid.Row="1" BorderThickness="3" Background="AliceBlue" BorderBrush="Red">
    <Grid>
        <Image x:Name="videoPlayer" Stretch="Uniform" MouseDown="videoPlayer_MouseDown"></Image>
        <Canvas x:Name="ObjectsCanvas"></Canvas>
    </Grid>
</Border>

Border element in the above XAML is used just to draw a thick red line border around the Grid containing videoPlayer and ObjectsCanvas. Stretch="Uniform" is set to preserve image aspect ratio while being able it to stretch when application window gets maximized.

Every time the new frame arrives from the camera videoPlayer.Source gets updated with frame's bitmap whereas blob detection method yields a list of coordinates used for drawing a Polyline. The Polyline object is then added to ObjectsCanvas to be shown on top of the actual image frame.

Here's a part which draws the blob and adds it to the ObjectsCanvas.Children:

private void DrawBlob(List<Point> corners)
{
    this.Dispatcher.Invoke(() =>
    {
        var myPolyline = new Polyline();
        myPolyline.Stroke = System.Windows.Media.Brushes.Yellow;
        myPolyline.StrokeThickness = 4;
        myPolyline.FillRule = FillRule.EvenOdd;
        myPolyline.Points = corners;

        Canvas.SetLeft(myPolyline, 0);
        Canvas.SetTop(myPolyline, 0);

        ObjectsCanvas.Children.Clear(); // remove any old blob polyline
        ObjectsCanvas.Children.Add(myPolyline); // add new polyline
    });
}

When running the application I observe imperfect overlap of the blob object (thick yellow polyline), it gets somewhat right-shifted as shown in the image below. Partial screenshot of the app when not maximized

Observed imperfection is not due to blob detection algorithm! I verified that by drawing the polylines of very same coordinates using old-fashion GDI methods on the actual bitmap.

It gets worse when I maximize the application window, an action causing videoPlayer to stretch: enter image description here

I tried setting HorizontalAlignment and VerticalAlignment properties of ObjectsCanvas to Stretch but that does not help. Is there any method to align canvas exactly with the actual displayed image region? I could get back to drawing the polylines using GDI, but I think it's a shame doing so in WPF...

Upvotes: 0

Views: 165

Answers (1)

Legkov Ivan
Legkov Ivan

Reputation: 447

I think I found a solution.

So, in order to stretch your canvas up to your image size you could wrap your canvas in ViewvBox control and bind your Canvas.Height and Canvas.Width to the image source's Height and Width like so:

<Grid>
    <Image x:Name="MyFrame" Stretch="Uniform" />
    <Viewbox Stretch="Uniform">
        <Canvas
            x:Name="MyCanvas"
            Width="{Binding ElementName=MyFrame, Path=Source.Width}"
            Height="{Binding ElementName=MyFrame, Path=Source.Height}">
            <Canvas.Background>
                <SolidColorBrush Opacity="0" Color="White" />
            </Canvas.Background>
        </Canvas>
    </Viewbox>
</Grid>

However you need to set your Canvas.Background (you can make it transparent like in the example above), or ViewvBox will hide all of the canvas children otherwise.

Here are some screenshots of it working with a yellow polyline:

TallExample

WideExample

One more thing to note here - the coordinates of your polyline should be relative to image resolution.

Hope that helps!

Upvotes: 0

Related Questions