返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV 实现霍夫圆变换(52)
下一篇 :OpenCV实现仿射变换(54)
目标
在本教程中,您将学习如何:
一个。使用 OpenCV 函数 cv::remap 实现简单的重新映射例程。
理论
什么是重映射?
- 它是从图像中的一个位置获取像素并将它们定位在新图像中的另一个位置的过程。
- 为了完成映射过程,可能需要对非整数像素位置进行一些插值,因为源图像和目标图像之间并不总是存在一对一的像素对应关系。
-
我们可以表示每个像素位置的重新映射(x,y)如:
哪里g()是重新映射的图像,f()源图像和ℎ(x,y)是操作的映射函数(x,y).
-
让我们举个简单的例子。想象一下,我们有一个图像我而且,比如说,我们想做一个重新映射,以便:
会发生什么?很容易看出,图像会在x方向。例如,考虑输入图像:
观察红色圆圈相对于 x 如何改变位置(考虑到x水平方向):
- 在 OpenCV 中,函数 cv::remap 提供了一个简单的重新映射实现。
C++代码
- 这个程序是做什么的?
- 加载图像
- 每秒将 4 个不同的重新映射过程中的 1 个应用于图像,并在窗口中无限期地显示它们。
- 等待用户退出程序
-
- 教程代码如下所示。您也可以从这里下载
#include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include "opencv2/imgproc.hpp" #include <iostream> using namespace cv; void update_map( int &ind, Mat &map_x, Mat &map_y ); int main(int argc, const char** argv) { CommandLineParser parser(argc, argv, "{@image |chicky_512.png|input image name}"); std::string filename = parser.get<std::string>(0); Mat src = imread( samples::findFile( filename ), IMREAD_COLOR ); if (src.empty()) { std::cout << "Cannot read image: " << filename << std::endl; return -1; } Mat dst(src.size(), src.type()); Mat map_x(src.size(), CV_32FC1); Mat map_y(src.size(), CV_32FC1); const char* remap_window = "Remap demo"; namedWindow( remap_window, WINDOW_AUTOSIZE ); int ind = 0; for(;;) { update_map(ind, map_x, map_y); remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) ); imshow( remap_window, dst ); char c = (char)waitKey( 1000 ); if( c == 27 ) { break; } } return 0; } void update_map( int &ind, Mat &map_x, Mat &map_y ) { for( int i = 0; i < map_x.rows; i++ ) { for( int j = 0; j < map_x.cols; j++ ) { switch( ind ) { case 0: if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 ) { map_x.at<float>(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f; map_y.at<float>(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f; } else { map_x.at<float>(i, j) = 0; map_y.at<float>(i, j) = 0; } break; case 1: map_x.at<float>(i, j) = (float)j; map_y.at<float>(i, j) = (float)(map_x.rows - i); break; case 2: map_x.at<float>(i, j) = (float)(map_x.cols - j); map_y.at<float>(i, j) = (float)i; break; case 3: map_x.at<float>(i, j) = (float)(map_x.cols - j); map_y.at<float>(i, j) = (float)(map_x.rows - i); break; default: break; } // end of switch } } ind = (ind+1) % 4; }
解释
-
加载图像
Mat src = imread( samples::findFile( filename ), IMREAD_COLOR ); if (src.empty()) { std::cout << "Cannot read image: " << filename << std::endl; return -1; }
-
创建目标映像和两个映射矩阵(对于 x 和 y )
Mat dst(src.size(), src.type()); Mat map_x(src.size(), CV_32FC1); Mat map_y(src.size(), CV_32FC1);
-
创建一个窗口以显示结果
const char* remap_window = "Remap demo"; namedWindow( remap_window, WINDOW_AUTOSIZE );
-
建立循环。每隔 1000 毫秒,我们就会更新映射矩阵(mat_x 和 mat_y),并将它们应用于我们的源图像:
int ind = 0; for(;;) { update_map(ind, map_x, map_y); remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) ); imshow( remap_window, dst ); char c = (char)waitKey( 1000 ); if( c == 27 ) { break; } }
void update_map( int &ind, Mat &map_x, Mat &map_y )
{
for( int i = 0; i < map_x.rows; i++ )
{
for( int j = 0; j < map_x.cols; j++ )
{
switch( ind )
{
case 0:
if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 )
{
map_x.at<float>(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f;
map_y.at<float>(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f;
}
else
{
map_x.at<float>(i, j) = 0;
map_y.at<float>(i, j) = 0;
}
break;
case 1:
map_x.at<float>(i, j) = (float)j;
map_y.at<float>(i, j) = (float)(map_x.rows - i);
break;
case 2:
map_x.at<float>(i, j) = (float)(map_x.cols - j);
map_y.at<float>(i, j) = (float)i;
break;
case 3:
map_x.at<float>(i, j) = (float)(map_x.cols - j);
map_y.at<float>(i, j) = (float)(map_x.rows - i);
break;
default:
break;
} // end of switch
}
}
ind = (ind+1) % 4;
}
结果
- 编译上面的代码后,您可以执行它,并给出一个图像路径作为参数。例如,使用下图:
- 这是将其减小到一半大小并居中的结果:
- 把它颠倒过来:
- 在 x 方向上反映它
- 在两个方向上反映它:
参考文献:
1、《Remapping》------Ana Huamán