[OpenCV] 数字图像处理 C++ 学习——15像素重映射(cv::remap) 附完整代码

前言

像素重映射将图像中的每个像素映射到新位置,实现图像的扭曲、校正等操作。在 OpenCV 中,cv::remap() 函数就是用于实现这种功能的。本文将详细介绍像素重映射的基本原理以及在 OpenCV 中的实现方法,并给出完整代码。

1.像素重映射理论基础

像素重映射的原理是将图像的每个像素通过预定义的映射规则重新分配到新的位置。映射规则可以是任意的数学函数,比如旋转、缩放、扭曲等,甚至可以通过查表的方式进行非线性的映射。

在这里插入图片描述

像素重映射可以用以下公式表示:
dst ( x ′ , y ′ ) = src ( x , y ) \text{dst}(x', y') = \text{src}(x, y) dst(x,y)=src(x,y)
其中 (x, y) 是源图像中的像素位置,(x', y') 是目标图像中的像素位置。通过映射函数,可以将源图像的像素映射到目标图像的相应位置。

常见的重映射应用

图像扭曲:将图像以某种方式进行扭曲处理,使其变形。

镜头畸变校正:通过重映射可以校正图像中由于镜头引起的畸变,如鱼眼镜头畸变。

图像旋转与缩放:可以将图像按照指定的角度和比例进行旋转与缩放。

2.代码实现

实验用到图像,供学习使用sherlock.jpg

(1) remap()细节

cv::remap(
InputArray src,// 输入图像
OutputArray dst,// 输出图像
InputArray  map1,// x 映射表 CV_32FC1/CV_32FC2
InputArray map2,// y 映射表
int interpolation,// 选择的插值方法,常见线性插值,可选择立方等
int borderMode,// 指定图像边界的处理方式,默认为 BORDER_CONSTANT。
const Scalar borderValue// 用于边界像素的值,默认是黑色。
)

(2)水平翻转

map1 中的列坐标从右向左映射,map2 保持原始的行坐标不变。

for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			map1.at<float>(i, j) = static_cast<float>(src.cols - j - 1);  // 水平翻转
			map2.at<float>(i, j) = static_cast<float>(i);
		}
	}
	Mat dst_hflip;
	remap(src, dst_hflip, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	namedWindow("Horizontal Flip", WINDOW_AUTOSIZE);
	imshow("Horizontal Flip", dst_hflip);

结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)垂直翻转

map2 中的行坐标从下向上映射,而 map1 保持列坐标不变。

	// 2. 垂直翻转
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			map1.at<float>(i, j) = static_cast<float>(j);
			map2.at<float>(i, j) = static_cast<float>(src.rows - i - 1);  // 垂直翻转
		}
	}
	Mat dst_vflip;
	remap(src, dst_vflip, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	
	namedWindow("Vertical Flip", WINDOW_AUTOSIZE);
	imshow("Vertical Flip", dst_vflip);

结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)旋转 180 度

同时进行水平和垂直翻转

	// 3. 旋转 180 度
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			map1.at<float>(i, j) = static_cast<float>(src.cols - j - 1);  // 水平翻转
			map2.at<float>(i, j) = static_cast<float>(src.rows - i - 1);  // 垂直翻转
		}
	}
	Mat dst_rotate180;
	remap(src, dst_rotate180, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	namedWindow("Rotate 180 degrees", WINDOW_AUTOSIZE);
	imshow("Rotate 180 degrees", dst_rotate180);

结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(4)径向扭曲

通过对极坐标中的半径进行二次变换,产生径向扭曲效果,图像向中心点扭曲,产生类似鱼眼镜头的效果。

	// 4. 径向扭曲效果
	float cx = src.cols / 2.0;
	float cy = src.rows / 2.0;
	float radius = min(cx, cy);
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			float dx = j - cx;
			float dy = i - cy;
			float r = sqrt(dx * dx + dy * dy);
			float theta = atan2(dy, dx);

			float r_distorted = radius * (r / radius) * (r / radius);  // 径向扭曲
			map1.at<float>(i, j) = cx + r_distorted * cos(theta);
			map2.at<float>(i, j) = cy + r_distorted * sin(theta);
		}
	}
	Mat dst_radial;
	remap(src, dst_radial, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	namedWindow("Radial Distortion", WINDOW_AUTOSIZE);
	imshow("Radial Distortion", dst_radial);

结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.完整代码

#include<opencv2/opencv.hpp>
#include<highgui.hpp>
#include<iostream>
#include<math.h>

using namespace cv;
using namespace std;

void remap_image()
{
	cv::Mat src;
	src = imread("sherlock.jpg");
	if (src.empty()) {
		printf("could not find the image...\n");
		return;
	}
	namedWindow("Source Image", WINDOW_AUTOSIZE);
	imshow("Source Image", src);
	//创建映射矩阵
	Mat map1(src.size(), CV_32FC1);
	Mat map2(src.size(), CV_32FC1);
    
	// 1. 水平翻转
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			map1.at<float>(i, j) = static_cast<float>(src.cols - j - 1);  // 水平翻转
			map2.at<float>(i, j) = static_cast<float>(i);
		}
	}
	Mat dst_hflip;
	remap(src, dst_hflip, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	
	namedWindow("Horizontal Flip", WINDOW_AUTOSIZE);
	imshow("Horizontal Flip", dst_hflip);

	// 2. 垂直翻转
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			map1.at<float>(i, j) = static_cast<float>(j);
			map2.at<float>(i, j) = static_cast<float>(src.rows - i - 1);  // 垂直翻转
		}
	}
	Mat dst_vflip;
	remap(src, dst_vflip, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	
	namedWindow("Vertical Flip", WINDOW_AUTOSIZE);
	imshow("Vertical Flip", dst_vflip);

	// 3. 旋转 180 度
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			map1.at<float>(i, j) = static_cast<float>(src.cols - j - 1);  // 水平翻转
			map2.at<float>(i, j) = static_cast<float>(src.rows - i - 1);  // 垂直翻转
		}
	}
	Mat dst_rotate180;
	remap(src, dst_rotate180, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	namedWindow("Rotate 180 degrees", WINDOW_AUTOSIZE);
	imshow("Rotate 180 degrees", dst_rotate180);

	// 4. 径向扭曲效果
	float cx = src.cols / 2.0;
	float cy = src.rows / 2.0;
	float radius = min(cx, cy);
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			float dx = j - cx;
			float dy = i - cy;
			float r = sqrt(dx * dx + dy * dy);
			float theta = atan2(dy, dx);

			float r_distorted = radius * (r / radius) * (r / radius);  // 径向扭曲
			map1.at<float>(i, j) = cx + r_distorted * cos(theta);
			map2.at<float>(i, j) = cy + r_distorted * sin(theta);
		}
	}
	Mat dst_radial;
	remap(src, dst_radial, map1, map2, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	namedWindow("Radial Distortion", WINDOW_AUTOSIZE);
	imshow("Radial Distortion", dst_radial);

	waitKey(0);
}
int main() 
{
	remap_image();
    return 0;
}
`cv::remap` 函数是 OpenCV 中的一个功能,主要用于将一张图像从一种坐标系转换到另一种坐标系,通常用于纠正图像因投影、旋转或其他变换导致的几何变形。这个函数接受四个输入参数:源图像 (`src`), 目标图像 (`dst`), 源图像的映射矩阵 (`map1`, `map2`) 和插值模式 (`interpolation`), 可选的还有边界处理方式(`borderMode`). 源代码不是直接在这里展示的,因为它是 OpenCV 库的一部分,属于 C++ 内部实现,而不是简单的函数调用。但是,基本的工作原理可以概括如下: 1. `map1` 和 `map2` 是两个包含对应点坐标变换的数组,通常是 `cv::Rect` 或 `cv::Point2f` 类型,它们描述了如何将原图像中的每个像素点映射到新图像上相应的像素位置。 2. `interpolation` 参数决定了插值算法,比如 `cv::INTER_LINEAR` (线性插值)、`cv::INTER_CUBIC` (立方插值)等,用于在两个已知像素之间平滑地生成新的像素值。 3. `borderMode` 则指定了边缘处理策略,如 `cv::BORDER_CONSTANT` (常数填充)、`cv::BORDER_REPLICATE` (复制边界的值) 等,当需要访问超出原始图像边界的地方时,按照指定的方式处理。 举个例子,如果你有一个经过畸变的图像,你想将其还原成正常透视下的图像,你可以计算出一个逆变换矩阵,并用 `cv::remap` 将图像映射回未畸变的状态。 ```cpp // 假设我们有源图像 src,目标映射 mapX, mapY,以及插值模式和边界处理方式 cv::Mat dst; cv::remap(src, dst, mapX, mapY, interpolation, borderMode); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mirror_zAI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值