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

運行結果:

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