欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenCV图像金字塔

程序员文章站 2022-07-14 11:34:08
...

图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。如下图所示。

OpenCV图像金字塔

常用的图像金字塔有高斯金字塔(Gaussian pyramid)和拉普拉斯金字塔(Laplacian pyramid)。高斯金字塔用来向下采样,而拉普拉斯金字塔用来从金字塔低层图像重建上层未采样图像。

高斯金字塔

下采样pyrDown

下采样,也叫做降采样,这个过程中是隔行隔列删去图像中的对应行和列,这样原图中那些精细的细节边缘等地方会变得锯齿状,产生失真,因此为了缩小之后的图像看起来自然,必须进行平滑。因此pyrDown函数在降采样之前要先对图像进行高斯模糊。此时采用的高斯核如下:

OpenCV图像金字塔

 代码:

#include "stdafx.h"
#include <opencv2/opencv.hpp>


int main()
{
	// 声明两个图像矩阵
	cv::Mat img1, img2;
	
	// 创建两个窗口
	cv::namedWindow("image1", cv::WINDOW_AUTOSIZE);
	cv::namedWindow("image2", cv::WINDOW_AUTOSIZE);

	// 读取文件,并将原始图像显示在image1窗口
	img1 = cv::imread("test.jpg");
	cv::imshow("image1", img1);

	// 对原始图像进行下采样和高斯滤波处理,长宽各缩小一半,并显示在imge2窗口
	cv::pyrDown(img1, img2);
	cv::imshow("image2", img2);

	// 等待键盘事件
	cv::waitKey(0);
	// 关闭所有窗口,并释放关联内存
	cv::destroyAllWindows();
    return 0;
}

运行结果:

OpenCV图像金字塔

上采样pyrUp

上采样过程首先是将图像在每个方向上扩大为原来的两倍,新增的行和列都以0填充,然后使用下采样时用的高斯核乘以四与放大后的图像进行卷积,获得“新增像素”的近似值。因此处理后的图像尺寸变大,但是分辨率不变。

代码:

#include "stdafx.h"
#include <opencv2/opencv.hpp>


int main()
{
	// 声明两个图像矩阵
	cv::Mat img1, img2;

	// 创建两个窗口
	cv::namedWindow("image1", cv::WINDOW_AUTOSIZE);
	cv::namedWindow("image2", cv::WINDOW_AUTOSIZE);

	// 读取文件,并将原始图像显示在image1窗口
	img1 = cv::imread("test.jpg");
	cv::imshow("image1", img1);

	// 对原始图像进行下采样和高斯滤波处理,长宽各放大一半,并显示在imge2窗口
	cv::pyrUp(img1, img2);
	cv::imshow("image2", img2);

	// 等待键盘事件
	cv::waitKey(0);
	// 关闭所有窗口,并释放关联内存
	cv::destroyAllWindows();
	return 0;
}

运行结果:

OpenCV图像金字塔

拉普拉斯金字塔

拉普拉斯金字塔可以有高斯金字塔计算得来,公式如下:

OpenCV图像金字塔

式中: OpenCV图像金字塔 代表第i层高斯图像;

            OpenCV图像金字塔 代表第i+1层高斯图像;

            OpenCV图像金字塔 代表上采样;

            OpenCV图像金字塔  代表卷积运算符;

            OpenCV图像金字塔 代表5 × 5的卷积内核。

拉普拉金字塔的图像看起来就像边界图,其中很多像素都是 0,经常被用在图像压缩中。

代码:

#include "stdafx.h"
#include <opencv2/opencv.hpp>


int main()
{
	// 声明两个图像矩阵
	cv::Mat img1, img2, img3;

	// 创建两个窗口
	cv::namedWindow("image1", cv::WINDOW_AUTOSIZE);
	cv::namedWindow("image2", cv::WINDOW_AUTOSIZE);

	// 读取文件,并将原始图像显示在image1窗口
	img1 = cv::imread("test.jpg");
	cv::imshow("image1", img1);

	// 对原始图像进行下采样和高斯滤波处理,长宽各放大一半,并显示在imge2窗口
	cv::pyrDown(img1, img2);
	cv::pyrDown(img2, img3);
	cv::pyrUp(img3, img3);
	img3 = img2 - img3;
	cv::imshow("image2", img3);

	// 等待键盘事件
	cv::waitKey(0);
	// 关闭所有窗口,并释放关联内存
	cv::destroyAllWindows();
	return 0;
}

运行结果:

OpenCV图像金字塔

金字塔图像融合

OpenCV图像金字塔

分析:

① 首先通过图1建立高斯金字塔;

② 然后通过得到的高斯金字塔生成拉普拉斯金字塔。以图1、图2和图4为例:图4是公式中的OpenCV图像金字塔,图1是公式中的OpenCV图像金字塔,图2是公式中的OpenCV图像金字塔,则图4是由图1减去图2向上采样并高斯模糊的结果得到的。

③ 因为拉普拉斯图像是用来从金字塔低层图像重建上层未采样图像的,所以可以通过将其与上一层的上采样的结果相加来重建原图。以图4、图5和图6为例:图6=图4+pyrUp(图5)。

注:重建原图金字塔的塔顶和高斯金字塔的塔顶是一样的。

代码:

注:这里选取的图片最好是大小相同,且行数和列数是能除尽2的6次方的值,否则上采样后的行数和列数可能和原来的相差1,需再进行处理。

#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>


int main()
{
	// 声明两个图像矩阵
	cv::Mat img1, img2;
	// 读取图片
	img1 = cv::imread("apple.jpg");
	img2 = cv::imread("orange.jpg");
	
	// 创建三个窗口
	cv::namedWindow("img1", cv::WINDOW_NORMAL);
	cv::namedWindow("img2", cv::WINDOW_NORMAL);
	cv::namedWindow("img", cv::WINDOW_NORMAL);

	// 用apple图像生成高斯金字塔,共7层
	cv::Mat gp1[7];
	cv::Mat gtmp1 = img1;
	gp1[0] = gtmp1;
	for (int i = 1; i < 7; i++) {
		cv::pyrDown(gtmp1, gtmp1);
		gp1[i] = gtmp1;
	}

	// 用orange图像生成高斯金字塔,共7层
	cv::Mat gp2[7];
	cv::Mat gtmp2 = img2;
	gp2[0] = gtmp2;
	for (int i = 1; i < 7; i++) {
		cv::pyrDown(gtmp2, gtmp2);
		gp2[i] = gtmp2;
	}

	// 用apple图像生成拉普拉斯金字塔,共6层
	cv::Mat lp1[7];
	cv::Mat ltmp1;
	lp1[6] = gp1[6];
	for (int i = 5; i >= 0; i--) {
		cv::pyrUp(gp1[i+1], ltmp1);
		cv::subtract(gp1[i], ltmp1, lp1[i]);
	}

	// 用orange图像生成拉普拉斯金字塔,共6层
	cv::Mat lp2[7];
	cv::Mat ltmp2;
	lp2[6] = gp2[6];
	for (int i = 5; i >= 0; i--) {
		cv::pyrUp(gp2[i+1], ltmp2);
		cv::subtract(gp2[i], ltmp2, lp2[i]);
	}

	// 将apple拉普拉斯金字塔的左半边和orang拉普拉斯金字塔的右半边拼接,生成融合后的拉普拉斯金字塔
	cv::Mat LS[7];
	for (int i = 0; i < 7; i++) {
		cv::Size shape = lp1[i].size();
		int width = shape.width;
		// 将apple拉普拉斯图像赋给融合图像
		LS[i] = lp1[i];
		// 获取orange拉普拉斯图像的右半边取出
		cv::Mat roi2 = lp2[i].colRange(width / 2, width);
		// 将取出的半边图像复制到融合图像的右半边,实现图像融合
		roi2.copyTo(LS[i].colRange(width/2, width));
	}

	// 重建原图
	cv::Mat ls_ = LS[6];
	for (int i = 5; i >= 0; i--)
	{
		cv::pyrUp(ls_, ls_);
		std::cout << ls_.size() << std::endl;
		std::cout << LS[i].size() << std::endl;
		cv::add(ls_, LS[i], ls_);
	}

	// 显示原图和重建图像
	cv::imshow("img1", img1);
	cv::imshow("img2", img2);
	cv::imshow("img", ls_);
	// 等待键盘事件
	cv::waitKey(0);
	// 关闭窗口,并释放相关联的内存
	cv::destroyAllWindows();
    return 0;
}

运行结果:

OpenCV图像金字塔