0

我有一个关于用 C++ 实现图像插值(双三次和双线性方法)的问题。我主要关心的是速度。根据我对问题的理解,为了让插值程序快速高效,可以采用以下策略:

  1. 使用流式 SIMD 扩展 (SSE) 的快速图像插值

  2. 使用多线程或 GPU 进行图像解释

  3. 快速图像插值算法

  4. C++ 实现技巧

在这里,我对最后一个策略更感兴趣。我设置了一个插值类:

     /**
        * This class is used to perform interpretaion for a certain poin in 
        * the image grid.
        */
        class  Sampling
        {
        public:
            //   samples[0] *-------------* samples[1]
            //              --------------
            //              --------------
            //   samples[2] *-------------*samples[3]
            inline void sampling_linear(unsigned char *samples, unsigned char &res)
            {
                unsigned char res_temp[2];
                sampling_linear_1D(samples,res_temp[0]);
                sampling_linear_1D(samples+2,res_temp[1]);
                sampling_linear_1D(res_temp,res);
            }
        private:
            inline void sampling_linear_1D(unsigned char *samples, unsigned char &res)
            {
            }
        }

这里我只举一个双线性插值的例子。为了使程序运行得更快,使用了内联函数。我的问题是这种实施方案是否有效。此外,在解释过程中,如果我让用户选择在不同的插值方法之间进行选择。然后我有两个选择:

  1. 根据插值方法,调用函数对整个图像执行插值。
  2. 对于每个输出图像像素,首先确定其在输入图像中的位置,然后根据插值方法设置,确定插值函数。

第一种方法意味着程序中的代码更多,而第二种方法可能导致效率低下。那么,我该如何在这两种方案之间进行选择呢?谢谢!

4

1 回答 1

9

使用流式 SIMD 扩展 (SSE) 的快速图像插值

这可能无法提供所需的结果,因为我希望您的算法将是内存限制的,而不是 FLOP/s 限制的。

我的意思是 - 这肯定会有所改进,但与实施成本相比没有好处。

顺便说一句,现代编译器可以执行自动矢量化(即使用 SSE 和其他扩展):GCC 从 4.0开始,MSVC 从 2012 开始MSVC Auto-Vectorization 视频讲座

使用多线程或 GPU 进行图像解释

多线程版本应该会产生良好的效果,因为它可以让您利用所有可用的内存吞吐量。

如果你不打算多次处理数据,或者在 GPU 上以某种方式使用它,那么 GPGPU 可能不会给出想要的结果。是的,它会更快地产生结果(主要是由于更高的内存速度),但是这种效果会被主 RAM 和 GPU 的 RAM 之间的缓慢传输所抵消。

例如,近似现代吞吐量:

  1. CPU RAM ~ 20GiB/s
  2. GPU RAM ~ 150GiB/s
  3. 在 CPU RAM <-> GPU RAM 之间传输 ~ 3-5 GiB/s

对于单程内存限制算法,在大多数情况下,第三项使 GPU 的使用变得不切实际(对于此类算法)。

为了使程序运行得更快,采用了内联函数

默认情况下,类成员函数是“内联”的。请注意,“内联”的主要目的实际上并不是“内联”,而是有助于防止在标题中定义函数时违反单一定义规则。

有依赖于编译器的“forceinline”特性,例如 MSVC 有__forceinline。或者从编译器 ifdef'ed BOOST_FORCEINLINE 宏中抽象出来。

无论如何,除非您没有证明,否则请相信您的编译器(例如在汇编程序的帮助下)。最重要的事实是编译器应该看到函数定义 - 然后它可以决定自己内联,即使函数本身不是内联的。

我的问题是这种实施方案是否有效。

据我了解,作为预步骤,您将样本收集到 2x2 矩阵中。我认为直接将两个指针直接传递给图像中两个元素的数组可能会更好,或者一个指针+宽度大小(自动计算第二个指针)。但是,这不是一个大问题,您的临时 2x2 矩阵很可能会被优化掉。


真正重要的是——你如何遍历你的图像。

假设对于给定的 x 和 y,索引计算为:

i=width*y+x;

那么你的遍历循环应该是:

for(int y=/*...*/)
    for(int x=/*...*/)
    {
        // loop body
    }

因为,如果您选择另一个顺序(先是 x,然后是 y) - 它对缓存不友好,因此性能下降可能高达 64 倍(取决于您的像素大小)。你可以检查它只是为了你的兴趣。

第一种方法意味着程序中的代码更多,而第二种方法可能导致效率低下。那么,我该如何在这两种方案之间进行选择呢?谢谢!

在这种情况下,您可以使用编译时多态性来减少第一个版本的代码量。例如,基于模板。

只需看一下std::accumulate - 它可以编写一次,然后它将适用于不同类型的迭代器、不同的二进制操作(函数或函子),而不会因为它的多态性而暗示任何运行时惩罚。

亚历山大·斯捷潘诺夫 说

多年来,我试图在更高级的语言(例如,Ada 和 Scheme)中实现相对效率,但失败了。即使是简单算法的通用版本也无法与内置原语竞争。但在 C++ 中,我终于不仅能够实现相对效率,而且非常接近于绝对效率这一更雄心勃勃的目标。为了验证这一点,我花了无数个小时查看不同编译器在不同架构上生成的汇编代码。


检查Boost 的通用图像库- 它有很好的教程,并且有作者的视频演示

于 2012-10-30T18:17:23.100 回答