2

I need to read a greyscale png image, so that the pixel values(0-255) are stored in a 2D matrix of unsigned chars. Currently I am using CImg in C++, which succesfully reads the image but I couldn't figure it out how to get the pixel data out of the CImg container.

I can do this:

CImg<unsigned char> image("img1.png");
unsigned char* img1 = image.data();

but it gives me a char*, which is "the pointer to the first value of the image" according to the documentation, but I dont know what to do with it, or how to access the other values.

Any tips on this?

Thank you, David

Gold007
  • 23
  • 1
  • 4

2 Answers2

0

There really many possibilities to do so.

  1. You can use your current approach and just iterate through raw array of pixels:

    CImg<unsigned char> image("img1.png");
    unsigned char* img1 = image.data();
    for(int i=0; i < image.size(); i++) {
        logger_->info("*img1 value: {}", *(img1+i));
    }
    

According to the docs:

Size() returns width()*height()*depth()*spectrum(), i.e. the total number of values of type T in the pixel buffer of the image instance

So you can also use these dimensions directly:

  1. Like this:

    CImg<unsigned char> image("img1.png");
    unsigned char* img1 = image.data();
    for(int i=0; i < image.width()*image.height()*image.depth()*image.spectrum(); i++) {
        logger_->info("*img1 value: {}", *(img1+i));
    }
    

    which of course allows you to write nested loops iterating through each dimension separately.

  2. If you don't like to iterate through raw array, you can use iterator:

    CImg<unsigned char> image("img1.png");
    unsigned char* img1 = image.data();
    for (CImg<unsigned char>::iterator it = image.begin(); it < image.end(); ++it) {
        logger_->info("it value: {}", *it);
    }
    

Or if you would like to try something else, go through documentation, examples that I provided are not exhaustive.

pptaszni
  • 5,591
  • 5
  • 27
  • 43
  • Okay, now I get it. I tried both methods suggested but they are painfully slow with a ~10MP image. Originally I wanted to move it to an other array to make it faster, but since there is no way to directly copy the array, it is much slower iterating through and changing the vectors size on every iteration... So I will just stick to using the img(x,y) access method – Gold007 Jul 03 '18 at 07:49
  • Of course there is a way to copy the data to another array: `std::memcpy(dest, source, size);`. I just don't know how are you supposed to make it faster after the copy. – pptaszni Jul 03 '18 at 13:15
0

Not sure why you would want to copy all the data from within a CImg structure/class to another 2D array when you can access it exactly like that anyway. So, if you want the pixel at location [x,y], just use:

img(x,y)

Here is a complete program to dump an image - accessing individual pixels in the inner loop:

#include <iostream>
#include <cstdlib>

#define cimg_display 0
#include "CImg.h"

using namespace cimg_library;
using namespace std;

int main() {
    CImg<unsigned char> img("test.pgm");

    // Get width, height, number of channels
    int w=img.width();
    int h=img.height();
    int c=img.spectrum();
    cout << "Dimensions: " << w << "x" << h << " " << c << " channels" <<endl;

    // Dump all pixels
    for(int y=0;y<h;y++){
       for(int x=0;x<w;x++){
          cout << y << "," << x << " " << (int)img(x,y) << endl;
       }
    }
}

I used this test image - which is 5x3 PGM (Portable Grey Map) so you can easily see the pixel values but it is just the same with a PNG image:

P2
5 3
255
0 1 2 3 4
10 11 12 13 14
100 101 102 103 104

Here is the output, which you can see matches the image:

Dimensions: 5x3 1 channels
0,0 0
0,1 1
0,2 2
0,3 3
0,4 4
1,0 10
1,1 11
1,2 12
1,3 13
1,4 14
2,0 100
2,1 101
2,2 102
2,3 103
2,4 104
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432