【问题标题】:C++ : Create 3D array out of stacking 2D arraysC++:从堆叠的 2D 数组中创建 3D 数组
【发布时间】:2023-03-23 20:35:02
【问题描述】:

在 Python 中,我通常使用 vstackstack 等函数,通过将 2D 数组堆叠在一起来轻松创建 3D 数组。

在 C++ 中有没有办法做到这一点?

特别是,我已经使用 OpenCV 将图像加载到 Mat 变量中,例如:

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

我想通过堆叠该 Mat 变量的副本来制作 N 层的 3D 数组/Mat。

编辑:这个新的 3D 矩阵必须是“可移动的”,方法是向它的任何组件添加一个整数,这样如果我在位置 (x1,y1,1) 并且我添加+1 到最后一个组件,我到达 (x1,y1,2)。对于 3D 矩阵的任何坐标/分量也是如此。

已解决:@Aram 和 @Nejc 的两个答案都符合预期。我将@Nejc 的答案设置为他较短代码的正确答案。

【问题讨论】:

    标签: c++ opencv 3d stack 2d


    【解决方案1】:

    Numpy 函数vstack 返回一个连续数组。任何产生 cv::Mat 对象的向量或数组的 C++ 解决方案都不会反映 vstack 在这方面的行为,因为属于单个 cv::Mat 对象的单独“层”不会存储在连续缓冲区中(除非小心底层缓冲区的分配当然是提前完成的)。

    我提出了将所有数组复制到具有连续缓冲区的三维cv::Mat 对象的解决方案。就想法而言,这个答案类似于Aram's answer。但我没有一一分配像素值,而是利用了 OpenCV 函数。一开始我分配了大小为N X ROWS X COLS 的矩阵,其中N 是我想要“堆叠”的二维图像的数量,ROWS x COLS 是每个图像的尺寸。

    然后我做N 步骤。在每一步,我都会获得指向“外部”维度上第一个元素位置的指针。我将该指针传递给临时 Mat 对象的构造函数,该对象充当大小为 ROWS x COLS(但不制作副本)的内存块的一种包装器,该内存块从指针指向的地址开始。然后我使用copyTo 方法将i-th 图像复制到该内存块中。 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);
    }
    

    【讨论】:

    • 我正在执行此操作,并且 joined 似乎没有预期的尺寸(当我 cout joined.size() 时我得到 100x2) .
    • 我的错,一旦我使用 joined.size (没有括号)它显示正确的大小。
    【解决方案2】:

    此答案是对上述问题的回应:

    在 Python 中,我通常使用 vstack、stack 等函数通过将 2D 数组堆叠在一起来轻松创建 3D 数组。

    这当然是可能的,你可以将矩阵添加到一个向量中,这将是你的“堆栈”

    例如,您可以使用

    std::vector<cv::Mat>>
    

    这会给你一个垫子向量,这将是一个切片,然后你可以通过添加更多切片向量来“分层”它们

    如果您想拥有多个堆栈,您可以将该向量添加到另一个向量中:

    std::vector<std::vector<cv::Mat>>
    

    要将矩阵添加到数组中:

    myVector.push_back(matrix);
    

    编辑以下问题

    在这种情况下,我是否可以从一个位置 (x1, y1, z1) 移动到紧靠上方的位置 (x1,y1,z1+1),这样我在矩阵中的新位置将是 (x1,y1 ,z2)?

    你最终会得到一个看起来很像这样的东西。如果您在向量中的元素 1 处有一个矩阵,则它与元素 [2] 没有任何关系,除非您已将其添加到该点。如果你想建立关系,那么你需要自己编写代码。

    【讨论】:

    • 能否请您说明如何添加切片?
    • 已添加,但我强烈建议阅读有关 C++ 向量的教程
    • 在这种情况下,我是否可以从一个位置 (x1, y1, z1) 移动到紧靠上方的位置 (x1,y1,z1+1),这样我在矩阵中的新位置会是 (x1,y1,z2)?
    • @thepirate16 您是否希望访问 OpenCV 图像中的三个通道?
    • @skr_robo 不,我只想通过向其任何组件添加标量来遍历 3D 矩阵。
    【解决方案3】:

    您实际上可以使用 opencv 创建 3D 或 ND mat,您需要使用将 dimensions 作为输入的构造函数。然后将每个矩阵复制到(本例)3D 数组中

    #include <opencv2/opencv.hpp>
    
    using namespace cv;
    using namespace std;
    
    int main() {
        // Dimensions for the constructor... set dims[0..2] to what you want
        int dims[] = {5, 5, 5}; // 5x5x5 3d mat
    
        Mat m = Mat::zeros(5, 5, CV_8UC1);
    
        for (size_t i = 0; i < 5; i++) {
            for (size_t k = 0; k < 5; k++) {
                m.at<uchar>(i, k) = i + k;
            }
        }
    
        // Mat with constructor specifying 3 dimensions with dimensions sizes in dims.
        Mat 3DMat = Mat(3, dims, CV_8UC1);
    
        // We fill our 3d mat.
        for (size_t i = 0; i < m2.size[0]; i++) {
            for (size_t k = 0; k < m2.size[1]; k++) {
                for (size_t j = 0; j < m2.size[2]; j++) {
                    3DMat.at<uchar>(i, k, j) = m.at<uchar>(k, j);
                }
            }
        }
    
        // We print it to show the 5x5x5 array.
        for (size_t i = 0; i < m2.size[0]; i++) {
            for (size_t k = 0; k < m2.size[1]; k++) {
                for (size_t j = 0; j < m2.size[2]; j++) {
                    std::cout << (int) 3DMat.at<uchar>(i, k, j) << " ";
                }
                std::cout << endl;
            }
            std::cout << endl;
        }
    
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      基于问题和 cmets,我认为您正在寻找这样的东西:

      std::vector<cv::Mat> vec_im;
      //In side for loop:
      vec_im.push_back(im);
      

      然后,您可以通过以下方式访问它:

      Scalar intensity_1 = vec_im[z1].at<uchar>(y, x);
      Scalar intensity_2 = vec_im[z2].at<uchar>(y, x);
      

      这假设图像是单通道的。

      【讨论】:

        猜你喜欢
        • 2019-09-27
        • 2021-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多