【Learning OpenCV】OpenCV之像素重映射:remap()函数

一、映射是个什么玩意?

映射是个数学术语,指两个元素的集之间元素相互“对应”的关系,为名词。映射,或者射影,在数学及相关的领域经常等同于函数。 基于此,部分映射就相当于部分函数,而完全映射相当于完全函数。

说的简单点,每个人都有一个名字,都有身份证号,人对应人名字,对应自己的身份证号,这种对应关系就叫映射。

二、像素重映射是个什么玩意?

在这里插入图片描述
重映射,就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。为了完成映射过程, 我们需要获得一些插值为非整数像素的坐标,因为源图像与目标图像的像素坐标不是一一对应的。

一般情况下,我们通过重映射来表达每个像素的位置 (x,y),像这样 :

								g(x,y) = f ( h(x,y) )

在这里, g( ) 是目标图像, f() 是源图像, 而h(x,y) 是作用于 (x,y) 的映射方法函数。简单的说就是改变图片的位置(左,右,上,下,颠倒翻转)。

来看个例子。 若有一幅图像 I ,想满足下面的条件作重映射:

								h(x,y) = (I.cols - x, y )

这样的话,图像会按照 x 轴方向发生翻转。那么,源图像和效果图分别如下:

三、像素重映射API——cv::remap()

下面是函数原型:

cv::remap  (   
	InputArray	src,
	OutputArray	dst,
	InputArray	map1,
	InputArray	map2,
	int		interpolation,
	int		borderMode = BORDER_CONSTANT,
	const Scalar    borderValue = Scalar()
	)

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。

  • 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和源图片有一样的尺寸和类型。

  • 第三个参数,InputArray类型的map1,它有两种可能的表示对象。
    (1).表示点(x,y)的第一个映射。
    (2).表示CV_16SC2 , CV_32FC1 或CV_32FC2类型的X值。

  • 第四个参数,InputArray类型的map2,同样,它也有两种可能的表示对象,而且他是根据map1来确定表示哪种对象。
    (1).若map1表示点(x,y)时。这个参数不代表任何值。
    (2).表示CV_16UC1 , CV_32FC1类型的Y值(第二个值)。

  • 第五个参数,int类型的interpolation,插值方式,之前的resize( )函数中有讲到,需要注意,resize(
    )函数中提到的INTER_AREA插值方式在这里是不支持的,所以可选的插值方式如下:
    (1)INTER_NEAREST——最近邻插值
    (2)INTER_LINEAR——双线性插值(默认)
    (3)INTER_CUBIC——双三样条插值(逾4×4像素邻域内的双三次插值)
    (4)INTER_LANCZOS4——lanczos插值(逾8×8像素邻域的Lanczos插值)

  • 第六个参数,int类型的borderMode,边界模式,有默认值BORDER_CONSTANT,表示目标图像中“离群点(outliers)”的像素值不会被此函数修改。

  • 第七个参数,const Scalar&类型的borderValue,当有常数边界时使用的值,其有默认值Scalar( ),即默认值为0。

首先我们要先考虑,我们要做的情况有如下四种:缩小(行与列均为原来的1/2),左右翻转,上下翻转,中心旋转。下面的图表示的就是四种变换模式:
在这里插入图片描述
最简单的是翻转,行不变,第一列跟最后一列转换,第二列跟倒数第二列转换……这样就实现了左右翻转。同理,列不变,行转换,实现上下翻转。如果上下左右都翻转,那就是旋转180°,也就是中心旋转。

还有一个就是图像缩小,图像缩小就是将长跟宽变换为原来的1/2。所以只在图像的1/4处到3/4处有新图像。将图像范围控制在0原图像的0.25-0.75之间,其他的全部为0;设置图像的x和y方向的映射。对于x方向,是列的映射。图像的每个像素点,减去原图像的1/4,再×2,就表示图像缩小1/2.不过,经过我自己的实践发现,图像宽,高缩小一半不能再后面+0.5,应该是列不加,行-0.25。

四、测试代码

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

Mat src, dst,map_x,map_y;
const char* output_win = "remap demo";
int index = 0;
void update_map();

int main(int argc, char** argv)
{
	
	src = imread("C:\\Users\\admin\\Pictures/lena_std.tif");
	if (src.empty())
	{
		cout << "Loading image error..." << endl;
		return -1;
	}
	char input_win[] = "input image";
	namedWindow(input_win, WINDOW_AUTOSIZE);
	imshow(input_win, src);

	//创建和原始图一样的效果图,建立x映射表,y重映射图
	dst.create(src.size(), src.type());
	map_x.create(src.size(), CV_32FC1);
	map_y.create(src.size(), CV_32FC1);

	int c = 0;
	while (true)
	{
		c = waitKey(500);
		if ((char) c==27)
		{
			break;
		}
		index = c % 4;
		update_map();
		remap(src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 255, 255));
		imshow(output_win, dst);
	}

	return 0;
}

void update_map()
{
	//双层循环,遍历每一个像素点,改变map_x & map_y的值
	for (int row=0;row<src.rows;row++)
	{
		for (int col=0;col<src.cols;col++)
		{
			switch (index)
			{
				//index = 0 图像的行跟列为原来的1/2。
				//index = 1,为左右翻转(列变换,行不变)
				//index = 2,为上下翻转(行变换,列不变)
				//index = 3,为中心旋转
			case 0:
				if (col > (src.cols*0.25) && (col < src.cols*0.75) && row > (src.rows*0.25) && row < (src.rows*0.75))
				{
					map_x.at<float>(row, col) = 2 * (col - (src.cols*0.25));
					map_y.at<float>(row, col) = 2 * (row - (src.cols*0.25));
				}
				else
				{
					map_x.at<float>(row, col) = 0;
					map_y.at<float>(row, col) = 0;
				}
				break;
			case 1:
				map_x.at<float>(row, col) = src.cols - col - 1;
				map_y.at<float>(row, col) = row;
				break;
			case 2:
				map_x.at<float>(row, col) = col;
				map_y.at<float>(row, col) = src.rows - row - 1;
				break;
			case 3:
				map_x.at<float>(row, col) = src.cols - col - 1;
				map_y.at<float>(row, col) = src.rows - row - 1;

			default:
				break;
			}
		}
	}
}

在这里插入图片描述
1
在这里插入图片描述
图片3

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值