如何在WPF的C#中对JPG图片绘制矩形

3
我有一个包含一系列jpg的文件夹,这些jpg是视频帧转换成的。我编写了一些代码来遍历它们并显示它们。
我正在尝试从C#中的JPG图像上绘制一个绿色框。高度、宽度、XC和YC在XML中,我只需访问每个框架的数据即可。我使用位图使其正常工作,但是为了在WPF中显示它,我必须首先将其转换为位图图像。问题是这需要太长时间。我希望视频以25 fps播放。因此,所有处理都需要在40毫秒内完成。目前,显示新图像需要花费0.01到0.3秒不等的时间。
以下是我目前拥有的代码-
public void UpdateImage(string imageName, int[] boxData)
{


    // imageName is the file path the image
    Bitmap oldImage = new Bitmap(imageName);


    // if objects are detected
    if (boxData.Length != 0)
    {
        // transforms x and y cords to align box better to light
        int newXC = boxData[0] - (boxData[2] / 2);
        int newYC = boxData[1] - (boxData[3] / 2);

        // ensures cords are not negative
        if (newXC < 0)
            newXC = 0;
        if (newYC < 0)
            newYC = 0;

        // uses the DrawRectangleBorder to draw rectangles 
        Bitmap newImage = DrawRectangleBorder(oldImage, new Rectangle(new System.Drawing.Point(newXC, newYC), new System.Drawing.Size(boxData[2], boxData[3])), Color.Green);

        // converts Bitmap to BitmapImage
        using (MemoryStream memory = new MemoryStream())
        {
            newImage.Save(memory, ImageFormat.Png);
            memory.Position = 0;
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = memory;
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            ImportImage.Source = bitmapImage;
        }
    }

    else
    {
        Bitmap newImage = oldImage;

        // converts Bitmap to BitmapImage
        using (MemoryStream memory = new MemoryStream())
        {
            newImage.Save(memory, ImageFormat.Png);
            memory.Position = 0;
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = memory;
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            ImportImage.Source = bitmapImage;
        }
    }
}

绘制矩形边框方法 -
private static Bitmap DrawRectangleBorder(Bitmap image, Rectangle rectangle, Color colour)
{

    // makes new blank Bitmap from the old ones width and height
    Bitmap newBitmap = new Bitmap(image.Width, image.Height);

    // opens up the blank bit
    using (Graphics graphics = Graphics.FromImage(newBitmap))
        graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
            new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);

    // what actually draws the rectangles
    for (Int32 x = rectangle.Location.X; x <= rectangle.Right && x < image.Width; x++)
        for (Int32 y = rectangle.Location.Y; y <= rectangle.Bottom && y < image.Height; y++)
            if (y == rectangle.Location.Y || y == rectangle.Bottom || x == rectangle.Location.X || x == rectangle.Right)
                newBitmap.SetPixel(x, y, colour);

    return newBitmap;
}

以下是其中一张图片的样子,它们的分辨率为640x480 - [1]: https://istack.dev59.com/ZiocC.webp 希望这能有所帮助!


你当前的策略需要多长时间? - Sneaky Polar Bear
你可以发布一张图片吗?你不能缓存此操作吗(即预先加载和编辑帧,然后在完成编辑后显示视频)? - Sneaky Polar Bear
放置矩形时是否进行任何图像处理,还是它们处于静态位置? - Sneaky Polar Bear
我也可以提供一个JPG的.zip文件链接。 - ChaseRatliff
你需要在实际图像中添加矩形(即编辑视频),还是只是想让矩形出现在视频上?比如,你可以在图像前面叠加一个矩形对象,并移动它,而不必费心处理图形或图像吗? - Sneaky Polar Bear
是的,那样可以,我只需要看一下。 - ChaseRatliff
2个回答

4
你可以通过使用这个XAML来简化你的代码。
<Canvas>
    <Image x:Name="ImportImage"/>
    <Path x:Name="ObjectBox"
          Width="{Binding ActualWidth, ElementName=ImportImage}"
          Height="{Binding ActualHeight, ElementName=ImportImage}"
          Stretch="None" Stroke="Green" StrokeThickness="1"/>
</Canvas>

有一个如下所示的UpdateImage方法:

public void UpdateImage(string imagePath, int[] boxData)
{
    ImportImage.Source = new BitmapImage(new Uri(imagePath));

    var boxes = new GeometryGroup();

    for (int i = 0; i <= boxData.Length - 4; i += 4)
    {
        int width = boxData[i + 2];
        int height = boxData[i + 3];
        int x = boxData[i] - width / 2;
        int y = boxData[i + 1] - height / 2;

        boxes.Children.Add(new RectangleGeometry(new Rect(x, y, width, height)));
    }

    ObjectBox.Data = boxes;
}

那太棒了!但是如果我需要同时绘制多个框或防止它绘制到图像外面怎么办? - ChaseRatliff
您可以使用多个RectangleGeometries创建一个GeometryGroup。为了避免绘制到外部,请查看Path元素上的其他属性。 - Clemens
GeometryGroup能否动态添加矩形? - ChaseRatliff
一个路径,其中包含一个GeometryGroup作为数据。在GeometryGroup的Children中设置了3个RectangleGeometries,在UpdateImage方法中进行设置。这些附加框是否在“boxData”参数中? - Clemens
请查看编辑 - 如果boxData参数中有多个框。 - Clemens
显示剩余4条评论

1
这是一个使用矩形覆盖在图像前面并控制其位置和大小的简单演示。抱歉,我以前从未在SO上分享过WPF,但我认为这段代码就是你需要的。我测试了时间,可以低于40毫秒(但我没有更新图像,只有矩形叠加层)。如果需要的话,我还找到了Fast Video Display WPF,但我没有实现或测试它。

".cs"

namespace ImageStreamer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
        Stopwatch stopWatch = new Stopwatch();
        long lastTime = 0;

        public MainWindow()
        {
            InitializeComponent();
            
            dispatcherTimer.Tick += dispatcherTimer_Tick;
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0,0,25);
            stopWatch.Start();

        }

        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            targetRect.Margin = new Thickness(targetRect.Margin.Left+1, targetRect.Margin.Top+1,0,0);
            targetRect.Width += 1;
            targetRect.Height += 1;
            Trace.WriteLine(lastTime - stopWatch.ElapsedMilliseconds);
            lastTime = stopWatch.ElapsedMilliseconds;
        }

        private void Grid_Initialized(object sender, EventArgs e)
        {

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            dispatcherTimer.Start();
        }
    }
}

"xaml"

<Window x:Class="ImageStreamer.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"
        xmlns:local="clr-namespace:ImageStreamer"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid Initialized="Grid_Initialized">
        <Image x:Name="imgBox" HorizontalAlignment="Left" Height="265" Margin="166,87,0,0" VerticalAlignment="Top" Width="512" Source="/ZiocC.jpg"/>
        <Rectangle x:Name="targetRect" HorizontalAlignment="Left" Height="49" Margin="323,228,0,0" Stroke="Red" VerticalAlignment="Top" Width="113" StrokeThickness="5"/>
        <Button Content="Button" HorizontalAlignment="Left" Margin="24,43,0,0" VerticalAlignment="Top" Click="Button_Click"/>
    </Grid>
</Window>

1
我认为慢的部分是加载图像。 - ChaseRatliff
你知道加载或绘制图像是否是限制因素吗:即如果在播放视频之前将所有图像加载到数组中,然后再播放它。 - Sneaky Polar Bear
当没有绘制任何内容时,它只需要大约0.015秒。但是当我绘制矩形时,时间会增加到0.3秒左右。 - ChaseRatliff
等待一下,这样你就可以可靠地以0.015的速度显示帧了吗? - Sneaky Polar Bear
不是所有的,最低为0.015。平均值大约为0.15。 - ChaseRatliff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接