2d image

A forum dedicated to WPF version of LightningChart Ultimate.

Moderator: Arction_LasseP

juergen
Posts: 27
Joined: Tue Feb 04, 2014 8:11 am

2d image

Post by juergen » Sun Mar 02, 2014 9:36 am

Hi,
In our company we aquire data coming from a scientific ccd camera. For this it's needed to display the data as a spectrum and also as an image which should happen as fast as possible.
In principal it looks like your Heat-Map example, but it makes no sence to generate 3D data from a 2D image just to show it as a 2D image (and it's also to slow).
Is it possible to display an image in View2D maybe as a sprite? (of max.2048x2048 pixels)

Best regards,
Juergen

User avatar
ArctionPasi
Posts: 1365
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: 2d image

Post by ArctionPasi » Sun Mar 02, 2014 11:57 am

Hi Juergen,

To understand your goal right and to provide your with optimal solution, a couple of questions...
1. How many times per second do you want to update the image?
2. Has the image to be bound to specific X...Y axis ranges and to be zoomable and pannable, or can it stretch-fill the whole chart area?
3. Do you need to track the value of specific image pixel?
LightningChart Support Team, PT

juergen
Posts: 27
Joined: Tue Feb 04, 2014 8:11 am

Re: 2d image

Post by juergen » Sun Mar 02, 2014 9:14 pm

1. As fast as possible, with a minimum of 10 frames/second
2. two scenarios
- just the pixel-position on x- and y-axis
- each pixel in x-direction has it's own value on the axis. the table is calculated before (wavelength or wavenumbers)
In that case the axis is not linear, it's calculated by some values in polynomial order.
It has to be zoomable and pannable.
3. I need a cursor which is displayed on top of the image. It's enough to get the x and y position of the cursor or percentage values.

At the moment I use a WritableBitmap to display the images. It's really fast and zooming and panning is implemented.
Also the spectrum view is done with a WritableBitmap.
I want to change the complete display to LightningChart because of the different possibilites for now and the future.
The only view that is missing for us is the image view in 2D and I don't want to use different controls for different views (look and feel should be the same for the customer)

Maybe it's possible to use a View3D with a Rectangle3D and a fixed camera at the front, but I don't know if it's fast enough to change the image.

User avatar
ArctionPasi
Posts: 1365
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: 2d image

Post by ArctionPasi » Mon Mar 03, 2014 10:27 am

Please test this app, and let me know how it works for you. Remember to run without a debugger, otherwise it will run very slowly.

m_data[][] is the array you should use to input the values.

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading; 
using Arction.WPF.LightningChartUltimate;
using Arction.WPF.LightningChartUltimate.SeriesXY;
using Arction.WPF.LightningChartUltimate.Axes;
using Arction.WPF.LightningChartUltimate.Views.ViewXY; 

namespace WpfApplicationFastImageSetting
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer m_timer; 
        LightningChartUltimate m_chart;
        IntensityGridSeries m_heatmap;
        
        const int DataColumnCount = 2000;
        const int DataRowCount = 2000;
        double[][] m_data = null;
        const double XMin = 0;
        const double XMax = 100;
        const double YMin = 0;
        const double YMax = 100; 
        int m_iRound = 0; 

        public MainWindow()
        {
            InitializeComponent();
            CreateChart();

            m_timer = new DispatcherTimer();
            m_timer.Interval = TimeSpan.FromMilliseconds(1); 
            m_timer.Tick += new EventHandler(m_timer_Tick);
            m_timer.Start(); 


        }

    
        
        void CreateChart()
        {
            m_chart = new LightningChartUltimate();

            gridMain.Children.Add(m_chart);

            m_chart.BeginUpdate();

            IntensityGridSeries heatmap = new IntensityGridSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
            heatmap.PixelRendering = true;
            heatmap.Fill = IntensityFillStyle.Paletted;
            heatmap.WireframeType = SurfaceWireframeType.None;
            heatmap.ContourLineType = ContourLineType.None;
            heatmap.SetRangesXY(XMin, XMax, YMin, YMax);
            heatmap.TraceMouseCell = true;
            heatmap.MouseTraceCellChanged += new IntensitySeriesBase.MouseTraceCellChangedHandler(heatmap_MouseTraceCellChanged);
            m_chart.ViewXY.IntensityGridSeries.Add(heatmap);

            m_heatmap = heatmap; 


            m_chart.ViewXY.LegendBox.Layout = LegendBoxLayout.Vertical;
            m_chart.ViewXY.XAxes[0].SetRange(XMin, XMax);
            m_chart.ViewXY.YAxes[0].SetRange(YMin, YMax);


            m_chart.EndUpdate(); 
        }

        void heatmap_MouseTraceCellChanged(IntensitySeriesBase sender, int mouseX, int mouseY, int newCellColumn, int newCellRow, int nearestDataColumnIndex, int nearestDataRowIndex, IntensityPoint nearestCellCorner, float nearestCellCornerCoordX, float nearestCellCornerCoordY)
        {
            System.Diagnostics.Debug.WriteLine("Mouse trace ["+newCellColumn.ToString()+"]["+newCellRow.ToString()+"] = "+m_data[newCellColumn][newCellRow].ToString("0.0"));
        }

        void m_timer_Tick(object sender, EventArgs e)
        {
            if(m_data == null)
            {
                //Create a data array 
                m_data = new double[DataColumnCount][]; 
                for(int iCol  =0; iCol < DataColumnCount; iCol++)
                {
                    m_data[iCol] = new double[DataRowCount];  
                }
                m_heatmap.SetValuesData(m_data, IntensityGridValuesDataOrder.ColumnsRows);
                
            }

            //Set new values to data array
            for (int iCol = 0; iCol < DataColumnCount; iCol++)
            {
                for (int iRow = 0; iRow < DataRowCount; iRow++)
                {
                    m_data[iCol][iRow] = (double)m_iRound + 20.0 * Math.Sin((double)iCol * (double)iRow / 5000.0);
                } 
            }

            
            
            if (m_chart != null && m_heatmap != null)
            {
                m_chart.BeginUpdate();

                m_heatmap.InvalidateValuesDataOnly(); 

                m_chart.EndUpdate(); 
            }

            m_iRound++;
            if (m_iRound == 50)
                m_iRound = 0; 


        }
        
    }
}
By running with my PC, I could achieve about 7 FPS.

About 95% of CPU time is used in color encoding from double value to bitmap pixel color. There's 4M pixels to encode. GPU load is almost 0.

In this approach, LightningChart does all the encoding of the colors, and it is already made in multi-core approach. This saves CPU time from your application side.

The mouse tracking works, it solves column and cell coordinates, and extracts the value in that position.

If you are looking for a solution to show just a photo, and not to encode the values into a heatmap, I'll think of some other approach.
LightningChart Support Team, PT

juergen
Posts: 27
Joined: Tue Feb 04, 2014 8:11 am

Re: 2d image

Post by juergen » Mon Mar 03, 2014 2:33 pm

Thank you for your sample. It works but it's to slow - sorry ...

Is there a chance to show just a photo (bitmap)?
I have the functions to generate the bitmap (photo) in a few milliseconds with help of lookup-tables, so it looks like a heatmap with different filters, ...

Image

It would be enough to use a texture to display but with zooming and panning. I would buy the version with source code so if it's not to difficult I would try to implement a non linear axis.

User avatar
ArctionPasi
Posts: 1365
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: 2d image

Post by ArctionPasi » Tue Mar 04, 2014 12:07 pm

I noticed it's in fact the application side that is the bottleneck in my example code.

Instead of

Code: Select all

 //Set new values to data array
            for (int iCol = 0; iCol < DataColumnCount; iCol++)
            {
                for (int iRow = 0; iRow < DataRowCount; iRow++)
                {
                    m_data[iCol][iRow] = (double)m_iRound + 20.0 * Math.Sin((double)iCol * (double)iRow / 5000.0);
                } 
            }
use this code

Code: Select all

//Set new values to data array
            Parallel.For(0, DataColumnCount, ((iCol) =>
            {
                for (int iRow = 0; iRow < DataRowCount; iRow++)
                {
                    m_data[iCol][iRow] = (double)((iRow+iCol) * m_iRound) / 1000.0;
                }
                
            }));
The chart rendering delay is approximately 17 ms. Overall this application run 35 FPS in my computer, by using 2000x2000 heatmap.

Can you verify is using the external double[][] data array a suitable solution for your purpose?
LightningChart Support Team, PT

juergen
Posts: 27
Joined: Tue Feb 04, 2014 8:11 am

Re: 2d image

Post by juergen » Tue Mar 04, 2014 3:23 pm

I tried it and it works perfect.
I checked it also with lower resolutions (1600 x 200) and it's really fast (with my labtop > 65 frames/sec)

I'm sorry but I have one last thing:
I need a cursor (x and y) inside the ViewXY which I can set with a MouseClick event.
I found a class LineSeriesCursor but it's just in one direction.

User avatar
ArctionPasi
Posts: 1365
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland

Re: 2d image

Post by ArctionPasi » Tue Mar 04, 2014 3:56 pm

Great :D

For hair-cross cursor, use the "Intensity Grid mouse control" example from the DemoApp.
LightningChart Support Team, PT

juergen
Posts: 27
Joined: Tue Feb 04, 2014 8:11 am

Re: 2d image

Post by juergen » Tue Mar 04, 2014 9:25 pm

I got my cursor 8-)
The only problem was if "TraceMouseCell" and "MouseInteraction" is enabled the update of the image is stopped during mouse movement.

So I added "m_chart.MouseLeftButtonUp" event and disbaled "TraceMouseCell" and "MouseInteraction" in the IntensityGridSeries.

Code: Select all

void m_chart_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    Point p = Mouse.GetPosition(m_chart);
    if (m_heatmap.IsMouseOver((int)p.X, (int)p.Y))
    {
        double dX, dY;
        bool bXValueSolved = m_chart.ViewXY.XAxes[0].CoordToValue((Int32)p.X, out dX, true);
        bool bYValueSolved = m_chart.ViewXY.YAxes[0].CoordToValue((Int32)p.Y, out dY);

        if (bXValueSolved && bYValueSolved)
        {
            fplsHorizontal.Points[0].Y = dY;
            fplsHorizontal.Points[1].Y = dY;

            fplsVertical.Points[0].X = dX;
            fplsVertical.Points[1].X = dX;

            m_chart.BeginUpdate();

            fplsHorizontal.InvalidateData();
            fplsVertical.InvalidateData();

            m_chart.EndUpdate();
        }
    }
}
Now it works very good.

I had a look with the debugger and saw that there is the actual position and size value of the "IntensityGridSeries" but not declared as public. Sometimes it's usefull to get these values so maybe you can think about that.

At the end:
Thank you for the very good support !!!

juergen
Posts: 27
Joined: Tue Feb 04, 2014 8:11 am

Re: 2d image

Post by juergen » Tue Mar 04, 2014 10:25 pm

And I forgot:

CPU usage below 20% maximum !

Wow ...