Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ : Create 3D array out of stacking 2D arrays

Tags:

c++

stack

opencv

2d

3d

In Python I normally use functions like vstack, stack, etc to easily create a 3D array by stacking 2D arrays one onto another.

Is there any way to do this in C++?

In particular, I have loaded a image into a Mat variable with OpenCV like:

cv::Mat im = cv::imread("image.png", 0);

I would like to make a 3D array/Mat of N layers by stacking copies of that Mat variable.

EDIT: This new 3D matrix has to be "travellable" by adding an integer to any of its components, such that if I am in the position (x1,y1,1) and I add +1 to the last component, I arrive to (x1,y1,2). Similarly for any of the coordinates/components of the 3D matrix.

SOLVED: Both answers from @Aram and @Nejc do exactly what expected. I set @Nejc 's answer as the correct one for his shorter code.

like image 233
daniglezad Avatar asked Jan 03 '23 15:01

daniglezad


1 Answers

The Numpy function vstack returns a contiguous array. Any C++ solution that produces vectors or arrays of cv::Mat objects does not reflect the behaviour of vstack in this regard, becase separate "layers" belonging to individual cv::Mat objects will not be stored in contiguous buffer (unless a careful allocation of underlying buffers is done in advance of course).

I present the solution that copies all arrays into a three-dimensional cv::Mat object with a contiguous buffer. As far as the idea goes, this answer is similar to Aram's answer. But instead of assigning pixel values one by one, I take advantage of OpenCV functions. At the beginning I allocate the matrix which has a size N X ROWS X COLS, where N is the number of 2D images I want to "stack" and ROWS x COLS are dimensions of each of these images.

Then I make N steps. On every step, I obtain the pointer to the location of the first element along the "outer" dimension. I pass that pointer to the constructor of temporary Mat object that acts as a kind of wrapper around the memory chunk of size ROWS x COLS (but no copies are made) that begins at the address that is pointed-at by pointer. I then use copyTo method to copy i-th image into that memory chunk. Code for N = 2:

cv::Mat img0 = cv::imread("image0.png", CV_IMREAD_GRAYSCALE);
cv::Mat img1 = cv::imread("image1.png", CV_IMREAD_GRAYSCALE);

cv::Mat images[2] = {img0, img1};  // you can also use vector or some other container

int dims[3] = { 2, img0.rows, img0.cols }; // dimensions of new image  

cv::Mat joined(3, dims, CV_8U); // same element type (CV_8U) as input images

for(int i = 0; i < 2; ++i)
{
  uint8_t* ptr = &joined.at<uint8_t>(i, 0, 0); // pointer to first element of slice i

  cv::Mat destination(img0.rows, img0.cols, CV_8U, (void*)ptr); // no data copy, see documentation

  images[i].copyTo(destination);
}
like image 136
Nejc Avatar answered Jan 05 '23 06:01

Nejc