【OpenCV C++20 学习笔记】图像缩放-高斯金字塔

原理

在图像处理中,经常需要将图像转化成不同的尺寸,即放大或缩小。
除了直接用resize()函数重新设置图片尺寸,另一种常用的方法就是“图像金字塔”。
图像金字塔是从底层的原始图像开始,通过层层放大叠加而成的金字塔形状的图像集合。
主要有两种图像金字塔:

  • 高斯金字塔:常用来生成图片的缩小版本
  • 拉普拉斯金字塔:常用来将图片的缩小版本放大

高斯金字塔

用图片来表示高斯金字塔会更形象:
高斯金字塔
图层越高,图像越小。
图像的编号又下往上依次增大。从下往上数第 i + 1 i+1 i+1个图层,记为 G i G_{i} Gi;则第 i + 2 i+2 i+2个图层 G i + 1 G_{i+1} Gi+1 G i G_i Gi小。
要生成 G i + 1 G_{i+1} Gi+1就要对 G i G_i Gi进行如下操作:

  1. 用高斯卷积核 K K K G i G_i Gi进行卷积运算;
    K = 1 256 [ 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 ] K=\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} K=2561 1464141624164624362464162416414641
  2. 移除所有的偶数行和偶数列
    很显然,最后的结果会变成原来图片的1/4。将这个操作迭代地运用在原图 G 0 G_0 G0上,就会形成上图所示的金字塔。
    其实高斯金字塔方法也可以用来放大图片。只要进行以下操作:
  3. 将行和列都扩大到原来的2倍;
  4. 将上面的卷积核 K K K乘以4之后近似地模拟出填充值来填充扩大出来的行和列

注意:缩小图片实际上丢失了一部分原图的信息。

代码实现

这里使用的图片是OpenCV安装文件中自带的示例图,其路径为:"...\opencv\sources\samples\data\chicky_512.png"
chick_512.png

放大

在OpenCV中可以pyrUp()函数来进行高斯金字塔中的放大操作。其函数原型如下:

void cv::pyrUp(InputArray	src,
			OutputArray		dst,
			const Size& 	dstsize = size(),
			int				borderType = BORDER_DEFAULT)

第1个和第2个参数不用介绍了,分别是输入矩阵和输出矩阵。
第3个参数为放大后的尺寸,默认为Size(src.cols*2, src.rows*2),即行和列都放大为原来的2倍。但是如果用户要自定义放大倍数的话,需要满足以下条件:
{ ∣ d s t s i z e . w i d t h − s r c . c o l s ∗ 2 ∣ ≤ ( d s t s i z e . w i d t h m o d 2 ) ∣ d s t s i z e . h e i g h t − s r c . r o w s ∗ 2 ∣ ≤ ( d s t s i z e . h e i g h t m o d 2 ) \begin{cases} |dstsize.width - src.cols*2| \leq (dstsize.width mod 2) \\ |dstsize.height - src.rows*2| \leq (dstsize.height mod 2) \end{cases} {dstsize.widthsrc.cols2∣(dstsize.widthmod2)dstsize.heightsrc.rows2∣(dstsize.heightmod2)
乍一看有点复杂,我们来仔细分析一下:
首先看两个不等式的右边,因为图片的宽和高一般都是整数,所以无论是宽还是高,除以2的余数都只有两种情况:0或1。

  • 不等式右边为0:说明放大后的图片的宽或高是偶数,且等式左边必须小于等于0;又因为不等式左边为一个绝对值的结果,那么只有一种可能——不等式左边等于0,即 d s t s i z e . w i d t h = s r c . c o l s ∗ 2 dstsize.width = src.cols*2 dstsize.width=src.cols2 d s t s i z e . h e i g h t = s r c . r o w s ∗ 2 dstsize.height = src.rows*2 dstsize.height=src.rows2,也就是这个参数的默认情况。
  • 不等式右边为1:说明放大的图片的宽或高是奇数,且等式左边必须小于等于1;因为奇数减偶数只能等于奇数,所以等式左边的结果只能为1,即只能是 s r c . c o l s ∗ 2 + 1 src.cols * 2 + 1 src.cols2+1或者 s r c . r o w s ∗ 2 + 1 src.rows*2+1 src.rows2+1

综上所述,第3个参数只能有两种:

  1. 宽和高都放大为原来的2倍,即默认情况
  2. 宽和高都放大为原来的2倍加1。

为什么有这种限制?答案在最后一节。

第4个参数基本使用默认值,先不用管它。

我们看看宽和高都放大为原来的宽和高的2倍的具体实现:

pyrUp(src,							//原图
	up_dst,							//输出图
	Size(src.cols*2, src.rows*2));	//放大后的尺寸:原图的2*2

一次放大后的效果:
放大后的效果

缩小

类似的,可以使用pryDown()函数来缩小图片。这个函数的参数与pryUp()相同,但第3个参数,即缩小后的尺寸的具体条件不一样。
pryDown()中,第3个参数dstsize默认值为Size((src.cols+1)/2, (src.rows+1)/2),即原图的宽和高加1的一半。如果用户要自定义缩小尺寸,需要满足以下条件:
{ ∣ d s t s i z e . w i d t h ∗ 2 − s r c . c o l s ∣ ≤ 2 ∣ d s t s i z e . h e i g h t ∗ 2 − s r c . r o w s ∣ ≤ 2 \begin{cases} |dstsize.width*2 - src.cols| \leq 2 \\ |dstsize.height*2 - src.rows| \leq 2 \end{cases} {dstsize.width2src.cols2dstsize.height2src.rows2
这也就是说缩小之后的图片的宽和高的2倍不能与原来的宽高相差2以上,即缩小后的图片的尺寸只有这些选择:

  1. 原图的宽高的一半,此时不等式左边就是0
  2. 原图的宽高加1的一半,即默认情况,不等式左边为1
  3. 原图的宽高加2的一半,此时不等式左边为2

为什么有这种限制?答案在最后一节。

第1中情况的具体用法如下:

pryDown(src,							//原图
		down_dst,						//输出图
		Size( src.cols/2, src.rows/2));	//缩小后的尺寸:原图的(1/2)*(1/2)

原图缩小一次的效果如下:
缩小后的图片

形成金字塔

要形成金字塔不能只进行单次缩放的操作,需要进行迭代。
正是因为要迭代,所以前面才会对pyrUp()pyrDown()函数的第3个参数有那么严格的限制。如果缩放的尺寸没有限制,那可以1次迭代就放大100或缩小100倍,那很有可能就没有进行第2次迭代的必要了。要形成有意义的金字塔,缩放的比例必须要控制在一个相对较小的范围内,而这两个函数都将比例限制在了2或1/2左右。

如果我们将迭代次数限定为5次,我们可以进行这样的缩小图片的迭代:

#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace std;

int main() {
	Mat src{ imread("chicky_512.png") };

	//缩小迭代
	String winName{ "缩小" };
	for (int i{ 0 }; i < 5; i++) {
		imshow(winName+to_string(i), src);
		pyrDown(src,
			src,
			Size(src.cols / 2, src.rows / 2));
	}
	waitKey(0);
}

结果如下:
高斯金字塔

  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值