1. 程式人生 > >OpenCV3圖片縮放

OpenCV3圖片縮放

圖片縮放方式

如果放大或者縮小圖片的尺寸,可以使用OpenCV為我們提供的如下兩種方式:
(1)resize函式。這是最直接的方式,
(2)pyrUp()pyrDown()函式。即影象金字塔相關的兩個函式,對影象進行向上取樣,向下取樣的操作。
pyrUp、pyrDown其實和專門用作放大縮小影象尺寸的resize在功能上差不多,披著影象金字塔的皮,說白了還是在對影象進行放大和縮小操作。另外需要指出的是,pyrUp、pyrDown在OpenCV的imgproc模組中的Image Filtering子模組裡。而resize在imgproc 模組的Geometric Image Transformations子模組裡。

影象金字塔

影象金字塔是影象中多尺度表達的一種,最主要用於影象的分割,是一種以多解析度來解釋影象的有效但概念簡單的結構。
影象金字塔最初用於機器視覺和影象壓縮,一幅影象的金字塔是一系列以金字塔形狀排列的解析度逐步降低,且來源於同一張原始圖的影象集合。其通過梯次向下取樣獲得,直到達到某個終止條件才停止取樣。
金字塔的底部是待處理影象的高解析度表示,而頂部是低解析度的近似。
我們將一層一層的影象比喻成金字塔,層級越高,則影象越小,解析度越低。

一般情況下有兩種型別的影象金字塔常常出現在文獻和以及實際運用中。他們分別是:
高斯金字塔(Gaussianpyramid): 用來向下取樣,主要的影象金字塔
拉普拉斯金字塔(Laplacianpyramid)

: 用來從金字塔低層影象重建上層未取樣影象,在數字影象處理中也即是預測殘差,可以對影象進行最大程度的還原,配合高斯金字塔一起使用。
兩者的簡要區別:高斯金字塔用來向下降取樣影象,而拉普拉斯金字塔則用來從金字塔底層影象中向上取樣重建一個影象。
要從金字塔第i層生成第i+1層(我們表示第i+1層為G_i+1),我們先要用高斯核對G_1進行卷積,然後刪除所有偶數行和偶數列。當然的是,新得到影象面積會變為源影象的四分之一。按上述過程對輸入影象G_0執行操作就可產生出整個金字塔。
當影象向金字塔的上層移動時,尺寸和解析度就降低。OpenCV中,從金字塔中上一級影象生成下一級影象的可以用PryDown。而通過PryUp將現有的影象在每個維度都放大兩遍。
影象金字塔中的向上和向下取樣分別通過OpenCV函式 pyrUp 和 pyrDown 實現。
對影象向上取樣:pyrUp函式
對影象向下取樣:pyrDown函式
但需要注意的是,PryUp和PryDown不是互逆的,即PryUp不是降取樣的逆操作。這種情況下,影象首先在每個維度上擴大為原來的兩倍,新增的行(偶數行)以0填充。然後給指定的濾波器進行卷積(實際上是一個在每個維度都擴大為原來兩倍的過濾器)去估計“丟失”畫素的近似值。

PryDown( )是一個會丟失資訊的函式。為了恢復原來更高的解析度的影象,我們要獲得由降取樣操作丟失的資訊,這些資料就和拉普拉斯金字塔有關係了。

示例

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//		描述:包含程式所依賴的標頭檔案
//---------------------------------------------------------------------------------------------- 
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

//-----------------------------------【巨集定義部分】--------------------------------------------
//	描述:定義一些輔助巨集
//------------------------------------------------------------------------------------------------
#define WINDOW_NAME "【程式視窗】"		//為視窗標題定義的巨集


//-----------------------------------【名稱空間宣告部分】--------------------------------------
//		描述:包含程式所使用的名稱空間
//----------------------------------------------------------------------------------------------- 
using namespace std;
using namespace cv;


//-----------------------------------【全域性變數宣告部分】--------------------------------------
//		描述:全域性變數宣告
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_dstImage, g_tmpImage;


//-----------------------------------【全域性函式宣告部分】--------------------------------------
//		描述:全域性函式宣告
//-----------------------------------------------------------------------------------------------
static void ShowHelpText();


//-----------------------------------【ShowHelpText( )函式】----------------------------------
//		描述:輸出一些幫助資訊
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
	//輸出一些幫助資訊
	printf("\n\n\n\t歡迎來到OpenCV影象金字塔和resize示例程式~\n\n");
	printf("\n\n\t按鍵操作說明: \n\n"
		"\t\t鍵盤按鍵【ESC】或者【Q】- 退出程式\n"
		"\t\t鍵盤按鍵【1】或者【W】- 進行基於【resize】函式的圖片放大\n"
		"\t\t鍵盤按鍵【2】或者【S】- 進行基於【resize】函式的圖片縮小\n"
		"\t\t鍵盤按鍵【3】或者【A】- 進行基於【pyrUp】函式的圖片放大\n"
		"\t\t鍵盤按鍵【4】或者【D】- 進行基於【pyrDown】函式的圖片縮小\n"
	);
}

//-----------------------------------【main( )函式】--------------------------------------------
//		描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main()
{
	//顯示幫助文字
	ShowHelpText();

	//載入原圖
	g_srcImage = imread("jinan.jpg");//工程目錄下需要有一張名為jinan.jpg的測試影象,且其尺寸需被2的N次方整除,N為可以縮放的次數
	if (!g_srcImage.data) { printf("讀取srcImage錯誤~! \n"); return false; }

	// 建立顯示視窗
	namedWindow(WINDOW_NAME, CV_WINDOW_AUTOSIZE);
	imshow(WINDOW_NAME, g_srcImage);

	//引數賦值
	g_tmpImage = g_srcImage;
	g_dstImage = g_tmpImage;

	int key = 0;

	//輪詢獲取按鍵資訊
	while (1)
	{
		key = waitKey(9);//讀取鍵值到key變數中

		//根據key變數的值,進行不同的操作
		switch (key)
		{
			//======================【程式退出相關鍵值處理】=======================  
		case 27://按鍵ESC
			return 0;
			break;

		case 'q'://按鍵Q
			return 0;
			break;

			//======================【圖片放大相關鍵值處理】=======================  
		case 'a'://按鍵A按下,呼叫pyrUp函式
			pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
			printf(">檢測到按鍵【A】被按下,開始進行基於【pyrUp】函式的圖片放大:圖片尺寸×2 \n");
			break;

		case 'w'://按鍵W按下,呼叫resize函式
			resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
			printf(">檢測到按鍵【W】被按下,開始進行基於【resize】函式的圖片放大:圖片尺寸×2 \n");
			break;

		case '1'://按鍵1按下,呼叫resize函式
			resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
			printf(">檢測到按鍵【1】被按下,開始進行基於【resize】函式的圖片放大:圖片尺寸×2 \n");
			break;

		case '3': //按鍵3按下,呼叫pyrUp函式
			pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
			printf(">檢測到按鍵【3】被按下,開始進行基於【pyrUp】函式的圖片放大:圖片尺寸×2 \n");
			break;
			//======================【圖片縮小相關鍵值處理】=======================  
		case 'd': //按鍵D按下,呼叫pyrDown函式
			pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
			printf(">檢測到按鍵【D】被按下,開始進行基於【pyrDown】函式的圖片縮小:圖片尺寸/2\n");
			break;

		case  's': //按鍵S按下,呼叫resize函式
			resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
			printf(">檢測到按鍵【S】被按下,開始進行基於【resize】函式的圖片縮小:圖片尺寸/2\n");
			break;

		case '2'://按鍵2按下,呼叫resize函式
			resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2), (0, 0), (0, 0), 2);
			printf(">檢測到按鍵【2】被按下,開始進行基於【resize】函式的圖片縮小:圖片尺寸/2\n");
			break;

		case '4': //按鍵4按下,呼叫pyrDown函式
			pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
			printf(">檢測到按鍵【4】被按下,開始進行基於【pyrDown】函式的圖片縮小:圖片尺寸/2\n");
			break;
		}

		//經過操作後,顯示變化後的圖
		imshow(WINDOW_NAME, g_dstImage);

		//將g_dstImage賦給g_tmpImage,方便下一次迴圈
		g_tmpImage = g_dstImage;
	}

	return 0;
}