【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));

结果如下:

相关推荐
阿阳微客5 小时前
Steam 搬砖项目深度拆解:从抵触到真香的转型之路
前端·笔记·学习·游戏
Chef_Chen10 小时前
从0开始学习R语言--Day18--分类变量关联性检验
学习
键盘敲没电10 小时前
【IOS】GCD学习
学习·ios·objective-c·xcode
海的诗篇_10 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
AgilityBaby11 小时前
UE5 2D角色PaperZD插件动画状态机学习笔记
笔记·学习·ue5
AgilityBaby11 小时前
UE5 创建2D角色帧动画学习笔记
笔记·学习·ue5
武昌库里写JAVA12 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
一弓虽13 小时前
git 学习
git·学习
audyxiao00113 小时前
计算机视觉顶刊《International Journal of Computer Vision》2025年5月前沿热点可视化分析
图像处理·人工智能·opencv·目标检测·计算机视觉·大模型·视觉检测
Moonnnn.15 小时前
【单片机期末】串行口循环缓冲区发送
笔记·单片机·嵌入式硬件·学习