1. 目标
在本教程中,将学习如何:
2. 理论
-
笔记
下面的解释来自于Bradski 和 Kaehler的《Learning OpenCV 》一书。
-
以下两种做法可以将图像转换为不同的大小:
- 上采样图像(放大)
- 下采样图像(缩小)
-
虽然OpenCV 中有一个几何变换函数可以调整图像大小( resize,将在以后的文章中展示),但在本节中,只会分析图像金字塔的使用,它被广泛应用于视觉应用。
图像金字塔
-
图像金字塔是图像的集合——所有图像都来自单个原始图像——这些图像被连续下采样,直到达到某个所需的停止点。
-
有两种常见的图像金字塔:
- 高斯金字塔: 用于对图像进行下采样
- 拉普拉斯金字塔: 用于从底部金字塔的图像(分辨率较低)重建上采样图像
-
在本文中,将使用高斯金字塔。
高斯金字塔
-
将金字塔想象成一组层,其中层越高,尺寸越小。
-
层都是从下到上编号的,所以层(i+1)(小于层i(即).
-
在高斯金字塔中为了生成 层(i+1),需要执行以下操作:
- 将与高斯核卷积:
\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.png
samples/data
。图像大小是512×512,因此下采样不会产生任何错误()。原图如下所示: -
首先,通过按“d”应用两个连续的pyrDown()操作。 输出是:
-
由于正在减小图像的大小,会丢失一些分辨率。在应用pyrUp() 两次(通过按 'u')后很明显。现在输出是: