Home > Net >  How to draw rectangle on JPG in C# for WPF
How to draw rectangle on JPG in C# for WPF

Time:09-21

[![enter image description here][1]][1]I have folder with a series of jpgs, which are frames of video that have been converted into jpgs. I made some code that iterates through them and displays them.

I am trying to draw a green box on a JPG image from C#. The Hight, Width, XC and YC are in a XML I just access the data there for each frame. I got it to work using a bitmap but then to display it in WPF I have to convert it into a bitmapImage first. The problem is it takes way to long. I want the video to be played at 25 fps. so all the processing needs to happen under 40 ms. Right now it takes anywhere between .01 and .3 seconds to displays the new image.

Here is the code I have so far-

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

The DrawRectangleBorder Method-

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

Here is what one of the pictures look like, they are 640 by 480 resolution- [1]: https://i.stack.imgur.com/ZiocC.jpg Any help would be great!

CodePudding user response:

You could simplify your code by using this 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>

with an UpdateImage method like this:

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

CodePudding user response:

here is a rough demo of using a rectangle overlaid in front of an image and controlling its position and size. Sorry, never shared wpf on SO before, but think this code is all you need. I tested the time and it can go well below 40 ms (but I am not updating the image at all, just the rectangle overlay. In case it is needed, I also found this Fast Video Display WPF but ya, did not implement it or test it out.

".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>
  • Related