make 3d array from another data structure

I have old C/C++ code. Here is fragment

1
2
3
4
5
unsigned char * data;
unsigned long data_size;
...
data_size = x * y * channels;
data = (unsigned char *)malloc(data_size);


It's unconvinient to further processing.
I would like to do copy of 'data' as 3d array. How to do that ?

Thanks at advance
Are any of the dimensions of your 3D array known at compile time?
That code dynamically allocates a 1D array.
The array elements are of type unsigned char and there are x*y*channels elements.

The C++ equivalent is: std::vector<unsigned char> data( x * y * channels );

You say "3D array". Do you want to do something similar to:
1
2
3
4
5
6
7
for ( int row=0; row < y; ++row ) {
  for ( int col=0; col < x; ++col ) {
    for ( int cha=0; cha < channels; ++cha ) {
      // access element (row,col,cha) of data
    }
  }
}

You can map those to 1D: data[ row*x*channels + col*channels + cha ]

For convenience you can have a class that has data in vector and provides a element access function, i.e. wrap that 3D->1D behind interface.
Maybe you mean something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>

int main() {
    using std::cout;
    int layers = 3, rows = 4, cols = 5;

    // Allocate the 3D array.
    auto a = new char**[layers];              // layer pointers (1D)
    a[0] = new char*[layers * rows];          // row pointers (2D)
    for (int l = 1; l < layers; ++l)
        a[l] = a[l - 1] + rows;
    a[0][0] = new char[layers * rows * cols]; // data block (3D)
    for (int l = 0; l < layers; ++l)
        for (int r = 0; r < rows; ++r)
            if (l != 0 || r != 0)
                a[l][r] = a[l][r - 1] + cols;

    // Fill the data block with consecutive letters.
    char *p = &a[0][0][0];
    for (int i = 0; i < layers * rows * cols; ++i)
        *p++ = 'a' + i % 26;

    // Print it.
    for (int l = 0; l < layers; ++l) {
        for (int r = 0; r < rows; ++r) {
            for (int c = 0; c < cols; ++c)
                cout << a[l][r][c];
            cout << '\n';
        }
        cout << '\n';
    }

    // Delete it.
    delete a[0][0];
    delete a[0];
    delete a;
}


abcde
fghij
klmno
pqrst

uvwxy
zabcd
efghi
jklmn

opqrs
tuvwx
yzabc
defgh

Consider wrapping the array in a class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <vector>
#include <cassert>

template<class T>
class Array3D
{
  std::vector<T> data;
  size_t m_dim1, m_dim2, m_dim3;
public:
  Array3D(size_t dim1, size_t dim2, size_t dim3):
    m_dim1(dim1), m_dim2(dim2), m_dim3(dim3)
  {
    assert(m_dim1 > 1 && m_dim2 > 1 && m_dim3 > 1);
    data.resize(m_dim1 * m_dim2 * m_dim3);
  }
  T& operator()(size_t d1, size_t d2, size_t d3)
  {
    assert(d1 < m_dim1 && d2 < m_dim2 && d3 < m_dim3);
    return data[d1*d2*m_dim3 + d2*m_dim3 + d3];
  }
  const T& operator()(size_t d1, size_t d2, size_t d3) const
  {
    assert(d1 < m_dim1 && d2 < m_dim2 && d3 < m_dim3);
    return data[d1*d2*m_dim3 + d2*m_dim3 + d3];
  }
  size_t dim1() {return m_dim1;}
  size_t dim2() {return m_dim2;}
  size_t dim3() {return m_dim3;}
};

int main()
{
  Array3D<int> array3d(2,2,2);

  for ( size_t d1=0; d1 < 2; ++d1 )
  {
    for ( size_t d2=0; d2 < 2; ++d2 )
    {
      for ( size_t d3=0; d3 < 2; ++d3 )
      {
        array3d(d1,d2,d3) = 3;
      }
    }
  }

  array3d(1,1,1) = -99;

  for ( size_t d1=0; d1 < 2; ++d1 )
  {
    for ( size_t d2=0; d2 < 2; ++d2 )
    {
      for ( size_t d3=0; d3 < 2; ++d3 )
        std::cout << "array3d["<< d1 << "][" << d2 << "][" << d3 << "] = " << array3d(d1, d2, d3) << "\n";
    }
  }
}
It's a little wordy, but I believe it compiles down to efficient code. The idea is to create class ChannelData that contains the 1D array.
 
ChannelData::operator[]
returns a helper class called Idx1.

Idx1::operator[] returns a reference to helper class called Idx2.

Idx2::operator[] returns a reference to the appropriate ChannelData element.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <vector>
using std::cout;
using std::vector;

class ChannelData {
public:
    ChannelData(unsigned x_, unsigned y_, unsigned c_) :
	x(x_), y(y_), channels(c_),
	arr(x_ * y_ * c_) {}

private:
    unsigned x, y, channels;
    vector<unsigned char> arr;

public:
    struct Idx2 {		// returned by ChannelData[x][y]
	Idx2(ChannelData &cd_) : cd(cd_) {}
	unsigned char & operator[](unsigned c_) {
	    return cd.arr[c_ + cd.channels * (y + cd.y * x)];
	};

	ChannelData &cd;
	unsigned x, y;
    };

    struct Idx1 : public Idx2 {	// returned by ChannelData[x]
	Idx1(ChannelData &cd_) : Idx2(cd_) {}

	Idx2& operator[](unsigned y_) {
	    y = y_;
	    return *this;
	}
    };

    Idx1 operator[] (unsigned x_) {
	Idx1 result(*this);
	result.x = x_;
	return result;
    }
    
    

};


int main()
{
    ChannelData cd(5,10,15);
    cd[3][2][4] = 99;
    cout << (int)cd[3][2][4] << '\n';
}

> Consider wrapping the array in a class.

+1

Boost MultiArray provides such a class (it can handle any number of extents).

For example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <vector>
#include <numeric>
#include <functional>
#include <boost/multi_array.hpp>

int main()
{
    // extents of a 3x4x5x6x7 multidimensional array
    const std::vector<std::size_t> extents { 3, 4, 5, 6, 7 } ;

    // create a flattened 1d array of the above, fill it with 1, 2, 3 ...
    const auto N =  std::accumulate( extents.begin(), extents.end(), 1U, std::multiplies<>() ); // 3*4*5*6*7
    std::vector<double> array_1d(N) ;
    std::iota( array_1d.begin(), array_1d.end(), 1.0 ) ;

    // create a view of it as a 3x4x5x6x7 multidimensional array
    // multi_array_ref adapts an existing array of data to provide the multi_array interface. 
    // multi_array_ref does not own the data passed to it.
    // https://www.boost.org/doc/libs/1_75_0/libs/multi_array/doc/user.html
    boost::multi_array_ref<double,5> array_view_5d( array_1d.data(), extents ) ;

    // access elements using the view
    std::cout << array_view_5d[2][3][4][5][6] << " == " << N << '\n' ;
    array_view_5d[1][2][3][4][5] = 12.34 ;
    // etc.
}

http://coliru.stacked-crooked.com/a/b52914ec4ee71558
Last edited on
https://stackoverflow.com/questions/26719828/how-to-reshape-a-matrix


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

int main()
{
   const int ni = 3, nj = 4, nchannels = 5;    // need to be compile-time constants

   // Original array
   char * a = new char[ni*nj*nchannels];
   for ( int i = 0; i < ni * nj * nchannels; i++ ) a[i] = 'a' + i % 26;

   // Reshaped array
   char (* a3d)[ni][nj][nchannels] = reinterpret_cast<char(*)[ni][nj][nchannels]>( a );

   // Print reshaped array
   for ( int i = 0; i < ni; i++ )
   {
      for ( int j = 0; j < nj; j++ )
      {
         for ( int k = 0; k < nchannels; k++ ) cout << (*a3d)[i][j][k];
         cout << '\n';
      }
      cout << '\n';
   }
   delete a;
}


abcde
fghij
klmno
pqrst

uvwxy
zabcd
efghi
jklmn

opqrs
tuvwx
yzabc
defgh




Easier in Fortran:
program test
   implicit none;
   character, allocatable :: a(:), a3d(:,:,:)
   character :: NL = new_line( 'x' )
   integer ni, nj, nchannels
   integer i, j

   ni = 3;   nj = 4;   nchannels = 5

   ! Original array
   a = [ ( achar( iachar( 'a' ) + modulo( i - 1, 26 ) ), i = 1, ni * nj * nchannels ) ]

   ! Reshaped array
   a3d = reshape( a, [nchannels,nj,ni] )         ! allocate, copy and reshape

   ! Print reshaped array
   write( *, "( *( a ) )" ) ( ( a3d(:,j,i), NL, j = 1, nj ), NL, i = 1, ni )

   deallocate( a, a3d )
end program test


To test in an online compiler (online GDB):
https://onlinegdb.com/O-IChNii2
Last edited on
Easier in Fortran:


Wow! Fortran has sure changed massively since I last used it (Fortran IV & 77). I wouldn't have recognised that code as Fortran at all. I see From Wiki that Fortran has been steadily updated over the years with the latest change in 2018. I guess it must still be popular. 64 years and counting!
Topic archived. No new replies allowed.