世界坐标系投影到像素坐标系【python实验】

news2024/11/28 8:39:49

对于三维视觉而言,需要清晰了解世界坐标系和像素坐标系的对应关系。本文用python做实验。
相机内外参数的数学推导可以看我之前的博客《【AI数学】相机成像之内参数
》,《【AI数学】相机成像之外参数》。


实验开始
首先明确相机的内外参数:

intri = [[1111.0, 0.0, 400.0],
         [0.0, 1111.0, 400.0],
         [0.0, 0.0, 1.0]]
         
extri = [[-9.9990e-01,  4.1922e-03, -1.3346e-02, -5.3798e-02],
        [-1.3989e-02, -2.9966e-01,  9.5394e-01,  3.8455e+00],
        [-4.6566e-10,  9.5404e-01,  2.9969e-01,  1.2081e+00],
        [0.0, 0.0, 0.0, 1.0]]

我们在世界坐标系上找一个点,比如p1=[0, 0, 0],我们将其投影到像素坐标系,然后观察其投影坐标。

首先,我们将其转换到相机坐标系:(我们仅用外参数extri就可以得到相机坐标系)

import numpy as np

extri = np.array(
        [[-9.9990e-01,  4.1922e-03, -1.3346e-02, -5.3798e-02],
        [-1.3989e-02, -2.9966e-01,  9.5394e-01,  3.8455e+00],
        [-4.6566e-10,  9.5404e-01,  2.9969e-01,  1.2081e+00],
        [0.0, 0.0, 0.0, 1.0]])
p1 = np.array([0, 0, 0])

R = extri[:3, :3] # rotation
T = extri[:3, -1] # trans
cam_p1 = np.dot(p1 - T, R)

print(cam_p1)

然后,我们将相机坐标系转换到像素坐标系:

intri = [[1111.0, 0.0, 400.0],
         [0.0, 1111.0, 400.0],
         [0.0, 0.0, 1.0]]
screen_p1 = np.array([-cam_p1[0] * intri[0][0] / cam_p1[2] + intri[0][2],
					  cam_p1[1] * intri[1][1] / cam_p1[2] + intri[1][2]
					])
# Formula:				
#                          x                                y
#            x_im = f_x * --- + offset_x      y_im = f_y * --- + offset_y
#                          z                                z
       

print(screen_p1)
# [400.00057322 400.00211168]

由此可知,世界坐标系中的原点[0, 0, 0]经过这一套相机内外参数矩阵变换后,投影到屏幕的位置是[400, 400]。我们的图像大小刚好是[800, 800],恰好是中间位置。


上面的实验改成面向对象的写法,更方便调用:

import numpy as np

class CamTrans:
    def __init__(self, intri, extri, h=None, w=None):
        self.intri = intri
        self.extri = extri
        self.h = int(2 * intri[1][2]) if h is None else h
        self.w = int(2 * intri[0][2]) if w is None else w

        self.R = extri[:3, :3]
        self.T = extri[:3, -1]
        self.focal_x = intri[0][0]
        self.focal_y = intri[1][1]
        self.offset_x = intri[0][2]
        self.offset_y = intri[1][2]

    def world2cam(self, p):
        return np.dot(p - self.T, self.R)

    def cam2screen(self, p):
        """
                          x                                y
            x_im = f_x * --- + offset_x      y_im = f_y * --- + offset_y
                          z                                z
        """
        x, y, z = p
        return [-x * self.focal_x / z + self.offset_x, y * self.focal_y / z + self.offset_y]

    def world2screen(self, p):
        return self.cam2screen(self.world2cam(p))


if __name__ == "__main__":
    p = [0, 0, 1]
    intri = [[1111.0, 0.0, 400.0],
         [0.0, 1111.0, 400.0],
         [0.0, 0.0, 1.0]]
    extri = np.array(
        [[-9.9990e-01,  4.1922e-03, -1.3346e-02, -5.3798e-02],
        [-1.3989e-02, -2.9966e-01,  9.5394e-01,  3.8455e+00],
        [-4.6566e-10,  9.5404e-01,  2.9969e-01,  1.2081e+00],
        [0.0, 0.0, 0.0, 1.0]])
    
    # another camera
    extri1 = np.array([[-3.0373e-01, -8.6047e-01,  4.0907e-01,  1.6490e+00],
        [ 9.5276e-01, -2.7431e-01,  1.3041e-01,  5.2569e-01],
        [-7.4506e-09,  4.2936e-01,  9.0313e-01,  3.6407e+00],
        [0.0, 0.0, 0.0, 1.0]])

    ct = CamTrans(intri, extri)
    print(ct.world2screen(p))
    
    ct1 = CamTrans(intri, extri1)
    print(ct1.world2screen(p))

通过实验发现,世界坐标系的原点在两个相机的像素坐标中都位于中心点(400, 400),由此验证,我们的坐标投影是正确的~


射线投影实验
有了intri和extri两个矩阵,我们同样可以从像素坐标向世界坐标发出射线,如图:
camera ray

光心连接某个具体像素就可以确定一条射线了。我们这个实验是为了验证同一条射线上的采样点投影回像素坐标时也会投影在同一点

import numpy as np
import torch


def get_rays(h, w, K, c2w):
    i, j = torch.meshgrid(torch.linspace(0, w - 1, w),
                          torch.linspace(0, h - 1, h))
    # pytorch's meshgrid has indexing='ij'

    i = i.t()
    j = j.t()

    # directions
    dirs = torch.stack([(i - K[0][2]) / K[0][0],
                        -(j - K[1][2]) / K[1][1],
                        -torch.ones_like(i)],
                        -1)

    # camera to world
    # Rotate ray directions from camera frame to the world frame
    # rays_d = torch.sum(dirs[..., np.newaxis, :] * c2w[:3, :3], -1)
    rays_d = torch.sum(dirs[..., None, :] * c2w[:3, :3], -1)
    # dot product, equals to: [c2w.dot(dir) for dir in dirs]

    # Translate camera frame's origin to the world frame. It is the origin of all rays.
    rays_o = c2w[:3, -1].expand(rays_d.shape)

    return rays_o, rays_d

def _main():
    intri = [[1111.0, 0.0, 400.0],
         [0.0, 1111.0, 400.0],
         [0.0, 0.0, 1.0]]
    extri = np.array(
        [[-9.9990e-01,  4.1922e-03, -1.3346e-02, -5.3798e-02],
        [-1.3989e-02, -2.9966e-01,  9.5394e-01,  3.8455e+00],
        [-4.6566e-10,  9.5404e-01,  2.9969e-01,  1.2081e+00],
        [0.0, 0.0, 0.0, 1.0]])
    h, w = 800, 800
    rays_o, rays_d = get_rays(h, w, K, pose) # all rays
    # rays_o means origins, rays_d means directions
    
    ray_o = rays_o[400][400] # choose (400, 400)th rays
    ray_d = rays_d[400][400] # choose (400, 400)th rays
    
    z_vals = [2., 6.] # pick 2 z-values
    pts = ray_o[..., None, :] + ray_d[..., None, :] * z_vals[..., :, None] # sampling 2 points from the ray
    print(pts)
    ct = CamTrans(intri, extri)
    print(ct.world2screen(pts[0]))
    print(ct.world2screen(pts[1]))
    # [399.99682432604334, 400.010187052666]
    # [399.99608371858295, 399.992518381194]

通过实验发现,在同一条射线上的采样点会反投影同一个二维点上。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI智能识别如何助力PDF,轻松实现文档处理?

AI智能识别如何助力PDF,轻松实现文档处理? 随着科技的不断发展,人工智能(AI)在各个领域都发挥着重要的作用。其中,文档智能( Document AI )在金融、医疗、教育、保险、能源、物流等…

yolov7改进之使用QFocalLoss

深度学习三大件:数据、模型、Loss。一个好的Loss有利于让模型更容易学到需要的特征,不过深度学习已经白热化了,Loss这块对一个成熟任务的提升是越来越小了。虽然如此,也不妨碍我们在难以从数据和模型层面入手时,从这个…

Object转List<>,转List<Map<>>

这样就不会局限在转换到List<Map<String,Object>>这一种类型上了.可以转换成List<Map<String,V>>上等,进行泛型转换虽然多了一个参数,但是可以重载啊注: 感觉field.get(key) 这里处理的不是很好,如果有更好的办法可以留言 public static <K, V> …

大数据Doris(十四):Doris表中的数据基本概念

文章目录 Doris表中的数据基本概念 一、​​​​​​​Row & Column

前端项目 index.html 中发请求 fetch

想要在前端项目 index.html文件中向后端发起请求&#xff0c;但是引入axios报错&#xff08;我这边会报错&#xff09;&#xff0c;可以使用fetch。 //window.location.origin----获取域名&#xff0c;包括协议、主机号、端口号fetch(window.location.origin "/api/pla…

el-tabs 默认选中第一个

1. 实际开发中el-tabs 都会设置第一个为默认值 ,这样会好看一点, 而渲染的数据经常是通过后端返回的数据 , v-model 无法写死默认值 解决办法 , 通过计算机属性 ,在data 定义一个 selectedTab watch: {defaultTab(newVal) {this.selectedTab newVal; // 设置第一个标签页…

腾讯云双11云服务器活动:3年366元,超多超值云服务器!

腾讯云在双11活动中推出了一款3年366元的云服务器&#xff0c;配置为2核2G 40GB SSD盘&#xff0c;300GB月流量&#xff0c;4M带宽。这一配置相较于其他厂商同等规格的云服务器&#xff0c;具有较高的性价比。在市场上很少有厂商提供3年期的优惠服务器&#xff0c;因此此次双11…

函数式接口详解(Java)

函数式接口详解&#xff08;Java&#xff09;_函数式接口作为参数_凯凯凯凯.的博客-CSDN博客 函数式接口&#xff1a;有且仅有一个抽象方法的接口 Java中函数式编程体现就是Lambda表达式&#xff0c;所以函数式接口就是可以适用于Lambda使用的接口 只有确保接口中仅有一个抽…

第二证券:怎么判断股票浮筹多少?

股票的浮筹是指公司的股份中&#xff0c;揭露生意在市场上的股份&#xff0c;一般是指除了大股东和筹码安稳的组织等&#xff0c;其他组织和个人能够自在生意的股份。在出资股票时&#xff0c;了解公司的浮筹是非常重要的&#xff0c;由于它直接联络到股票的供需联络和股价动摇…

初识JavaScript(一)

文章目录 一、JavaScript介绍二、JavaScript简介1.ECMAScript和JavaScript的关系2.ECMAScript的历史3.什么是Javascript&#xff1f;4.JavaScript的作用?5.JavaScript的特点 三、JavaScript基础1.注释语法2.JavaScript的使用 四、JavaScript变量与常量变量关键字var和let的区别…

苹果AirTag固件更新

苹果公司针对其热销的物品追踪器 AirTag 于今天发布了新的固件更新&#xff0c;最新版本号为 2A61&#xff0c;但是这次更新苹果并未提供发布说明&#xff0c;所以目前还不知道这次更新有什么新内容。 关于这次更新&#xff0c;用户无法自己手动更新 AirTag 固件&#xff0c;因…

5.1 运输层协议概述

思维导图&#xff1a; 前言&#xff1a; 第5章 运输层笔记 1. 概览 主要内容&#xff1a;介绍运输层协议的特点、进程间通信、端口、UDP和TCP协议、可靠传输、TCP报文段的首部格式、TCP的关键概念&#xff08;如滑动窗口、流量控制、拥塞控制和连接管理&#xff09;。重要性…

自定义类型结构体(上)

目录 结构体类型的声明结构体的概念结构体的声明特殊的声明结构的自引用 结构体变量的创建和初始化结构成员访问操作符 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x1…

linux下df -h 命令一直卡住的解决方法

在Linux中&#xff0c;偶尔遇到用 df -h 查看磁盘情况时&#xff0c;一直卡住无法显示结果。 解决方法&#xff1a; 1、首先使用strace追踪到底执行到哪里卡住 $ strace df -h 2、如果没有strace命令则进行安装 $ yum install strace -y 3、显示出卡住的地方&#xff0c;如…

[0xGame 2023 公开赛道] week4 crypto/pwn/rev

最后一周结束了&#xff0c;难度也很大&#xff0c;已经超出我这认为的新生程度了。 crypto Orac1e 先看题&#xff0c;题目先是给了加密过的flag然后提供不限次数的解密&#xff0c;不过仅提供解密后unpad的结果。 from Crypto.Util.number import * from Crypto.Cipher i…

WinCC7.5 将归档数据打印到MSHGrid(不是MSFlexGrid控件)

参考网址&#xff1a;https://www.cnblogs.com/fishingsriver/p/14397431.html 新建变量 MSHGrid控件 查询按钮 Sub OnClick(ByVal Item) Dim myCatalog,myDS,PCN…

【python】路径管理+路径拼接问题

路径管理 问题相对路径问题绝对路径问题 解决os库pathlib库最终解决 问题 环境&#xff1a;python3.7.16 win10 相对路径问题 因为python的执行特殊性&#xff0c;使用相对路径时&#xff0c;在不同路径下用python指令会有不同的索引效果&#xff08;python的项目根目录根据执…

【嵌入式】HC32F07X CAN通讯配置和使用配置不同缓冲器以连续发送

一 背景说明 使用小华&#xff08;华大&#xff09;的MCU HC32F07X实现 CAN 通讯配置和使用 二 原理分析 【1】CAN原理说明&#xff08;参考文章《CAN通信详解》&#xff09;&#xff1a; CAN是控制器局域网络(Controller Area Network, CAN)的简称&#xff0c;是一种能够实现…

公司上网行为监控能监控到什么

公司上网行为监控是一个备受关注的话题&#xff0c;它可以监控员工的网络行为&#xff0c;保护企业的机密和数据安全。但是&#xff0c;这种监控行为也会涉及到员工的隐私权和数据安全问题。 公司上网行为监控能监控到什么&#xff1a; 1、访问网站精确到员工信息、计算机名称…

uni-starter 使用常见问题

1. Invalid uni-id config file 没有找到uni-id文件导致 需要在uniCloud-aliyun/cloudfunctions/common/uni-config-center/uni-id/下新建 config.json 如果没有uni-id 就新建一个。 注意&#xff1a;config.json是一个标准json文件&#xff0c;不支持注释 uni-starter 按照…