Goal
在本教程中,您将学习如何:
使用 OpenCV 函数 pyrUp() 和 pyrDown() 对给定图像进行下采样或上采样。
Theory
笔记
下面的解释属于 Bradski 和 Kaehler 的《Learning OpenCV》一书。
通常我们需要将图像转换为与原始大小不同的大小。 为此,有两种可能的选择:
- Upsize the image (zoom in) or 放大图像(放大)或
- Downsize it (zoom out). 缩小它(缩小)。
尽管 OpenCV 中有一个几何变换函数——字面意思——调整图像大小(resize,我们将在以后的教程中展示),但在本节中,我们首先分析Image Pyramids图像金字塔的使用,它广泛应用于各种 视觉应用。
Image Pyramid
图像金字塔是图像的集合——所有图像都来自单个原始图像——这些图像被连续下采样,直到达到某个所需的停止点。
有两种常见的图像金字塔:
Gaussian pyramid:高斯金字塔:用于对图像进行下采样
Laplacian pyramid: 拉普拉斯金字塔:用于从金字塔较低的图像(分辨率较低)重建上采样图像
在本教程中,我们将使用高斯金字塔。
Gaussian Pyramid
- 将金字塔想象成一组层,其中层越高,尺寸越小。
- 每一层都是从下到上编号的,所以第(i+1)层(记为Gi+1)小于第i层(Gi)。
- 为了在高斯金字塔中生成第 (i+1) 层,我们执行以下操作:
用高斯核卷积 Gi:
删除每个偶数行和列。
- 您可以很容易地注意到,生成的图像将恰好是其前身的四分之一。 在输入图像 G0(原始图像)上迭代这个过程会产生整个金字塔。
- 上述过程对于对图像进行下采样很有用。 如果我们想让它更大怎么办?:用zeros ( 0)填充列
首先,将图像在每个维度上放大到原始图像的两倍,新的偶数行和.
使用上面显示的相同内核执行卷积(乘以 4)以近似“缺失像素”的值.
- First, upsize the image to twice the original in each dimension, with the new even rows and
- Perform a convolution with the same kernel shown above (multiplied by 4) to approximate the values of the "missing pixels"
- 这两个过程(如上所述的下采样和上采样)由 OpenCV 函数 pyrUp() 和 pyrDown() 实现,我们将在下面的代码示例中看到:
笔记
当我们减小图像的大小时,我们实际上是在丢失图像的信息。
Code
本教程代码如下所示。
你也可以从这里下载raw.githubusercontent.com
/**
* @file Pyramids.cpp
* @brief Sample code of image pyramids (pyrDown and pyrUp)
*图像金字塔示例代码(pyrDown和pyrUp)
* @author OpenCV team
*/
#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";
/**
* @function main
*/
int main( int argc, char** argv )
{
/// General instructions
cout << "\n Zoom In-Out demo \n "
"------------------ \n"
" * [i] -> Zoom in \n"
" * [o] -> Zoom out \n"
" * [ESC] -> Close program \n" << endl;
//![load]
const char* filename = argc >=2 ? argv[1] : "chicky_512.png";
// 加载图像
Mat src = imread( samples::findFile( filename ) );
// 检查图像是否加载正常
if(src.empty()){
printf(" Error opening image\n");
printf(" Program Arguments: [image_name -- default chicky_512.png] \n");
return EXIT_FAILURE;
}
//![load]
//![loop]
for(;;)
{
//![显示图像]
imshow( window_name, src );
//![show_image]
char c = (char)waitKey(0);
if( c == 27 )//ESC
{ break; }
//![pyrup]上采样
else if( c == 'i' )
{ pyrUp( src, src, Size( src.cols*2, src.rows*2 ) );
printf( "** Zoom In: Image x 2 \n" );
}
//![pyrup]
//![pyrdown]下采样
else if( c == 'o' )
{ pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
printf( "** Zoom Out: Image / 2 \n" );
}
//![pyrdown]
}
//![loop]
return EXIT_SUCCESS;
}
Explanation
让我们检查一下程序的一般结构:
Load an image
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;
}
Create window
imshow( window_name, src );
Loop
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,我们的程序就会退出。 此外,它有两种选择:
Perform upsampling - Zoom 'i'n (after pressing 'i')
执行上采样 - 缩放“i”(按“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( "** Zoom In: Image x 2 \n" );
}
Perform downsampling - Zoom 'o'ut (after pressing 'o')
执行下采样 - 缩小“o”(按“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( "** Zoom Out: Image / 2 \n" );
}
请注意,输入图像可以除被2整除(在两个维度上)是很重要的。 否则,将显示错误。
Results
该程序默认调用位于 samples/data 文件夹中的图像 chiky_512.png。 请注意,此图像为 512×512,因此下采样不会产生任何错误。 原图如下所示:
首先,我们通过按“d”应用两个连续的 pyrDown() 操作。 我们的输出是:
请注意,由于我们正在减小图像的大小,我们应该会丢失一些分辨率。 这在我们应用 pyrUp() 两次(通过按 'u')后很明显。 我们的输出现在是: