WPF-Bild, Schwenken, Zoomen und Blättern mit den Ebenen auf einer Leinwand
Ich hoffe jemand kann mir helfen hier. Ich bin erstellen einer WPF-imaging-Anwendung, die nimmt live-Bilder von einer Kamera, so dass Anwender, um das Bild anzuzeigen, und anschließend markieren Sie die Regionen von Interesse (ROI) auf das Bild. Informationen über die ROIs (width, height, Position relativ zu einem Punkt auf dem Bild, etc) wird dann zurück zu der Kamera in der Tat sagen/training der Kamera-firmware, wo Sie zu suchen für Dinge wie barcodes, text, Füllstände, dreht an einer Schraube, usw. auf dem Bild). Die gewünschte Funktion ist die Möglichkeit zum schwenken und Zoomen auf das Bild und es ist ROIs, sowie scrollen, wenn das Bild gezoomt wird, die größer als der Anzeigebereich. Die StrokeThickness und-Größe der ROI ' s halten müssen, um dort original-Skala, aber die Breite und Höhe des shapes innerhalb einer ROI-Maßstab mit dem Bild (das ist wichtig zu erfassen genauen pixel zu übertragen, um die Kamera). Ich habe die meisten dieser aus-gearbeitet, mit Ausnahme von Blättern und ein paar andere Probleme. Meine zwei Problembereiche sind:
-
Wenn ich die Einführung eines ScrollViewer-ich bekomme keine scroll-Verhalten. Wie ich es verstehe, muss ich ein LayoutTransform, um die richtigen ScrollViewer-Element Verhalten. Allerdings, wenn ich tun, dass auch andere Bereiche beginnen zu brechen (z.B. ROIs nicht halten Ihre richtige position über dem Bild, oder der Mauszeiger beginnt zu kriechen, Weg von den ausgewählten Punkt auf dem Bild beim schwenken, oder die linke Ecke von meinem Bild springt auf die aktuelle position der Maus auf MouseDown .)
-
Kann ich nicht so Recht, die Skalierung der mein ROI ist die Art, wie ich Sie brauche. Ich habe diese arbeiten, aber es ist nicht ideal. Was habe ich nicht behalten, die genaue Strichstärke, und ich habe nicht geschaut in ignorieren Skala des textblocks. Hoffentlich wirst du sehen, was ich mache in den Codebeispielen.
Ich bin sicher, meine Frage hat etwas zu tun mit meinen Mangel an Verständnis von Transformationen und Ihre Beziehung zu den WPF-layout-system. Hoffentlich eine Darstellung der code, der das demonstriert, was ich bisher erreicht habe helfen (siehe unten).
FYI, wenn Funktionsindikatoren sind die Anregung, das wird nicht funktionieren in meinem Fall, weil ich könnte am Ende mit mehr funktionsindikatoren als unterstützt werden (Gerücht 144 funktionsindikatoren ist, wenn Dinge beginnen zu brechen nach unten).
First off, unten ist ein screenshot zeigt ein Bild mit der ROI ' s (text und eine Form). Das Rechteck, ellipse und text Folgen müssen Sie den Bereich auf dem Bild in der Skalierung und rotation, aber nicht Sie sollten nicht Maßstab in der Dicke oder Größe.
Hier ist der XAML-Code, der zeigt das Bild oben, zusammen mit einem Schieberegler für das Zoomen (Mausrad-zoom wird später kommen)
<Window x:Class="PanZoomStackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="MainWindow" Height="768" Width="1024">
<DockPanel>
<Slider x:Name="_ImageZoomSlider" DockPanel.Dock="Bottom"
Value="2"
HorizontalAlignment="Center" Margin="6,0,0,0"
Width="143" Minimum=".5" Maximum="20" SmallChange=".1"
LargeChange=".2" TickFrequency="2"
TickPlacement="BottomRight" Padding="0" Height="23"/>
<!-- This resides in a user control in my solution -->
<Grid x:Name="LayoutRoot">
<ScrollViewer Name="border" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Grid x:Name="_ImageDisplayGrid">
<Image x:Name="_DisplayImage" Margin="2" Stretch="None"
Source="Untitled.bmp"
RenderTransformOrigin ="0.5,0.5"
RenderOptions.BitmapScalingMode="NearestNeighbor"
MouseLeftButtonDown="ImageScrollArea_MouseLeftButtonDown"
MouseLeftButtonUp="ImageScrollArea_MouseLeftButtonUp"
MouseMove="ImageScrollArea_MouseMove">
<Image.LayoutTransform>
<TransformGroup>
<ScaleTransform />
<TranslateTransform />
</TransformGroup>
</Image.LayoutTransform>
</Image>
<AdornerDecorator> <!-- Using this Adorner Decorator for Move, Resize and Rotation and feedback adornernments -->
<Canvas x:Name="_ROICollectionCanvas"
Width="{Binding ElementName=_DisplayImage, Path=ActualWidth, Mode=OneWay}"
Height="{Binding ElementName=_DisplayImage, Path=ActualHeight, Mode=OneWay}"
Margin="{Binding ElementName=_DisplayImage, Path=Margin, Mode=OneWay}">
<!-- This is a user control in my solution -->
<Grid IsHitTestVisible="False" Canvas.Left="138" Canvas.Top="58" Height="25" Width="186">
<TextBlock Text="Rectangle ROI" HorizontalAlignment="Center" VerticalAlignment="Top"
Foreground="Orange" FontWeight="Bold" Margin="0,-15,0,0"/>
<Rectangle StrokeThickness="2" Stroke="Orange"/>
</Grid>
<!-- This is a user control in my solution -->
<Grid IsHitTestVisible="False" Canvas.Left="176" Canvas.Top="154" Height="65" Width="69">
<TextBlock Text="Ellipse ROI" HorizontalAlignment="Center" VerticalAlignment="Top"
Foreground="Orange" FontWeight="Bold" Margin="0,-15,0,0"/>
<Ellipse StrokeThickness="2" Stroke="Orange"/>
</Grid>
</Canvas>
</AdornerDecorator>
</Grid>
</ScrollViewer>
</Grid>
</DockPanel>
Hier ist das C# - Verwaltung von pan und zoom.
public partial class MainWindow : Window
{
private Point origin;
private Point start;
private Slider _slider;
public MainWindow()
{
this.InitializeComponent();
//Setup a transform group that we'll use to manage panning of the image area
TransformGroup group = new TransformGroup();
ScaleTransform st = new ScaleTransform();
group.Children.Add(st);
TranslateTransform tt = new TranslateTransform();
group.Children.Add(tt);
//Wire up the slider to the image for zooming
_slider = _ImageZoomSlider;
_slider.ValueChanged += _ImageZoomSlider_ValueChanged;
st.ScaleX = _slider.Value;
st.ScaleY = _slider.Value;
//_ImageScrollArea.RenderTransformOrigin = new Point(0.5, 0.5);
//_ImageScrollArea.LayoutTransform = group;
_DisplayImage.RenderTransformOrigin = new Point(0.5, 0.5);
_DisplayImage.RenderTransform = group;
_ROICollectionCanvas.RenderTransformOrigin = new Point(0.5, 0.5);
_ROICollectionCanvas.RenderTransform = group;
}
//Captures the mouse to prepare for panning the scrollable image area
private void ImageScrollArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_DisplayImage.ReleaseMouseCapture();
}
//Moves/Pans the scrollable image area assuming mouse is captured.
private void ImageScrollArea_MouseMove(object sender, MouseEventArgs e)
{
if (!_DisplayImage.IsMouseCaptured) return;
var tt = (TranslateTransform)((TransformGroup)_DisplayImage.RenderTransform).Children.First(tr => tr is TranslateTransform);
Vector v = start - e.GetPosition(border);
tt.X = origin.X - v.X;
tt.Y = origin.Y - v.Y;
}
//Cleanup for Move/Pan when mouse is released
private void ImageScrollArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_DisplayImage.CaptureMouse();
var tt = (TranslateTransform)((TransformGroup)_DisplayImage.RenderTransform).Children.First(tr => tr is TranslateTransform);
start = e.GetPosition(border);
origin = new Point(tt.X, tt.Y);
}
//Zoom according to the slider changes
private void _ImageZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//Panel panel = _ImageScrollArea;
Image panel = _DisplayImage;
//Set the scale coordinates on the ScaleTransform from the slider
ScaleTransform transform = (ScaleTransform)((TransformGroup)panel.RenderTransform).Children.First(tr => tr is ScaleTransform);
transform.ScaleX = _slider.Value;
transform.ScaleY = _slider.Value;
//Set the zoom (this will affect rotate too) origin to the center of the panel
panel.RenderTransformOrigin = new Point(0.5, 0.5);
foreach (UIElement child in _ROICollectionCanvas.Children)
{
//Assume all shapes are contained in a panel
Panel childPanel = child as Panel;
var x = childPanel.Children;
//Shape width and heigh should scale, but not StrokeThickness
foreach (var shape in childPanel.Children.OfType<Shape>())
{
if (shape.Tag == null)
{
//Hack: This is be a property on a usercontrol in my solution
shape.Tag = shape.StrokeThickness;
}
double orignalStrokeThickness = (double)shape.Tag;
//Attempt to keep the underlying shape border/stroke from thickening as well
double newThickness = shape.StrokeThickness - (orignalStrokeThickness / transform.ScaleX);
shape.StrokeThickness -= newThickness;
}
}
}
}
Sollte der code funktionieren in einem .NET 4.0 oder 4.5-Projekt und Lösung, vorausgesetzt, es ist kein cut/paste Fehler.
Irgendwelche Gedanken? Vorschläge sind willkommen.
- Die Anwendung
RenderTransforms
nur die Darstellung beeinflusst. Es hat keinen Einfluss aufLayout
, das ist, warum Sie sind nicht immer alle Scrollbalken. - Ok, ich habe eine komplette Umgestaltung Ihrer Probe in einem MVVM-Mode (das ist der RICHTIGE Weg zu tun, WPF). Ich poste es morgen. Ich werde jetzt gleich schlafen.
- HighCore, ja, ich erkenne den Grund bin ich nicht zu sehen, scrollbars, ist, weil ich bin nicht der Anwendung einer LayoutTransform. Wie ich bereits in meinem ursprünglichen post, wenn ich mir vorstellen, dass in der Mischung der Stücke, die arbeiten (Pan, Zoom, ROI-Positionierung) starten, um ungewöhnliches Verhalten. Ich habe versucht, dass sowohl meine Waage und Übersetzen Sie verwandelt sich in eine LayoutTransform, und ich habe versucht, trennen Sie die Waage in einer RenderTransform und das Übersetzen in eine LayoutTransform. Jedes mal, wenn Dinge beginnen zu brechen ziemlich schnell. Ich bin interessiert an einem code-Beispiel, wenn Sie Ideen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
In der Umsetzung von shapes ohne änderung Ihrer Strichstärke, können Sie
Path
Objekte mit transformierten Geometrien.Folgenden XAML-Code stellt eine Bild-und zwei Wege, die auf eine Leinwand. Das Bild wird skaliert und übersetzt von RenderTransform. Die Transformation ist auch für die
Transform
Eigenschaft der Geometrien der beiden Pfade.Ihrem Antrag nun ändern Sie einfach die
transform
Objekt in Reaktion auf die Eingabe-Ereignisse wie MouseMove oder dem Mausrad.Werden die Dinge ein wenig komplizierter, wenn es darum geht, auch transformieren Textblöcke oder andere element, das sollte nicht skaliert werden, sondern nur verschoben werden, um einen geeigneten Ort.
Können Sie erstellen eine spezielle Panel, welches in der Lage ist, die Anwendung dieser Art der Transformation, um seine untergeordneten Elemente. Ein solcher Bereich würde definieren Sie eine angefügte Eigenschaft steuert die position eines untergeordneten Elements, und würden Sie die Transformation an dieser Stelle der
RenderTransform
oderLayoutTransform
des Kindes.Dies kann Ihnen eine Idee geben, wie so ein Panel umgesetzt werden könnte:
Genutzt werden soll es im XAML-Code wie dieser:
Path
Vorschläge und war in der Lage, um zu sehen, das richtige Verhalten. Aber letztlich muss ich die Formen zum Teil ein benutzerdefiniertes Steuerelement mit Eigenschaften, Methoden, Ereignisse und andere Steuerelemente, plus funktionsindikatoren und Daumen für das ändern der Größe, verschieben und drehen. Nachdem ich packe meinePath
Elemente in einigenPanel
die Striche Skala wieder. Ich bin mir nicht sicher, ob die Implementierung einer benutzerdefiniertenPath
ist der richtige Weg zu gehen.Path
Dicke und Text nicht skalieren, plus ich muss in der Lage sein, sich zu bewegen, vergrößern, verkleinern, drehen, wählen Sie die einzelnenPath
über das Bild. Ich habe alle diese arbeiten heute. Es ist das scrollviewer-Element und Strichstärke entzieht sich mir. Ich werde versuchen, Ihre benutzerdefiniertenPanel
Idee, um zu sehen, ob etwas sticht und Bericht zurück.local:TransformPanel
Sie werden sehen, dass jeder Pfad der geometrie-Transformation ist an dieTransform
- Eigenschaft des Bereichs. Diese Transformation ist nicht auf das Panel selbst, sondern nur an ausgewählte Kinder.local:TransformPanel
bietet.