鱼眼相机去畸变(图像拉直/展开/矫正)算法及实战总结

news2025/1/12 16:03:31

本文介绍两种方法

1、经纬度矫正法

2、棋盘格矫正法

一、经纬度矫正法

1、算法说明

经纬度矫正法, 可以把鱼眼图想象成半个地球, 然后将地球展开成地图,经纬度矫正法主要是利用几何原理, 对图像进行展开矫正。

        经过P点的入射光线没有透镜的话,本应交于相机成像平面的e点。然而,经过鱼眼相机的折射,光线会交于相机成像平面的d点,就产生了畸变,因此畸变图像整体上呈现出像素朝图像中心点聚集的态势。

        而去畸变,就是将折射到d点的点,重新映射回到e点,因此去畸变之后的图像与原始的鱼眼图像相比,仿佛是把向心聚集的像素又重新向四周铺展开来。

       详细的推导流程及公式见地址:AVM环视系统——鱼眼相机去畸变算法 - 知乎

2、 代码

import math
from PIL import Image


im = Image.open("/Users/Fisheye_photo-600x600.jpg")
im.show()

width, high = im.size
sqrt_len = min(width, high)
im = im.transform((sqrt_len, sqrt_len),
                    Image.EXTENT,
                    ((width-sqrt_len)/2, (high-sqrt_len)/2, 
                    sqrt_len+(width-sqrt_len)/2, sqrt_len+(high-sqrt_len)/2)
                    )
width = high = sqrt_len

idata = im.getdata()
odata = []

alpha = math.pi/2

out_high = round(high * math.tan(alpha/2))
out_width = round(width * math.tan(alpha/2))
out_radius = round(high * math.tan(alpha/2))
out_center_x = out_width / 2
out_center_y = out_high / 2

out_bl_x = 0
out_br_x = out_width - 1
out_bt_y = 0
out_bb_y = out_high - 1

out_bl_cx = out_bl_x - out_center_x
out_br_cx = out_br_x - out_center_x
out_bt_cy = out_bt_y - out_center_y
out_bb_cy = out_bb_y - out_center_y

src_radius = round(high * math.sin(alpha/2))
src_center_x = width / 2
src_center_y = high / 2

for i in range(0, high * width):
    ox = math.floor(i / out_width)
    oy = i % out_high
    
    cx = ox - out_center_x;
    cy = oy - out_center_y;
    
    out_distance = round(math.sqrt(pow(cx, 2) + pow(cy, 2)))
    theta = math.atan2(cy, cx)
    if (-math.pi/4 <= theta <= math.pi/4):
        bx = out_radius * math.cos(math.pi/4)
        by = bx * math.tan(theta)
    elif (math.pi/4 <= theta <= math.pi*3/4):
        by = out_radius * math.sin(math.pi/4)
        bx = by / math.tan(theta)
    elif (-math.pi*3/4 <= theta <= -math.pi/4):
        by = out_radius * math.sin(-math.pi/4)
        bx = by / math.tan(theta)
    else:
        bx = out_radius * math.cos(-math.pi*3/4)
        by = bx * math.tan(theta)
        
    
    bdy_distance = round(math.sqrt(pow(cx, 2) + pow(cy, 2)))
    src_distance = src_radius * bdy_distance / out_radius
        
    
    src_x = round(src_center_x + math.cos(theta) * src_distance)
    src_y = round(src_center_y + math.sin(theta) * src_distance)
    
    src_idx = src_x*width + src_y    
    if(0 < src_idx < high*width):
        odata.append(idata[src_idx])
    else:
        odata.append((0,0,0))
    


om = Image.new("RGB", (high, width))
om.putdata(odata)
om.show()

3、代码及图片地址:GitHub - duducosmos/defisheye: Fast Corrects for fisheye distortion in an image.

二、棋盘格矫正方法

1、算法说明

利用棋盘格进行标定, 然后计算鱼眼镜头的畸变系数以及内参, opencv中自带有fisheye模块, 可以直接根据棋盘格标定结果,采用cv2.fisheye.calibrate计算畸变系数以及内参, 然后使用cv2.fisheye.initUndistortRectifyMap函数计算映射矩阵, 最后根据映射矩阵, 使用cv2.remap进行矫正。

2、代码

import cv2
import numpy as np
import math
import time

# 鱼眼有效区域截取
def cut(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    (_, thresh) = cv2.threshold(img_gray, 20, 255, cv2.THRESH_BINARY)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = sorted(contours, key=cv2.contourArea, reverse=True)[0]
    x,y,w,h = cv2.boundingRect(cnts)
    r = max(w/ 2, h/ 2)
    # 提取有效区域
    img_valid = img[y:y+h, x:x+w]
    return img_valid, int(r)

# 鱼眼矫正
def undistort(src,r):
    # r: 半径, R: 直径
    R = 2*r
    # Pi: 圆周率
    Pi = np.pi
    # 存储映射结果
    dst = np.zeros((R, R, 3))
    src_h, src_w, _ = src.shape
    # 圆心
    x0, y0 = src_w//2, src_h//2

    for dst_y in range(0, R):

        theta =  Pi - (Pi/R)*dst_y
        temp_theta = math.tan(theta)**2

        for dst_x in range(0, R):
            # 取坐标点 p[i][j]
            # 计算 sita 和 fi

            phi = Pi - (Pi/R)*dst_x
            temp_phi = math.tan(phi)**2

            tempu = r/(temp_phi+ 1 + temp_phi/temp_theta)**0.5
            tempv = r/(temp_theta + 1 + temp_theta/temp_phi)**0.5

            if (phi < Pi/2):
                u = x0 + tempu
            else:
                u = x0 - tempu

            if (theta < Pi/2):
                v = y0 + tempv
            else:
                v = y0 - tempv

            if (u>=0 and v>=0 and u+0.5<src_w and v+0.5<src_h):
                dst[dst_y, dst_x, :] = src[int(v+0.5)][int(u+0.5)]

                # 计算在源图上四个近邻点的位置
                # src_x, src_y = u, v
                # src_x_0 = int(src_x)
                # src_y_0 = int(src_y)
                # src_x_1 = min(src_x_0 + 1, src_w - 1)
                # src_y_1 = min(src_y_0 + 1, src_h - 1)
                #
                # value0 = (src_x_1 - src_x) * src[src_y_0, src_x_0, :] + (src_x - src_x_0) * src[src_y_0, src_x_1, :]
                # value1 = (src_x_1 - src_x) * src[src_y_1, src_x_0, :] + (src_x - src_x_0) * src[src_y_1, src_x_1, :]
                # dst[dst_y, dst_x, :] = ((src_y_1 - src_y) * value0 + (src_y - src_y_0) * value1 + 0.5).astype('uint8')

    return dst

if __name__ == "__main__":
    t = time.perf_counter()
    frame = cv2.imread('../imgs/pig.jpg')
    cut_img,R = cut(frame)
    result_img = undistort(cut_img,R)
    cv2.imwrite('../imgs/pig_nearest.jpg',result_img)
    print(time.perf_counter()-t)

效果图

3、代码地址

https://github.com/HLearning/fisheye

三、总结:比对两个算法

本人用两个算法对一张图像进行拉直,发现经过经纬度矫正算法生成的图像原作者裁剪掉了边缘部分,见下图效果图,中间黑框内的图像是经过“经纬度矫正法”得到的效果图,外面的大图是用“棋盘格矫正法”得到的效果图

为了更直观,更改了图像的透明度,可以看出两个算法的效果还是多少有些差别的。

其实,两个算法的边缘部分都被严重拉伸,丢不丢掉看适用场景和个人需要吧。

四、知识拓展

立体标定

算法说明

坐标映射建立,各区域的角点都有一维世界坐标为0,对应图5中三幅子图像分别为Y=0,X=0,Z=0。根据棋盘方格边长以及与世界坐标原点间隔的方格数,可得到所有角点的世界坐标。从而建立起二维图像坐标与三维世界坐标的一一映射,用于模型参数的求解。

 

参考地址:采用立体标定板的鱼眼相机快速标定方法_真空技术_新闻动态_深圳市鼎达信装备有限公司

基于双经度模型的鱼眼图像畸变矫正方法

基于双经度模型的鱼眼图像畸变矫正方法 - 百度文库

 

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

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

相关文章

操作系统-《王道 操作系统》

概念、功能和目标 概念 什么是操作系统 功能和目标 作为系统的管理者向上提供方便简易的服务作为最接近底层硬件的层次 特征 并发 共享 并发与共享的关系—互为存在条件 虚拟 异步 发展与分类 单道批处理系统多道批处理系统 优点&#xff1a;多道程序并发执行&#xff0c…

Java笔记六(面向对象:类与对象)

面向对象编程的本质&#xff1a;以类的方式组织代码&#xff0c;以对象的组织&#xff08;封装&#xff09;数据 抽象 三大特征&#xff1a;封装 继承 多态 从认识角度考虑是先有对象后有类。对象&#xff0c;是具体的事物。类&#xff0c;是抽象的&#xff0c;是对对象的抽…

【JVM】 类加载机制、类加载器、双亲委派模型详解

文章目录 前言一、类加载机制二、类加载器三、双亲委派模型总结 前言 &#x1f4d5;各位读者好, 我是小陈, 这是我的个人主页 &#x1f4d7;小陈还在持续努力学习编程, 努力通过博客输出所学知识 &#x1f4d8;如果本篇对你有帮助, 烦请点赞关注支持一波, 感激不尽 &#x1f4d…

Foxit PDF

Foxit PDF 福昕PDF 软件&#xff0c;可以很好的编辑PDF文档。 调整&#xff30;&#xff24;&#xff26;页面大小 PDF文档中&#xff0c;一个页面大&#xff0c;一个页面小 面对这种情况,打开Foxit PDF 右键单击需要调整的页面,然后选择"调整页面大小". 可以选择…

【网络安全-sql注入(5)】sqlmap以及几款自动化sql注入工具的详细使用过程

一&#xff0c;sqlmap 工具的详细使用 kali系统自带这个工具&#xff0c;无需安装直接sqlmap 后面接参数使用 Windows上参照以下方法安装即可 1-1 工具下载 1-1-1 sqlmap下载 sqlmap 工具下载地址&#xff1a; GitHub - sqlmapproject/sqlmap: Automatic SQL injection a…

【C语言】【动态内存管理】malloc,free,calloc,realloc

1.malloc函数 void* malloc(size_t size)功能&#xff1a;向内存申请字节为 size大小的空间 使用时要包含头文件&#xff1a;<stdlib.h> 开辟成功&#xff1a;返回开辟好的空间初始地址的指针 开辟失败&#xff1a;返回空指针 NULL 使用举例&#xff1a; (malloc和free…

Win10系统中GPU深度学习环境配置记录

运行环境 系统&#xff1a;Win10 处理器 Intel(R) Core(TM) i7-9700K CPU 3.60GHz 3.60 GHz 机带 RAM 16.0 GB 设备 ID A18D4ED3-8CA1-4DC6-A6EF-04A33043A5EF 产品 ID 00342-35285-64508-AAOEM 系统类型 64 位操作系统, 基于 x64 的处理器 显卡&#xff1a;NVIDIA GeF…

快速上手Apache POI

哈喽~大家好&#xff0c;这篇我们来看看快速上手Apache POI。 &#x1f947;个人主页&#xff1a;个人主页​​​​​ &#x1f948; 系列专栏&#xff1a;【日常学习上的分享】 &#x1f949;与这篇相关的文章&#xff1a; Red…

你写过的最蠢的代码是?——一起探讨编程的奇葩时刻

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

常见web信息泄露

一、源码(备份文件)泄露 1、git泄露 Git是一个开源的分布式版本控制系统&#xff0c;在执行git init初始化目录的时候&#xff0c;会在当前目录下自动创建一个.git目录&#xff0c;用来记录代码的变更记录等。发布代码的时候&#xff0c;如果没有把.git这个目录删除&#xff…

CCF CSP认证 历年题目自练 Day20

题目一 试题编号&#xff1a; 201903-1 试题名称&#xff1a; 小中大 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 题目分析&#xff08;个人理解&#xff09; 常规题目&#xff0c;先看输入&#xff0c;第一行输入n表示有多少数字&am…

Rabbitmq安装-docker版

1.简介 2.安装消息队列 下载地址https://www.rabbitmq.com/download.html 使用docker方式安装 需要先下载docker&#xff0c;参考文章https://blog.csdn.net/weixin_43917045/article/details/104747341?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22arti…

基于SSM在线医疗服务系统+jsp【附开题|万字文档(LW)和搭建文档】

主要功能 前台登录&#xff1a; 注册用户&#xff1a;用户名、密码、姓名、联系电话 注册医生&#xff1a;医生工号、密码、医生姓名、职称、联系电话、邮箱 用户&#xff1a; ①首页、药品推荐、新闻资讯、健康资讯展示 ②医生坐诊、个人介绍、评价、预约挂号、点我收藏、药品…

实现map和set

map和set的底层就是红黑树&#xff0c;所以只要实现了红黑树&#xff0c;就可以考虑封装出map和set。所以本文会重点介绍我是如何一步步封装map和set&#xff0c;就不会再介绍红黑树的实现&#xff0c;实现可看我上篇博客&#xff0c;个人觉得封装中最麻烦的就是模板参数和迭代…

linux系统的启动流程

目录 简述linux的启动流程 git简介 Linux文件 Ubuntu文件汇总 linux文件属性 Linux命令行 更换软件源 简述linux的启动流程 韦东山课程学习路线&#xff1a;APP应用--DEV驱动--项目。 百问网官网 git资料&#xff1a;https://e.coding.net/weiongshan/01_all_series_qu…

来看看这个JS题输出什么?教你通过断电调试一步步看原因

&#x1f3b6;让我们调试看看这段代码 var foo { n: 1 };(function (foo) {console.log(foo.n) foo.n 3var foo { n: 2 }foo.n 4console.log(foo.n)})(foo)console.log(foo.n);&#x1f367;输出结果 &#x1f3a1;调试解析 &#x1f389;第一步 &#x1f38f;第二步 ✨第…

新一代网络框架UringNet,基于最新的异步I/O

介绍 在去年的一篇文章中&#xff0c;笔者曾经提到了最新一代的网络I/O框架UringNet。具体内容可以参考Rings’ Power,性能“世界第一”的Web I/O框架。这是基于最新Linux内核的异步I/O组件io_uring开发的网络框架。由于采用了最新的异步框架&#xff0c;因此在同等硬件配置条件…

五款可替代163邮箱的电子邮件服务

在众多邮箱品牌中&#xff0c;163邮箱作为中国最早的邮箱服务提供商之一&#xff0c;其出海之路并不顺利。本文将探讨163邮箱出海的劣势&#xff0c;并介绍一些替代品&#xff0c;以帮助用户更好地选择适合自己的邮箱服务。 “163邮箱的替代品有哪些&#xff1f;外贸行业适合选…

指针详解第二部分

目录 1. 数组名的理解 2. 使⽤指针访问数组 3. ⼀维数组传参的本质 4. 冒泡排序 5. ⼆级指针 6. 指针数组 7. 指针数组模拟⼆维数组 1. 数组名的理解 首先先看一个代码 #include <stdio.h> int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };printf("&a…

Java8 Lambda.stream.sorted() 方法使用浅析分享

文章目录 Java8 Lambda.stream.sorted() 方法使用浅析分享sorted() 重载方法一升序降序 sorted() 重载方法二升序降序多字段排序 mock代码 Java8 Lambda.stream.sorted() 方法使用浅析分享 本文主要分享运用 Java8 中的 Lambda.stream.sorted方法排序的使用&#xff01; sorted…