How to shift a bitmap efficiently (Spectrogram Application) [modified]
-
If I understand correctly, you have at most a 1024*1024 color image, that is scrolling horizontally and gets up to 25 columns added per second, with a producer you control. That should not be any problem. Yes, I think I have used ScrollWindows in the dark ages of Win32, I wouldn't need it in .NET I would consider a Control (say a Panel) that would show a Bitmap and accept a columnar data update. Give me 24 hours, I'll experiment a bit in C#, and get back to you. What would you want for initial content? axes? grid lines? legend? BTW: I found a plotting graph here: High Speed, Feature Rich, and Easy-To-Use Graphs and Charts[^]. Haven't looked inside yet. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
Hi Luc, Sounds cool! looking forward to see what you come up with. For the spectrogram I wouldn't worry about axis for my app but if you want to write an article you might want to put a frequency and time axis and possible a colorbar which maps the spectrogram colors to an intensity for the frequency components. Let me know if I can contribute in any way...going to bed know Thx Tom
-
Hi I'm working on a Spectrum analyzer application and need some help to get the graphics updated in an optimal manner. Whenever my program receives a new block of audio data I do an FFT and I then update the oldest FFT data (a column) in my 2D-matrix which is then copied to a bitmap for display. So this spectrogram is shifted horizontally as data arrives in blocks. I see two options for achieving the shifting of the NxM bitmap whenever a new FFT output block arrives: 1. Copy columns [0:M-2] to [1:M-1] and then copy in the new FFT data...hope the notation makes sense. This requires a lot of memory copying and I hope to avoid that using GDI. 2. Replace the "oldest" column (the matrix works as a circular buffer in the time-direction) and let GDI ensure that when wrap-around happens GDI the remaining columns (the columns up to the column where the new FFT data resides) are copied to the bitmap. I once did a spectrum Analyzer in C++ project using OpenGL that supports this functionality. Can find the project anymore..I tried to look for it to find the name of the instruction used. Does GDI/GDI+ support this? Do any of you guys know how I get to do this efficiently? Thx Tom
modified on Sunday, September 19, 2010 8:00 PM
Hi Tom, 1. I studied the article High Speed, Feature Rich, and Easy-To-Use Graphs and Charts[^] a bit; it has a
Plotter
class which is a UserControl displaying a number of traces, each trace defined as a sequence of points. So there is no bitmap, no image, involved at all. It basically is vector graphics, which makes it high-performance. 2. I did some experiments with a bitmap-based plotter-like Panel, where one column of pixels would be updated at a time, using the circular buffer approach we discussed earlier. It is double-buffered to avoid flickering, and it works remarkably smooth, in fact it looks much better than the other article's demo, however I suspect that is due to the demo having fewer data points than the plotter is wide, causing the graph to jump all the time. At 512*512 my plotter happily handles 30 updates (frames) per second, using between 10 and 15% of a middle-of-the-road dual-core (I use both cores, as one executes the main thread, i.e. the painting, whereas the other calculates the new column in a BackgroundWorker). Yet I am convinced it takes too much CPU load for the net result. So I think I should look into other technologies, being OpenGL and DirectX. And I will, in time. 3. For the moment, I'm convinced if all that gets shown basically is a line graph, one should not resort to images at all. It will be much more lightweight when the graphs are drawn straight from a sequence of points, no matter what technology is used for painting images. ADDED Of course, when doing so, there is no problem whatsoever to make it scroll. /ADDED :)Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
modified on Monday, September 20, 2010 10:43 PM
-
Hi Tom, 1. I studied the article High Speed, Feature Rich, and Easy-To-Use Graphs and Charts[^] a bit; it has a
Plotter
class which is a UserControl displaying a number of traces, each trace defined as a sequence of points. So there is no bitmap, no image, involved at all. It basically is vector graphics, which makes it high-performance. 2. I did some experiments with a bitmap-based plotter-like Panel, where one column of pixels would be updated at a time, using the circular buffer approach we discussed earlier. It is double-buffered to avoid flickering, and it works remarkably smooth, in fact it looks much better than the other article's demo, however I suspect that is due to the demo having fewer data points than the plotter is wide, causing the graph to jump all the time. At 512*512 my plotter happily handles 30 updates (frames) per second, using between 10 and 15% of a middle-of-the-road dual-core (I use both cores, as one executes the main thread, i.e. the painting, whereas the other calculates the new column in a BackgroundWorker). Yet I am convinced it takes too much CPU load for the net result. So I think I should look into other technologies, being OpenGL and DirectX. And I will, in time. 3. For the moment, I'm convinced if all that gets shown basically is a line graph, one should not resort to images at all. It will be much more lightweight when the graphs are drawn straight from a sequence of points, no matter what technology is used for painting images. ADDED Of course, when doing so, there is no problem whatsoever to make it scroll. /ADDED :)Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
modified on Monday, September 20, 2010 10:43 PM
Hi Luc Glad to hear you are in good progress. I will take a look at the article you are refering to. I've advanced a bit tonight myself because the spectrogram is now up running. I'm currently using drawImage() which seems to do well. My laptop (Lenovo T61p) uses about 20% of it's resources but the app also does FFTs 50 times a second and computes the log-spectrum of these FFT outputs which costs a little too so I'm pretty impressed with the GDI performance. Even though I running double-buffering I still think my 768x1024 pixel spectrogram does some flickering..do you have a way to get rid of this? Also, I need an efficient way of mapping from FFT log-outputs to a colorrange...you know of an easy way of doing this? Sorry for all the question...I have a lot to learn with .NET (and programming in general besides Matlab) Addition: I guess the flickering comes because I'm manipulating the bitmap directly and not bitmapdata. If that is the case maybe drawimage is not such a good option???? Thx Thomas
modified on Tuesday, September 21, 2010 12:09 AM
-
Hi Luc Glad to hear you are in good progress. I will take a look at the article you are refering to. I've advanced a bit tonight myself because the spectrogram is now up running. I'm currently using drawImage() which seems to do well. My laptop (Lenovo T61p) uses about 20% of it's resources but the app also does FFTs 50 times a second and computes the log-spectrum of these FFT outputs which costs a little too so I'm pretty impressed with the GDI performance. Even though I running double-buffering I still think my 768x1024 pixel spectrogram does some flickering..do you have a way to get rid of this? Also, I need an efficient way of mapping from FFT log-outputs to a colorrange...you know of an easy way of doing this? Sorry for all the question...I have a lot to learn with .NET (and programming in general besides Matlab) Addition: I guess the flickering comes because I'm manipulating the bitmap directly and not bitmapdata. If that is the case maybe drawimage is not such a good option???? Thx Thomas
modified on Tuesday, September 21, 2010 12:09 AM
bimbambumbum wrote:
.do you have a way to get rid of this?
yes. do it correctly. and no. there is no perfect solution, switching from one image to the next always takes a finite amount of time, it is a matter of getting it minimal. you will not see flickering when the repaint time is small compared to the steady time. I suggest you show the relevant code.
bimbambumbum wrote:
mapping from FFT log-outputs to a colorrange
technically, have an array of colors, one for each integer in a limited range of possible values (if need be, divide by some constant so only say 256 colors remain). esthetically, whatever you like. You could create a hue-wig, i.e. all colors with same (probably max) saturation and brightness but different hue values. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
-
Hi Luc Glad to hear you are in good progress. I will take a look at the article you are refering to. I've advanced a bit tonight myself because the spectrogram is now up running. I'm currently using drawImage() which seems to do well. My laptop (Lenovo T61p) uses about 20% of it's resources but the app also does FFTs 50 times a second and computes the log-spectrum of these FFT outputs which costs a little too so I'm pretty impressed with the GDI performance. Even though I running double-buffering I still think my 768x1024 pixel spectrogram does some flickering..do you have a way to get rid of this? Also, I need an efficient way of mapping from FFT log-outputs to a colorrange...you know of an easy way of doing this? Sorry for all the question...I have a lot to learn with .NET (and programming in general besides Matlab) Addition: I guess the flickering comes because I'm manipulating the bitmap directly and not bitmapdata. If that is the case maybe drawimage is not such a good option???? Thx Thomas
modified on Tuesday, September 21, 2010 12:09 AM
bimbambumbum wrote:
I'm manipulating the bitmap directly
so do I. The alternative with LockBits prevents multi-threading, i.e. you can no longer have concurrent image updates and image repaints by different threads. However, SetPixel() is a very slow method. My test does two DrawLine() to update one column (one full-height background, one foreground). :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
-
bimbambumbum wrote:
.do you have a way to get rid of this?
yes. do it correctly. and no. there is no perfect solution, switching from one image to the next always takes a finite amount of time, it is a matter of getting it minimal. you will not see flickering when the repaint time is small compared to the steady time. I suggest you show the relevant code.
bimbambumbum wrote:
mapping from FFT log-outputs to a colorrange
technically, have an array of colors, one for each integer in a limited range of possible values (if need be, divide by some constant so only say 256 colors remain). esthetically, whatever you like. You could create a hue-wig, i.e. all colors with same (probably max) saturation and brightness but different hue values. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
Hi Luc Here is the core of my bitmap shifting. If you run it you can see that the shifting flickers a little and I would like to avoid that. Hope you have a simple way of getting rid of the flickering. Sorry for the messy code....Mind you I'm a newbie :)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using fftwlib;namespace GDI_evaluation
{
public partial class Form1 : Form
{
Timer timer = new Timer();
Graphics g;int XREF = 50; int YREF = 50; // Bitmap Bitmap myBitmap; Random rnd; int tIndx = 0; public Form1() { InitializeComponent(); rnd = new Random(); this.SetStyle( ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); myBitmap = new Bitmap(768, 512, PixelFormat.Format24bppRgb); timer.Tick += new EventHandler(timer\_tick); timer.Interval = 1000 / 25; // opdater grafik 50 /sekund!!! timer.Enabled = true; timer.Start(); } void timer\_tick(object sender, EventArgs e) { this.Refresh(); // Update GUI } private void Form1\_Paint(object sender, PaintEventArgs e) { } protected override void OnPaint(PaintEventArgs pe) { g = pe.Graphics; Rectangle block1 = new Rectangle(XREF + tIndx, YREF, myBitmap.Width - tIndx, myBitmap.Height); g.DrawImage(myBitmap, block1, 0, 0, myBitmap.Width - tIndx, myBitmap.Height, GraphicsUnit.Pixel); Rectangle block2 = new Rectangle(XREF, YREF, tIndx, myBitmap.Height); g.DrawImage(myBitmap, block2, myBitmap.Width - tIndx, 0, tIndx, myBitmap.Height, GraphicsUnit.Pixel); int tIndx2 = tIndx - 1; if (tIndx2 < 0) tIndx2 = myBitmap.Width - 1; // Insert new FFT data for (int ii = 0; ii < myBitmap.Height; ii++) myBitmap.SetPixel(myBitmap.Width - 1 - tIndx2, ii, Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255))); if (++tIndx == myBitmap.Width)
-
Hi Luc Here is the core of my bitmap shifting. If you run it you can see that the shifting flickers a little and I would like to avoid that. Hope you have a simple way of getting rid of the flickering. Sorry for the messy code....Mind you I'm a newbie :)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using fftwlib;namespace GDI_evaluation
{
public partial class Form1 : Form
{
Timer timer = new Timer();
Graphics g;int XREF = 50; int YREF = 50; // Bitmap Bitmap myBitmap; Random rnd; int tIndx = 0; public Form1() { InitializeComponent(); rnd = new Random(); this.SetStyle( ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); myBitmap = new Bitmap(768, 512, PixelFormat.Format24bppRgb); timer.Tick += new EventHandler(timer\_tick); timer.Interval = 1000 / 25; // opdater grafik 50 /sekund!!! timer.Enabled = true; timer.Start(); } void timer\_tick(object sender, EventArgs e) { this.Refresh(); // Update GUI } private void Form1\_Paint(object sender, PaintEventArgs e) { } protected override void OnPaint(PaintEventArgs pe) { g = pe.Graphics; Rectangle block1 = new Rectangle(XREF + tIndx, YREF, myBitmap.Width - tIndx, myBitmap.Height); g.DrawImage(myBitmap, block1, 0, 0, myBitmap.Width - tIndx, myBitmap.Height, GraphicsUnit.Pixel); Rectangle block2 = new Rectangle(XREF, YREF, tIndx, myBitmap.Height); g.DrawImage(myBitmap, block2, myBitmap.Width - tIndx, 0, tIndx, myBitmap.Height, GraphicsUnit.Pixel); int tIndx2 = tIndx - 1; if (tIndx2 < 0) tIndx2 = myBitmap.Width - 1; // Insert new FFT data for (int ii = 0; ii < myBitmap.Height; ii++) myBitmap.SetPixel(myBitmap.Width - 1 - tIndx2, ii, Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255))); if (++tIndx == myBitmap.Width)
Hi, I did not run your code, I have some comments just by looking at it: 1. you paint onto your Form, I always avoid that. I prefer adding a Panel to the Form, then painting to the Panel. Make the Panel the exact size of the plot, and make the Panel double-buffered, not the entire Form. ADDED and only repaint (i.e. Invalidate or Refresh) the panel. /ADDED 2. I see you modify your data inside OnPaint; one should not do that, as OnPaint may be called by the system whenever it feels a need (e.g. when an unrelated window gets shown, then disappears, uncovering your window). 3. you should use a local variable to store the value of myBitmap.Height, and not call any unnecessary property inside a loop; it will typically not be optimized away! 4.
bimbambumbum wrote:
opdater grafik 50 /sekund!!!
? :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
modified on Tuesday, September 21, 2010 5:58 PM
-
Hi, I did not run your code, I have some comments just by looking at it: 1. you paint onto your Form, I always avoid that. I prefer adding a Panel to the Form, then painting to the Panel. Make the Panel the exact size of the plot, and make the Panel double-buffered, not the entire Form. ADDED and only repaint (i.e. Invalidate or Refresh) the panel. /ADDED 2. I see you modify your data inside OnPaint; one should not do that, as OnPaint may be called by the system whenever it feels a need (e.g. when an unrelated window gets shown, then disappears, uncovering your window). 3. you should use a local variable to store the value of myBitmap.Height, and not call any unnecessary property inside a loop; it will typically not be optimized away! 4.
bimbambumbum wrote:
opdater grafik 50 /sekund!!!
? :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
modified on Tuesday, September 21, 2010 5:58 PM
Hi Luc Thanks for your comments. I get your points - I think :) Will try to follow your recipe when I have time tomorrow. If you have an example I can steal from that would be great... Thanks Thomas
-
bimbambumbum wrote:
I'm manipulating the bitmap directly
so do I. The alternative with LockBits prevents multi-threading, i.e. you can no longer have concurrent image updates and image repaints by different threads. However, SetPixel() is a very slow method. My test does two DrawLine() to update one column (one full-height background, one foreground). :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
One last thing... Will my spectrogram shift more smoothly if I do it the panel way? Cheers Tom
-
One last thing... Will my spectrogram shift more smoothly if I do it the panel way? Cheers Tom
My plotting Panel is perfectly smooth, however I can't compare, I never do it the Form way, as 1) it isn't really object-oriented 2) too many things probably are going on with Forms. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.