影像金字塔(Image Pyramid)是一種多尺度表示影像的技術,它通過將原始影像在不同尺度下進行分解和重建,以實現在不同層次上檢測和分析影像中的特徵。這種技術可以從不同的尺度呈現同一影像,從而在金字塔的不同層次上觀察影像,捕捉到影像中的結構和特徵的變化。
高斯金字塔使用符號G(i)來表示,其中i為金字塔的第幾層,而G(i)表示在第i層高斯金字塔的影像。如下圖所示,層數越高影像就會越小,且解析度會越來越低,影像遺失的細節也會越來越多。G(0)是在高斯金字塔的最下層,而G(0)為我們的輸入影像f(x,y)。我們需要使用G(0)來生成金字塔的其他上層影像,每一個G(i)都可以生成下一層G(i+1)的影像,而G(i+1)的面積會是G(i)的4倍小。
要取得高斯金字塔首先要使用一個5x5的高斯核對G(i)進行摺積,並移除圖片中奇數行、奇數列的像素,這個步驟稱為向下取樣(Downsampling),將影像的大小降低一半,我們就會得到G(i+1)的高斯金字塔影像。
拉普拉斯金字塔使用L(i)來表示,其中i為金字塔的第幾層。而拉普拉斯金字塔無法透過單獨的G(i)直接生成,需要透過G(i)和G(i+1)生成L(i)。拉普拉斯金字塔保存的是經過向下取樣後遺失的細節,而遺失這的動作是相對的,我們要知道曾經擁有什麼才會知道現在失去什麼對吧!所以L(i)其實保存的是G(i)在經過向下取樣變成G(i+1)時,中間的過程遺失掉的細節。
拉普拉斯金字塔可以用數學公式可以表示成:
其中:
可以看到下圖為一張完整的影像金字塔,其中包含了高斯金字塔(Gaussian Pyramid)和拉普拉斯金字塔(Laplacian Pyramid),完美的描述了兩個金字塔之間的關係。
建立高斯金字塔。
cv::buildPyramid(grayImage, gaussian_pyramid, max_level);
grayImage
:輸入的灰階影像,也就是要建立金字塔的原始影像。gaussian_pyramid
:用來存儲建立的金字塔的不同層的向量(vector)。max_level
:這是指定金字塔的最大層數。當被設置為6,會輸出6+1層,其中第0層為原始影像。使用OpenCV函數cv::pyrUp
,使用5x5的高斯核濾波,並將影像進行向下取樣(或稱縮小)。
cv::pyrDown(src, m);
src
:輸入的影像。m
:存儲向下取樣(縮小)後的結果影像。使用OpenCV函數cv::pyrUp
,將影像進行向上取樣(或稱放大)操作,並使用5x5的高斯核濾波。
cv::pyrUp(src, m);
src
:輸入的影像。m
:存儲向上取樣(放大)後的結果影像。gaussian_pyramid
和 Laplacian 金字塔容器 laplacian_pyramid
。cv::buildPyramid
建立 Gaussian 金字塔,其中 max_level
指定了金字塔的最大層數。#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/utils/logger.hpp"
using namespace std;
int main()
{
// 設置 OpenCV 的日誌級別為無日誌輸出
cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_SILENT);
// 讀取一張灰階影像(以灰階模式讀取)
cv::Mat grayImage = cv::imread("C:\\Users\\vince\\Downloads\\Lenna.png", cv::IMREAD_GRAYSCALE);
// 定義一個 5x5 的高斯核
float data[] = {
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,
};
cv::Mat gaussian_kernel = cv::Mat(5, 5, CV_32FC1, data);
// 正規化高斯核
gaussian_kernel /= 256;
// 定義 Gaussian 金字塔和 Laplacian 金字塔的向量
vector<cv::Mat> gaussian_pyramid, laplacian_pyramid;
int max_level = 6; // 金字塔的最大層數
// 建立 Gaussian 金字塔
cv::buildPyramid(grayImage, gaussian_pyramid, max_level);
// 生成 Laplacian 金字塔
for (int i = 0; i < max_level + 1; i++) {
if (i == max_level)
continue;
cv::Mat m;
cv::pyrUp(gaussian_pyramid[i + 1], m);
cv::filter2D(m, m, CV_8U, gaussian_kernel);
laplacian_pyramid.push_back(gaussian_pyramid[i] - m);
}
// 迭代還原影像
cv::Mat iterate_image = gaussian_pyramid[gaussian_pyramid.size() - 1];
for (int i = laplacian_pyramid.size() - 1; i >= 0; i--) {
cv::Mat m;
cv::pyrUp(iterate_image, m);
cv::filter2D(m, m, CV_8U, gaussian_kernel);
iterate_image = m + laplacian_pyramid[i];
}
// 顯示最終還原的G(0)影像
cv::imshow("Output", iterate_image);
cv::waitKey(0);
return 0;
}
這隻程式其實就是一個從G(7)這個低解析度的影像透過拉普拉斯金字塔迭帶還原原來的影像,雖然圖片看起來的整體亮度和原圖有差異,這張圖片的細節大部分還是完整的被保留了下來。