OPENCV Is there a better way to access data across dimension?

500 views Asked by At

I have a 3 dimensional cv::Mat with size (10, rows=M, cols=N). This is 10 images with MxN pixels stacked in a cube. I would like to slice the cube by rows in the dimension across the images such that at the end I have M slices of (10, N) to which I'll apply some other opencv algorithms on. I found that I can do this with cv::Ranges(); however, I have to use reshape to make it 2D and thus I have to use clone() to make the slice continuous. Below is the code snippet that I used to do this but the execution time is slow (I think do to the clone/copy that is done for each row slice). Is there a better way to do this? I also found this, which is not encouraging.

const int img_dim[3] = {10, 20, 40};
Mat data = Mat::zeros(3, img_dim, CV_64FC1);
for (int row = 0; row < data.size[1]; row++ {
    std::vector<Range> range;
    range.push_back(Range(0, data.size[0]));
    range.push_back(Range(row, row+1));
    range.push_back(Range(0, data.size[2]));

    // Below slice is still 3D with (10, 1, 40) so I use reshape to make it (10, 40)
    // which requires the clone()
    Mat slice = data(&range[0]).clone();
    const int sz[] {data.size[0], data.size[2]};
    slice = slice.reshape(1, 2, sz);

    // Processing of slice
    // e.g cv::GaussianBlur(slice, dst, Size(0,0), r, r);
}
1

There are 1 answers

3
Rotem On

You may set the step (bytes stride), and build the slice without copying the data:

Assume data is continuous in memory:

  • The row starts in offset of row*N double elements from data.data (data.data points matrix data).
  • The slice step (bytes stride between rows), equals M * N *sizeof(double).

[In case data is not continuous in memory, solution is more complicated].

Here is a code sample (builds a slice of 5th row):

const int M = 20;
const int N = 40;
const int img_dim[3] = { 10, M, N };
Mat data = cv::Mat::zeros(3, img_dim, CV_64FC1);

int row = 5;

data.at<double>(0, row, 0) = 0;
data.at<double>(1, row, 0) = 1;
data.at<double>(2, row, 0) = 2;
data.at<double>(3, row, 0) = 3;

data.at<double>(0, row, 1) = 100;
data.at<double>(1, row, 1) = 101;
data.at<double>(2, row, 1) = 102;
data.at<double>(3, row, 1) = 103;

//Byte stride between M of slice equals M*N*8 bytes
size_t step = M * N *sizeof(double);

//slice begins at data.data + row*N
Mat slice(10, N, CV_64FC1, (uchar*)data.data + row*N*sizeof(double), step);

std::cout << slice.at<double>(0, 0) << std::endl << slice.at<double>(1, 0) << std::endl << slice.at<double>(2, 0) << std::endl << slice.at<double>(3, 0) << std::endl << std::endl;
std::cout << slice.at<double>(0, 1) << std::endl << slice.at<double>(1, 1) << std::endl << slice.at<double>(2, 1) << std::endl << slice.at<double>(3, 1) << std::endl << std::endl;

Note: Processing data with large strides may be inefficient due cache misses - it may be more efficient to copy the data (check if data processing gets slower).


Output:

0
1
2
3

100
101
102
103