详解OpenCV的坐标重映射函数remap()的两种使用方法并附使用它得到图像的水平镜像和垂直镜像的示例代码

Posted 昊虹算法

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解OpenCV的坐标重映射函数remap()的两种使用方法并附使用它得到图像的水平镜像和垂直镜像的示例代码相关的知识,希望对你有一定的参考价值。

图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

OpenCV的函数remap()可以实现图像像素坐标的重映射。

什么叫图像像素坐标的重映射?

注意:“重映射”中的“重”字的含义是“重新”的意思。

下面这张图显示了什么叫像素坐标的重映射。

 上图中:
坐标为(1,2)的点的坐标变成了(7,8);
坐标为(1,3)的点的坐标变成了(7,9);
坐标为(1,4)的点的坐标变成了(7,10);

这就是坐标重映射的一个简单例子。

接下来说说函数remap()。

函数remap()的原型如下:

C++: void remap(InputArray src, 
				OutputArray dst, 
				InputArray map1, 
				InputArray map2, 
				int interpolation, 
				int borderMode=BORDER_CONSTANT, 
				const Scalar& borderValue=Scalar())

下面介绍各个参数的意义:

src---输入图像。
dst---目标图像,它和映射表map1的尺寸相同,数据类型与src相同。
map1---映射表map1,映射表map1可以是一通道的矩阵,也可以是二通道的矩阵。

当它为一通道的矩阵时,映射表map1中坐标为(x,y)的点的值代表把原图像中坐标为(x,y)的点进行重映射后其新坐标在x轴方向上的坐标。举个简单的例子,假如map1中坐标为(2,3)的点的值为5,则表示进行重映射后,原图中坐标为(2,3)的点的新坐标的x轴坐标变为5。当映射表map1为一通道时,其type只能为CV_32FC1。

当它为二通道矩阵时,映射表map1中坐标为(x,y)的点的两个通道值代表把原图像中坐标为(x,y)的点进行重映射后的新坐标值。举个简单的例子,假如map1中坐标为(2,3)的点的0通道为5,1通道值为7,则表示进行重映射后,原图中坐标为(2,3)的点的新坐标为(5,7)。当映射表map1为二通道时,其type可以为CV_16SC2或CV_32FC2。

正是由于映射表map1可以为一通道或两通道,所以我在本文标题中说函数remap()有两种使用方法。

map2---映射表map2,映射表map2只可以是一通道的矩阵。其意义与映射表map1相同,只是它里面存储的是映射后新坐标在y方向上的坐标。其type只能为CV_32FC1(虽然官方文档上说它的type可以为CV_16UC1,但是经实测是不行的)。当映射表map1为二通道矩阵时,map2为空矩阵,即按下面这句话定义:

cv::Mat map2;


interpolation---这个参数用于选择插值方式,可选的插值方式如下:

上图中用红线杠了的是不能选的插值方式。

问:函数remap()为什么会牵涉到插值?答:因为输出图像的尺寸并不一定和原图像尺寸相同,而与映射表的尺寸相同。举个例子来说,原图像为100×50的图像,而映射表是120×60的矩阵,那输出图像的尺寸也就是120×60,可见比原图扩大了,也就是说多出来一些像素点,那多出来的像素点的值怎么取呢?这就需要作插值运算来得到了。

borderMode--边界扩展模式,默认值为BORDER_CONSTANT。为什么函数remap()会牵涉到边界的扩展处理,因为在插值的时候我们需要用到窗口矩阵,一旦涉汲到窗口矩阵参与运算,就要牵涉到边界的扩展处理了。详情见我的另一篇博文:https://blog.csdn.net/wenhao_ir/article/details/124177989

borderValue---当边界扩展模式为BORDER_CONSTANT时,是需要指定一个常数的。borderValue的默认值为0。

下面是映射表map1为单通道时实现图像水平镜像和垂直镜像的源码:
源码中用到的图像下载链接:
链接:https://pan.baidu.com/s/1rmeL7Pz0AzM88iosUmjPFA 提取码:6pkm 

//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601

//OpenCV版本:3.0
//VS版本:2012

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>

int main()

    cv::Mat srcImage = cv::imread("F:/material/images/P0038-Four_colors.bmp"); 
    if(!srcImage.data) 
       return -1;
    // 输出矩阵定义
    cv::Mat resultImage(srcImage.size(), srcImage.type());
    // X与Y方向矩阵
    cv::Mat xMapImage(srcImage.size(), CV_32FC1);
    cv::Mat yMapImage(srcImage.size(), CV_32FC1);
    int rows = srcImage.rows;
    int cols = srcImage.cols;
    for( int j = 0; j < rows; j++ )
    
      for( int i = 0; i < cols; i++ )
       
            //下面这句代码实现原图像在水平方向上的镜像
            xMapImage.at<float>(j,i) = cols - i ;
			//下面这句代码实现原图像在垂直方向上的镜像
            yMapImage.at<float>(j,i) = rows - j ;
       
    
    // 重映射操作
    remap( srcImage, resultImage, xMapImage, yMapImage,
           CV_INTER_LINEAR, cv::BORDER_CONSTANT,
           cv::Scalar(0,0, 0) );
    // 输出结果
    cv::imshow("srcImage", srcImage);
    cv::imshow("resultImage", resultImage);
    cv::waitKey(0);
    return 0;

运行结果如下:

从上面可以看了,实际了原图像的水平镜像和垂直镜像。

下面是映射表map1为双通道时实现图像水平镜像的源码:
源码中用到的图像下载链接:
链接:https://pan.baidu.com/s/1rmeL7Pz0AzM88iosUmjPFA 提取码:6pkm 

//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601

//OpenCV版本:3.0
//VS版本:2012

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>


int main()

    cv::Mat srcImage = cv::imread("F:/material/images/P0038-Four_colors.bmp"); 
    if(!srcImage.data) 
       return -1;
    
	// 定义输出矩阵
    cv::Mat resultImage;

    cv::Mat Map1(srcImage.size(), CV_32FC2);//map1为双通道矩阵

	cv::Mat Map2;//当map1为双通道矩阵时,虽然不需要map2参与运算,但是也要初始化一个空的map2

	int rows = srcImage.rows;
    int cols = srcImage.cols;

	for( int i = 0; i < rows; i++ )
    
      for( int j = 0; j < cols; j++ )
           
		   //下面这两句代码实现原图像在水平方向上的镜像
            Map1.at<cv::Vec2f>(i,j)[0] = cols - j;
			Map1.at<cv::Vec2f>(i,j)[1] = i;
       
    


    // 重映射操作
    remap( srcImage, resultImage,Map1, Map2,CV_INTER_LINEAR, cv::BORDER_CONSTANT,cv::Scalar(0,0, 0) );

    // 输出结果
    cv::imshow("srcImage", srcImage);
    cv::imshow("resultImage", resultImage);
    cv::waitKey(0);
    return 0;

 从上面的代码中可以看出,代码实现了原图像的水平镜像。

图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

以上是关于详解OpenCV的坐标重映射函数remap()的两种使用方法并附使用它得到图像的水平镜像和垂直镜像的示例代码的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV - remap() - 获取黑色像素

OpenCV 例程300篇249. 图像的重映射(cv2.remap)

OpenCV 例程300篇249. 图像的重映射(cv2.remap)

如何在OpenCV中使用双重类型的地图重新映射

重映射

OpenCV探索之路:重映射与仿射变换