文章目录
前言
像素重映射将图像中的每个像素映射到新位置,实现图像的扭曲、校正等操作。在 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
保持原始的行坐标不变。
cpp
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
保持列坐标不变。
cpp
// 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 度
同时进行水平和垂直翻转
cpp
// 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)径向扭曲
通过对极坐标中的半径进行二次变换,产生径向扭曲效果,图像向中心点扭曲,产生类似鱼眼镜头的效果。
cpp
// 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.完整代码
cpp
#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;
}