简体   繁体   中英

C++: Reshape vector to 3D array

Edit: I have uploaded the vector to Drive as a text file, in case anyone want to have a look: https://drive.google.com/file/d/0B0wsPU8YebRQbDUwNFYza3ljSnc/view?usp=sharing

I'm trying to reshape my vector h into a 3D array. h contains 295788 elements. In this case height = 314 , width = 314 and depth = 3 . Basically what I'm trying to do is what MATLAB does with its reshape function.

h = reshape(h, height, width, depth)

This is my attempt so far, but when I print it all I see is zeroes, which is not right. I have double checked that h contains the numbers I'm expecting.

vector<vector<vector<double> > > array3D;

int height = 314, width = 314, depth = 3;
// Set up sizes
array3D.resize(height);
for (int i = 0; i < height; ++i) {
    array3D[i].resize(width);

    for (int j = 0; j < width; ++j)
        array3D[i][j].resize(depth);
}

for (int i = 0; i < height; i++)
{
    array3D[i][0][0] = h[i];
    for (int j = 0; j < width; j++)
    {
        array3D[i][j][0] = h[i+j];
        for (int k = 0; k < depth; k++)
        {
            array3D[i][j][k] = h[i+j+k];
        }
    }
}

Printing:

for (vector<vector<vector<double>>>::const_iterator i = array3D.begin(); i != array3D.end(); ++i)
{
    for (vector<vector<double>>::const_iterator j = i->begin(); j != i->end(); ++j)
    {
        for (vector<double>::const_iterator k = j->begin(); k != j->end(); ++k)
        {
            cout << *k << ' ';
        }
    }
}

So my question is, how do I convert my vector into a 3D array properly?

I managed to do this by using Eigen::Tensor as suggested by Henri Menke. I ended up creating an array for the initial 314x314x3 matrix, and then another one for the 300x300x3 matrix. It's neither fast nor pretty, but for now that is what I could come up with. Looks like this.

For clarification: margin is calculated further up in the code, but in this example with the 314x314x3 matrix it's margin=7 . h is a vector with 295788 elements. nrh=314 , nch=314 and nradii=3 .

Tensor<int, 3> t(nrh, nch, nradii);
int counter = 0;
for (int k = 0; k < nradii; k++)
{
    for (int col = 0; col < nch; col++)
    {
        for (int row = 0; row < nrh; row++) 
        {
            t(row, col, k) = h[counter];
            counter += 1;
        }
    }
}

int height = nrh - margin * 2;
int width = nch - margin * 2;
int depth = nradii;
Tensor<int, 3> out(height, width, depth);
int count1 = 0, count2 = 0, count3 = 0;

for (int k = 0; k < depth; k++)
{
    for (int j = margin; j < nch - margin; j++)
    {
        for (int i = margin; i < nrh - margin; i++)
        {
            out(count1, count2, count3) = t(i, j, k);
            count1 += 1;
        }
        count1 = 0;
        count2 += 1;
    }
    count2 = 0;
    count3 += 1;
}

Edit: Solution #2 with Tensor.slice()

int height = nrh - margin * 2;
int width = nch - margin * 2;
int depth = nradii;
Tensor<int, 3> tensor(height, width, depth);
DSizes<ptrdiff_t, 3> indices(margin, margin, 0);
DSizes<ptrdiff_t, 3> sizes(height, width, nradii);
tensor = t.slice(indices, sizes);

How about:

array3D[i][j][k] = h[i*(depth*width)+j*depth+k];

That may or may not be scanning the vector in the correct order.

Notice how when the index k resets the index j increments so you move on exactly one until the index j resets in which case i increments and the same. It's easy to show this calculation reads every element exactly once.

I'd normally expect a width , height then depth and you're scanning in the opposite order!

Footnote: Depending on the application is may be worthwhile to just access the vector using this approach. In general it turns out to be faster than accessing a vector of vectors of vectors. That can be relevant when dealing with massive arrays.

Actually, your the structure of your code is already ok, however, there are two mistakes:

  • The lines

     array3D[i][0][0] = h[i]; 

    and

     array3D[i][j][0] = h[i+j]; 

    are pointless. You are overwriting those entries later on with the line

     array3D[i][j][k] = h[i+j+k]; 
  • The index calculation for h[] is wrong: You must multiply the row index by the length of a row before adding the cell index. The assignment should look like this:

     array3D[i][j][k] = h[(i*width+j)*depth+k]; 

    Otherwise, you will get the same result for (i, j, k) == (3, 2, 1) as for (i, j, k) == (1, 3, 2) , which is obviously wrong. In the index calculation above, I have assumed that k is the fastest changing dimension. If that is not the order in which your data is stored in h , you need to change the positions of i , j , and k and adjust the factors accordingly.

Putting this together, your assignment loop should read:

for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
        for (int k = 0; k < depth; k++) {
            array3D[i][j][k] = h[(i*width+j)*depth+k];
        }
    }
}

Slightly off-topic:
If you were using C instead of C++, you could "simply" do this:

size_t dataSize;
//Create a real 3D array with the dimensions (height, width, depth).
double (*array3D)[width][depth] = malloc(dataSize = height*sizeof(*array3D));
//Copy over the data from the file.
memcpy(array3D, h, dataSize);


//Print the array contents:
for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
        for (int k = 0; k < depth; k++) {
            printf("%d ", array3D[i][j][k]);
        }
    }
}

This uses a real 3D array instead of an array of pointers to arrays of pointers to arrays of doubles (which is roughly what a vector<vector<vector<double>>> is). However, this cannot be done in C++ as C++ does not allow for array types with dynamic sizes as C does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM