OpenCV重新映射(Remapping)

2018-09-14 10:48 更新

目標

在本教程中,您將學習如何:

使用OpenCV函數(shù)cv :: remap來實現(xiàn)簡單的重映射例程。

理論

什么是重映射?

  • 這是從圖像中的一個位置獲取像素并將它們定位在新圖像中的另一位置的過程。
  • 為了完成映射過程,可能需要對非整數(shù)像素位置進行一些插值,因為在源圖像和目的圖像之間不一定存在一對一像素的對應關(guān)系。
  • 我們可以將每個像素位置的重映射表示為:(x,y)

OpenCV重新映射

其中 g() 是重映射圖像, f() 源圖像和 h(x,y),是對操作(x,y)的映射函數(shù)。

  • 讓我們來看一個簡單的例子。想象一下,我們有一個圖像 I,我們想做一個重映射,比如:

OpenCV重新映射

會發(fā)生什么?很容易看出,圖像將在x方向上翻轉(zhuǎn)。例如,輸入圖像:

OpenCV重新映射

觀察紅色圓圈如何相對于x改變位置(考慮x的水平方向):

OpenCV重新映射    

  • OpenCV中,函數(shù)cv :: remap提供了一個簡單的重新映射實現(xiàn)。

Code

  • 這個程序是做什么的?
  1. 加載圖像
  2. 每秒,將4個不同的重映射過程中的1個應用于圖像,并在窗口中無限期地顯示它們。
  3. 等待用戶退出程序
  • 教程代碼如下所示。您也可以從這里下載
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
Mat src, dst;
Mat map_x, map_y;
const char* remap_window = "Remap demo";
int ind = 0;
void update_map( void );
int main(int argc, const char** argv)
{
  CommandLineParser parser(argc, argv, "{@image |../data/chicky_512.png|input image name}");
  std::string filename = parser.get<std::string>(0);
  src = imread( filename, IMREAD_COLOR );
  dst.create( src.size(), src.type() );
  map_x.create( src.size(), CV_32FC1 );
  map_y.create( src.size(), CV_32FC1 );
  namedWindow( remap_window, WINDOW_AUTOSIZE );
  for(;;)
  {
    char c = (char)waitKey( 1000 );
    if( c == 27 )
      { break; }
    update_map();
    remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) );
    // Display results
    imshow( remap_window, dst );
  }
  return 0;
}
void update_map( void )
{
  ind = ind%4;
  for( int j = 0; j < src.rows; j++ )
    { for( int i = 0; i < src.cols; i++ )
     {
           switch( ind )
         {
         case 0:
           if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 )
                 {
               map_x.at<float>(j,i) = 2*( i - src.cols*0.25f ) + 0.5f ;
               map_y.at<float>(j,i) = 2*( j - src.rows*0.25f ) + 0.5f ;
              }
           else
         { map_x.at<float>(j,i) = 0 ;
               map_y.at<float>(j,i) = 0 ;
                 }
                   break;
         case 1:
               map_x.at<float>(j,i) = (float)i ;
               map_y.at<float>(j,i) = (float)(src.rows - j) ;
           break;
             case 2:
               map_x.at<float>(j,i) = (float)(src.cols - i) ;
               map_y.at<float>(j,i) = (float)j ;
           break;
             case 3:
               map_x.at<float>(j,i) = (float)(src.cols - i) ;
               map_y.at<float>(j,i) = (float)(src.rows - j) ;
           break;
             } // end of switch
     }
    }
  ind++;
}

說明

  • 創(chuàng)建一些我們將使用的變量:
Mat src,dst;
Mat map_x,map_y;
char * remap_window = “ Remap demo” ;
int ind = 0;
  • 加載圖片:
src = imread(argv [1],1);
  • 創(chuàng)建目標圖像和兩個映射矩陣(對于x和y)
dst.create(src.size(),src.type());
map_x.create(src.size(),CV_32FC1);
map_y.create(src.size(),CV_32FC1);
  • 創(chuàng)建一個窗口以顯示結(jié)果
namedWindow(remap_window,WINDOW_AUTOSIZE);
  • 建立循環(huán)。每1000 ms我們更新我們的映射矩陣(mat_x和mat_y)并將它們應用到我們的源映像:
while(true)
{
  char c =(char)waitKey(1000);
  如果(c == 27)
    { break ; }
  update_map();
  remap(src,dst,map_x,map_y,INTER_LINEAR,BORDER_CONSTANT,Scalar(0,0,0));
  imshow(remap_window,dst);
}

應用重映射的函數(shù)是cv :: remap。我們給出以下參數(shù):

  1. src:源圖像
  2. dst:與src大小相同的目標映像
  3. map_x:x方向的映射函數(shù)。它等價于的第一個分量,h(i,j)
  4. map_y:同上,但在y方向。請注意,map_y和map_x的大小與src大小相同
  5. INTER_LINEAR:用于非整數(shù)像素的插值類型。這是默認情況。
  6. BORDER_CONSTANT:默認

我們?nèi)绾胃挛覀兊挠成渚仃噈at_x和mat_y?繼續(xù)閱讀:

  • 更新映射矩陣:我們將執(zhí)行4種不同的映射:

將圖片縮小到一半,并顯示在中間:

OpenCV重新映射

對所有的 (i,j) ,比如:OpenCV

將圖像倒過來:重新映射

從左到右反映圖像:OpenCV重新映射

b和c的組合:OpenCV重新映射

這在以下代碼片段中表示。這里,map_x表示第一坐標H(I,J)和map_y第二坐標。

for( int j = 0; j < src.rows; j++ )
{ for( int i = 0; i < src.cols; i++ )
{
      switch( ind )
  {
    case 0:
      if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 )
            {
          map_x.at<float>(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ;
          map_y.at<float>(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ;
         }
      else
    { map_x.at<float>(j,i) = 0 ;
          map_y.at<float>(j,i) = 0 ;
            }
              break;
    case 1:
          map_x.at<float>(j,i) = i ;
          map_y.at<float>(j,i) = src.rows - j ;
      break;
        case 2:
          map_x.at<float>(j,i) = src.cols - i ;
          map_y.at<float>(j,i) = j ;
      break;
        case 3:
          map_x.at<float>(j,i) = src.cols - i ;
          map_y.at<float>(j,i) = src.rows - j ;
      break;
      } // end of switch
}
  }
 ind++;
}

結(jié)果

在編譯上面的代碼之后,可以執(zhí)行它作為參數(shù)給出一個圖像路徑。例如,通過使用以下圖像:

OpenCV重新映射

這是將其減小到一半的大小并使其居中的結(jié)果:

OpenCV重新映射

把它顛倒過來:

OpenCV重新映射

反映在x方向:

OpenCV重新映射

反映在兩個方向:

OpenCV重新映射

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號