WPF Image Panoramique, Zoom et de Défilement avec les couches de toile

J'espère que quelqu'un pourra m'aider ici. Je suis en train de construire un WPF application d'imagerie qui prend des images en direct à partir d'un appareil photo permettant aux utilisateurs d'afficher l'image, et par la suite mettre en évidence les régions d'intérêt (ROI) sur l'image. Informations sur les ROIs (largeur, hauteur, emplacement par rapport à un point de l'image, etc) est ensuite envoyé à l'appareil photo, en effet révélateur de la formation/le firmware de l'appareil où regarder pour des choses comme les codes-barres, du texte, de niveau des liquides, tourne sur une vis, etc. sur l'image). Une caractéristique recherchée est la capacité de panoramique et de zoom de l'image et du Roi, ainsi que de défilement lorsque l'image est agrandie, plus grande que la zone d'affichage. La StrokeThickness et FontSize du ROI de la nécessité de garder l'échelle d'origine, mais la largeur et la hauteur de l'formes au sein d'un retour sur l'investissement nécessaire à l'échelle de l'image (ce qui est essentiel pour capturer exacte emplacements de pixels à transmettre à l'appareil photo). J'ai plus de cela a fonctionné, à l'exception de défilement et de quelques autres questions. Mes deux domaines de préoccupation sont:

  1. Lorsque j'introduis un ScrollViewer je n'ai pas tout de défilement comportement. Si je comprends bien j'ai besoin d'introduire un LayoutTransform pour obtenir le ScrollViewer comportement. Cependant quand je fais que d'autres secteurs commencent à décomposer (par exemple, les ROIs ne pas tenir leur position correcte sur l'image, ou le pointeur de la souris commence à s'introduire à l'écart à partir du point sélectionné sur l'image lors d'un panoramique, ou le coin gauche de mon image rebondit à la position actuelle de la souris sur MouseDown .)

  2. Je ne peux pas tout à fait obtenir la mise à l'échelle de mon ROI est la façon dont j'en ai besoin. J'ai ce travail, mais c'est pas l'idéal. Ce que je ne conserve pas l'exacte épaisseur du trait, et je n'ai pas regardé en ignorant l'échelle sur la textblocks. J'espère que vous verrez ce que je fais dans les exemples de code.

Je suis sûr que ma question a quelque chose à voir avec mon manque de compréhension de Transformations et de leur relation à la WPF système de mise en page. Espérons-le, une interprétation du code qui expose ce que j'ai accompli jusqu'à présent de l'aide (voir ci-dessous).

Pour info, si Ornements sont de la suggestion, qui peut ne pas fonctionner dans mon cas parce que je pouvais plus d'ornements qui sont pris en charge (la rumeur 144 ornements est quand les choses commencent à briser vers le bas).

Tout d'abord, ci-dessous est une capture d'écran montrant une image avec de de ROI (texte et une forme). Le rectangle, l'ellipse et le texte doivent suivre la zone de l'image à l'échelle et la rotation, mais pas qu'ils ne devraient pas l'échelle dans l'épaisseur de la ou fontsize.

WPF Image Panoramique, Zoom et de Défilement avec les couches de toile

Voici le code XAML qui montre l'image ci-dessus, avec un Curseur de zoom (molette de zoom viendra plus tard)

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

Voici le C# qui gère de panoramique et de 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;
}
}
}
}

Le code doit travailler dans un .NET 4.0 ou 4.5 projet et de la solution, en supposant qu'aucun copier/coller des erreurs.

Toute pensée? Les Suggestions sont les bienvenues.

  • L'application de RenderTransforms seulement affecte le rendu. Il n'affecte pas Layout, c'est pourquoi vous ne réussissez pas à obtenir des barres de défilement.
  • Ok, j'ai fait un complet refactoriser de votre échantillon dans un MVVM de la mode (qui est la bonne manière de faire WPF). Je vais poster demain. Je vais dormir maintenant.
  • HighCore, oui je me rends compte de la raison pour laquelle je ne vois pas les barres de défilement est parce que je ne suis pas de l'application d'un LayoutTransform. Comme je l'ai dit dans mon premier post, quand je présenterai que dans le mélange des pièces de travail (Pan, Zoom, ROI de positionnement) commencent à un comportement étrange. J'ai essayé de mettre mon Échelle et translation se transforme en un LayoutTransform, et j'ai essayé de séparer l'Échelle dans un RenderTransform et de les Traduire dans un LayoutTransform. Chaque fois que les choses commencent à disparaître assez rapidement. Je suis intéressé par un exemple de code si vous avez des idées.
InformationsquelleAutor KyleLib | 2013-06-05