HemHem
HemHem

Reputation: 55

How to modify part of the multi-dimensional matrix in openCV?

I want to modify a part of a multi-dimensional matrix using openCV. Basically I want to achieve the same as written in Matlab:

A = zeros(5,5,25);
A(:,:,1) = some_matrix1;
A(:,:,2) = some_matrix2;

I am not sure if I should use a 5x5 matrix with 25 channels or a 5x5x25 matrix with single channel. Here is what I tried:

int dim[3] = { 5,5,25 };
Mat A(3, dim, CV_32FC(1), Scalar::all(0));
A(Range::all(),Range::all(),0) = some_matrix;

But it seems like I can only use Range for two dimensions. Or

Mat A(5, 5, CV_32FC(25), Scalar::all(0));
A(Range::all(),Range::all())[0] = some_matrix;

But in this case, I don't know how to access the channel. Can you please help me with it?

Upvotes: 0

Views: 1376

Answers (2)

Miki
Miki

Reputation: 41765

OpenCV is optimized for 2D matrices. Multidimensional matrix will work, but are rather inefficient and difficult to access.

This example code will show you how to write and read values from an 3D matrix:

#include <opencv2\opencv.hpp>
using namespace cv;

int main()
{
    int sizes[] = { 5, 5, 25 };
    Mat data(3, sizes, CV_32F);

    Mat1f some_matrix(sizes[0], sizes[1]);
    randu(some_matrix, 0.f, 100.f); // some random  values

    // Init data with each plane a constant increasing value 
    for (int z = 0; z < data.size[2]; ++z)
    {
        // Set each z-plane to some scalar value
        Range ranges[] = { Range::all(), Range::all(), Range(z, z + 1) };
        data(ranges) = data.size[2] - z;
    }

    // Set the n-th z-plane to some_matrix
    int z = 0;
    for (int r = 0; r < sizes[0]; ++r)
    {
        for (int c = 0; c < sizes[1]; ++c)
        {
            data.at<float>(r, c, z) = some_matrix(r, c);
        }
    }

    // Access all slices along z dimension
    for (int z = 0; z < data.size[2]; ++z)
    {
        Range ranges[] = { Range::all(), Range::all(), Range(z, z + 1) };
        Mat slice3d(data(ranges).clone()); // with clone slice is continuous, but still 3d
        Mat slice(2, &data.size[0], data.type(), slice3d.data);
    }

    return 0;
}

However, it's far easier and practical to store your 5x5x25 3D matrix as a std::vector<Mat>, where the vector has length 25, and each matrix is a 2D 5x5.

See the code:

#include <opencv2\opencv.hpp>
using namespace cv;

int main()
{
    int sizes[] = { 5, 5, 25 };

    vector<Mat> data(sizes[2]);
    // Init data with each plane a constant increasing value
    for (int z = 0; z < sizes[2]; ++z)
    {
        data[z] = Mat(sizes[0], sizes[1], CV_32F, float(sizes[2] - z));
    }

    Mat1f some_matrix(sizes[0], sizes[1]);
    randu(some_matrix, 0.f, 100.f); // some random  values

    // Set the n-th z-plane to some_matrix
    int z = 0;
    data[z] = some_matrix;

    return 0;
}

Upvotes: 2

Here is the piece of code to access the pixel from the channel, you can try it.

int dim[3] = { 5,5,25 };
Mat A(3, dim, CV_32FC1, Scalar::all(0));

 for (int m = 0; m < 5; m++)
{
  for (int n = 0; n < 5; n++)
  {
    for (int a = 0; a < 25; a++) // no of channels
    {
      cout << A.at<cv::Vec3f>(m,n)[a] << endl;
    }
  }
}

Upvotes: 2

Related Questions