OpenCV图像金字塔

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

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

高斯金字塔

下采样pyrDown

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

 代码:

#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;
}

运行结果:

上采样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;
}

运行结果:

拉普拉斯金字塔

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

L_{i}=G_{i}-Up(G_{i+1})\bigotimes \kappa _{5\times 5}=G_{i}-PyrUp(G_{i+1})

式中: G_{i} 代表第i层高斯图像;

            G_{i+1} 代表第i+1层高斯图像;

            Up 代表上采样;

            \bigotimes  代表卷积运算符;

            \kappa _{5\times 5} 代表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;
}

运行结果:

金字塔图像融合

分析:

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

② 然后通过得到的高斯金字塔生成拉普拉斯金字塔。以图1、图2和图4为例:图4是公式中的L_{i},图1是公式中的G_{i},图2是公式中的G_{i+1},则图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;
}

运行结果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章