【问题标题】:Fast data structure or algorithm to find mean of each pixel in a stack of images快速数据结构或算法,用于查找图像堆栈中每个像素的平均值
【发布时间】:2016-11-17 16:31:26
【问题描述】:

我有一堆图像,我想在其中计算堆栈中每个像素的平均值。

例如,让(x_n,y_n) 成为第 n 个图像中的(x,y) 像素。因此,图像堆栈中三个图像的像素(x,y) 的平均值为:

mean-of-(x,y) = (1/3) * ((x_1,y_1) + (x_2,y_2) + (x_3,y_3))

我的第一个想法是将每个图像的所有像素强度加载到具有单个线性缓冲区的数据结构中,如下所示:

|All pixels from image 1| All pixels from image 2| All pixels from image 3|

为了在图像堆栈中找到一个像素的总和,我执行了一系列嵌套的 for 循环,如下所示:

for(int col=0; col<img_cols; col++)
{
    for(int row=0; row<img_rows; row++)
    {
        for(int img=0; img<num_of_images; img++)
        {
            sum_of_px += px_buffer[(img*img_rows*img_cols)+col*img_rows+row];
        }
    }
}

基本上,img*img_rows*img_cols 给出了第 n 个图像中第一个像素的缓冲区元素,col*img_rows+row 给出了我想为堆栈中的每个 n 个图像找到的 (x,y) 像素。

是否有一种数据结构或算法可以帮助我总结图像堆栈中的像素强度,比我当前的实现更快、更有条理?

我的目标是可移植性,所以我不会使用 OpenCV,而是在 linux 上使用 C++。

【问题讨论】:

  • 我认为您不是在寻找更好的数据结构。我认为您正在寻找更好的算法。每个图像的每个像素都需要存在于内存中的某个点或另一个点;不同的数据结构唯一能做的就是减少你在任何给定时刻使用的内存量。我很难想象一个数据结构会产生 速度 的提高,而不是避免缓存未命中的数据结构,但在这一点上,你可以做其他事情来提高性能。跨度>
  • 谢谢。现在编辑更正。我非常愿意接受任何可以提高性能,尤其是速度的建议。
  • 更好的解决方案是不要将它们同时保存在内存中根本,而只需对每个图像进行一次传递。
  • 为此使用 AVX。它将并行执行乘法和加法。 Google 使用 AVX 计算点积。另请查看具有“Mused 乘加”指令的 Haswell。

标签: c++ algorithm performance image-processing data-structures


【解决方案1】:

问题中嵌套循环的问题在于它对缓存不是很友好。您大步跳过内存,有效地使您的数据缓存无用。您将花费大量时间来访问内存。

如果您可以节省内存,您可以创建一个额外的图像大小的缓冲区,以在您按内存顺序遍历所有图像中的所有像素时累积每个像素的总数。然后你通过缓冲区进行一次除法。

您的累积缓冲区可能需要使用比单个像素值更大的类型,因为它必须累积许多像素值。如果您的像素值是 8 位整数,那么您的累积缓冲区可能需要 32 位整数或浮点数。

【讨论】:

    【解决方案2】:

    通常是一堆像素

    (x_1,y_1),...,(x_n,y_n)
    

    有条件地独立于堆栈

    (a_1,b_1),...,(a_n,b_n) 
    

    即使它们不是(假设特定数据集),对它们的交互进行建模也是一项复杂的任务,并且只会为您提供平均值的估计值。因此,如果您想计算每个堆栈的确切平均值,您别无选择,只能遍历您提供的三个循环。诸如 Matlab/octave 之类的语言和诸如 Theano (python) 或 Torch7 (lua) 之类的库都将这些迭代并行化。如果您使用的是 C++,那么您所做的非常适合 Cuda 或 OpenMP。至于可移植性,我认为 OpenMP 是更简单的解决方案。

    【讨论】:

    • 我已经在 Cuda 中实现了这样的东西,但是平均值会通过。 Cuda 的开销实际上可能导致整个程序的运行时间更长
    • 这不取决于数据的大小吗?如果你有 100000 个大小为 1240*370 的 RGB 图像……那么 Cuda 不是更快吗?
    【解决方案3】:

    一个可移植的、快速的数据结构专门用于平均计算可以是:

    std::vector<std::vector<std::vector<sometype> > > VoVoV;
    VoVoV.resize(img_cols);
    int i,j;
    for (i=0 ; i<img_cols ; ++i)
    {
      VoVoV[i].resize(img_rows);
      for (j=0 ; j<img_rows ; ++j)
      {
        VoVoV[i][j].resize(num_of_images);
        // The values of all images at this pixel are stored continguously,
        // therefore should be fast to access.
      }
    }
    
    VoVoV[col][row][img] = foo;
    

    附带说明,您的示例中的1/3 将评估为0,这不是您想要的。

    为了快速求和/平均,您现在可以这样做:

    sometype sum = 0;
    std::vector<sometype>::iterator it     = VoVoV[col][row].begin();
    std::vector<sometype>::iterator it_end = VoVoV[col][row].end();
    for ( ; it != it_end ; ++it)
      sum += *it;
    sometype avg = sum / num_of_images; // or similar for integers; check for num_of_images==0
    

    基本上你不应该依赖编译器会优化掉总是相同偏移量的重复计算。

    【讨论】:

    • 是的,对 1/3 感到抱歉。我更专注于正确传达我的想法!
    • 一个由多个向量组成的向量会非常慢,因为它非常间接。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    • 2023-04-03
    • 1970-01-01
    • 2014-04-24
    • 2020-11-21
    • 2021-04-03
    • 1970-01-01
    相关资源
    最近更新 更多