WPF图像在画布上平移,缩放和滚动图层

我希望有人可以在这里帮助我。我正在构建一个WPF成像应用程序,该应用程序从照相机中获取实时图像,从而允许用户查看图像,并随后突出显示该图像上的关注区域(ROI)。然后将有关ROI的信息(宽度,高度,相对于图像上某个点的位置等)发送回相机,实际上是告诉/培训相机固件在哪里寻找诸如条形码,文本,液位,转弯等信息。在螺丝等上)。所需的功能是平移和缩放图像及其ROI的能力,以及在图像缩放到大于查看区域时滚动的能力。ROI的StrokeThickness和FontSize需要保持原始比例,但是ROI中形状的宽度和高度需要与图像成比例(这对于捕获确切的像素位置以传输到相机至关重要)。除了滚动和其他一些问题外,我已经完成了大部分工作。我关注的两个领域是:


当我介绍ScrollViewer时,我没有任何滚动行为。据我了解,我需要引入LayoutTransform以获得正确的ScrollViewer行为。但是,当我这样做时,其他区域就会开始崩溃(例如,ROI在图像上的位置不正确,或者在平移时鼠标指针或图像的左上角开始向远离图像上所选点的方向爬行。弹到MouseDown上的当前鼠标位置。)


我无法完全按照我需要的方式来扩展投资回报率。我有这个工作,但这不是理想的。我没有保留确切的笔触粗细,也没有考虑忽略文本块上的比例。希望您能在代码示例中看到我在做什么。


我确信我的问题与我对Transforms及其与WPF布局系统的关系缺乏了解有关。希望呈现出我到目前为止已完成的代码的翻译将有所帮助(请参见下文)。


仅供参考,如果有装饰者的建议,这可能对我不起作用,因为最终我得到的装饰者可能会多于所支持的(传闻144个装饰者是当事情开始崩溃的时候)。


首先,下面是屏幕截图,显示的图像具有ROI(文本和形状)。矩形,椭圆形和文本需要按比例缩放和旋转以跟随图像上的区域,但是它们不应该按宽度或字体大小按比例缩放。


该屏幕截图显示了具有ROI的样本图像


这是显示以上图像的XAML,以及用于缩放的Slider(稍后会进行鼠标轮缩放)


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

   

这是管理平移和缩放的C#。

该代码应该在.NET 4.0或4.5项目和解决方案中工作,假定没有剪切/粘贴错误。

有什么想法吗?欢迎提出建议。


白衣染霜花
浏览 2196回答 3
3回答

梦里花落0921

为了在不更改笔触粗细的情况下变换形状,可以使用Path具有变换后几何形状的对象。以下XAML在画布上放置一个Image和两个路径。图像通过RenderTransform缩放和转换。相同的变换也用于Transform两个路径的几何属性。<Canvas>&nbsp; &nbsp; <Image Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg">&nbsp; &nbsp; &nbsp; &nbsp; <Image.RenderTransform>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <TransformGroup x:Name="transform">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ScaleTransform ScaleX="0.5" ScaleY="0.5"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <TranslateTransform X="100" Y="50"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </TransformGroup>&nbsp; &nbsp; &nbsp; &nbsp; </Image.RenderTransform>&nbsp; &nbsp; </Image>&nbsp; &nbsp; <Path Stroke="Orange" StrokeThickness="2">&nbsp; &nbsp; &nbsp; &nbsp; <Path.Data>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RectangleGeometry Rect="50,100,100,50"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Transform="{Binding ElementName=transform}"/>&nbsp; &nbsp; &nbsp; &nbsp; </Path.Data>&nbsp; &nbsp; </Path>&nbsp; &nbsp; <Path Stroke="Orange" StrokeThickness="2">&nbsp; &nbsp; &nbsp; &nbsp; <Path.Data>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <EllipseGeometry Center="250,100" RadiusX="50" RadiusY="50"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Transform="{Binding ElementName=transform}"/>&nbsp; &nbsp; &nbsp; &nbsp; </Path.Data>&nbsp; &nbsp; </Path></Canvas>您的应用程序现在可以简单地更改transform对象以响应MouseMove或MouseWheel之类的输入事件。当还要转换TextBlocks或其他不应该缩放但只能移动到适当位置的元素时,事情会变得有些棘手。您可以创建一个专门的面板,该面板可以将这种转换应用于其子元素。这样的面板将定义一个控制子元素位置的附加属性,并将变换应用于该位置而不是子元素RenderTransform或LayoutTransform的位置。这可以使您了解如何实施这样的小组:public class TransformPanel : Panel{&nbsp; &nbsp; public static readonly DependencyProperty TransformProperty =&nbsp; &nbsp; &nbsp; &nbsp; DependencyProperty.Register(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Transform", typeof(Transform), typeof(TransformPanel),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new FrameworkPropertyMetadata(Transform.Identity,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FrameworkPropertyMetadataOptions.AffectsArrange));&nbsp; &nbsp; public static readonly DependencyProperty PositionProperty =&nbsp; &nbsp; &nbsp; &nbsp; DependencyProperty.RegisterAttached(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Position", typeof(Point?), typeof(TransformPanel),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new PropertyMetadata(PositionPropertyChanged));&nbsp; &nbsp; public Transform Transform&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; get { return (Transform)GetValue(TransformProperty); }&nbsp; &nbsp; &nbsp; &nbsp; set { SetValue(TransformProperty, value); }&nbsp; &nbsp; }&nbsp; &nbsp; public static Point? GetPosition(UIElement element)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return (Point?)element.GetValue(PositionProperty);&nbsp; &nbsp; }&nbsp; &nbsp; public static void SetPosition(UIElement element, Point? value)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; element.SetValue(PositionProperty, value);&nbsp; &nbsp; }&nbsp; &nbsp; protected override Size MeasureOverride(Size availableSize)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var infiniteSize = new Size(double.PositiveInfinity,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; double.PositiveInfinity);&nbsp; &nbsp; &nbsp; &nbsp; foreach (UIElement element in InternalChildren)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; element.Measure(infiniteSize);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return new Size();&nbsp; &nbsp; }&nbsp; &nbsp; protected override Size ArrangeOverride(Size finalSize)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (UIElement element in InternalChildren)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ArrangeElement(element, GetPosition(element));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return finalSize;&nbsp; &nbsp; }&nbsp; &nbsp; private void ArrangeElement(UIElement element, Point? position)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var arrangeRect = new Rect(element.DesiredSize);&nbsp; &nbsp; &nbsp; &nbsp; if (position.HasValue && Transform != null)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; arrangeRect.Location = Transform.Transform(position.Value);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; element.Arrange(arrangeRect);&nbsp; &nbsp; }&nbsp; &nbsp; private static void PositionPropertyChanged(&nbsp; &nbsp; &nbsp; &nbsp; DependencyObject obj, DependencyPropertyChangedEventArgs e)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var element = (UIElement)obj;&nbsp; &nbsp; &nbsp; &nbsp; var panel = VisualTreeHelper.GetParent(element) as TransformPanel;&nbsp; &nbsp; &nbsp; &nbsp; if (panel != null)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; panel.ArrangeElement(element, (Point?)e.NewValue);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}它将在XAML中像这样使用:<local:TransformPanel>&nbsp; &nbsp; <local:TransformPanel.Transform>&nbsp; &nbsp; &nbsp; &nbsp; <TransformGroup>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ScaleTransform ScaleX="0.5" ScaleY="0.5" x:Name="scale"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <TranslateTransform X="100"/>&nbsp; &nbsp; &nbsp; &nbsp; </TransformGroup>&nbsp; &nbsp; </local:TransformPanel.Transform>&nbsp; &nbsp; <Image Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RenderTransform="{Binding Transform, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TransformPanel}}"/>&nbsp; &nbsp; <Path Stroke="Orange" StrokeThickness="2">&nbsp; &nbsp; &nbsp; &nbsp; <Path.Data>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RectangleGeometry Rect="50,100,100,50"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Transform="{Binding Transform, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TransformPanel}}"/>&nbsp; &nbsp; &nbsp; &nbsp; </Path.Data>&nbsp; &nbsp; </Path>&nbsp; &nbsp; <Path Stroke="Orange" StrokeThickness="2">&nbsp; &nbsp; &nbsp; &nbsp; <Path.Data>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <EllipseGeometry Center="250,100" RadiusX="50" RadiusY="50"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Transform="{Binding Transform, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TransformPanel}}"/>&nbsp; &nbsp; &nbsp; &nbsp; </Path.Data>&nbsp; &nbsp; </Path>&nbsp; &nbsp; <TextBlock Text="Rectangle" local:TransformPanel.Position="50,150"/>&nbsp; &nbsp; <TextBlock Text="Ellipse" local:TransformPanel.Position="200,150"/></local:TransformPanel>

子衿沉夜

可以选择将哪些变换应用于哪些元素吗?有没有一种方法可以有选择地应用,或者忽略该变换。除了Path厚度和文本不缩放外,我需要所有图层都像一幅图像一样工作,另外我还需要能够Path在图像上移动,调整大小,旋转并选择每个图层。今天我已经完成所有这些工作。是我的滚动查看器和笔触粗细。我将尝试您的自定义Panel想法,看看是否有什么突出之处并报告。
打开App,查看更多内容
随时随地看视频慕课网APP