张春成

V2

2022/08/31阅读:34主题:默认主题

鱼眼看世界

鱼眼看世界

很好奇鱼眼镜头里的“扭曲”是如何生成的,于是就有了这个模拟计算。


鱼眼镜头

偶然看到了这个搞全景相机的公司,Instra360。

全景相机,运动相机 - 影石Insta360官网,360度全景运动相机

它勾起了我一直以来的好奇,那就是全景相机的图像为什么是以这样的方式扭曲的?或者更具体一点的问题是,线性变换为什么会造成类似球面的扭曲?

Untitled
Untitled

我们首先假设真实的物理场景是一个简单的幕布,幕布用方格的方式进行染色

其中,下标代表求模数

经过颜色映射,即可以得到周期性方格的纹理图样。我们进一步假设这个图样在整个空间中构成一个平面,该平面与视线垂直。

texture.jpg
texture.jpg

接下来,我们假设有一个鱼眼镜头,那么在它的视野中,每个像素的位置可以用一对角度表示

其中,两个代数分别代表视线与水平和垂直轴之间的夹角,你可以想象成眼睛左右扫视和上下运动时视线的变化,由于眼睛始终在前方,因此它的视野范围不会超过180度。也因此,视线与平面的交点可以表示为三角函数的形式

在映射完成后,只要将位置代入到纹理方程,就可以计算得到该视线角度的颜色值,绘制如下。可以看到,虽然每条线都仍然是直的,但对角线的部分已经能看出扭曲

texture-in-fish-eye.jpg
texture-in-fish-eye.jpg

接下来,我将纹理稍微换个方向,就可以便这个现象更明显一些

texture.jpg
texture.jpg
texture-in-fish-eye.jpg
texture-in-fish-eye.jpg

代码

以下是实现计算和绘图的代码

import numpy as np
import plotly.express as px
import matplotlib.pyplot as plt
from tqdm.auto import tqdm

# Picture size
width = 400
height = 300

# Cycle length of the texture
length = 50

# Distance between fish eye and scene 
distance = 100

# Generate and draw the texture
mat = np.zeros((height, width))

for x in tqdm(range(width)):
    for y in range(height):
        v = max(x % length, y % length)
        # Texture 2 in 45 degrees
    # v = (x + y) % length
        mat[y, x] = v

fig = px.imshow(mat, title='Texture')
fig.write_image('texture.jpg')
fig.show()

# Generate and draw the fish-eye view

y_angles = np.linspace(-np.pi/2, np.pi/2, height)
x_angles = np.linspace(-np.pi/2, np.pi/2, width)

fish_mat = np.zeros((height, width))

for yj, ya in tqdm(enumerate(y_angles)):
    y = distance * np.tan(ya)
    for xj, xa in enumerate(x_angles):
        x = distance * np.tan(xa)
        v = max(x % length, y % length)
    # Texture 2 in 45 degrees
    # v = (x + y) % length
        fish_mat[yj, xj] = v

fig = px.imshow(fish_mat, title='Texture in fish-eye')
fig.write_image('texture-in-fish-eye.jpg')
fig.show()

分类:

后端

标签:

后端

作者介绍

张春成
V2