opencv基础:图像金字塔

86 阅读4分钟

1. 目标

在本教程中,将学习如何:

  • 使用 OpenCV 函数pyrUp()pyrDown()对图像进行下采样或上采样。

2. 理论

  • 笔记

    下面的解释来自于Bradski 和 Kaehler的《Learning OpenCV 》一书。

  • 以下两种做法可以将图像转换为不同的大小:

    1. 上采样图像(放大)
    2. 下采样图像(缩小)
  • 虽然OpenCV 中有一个几何变换函数可以调整图像大小( resize,将在以后的文章中展示),但在本节中,只会分析图像金字塔的使用,它被广泛应用于视觉应用。

图像金字塔

  • 图像金字塔是图像的集合——所有图像都来自单个原始图像——这些图像被连续下采样,直到达到某个所需的停止点。

  • 有两种常见的图像金字塔:

    • 高斯金字塔: 用于对图像进行下采样
    • 拉普拉斯金字塔: 用于从底部金字塔的图像(分辨率较低)重建上采样图像
  • 在本文中,将使用高斯金字塔

高斯金字塔

  • 将金字塔想象成一组层,其中层越高,尺寸越小。

    Pyramids_Tutorial_Pyramid_Theory.png

  • 层都是从下到上编号的,所以层(i+1)(Gi+1G_{i+1}小于层i(即GiG_i).

  • 在高斯金字塔中为了生成 层(i+1),需要执行以下操作:

    • GiG_i与高斯核卷积:
    \frac{1}{256} \begin{bmatrix}

1 & 4 & 6 & 4 & 1\
4 & 16 & 24 & 16 & 4\ 6 & 24 & 36 & 24 & 6\ 4 & 16 & 24 & 16 & 4\ 1 & 4 & 6 & 4 & 1\ \end{bmatrix} $$

-   删除每个偶数行和列。
  • 生成的图像正好是其前身的四分之一。在输入图像G0(原始图像)上迭代这个过程产生整个金字塔。

  • 上述过程对于对图像进行下采样很有用。如果想让图像变大怎么办?:用零填充的列(0)

    • 首先,将图像在每个维度上放大到原始图像的两倍,对新的偶数行
    • 使用上面显示的相同内核执行卷积(乘以 4)以近似“缺失像素”的值
  • 这两个过程(如上所述的下采样和上采样)由 OpenCV 函数pyrUp()pyrDown() 实现,将在下面的代码示例中看到:

  • 笔记

    当减小图像的大小时,实际上是在丢失图像的信息。

3. 代码

代码如下所示。

这里下载代码


#include "iostream"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
const char* window_name = "Pyramids Demo";
int main( int argc, char** argv )
{
    cout << "\n Zoom In-Out demo \n "
            "------------------  \n"
            " * [i] -> Zoom in   \n"
            " * [o] -> Zoom out  \n"
            " * [ESC] -> Close program \n" << endl;
    const char* filename = argc >=2 ? argv[1] : "chicky_512.png";
    // Loads an image
    Mat src = imread( samples::findFile( filename ) );
    // Check if image is loaded fine
    if(src.empty()){
        printf(" Error opening image\n");
        printf(" Program Arguments: [image_name -- default chicky_512.png] \n");
        return EXIT_FAILURE;
    }
    for(;;)
    {
        imshow( window_name, src );
        char c = (char)waitKey(0);
        if( c == 27 )
        { break; }
        else if( c == 'i' )
        { pyrUp( src, src, Size( src.cols*2, src.rows*2 ) );
            printf( "** Zoom In: Image x 2 \n" );
        }
        else if( c == 'o' )
        { pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
            printf( "** Zoom Out: Image / 2 \n" );
        }
    }
    return EXIT_SUCCESS;
}

4. 代码解释

下边分析一下代码的主干:

4.1 加载图像


 const char* filename = argc >=2 ? argv[1] : "chicky_512.png";
    // Loads an image
    Mat src = imread( samples::findFile( filename ) );
    // Check if image is loaded fine
    if(src.empty()){
        printf(" Error opening image\n");
        printf(" Program Arguments: [image_name -- default chicky_512.png] \n");
        return EXIT_FAILURE;
    }

4.2 创建窗口


 imshow( window_name, src );

4.3 主循环


for(;;)
    {
        imshow( window_name, src );
        char c = (char)waitKey(0);
        if( c == 27 )
        { break; }
        else if( c == 'i' )
        { pyrUp( src, src, Size( src.cols*2, src.rows*2 ) );
            printf( "** Zoom In: Image x 2 \n" );
        }
        else if( c == 'o' )
        { pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
            printf( "** Zoom Out: Image / 2 \n" );
        }
    }

执行无限循环等待用户输入。如果用户按下ESC ,程序就会退出。此外,它有两种选择:

  • 执行上采样 - 放大(按“i”后)

    使用带有三个参数的函数pyrUp() :

    • src:当前和目标图像(显示在屏幕上,应该是输入图像的两倍)
    • Size( tmp.cols2, tmp.rows2 ) :目标大小。上采样,因此 pyrUp() 期望大小是输入图像的两倍(在本例中为src)。

else if( c == 'i' )
{ pyrUp( src, src, Size( src.cols*2, src.rows*2 ) );
            printf( "** Zoom In: Image x 2 \n" );
}
  • 执行下采样 - 缩小(按“o”后)

    使用带有三个参数的函数pyrDown()(类似于pyrUp()):

    • src:当前和目标图像(显示在屏幕上,应该是输入图像的一半)
    • 大小(tmp.cols/2, tmp.rows/2) :目标大小。下采样,因此 pyrDown()期望输入图像大小的一半(在本例中为src)。

else if( c == 'o' )
{ pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
            printf( "** Zoom Out: Image / 2 \n" );
}

请注意,输入图像大小可以整除2(在两个维度上)是很重要的。否则,将会错误。

5. 结果

  • 该程序默认调用文件夹中的图像chicky_512.pngsamples/data。图像大小是512×512,因此下采样不会产生任何错误(512=29512=2^9)。原图如下所示:

    Pyramids_Tutorial_Original_Image.jpg

  • 首先,通过按“d”应用两个连续的pyrDown()操作。 输出是:

    Pyramids_Tutorial_PyrDown_Result.jpg

  • 由于正在减小图像的大小,会丢失一些分辨率。在应用pyrUp() 两次(通过按 'u')后很明显。现在输出是:

    Pyramids_Tutorial_PyrUp_Result.jpg

原文地址