OpenCV8-图像金字塔
1.图像金字塔
图像"金字塔"是通过多个分辨率表示图像的一种有效且简单的结构。一个图像"金字塔"是一系列以金字塔形状排列、分辨率逐步降低的图像集合。图像"金字塔"的底部是待处理图像的高分辨率表示,顶部是低分辨率表示。本文中将介绍图像"金字塔"中比较著名的两种:高斯"金字塔"和拉普拉斯"金字塔"。
2.高斯金字塔
高斯"金字塔"是指通过下采样不断地将图像的尺寸缩小,进而在图像"金字塔"中包含多个尺寸的图像。一般情况下,高斯"金字塔"的底层为图像的原图,每往上一层就会通过下采样缩小一次图像的尺寸,通常情况下,尺寸会缩小为原来的一半,但是如果有特俗需求,缩小的尺寸也可以根据实际情况进行调整。由于每次图像的尺寸都缩小为原来的一半,图像尺寸缩小的速度非常快,因此常见的高斯"金字塔"的层数为3~6层。
OpenCV中提供了pyrDown函数专门用于图像的下采样计算,以便构建图像的高斯"金字塔":
cpp
void pyrDown(
InputArray src, // 输入待下采样的图像
OutputArray dst, // 输出下采样后的图像
const Size& dstsize = Size(), // 输出图像尺寸,可以默认
int borderType = BORDER_DEFAULT // 像素边界外推方法的标志,图像几何变换一文中-仿射变换有说明
);
该函数用于实现图像模糊并对其进行下采样,默认标志下,函数输出图像的尺寸为输入图像尺寸的一半,但是也可以通过dstsize参数来设置输出图像的大小,需要注意的是,无论输出尺寸为多少,应该满足下面公式的条件:
{ ∣ 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 l s ∣ ≤ 2 \left\{ \begin{matrix} | dstsize.width2 - src.cols | \leq 2 \\ | dstsize.height2 - src.rols | \leq 2 \\ \end{matrix} \right. {∣dstsize.width2−src.cols∣≤2∣dstsize.height2−src.rols∣≤2
该函数首先将原始图像与内核矩阵进行卷积,之后通过不适用偶数行和列的方式对图像进行下采样,最终实现尺寸缩小的下采样,内核矩阵如下:
k = 1 256 [ 1 4 6 4 1 4 6 24 6 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 & 6 & 24 & 6 & 4 \\ 6 & 24 & 36 & 24 & 6 \\ 4 & 16 & 24 & 16 & 4 \\ 1 & 4 & 6 & 4 & 1 \\ \end{bmatrix} k=2561 14641462416462436246462416414641
该函数的功能与resize函数将图像尺寸缩小一样,但是使用的内部算法不同。
3.拉普拉斯金字塔
拉普拉斯"金字塔"是通过上层小尺寸的图像构建下层大尺寸的图像。拉普拉斯"金字塔"具有预测残差的作用,需要与高斯"金字塔"一起使用。
假设我们有了一个高斯图像"金字塔",对于其中的第i层图像(高斯"金字塔"最下面为第0层),首先通过下采样得到一个尺寸缩小一半图像,即高斯"金字塔"中的第i+1层或者不在高斯"金字塔"中,之后对这福图像进行上采样,将图像尺寸恢复到第i层图像的大小,最后求取高斯"金字塔"第i层图像与经过上采样后的图像的残差图像,这个残差图像就是拉普拉斯"金字塔"的第i层图像。
对于上采样操作OpenCV提供了pyrUp函数:
cpp
void pyrUp(
InputArray src,
OutputArray dst,
const Size& dstsize = Size(),
int borderType = BORDER_DEFAULT
);
// 函数参数同pyrDown
4.Demo
cpp
#include <opencv2\opencv.hpp>
#include <opencv2/core/utils/logger.hpp> // debug no log
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
Mat img = imread("lena.png");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
vector<Mat> Gauss, Lap; //高斯金字塔和拉普拉斯金字塔
int level = 3; //高斯金字塔下采样次数
Gauss.push_back(img); //将原图作为高斯金字塔的第0层
//构建高斯金字塔
for (int i = 0; i < level; i++)
{
Mat gauss;
pyrDown(Gauss[i], gauss); //下采样
Gauss.push_back(gauss);
}
//构建拉普拉斯金字塔
for (int i = Gauss.size() - 1; i > 0; i--)
{
Mat lap, upGauss;
if (i == Gauss.size() - 1) //如果是高斯金字塔中的最上面一层图像
{
Mat down;
pyrDown(Gauss[i], down); //上采样
pyrUp(down, upGauss);
lap = Gauss[i] - upGauss;
Lap.push_back(lap);
}
pyrUp(Gauss[i], upGauss);
lap = Gauss[i - 1] - upGauss;
Lap.push_back(lap);
}
//查看两个金字塔中的图像
for (int i = 0; i < Gauss.size(); i++)
{
string name = to_string(i);
imshow("G" + name, Gauss[i]);
imshow("L" + name, Lap[i]);
}
waitKey(0);
return 0;
}