【OpenCV C++20 学习笔记】图片重映射-remapping

图片重映射-remapping

原理

图片重映射就是将图片中的像素从一个位置移动到另一个位置,从而形成新的图片。

要实现图片重映射,就有必要使用插值和非整数的像素位置,因此原图和结果图之间不可能总是存在像素的一一对应的。

用公式表示重映射的过程,如下:
g ( x , y ) = f ( h ( x , y ) ) g(x,y)=f(h(x,y)) g(x,y)=f(h(x,y))

  • g ( x , y ) g(x,y) g(x,y)是重映射之后的结果图
  • f ( ) f() f()是原图
  • h ( x , y ) h(x,y) h(x,y)是在 ( x , y ) (x,y) (x,y)像素上的重映射操作

比如,对图片 I I I进行如下重映射操作:
h ( x , y ) = ( I . c o l s − x , y ) h(x,y)=(I.cols-x,y) h(x,y)=(I.cols−x,y)

通过用总列数减去当前列数,可以实现图片在 x x x方向的翻转,就像下面的2张图一样:

API

在OpenCV中,提供了专门的重映射方法cv::remap(),其函数原型如下:

cpp 复制代码
void cv::remap(	InputArray		src,
				OutputArray		dst,
				InputArray		map1,
				InputArray		map2,
				int				interpolation,
				int				borderMode = BORDER_CONSTANT,
				const Scalar&	borderValue = Scalar())
  • 虽然该函数提供了map1map2两个map参数,但是可以只有map1用来提供 ( x , y ) (x,y) (x,y)两个方向上的重映射矩阵;或者map1提供 x x x上的重映射矩阵、而map2提供 y y y上的重映射矩阵;
  • interpolation参数指定插值方法,包括:双线性插值(INTER_LINEAR)、双三次插值(INTER_CUBIC)、最近邻插值(INTER_NEARES)等;注意,在该函数中不能使用INTER_AREA, INTER_LINEAR_EXACT, INTER_NEAREST_EXACT,这3中插值方法
  • borderMode为边框扩充方法,默认是BORDER_CONSTANT,即单一颜色边框;当该参数取BORDER_TRANSPARENT值的时候,表示重映射后超出原图范围的像素将不会被函数修改
  • borderValue只有在borderMode = BORDER_CONSTANT的时候才需要提供,用来指定边框的颜色。

注意:该函数目前只支持小于 32767 × 32767 32767 \times 32767 32767×32767尺寸的输入图和输出图。

实现

重映射的关键就是建立映射矩阵。映射矩阵要和原图具有相同的尺寸:

cpp 复制代码
	//导入原图
	Mat src{ imread("chicky_512.png", IMREAD_COLOR) };

	Mat map_x(src.size(), CV_32FC1);//建立x方向上的空映射矩阵
	Mat map_y(src.size(), CV_32FC1);//建立y方向上的空映射矩阵

接下来就是往映射矩阵里面填值了。

这个例子中我们分别建立下面4种重映射矩阵:

缩小成原来的一半并在中间显示

这种重映射的算法如下:

对于第 i i i列、第 j j j行的像素,如果: s r c . c o l s 4 < i < 3 ⋅ s r c . c o l s 4 \frac{src.cols}{4}<i<\frac{3 \cdot src.cols}{4} 4src.cols<i<43⋅src.cols且 s r c . r o w s 4 < i < 3 ⋅ s r c . r o w s 4 \frac{src.rows}{4}<i<\frac{3 \cdot src.rows}{4} 4src.rows<i<43⋅src.rows
h ( i , j ) = ( 2 × i − s r c . c o l s / 2 + 0.5 , 2 × j − s r c . r o w s / 2 + 0.5 ) h(i,j)=(2 \times i-src.cols/2+0.5, 2 \times j-src.rows/2+0.5) h(i,j)=(2×i−src.cols/2+0.5,2×j−src.rows/2+0.5)

经过上面的计算,连续的整数就会变成相差2的浮点数或整数数列。这样行和列就只有原来的一半了。

而不满足 s r c . c o l s 4 < i < 3 ⋅ s r c . c o l s 4 \frac{src.cols}{4}<i<\frac{3 \cdot src.cols}{4} 4src.cols<i<43⋅src.cols且 s r c . r o w s 4 < i < 3 ⋅ s r c . r o w s 4 \frac{src.rows}{4}<i<\frac{3 \cdot src.rows}{4} 4src.rows<i<43⋅src.rows条件,即位于原图的最外围1/4的像素点,全部设为0,即黑色。

所以,重映射矩阵中只要将上式的计算结果储存到相应位置就可以了(x方向和y方向分开储存)其实现代码如下:

cpp 复制代码
void zoomOutMap(Mat& map_x, Mat& map_y)
{
	for (int i{ 0 }; i < map_x.rows; i++) {
		for (int j{ 0 }; j < map_x.cols; j++) {
			//判断是否满足位置条件
			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_y.rows * 0.25f) + 0.5f;
			}
			else {//不满组则设为0
				map_x.at<float>(i, j) = 0;
				map_y.at<float>(i, j) = 0;
			}
		}
	}
}

调用函数完成重映射矩阵的初始化,然后进行重映射:

cpp 复制代码
Mat zoomOut{ src.size(), src.type() };	//结果图片
zoomOutMap(map_x, map_y);				//调用重映射矩阵初始化函数
//重映射方法
remap(src, zoomOut, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));

结果如下:

上下翻转

上下翻转的重映射算法如下:
h ( i , j ) = ( i , s r c . r o w s − j ) h(i,j)=(i,src.rows-j) h(i,j)=(i,src.rows−j)

用总行数减去当前行数,就可以实现上下翻转了。

重映射矩阵的实现代码如下:

cpp 复制代码
void upDownMap(Mat& map_x, Mat& map_y)
{
	for (int i{ 0 }; i < map_x.rows; i++) {
		for (int j{ 0 }; j < map_x.cols; j++) {
			map_x.at<float>(i, j) = static_cast<float>(j);
			map_y.at<float>(i, j) = static_cast<float>(map_x.rows - i);
		}
	}
}

调用函数初始化重映射矩阵,然后进行重映射操作:

cpp 复制代码
Mat upDown;
upDownMap(map_x, map_y);
remap(src, upDown, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));

结果如下:

左右翻转

左右翻转的重映射算法与上下翻转的类似:
h ( i , j ) = ( s r c . c o l s − i , j ) h(i,j)=(src.cols - i, j) h(i,j)=(src.cols−i,j)

其重映射矩阵的实现代码如下:

cpp 复制代码
void leftRightMap(Mat& map_x, Mat& map_y)
{
	for (int i{ 0 }; i < map_x.rows; i++) {
		for (int j{ 0 }; j < map_x.cols; j++) {
			map_x.at<float>(i, j) = static_cast<float>(map_x.cols - j);
			map_y.at<float>(i, j) = static_cast<float>(i);
		}
	}
}

调用函数对重映射矩阵进行初始化,然后进行重映射操作:

cpp 复制代码
Mat leftRight;
leftRightMap(map_x, map_y);
remap(src, leftRight, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));

上下翻转+左右翻转

将两个重映射算法合并
h ( i , j ) = ( s r c . c o l s − i , s r c . r o w s − j ) h(i,j)=(src.cols-i, src.rows-j) h(i,j)=(src.cols−i,src.rows−j)

其重映射矩阵的实现代码如下:

cpp 复制代码
void reverseMap(Mat& map_x, Mat& map_y)
{
	for (int i{ 0 }; i < map_x.rows; i++) {
		for (int j{ 0 }; j < map_x.cols; j++) {
			map_x.at<float>(i, j) = static_cast<float>(map_x.cols - j);
			map_y.at<float>(i, j) = static_cast<float>(map_x.rows - i);
		}
	}
}

调用函数对重映射矩阵进行初始化,然后进行重映射操作:

cpp 复制代码
Mat reverse;
reverseMap(map_x, map_y);
remap(src, reverse, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));

结果如下:

相关推荐
小弥儿16 小时前
GitHub今日热榜 | 2026-09-18:腾讯双项目上榜
学习·开源
知识分享小能手18 小时前
深度学习学习教程,从入门到精通,深度生成模型 —— 知识点详解与代码实现(20)
人工智能·深度学习·学习
He BianGu18 小时前
【WPF-VisionMaster】机器视觉通用平台V5.0版本发行说明
opencv·c#·wpf·halcon·机器视觉·visionmaster
m4Rk_18 小时前
【论文阅读】Agent 记忆机制(74):CompassMem——从相似度检索走向事件图上的记忆导航
论文阅读·人工智能·学习·开源·github
小李不想当小白18 小时前
DMA直接存储器存取(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·分享
hhzz19 小时前
【OpenCV 入门到精通 10】视频分析与光流跟踪:背景减除与运动检测
人工智能·python·opencv·性能优化
2601_9499506320 小时前
练题簿在线练题全流程:从资料导入到模拟考试与错题复盘
学习·考研·小程序·刷题·小程序推荐
别动我齐刘海20 小时前
ROS2 Jazzy + C++ 实战路线——基础学习2
c++·人工智能·vscode·python·学习·机器学习·机器人
水云桐程序员1 天前
量子力学概论,量子力学概念总结
笔记·科技·学习·量子计算
三克的油1 天前
java-学习1
java·开发语言·学习