6.6 图像金字塔与图片尺寸缩放
6.6.1 图像金字塔
1.图像金字塔是图像中多尺度表达的一种,主要用于图像分割,是一种以多分辨率解释图像的结构,通过梯次向下采样获得分辨率逐步降低的图象集合
2.分类:
(1)高斯金字塔(Gaussianpyramid):向下采样
(2)拉普拉斯金字塔(Laplacianpyramid):从金字塔底层图像重建上层未采样图像,在数字图像处理中即是预测残差,可以对图像进行最大程度地还原,配合高斯金字塔一起使用
3.涉及函数:
pyrUp函数和pyrDown函数,2个函数在imgproc的Image Filtering子模块中,pyrUp和pyrDown不互逆
6.6.2 高斯金字塔
1.高斯金字塔通过高斯平滑和亚采样进行向下采样,包含了一系列低通滤波器,截止频率从上一层到下一层以因子2逐渐增加
2.方法:
对金字塔第i层Gi进行高斯内核卷积,然后删除所有偶数行和偶数列,新得到图像Gi+1面积会变为原图像的1/4,可对输入图像迭代产生金字塔
3.pyrDown函数:
向下采样(图像尺寸减半),生成下一级图像,是会丢失信息的函数
4.函数原型:
void pyrDown(InputArray src, OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT)
5.参数说明:
(1)输入图像
(2)目标图像
(3)输出图像大小,默认值Size(),默认时由Size((src.cols+1)/2,(src.row+1)/2)计算,需满足:
|dstsize.width*2-src.cols|<=2
|dstsize.height*2-src.rows|<=2
(4)边界模式
6.调用示例:
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
//载入原始图
Mat srcImage = imread("love.jpg");
Mat dstImageDown;
//显示原始图
namedWindow("【原始图】");
imshow("【原始图】", srcImage);
//向下取样操作
pyrDown(srcImage, dstImageDown, Size(srcImage.cols / 2, srcImage.rows / 2));
//显示效果图
namedWindow("【向下取样效果图】");
imshow("【向下取样效果图】", dstImageDown);
waitKey(0);
return 0;
}
6.6.3 拉普拉斯金字塔
1.数学定义:
式中,Gi表示第i层图像,UP()操作时将原图像中位置为(x,y)的像素映射到目标图像的(2x+1,2y+1)位置,即在向上取样,符号表示卷积,
表示5*5的高斯内核
2.方法:
将现有图像每个维度都放大两倍,新增行和列(偶数行和列)以0填充,然后使用先前同样的内核(乘以4)与放大后的图像卷积,来估计“丢失”像素的近似值
3.pyrUp函数:向上采样(图像尺寸加倍),利用函数进行拉普拉斯运算:
4.函数原型:
void pyrUp(InputArray src, OutputArray dst, const Size& dstsize=Size(), int borserType=BORDER_DEFAULT)
5.参数说明:
(1)输入图像
(2)目标图像
(3)输出图像大小,默认值Size(),默认时由Size(src.cols*2,src.row*2)计算,需满足:
|dstsize.width-src.cols*2|<=(dstsize.width mod2)
|dstsize.height-src.rows*2|<=(dstsize.height mod2)
(4)边界模式
6. 调用示例:
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
//载入原始图
Mat srcImage = imread("love.jpg");
Mat dstImageUp;
//显示原始图
namedWindow("【原始图】");
imshow("【原始图】", srcImage);
//向上取样操作
pyrUp(srcImage, dstImageUp, Size(srcImage.cols * 2, srcImage.rows * 2));
//显示效果图
namedWindow("【向上取样效果图】");
imshow("【向上取样效果图】", dstImageUp);
waitKey(0);
return 0;
}
6.6.4 尺寸调整函数:resize()函数
函数resize函数在imgproc模块的Geometric Image Transformations子模块中
1.函数原型:
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)
2.参数说明:
(1)输入图像
(2)目标图像
(3)输出图像大小,等于0时由下式计算图像大小:
dsize=Size(round(fx*src.cols),round(fy*src.rows))
(4)水平轴的缩放系数,默认0,等于0时由下式计算:
(double)dsize.width/src.cols
(5)垂直轴的缩放系数,默认值0,等于0时由下式计算:
(double)dsize.height/src.rows
(6)插值方式,默认为INTER_LINEAR(线性插值),可选插值方式有:
INTER_NEAREST–最近邻插值
INTER_LINEAR–线性插值(默认,放大图像,效率高)
INTER_AREA–区域插值(利用像素区域关系的重采样插值,缩小图像)
INTER_CUBIC–三次样条插值(超过4*4像素领域的双三次插值,放大图像,效率低)
INTER_LANCZOS4–Lanczos插值(超过8*8像素领域的Lanczos插值)
3.调用示例:
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
/*
//调用方式1
Mat dstImage = Mat::zeros(512, 512, CV_8UC3);//新建一张512*512尺寸的图片
Mat srcImage = imread("love.jpg");
resize(srcImage, dstImage, dstImage.size());//指定dsize=dstImage.size(),fx和fy会被计算不需要额外指定
//调用方式2
Mat dstImage;
Mat srcImage = imread("love.jpg");
resize(srcImage, dstImage, Size(), 0.5, 0.5);//指定fx和fy
*/
//载入原图
Mat srcImage = imread("love.jpg");
Mat dstImage1, dstImage2;
//显示原图
namedWindow("【原始图】");
imshow("【原始图】", srcImage);
//尺寸调整操作
resize(srcImage, dstImage1, Size(srcImage.cols / 2, srcImage.rows / 2), 0.0, 0.0, 3);
resize(srcImage, dstImage2, Size(srcImage.cols * 2, srcImage.rows * 2), 0.0, 0.0, 3);
//显示效果图
imshow("【效果图1】", dstImage1);
imshow("【效果图2】", dstImage2);
waitKey(0);
return 0;
}
6.6.5 综合示例
/*
效果:
键盘1、2、3、4控制图片放大与缩小
*/
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
#define WINDOW_NAME "【程序窗口】"
Mat g_srcImage, g_dstImage, g_tmpImage;
//键盘操作说明
static void ShowHelpText()
{
printf("--------------------------------------------------------------------\n");
printf("欢迎来到OpenCV图像金字塔和resize示例程序~\n");
printf("按键操作说明:\n");
printf("\t\t键盘按键[ESC]-退出程序\n");
printf("\t\t键盘按键[1]-进行[resize]函数图片放大\n");
printf("\t\t键盘按键[2]-进行[resize]函数图片缩小\n");
printf("\t\t键盘按键[3]-进行[pyrUp]函数图片放大\n");
printf("\t\t键盘按键[4]-进行[pyrDown]函数图片缩小\n");
printf("--------------------------------------------------------------------\n");
}
int main()
{
//键盘操作说明
ShowHelpText();
//载入原图
g_srcImage = imread("love.jpg");
if (!g_srcImage.data)
{
printf("载入原图失败~!\n");
return false;
}
//创建显示窗口
namedWindow(WINDOW_NAME,WINDOW_AUTOSIZE);
imshow(WINDOW_NAME, g_srcImage);
//参数赋值
g_tmpImage = g_srcImage;
g_dstImage = g_tmpImage;
int key = 0;
//轮询获取按键信息
while (1)
{
key = waitKey(9);//读取键值到变量
//根据key值,做不同操作
switch (key)
{
case 27: //按键ESC
return 0;
break;
case '1': //按键【1】,调用resize函数
resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
printf(">检测到按键【1】被按下,进行【resize】函数的图片放大,图片尺寸*2\n");
break;
case '2': //按键【2】,调用resize函数
resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
printf(">检测到按键【2】被按下,进行【resize】函数的图片缩小,图片尺寸/2\n");
break;
case '3': //按键【3】,调用pyrUp函数
pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
printf(">检测到按键【3】被按下,进行【pyrUp】函数的图片放大,图片尺寸*2\n");
break;
case '4': //按键【4】,调用pyrDown函数
pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
printf(">检测到按键【4】被按下,进行【pyrDown】函数的图片缩小,图片尺寸/2\n");
break;
default:
break;
}
imshow(WINDOW_NAME, g_dstImage);
//将g_dstImage赋给g_tmpImage,方便下一次循环
g_tmpImage = g_dstImage;
}
return 0;
}