无人机影像配准并发布(共线方程)

无人机影像 + DEM 计算四个角点坐标(刚性变换)

  1. 像空间坐标(x,y,-f)

  2. 像空间坐标畸变纠正 deltax,deltay
    在这里插入图片描述

  3. 已知(x,y),求解(X,Y, Z)或者(Lat,Lon)
    在这里插入图片描述
    这里的Z是DEM上获取的坐标和Zs为相机坐标的高程,如果均为已知的情况下,则可以求解(X,Y),这里的(X,Y,Z)为地固地心坐标,单位为米。平地的情况只需要获取行高即可求解(X,Y),接着使用proj库将地固地心坐标转化为经纬度坐标即可。

  4. 地理配准
    这里直接采用**gdal_translategdal_wrap**,gdal_translate转换过程如下,大概就是将jpg进行地理配准。请注意,GDAL的影像起点是左上角,但是我们的相机模型是左下角,所以需要变换Y轴。转换之后的tif只有专业的QGIS之类的软件才能读取。
    在这里插入图片描述

    下面是QGIS读取的效果。但是为了geoserver能够识别,还需要转换,这时候需要gdal_wrap,这个时候就很关键,我们需要设置其为透明。

    gdalwarp -r cubic -ovr AUTO -dstalpha D:\code\roadProj\public\demo\DSC00002\test.tif D:\code\roadProj\public\demo\DSC00002\test3_geotiff.tif
    

    在这里插入图片描述

    gdal_translate.exe -of GTiff -gcp 0 5304 102.1265090139 29.6453703982 -gcp 7952 5304 102.1164515460 29.6474820822 -gcp 7952 0 102.1131839750 29.6445496193 -gcp 0 0 102.1233217949 29.6424804051 -ovr AUTO -co GCPs_Creation=YES D:\code\roadProj\public\demo\DSC00002\DSC00002.jpg D:\code\roadProj\public\demo\DSC00002\test.tif
    
    1. geoserver发布,具体操作比较简单

代码逻辑

  1. 下面是求解影像四个角点经纬度的简单思路,主要还是共线方程,代码中的1000还是得根据距离地面的高度,即需要DEM的高程值才能求解得到较为精确的精度。

  2. 具体实现分为相机模型(固定的参数不部分),大疆无人机是WGS84椭球,EPSG:4978是地心地固的转换参数。

struct CameraModel {
    double f = 7538.508;  // 像素为单位

    double u0 = 3982.417; // 像素为单位
    double v0 = 2671.637;

    double pixelSize = 4.5e-6; // 米为单位

    double k1 = 2.470920e-9;   // 径向畸变系数
    double k2 = -2.767172e-16;
    double k3 = 2.479935e-23;
    double k4 = -6.583598e-31;

    double p1 = 1.388595e-8; // 偏心畸变系数
    double p2 = 1.781812e-7;

    double alpha = -4.697031e-4; // CCD非正方形比例系数
    double beta = -1.300023e-4;

    double width = 7952; // 影像的高度和宽度
    double height = 5304;
};```

```cpp
///
/// \brief The ComputeBoundingBox class
/// 计算影像的包围盒的经纬度坐标 + 高程,然后贴地
///
class ComputeBoundingBox {
public:
    ComputeBoundingBox() {
        transTool.init("EPSG:4326",
                       "EPSG:4978"); // 椭球坐标->地心坐标 XYZ
    }

    ///
    /// \brief resetR
    /// \param phi 俯仰角
    /// \param omega 横滚角
    /// \param kappa 旋转角
    ///
    void resetR(double phi, double omega, double kappa) {
        this->phi = degreeToRadian(phi);
        this->omega = degreeToRadian(omega);
        this->kappa = degreeToRadian(kappa);
    }

    ///
    /// \brief resetCamera
    /// \param lat 维度
    /// \param lon 经度
    /// \param height 高程
    ///
    void resetCamera(const QString &imgNumber, double lat, double lon, double height) {
        this->imgNumber = imgNumber;
        this->cameraLat = lat;
        this->cameraLon = lon;
        this->height = height;
    }

    ///
    /// \brief compute 计算相机位置 + 旋转矩阵
    ///
    void compute();

private:
    ///
    /// \brief computeLatlon 根据像点坐标计算经纬度坐标(共线方程的逻辑)
    /// @param vector2d uv x轴,y轴坐标
    ///
    Eigen::Vector2d computeLatlon(Eigen::Vector2d &uv);

    ///
    /// \brief degreeToRadian 度转弧度
    /// \param degree
    /// \return
    ///
    double degreeToRadian(double degree) { return degree * M_PI / 180.0; }

    //    double computeDeltaX();

    //    double computeDeltaY();

    Eigen::Vector3d cameraGeo;

    Eigen::Matrix3d rMatrix;

    Transform transTool;

    CameraModel intrinsic; // 内参数矩阵

    QString imgNumber;     // 影像编号

    double cameraLat;      // 相机外参数矩阵
    double cameraLon;
    double height;

    double phi;
    double omega;
    double kappa;
};
Eigen::Vector2d ComputeBoundingBox::computeLatlon(Eigen::Vector2d &uv) {
    double u = uv.x();
    double v = uv.y();
    Eigen::Vector3d cameraSpace(u, v, -this->intrinsic.f); // 像空间坐标

    double r = qSqrt(pow(u - this->intrinsic.u0, 2) + pow(v - this->intrinsic.v0, 2));

    // (x-x0) * (k1*r^2 + k2*r^4 + k3*r^6 + k4*r8) + p1*(r^2 + 2*(x-x)^2) + 2p2*(x-x0)(y-y0) + alpha*(x-x0) +
    // beta*(y-y0)
    double deltaX = (u - this->intrinsic.u0) * (this->intrinsic.k1 * pow(r, 2) + this->intrinsic.k2 * pow(r, 4) +
                                                this->intrinsic.k3 * pow(r, 6) + this->intrinsic.k4 * pow(r, 8)) +
                    this->intrinsic.p1 * (pow(r, 2) + 2 * pow((u - this->intrinsic.u0), 2)) +
                    2 * this->intrinsic.p2 * (u - this->intrinsic.u0) * (v - this->intrinsic.v0) +
                    this->intrinsic.alpha * (u - this->intrinsic.u0) + this->intrinsic.beta * (v - this->intrinsic.v0);
    double deltaY = (v - this->intrinsic.v0) * (this->intrinsic.k1 * pow(r, 2) + this->intrinsic.k2 * pow(r, 4) +
                                                this->intrinsic.k3 * pow(r, 6) + this->intrinsic.k4 * pow(r, 8)) +
                    this->intrinsic.p2 * (pow(r, 2) + 2 * pow((v - this->intrinsic.v0), 2)) +
                    2 * this->intrinsic.p1 * (u - this->intrinsic.u0) * (v - this->intrinsic.v0);

    Eigen::Vector3d cameraOffset(deltaX - this->intrinsic.u0, deltaY - this->intrinsic.v0,
                                 0);                              // 像点坐标偏移
    Eigen::Vector3d cameraSpaceTrue = cameraSpace + cameraOffset; // 实际的像点坐标[最后一位该如何求解]

    Eigen::Vector3d worldCoordBa =
        this->rMatrix * cameraSpaceTrue * this->intrinsic.pixelSize; // (xBa, yBa, zBa) pixelSize这个参数多余

    worldCoordBa =
        Eigen::Vector3d(worldCoordBa.x() / worldCoordBa.z() * 1000, worldCoordBa.y() / worldCoordBa.z() * 1000, 0);

    //    qDebug() << worldCoordBa.x() << " " << worldCoordBa.y() << " " << worldCoordBa.z();

    Eigen::Vector3d worldCoord = worldCoordBa + this->cameraGeo; // 真正的坐标
    //    std::cout << worldCoord.x() << " " << worldCoord.y() << " " << worldCoord.z();

    //    PJ_COORD latlonh = proj_coord(cameraLon, cameraLat, height, 0);
    PJ_COORD geoxyz = proj_coord(worldCoord.x(), worldCoord.y(), worldCoord.z(), 0); // 地心坐标
    PJ_COORD latlon = this->transTool.backward(geoxyz);
    //    std::cout << "lat:" << latlon.lpz.lam << " ,lon:" << latlon.lpz.phi << ", " << latlon.lpz.z;
    return Eigen::Vector2d(latlon.lp.phi, latlon.lp.lam); // lat and lon
}
void ComputeBoundingBox::compute() {

    PJ_COORD latlonh = proj_coord(cameraLon, cameraLat, height, 0);
    PJ_COORD geoxyz = transTool.forward(latlonh);

    this->cameraGeo = Eigen::Vector3d(geoxyz.xyz.x, geoxyz.xyz.y, geoxyz.xyz.z); // 地心坐标

    // 计算绕X轴旋转的旋转矩阵 Rx(φ)
    Eigen::Matrix3d Rx;
    Rx << 1, 0, 0, 0, std::cos(phi), -std::sin(phi), 0, std::sin(phi), std::cos(phi);

    // 计算绕Y轴旋转的旋转矩阵 Ry(ω)
    Eigen::Matrix3d Ry;
    Ry << std::cos(omega), 0, std::sin(omega), 0, 1, 0, -std::sin(omega), 0, std::cos(omega);

    // 计算绕Z轴旋转的旋转矩阵 Rz(κ)
    Eigen::Matrix3d Rz;
    Rz << std::cos(kappa), -std::sin(kappa), 0, std::sin(kappa), std::cos(kappa), 0, 0, 0, 1;

    // 计算总的旋转矩阵 R_total = Rz(κ) * Ry(ω) * Rx(φ)
    this->rMatrix = Rz * Ry * Rx;

    // 像素点畸变纠正

    Eigen::Vector2d lb(0, 0);                                          // 左下角为起点
    Eigen::Vector2d rb(this->intrinsic.width, 0);                      // 右下角坐标
    Eigen::Vector2d rt(this->intrinsic.width, this->intrinsic.height); // 右上角坐标
    Eigen::Vector2d lt(0, this->intrinsic.height);                     // 左上角成果

    std::vector<Eigen::Vector2d> latlonVec = {lb, rb, rt, lt};
    qDebug() << "####################" << this->imgNumber << "########################";
    for (Eigen::Vector2d &pixelCoord : latlonVec) {
        pixelCoord = this->computeLatlon(pixelCoord);
        qDebug() << "lat: " << QString::number(pixelCoord.x(), 'f', 10)
                 << ",lon:" << QString::number(pixelCoord.y(), 'f', 10);
    }
}

效果图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/49203.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Django on_delete参数在sql级别操作中不生效问题

class AA(models.Model):name models.CharField(max_length128)class Meta:db_table aaclass BB(models.Model):name models.CharField(max_length128)aa models.ForeignKey(AA, nullTrue, on_deletemodels.CASCADE)class Meta:db_table bb 如上当使用ORM删除aa表中的数据…

12-1_Qt 5.9 C++开发指南_自定义插件和库-自定义Widget组件(提升法(promotion)创建自定义定制化组件)

当UI设计器提供的界面组件不满足实际设计需求时&#xff0c;可以从 QWidget 继承自定义界面组件。 有两种方法使用自定义界面组件&#xff1a; 一种是提升法(promotion)&#xff0c;例如在8.3 节将一个QGraphicsView组件提升为自定义的 QWGraphicsView 类&#xff0c;提升法用…

html实现蜂窝菜单

效果图 CSS样式 keyframes _fade-in_mkmxd_1 {0% {filter: blur(20px);opacity: 0}to {filter: none;opacity: 1} } keyframes _drop-in_mkmxd_1 {0% {transform: var(--transform) translateY(-100px) translateZ(400px)}to {transform: var(--transform)} } ._examples_mkmx…

MHA高可用配置及故障切换

文章目录 MHA高可用配置及故障切换一. MySQL MHA1.什么是MHA&#xff12;.&#xff2d;&#xff28;&#xff21;的组成&#xff12;.&#xff11;MHA Node (数据节点)&#xff12;.&#xff12;MHA Manager (管理节点) &#xff13;.&#xff2d;&#xff28;&#xff21;的特…

使用python库uvicorn替代Nginx发布Vue3项目

目录 一、Vue3项目打包 二、将打包文件放到python项目 三、配置uvicorn服务 四、启动服务 【SpringBoot版传送门&#xff1a;使用SpringBoot替代Nginx发布Vue3项目_苍穹之跃的博客-CSDN博客】 一、Vue3项目打包 &#xff08;博主vue版本&#xff1a;3.2.44&#xff09; 由…

POI 导出 树形结构

参考文章&#xff1a;(327条消息) Excel树状数据绘制导出_excel导出树形结构_Deja-vu xxl的博客-CSDN博客https://blog.csdn.net/weixin_45873182/article/details/120132409?spm1001.2014.3001.5502 Overridepublic void exportPlus(String yearMonth, HttpServletRequest re…

spring5源码篇(12)——spring-mvc请求流程

spring-framework 版本&#xff1a;v5.3.19 文章目录 一、请求流程1、处理器映射器1.1、 RequestMappingHandlerMapping1.2、获取对应的映射方法1.3、添加拦截器 2、获取合适的处理器适配器3、通过处理器适配器执行处理器方法3.1、拦截器的前置后置3.2、处理器的执行3.2.1 参数…

重生之我要学C++第四天

这篇文章的主要内容是类的默认成员函数。如果对大家有用的话&#xff0c;希望大家三连支持&#xff0c;博主会继续努力&#xff01; 目录 一.类的默认成员函数 二.构造函数 三.析构函数 四.拷贝构造函数 五.运算符重载 一.类的默认成员函数 如果一个类中什么成员都没有&…

目标检测-击穿黑夜的PE-YOLO

前言 当前的目标检测模型在许多基准数据集上取得了良好的结果&#xff0c;但在暗光条件下检测目标仍然是一个巨大的挑战。为了解决这个问题&#xff0c;作者提出了金字塔增强网络&#xff08;PENet&#xff09;并将其与YOLOv3结合&#xff0c;构建了一个名为PE-YOLO的暗光目标检…

Linux中的ldd命令使用方法总结

ldd&#xff08;List Dynamic Dependencies&#xff09;命令是Linux系统中的一个工具 它用于打印出一个可执行文件所依赖的共享库文件&#xff08;动态链接库&#xff09; 当你运行ldd命令&#xff0c;并跟上一个可执行文件作为参数&#xff0c;它会列出该可执行文件所需要的…

【Spring】Spring 总览

一、简单介绍一下 Spring Spring是一个全面的、企业应用开发的一站式解决方案&#xff0c;贯穿表现层、业务层、持久层&#xff0c;可以轻松和其他框架整合&#xff0c;具有轻量级、控制反转、面向切面、容器等特征。 轻量级 &#xff1a; 空间开销和时间开销都很轻量 控制反…

栈和队列第二弹,完结篇

&#x1f49b;1.队列的基本底层实现 public class MyQueue {int array[];int usedsize0;public MyQueue(){this.arraynew int [5];} &#x1f499;2.判断是否满&#xff0c;满了需要扩容 Arrays.copyOf(数组&#xff0c;数组的长度&#xff09;&#xff1b;我常常会忘记哈…

Java版本企业工程项目管理系统平台源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…

Safari 查看 http 请求

文章目录 1、开启 Safari 开发菜单2、显示 JavaScript 控制台 1、开启 Safari 开发菜单 Safari 设置中&#xff0c;打开开发菜单选项 *** 选择完成后&#xff0c;Safari 的目录栏就会出现一个 开发 功能。 2、显示 JavaScript 控制台 开启页面后&#xff0c;在开发中选中 显…

掌握Python的X篇_10+11_if分支语句、else语句、elif语句

文章目录 1. if关键字及语法2. 语句块的概念3. else语句4. elif语句 1. if关键字及语法 基本语法如下&#xff1a; if 条件表达式:条件为True时&#xff0c;要执行的语句举例&#xff1a; number int(input("Input an number")) if number > 5 :print("这…

F12 浏览器调试模式页面刷新 network 日志刷新消失的解决办法

每次请求刷新后都把之前的请求记录刷新掉了&#xff0c;把preserve log勾选上后&#xff0c;所有的请求都会保留&#xff0c;再也不怕抓不到记录了。

SpringBoot项目部署在Windows与Centos上

文章目录 Windows部署一、github上下载文件winsw二、文件目录三、编辑xml文件四、安装服务五、启动服务六、把jar包放到项目外面七、添加限制内存 Linux部署一、准备二、服务三、操作 Windows部署 windows部署服务借鉴于此篇博文 一、github上下载文件winsw 点击链接下载下图…

windows切换php版本以及composer

前提 安装php8.2 安装Php7.4 下载 nts是非线程安全的&#xff0c;这里选择线程安全的&#xff0c;选择64位 解压缩 修改系统环境变量 修改为php-7的 cmd中输入php -v查看 找到composer存放路径C:\ProgramData\ComposerSetup\bin 将三个文件复制到php目录下 重启电脑…

【云原生】Docker容器命令监控+Prometheus监控平台

目录 1.常用命令监控 docker ps docker top docker stats 2.weave scope 1.下载 2.安装 3.访问查询即可 3.Prometheus监控平台 1.部署数据收集器cadvisor 2.部署Prometheus 3.部署可视化平台Gragana 4.进入后台控制台 1.常用命令监控 docker ps [rootlocalhost ~…

GitHub Copilot:让开发编程变得像说话一样简单

引用&#xff1a; 人类天生就梦想、创造、创新。但今天&#xff0c;我们花太多时间被繁重的工作所消耗&#xff0c;花在消耗我们时间、创造力和精力的任务上。为了重新连接我们工作的灵魂&#xff0c;我们不仅需要一种更好的方式来做同样的事情&#xff0c;更需要一种全新的工…