2D Plot (CImg)

Oct 21, 2012 at 10:14pm
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 Oct 22, 2012 at 2:17am
Oct 21, 2012 at 10:22pm
Do you just want to make a single image? Do you want to show it on screen and/or save it to file?
Oct 21, 2012 at 10:30pm
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.
Oct 21, 2012 at 10:52pm
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.
Oct 21, 2012 at 11:14pm
Awesome. Is there a way to draw a curve connecting these data points, using this library?
Oct 22, 2012 at 12:22am
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?
Oct 22, 2012 at 1:59am
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 Oct 22, 2012 at 3:17am
Oct 22, 2012 at 3:16am
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 Oct 22, 2012 at 3:17am
Oct 24, 2012 at 11:07pm
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.