31.图像金字塔(pyrUp/pyrDown/高斯金字塔)---
本文作者:小嗷
微信公众号:aoxiaoji
吹比QQ群:736854977
image image image
说白了,图像金字塔就是用来进行图像缩放的,干的事情跟resize函数没两样,那我们还需要学它吗?我觉得有必要的额,因为在学习卷积神经网络中会遇到这个名词,所以都学一学吧,搞图形都绕不过他!
说说什么是图像金字塔。
在本篇中,您将学习:
使用OpenCV函数pyrUp()和pyrDown()对给定的图像进行下样或上样。
- 通常我们需要将一个图像转换成与它原来的大小不同的图像。为此,有两种可能的选择:
-
扩大图像
-
缩小图像
- 虽然OpenCV中有一个几何变换函数——字面上——调整图像大小(resize , 我们将在以后的篇章中展示),在这一节中,我们首先分析了图像金字塔的使用,它在大量的视觉应用中得到了广泛的应用。
本文你会找到以下问题的答案:
-
pyrUp()
-
pyrDown()
2.1 图像金字塔
图像金字塔是一个图像的集合,所有的图像都来自于一个原始的图像,它们被连续地向下采样,直到到达某个想要的停止点。
image一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。
其实非常好理解,如上图所示,我们将一层层的图像比喻为金字塔,层级越高,则图像尺寸越小,分辨率越低。
有两种常见的图像金字塔:
-
高斯金字塔:用于下采样图像
-
Laplacian金字塔:用金字塔较低的图像重建上采样的图像(分辨率较低)
在本篇中,我们将使用高斯金字塔。
2.2 高斯金字塔
- 把金字塔想象成一层一层,层次越高,尺寸越小。
-
每个层都从下到上编号,因此层(i+1)(表示为Gi+1)比层i (Gi)小。
-
为了在高斯金字塔中生成层(i+1),我们做以下工作:
image.gif -
删除所有偶数行和列
-
与高斯核的卷积(下图为高斯核):
您可以很容易地注意到,生成的图像的面积将恰好是其前身的四分之一。在输入图像G0(原始图像)上迭代此过程将生成整个金字塔。
如何理解生成的图像的面积将恰好是其前身的四分之一?(删除所有偶数行和列)
即:20/2=10,10/2=5
上面的过程对于降低图像的采样很有用。如果我们想让它变大呢?:填充0(0)的列
-
首先,在每个维度上将图像放大到原来的两倍,并使用新的偶数行。
-
与上面所示的相同内核执行卷积(乘以4)以近似“丢失像素”的值
这两个过程(如上所述的向下采样和向上采样)是由OpenCV函数pyrUp()和pyrDown()实现的,我们将在下面第4点的代码示例中看到:
注意:当我们缩小图像的尺寸时,我们实际上是在丢失图像的信息。
其实就是:N次循环【图像高斯完,删除所有偶数行和列,循环】
image3.1 pyrUp()
把一个图像放大,然后模糊它。
1void cv::pyrUp ( InputArray src,2 OutputArray dst,3 const Size & dstsize = Size(),4 int borderType = BORDER_DEFAULT 5 )
默认情况下,输出图像的大小被计算为size(src.cols2,src.rows2),但在任何情况下,应满足以下条件:
image函数执行高斯金字塔结构的向上采样步骤,虽然它实际上可以用来构建拉普拉斯金字塔。
-
首先,它通过注入零行和列对源映像进行向上采样,
-
然后将结果与pyrDown中相同的内核相乘4进行卷积
参数:
-
src:输入图片.
-
dst:输出图片. 它具有指定的大小和与src相同的类型.
-
dstsize:输出图像的大小.
-
borderType:int类型的borderType边界模式
borderType的API网址:
https://docs.opencv.org/master/d2/de8/groupcorearray.html#ga209f2f4869e304c82d07739337eae7c5
上采样步骤:
将图像在每个方向放大为原来的两倍,新增的行和列用0填充; 使用先前同样的内核(乘以4)与放大后的图像卷积,获得新增像素的近似值。
3.2 pyrDown()
模糊图像并向下采样。
1void cv::pyrDown ( InputArray src, 2 OutputArray dst, 3 const Size & dstsize = Size(), 4 int borderType = BORDER_DEFAULT 5 )
默认情况下,输出图像的大小被计算为大小((src.cols+1)/2,(src.row+1)/2),但无论如何,应该满足以下条件:
image如果width是偶数,那么必须dstsize.width是src.cols的2倍;
该函数执行高斯金字塔结构的下采样步骤。首先,它将源图像与内核进行卷积:
image将所有偶数行和列去除.
下采样将步骤:
-
对图像进行高斯内核卷积
-
将所有偶数行和列去除
上、下采样都存在一个严重的问题,那就是图像变模糊了,因为缩放的过程中发生了信息丢失的问题。要解决这个问题,就得看拉普拉斯金字塔了(下篇在讲)。
image本篇文章的代码如下所示。
1#include "iostream" 2#include "opencv2/imgproc.hpp" 3#include "opencv2/imgcodecs.hpp" 4#include "opencv2/highgui.hpp" 5 6using namespace std; 7using namespace cv; 8 9const char* window_name = "Pyramids Demo";1011int main(int argc, char** argv)12{13 cout << "\n Zoom In-Out demo \n "14 "------------------ \n"15 " * [i] -> Zoom in \n"16 " * [o] -> Zoom out \n"17 " * [ESC] -> Close program \n" << endl;1819 const char* filename = argc >= 2 ? argv[1] : "D://6.30pian.jpg";2021 // Loads an image22 Mat src = imread(filename);2324 // Check if image is loaded fine25 if (src.empty()) {26 printf(" Error opening image\n");27 printf(" Program Arguments: [image_name -- default ..D://6.30pian.jpg] \n");28 return -1;29 }3031 for (;;)32 {33 imshow(window_name, src);34 char c = (char)waitKey(0);3536 if (c == 27)37 {38 break;39 }40 else if (c == 'i')41 {42 pyrUp(src, src, Size(src.cols * 2, src.rows * 2));43 printf("** Zoom In: Image x 2 \n");44 }45 else if (c == 'o')46 {47 pyrDown(src, src, Size(src.cols / 2, src.rows / 2));48 printf("** Zoom Out: Image / 2 \n");49 }50 }5152 return 0;53}
源图:
image效果图:(按i放大,o就缩小)
- 放大
- 缩小
解释一下:
让我们来看看这个项目的总体结构:
加载一个图像
1const char* filename = argc >=2 ? argv[1] : "D://6.30pian.jpg";2// Loads an image3Mat src = imread( filename );4// Check if image is loaded fine5if(src.empty()){6 printf(" Error opening image\n");7 printf(" Program Arguments: [image_name -- default D://6.30pian.jpg] \n");8 return -1;9}
创建窗体
imshow( window_name, src );
循环
1for(;;) 2{ 3 imshow( window_name, src ); 4 char c = (char)waitKey(0); 5 if( c == 27 ) 6 { break; } 7 else if( c == 'i' ) 8 { pyrUp( src, src, Size( src.cols*2, src.rows*2 ) ); 9 printf( "** Zoom In: Image x 2 \n" );10 }11 else if( c == 'o' )12 { pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );13 printf( "** Zoom Out: Image / 2 \n" );14 }15}
执行一个等待用户输入的无限循环。如果用户按ESC,程序将退出。此外,它还有两个选择:
-
执行向上采样-放大(按i后)
-
我们使用了函数pyrUp()的三个参数:
-
src:当前图像和目标图像(显示在屏幕上,应该是输入图像的两倍)
-
Size(tmp.cols2, tmp.rows2 ):目标大小。由于我们正在进行向上采样,所以pyrUp()期望大小比输入图像(在本例中是src)大一倍。
1 else if( c == 'i' )2 { pyrUp( src, src, Size( src.cols*2, src.rows*2 ) );3 printf( "** Zoom In: Image x 2 \n" );4 }
-
执行向下采样-缩小(按'o')
-
我们使用了函数pyrDown()的三个参数:
-
src:当前图像和目标图像(显示在屏幕上,大概是输入图像的一半)
-
Size(tmp.cols2, tmp.rows2 ):目标大小。由于我们正在进行向上采样,所以pyrUp()期望大小是输入图像的一半大小(在本例中为src)。
1 else if( c == 'o' )2 { pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );3 printf( "** Zoom Out: Image / 2 \n" );4 }
小嗷提醒一下注意:
输入图像必须要可以除以二。否则,会报错。
(10/2,但是5/2。就会报错。当然,大家可以写一个判断是否是奇数)
image-
本人是抱着玩一玩的心态,学习opencv(其实深度学习没有外界说的这么高深,小嗷是白板,而且有工作在身并且于代码无关)
-
大家可以把我的数学水平想象成初中水平,毕竟小嗷既不是代码靠吃饭又不是靠数学吃饭,毕业N年
-
写文章主要是为了后人少走点弯路,多交点朋友,一起学习
-
如果有好的图像识别群拉我进去QQ:631821577
-
就我一个白板,最后还是成的,你们别怕,慢慢来吧
分享可以无数次,转载成自己文章QQ邮箱通知一下,未经授权请勿转载。
邮箱:631821577@qq.com
QQ群:736854977
有什么疑问公众号提问,下班或者周六日回答,ths
感言
嗷嗷嗷~~~,喜欢就推荐一下好友,下篇就拉普拉斯吧。
推荐文章:
18.图像处理之线性滤波(空间域/高低频/方框/均值/高斯) --- OpenCV从零开始到图像(人脸 + 物体)识别系列
20.方差/标准差/数学期望/正态分布/高斯函数(数学篇)--- OpenCV从零开始到图像(人脸 + 物体)识别系列
代码链接:
https://pan.baidu.com/s/1hwPOGQ_xCoYCDwGNOTwdOQ
密码: w6h9