OpenCV学习笔记(6)_由例程学习高斯图像金字塔和拉普拉斯金字塔

2023-09-12 22:45:09

在这里插入图片描述

1 图像金字塔

图像金字塔是图像多尺度表达的一种。

尺度,顾名思义,可以理解为图像的尺寸和分辨率。处理图像时,经常对源图像的尺寸进行缩放变换,进而变换为适合我们后续处理的大小的目标图像。这个对尺寸进行放大缩小的变换过程,称之为尺度调整。

图像金字塔则是图像多尺度调整表达的一种重要的方式,图像金字塔方法的原理是:将参加整合的每幅图像分解为拥有多个尺度的金字塔图像序列,越低分辨率的图像,位于金字塔的越上层,反之,越高分辨率的图像,则位于金字塔的越下层。金字塔的层数,则由下而上分别为0, 1, 2, …, N.

每一层的图像的大小都为上一层的1/4。

把这些不同尺度的金字塔图像按照上述规则组合起来,则得到一个形如金字塔的结构,即称为图像金字塔。

在这里插入图片描述

图像金字塔最初用于机器视觉和图像压缩,主要用于图像的分割和融合。

2 高斯金字塔和拉普拉斯金字塔

有两种类型的金字塔经常出现在文献和应用当中,它们分别是:

  • 高斯金字塔(Gaussian Pyramid):主要是用来向下采样(缩小);
  • 拉普拉斯金字塔(Laplacian Pyramid):用来从金字塔低层图像重建上层未采样的图像,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。

2.1 高斯金字塔

在图像向下取样中,一般分两步:
(1) 对图像 G i G_i Gi进行高斯卷积核(高斯滤波);
(2) 删除所有的偶数行和列。

在第一步中,使用的高斯卷积核为:
1 16 [ 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 ] \frac{1}{16}\left[ \begin{array}{c} 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{array}\right] 161 1464141624164624362464162416414641
卷积完成后,将偶数行和偶数列抽走删除,即得到下一层图像。

若是向上取样,一般也分两步:
(1) 对图像 G i + 1 G_{i+1} Gi+1 增加偶数行和偶数列,都用0填充;
(2) 对图像进行高斯卷积,插值时滤波器乘以系数4。

2.2 拉普拉斯金字塔

在高斯金字塔下采样的过程中,可以看出图像信息出现了损失。这意味着如果下采样之后再上采样,图像并不能还原。图像将丢失掉部分高频信息。

此时如果想要保留图像的高频信息,则需要引入拉普拉斯金字塔:
L i = G i − P y r U p ( P y r D o w n ( G i ) ) L_i = G_i - PyrUp(PyrDown(G_i)) Li=GiPyrUp(PyrDown(Gi))
即第i层的拉普拉斯金字塔图像,先计算高斯金字塔下采样再上采样的图像 P y r U p ( P y r D o w n ( G i ) ) PyrUp(PyrDown(G_i)) PyrUp(PyrDown(Gi)),然后用第i层的原图 G i G_i Gi减去它。

3 OpenCV中的图像金字塔例程

3.1 pyrUp和pyrDown函数

OpenCV中的pyrUp函数原型为:

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

pyrDown函数原型为:

void cv::pyrDown(InputArray src,
				OutputArray dst,
				const Size & dstSize = Size(),
				int borderType = BORDER_DEFAULT)

其中,src为原始图像,dst为上采样/下采样的目标图像, dstSize为目标图像的尺寸, borderType为边界类型,这里默认为BORDER_DEFAULT.

3.2 Pyramids.cpp例程

原理部分讲述结束,下面进入OpenCV的例程:

OpenCV的Sample文件夹下有关于图像金字塔的例程:
samples/cpp/tutorial_code/ImgProc/Pyramids/Pyramids.cpp

#include "iostream"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
const char* window_name = "Pyramids Demo";
int main(int argc, char** argv)
{
	cout << "\n Zoom In-Out demo \n "
		"------------------  \n"
		" * [i] -> Zoom in   \n"
		" * [o] -> Zoom out  \n"
		" * [ESC] -> Close program \n" << endl;
	const char* filename = argc >= 2 ? argv[1] : "../data/chicky_512.png";
	// Loads an image
	Mat src = imread(samples::findFile(filename));
	// Check if image is loaded fine
	if (src.empty()) {
		printf(" Error opening image\n");
		printf(" Program Arguments: [image_name -- default chicky_512.png] \n");
		return EXIT_FAILURE;
	}
	for (;;)
	{
		imshow(window_name, src);
		char c = (char)waitKey(0);
		if (c == 27)
		{
			break;
		}
		else if (c == 'i')
		{
			pyrUp(src, src, Size(src.cols * 2, src.rows * 2));
			printf("** Zoom In: Image x 2 \n");
		}
		else if (c == 'o')
		{
			pyrDown(src, src, Size(src.cols / 2, src.rows / 2));
			printf("** Zoom Out: Image / 2 \n");
		}
	}
	return EXIT_SUCCESS;
}

注意以上源码是被小白修改了的,因为小白的工程里面用于放缩的图片的位置是在data文件夹下

const char* filename = argc >= 2 ? argv[1] : "../data/chicky_512.png";

这个程序打开后会显示一幅小狗的图像,并一直等待键盘输入。
在这里插入图片描述

如果你输入一个"i",则图像将被放大,即Zoom In;
如果你输入一个"o",则图像将被缩小,即Zoom Out;
直到你输入一个"ESC",程序退出。

但是这个程序只使用了高斯金字塔,反复测试几次你就会发现:如果先缩小了很多倍,再重新放大,那么由于下采样时总是在丢失图像信息,多操作几次之后,小狗的面目就变得非常模糊。
下图就是缩小了3次之后再放大到原始尺寸时所看到的小狗:
在这里插入图片描述

那么有没有办法把这个例程改造成可以带有拉普拉斯金字塔,可以在放缩后仍然还原原始分辨率细节的小狗图呢?当然有。

3.3 改造后的图像金字塔例程

使用STL中的stack来保存拉普拉斯金字塔图像,则可以简单地实现分辨率还原的功能:

#include "iostream"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <stack>
using namespace std;
using namespace cv;
const char* window_name = "Pyramids Demo";
int main(int argc, char** argv)
{
	cout << "\n Zoom In-Out demo \n "
		"------------------  \n"
		" * [i] -> Zoom in   \n"
		" * [o] -> Zoom out  \n"
		" * [ESC] -> Close program \n" << endl;
	const char* filename = argc >= 2 ? argv[1] : "../data/chicky_512.png";
	// 加载一幅图像
	Mat src = imread(samples::findFile(filename));
	int picMaxWidth = src.cols;
	int picMaxHeight = src.rows;
	// 检查是否加载成功
	if (src.empty()) {
		printf(" Error opening image\n");
		printf(" Program Arguments: [image_name -- default chicky_512.png] \n");
		return EXIT_FAILURE;
	}

	// 用于存储拉普拉斯金字塔的栈结构
	stack<Mat> stackMat;

	for (;;)
	{
		imshow(window_name, src);
		char c = (char)waitKey(0);
		if (c == 27)												// ESC Key 则退出
		{
			break;
		}
		// 放大图像但不允许超过原图大小
		else if (c == 'i')
		{
			if (src.cols >= picMaxWidth)
			{
				printf("** cannot Zoom In: Image reaches max size!\n");
				continue;
			}
			pyrUp(src, src, Size(src.cols * 2, src.rows * 2));
			if (!stackMat.empty())
			{
				Mat temp;
				temp = stackMat.top();
				src.convertTo(src, CV_16SC3);						// 对图像格式进行转换,目的是进行负差值的正确计算
				src += temp;										// 取栈顶的拉普拉斯金字塔图像进行原图还原
				src.convertTo(src, CV_8UC3);						// 将图像还原成8位3通道,便于显示
				stackMat.pop();
			}
			printf("** Zoom In: Image x 2 \n");
		}
		// 缩小图像
		else if (c == 'o')
		{
			Mat temp1, temp2;
			src.copyTo(temp1);
			pyrDown(src, src, Size(src.cols / 2, src.rows / 2));
			pyrUp(src, temp2, Size(src.cols * 2, src.rows * 2));
			// 对图像进行格式转换, 目的是得到差值图像的正确负值
			temp1.convertTo(temp1, CV_16SC3);
			temp2.convertTo(temp2, CV_16SC3);
			temp1 -= temp2;											// 计算拉普拉斯金字塔差值图像
			stackMat.push(temp1);									// 将拉普拉斯金字塔压进栈中
			printf("** Zoom Out: Image / 2 \n");
		}
	}
	return EXIT_SUCCESS;
}

这段代码中:

  • 通过一个stackMat来记录缩小时造成的图像信息损失;
  • 通过压栈和出栈的方式保证每一层下采样时的拉普拉斯金字塔都能得以保存;
  • 通过图像类型反复转换来保证在计算差值时能够正确地保留像素差的负值;
  • 通过限制放大的倍率,防止在放大到超过原始图像分辨率,再重新下采样时造成不可逆的图像信息损失。

细节其实挺多,针对第三点和第四点简单铺开讲一下:

  • 如果不进行图像类型的转换(大家可以试试把那几句图像类型转换的代码注释掉),那么在计算拉普拉斯金字塔时,图像差值将无法保留负值,这将会导致在反复缩放过程中,图像的部分信息发生丢失,小狗图中的高频细节会越来越亮,像下图中一样;
    在这里插入图片描述

  • 如果不限制图像的放缩倍率,那么当图像先放大到原始图像尺寸的2倍,再重新缩小到原始尺寸时,图像先经历了一次上采样,放大时的插值部分属于无中生有,再重新下采样时,就引入了信息失真,因此对原例程进行了修改,使其无法将图像放大到超过原始尺寸,这样就保证了在图像回到原始尺寸大小时,其图像细节能与原始图像保持一致。下图是如果不限制放大倍率,在放大超过原始尺寸后再重新回到原始尺寸时,图像的效果:(如果你将它和原始图像比较一下,会发现有细微的差别)
    在这里插入图片描述

至此,小白和大家一起学习了这个例程,并对高斯图像金字塔和拉普拉斯图像金字塔有了更深层次的理解。

在这里插入图片描述

更多推荐

【LLM】解析pdf文档生成摘要 | 智能文档概览

文章目录一、整体思路二、代码三、小结与改进1.小结2.后续可继续优化的点四、智能文档Reference一、整体思路非常简单的一个v1版本利用langchain和pdfminer切分pdf文档为k块,设置overlap等参数先利用prompt1对每个chunk文本块进行摘要生成,然后利用prompt2对多个摘要进行连贯组

Qt Charts简介

文章目录一.图标类型Charts分类1.折线图和样条曲线图2.面积图和散点图3.条形图4.饼图5.误差棒图6.烛台图7.极坐标图二.坐标轴Axes类型分类三.图例四.图表的互动五.图表样式主题一.图标类型Charts分类图表是通过使用系列类的实例并将其添加到QChart或ChartView实例来创建的。Qt图表模块提供

Laravel框架 - IOC容器详解

IOC容器代码好了,说了这么多,下面要上一段容器的代码了.下面这段代码不是laravel的源码,而是来自一本书《laravel框架关键技术解析》.这段代码很好的还原了laravel的服务容器的核心思想.代码有点长,小伙伴们要耐心看.当然小伙伴完全可以试着运行一下这段代码,然后调试一下,这样会更有助于理解.<?php//

R语言风险价值:ARIMA,GARCH,Delta-normal法滚动估计VaR(Value at Risk)和回测分析股票数据...

全文链接:http://tecdat.cn/?p=24492此分析的目的是构建一个过程,以在给定时变波动性的情况下正确估计风险价值。风险价值被广泛用于衡量金融机构的市场风险。我们的时间序列数据包括1258天的股票收益(点击文末“阅读原文”获取完整代码数据)。介绍为了解释每日收益率方差的一小部分,我们使用Box-Jenk

docker 安装 redis

文章目录redis单机版1.拉取redis镜像并创建数据卷目录2.复制和修改redis.conf3.启动redis容器4.进入容器并连接redisredis一主两从集群搭建1.复制三份redis.conf2.启动master和slave3.查看关系和数据测试redis高可用集群搭建1.复制三份sentinel.conf

基于微信小程序的语言课学习系统设计与实现(源码+lw+部署文档+讲解等)

前言💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗👇🏻精彩专栏推荐订阅👇🏻2023-2024年最值得选的微信小程序毕业设计选题大全:100个热门选

『贪吃蛇』AI 算法简易实现(中秋特别版)

前言一年一度的中秋节就快到了,平台也有各种各样的中秋发文活动,正在翻阅时偶然间我看到了这篇文章:《兔饼大作战》:吃月饼、见月亮,还能咬自己?|欢庆中秋特制版-掘金(juejin.cn)大家肯定比较熟悉了,这个游戏的内核就是贪吃蛇,作者也是对玩法做了很多调整,趣味性十足,同时加入兔饼、月亮等元素增加节日氛围,可以说创意性

【C++】仿函数和priority_queue(优先级队列)

目录一、仿函数二、priority_queue(优先级队列)1、概念:2、使用:3、数组中第K个最大元素4、priority_queue的模拟实现一、仿函数①、概念:仿函数,即函数对象。一种行为类似函数的对象,调用者可以像函数一样使用该对象,其实现起来也比较简单:用户只需实现一种新类型,在类中重载operator()即

Vue中的路由介绍以及Node.js的使用

🏅我是默,一个在CSDN分享笔记的博主。📚📚🌟在这里,我要推荐给大家我的专栏《Vue》。🎯🎯🚀无论你是编程小白,还是有一定基础的程序员,这个专栏都能满足你的需求。我会用最简单易懂的语言,带你走进Vue的世界,让你从零开始,一步步成为JAVA大师。🚀🏆🌈让我们在Vue的世界里畅游吧!🌈🎁如果感觉还

【C++进阶】:哈希

哈希一.unordered_map二.底层结构1.哈希概念2.解决哈希冲突1.闭散列2.开散列在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到log2Nlog_2Nlog2​N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数

你对Spring Security使用场景以及底层原理有了解吗?

SpringSecurity使用场景有哪些?SpringSecurity是一个基于Spring框架的安全性解决方案,提供了全面的身份验证、授权和安全功能。它可以应用于多种场景以确保应用程序的安全性和保护敏感资源。以下是一些常见的SpringSecurity的使用场景:用户登录和认证:SpringSecurity可以处理

热文推荐