OpenCV
开源计算机视觉库
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
图像金字塔

上一教程: 使用形态学运算提取水平和垂直线
下一教程: 基本的阈值操作

原作者Ana Huamán
兼容性OpenCV >= 3.0

目标

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

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

理论

注意
下面的解释来自 Bradski 和 Kaehler 编写的 Learning OpenCV 一书。
  • 通常我们需要将图像转换为与其原始大小不同的尺寸。为此,有两种可能的选择:
    1. 放大图像(放大)或
    2. 缩小图像(缩小)。
  • 虽然 OpenCV 中有一个几何变换函数可以真正地调整图像大小(resize,我们将在以后的教程中介绍),但在本节中,我们首先分析图像金字塔的使用,图像金字塔广泛应用于各种视觉应用中。

图像金字塔

  • 图像金字塔是由一系列图像组成的集合——所有图像都源自单个原始图像——这些图像被连续降采样,直到达到某个所需的停止点。
  • 图像金字塔有两种常见的类型:
    • 高斯金字塔:用于图像降采样
    • 拉普拉斯金字塔:用于从金字塔中较低层(分辨率较低)的图像重建上采样图像
  • 在本教程中,我们将使用高斯金字塔

高斯金字塔

  • 想象一下金字塔是一组层,层级越高,尺寸越小。
  • 每一层都从下到上编号,因此第i+1层(表示为Gi+1)小于第i层(Gi)。
  • 为了生成高斯金字塔中的第i+1层,我们执行以下操作:

    • 用高斯核卷积Gi

    1256[1464141624164624362464162416414641]

    • 移除所有偶数行和偶数列。
  • 您可以很容易地注意到,生成的图像面积将恰好是其前驱图像的四分之一。对输入图像G0(原始图像)迭代此过程会生成整个金字塔。
  • 上述过程用于图像降采样。如果我们想让图像更大呢?用零(0)填充偶数行和偶数列
    • 首先,将图像在每个维度上放大到原始图像的两倍,并用新的偶数行和偶数列填充
    • 用上面显示的相同内核(乘以 4)进行卷积,以近似“缺失像素”的值
  • 这两个过程(如上所述的降采样和上采样)由 OpenCV 函数 pyrUp()pyrDown() 实现,我们将在下面的代码示例中看到。
注意
当我们减小图像大小时,实际上是丢失了图像信息。

代码

本教程代码如下所示。

您也可以从此处下载:此处

#include "iostream"
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] -> 放大 \n"
" * [o] -> 缩小 \n"
" * [ESC] -> 关闭程序 \n" << endl;
const char* filename = argc >=2 ? argv[1] : "chicky_512.png";
// 加载图像
Mat src = imread( samples::findFile( filename ) );
// 检查图像是否加载成功
if(src.empty()){
printf(" 打开图像出错\n");
printf(" 程序参数:[图像名称 -- 默认 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( "** 放大:图像 x 2 \n" );
}
else if( c == 'o' )
{ pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
printf( "** 缩小:图像 / 2 \n" );
}
}
return EXIT_SUCCESS;
}
n维稠密数组类
定义 mat.hpp:829
int cols
定义 mat.hpp:2155
bool empty() const
如果数组没有元素,则返回 true。
int rows
行和列的数量,当矩阵的维度超过 2 时为 (-1, -1)
定义 mat.hpp:2155
用于指定图像或矩形大小的模板类。
定义 types.hpp:335
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
定义 core.hpp:107
STL 命名空间。

说明

让我们检查程序的总体结构

加载图像

const char* filename = argc >=2 ? argv[1] : "chicky_512.png";
// 加载图像
Mat src = imread( samples::findFile( filename ) );
// 检查图像是否加载成功
if(src.empty()){
printf(" 打开图像出错\n");
printf(" 程序参数:[图像名称 -- 默认 chicky_512.png] \n");
return EXIT_FAILURE;
}

创建窗口

imshow( window_name, src );

循环

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( "** 放大:图像 x 2 \n" );
}
else if( c == 'o' )
{ pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
printf( "** 缩小:图像 / 2 \n" );
}
}

执行无限循环,等待用户输入。如果用户按下 **ESC**,程序将退出。此外,它还有两个选项

  • 执行上采样 - 放大 (按下 'i' 后)

    我们使用具有三个参数的函数 **pyrUp()**

    • src:当前和目标图像(将在屏幕上显示,大概是输入图像的两倍)
    • Size( tmp.cols*2, tmp.rows*2 ):目标大小。由于我们正在进行上采样,**pyrUp()** 期望大小是输入图像(在本例中为 src)的两倍。
else if( c == 'i' )
{ pyrUp( src, src, Size( src.cols*2, src.rows*2 ) );
printf( "** 放大:图像 x 2 \n" );
}
  • 执行下采样 - 缩小 (按下 'o' 后)

    我们使用具有三个参数的函数 **pyrDown()**(与 **pyrUp()** 类似)

    • src:当前和目标图像(将在屏幕上显示,大概是输入图像的一半)
    • Size( tmp.cols/2, tmp.rows/2 ):目标尺寸。由于我们进行的是下采样,因此pyrDown() 函数期望目标尺寸为输入图像(此处为src)尺寸的一半。
else if( c == 'o' )
{ pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
printf( "** 缩小:图像 / 2 \n" );
}

请注意,输入图像的尺寸必须能够被2整除(长和宽都必须是2的倍数)。否则,程序会报错。

结果

  • 程序默认调用samples/data文件夹中的图像chicky_512.png。请注意,该图像尺寸为512×512,因此下采样不会产生任何错误(512=29)。原始图像如下所示
  • 首先,我们通过按下'd'键连续执行两次pyrDown()操作。输出结果为:
  • 请注意,由于图像尺寸减小,我们应该会损失一些分辨率。在连续两次应用pyrUp()操作(通过按下'u'键)后,这一点就显而易见了。现在的输出结果为: