1、重映射的概念
重映射是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。为了完成重映射过程,需要获得一些插值为非整数像素的坐标,因为源图像与目标图像像素坐标不是一一对应的。如下:
g(x,y)=f(h(x,y))
g()是目标图像,f()是源图像,而h(x,y)是作用于(x,y)的映射方法函数。
2、实现重映射:remap()函数
remap()函数会根据指定的映射形式,将源图像进行重映射几何变换:
dst(x,y)=src(mapx(x,y),mapy(x,y))
void remap(InputArray src,OutputArray dst,InputArray map1,InputArray map2,int interpolation,intborderMode=BORDER_CONSTANT,const Scalar& borderValue=Scalar())
第一个参数:输出图像
第二个参数:函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和源图片有一样的尺寸和类型
第三个参数:InputArray类型的 map1,有两种可能的表示对象
表示点(x,y)的第一映射
表示CV_16SC2,CV_32FC1,CV_32FC2类型的X值
第四个参数:InputArray类型的 map2,有两种可能的表示对象
若map1表示点(x,y)时,这个参数不代表任何值
表示CV_16UC1,CV_32FC1类型的Y值
第五个参数:int 类型的interpolation,插值方式,所选的插值方式如下:
INTER_NEAREST——最近邻插值
INTER_LINEAR——双线性插值(默认值)
INTER_CUBIC——双三次样条插值
INTER_LANCZOS4——Lanczos插值
第六个参数:边界模式
第七个参数:const Scalar&类型的borderValue,当有常数边界使用的值,其默认值Scalar(),即默认值为0
3、基本重映射
说明:下面是精简后的以remap函数为核心的示例程序
#include<opencv2/opencv.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> #include<iostream> using namespace std; using namespace cv; int main() { Mat srcImage, dstImage; Mat map_x, map_y; //载入原始图像 srcImage = imread("E:\\Pec\\car.jpg", 1); imshow("【原始图】", srcImage); //创建和原始图一样的效果图,x重映射图,y重映射图 dstImage.create(srcImage.size(), srcImage.type()); map_x.create(srcImage.size(), CV_32FC1); map_y.create(srcImage.size(), CV_32FC1); //双层循环,遍历每一个像素点,改变map_x和Map_y for (int j = 0; j < srcImage.rows; j++) { for (int i = 0; i < srcImage.cols; i++) { //通过at获取像素值 map_x.at<float>(j, i) = static_cast<float>(i); map_y.at<float>(j, i) = static_cast<float>(srcImage.rows-j); } } //进行重映射 remap(srcImage, dstImage, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0)); imshow("【效果图】", dstImage); waitKey(0); }
4、实现多重映射
#include<opencv2/opencv.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> #include<iostream> using namespace std; using namespace cv; #define WINDOW_NAME "【程序窗口】" Mat g_srcImage, g_dstImage; Mat g_map_x, g_map_y; int update_map(int key); static void ShowHelpText(); int main() { system("color 2F"); ShowHelpText(); //载入原始图像 g_srcImage = imread("E:\\Pec\\car.jpg", 1); imshow("【原始图】", g_srcImage); //创建和原始图一样的效果图,x重映射图,y重映射图 g_dstImage.create(g_srcImage.size(), g_srcImage.type()); g_map_x.create(g_srcImage.size(), CV_32FC1); g_map_y.create(g_srcImage.size(), CV_32FC1); namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE); imshow(WINDOW_NAME, g_srcImage); while (1) { int key = waitKey(0); if ((key & 255) == 27) { cout << "程序退出......" << endl; break; } update_map(key); //根据按下键盘按键来更新map_x & map_y的值,然后调用remap()进行重映射 remap(g_srcImage, g_dstImage, g_map_x, g_map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0)); imshow(WINDOW_NAME, g_dstImage); } return 0; } int update_map(int key) { //双层循环,遍历每一个像素点,改变map_x和Map_y for (int j = 0; j < g_srcImage.rows; j++) { for (int i = 0; i < g_srcImage.cols; i++) { switch (key) { case '1'://按键1按下,进行第一重映射操作 if (i > g_srcImage.cols*0.25&&i<g_srcImage.cols*0.75&&j>g_srcImage.rows*0.25&&j < g_srcImage.rows*0.75) { g_map_x.at<float>(j, i) = 2 * (i - g_srcImage.cols*0.25) + 0.5; g_map_y.at<float>(j, i) = 2 * (j - g_srcImage.rows*0.25) + 0.5; } else { g_map_x.at<float>(j, i) = 0; g_map_y.at<float>(j, i) = 0; } break; case '2': //按键2按下,进行第二重映射操作 g_map_x.at<float>(j, i) = static_cast<float>(i); g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows - j); break; case '3': g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols-i); g_map_y.at<float>(j, i) = static_cast<float>(j); break; case '4': g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols - i); g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows - j); break; } } } return 1; } static void ShowHelpText() { cout << "\n\n\n\t欢迎来到重映射示例程序\n"; cout << "\n\n\n\t按键操作如下:" ; cout << "\n\n\n\t键盘按下【ESC】-退出程序" ; cout << "\n\n\n\t键盘按下【1】-第一种映射方式,缩小2倍" ; cout << "\n\n\n\t键盘按下【2】-第二种映射方式,y方向翻转" ; cout << "\n\n\n\t键盘按下【3】-第三种映射方式,x方向反转" ; cout << "\n\n\n\t键盘按下【4】-第四种映射方式,x,y方向反转" ; }
第一种映射方式,缩小2倍
第二种映射方式,y方向翻转
第三种映射方式,x方向反转
第四种映射方式,x,y方向反转