使用C++实现图像插值

fee*_*ree 0 c++ implementation opencv stl image-processing

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

  1. 使用Streaming SIMD Extensions(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)
            {
            }
        }
Run Code Online (Sandbox Code Playgroud)

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

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

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

Evg*_*yuk 8

使用Streaming SIMD Extensions(SSE)进行快速图像插值

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

我的意思是 - 它肯定会有所改进,但与实施成本相比并没有益处.

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

使用多线程或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的使用不切实际(对于这样的算法).

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

类成员函数默认为"内联".Beaware,"内联"的主要目的实际上并不是"inling",而是在标题中定义函数时帮助防止违反One Definition Rule.

有依赖于编译器的"forceinline"功能,例如MSVC具有__forceinline.或者从编译器ifdef'ed BOOST_FORCEINLINE宏中抽象出来.

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

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

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


真正重要的是 - 你如何遍历你的形象.

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

i=width*y+x;
Run Code Online (Sandbox Code Playgroud)

然后你的遍历循环应该是:

for(int y=/*...*/)
    for(int x=/*...*/)
    {
        // loop body
    }
Run Code Online (Sandbox Code Playgroud)

因为,如果您选择另一个订单(先x先,然后是y) - 它将不会缓存,并且结果性能下降最多可达64x(取决于您的像素大小).您可以检查它只是为了您的兴趣.

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

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

只需看看std :: accumulate - 它可以写一次,然后它将在不同类型的迭代器,不同的二进制操作(函数或函子)上工作,而不会因为它的多态性而导致任何运行时惩罚.

Alexander Stepanov说:

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


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