图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。如下图所示。
常用的图像金字塔有高斯金字塔(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;
}
运行结果:
拉普拉斯金字塔
拉普拉斯金字塔可以有高斯金字塔计算得来,公式如下:
式中: 代表第i层高斯图像;
代表第i+1层高斯图像;
代表上采样;
代表卷积运算符;
代表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是公式中的,图1是公式中的,图2是公式中的,则图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;
}
运行结果: