天天看點

OpenCV3圖檔縮放和圖像金字塔

圖檔縮放方式

如果放大或者縮小圖檔的尺寸,可以使用OpenCV為我們提供的如下兩種方式:

(1)​​

​resize​

​​函數。這是最直接的方式,

(2)​​

​pyrUp()​

​​、​

​pyrDown()​

​​函數。即圖像金字塔相關的兩個函數,對圖像進行向上采樣,向下采樣的操作。

pyrUp、pyrDown其實和專門用作放大縮小圖像尺寸的resize在功能上差不多,披着圖像金字塔的皮,說白了還是在對圖像進行放大和縮小操作。另外需要指出的是,pyrUp、pyrDown在OpenCV的​​

​imgproc​

​​子產品中的​

​Image Filtering​

​​子子產品裡。而resize在​

​imgproc​

​ 子產品的Geometric Image Transformations子子產品裡。

圖像金字塔

圖像金字塔是圖像中多尺度表達的一種,最主要用于圖像的分割,是一種以多分辨率來解釋圖像的有效但概念簡單的結構。

圖像金字塔最初用于機器視覺和圖像壓縮,一幅圖像的金字塔是一系列以金字塔形狀排列的分辨率逐漸降低,且來源于同一張原始圖的圖像集合。其通過梯次向下采樣獲得,直到達到某個終止條件才停止采樣。

金字塔的底部是待處理圖像的高分辨率表示,而頂部是低分辨率的近似。

我們将一層一層的圖像比喻成金字塔,層級越高,則圖像越小,分辨率越低。

OpenCV3圖檔縮放和圖像金字塔

一般情況下有兩種類型的圖像金字塔常常出現在文獻和以及實際運用中。他們分别是:

高斯金字塔(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填充。然後給指定的濾波器進行卷積(實際上是一個在每個次元都擴大為原來兩倍的過濾器)去估計“丢失”像素的近似值。

示例

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

//-----------------------------------【宏定義部分】--------------------------------------------
//  描述:定義一些輔助宏
//------------------------------------------------------------------------------------------------
#define//為視窗标題定義的宏


//-----------------------------------【命名空間聲明部分】--------------------------------------
//    描述:包含程式所使用的命名空間
//----------------------------------------------------------------------------------------------- 
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;
}