iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0

一、介紹

影像金字塔(Image Pyramid)是一種多尺度表示影像的技術,它通過將原始影像在不同尺度下進行分解和重建,以實現在不同層次上檢測和分析影像中的特徵。這種技術可以從不同的尺度呈現同一影像,從而在金字塔的不同層次上觀察影像,捕捉到影像中的結構和特徵的變化。

二、原理

1. 影像金字塔(ImagePyramid)

1) 高斯金字塔(Gaussian 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)的高斯金字塔影像。

https://ithelp.ithome.com.tw/upload/images/20230925/20161732N421HzOB7P.png

圖片來源:OpenCV: Image Pyramids

2) 拉普拉斯金字塔(Laplacian Pyramid)

拉普拉斯金字塔使用L(i)來表示,其中i為金字塔的第幾層。而拉普拉斯金字塔無法透過單獨的G(i)直接生成,需要透過G(i)和G(i+1)生成L(i)。拉普拉斯金字塔保存的是經過向下取樣後遺失的細節,而遺失這的動作是相對的,我們要知道曾經擁有什麼才會知道現在失去什麼對吧!所以L(i)其實保存的是G(i)在經過向下取樣變成G(i+1)時,中間的過程遺失掉的細節。

拉普拉斯金字塔可以用數學公式可以表示成:
https://ithelp.ithome.com.tw/upload/images/20230925/20161732As8KXUC2wr.png

其中:

  • UP(G(i+1)):向上取樣(Upsampling ),將輸入的影像G(i+1)的每個奇數行、奇數列插入元素全部為0的行、列。輸出的影像的面積會是G(i+1)的4倍大。
  • g:一個5x5的高斯核。這裡會對UP(G(i+1))進行摺積。
  • Li:第i層的拉普拉斯金字塔。

可以看到下圖為一張完整的影像金字塔,其中包含了高斯金字塔(Gaussian Pyramid)拉普拉斯金字塔(Laplacian Pyramid),完美的描述了兩個金字塔之間的關係。

https://ithelp.ithome.com.tw/upload/images/20230925/2016173200nub96p1I.jpg

三、程式碼

1. 逐行解釋

1) 建立高斯金字塔

建立高斯金字塔。

cv::buildPyramid(grayImage, gaussian_pyramid, max_level);
  • grayImage:輸入的灰階影像,也就是要建立金字塔的原始影像。
  • gaussian_pyramid:用來存儲建立的金字塔的不同層的向量(vector)。
  • max_level:這是指定金字塔的最大層數。當被設置為6,會輸出6+1層,其中第0層為原始影像。

2) 向下取樣

使用OpenCV函數cv::pyrUp,使用5x5的高斯核濾波,並將影像進行向下取樣(或稱縮小)。

cv::pyrDown(src, m);
  • src:輸入的影像。
  • m:存儲向下取樣(縮小)後的結果影像。

3) 向上取樣

使用OpenCV函數cv::pyrUp,將影像進行向上取樣(或稱放大)操作,並使用5x5的高斯核濾波。

cv::pyrUp(src, m);
  • src:輸入的影像。
  • m:存儲向上取樣(放大)後的結果影像。

2. 完整程式碼

  1. 設置 OpenCV 的日誌級別,以停用日誌輸出。
  2. 建立 5x5 的高斯核,並將其正規化。
  3. 建立 Gaussian 金字塔容器 gaussian_pyramid 和 Laplacian 金字塔容器 laplacian_pyramid
  4. 使用 cv::buildPyramid 建立 Gaussian 金字塔,其中 max_level 指定了金字塔的最大層數。
  5. 生成 Laplacian 金字塔,首先對每一層 Gaussian 金字塔影像進行上取樣、高斯模糊,然後計算與上一層 Gaussian 金字塔的差異,這些差異被存儲在 Laplacian 金字塔中。
  6. 還原原始影像,進行迭代,將 Laplacian 金字塔與上取樣的 Gaussian 影像相加。
  7. 顯示還原的影像。
#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;
}

3. 輸出結果

這隻程式其實就是一個從G(7)這個低解析度的影像透過拉普拉斯金字塔迭帶還原原來的影像,雖然圖片看起來的整體亮度和原圖有差異,這張圖片的細節大部分還是完整的被保留了下來。

https://ithelp.ithome.com.tw/upload/images/20230925/201617328V6UcHoDW1.png


上一篇
【Day15】探索OpenCV中的影像平滑化:模糊、降噪
下一篇
【Day17】​使用OpenCV進行影像縮放、拼貼、剪裁
系列文
圖解C++影像處理與OpenCV應用:從基礎到高階,深入學習超硬核技術!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言