【问题标题】:Draw on BitmapSource high hardware usage借鉴 BitmapSource 高硬件使用率
【发布时间】:2019-03-31 21:07:55
【问题描述】:

我想在 BitmapSource 上绘图。

我的帧源来自位图类型的网络摄像头。我将其转换为 BitmapSource,绘制一个矩形并设置为带有数据绑定的 Image 控件源。

    //convert Bitmap to BitmapSource:

    //WinForms -> WPF
    public BitmapSource BitmapToBitmapSource(System.Drawing.Bitmap bitmap)
    {
        var bitmapData = bitmap.LockBits(
            new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
            System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);

        var bitmapSource = BitmapSource.Create(
            bitmapData.Width, bitmapData.Height,
            bitmap.HorizontalResolution, bitmap.VerticalResolution,
            PixelFormats.Bgr24, null,
            bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);

        bitmap.UnlockBits(bitmapData);
        return bitmapSource;
    }

    //Drawing code:

    public static BitmapSource DrawRect(BitmapSource frame)
    {
        RenderTargetBitmap rtb = new RenderTargetBitmap(frame.PixelWidth, frame.PixelHeight, frame.DpiX, frame.DpiY, PixelFormats.Pbgra32);
        DrawingVisual dv = new DrawingVisual();

        using (DrawingContext dc = dv.RenderOpen())
        {
            dc.DrawImage(frame, new Rect(0, 0, frame.Width, frame.Height));
            //dc.DrawLine(new Pen(Brushes.White, 1), new Point(0, 0), new Point(frame.Width, frame.Height));
            dc.DrawRectangle(null, new Pen(Brushes.Red, 1), new Rect(50, 50, 100, 100));
        }

        rtb.Render(dv);
        rtb.Freeze();

        return rtb;
    }

我的硬件使用结果:

CPU:24% 显卡:5.4%

在英特尔酷睿 i7-4900MQ @ 2.8Ghz 上; 4核; 8线程 / NVIDIA Quadro K2100M

结果视频不流畅,有点慢。

有人知道我怎样才能最快吗?

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    System.Drawing.Bitmap 转换为System.Windows.Media.Imaging.BitmapSourceSystem.Windows.Media.ImageSource(基类) 很容易,但你必须小心。

    首先,您需要在每次使用完 System.Drawing.Bitmap 时对其进行处理。这样你就可以在你的记忆中腾出空间

    转换可以通过两种方式完成。使用 GDI 或内存流。 我个人更喜欢 GDI 方式。 GDI 也会让你使用 GPU 而不是 CPU。如果您要渲染视觉效果,这就是您想要的。

    内存流方式

     public static System.Windows.Media.ImageSource ToImageSource2(this Bitmap bitmap)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                bitmap.Save(stream, ImageFormat.Bmp);
                stream.Position = 0;
                System.Windows.Media.Imaging.BitmapImage result = new System.Windows.Media.Imaging.BitmapImage();
                result.BeginInit();
                result.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
                result.StreamSource = stream;
                result.EndInit();
                result.Freeze();
                return result;
            }
        }
    

    GDI 方式

    [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteObject([In] IntPtr hObject);
    public static System.Windows.Media.ImageSource ToImageSource(this Bitmap bitmap)
    {
        System.Windows.Media.ImageSource image;
        IntPtr handle = bitmap.GetHbitmap();
        try
        {
            image = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
            image.Freeze();
        }
        finally
        {
           DeleteObject(handle);
        }
    
    
        return image;
    }
    

    测试样本

            public static void Test()
        {
            System.Windows.Media.ImageSource img;
            System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(@"filepath");
    
            //From GDI
            img =  bmp.ToImageSource();
    
            //From MemoryStream
            img =  bmp.ToImageSource2();
    
            //Dispose from memory
            bmp.Dispose();
    
        }
    

    【讨论】:

      【解决方案2】:

      如果您只是在图像周围绘制边框,您可以简单地对 BitmapSource 不做任何事情,然后在您的 UI 中,用适当的 Border 包装 Image

      <Window x:Class="WpfApp1.MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              mc:Ignorable="d">
          <Grid>
              <Border BorderBrush="Red" BorderThickness="1">
                  <Image x:Name="Image1" />
              </Border>
          </Grid>
      </Window>
      

      然后看看它是否以某种方式改进了它,否则你需要检查你调用方法的方式和频率。

      【讨论】:

      • 我没有画边框。我想绘制更多矩形来从图像中选择区域。
      • 您可能想尝试使用自定义上下文的 WriteableBitmapEx。
      • 我不想使用第三方库
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-27
      相关资源
      最近更新 更多