OpenCV重新映射(Remapping)
目标
在本教程中,您将学习如何:
使用OpenCV函数cv :: remap来实现简单的重映射例程。
理论
什么是重映射?
- 这是从图像中的一个位置获取像素并将它们定位在新图像中的另一位置的过程。
- 为了完成映射过程,可能需要对非整数像素位置进行一些插值,因为在源图像和目的图像之间不一定存在一对一像素的对应关系。
- 我们可以将每个像素位置的重映射表示为:(x,y)
其中 g() 是重映射图像, f() 源图像和 h(x,y),是对操作(x,y)的映射函数。
- 让我们来看一个简单的例子。想象一下,我们有一个图像 I,我们想做一个重映射,比如:
会发生什么?很容易看出,图像将在x方向上翻转。例如,输入图像:
观察红色圆圈如何相对于x改变位置(考虑x的水平方向):
- OpenCV中,函数cv :: remap提供了一个简单的重新映射实现。
Code
- 这个程序是做什么的?
- 加载图像
- 每秒,将4个不同的重映射过程中的1个应用于图像,并在窗口中无限期地显示它们。
- 等待用户退出程序
- 教程代码如下所示。您也可以从这里下载
#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++;
}
说明
- 创建一些我们将使用的变量:
Mat src,dst;
Mat map_x,map_y;
char * remap_window = “ Remap demo” ;
int ind = 0;
- 加载图片:
src = imread(argv [1],1);
- 创建目标图像和两个映射矩阵(对于x和y)
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);
- 建立循环。每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);
}
应用重映射的函数是cv :: remap。我们给出以下参数:
- src:源图像
- dst:与src大小相同的目标映像
- map_x:x方向的映射函数。它等价于的第一个分量,h(i,j)
- map_y:同上,但在y方向。请注意,map_y和map_x的大小与src大小相同
- INTER_LINEAR:用于非整数像素的插值类型。这是默认情况。
- BORDER_CONSTANT:默认
我们如何更新我们的映射矩阵mat_x和mat_y?继续阅读:
- 更新映射矩阵:我们将执行4种不同的映射:
将图片缩小到一半,并显示在中间:
对所有的 (i,j) ,比如:
将图像倒过来:
从左到右反映图像:
b和c的组合:
这在以下代码片段中表示。这里,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++;
}
结果
在编译上面的代码之后,可以执行它作为参数给出一个图像路径。例如,通过使用以下图像:
这是将其减小到一半的大小并使其居中的结果:
把它颠倒过来:
反映在x方向:
反映在两个方向: