Qiao

OpenCV鱼眼模型理解

鱼眼模型介绍 这里直接截取鱼眼镜头的成像原理到畸变矫正的原文作为此文的引子: 鱼眼镜头一般是由十几个不同的透镜组合而成的。在成像的过程中,入射光线经过不同程度的折射,投影到尺寸有限的成像平面上,使得鱼眼镜头与普通镜头相比起来拥有了更大的视野范围。 在研究鱼眼相机成像时,可以将上面的镜头组简化为一个球面: 图中,$O_1-X_cY_cZ_c$是相机坐标系,$O_2-xy$是成像平面。世界中有一点$P$,入射角为$\theta$。如果按照针孔相机模型,入射光线$PO_1$经过镜头后不改变路线,$p’$为$P$的成像点。但对于鱼眼相机,入射光线经过镜头后会发生折射,因此$p$才是$P$的成像点,极坐标表示为$(r, \varphi)$ 。 可以用投影函数来对光线的折射建模。根据投影函数的不同,鱼眼相机的传统模型大致能被分为五种:透视投影(即针孔相机模型)、等积投影、等距投影、体视投影、正交投影。 投影模型 投影函数 特征 i. 透视投影 (perspective projection) $r = ftan\theta$ 针孔相机模型 ii. 体视投影 (stereographic projection) $r = 2ftan\frac \theta 2$​ 任何直线相交的角度,在变换后保持不变 iii. 等距投影 (equidistance projection) $r = f\theta$ 物体成像面上距离画面中心的距离与入射角成正比 iv. 等积投影 (equisolid angle projection) $r = 2fsin\frac \theta 2$​ 在变换前后,物体所占的立体角大小不变 v. 正交投影 (orthogonal projection) $r = fsin\theta$ 投影畸变最大,而且最大视场角不能大于180° OpenCV所用模型 先看OpenCV文档的原文: Let $P$ be a point in 3D of coordinates X in the world reference frame (stored in the matrix X) The coordinate vector of $P$ in the camera reference frame is:...

2023-05-10 · Qiao

AirSim中针孔相机畸变的实现

查阅AirSim的文档可知,AirSim支持通过simSetDistortionParams设置K1, K2, K3, P1, P2这5个畸变系数。UE镜头畸变模拟:OpenCV Lens Distortion一文提及过,Unreal的OpenCV Lens Distortion插件是通过生成的Post Process Material对UV坐标施加偏移来模拟镜头畸变。那么,AirSim又是如何处理的呢? 基本流程探究 打开任意集成了AirSim插件的Unreal工程,用Visual Studio翻阅工程源码,重点是AirSim插件的cpp代码。 在整个项目中搜索simSetDistortionParams,找到RPC客户端和服务端的实现。客户端: // <path-to-project>\Plugins\AirSim\Source\AirLib\src\api\RpcLibClientBase.cpp void RpcLibClientBase::simSetDistortionParam(const std::string& camera_name, const std::string& param_name, float value, const std::string& vehicle_name, bool external) { pimpl_->client.call("simSetDistortionParam", camera_name, param_name, value, vehicle_name, external); } 服务端: // <path-to-project>\Plugins\AirSim\Source\AirLib\src\api\RpcLibServerBase.cpp // 省略其余binding... pimpl_->server.bind("simSetDistortionParam", [&](const std::string& camera_name, const std::string& param_name, float value, const std::string& vehicle_name, bool external) -> void { getWorldSimApi()->setDistortionParam(param_name, value, CameraDetails(camera_name, vehicle_name, external)); }); // ... 顺藤摸瓜,找到WorldSimApi::setDistortionParam:...

2023-05-04 · Qiao

UE镜头畸变模拟:OpenCV Lens Distortion

出于仿真的需要,希望在Unreal中能模拟鱼眼镜头的畸变效果,看到UE官方提供了Lens Distortion和OpenCV Lens Distortion两款插件用于模拟镜头畸变,但可惜相关资料不多。UE的博文UE中的相机标定、畸变模拟与矫正 - Unreal Engine做了些介绍,但步骤却又不是很详尽,初学者如我使用时不免多绕了些路,遂写下此文。 插件介绍 这里结合UE中的相机标定、畸变模拟与矫正 - Unreal Engine的介绍,总结如下: Lens Distortion和OpenCV Lens Distortion用的camera model是一样的,背后的原理也类似。都是根据相机畸变参数生成displacement map,以此模拟镜头畸变 Lens Distortion没有标定功能,且只包含了畸变系数的前几项 Lens Distortion通过shader生成displacement map,而OpenCV Lens Distortion通过OpenCV的API用CPU计算生成,前者效率更快。不过由于displacement map通常只生成一次,这里的效率差异不太重要 补充:什么是displacement map? A displacement map is a type of texture that is used in 3D computer graphics to create the illusion of depth and detail on a surface without actually changing the geometry of the mesh. A displacement map is typically a grayscale image where the brightness values of each pixel correspond to the amount of displacement that should be applied to the surface at that point....

2023-04-28 · Qiao

PySide6 实践

信号槽问题 Python中的任意函数都可以作为槽函数使用而无需使用@Slot装饰器,只要它符合与信号连接的规则(参数对应上),使用@Slot显示地声明可以提高效率,而不需要Python去隐式地转换类型。 python - Is the PySide Slot Decorator Necessary? - Stack Overflow 另一篇文章Should I decorate slots in Pyside2 and if so how?的回答: As to whether you should decorate the slots, it’s a little trickier to answer – but generally speaking no, you don’t need to. The only place I know the slot decorator is needed is when a) using threads, as it ensures the decorated method is started in the correct thread, or b) when you want to explicitly map a given slot to a specific call signature (types) in C++....

2023-04-25 · Qiao

从Hexo到Hugo

从Hexo到Hugo 背景 促使我从WordPress搬迁到Hexo,再决定从Hexo搬迁到Hugo的动机是:想尽可能简化写博客的流程,减少除文章撰写以外一切无关事务的精力消耗。 这一追求源于我对自身的观察。一天的精力里往往绝大部分都投入在工作之上,在闲暇中再挤出时间投入写作对意志力是个考验。以往使用WordPress时,一旦VPS的访问速度不佳,那登录后台、打开编辑器、调整样式过程中等待耗费的时间,就足以将不多的意志力消磨干净。 后续切到Hexo+Github Pages后,不得不说,这让博客的发布顺畅不少,不再需要登录后台,几乎不需要考虑排版,迁移站点时也不再有数据库的顾虑。唯一的麻烦是,每次修改文件后,需要调用Hexo CLI重新生成站点并推送到Github Pages仓库。这也意味着本地总得准备一份Node.js环境。思考一番后,前面通过使用Github Actions部署Hexo把生成和部署也自动化了,只需要写文章并推送即可。 这么愉快地用了些时日,但在和Obsidian的配合使用中,又发现了新的矛盾: Hexo如果需要用相对路径引用图片,图片应置于文章同名的文件夹下。而在Obsidian中,我所采取的方式是图片集中放置于media目录下 笔记现在总是用Obsidian创建,但需要为Hexo复制一份需要发布的文章及资源,而重复总是不利于维护的 如果将Hexo仓库置于Obsidian Vault中,再将需要发布的文章直接放到Hexo仓库的posts目录下,虽然不用复制文章,但Obsidian Vault中会带入Hexo/Node.js相关的文件。这些文件和笔记无关,在整理笔记时无异于噪音 这些问题都可以通过修改笔记来迁就Hexo,但这就是前文所说的,文章撰写以外的事务。我现在相信:应当让博客工具迁就写作习惯,而不是调整写作习惯来适应工具。 方法 为解决以上矛盾,想到的方法是: 在Vault中新建一个文件夹(这里记作Publish),在其中初始化Git仓库,存放供发布的文章 新建一个静态站点文件夹,同样初始话Git仓库,Publish仓库作为Git子模块加入其中 静态站点生成器应支持相对路径,且对文件夹的名称无要求 这样Obsidian Vault中只需要存放笔记,至于静态站点生成相关的内容则不再其中。为了方便博客发布的流程,还应该做到: 推送静态站点仓库时,应自动生成和部署站点,免去本地生成站点的麻烦 在Publish仓库中推送时,应自动更新静态站点仓库,让子模块引用Publish仓库的最新提交,免去需要在两个仓库中提交和推送的麻烦 做到这些后,应当可以: 不需要为了发布而改变记录笔记的方式 只维护一份需发布的资源 只需推送文章到远端,站点即能自动更新,本地不需要配置环境或手动生成站点 本想看看有没有办法让Hexo支持任意文件夹名的相对路径,但惊讶地发现并没有轻松的方式实现(我原以为这是非常常见的需求)。Gatsby/Jekyll/Hugo等一众工具也不原生支持此需求,这似乎与文件名到URL的映射有关。不过 GitHub - zoni/obsidian-export: Rust library and CLI to export an Obsidian vault to regular Markdown 提供了一个解决方式,遂决定切换到Hugo。 实施 支持相对路径 在Obsidian Vault中新建一个文件夹(这里记为Publish),在其中初始化Git仓库,存放供发布的文章。在Github上创建远端仓库并推送 用hugo new site命令创建站点文件夹(这里记为Site),同样初始化Git仓库。在Github上创建xxx.github.io名称的仓库并推送 与Hexo一样,Hugo同样支持主题,这里选用了PaperMod | Hugo Themes 在主题的layouts/_default/_markup/render-image.html中(若无该文件则新建),加入以下代码片段以支持相对路径引用图片: {{- $url := urls.Parse .Destination -}} {{- $scheme := $url.Scheme -}} <a href=" {{- if eq $scheme "" -}} {{- if strings....

2023-04-06 · Qiao