【问题标题】:OPENCV Is there a better way to access data across dimension?OPENCV 有没有更好的跨维度访问数据的方法?
【发布时间】:2020-03-11 22:25:45
【问题描述】:

我有一个大小为 (10, rows=M, cols=N) 的 3 维 cv::Mat。这是 10 张图像,MxN 像素堆叠在一个立方体中。我想在图像的维度中按行对多维数据集进行切片,以便最后我有 M 个 (10, N) 切片,我将在这些切片上应用一些其他 opencv 算法。我发现我可以用 cv::Ranges();但是,我必须使用 reshape 使其成为 2D,因此我必须使用 clone() 使切片连续。下面是我用来执行此操作的代码 sn-p,但执行时间很慢(我认为对每个行切片执行的克隆/复制执行此操作)。有一个更好的方法吗?我还发现了this,这并不令人鼓舞。

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);
}

【问题讨论】:

    标签: c++ opencv


    【解决方案1】:

    您可以设置step(字节步长),并在不复制数据的情况下构建切片:

    假设data在内存中是连续的:

    • 行从data.datadata.data 点矩阵数据)的row*Ndouble 元素的偏移开始。
    • 切片step(行间的字节跨度)等于M * N *sizeof(double)

    [如果data在内存中不连续,则解决方案更复杂]。

    这是一个代码示例(构建第 5 行的切片):

    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;
    

    注意:由于缓存未命中,处理大步幅的数据可能效率低下 - 复制数据可能更有效(检查数据处理是否变慢)。


    输出:

    0
    1
    2
    3
    
    100
    101
    102
    103
    

    【讨论】:

    • 我试过了,它就像你描述的那样工作。我现在的问题是,如果我要在切片上调用 cv::GaussianBlur() 或 cv::findContours(),它是否会因为切片本身不连续而永远进行复制/克隆?
    • 我用调试器进入cv::GaussianBlur()源代码。它执行sepFilter2D,执行hal::sepFilter2D,执行ocvSepFilter,执行f-&gt;apply,执行FilterEngine::proceed克隆输入。据我所知,OpenCV 函数支持“跨步输入”,因此不会克隆切片。
    • 太好了!我使用了一个切片并测试了它看起来快一点的时间。
    猜你喜欢
    • 2018-10-11
    • 1970-01-01
    • 1970-01-01
    • 2018-01-30
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多