傅里叶变换 & 基函数 & 曲线拟合 & Spherical Harmonics

时域信号与频域信号

时域信号即:描述一个信号的幅值y与时间t的关系
频域信号即:假设信号是有周期的(即使周期是无限也算是有周期的),用多个周期信号叠加起来拟合这个信号。频域信号就是用来描述一个信号的组成周期信号,频率从低到高,每个频率的信号都有自己对应的幅值,构成了一个频谱图。

频率信号与时域信号,都是信号的一种描述方式,信号本身是不变的,也就是说时域信号与频域信号之间是可以进行转换的,不会丢失信号的信息。但是时域信号和频域信号体现的是信号的不同性质。

傅里叶变换

刚才说到时域信号与频域信号之间是可以转换的,所使用的工具就是傅里叶变换。其思想就是用一系列正弦余弦函数合成一个信号来拟合原始信号。这些正弦余弦函数的幅值与频率,就构成了频域信号。

  • 离散傅里叶变换: F [ k ] = ∑ n = 0 N − 1 f [ n ] e − j 2 π N k n F[k]=\sum_{n=0}^{N-1}f[n]e^{-j\frac{2\pi}Nkn} F[k]=n=0N1f[n]ejN2πkn
  • 连续傅里叶变换: F ( ω ) = ∫ − ∞ ∞ f ( t ) e − j ω t d t F(\omega)=\int_{-\infty}^\infty f(t)e^{-j\omega t}dt F(ω)=f(t)etdt
  • 傅里叶反变换: f ( t ) = 1 2 π ∫ − ∞ ∞ F ( ω ) e j ω t d ω f(t)=\frac1{2\pi}\int_{-\infty}^\infty F(\omega)e^{j\omega t}d\omega f(t)=2π1F(ω)etdω

如果只是将信号用正弦函数余弦函数来拟合,这种将一个时域信号分解为多个正弦余弦函数组合的方法称为傅里叶展开。与之类似的还有泰勒展开,拉普拉斯变换

他们之间的详细关系可以参考这篇blog:拉普拉斯变换和傅里叶变换
注意,拉普拉斯展开是行列式按行展开,跟泰勒展开和傅里叶展开完全不同的概念。

作用

将时域信号转换为频域信号

应用

  • 电台调频,电台收音机收到很多频道的信号混杂一起,可以将信号转换为频域,然后通过调频单独拿出来对应频率的信号,得到想要的信息。
  • 解方程,可以将微分方程或者积分方程变换到频域,然后得到频域解,最后将这个解再反变换回时域,得到时域解。时域的卷积等于频域的乘积,所以将方程变换到频域会比时域好解。

延申

  • 快速傅里叶变换:FFT 是 DFT 的快速算法,用于高效计算 DFT 和 IDFT,广泛应用于数字信号处理。
  • 傅里叶神经算子

基函数与曲线拟合

上面谈傅里叶变换的时候,提到“用一系列正弦余弦函数拟合信号”,这个提炼到更为通用的数学中,即:用一系列简单函数拟合一个复杂函数。

作用

使用一系列简单函数拟合出一个复杂函数。

应用

这种方法在数值分析中很常用,这些简单函数被称为基函数。根据基函数的选择空间不同,可以分为不同的拟合方法。
拟合的常见应用:

机器学习

给出一系列离散点数据(x,y),拟合出x和y的对应关系。常见使用多项式拟合。还可以使用高斯函数即正态分布函数作为基函数,这种被称为高斯基函数。

曲线压缩 & 自由曲线拟合

考虑一个情况:要存储一个自由复杂曲线,如何存储?
可以进行采样,每隔0.01就采样一下,然后将这些都存储起来,需要点x对应的y时,找到x对应的 x i x_i xi x i + 1 x_{i+1} xi+1,然后使用线性插值方法计算出y,这样可以,但是存储一个曲线要用多大的空间?不可行。
这时候就体现曲线拟合的作用了,我们可以用一系列基函数拟合出这个曲线,这样就节省了非常非常多的空间嘎嘎。嘎嘎好使!而且,我们可以根据我们需求的精度,来控制使用基函数的个数,当我们对精度要求不高的时候,我们就可以使用少一点基函数,当我们精度要求高的时候,我们就可以用多一点基函数。

轨迹光顺

考虑我们开一辆车过一个拐弯,你会怎么拐弯?
在这里插入图片描述
你是会走虚线拐个钝角弯,还是走红色曲线弯?就算想走第一个,恐怕还得会漂移,不然还真实现不了这么高难度的动作。

这就是曲线拟合的另一个用途,轨迹光顺,在CAD设计,机械臂路径规划,车辆路径规划,以及数控系统进刀路径规划中都需要考虑光顺,毕竟现实不是图形学,你怎么折腾都能过,不过是显示是否顺畅的区别。现实中如果真的规划出了虚线这种路线,当场翻车给你看。

在之前没有CAD的时候,设计人员要画一条光滑曲线,需要那一种软样条Spline,把样条凹成想要的曲线,然后比着画,这种名称也被保留了下来,这种光顺曲线就被称为样条。
常见的样条有:贝塞尔样条,B样条,PH样条等等。
其中PH样条的参数和样条长度是有直接的解析关系的,而贝塞尔样条和B样条没有,即,曲线参数t增大 Δ t \Delta t Δt是否可以解出曲线长度增大多少。
这又会产生很多问题,这里不细究。

延申

曲线拟合可以理解为,由一些简单函数构成函数空间,在函数空间中找到一条符合要求的目标函数,主要分为三步:去哪里找?找哪个?怎么找?

去哪里找

基函数有很多种,这些函数是怎么找到的?如何判断一个函数是否可以成为基函数?
我们要选择一类基函数,这些基函数通过加加减减,可以成为非常多的函数,这些函数构成了一个函数空间。
不知道这么讨论的时候,是否想到了矩阵理论中的向量空间,以及概率论中的概率空间?
对的,就是这样,基向量构成向量空间,基函数构成函数空间,如何判断一组函数是否可以称为一组基函数,判断这组战术是否可以构成一个涵盖了目标函数的非凹集合。
常见的基函数:

  • 三角函数/傅里叶基函数
  • 多项式基函数
  • RBF基函数
  • 高斯基函数
  • sigmoid基函数

看到sigmoid,是否想到了神经网络?激活函数?
对的,神经网络也可以理解为函数拟合,每个神经元的激活函数可以理解为基函数,每个神经元的输入和线性变换,可以理解为在构造不同的幅值的基函数,而且这也可以解释为什么单层感知机只能处理线性可分问题,因为单层感知机本质就是单一频率的基函数组合,自然拟合能力有限,层数越多,基函数频率越多,拟合能力越强。如果加入残差连接,从拟合的角度看,就是将基函数的范围从单一频率扩展到了最高频率以下的所有频率,自然拟合能力就更强大了,也更容易训练了,因为虽然从本质上来说,将后续层的权重设置为1也可以实现相同的目的,但是这是需要不断训练才能达到的,而ResNet这种可以直接不用训练就可以达到降低频率的效果。

找哪个?

即,如何判断一个函数是不是我们想要的目标函数?

  • 最简单的方法:对于n次多项式,有n+1个未知量,,从原函数上找n+1个点,使得 y i = f ( x i ) y_i = f(x_i) yi=f(xi),构成了n+1个方程,解方程组就行。
    这种被称为插值法
  • 另一种方法:设定一个误差函数,或者叫损失函数(想到神经网络没?)
    这种方法被称为逼近法,即使得函数与原函数的误差最小,得到最逼近原函数的目标函数。
    损失函数: m i n ∑ i = 0 n ( y i − f ( x i ) ) 2 min\sum_{i=0}^n(y_i-f(x_i))^2 mini=0n(yif(xi))2
怎么找

有了上面的铺垫,问题就变成了如何求解上述损失函数的最优解

  • 插值法:拉格朗日插值法,构建拉格朗日多项式,求解n+1次方程
  • 逼近法:梯度下降求解法

这一部分在GAMES102中讲的非常好,可以去看一下。

二维傅里叶变换与曲面拟合

将傅里叶变换扩展到二维,就可以对曲面进行拟合。
二维傅里叶变换的基函数,是平面波。一维的正弦波,需要三个参数:频率,幅值,相位。二维的正弦平面波需要四个参数:频率,幅值,相位,方向。
一维的时候,我们可以用一个频谱图来表示,二维的时候如何表示呢?
在这里插入图片描述

就是这样表示,中间图像就是一张图片的频域显示。图中的每个pixel表示一个一个正弦平面波,这个pixel到中心点的距离体现了他的频率,与中心点连线的方向体现了他的方向,该pixel本身的灰度值体现了他的幅值。最右边的图是这张图的相位图,每个pixel体现了该像素对应的方波相位大小。
我们可以对频域信号进行处理,例如设置一个低通滤波,消掉高频信号,达到和在时域进行均值模糊一样的效果。
请添加图片描述

这也是压缩的原理,既然这么一点低频信号就可以重建出差不多质量的图像,那对于一些低精度的场合,就没必要存储太多高频信号。

对应代码

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

def lowpass_filter(dft,radius):
    N = dft.shape[0]
    center = N // 2
    x,y = np.ogrid[:N,:N]
    mask = (x - center) ** 2 + (y - center) ** 2 >= radius ** 2
    dft[mask] = 0
    # 等同于dft = dft * mask
    return dft
    

# 加载图像
image = Image.open('dog.jpg')  # 替换为你的图像路径
image = image.convert('L')  # 转换为灰度图像

# 将图像转换为numpy数组
image_array = np.array(image)

# 计算傅里叶变换
fft_image = np.fft.fft2(image_array)
fft_shifted = np.fft.fftshift(fft_image)  # 将DC分量移到频谱中心

# 计算幅值谱和相位谱
magnitude_spectrum = 20 * np.log(np.abs(fft_shifted) + 1)  # 加1避免对数为负无穷
phase_spectrum = np.angle(fft_shifted)


fig = plt.figure(figsize=(12, 6))

# 绘制原图
plt.subplot(1,3,1), plt.imshow(image_array, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])

# 绘制幅值谱
plt.subplot(1,3,2), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])

# 绘制相位谱
plt.subplot(1,3,3), plt.imshow(phase_spectrum, cmap='gray')
plt.title('Phase Spectrum'), plt.xticks([]), plt.yticks([])

plt.show()

lowpass_dft = lowpass_filter(fft_shifted, 60)

plt.figure(figsize=(12, 6))
lowpass_magnitude_spectrum = 20 * np.log(np.abs(lowpass_dft) + 1)
phase_spectrum = np.angle(lowpass_dft)

plt.subplot(1,3,1), plt.imshow(lowpass_magnitude_spectrum, cmap='gray')
plt.title('Lowpass Magnitude Spectrum'), plt.xticks([]), plt.yticks([])

plt.subplot(1,3,2), plt.imshow(phase_spectrum, cmap='gray')
plt.title('Phase Spectrum'), plt.xticks([]), plt.yticks([])

ifft_shifted = np.fft.ifftshift(lowpass_dft)
ifft_image = np.fft.ifft2(ifft_shifted)
reconstructed_image = np.abs(ifft_image)
plt.subplot(1,3,3), plt.imshow(reconstructed_image, cmap='gray')
plt.title('Reconstructed Image'), plt.xticks([]), plt.yticks([])

plt.show()

作用

拟合曲面。

应用

  • 自由曲面,存储的时候,如何存储
    • 注意,图像并不是这么存储的,图像存储专门的算法例如jpg,jpeg,png等,这些算法有用到二维傅里叶变换,但是还有一系列操作,比这个要复杂的多。
  • 图像转到频域进行处理,锐化,模糊,压缩,卷积

Spherical Harmonics与球面拟合

上面谈了曲线和曲面情况下的拟合。对于一个封闭曲面,例如球上面如何进行拟合呢?肯定是不能再用正常的直角坐标系中的函数进行拟合了,因为函数是一个输入对应一个输出,但是像球面这种,一个输入会对应两个输出,如何拟合?
一种办法是参数化,将x,y,z都进行参数化,分别拟合x(t),y(t),z(t),这种做法可以但是不是很好使。
第二种办法是展开,将一个球展开成一个曲面,就像地球仪一样,但是这种方法也不是和好使

GAMES202里面说不好使,会出现缝,具体为什么我也不知道,没有细究过

第三种办法就是我们要谈到的,Spherical Harmonics,其实就是将一个球使用球面函数进行拟合。球谐函数原本是研究量子力学和电子轨道用的,后面被拿到其他领域,广泛应用。
球面函数的输入是两个角度,对于球面函数 f ( θ , φ ) f(\theta,\varphi) f(θφ),值可以定义为距离 r = f ( θ , φ ) r = f(\theta,\varphi) r=f(θφ),也可以将球看作一个单位球,函数值表示的是灰度值,即 g = f ( θ , φ ) g = f(\theta,\varphi) g=f(θφ)
下图是将函数值看作距离的情况下,球谐函数基函数的显示,从低阶到高阶,第 l l l阶有 2 l + 1 个 2l+1个 2l+1基函数,第 n n n阶及以前一共有 ( n + 1 ) 2 (n+1)^2 (n+1)2个基函数,将这些基函数结合起来就可以拟合一个球面坐标系上的函数。
在这里插入图片描述
下图是拟合情况
在这里插入图片描述
下图是将函数值看作色彩的基函数显示,这里面写的XYZ是因为这里把基函数用直角坐标系表示出来了。
在这里插入图片描述

用球面坐标系也好,直角坐标系也好,体现的是距离也好,颜色也好,都无所谓,函数就是那么几个函数,用什么方式显示是看起来不一样,本质都是一样的。

应用

在光照渲染中,需要计算球面积分,可以用SH函数进行压缩,根据所需精度来控制SH函数的阶数,进而控制压缩程度。就如上面GAMES104那张图中画的一样,本来需要存储一个点周围整个圆每个点的光照信息,现在可以直接用几个参数就可以存储了,用的时候乘以SH基函数就可以复原出光照信息。妙哇妙哇。
存储光照信息一般使用1阶SH,即0阶和1阶一共4个参数。或者二阶,9个参数。RGB三个颜色各自使用SH存储的话,就是 3 × 9 = 27 3 \times 9 =27 3×9=27个参数。用这27个参数,就可以很精细表达光照。
对于环境光,我们可以只用SH0和SH1,即 3 × 4 = 12 3\times 4=12 3×4=12个参数就可以很好表达
在这里插入图片描述
阶数越高,计算量和存储量越大,而且难以理解,容易出现一些花里胡哨的情况。
而且使用SH还有一个好处,SH的基函数是正交的,而且原函数间卷积就等于他们相应的每个SH基函数的参数间卷积,结果是一样的。

这部分在GAMES202和GAMES104都有讲,202讲的更细一些,104主要是讲应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值