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

在这里插入图片描述

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 imageMat src = imread(samples::findFile(filename));// Check if image is loaded fineif (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倍,再重新缩小到原始尺寸时,图像先经历了一次上采样,放大时的插值部分属于无中生有,再重新下采样时,就引入了信息失真,因此对原例程进行了修改,使其无法将图像放大到超过原始尺寸,这样就保证了在图像回到原始尺寸大小时,其图像细节能与原始图像保持一致。下图是如果不限制放大倍率,在放大超过原始尺寸后再重新回到原始尺寸时,图像的效果:(如果你将它和原始图像比较一下,会发现有细微的差别)
    在这里插入图片描述

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/130888.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++ 学习 之 常函数 和 常对象

前言 常函数 成员函数后加 const 我们可以称这个函数为 常函数 常函数内不可以修改成员属性 成员属性声明时加关键字 mutable 后&#xff0c;在常函数中依然可以修改 常对象 常对象 声明对象前加 const 称该对象为常对象 常对象只能调用常函数 正文 常函数 class Person…

JAVAEE初阶相关内容第八弹--多线程(初阶)

本文目录 阻塞队列 阻塞队列是什么&#xff1f; 标准库中的阻塞队列 生产者消费者模型 阻塞队列的实现 普通队列实现&#xff1a; 入队列&#xff1a; 出队列&#xff1a; 完整代码&#xff1a; 加阻塞 加锁 加阻塞 阻塞队列 队列&#xff1a;先进先出&#xff0c;…

Redis缓存魔法:如何轻松提升你的应用性能

Redis&#xff0c;作为一个开源的、内存中的数据结构存储系统&#xff0c;已经成为了许多开发者和企业的首选工具。无论是作为数据库、缓存还是消息代理&#xff0c;Redis都展现出了其强大的性能和灵活性。在本文中&#xff0c;我们将深入探讨Redis的魅力&#xff0c;以及如何有…

量化:基于支持向量机的择时策略

文章目录 参考机器学习简介策略简介SVM简介整体流程收集数据准备数据建立模型训练模型测试模型调节参数 参考 Python机器学习算法与量化交易 利用机器学习模型&#xff0c;构建量化择时策略 机器学习简介 机器学习理论主要是设计和分析一些让计算机可以自动“学习”的算法。…

无涯教程-JavaScript - OCT2BIN函数

描述 OCT2BIN函数将八进制数转换为二进制数。 语法 OCT2BIN (number, [places])争论 Argument描述Required/OptionalNumber 您要转换的八进制数。 数字不能超过10个字符。数字的最高有效位是符号位。其余的29位是幅度位。 负数使用二进制补码表示。 RequiredPlaces 要使用的…

【C语言】字符串函数

文章目录 前言1.strcat2.strncpy3.strncat4.strncmp5.strstr6.strtok7.strerror8.strcat的模拟实现9.strstr的模拟实现 总结 添加链接描述 前言 大家好呀&#xff0c;今天给大家分享一下字符函数和字符串函数C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没…

随机密码生成器(Python)

随机密码生成器 想要生成一个随机密码&#xff0c;需要考虑下面两点&#xff1a; 1.字符集合 2.密码的位数 下面代码中引用了string模块和random模块&#xff0c;string.printable是string中的可打印字符&#xff0c;用strip函数首尾去掉空格&#xff1b;random模块用来取字符&…

C# wpf 实现桌面放大镜

文章目录 前言一、如何实现&#xff1f;1、制作无边框窗口2、Viewbox放大3、截屏显示&#xff08;1&#xff09;、截屏&#xff08;2&#xff09;、转BitmapSource&#xff08;3&#xff09;、显示 4、定时截屏 二、完整代码三、效果预览总结 前言 做桌面截屏功能时需要放大镜…

品牌策划经理工作内容|工作职责|品牌策划经理做什么?

一位美国作家曾说过“品牌是一系列期望、记忆、故事和关系&#xff0c;他们共同构成了消费者最终原则一个产品或者服务的原因。” 所以&#xff0c;品牌经理这个岗位主要是创造感知价值主张&#xff0c;激发消费者购买这个品牌后带来的感知价值&#xff0c;这种回报的本质相对…

C语言实现三子棋游戏(详解)

目录 引言&#xff1a; 1.游戏规则&#xff1a; 2.实现步骤&#xff1a; 2.1实现菜单&#xff1a; 2.2创建棋盘并初始化&#xff1a; 2.3绘制棋盘&#xff1a; 2.4玩家落子&#xff1a; 2.5电脑落子&#xff1a; 2.6判断胜负&#xff1a; 3.源码&#xff1a; 结语&…

论文阅读 (100):Simple Black-box Adversarial Attacks (2019ICML)

文章目录 1 概述1.1 要点1.2 代码1.3 引用 2 背景2.1 目标与非目标攻击2.2 最小化损失2.3 白盒威胁模型2.4 黑盒威胁模型 3 简单黑盒攻击3.1 算法3.2 Cartesian基3.3 离散余弦基3.4 一般基3.5 学习率 ϵ \epsilon ϵ3.6 预算 1 概述 1.1 要点 题目&#xff1a;简单黑盒对抗攻…

力扣刷题-移除指定值的链表元素

力扣203移除元素 题目来源&#xff1a; 力扣203 题目描述&#xff1a; 非常简单的一道题&#xff0c;主要强调两点 链表删除要记录删除位置的前驱节点 头节点没有前驱 因此直接headhead.next为了保持与后两种一致&#xff0c;加上虚拟节点&#xff0c;下一节点指向头节点 /***…

【Linux-Day10-信号量,共享内存,消息队列】

信号量 信号量描述 信号量是一个特殊的变量&#xff0c;一般取正数值。它的值代表允许访问的资源数目&#xff0c;获取资源 时&#xff0c;需要对信号量的值进行原子减一&#xff0c;该操作被称为 P 操作。 当信号量值为 0 时&#xff0c;代表没有资源可用&#xff0c;P 操作…

企业架构LNMP学习笔记27

Keepalived的配置补充&#xff1a; 脑裂&#xff08;裂脑&#xff09;&#xff1a;vip出现在了多台机器上。网络不通畅&#xff0c;禁用了数据包&#xff0c;主备服务器没法通讯&#xff0c;造成备服务器认为主服务器不可用&#xff0c;绑定VIP&#xff0c;主服务器VIP不会释放…

【设计模式】三、概述分类+单例模式

文章目录 概述设计模式类型 单例模式饿汉式&#xff08;静态常量&#xff09;饿汉式&#xff08;静态代码块&#xff09;懒汉式(线程不安全)懒汉式(线程安全&#xff0c;同步方法)懒汉式(线程安全&#xff0c;同步代码块)双重检查静态内部类枚举单例模式在 JDK 应用的源码分析 …

[N0wayback 2023春节红包题] happyGame python反编译

这个反编译的比较深 一&#xff0c;从附件的图标看是python打包的exe文件&#xff0c;先用pyinstxtractor.py 解包 生成的文件在main.exe_extracted目录下&#xff0c;在这里边找到main 二&#xff0c;把main改名为pyc然后加上头 这个头从包里找一个带头的pyc文件&#xff…

做题(1)

1.fileinclude 打开 发现提示flag在flag.php里边 查看页面源代码 $lan $_COOKIE[language]; 这句话是关键 变量lan是用cookie传参的 include("english.php"); 这句话表明了文件包含 include函数用php伪协议 filer绕过 include($lan.".php"); 这句话…

深入实现 MyBatis 底层机制的任务阶段4 - 开发 Mapper 接口和 Mapper.xml

&#x1f600;前言 在我们的自定义 MyBatis 底层机制实现过程中&#xff0c;我们已经深入研究了多个任务阶段&#xff0c;包括配置文件的读取、数据库连接的建立、执行器的编写&#xff0c;以及 SqlSession 的封装。每个任务阶段都为我们揭示了 MyBatis 内部工作原理的一部分&a…

【算法基础】时间复杂度和空间复杂度

目录 1 算法的评价 2 算法复杂度 2.1 时间复杂度&#xff08;Time Complexity&#xff09; 2.1.1 如何计算时间复杂度&#xff1a; 2.1.2 常见的时间复杂度类别与示例 2.2 空间复杂度 2.2.1 如何计算空间复杂度 2.2.2 常见的空间复杂度与示例 3 时间复杂度和空间复杂度…

Linux——环境变量

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——环境变量 ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;环境变量(environment variables)一般是指在操作系统中用来指定操作…