Draw rectangle over SwapChainPanel - c#

I'm receiving a live video from a DJI drone in a SwapChainPanel and I'm using template matching from OpenCV to find an image inside the frames of the video.
I want to draw a rectangle when the subgimage is detected, but there are white spaces in both sides of the frames and I don't know how to get the size of those or align the image to the left and top.
Here is the XAML:
<SwapChainPanel x:Name="swapChainPanel" Grid.Column="8" Grid.ColumnSpan="4" Grid.Row="2" Grid.RowSpan="11">
<Canvas>
<Rectangle x:Name="rectFiducial" Visibility="Collapsed" Stroke="Red"></Rectangle>
<Rectangle Stroke="Blue" Width="400" Height="400"></Rectangle>
</Canvas>
</SwapChainPanel>
Here is the code when I try to draw the rectangle with the results of the Template Matching:
SoftwareBitmap inputBitmap = SoftwareBitmap.CreateCopyFromBuffer(CryptographicBuffer.CreateFromByteArray(imageData),
BitmapPixelFormat.Rgba8,
imageWidth,
imageHeight);
if (inputBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8
|| inputBitmap.BitmapAlphaMode != BitmapAlphaMode.Premultiplied)
{
inputBitmap = SoftwareBitmap.Convert(inputBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}
int x = 0, y = 0, matchMethod = 0;
double matchResult = 0;
Stopwatch testWatch = new Stopwatch();
testWatch.Start();
await txtMatchMethod.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
int.TryParse(txtMatchMethod.Text, out matchMethod);
});
OpenCV.TemplateMatching(inputBitmap, template, matchMethod, out x, out y, out matchResult);
testWatch.Stop();
await txtMatchTime.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
txtMatchTime.Text = testWatch.ElapsedMilliseconds.ToString();
txtMatchResult.Text = matchResult.ToString();
double swapX = x * swapChainPanel.ActualWidth / imageWidth;
double swapY = y * swapChainPanel.ActualHeight / imageHeight;
double swapWidth = template.PixelWidth * swapChainPanel.ActualWidth / imageWidth;
double swapHeight = template.PixelHeight * swapChainPanel.ActualHeight / imageHeight;
rectFiducial.SetValue(Canvas.LeftProperty, swapX);
rectFiducial.SetValue(Canvas.TopProperty, swapY);
rectFiducial.Width = swapWidth;
rectFiducial.Height = swapHeight;
rectFiducial.Visibility = Visibility.Visible;
});
}
There is an event where I can get the bytes of the image and I tried to display it in an Image inside a Canvas, but the image didn't stretch to fit the canvas size.
Edit
I realized that I'm calculating the position based on the width and height of the SwapChainPanel, but I have to calculate it with the width and height of the image inside.
So I would need to get the data of the resized image that is inside the SwapChainPanel and the padding of the left side.

Well, I couldn't find a solution for the SwapChainPanel, but I found a solution with the image and the canvas.
I was trying to make something like this:
<Canvas x:Name="canvas" Grid.Column="8" Grid.ColumnSpan="4" Grid.Row="4" Grid.RowSpan="11">
<Image x:Name="imgDrone" HorizontalAlignment="Left"/>
<Rectangle x:Name="rectFiducial" Visibility="Collapsed" Stroke="Red"></Rectangle>
<Rectangle Stroke="Blue" Width="400" Height="400"></Rectangle>
</Canvas>
But I couldn't stretch the image, it was displayed with it's real size.
So instead of declaring the image inside the Canvas I just took it out and set it's column and row with the same values of the canvas.
Like this:
<Image x:Name="imgDrone" HorizontalAlignment="Left" Grid.Column="8" Grid.ColumnSpan="2" Grid.Row="2" Grid.RowSpan="11"/>
<Canvas x:Name="canvas" Grid.Column="8" Grid.ColumnSpan="4" Grid.Row="4" Grid.RowSpan="11">
<Rectangle x:Name="rectFiducial" Visibility="Collapsed" Stroke="Red"></Rectangle>
<Rectangle Stroke="Blue" Width="400" Height="400"></Rectangle>
</Canvas>

Related

Cover an image with black and reveal parts of the image using mouse

So I am creating a "Map Viewer" for a program I am working on. Basically I want to display a map and have it resize the image to fit in the grid. For this I am using Viewbox to hold the image and resize it. I was attempting to use this code to reveal the map but it does not center the circle on the mouse and it does not retain the revealed portions of the map (it ONLY reveals where the mouse is and not where it has been).
Here is the XAML:
<Viewbox x:Name="MapHolder" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="1" Grid.RowSpan="7" Margin="25,5,25,15">
<Image x:Name="SelectedMap" Source="/wizard_dungeon.jpg" Stretch="Uniform" MouseMove="SelectedMap_MouseMove" >
<Image.OpacityMask>
<VisualBrush Stretch="None" >
<VisualBrush.Visual>
<Ellipse Width="400" Height="400" StrokeThickness="1" Fill="Black"/>
</VisualBrush.Visual>
<VisualBrush.RelativeTransform>
<TransformGroup>
<TranslateTransform x:Name="OpacityFilterTransform" X="1" Y="1"/>
</TransformGroup>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Image.OpacityMask>
</Image>
</Viewbox>
And the code-behind:
private void SelectedMap_MouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(this);
var height = MapHolder.ActualHeight;
var width = MapHolder.ActualWidth;
// with the position values, interpolate a TranslateTransform for the opacity mask
var transX = position.X / width;
var transY = position.Y / height;
OpacityFilterTransform.X = transX - 0.5;
OpacityFilterTransform.Y = transY - 0.5;
}
I want there to basically be a Image under a Black screen and I can erase the black screen to reveal the image in the areas I have erased.

WPF Canvas Fill

I currently have a WPF windows with a Canvas is 600 x 400. Is it possible to scale or automatically zoom in so that the lines take up as much as the 600x600 as possible?
<Border>
<Canvas x:Name="cMap" Width="600" Height="400">
<Line X1="5" Y1="5" X2 ="10" Y2="10" StrokeThickness="2" Stroke="Black"/>
<Line X1="10" Y1="10" X2 ="15" Y2="25" StrokeThickness="2" Stroke="Black"/>
</Canvas>
</Border>
My intention will be to add lines programmatically via code instead of XAML.
Thanks.
Not sure what is your exact usecase, but you could probably benefit by using ViewBox:
<Border>
<Viewbox Stretch="Uniform">
<Canvas x:Name="cMap" Width="15" Height="25">
<Canvas.LayoutTransform>
<ScaleTransform />
</Canvas.LayoutTransform>
<Line X1="5" Y1="5" X2 ="10" Y2="10" StrokeThickness="2" Stroke="Black"/>
<Line X1="10" Y1="10" X2 ="15" Y2="25" StrokeThickness="2" Stroke="Black"/>
</Canvas>
</Viewbox>
</Border>
Hope this helps you!
To draw lines in code you shoud do something like this:
Line line = new Line();
Thickness thickness = new Thickness(101,-11,362,250);
line.Margin = thickness;
line.Visibility = System.Windows.Visibility.Visible;
line.StrokeThickness = 4;
line.Stroke = System.Windows.Media.Brushes.Black;
line.X1 = 10;
line.X2 = 40;
line.Y1 = 70;
line.Y2 = 70;
and don't forget to add:
myCanvas.Children.Add(line);
to put those line in some place
from: Drawing lines in code using C# and WPF
To resize your canvas please read this:
Canvas is the only panel element that has no inherent layout
characteristics. A Canvas has default Height and Width properties of
zero, unless it is the child of an element that automatically sizes
its child elements. Child elements of a Canvas are never resized, they
are just positioned at their designated coordinates. This provides
flexibility for situations in which inherent sizing constraints or
alignment are not needed or wanted. For cases in which you want child
content to be automatically resized and aligned, it is usually best to
use a Grid element.
So as a solution you would make it inside a GRID or using the following code:
public class CanvasAutoSize : Canvas
{
protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint)
{
base.MeasureOverride(constraint);
double width = base
.InternalChildren
.OfType<UIElement>()
.Max(i => i.DesiredSize.Width + (double)i.GetValue(Canvas.LeftProperty));
double height = base
.InternalChildren
.OfType<UIElement>()
.Max(i => i.DesiredSize.Height + (double)i.GetValue(Canvas.TopProperty));
return new Size(width, height);
}
}
at your XAML:
<local:CanvasAutoSize VerticalAlignment="Top" HorizontalAlignment="Left"></local:CanvasAutoSize>
from: WPF: How to make canvas auto-resize?

Click and drag box over image - WPF

I'm having trouble getting the selectionBox not to center. In the image below, I tried to draw a box around the word Final Bill (accented ms paint box) by clicking and dragging but the resulting selectionBox (dashed line in red) that outputs always starts in the center. The rectangle values that are calculated and saved in the mouseUp event are all correct, suggesting perhaps a XAML display issue?
I am very knew to WPF/XAML and front end stuff in general.
EDIT: By placing just the selectionBox in a <Canvas> tag I was able to get it almost working. It no longer centers but the start point appears to be twice as far from the left and top borders as the mouse is when clicked.
XAML
<DockPanel Width="Auto" Margin="225,65,5,5">
<Border x:Name="img_Border" ClipToBounds="True" Height="Auto" Width="Auto" Margin="0,0,0,0" VerticalAlignment="Top">
<Grid>
<ScrollViewer>
<Image x:Name="img_Box" ClipToBounds="True" MouseMove="img_Box_MouseMove" MouseWheel="img_Box_MouseWheel">
</Image>
</ScrollViewer>
<Rectangle x:Name="selectionBox" Visibility="Collapsed" Stroke="Red" StrokeThickness="3" StrokeDashArray="3,1">
</Rectangle>
</Grid>
</Border>
</DockPanel>
I can get the selectionBox to work correctly if I use <Canvas> tags. but that causes the image to not fit inside the <DockPanel>.
c#
public MainWindow()
{
InitializeComponent();
img_Box.MouseLeftButtonDown += img_Box_MouseLeftButtonDown;
img_Box.MouseLeftButtonUp += img_Box_MouseLeftButtonUp;
img_Box.MouseMove += img_Box_MouseMove;
Point mouseDownPos;
}
private void img_Box_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
img_Box.CaptureMouse();
var tt = (TranslateTransform)((TransformGroup)img_Box.RenderTransform).Children.First(tr => tr is TranslateTransform);
double BoxX = (e.GetPosition(img_Box).X / img_Box.ActualWidth) * 1;
double BoxY = (e.GetPosition(img_Box).Y / img_Box.ActualHeight) * 1;
double xValue = Math.Round((BoxX * img_Box.Source.Width), 0);
double yValue = Math.Round((BoxY * img_Box.Source.Height), 0);
StartDrag = new System.Windows.Point(xValue, yValue);
mouseDownPos.X = (int)xValue;
mouseDownPos.Y = (int)yValue;
Canvas.SetLeft(selectionBox, xValue);
Canvas.SetTop(selectionBox, yValue);
selectionBox.Width = 0;
selectionBox.Height = 0;
selectionBox.Visibility = Visibility.Visible;
}
private void img_Box_MouseMove(object sender, MouseEventArgs e)
{
double x = e.GetPosition(img_Box).X;
double y = e.GetPosition(img_Box).Y;
double BoxX = (x / img_Box.ActualWidth) * 1;
double BoxY = (y / img_Box.ActualHeight) * 1;
double xValue = Math.Round((BoxX * img_Box.Source.Width), 0);
double yValue = Math.Round((BoxY * img_Box.Source.Height), 0);
if (mouseDownPos.X < xValue)
{
Canvas.SetLeft(selectionBox, mouseDownPos.X);
selectionBox.Width = xValue - mouseDownPos.X;
}
else
{
Canvas.SetLeft(selectionBox, xValue);
selectionBox.Width = mouseDownPos.X - xValue;
}
if (mouseDownPos.Y < yValue)
{
Canvas.SetTop(selectionBox, mouseDownPos.Y);
selectionBox.Height = yValue - mouseDownPos.Y;
}
else
{
Canvas.SetTop(selectionBox, yValue);
selectionBox.Height = mouseDownPos.Y - yValue;
}
}
Not sure if I fully understand what exactly you are trying to accomplish here but if you just need a border around that text and not to allow the user to move it around with the mouse, set the image to be the background of the Grid and then partition the Grid so that one of the grid cells is directly above that text you want to place the border around. Place the border within that cell and If all the bill images are exactly the same size and have the text in exactly the same location it will always be directly over that text as long as you do not allow users to resize the image... If you want the scrolling just put the entire grid within the ScrollViewer control.
Something like this:
<Grid>
<Grid.Background>
<VisualBrush TileMode="None" >
<VisualBrush.Visual>
<Image Source="{Binding ImageSource}"/>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/> //Distance from the left edge of the image to the right edge of the border around the "Final Bill" text.
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="150"/> //Distance from the top of the image to the top of the desired location of the border around the "Final Bill" text.
<RowDefinition Height="50"/> //Desired height of the border around the "Final Bill" text.
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="1" BorderThickness="1"/>
</Grid>

How to rotate an image within a StackPanel or Grid in Silverlight

I have an image control sits inside a Grid control. I already have a button to enable zoom-in to this image. After zoom-in, the Horizontal/vertical scroll bars are displayed. And then I rotate the image contained grid, the image and the grid scroll bar are messed up. How should I incorporate both zoom-in and rotate for the image control? The following are the code that I am using in my project.
The image control zoom-in code I used (x is the image control):
if ((x as Image) != null) { x.Height = x.Height * 1.3; x.Width = x.Width * 1.3; }
The rotation code I used (x is the image control):
if ((x as Image) != null)
{
RotateTransform rotate = new RotateTransform(); rotate.Angle = rotateAngle;
rotate.CenterX = x.Width / 2;
rotate.CenterY = x.Height / 2;
x.RenderTransform = rotate;
};
The XAML is:
<ScrollViewer x:Name="scrollViewer" Height="480" Width="615"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ScrollViewer.Content>
<Grid x:Name="ImageGrid">
<StackPanel x:Name="ImageStackPanel">
<Image Source="..." VerticalAlignment="Center" Width="220" Height="170" ></Image>
</StackPanel>
</Grid>
</ScrollViewer.Content>
</ScrollViewer>
Does anybody have any existing code snippet that I can borrow to resolve this trick?
I think you need to use TransformGroup to use more than one transform at the time:
ScaleTransform myScaleTransform = new ScaleTransform();
myScaleTransform.ScaleY = 3;
RotateTransform myRotateTransform = new RotateTransform();
myRotateTransform.Angle = 45;
// Create a TransformGroup to contain the transforms
// and add the transforms to it.
TransformGroup myTransformGroup = new TransformGroup();
myTransformGroup.Children.Add(myScaleTransform);
myTransformGroup.Children.Add(myRotateTransform);
// Associate the transforms to the image.
x.RenderTransform = myTransformGroup;
This may work for your needs:
<Image x:Name="image" Source="myImageSource" Stretch="Uniform"
HorizontalAlignment="Center" VerticalAlignment="Center"
RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="Rotate"/>
<ScaleTransform x:Name="Scale" />
</TransformGroup>
</Image.RenderTransform>
</Image>
code behind:
Rotate.Angle = 45;
Scale = 0.25;
You may be missing the LayoutTransformer from the Silverlight Toolkit, and the AnimationMediator from one of the Toolkit developers.
With the LayoutTransformer you can set its content to anything, not just images, and apply any transformation with it, and as opposed to the usual RenderTransform, it will affect layout and actual sizes.
I have a similar scenario and I use it like this:
<Grid>
<fs:AnimationMediator x:Name="RotateMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding Angle, ElementName=RotateTransform, Mode=TwoWay}" />
<fs:AnimationMediator x:Name="ScaleXMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding ScaleX, ElementName=ScaleTransform, Mode=TwoWay}" />
<fs:AnimationMediator x:Name="ScaleYMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding ScaleY, ElementName=ScaleTransform, Mode=TwoWay}" />
<tkt:LayoutTransformer x:Name="LayoutTransformer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<tkt:LayoutTransformer.LayoutTransform>
<TransformGroup>
<RotateTransform x:Name="RotateTransform" />
<ScaleTransform x:Name="ScaleTransform" />
</TransformGroup>
</tkt:LayoutTransformer.LayoutTransform>
<Image x:Name="MyImage" Source="mysource.png" Width="600" Height="800" />
</tkt:LayoutTransformer>
</Grid>
Because of the lack of MultiBinding you'd probably additionally have to manually handle the input value (from Slider controls etc) changed events and then set the AnimationValues of RotateMediator etc accordingly.

Get the item index which is on focus inside a stackpanel

I have added 10 images in a stackpanel horizontally which is inside a scrollviewer. When user swipe the page the scrollviewer stops at certain position, if the scroll stops at &th image i want to get the name of the image. How to get that?
for (int i = 0; i <= 59; i++)
{
Uri uri = new Uri("http://d1mu9ule1cy7bp.cloudfront.net/2012/media/catalogues/47/pages/p_" + i + "/thump.jpg");
ImageSource img1 = new BitmapImage(uri);
Image rect = new Image { RenderTransform = new TranslateTransform() };
rect.Source = img1;
stack.Children.Add(rect);
}
XAML:
<ScrollViewer HorizontalContentAlignment="Left" HorizontalAlignment="Left" Name="scroll" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible">
<StackPanel Name="stack" Width="Auto" Orientation="Horizontal" HorizontalAlignment="Left" >
</StackPanel>
</ScrollViewer>
Assuming that your images are all the same size, you could calculate this by looking at the HorizontalOffset of the ScrollViewer.

Categories

Resources