去除图像畸变及C++实现

1.简介

图像畸变是指图像中出现的形变或失真,通常由于光学系统的非理想性(透镜和成像平面很难做到完全平行)或摄像机的透镜特性引起。图像畸变通常分为两种:径向畸变和切向畸变。

2.切向畸变

切向畸变(Tangential Distortion)通常是由于镜头与图像传感器之间的距离不正确造成的。这种畸变表现为图像边缘的直线向外延伸形成倾斜,使得物体的角部出现切割或拉伸的效果。

切向畸变有两种形式:

  • 第一类切向畸变:当透镜靠图像传感器太近时发生,导致图像边缘的直线向外弯曲。
  • 第二类切向畸变:当透镜离图像传感器太远时发生,导致图像边缘的直线向内弯曲。

好了,说人话。“切向畸变”就是矢量端点沿切线方向发生的变化,也就是角度的变化dt。还有一种解释是由于摄像机制造上的缺陷使得透镜本身与图像平面不平行而产生的。

注意,通常假设这些畸变呈多项式关系,以下是其数学模型:

x_{correct}=x+2p_1xy+p_2(r^2+2x^2)\\ y_{correct}=y+p_1(r^2+2y^2)+2p_2xy

其中,(x,y)是畸变点在图像上的原始坐标,(x_{correct},y_{correct})是校正后的新坐标,p_1p_2为切向畸变系数,r为极坐标系下畸变点到坐标原点的距离(即镜头中心到图像点的距离)。

3.径向畸变

径向畸变(Radial Distortion)主要是由于镜头内部的透镜元素对通过它们的光线施加了不均匀的放大率造成的。这种畸变通常在图像的边缘更为明显,因为边缘的光线通过镜头时光轴形成的角度较大。径向畸变通常有以下两种形式。

3.1桶形畸变

桶形畸变(Barrel Distortion)是当径向畸变导致图像中心的放大率高于边缘时发生的。结果是图像中心部分的直线向外弯曲,形成一种“桶”状的膨胀效果。桶形畸变常见于广角镜头。

3.2枕形畸变

枕形畸变(Pincushion Distortion)与桶形畸变相反,当边缘的放大率高于中心时,会发生枕形畸变。这种情况下,图像边缘的直线向内弯曲,好像被“挤压”进图像中心,形成枕形效果。枕形畸变常见于长焦镜头。

这是最经典的一张图,大家可以简单记作:桶形畸变(向外弯曲),枕形畸变(向内弯曲)。以下是其数学模型:

x_{correct}=x(1+k_1r^2+k_2r^4+k_3r^6)\\ y_{correct}=y(1+k_1r^2+k_2r^4+k_3r^6)

其中,(x,y)是畸变点在图像上的原始坐标,(x_{correct},y_{correct})是校正后的新坐标,k_1,k_2,k_3为径向向畸变系数,r为极坐标系下畸变点到坐标原点的距离(即镜头中心到图像点的距离)。

4.C++实现

以下代码均在ubuntu20.04上验证通过,使用的OpenCV C++版本为3.4.15。代码主要调用了OpenCV库中的undistort函数,并利用我的另一篇博客(相机标定及C++实现-CSDN博客)得到的相机内参和畸变系数来对图像进行校正。

在运行代码前可以按照我的相机标定那篇博客创建build,images,src文件夹和CMakeLists.txt文件,以下是放在src文件夹下的源代码:

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

using namespace std;

int main(int argc, char **argv)
{
    if(argc < 2)
    {
        cout<<"输入正确的图片路径"<<endl;
        return -1;
    }

    //读取图片
    cv::Mat image = cv::imread(argv[1],cv::IMREAD_COLOR);

    //图像是否读取成功
    if(image.empty())
    {
        cout<<"图片读取失败"<<endl;
        return -1;
    }

    //读取相机内参和畸变系数,这里的相机内参和畸变系数是相机标定的结果,需要提前标定
  
    cv::Mat cameraMatrix, distCoeffs;
    cameraMatrix = (cv::Mat_<double>(3,3)<<533.0082745226835, 0, 341.891649034585,
                    0, 533.0298665139785, 234.6399491136406,
                    0, 0, 1);
    distCoeffs = (cv::Mat_<double>(1,5)<<-0.2797096204667459, 0.02806520133705146, 0.001221734842500993, 3.843081231723055e-05, 0.128566132396488);
    
    //去除畸变
    cv::Mat undistortedImage;
    //使用相机内参和畸变系数进行去畸变
    cv::undistort(image,undistortedImage,cameraMatrix,distCoeffs,cv::Mat());


    //显示原图和去畸变后的图片
    cv::imshow("originalImage",image);
    cv::imshow("undistortedImage",undistortedImage);
    cv::waitKey(0);

    return 0;
}

以下是CMakeLists.txt中的代码,注意按照你的源代码文件名修改倒数第二行src/undistort.cpp这句代码。

#设置当前cmake运行的最低版本
cmake_minimum_required(VERSION 3.2)

# 设置项目名称
project(undistort)

# 设置C++标准为11
set(CMAKE_CXX_STANDARD 11)

# 查找OpenCV库
find_package(OpenCV REQUIRED)

#包含OpenCV的头文件
include_directories(${OpenCV_INCLUDE_DIRS})

# 添加可执行文件
add_executable(undistort src/undistort.cpp)

# 链接OpenCV库

target_link_libraries(undistort ${OpenCV_LIBS})

进入到build文件夹下,打开终端,输入以下命令:

cmake ..
make

具体运行结果如下:

上面ls命令下绿色的undistort就是我们的可执行文件,这个可执行文件名和我们CMakeLists.txt文件中最后两行是相对应的。接着在build文件夹下执行我们的可执行文件(undistort)

./undistort ./../images/left01.jpg

运行结果如下,看着有点效果。

  • 30
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 以下是一个用C语言去除相机畸变的代码示例: ``` #include <opencv2/opencv.hpp> using namespace cv; int main() { Mat image = imread("test.jpg"); // 读取图像 Mat cameraMatrix = (Mat_<double>(3, 3) << 100, , 320, , 100, 240, , , 1); // 相机内参矩阵 Mat distCoeffs = (Mat_<double>(1, 5) << .1, .01, , , -.01); // 畸变系数 Mat undistorted; // 去畸变后的图像 undistort(image, undistorted, cameraMatrix, distCoeffs); // 去畸变 imshow("Original Image", image); // 显示原始图像 imshow("Undistorted Image", undistorted); // 显示去畸变后的图像 waitKey(); // 等待按键 return ; } ``` 这段代码使用了OpenCV库中的`undistort`函数,通过传入相机内参矩阵和畸变系数,可以对图像进行去畸变处理。 ### 回答2: 下面是一个使用C语言编写的去除相机畸变的代码示例: ```c #include <stdio.h> #include <opencv2/opencv.hpp> int main() { // 读取图像 cv::Mat image = cv::imread("input_image.jpg"); // 相机内参 cv::Mat camera_matrix = (cv::Mat_<double>(3, 3) << fx, 0, cx, 0, fy, cy, 0, 0, 1); // 相机畸变参数 cv::Mat dist_coeffs = (cv::Mat_<double>(1, 5) << k1, k2, p1, p2, k3); // 去除相机畸变 cv::Mat undistorted_image; cv::undistort(image, undistorted_image, camera_matrix, dist_coeffs); // 显示去畸变后的图像 cv::imshow("Undistorted Image", undistorted_image); cv::waitKey(0); return 0; } ``` 以上代码基于OpenCV库进行图像处理。在代码中,首先需要根据相机的内参和畸变参数定义相机矩阵和畸变系数。然后使用`cv::undistort`函数对图像进行去畸变处理,最后将结果显示在窗口中。 需要注意的是,代码中的`fx, fy, cx, cy, k1, k2, p1, p2, k3`是相机的内参和畸变参数,需要根据实际相机的参数进行替换。 ### 回答3: 下面是一个使用C语言编写的用于去除相机畸变的代码: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define PI 3.14159265358979323846 typedef struct { double k1, k2, p1, p2; } DistortionParams; typedef struct { double x, y; } Point2D; Point2D undistortPoint(Point2D distortedPoint, DistortionParams dParams) { double r2 = distortedPoint.x * distortedPoint.x + distortedPoint.y * distortedPoint.y; double r4 = r2 * r2; double radialDistortion = 1.0 + dParams.k1 * r2 + dParams.k2 * r4; double dx = 2 * dParams.p1 * distortedPoint.x * distortedPoint.y + dParams.p2 * (r2 + 2 * distortedPoint.x * distortedPoint.x); double dy = dParams.p1 * (r2 + 2 * distortedPoint.y * distortedPoint.y) + 2 * dParams.p2 * distortedPoint.x * distortedPoint.y; Point2D undistortedPoint; undistortedPoint.x = distortedPoint.x * radialDistortion + dx; undistortedPoint.y = distortedPoint.y * radialDistortion + dy; return undistortedPoint; } int main() { // 设置相机畸变参数 DistortionParams dParams; dParams.k1 = 0.1; dParams.k2 = 0.05; dParams.p1 = 0.01; dParams.p2 = 0.02; // 设置畸变前的点坐标 Point2D distortedPoint; distortedPoint.x = 100.0; distortedPoint.y = 200.0; // 对点进行去畸变处理 Point2D undistortedPoint = undistortPoint(distortedPoint, dParams); // 输出去畸变后的点坐标 printf("去畸变后的点坐标:(%.2lf, %.2lf)\n", undistortedPoint.x, undistortedPoint.y); return 0; } ``` 这个代码中,`DistortionParams`结构体用于存储相机畸变的参数,包括径向畸变系数k1、k2和切向畸变系数p1、p2。`Point2D`结构体用于表示二维平面上的点的坐标。`undistortPoint`函数用来对畸变点进行去畸变处理。在`main`函数中,我们设定了畸变前的点坐标为(100, 200),经过去畸变处理后得到的点坐标将会打印出来,即去畸变后的点坐标。 请注意在实际应用中,畸变参数和输入的畸变点坐标需要根据具体的相机设备和畸变模型进行设置和获取。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值