2D Plot (CImg)

I'm using Visual Studio C++ 2008, and I'm wondering what it would take to plot data points (the x-axis will increment time at a constant rate, the y-axis contains data points that I currently have stored in a double array).

I've looked around for various libraries to help, and I've been at this for some hours, but I can't seem to find anything that works for me. Advice?
Last edited on
Do you just want to make a single image? Do you want to show it on screen and/or save it to file?
A single image would be fine, and saving it to a file would be fine.

Optionally, we can do a dynamic display (the data points represent a heartbeat waveform), and in that case, I would want to show it on screen.

However, I'm getting low on time, and that may be too ambitious at this point.
Here's how I'd do something using the CImg library. It's a single header file, so shouldn't be too tough.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "CImg.h"
#include <iostream>
  using namespace cimg_library;

  int main() {
    // create image size 500x400x1,3 channels (RGB) default colour white
    CImg<unsigned char>  theImage(500,400,1,3,1);
    for (int i=10; i<50; i++)
      {
	for (int j = 10; j<70; j++)
	  {
              theImage(i,j,1) = 255; // different colour on a pixel - green to max
	  }
      }
    CImgDisplay main_disp(theImage); // display it
    theImage.save_bmp("output.bmp"); // write it
    std::cin.ignore();
  }


I compiled it with the command line
g++ 316.cpp -lX11 -lpthread

It displays on screen an image with black baqckground, and a block of pixel in green (the for loops above), and also writes a bmp file of the same. All you need to is change the code to draw the pixels you want in the colour you need.
Awesome. Is there a way to draw a curve connecting these data points, using this library?
Using your example, I did this:

1
2
3
4
5
6
7
8
CImg<unsigned char> hrtBtPts(lineNmb,350,1,3,1);
for (int i = 0; i < (lineNmb - 1); ++i)
  {
    hrtBtPts(i,wavarray[i],1) = 255;
  }
CImgDisplay main_disp(hrtBtPts);
hrtBtPts.save_bmp("hrtBtPts.bmp");
cin.ignore();


There are two issues with the output:

1) It should be a waveform, and this can only provide the data points. I've tried playing around with draw_graph (which I found in the documentation), but it's not giving me the results I want. Any advice here?
2) It's upside down because (0,0) starts in the top left corner, rather than the bottom left. Is there a way to start from another corner?
An update on issue #2:

I found the rotate function, but the data was still backwards, so I mapped in my data backwards. Like so:

1
2
3
4
5
6
7
8
9
10
11
CImg<unsigned char> hrtBtPts(lineNmb,350,1,3,1);
int j = 0;
for (int i = 0; i < (lineNmb - 1); ++i)
  {
    j = lineNmb - i;
    hrtBtPts(j,wavarray[i],1) = 255;
  }
hrtBtPts.rotate(180);
CImgDisplay main_disp(hrtBtPts);
hrtBtPts.save_bmp("hrtBtPts.bmp");
cin.ignore();


However, I still have not been able to solve the first issue. I'll update again if I make any progress, and any help is appreciated.
Last edited on
This will have to do. It creates an output that more clearly demonstrates what is going on with the data (everything below a data point is blue, everything above it is green), but it still does not represent a waveform.

1
2
3
4
5
6
7
8
9
10
11
12
CImg<unsigned char> hrtBtPts(lineNmb,350,1,3,1);
int j = 0;
for (int i = 0; i < (lineNmb - 1); ++i)
  {
    j = lineNmb - i;
    for (int k = 0; k <= wavarray[i]; ++k)
      hrtBtPts(j,k,1) = 255;
  }
hrtBtPts.rotate(180);
CImgDisplay main_disp(hrtBtPts);
hrtBtPts.save_bmp("hrtBtPts.bmp");
cin.ignore();
Last edited on
Final update: The deadline for this has already past, but I wanted to keep at it until I got it to do what I needed for the static display. I realized that a best-fit curve probably wasn't necessary for the data, so I just mapped a line from point to point. I also realized that the reason that, using some methods, nothing was appearing on my display was because there was a discrepancy between the range of the data being plotted on the y-axis and the height of the display.

This includes a backing grid and y-axis labels as well.

Anyway, here's how I've approached it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  CImg<unsigned char> hrtBtPts(lineNmb,200,1,3,0);
  const unsigned char black[] = { 0,0,0 }, white[] = { 255,255,255 }, green[] = { 0,255,0 };
  int x0 = lineNmb, y0 = (wavarray[0] - 408), x1, y1; // data points for drawing line
  for (int i = 0; i < lineNmb; ++i)
    {
      x1 = lineNmb - i; // reverse data direction
      y1 = wavarray[i] - 408; // -408 for alignment (baseline value seems to be 507-509)
      hrtBtPts.draw_line(x0,y0,x1,y1,green);
      x0 = x1, y0 = y1;
    }
  hrtBtPts.rotate(180); // rotate image 180 degrees
  hrtBtPts.draw_grid(10,10,0,0,false,false,white,0.1f,0x55555555,0x55555555)
    .draw_grid(50,50,0,0,false,false,white,0.2f,0x55555555,0x55555555)
    .draw_axes(0,0,2,-2,white,0.8f);
  CImgDisplay disp(hrtBtPts, "Heartbeat Monitor Static Display");
  hrtBtPts.save_bmp("hrtBtPts.bmp");
  cin.ignore();
Topic archived. No new replies allowed.