图像“金字塔”
图像“金字塔”是通过多个分辨率表示图像的一种有效且简单的结构
一个图像“金字塔”一系列以金字塔排列、分辨率逐步降低的图像集合
图像“金字塔”的底部是待处理图像的高分辨率,顶部是低分辨率的表示
图像“金字塔”种比较著名的两种——高斯“金字塔”和拉普拉斯“金字塔”
高斯金字塔pyrDowm()
高斯“金字塔”的底层为图像的原图,每往上一层就会通过下采样缩小一次图像的尺寸
通常情况下,尺寸会缩小为原来的一半,有特殊需求,缩小的尺寸也可以根据时间情况进行调整
OpenCV4中提供了pyrDowm()函数专门用于图像的下采样计算
pyrDowm()函数原型:
void pyrDowm(InputArray src,
OutputArray dst,
const Size& dstsize = Size(),
int borderType = BORDER_DEFAULT
)
- src:输入待下采样的图像
- dst:输出下采样后的图像,图像的尺寸可以指定,但是数据类型和通道数与src相同
- dstsize:输出图像尺寸,可以默认
- borderType:像素边界外推方法的标志
边界外推方法标志
| 方法标志 | 简记 | 作用 | ||
|---|---|---|---|---|
| BORDER_CONSTANT | 0 | 用特定值填充,如iiiiii\ | abcdefgh\ | iiiiii |
| BORDER_REPLICATE | 1 | 两端复制填充,如aaaaaa\ | abcdefgh\ | bbbbbb |
| BORDER_REFLECT | 2 | 倒序填充,如fedcba\ | abcdefgh\ | hgfedcb |
| BORDER_WRAP | 3 | 正序填充,如cdefgh\ | abcdefgh\ | abcdefg |
| BORDER_REFLECT_101 | 4 | 不含边界值的倒序填充,如gfedcb\ | abcdefgh\ | gfedcba |
| BORDER_TRANSPARENT | 5 | 随机填充,uvwxyz\ | abcdefgh\ | ijklmno |
| BORDER_REFLECT101 | 4 | 与 BORDER_REFLECT_101 相同 | ||
| BORDER_DEFAULT | 4 | 与 BORDER_REFLECT_101 相同 | ||
| BORDER_ISOLATED | 16 | 不关心感兴趣区域之外的部分 |
该函数的功能与resize()函数将图像尺寸缩小一样,但是使用的内部算法不同
拉普拉斯“金字塔”pyrUp()
拉普拉斯“金字塔”与高斯“金字塔”正好相反,高斯“金字塔"通过底层图像构建上层图像
而拉普拉斯”金字塔“是通过上层小尺寸的图像构建下层大尺寸的图像
拉普拉斯”金字塔“具有预测残差的作用,需要与高斯”金字塔“联合使用
高斯”金字塔“第i-1层图像的下采样与高斯”金字塔“第i层上采样的差值图像作为拉普拉斯”金字塔“第i层图像
对于上采样操作,OpenCV4中提供pyrUp()函数
pyrUp()函数原型:
void pyrUp(InputArray src,
OutputArray dst,
const Szie& dstsize = Szie()
int borderType = BORDER_DEFAULT
)
- src:输入待下采样的图像
- dst:输出下采样后的图像,图像的尺寸可以指定,但是数据类型和通道数与src相同
- dstsize:输出图像尺寸,可以默认
- borderType:像素边界外推方法的标志
完成高斯”金字塔“的构建后,从上到下取出高斯”金字塔“中的每一次图像
如果是高斯”金字塔“中最上面的一层,则先下采样再上采样,将下采样与上采样的差值作为拉普拉斯”金字塔"的最上层一层
如果从高斯”金字塔“取出的第i层不是最上层,则直接对高斯金字塔中第i+1层图像进行上采样,计算与高斯”金字塔“第i层图像差值
将差值图像作为高斯”金字塔“的第i层
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("E:\\CLion\\opencv_xin\\SG\\SG_0.jpg");
if (img.empty()) {
cout << "Error" << 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,Size(Gauss[i].rows/2,Gauss[i].cols/2)); //下采样
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,Size(Gauss[i].rows,Gauss[i].cols));
lap = Gauss[i] - upGauss;
Lap.push_back(lap);
}
pyrUp(Gauss[i],upGauss,Size(Gauss[i-1].rows,Gauss[i-1].cols));
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;
}
注意:一定要有相同的尺寸和数据类型