OpenCV图像金字塔
目标
在本教程中,您将学习如何:
- 使用OpenCV函数cv :: pyrUp和cv :: pyrDown对给定图像进行下采样或上采样。
理论
- 注意
- 下面的解释属于Bradski和Kaehler 的“ 学习OpenCV ”一书。
- 通常我们需要将图像转换成与原始图像不同的大小。为此,有两个可能的选择:升高图像(放大)或缩小(缩小)。
- 虽然有一个几何变换中的OpenCV函数-literally-调整图像大小(CV ::调整大小,我们将在以后的教程显示),在该部分中,我们分析第一使用图像金字塔,这是在广泛应用的视觉范围广泛的应用。
图像金字塔
- 图像金字塔是由单个原始图像产生的图像的集合,它们被连续下采样,直到达到一些所需的停止点。
- 有两种常见的图像金字塔:高斯金字塔:用于缩减图像拉普拉斯金字塔:用于从金字塔中较低的图像重建上采样图像(分辨率较低)
- 在本教程中,我们将使用高斯金字塔。
高斯金字塔
- 想象一下金字塔是一组层,层数越高,尺寸越小。
- 每个层从底部到顶部编号,因此层(i+1)(表示为Gi+1)小于层i(Gi)。
- 为了在高斯金字塔中产生层(i+1),我们做如下:
将Gi与高斯内核进行卷积:
删除每个偶数行和列。
- 您可以轻松注意到,所得到的图像将是其前身的四分之一。在输入图像(原始图像)上迭代该过程产生整个金字塔。G0
- 上面的过程对于缩小图像是有用的。如果我们想使它更大,怎么办?:填充零的列()0
- 首先,将图像大小增加到每个维度的两倍,即新的偶数行和
- 用与上述相同的内核(乘以4)执行卷积近似“丢失像素”的值
- 这两个过程(如上所述的下采样和上采样)由OpenCV函数cv :: pyrUp和cv :: pyrDown实现,我们将在下面的代码示例中看到:
- 注意
- 当我们减小图像的大小时,我们实际上丢失了图像的信息。
Code
本教程代码如下所示。您也可以从这里下载
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
Mat src, dst, tmp;
const char* window_name = "Pyramids Demo";
int main( void )
{
printf( "\n Zoom In-Out demo \n " );
printf( "------------------ \n" );
printf( " * [u] -> Zoom in \n" );
printf( " * [d] -> Zoom out \n" );
printf( " * [ESC] -> Close program \n \n" );
src = imread( "../data/chicky_512.png" ); // Loads the test image
if( src.empty() )
{ printf(" No data! -- Exiting the program \n");
return -1; }
tmp = src;
dst = tmp;
imshow( window_name, dst );
for(;;)
{
char c = (char)waitKey(0);
if( c == 27 )
{ break; }
if( c == 'u' )
{ pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
printf( "** Zoom In: Image x 2 \n" );
}
else if( c == 'd' )
{ pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
printf( "** Zoom Out: Image / 2 \n" );
}
imshow( window_name, dst );
tmp = dst;
}
return 0;
}
说明
我们来看一下程序的一般结构:
- 加载图像(在这种情况下,它在程序中定义,用户不必将其作为参数输入)
src = imread( "../data/chicky_512.png" ); // Loads the test image
if( src.empty() )
{ printf(" No data! -- Exiting the program \n");
return -1; }
- 创建一个Mat对象以存储操作的结果(dst),另一个用于保存时间结果(tmp)。
Mat src,dst,tmp;
/ * ... * /
tmp = src;
dst = tmp;
- 创建一个窗口来显示结果
imshow(window_name,dst);
执行无限循环等待用户输入。
for(;;)
{
char c = (char)waitKey(0);
if( c == 27 )
{ break; }
if( c == 'u' )
{ pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
printf( "** Zoom In: Image x 2 \n" );
}
else if( c == 'd' )
{ pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
printf( "** Zoom Out: Image / 2 \n" );
}
imshow( window_name, dst );
tmp = dst;
}
如果用户按ESC键退出我们的程序。此外,它有两个选择:
- 执行上采样(按'u')
if( c == 'u' ) { pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) ); printf( "** Zoom In: Image x 2 \n" ); }
- 我们使用函数cv :: pyrUp与三个参数:
- tmp:当前图像,它是用src原始图像初始化的。
- dst:目标图像(要显示在屏幕上,据说是输入图像的两倍)
- *大小(tmp.cols * 2,tmp.rows * 2)*:目的地大小。由于我们是上采样,cv :: pyrUp的大小要比输入图像大一倍(在这种情况下是tmp)。
- 执行下采样(按'd'后)
else if( c == 'd' ) { pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) ); printf( "** Zoom Out: Image / 2 \n" ); }
- 类似于cv :: pyrUp,我们使用三个参数的函数cv :: pyrDown:
- tmp:当前图像,它是用src原始图像初始化的。
- dst:目标图像(要显示在屏幕上,据说是输入图像的一半)
- 大小(tmp.cols / 2,tmp.rows / 2):目的地大小。由于我们是上采样,cv :: pyrDown预期输入图像的大小(在这种情况下为tmp)的一半。
- 请注意,输入图像的重要性可以除以二分之一(两个维度)。否则,将显示错误。
- 最后,我们使用显示的当前图像更新输入图像tmp,因此执行后续操作。
tmp = dst;
结果
- 在编译上面的代码之后,我们可以测试一下。该程序调用图像chicky_512.jpg自带的样品/数据文件夹中。请注意,此图像为512 × 512,因此下采样不会产生任何错误(512 = 29)。原始图像如下所示:
- 首先我们通过按“d” 应用两个连续的cv :: pyrDown操作。我们的产出是:
- 请注意,由于我们正在减少图像的大小,我们应该已经失去了一些解决方案。这是很明显的,我们应用cv :: pyrUp两次(通过按'u')。我们的产品现在是: