ijverig
ijverig

Reputation: 2945

Treating 1D array (pointer) as 3D matrix at run time in C++

I receive some image data as unsigned char *image = load_image(…);

This data is a 3D matrix: x (char),y (char) and channel (RGB) (char).

How can I access each element as image[x][y][channel] by overloading []? E.g. row 999, column 10000, green channel: image[999][10000][1]

Clarifications:

  1. I'd like to use C multi-dimensional array syntax: array[x][y][z], not array[x * height * channels + y * channels + z]

  2. I can access a 1D array as a 2D array:

    unsigned char (*imageMatrix)[height] = (unsigned char (*)[height])image
    imageMatrix[x][y] = 100
    
  3. I already asked how to do it in pure C. Here I want to know how it can be better achieved in C++.

Upvotes: 3

Views: 525

Answers (2)

An0num0us
An0num0us

Reputation: 961

You can create Image class with a function that will get what you want

class Image {
    int imgSize;
    char * img;

public:
    Image(): imgSize(0), img(nullptr) {}
    Image(char* image, int size): imgSize(size), img(image) {}

    char getPixel(int x, int y, int z) {
        if(x < imgSize && y < imgSize && z < imgSize) {
            return img[x * imgSize * imgSize + y * imgSize + z];
        } else {
            // Error
        }
    }

    char operator(int x, int y, int z) {
        // The same as getPixel
    }
}

getPixel is in my opinion the best approach because whenever you call it, you (and people working with your code) will exactly know what you do thanks to its name (even after 6 months you will immediately know you get a pixel). Overloading operator() is also a good approach, but I wouldn't use it were it to cause confusion or unwanted behavior. Both hide the internal structure of your class and don't require any proxy classes.

Overloading operator[] is an option, but to support chaining like [][][] you need proxy classes which will badly affect the performance of your code if not designed correctly.

You can read C++ FAQ to learn more.

Upvotes: 3

R Sahu
R Sahu

Reputation: 206557

How can I access each element as image[x][y][channel] by overloading []?

When you overload an array operator for the image class, it can only support the syntax image[x] in its interface.

In order to be able to support image[x][y], image[x] needs to return an object or a reference to an object that needs to support the array operator.

In order to be able to support image[x][y][z], image[x][y] needs to return an object or a reference to an object that needs to support the array operator.

Here's a sample program that demonstrates how it can be done.

Disclaimer Please note that this is not production ready code.

#include <iostream>
#include <cassert>

struct Image
{
   Image(unsigned int x,
         unsigned int y,
         unsigned int z) : x_(x), y_(y), z_(z), data(new unsigned char [x*y*z]) {}

   // Need appropriate copy constructor and destructor.

   // Helper classes to support the 3D array operator syntax.

   struct image_2
   {
      image_2(Image& im, unsigned i, unsigned j) : im_(im), i_(i), j_(j) {}
      unsigned char& operator[](unsigned k)
      {
         assert( k < im_.z_ );
         unsigned int index = i_*im_.y_*im_.z_ + j_*im_.z_ + k;
         return im_.data[index];
      }

      Image& im_;
      unsigned int i_;
      unsigned int j_;
   };

   struct image_1
   {
      image_1(Image& im, unsigned i) : im_(im), i_(i) {}

      image_2 operator[](unsigned int j)
      {
         assert( j < im_.y_ );
         return image_2(im_, i_, j);
      }

      Image& im_;
      unsigned int i_;
   };

   // The toplevel array operator
   image_1 operator[](unsigned i)
   {
      assert( i < x_ );
      return image_1(*this, i);
   }

   unsigned x_;
   unsigned y_;
   unsigned z_;
   unsigned char* data;
};

int main()
{
   unsigned int x = 5;
   unsigned int y = 5;
   unsigned int z = 3;
   Image im(5, 5, 3);

   for ( unsigned int i = 0; i < x; ++i )
   {
      for ( unsigned int j = 0; j < y; ++j )
      {
         for ( unsigned int k = 0; k < z; ++k )
         {
            // Set some arbitrary value
            im[i][j][k] = (i+1) + (j+1)*5 + (k+1)*5;

            // Get the value
            std::cout << (int)im[i][j][k] << " ";
         }
         std::cout << std::endl;
      }
   }
}

Output:

11 16 21
16 21 26
21 26 31
26 31 36
31 36 41
12 17 22
17 22 27
22 27 32
27 32 37
32 37 42
13 18 23
18 23 28
23 28 33
28 33 38
33 38 43
14 19 24
19 24 29
24 29 34
29 34 39
34 39 44
15 20 25
20 25 30
25 30 35
30 35 40
35 40 45

Upvotes: 0

Related Questions