← 返回技术文章

前言

在 Poisson Surface Reconstruction 表面重建文章中, 提到了使用自身 n 次卷积的 box filter 来作为平滑滤波器来说对指示函数进行平滑,生成梯度场。 那么 box filter 自身的 n 次卷积是什么呢? 文章里面说用一个方差是采样分辨率的高斯函数是合适的,为什么可以用 box filter 自身 n 次卷积来代替高斯函数是合适的呢? 下面就这个问题详细进行展开讨论。 因为采用的滤波器会直接影响到算法的数值离散化方式和效率。我们需要在理论和实践上都去理解它。


🧩 一、背景:Poisson 重建中的卷积思想

在 Poisson 重建中,我们要求解一个方程:

Δχ=V\Delta \chi = \nabla \cdot \mathbf{V}

其中

离散时,我们通常在 三维格点上 近似求解这个方程。此时,χ\chi 会用 基函数展开

χ(x)=iciB(xxi)\chi(\mathbf{x}) = \sum_i c_i B(\mathbf{x} - \mathbf{x}_i)

这里的 B()B(\cdot) 就是一个 B-spline(Basis Spline) 核函数。 实际上 box filter 自身 n 次卷积又称为B-spline 核函数,或者称为 B 样条函数。 没错,这个也就是计算机图形学里面那个 B 样条函数,关于B样条函数的术语,可以参考附录B

B-spline 特点:


🧱 二、B-spline 和 box filter 的关系

✅ 结论一句话:

B-spline 核函数是 box filter 的多次自卷积结果。

具体地:

Bn(x)=(bbb)n+1 次B_n(x) = \underbrace{(b * b * \cdots * b)}_{n+1 \text{ 次}}

其中 b(x)b(x)box filter(单位宽度的矩形函数)

🧮 举例说明

  1. 0 阶 B-spline

    B0(x)={1x<120otherwiseB_0(x) = \begin{cases} 1 & |x| < \tfrac{1}{2}\\ 0 & \text{otherwise} \end{cases}

    这就是一个 box filter

  2. 1 阶 B-spline

    B1(x)=B0B0B_1(x) = B_0 * B_0

    也就是两个 box filter 卷积后的结果,形成一个 三角形核(tent function)

  3. 2 阶 B-spline

    B2(x)=B1B0B_2(x) = B_1 * B_0

    这会变成一个二次平滑的核(光滑的凸形),再往上阶就更平滑。

下图是通过Box filter卷积展示的B0-B3的 B-spline 核函数的形状,可以看到3阶 B-spline 的形状已经很像一个gaussian 核函数了。
alt text

如果感兴趣box filter 卷积的形状推导和代码示例,可以参考 附录 A,推导 B-spline 的分段表达式


🧠 三、为什么 Poisson 重建里用 B-spline

  1. 紧支撑(compact support)
    每个基函数只影响局部区域(通常一个格点的八邻域)。
  2. 可用 box filter 实现快速卷积
    因为 B-spline = 多次 box filter 卷积,所以在实现上可以利用积分图(summed-area table)或 box filtering 技巧来快速计算梯度、散度和能量项。
    卷积的次数越多,函数越光滑:
  3. Poisson 重建里通常用 三次 B-spline(cubic B-spline), 因为它在 C2C^2 连续的情况下计算梯度和 Laplace 都很稳定
    实现上则用 box filter 的多次积分来高效模拟卷积。

⚙️ 四、数值计算上的意义

当你要计算:

B(xxi)B(xxj)dx\int B(\mathbf{x} - \mathbf{x}_i) B(\mathbf{x} - \mathbf{x}_j)\, d\mathbf{x}

这样的项时,
用 box filter 实现比直接算 spline 快得多。
所以在实现中,Poisson 重建经常会:


🔍 五、总结对比表

概念 数学定义 在 Poisson 重建中的作用 与 box filter 的关系
Box filter 0 阶 B-spline 最基础的卷积单元 自身
B-spline (1 阶) 两次 box 卷积 平滑插值 box filter * box filter
B-spline (2 阶以上) 多次 box 卷积 提供光滑的基 Bn=b(n+1)B_n = b^{*(n+1)}
应用 定义 χ 的基函数 将法向场光滑化、构造系数矩阵 可快速实现 via box filter

📖 参考文献


附录 A,推导 B-spline 的分段表达式

一 1 阶 B-spline 是一个三角形核函数

下面给出证明,我们先证明 1 阶 B-spline 是一个三角形核函数。

证明:若

b(x)={1,x<12,0,otherwise,b(x)=\begin{cases}1,&|x|<\tfrac12,\\0,&\text{otherwise},\end{cases}

则一阶 B-spline (即 B1B_1)定义为 B1=bbB_1=b*b,其解析表达为

B1(x)={1x,x<1,0,x1.B_1(x)=\begin{cases} 1-|x|, & |x|<1,\\[4pt] 0, & |x|\ge1. \end{cases}

(注意这里 bb 的面积为 1,因而 B1B_1 的峰值为 1,整体积分仍为 1。)

1. 卷积定义

两个函数的卷积:

(B1)(x)=(bb)(x)=b(t)b(xt)dt.(B_1)(x)=(b*b)(x)=\int_{-\infty}^{\infty} b(t)\,b(x-t)\,dt.

由于 bb 是指示函数(indicator),可以写成

b(t)=1[12,12](t),b(xt)=1[12,12](xt).b(t)=\mathbf{1}_{[-\tfrac12,\tfrac12]}(t),\qquad b(x-t)=\mathbf{1}_{[-\tfrac12,\tfrac12]}(x-t).

于是被积函数为 1 当且仅当 t[12,12]t\in[-\tfrac12,\tfrac12]xt[12,12]x-t\in[-\tfrac12,\tfrac12]

第二个条件等价于 t[x12,x+12]t\in[x-\tfrac12,x+\tfrac12]

因此被积函数为 1 当且仅当

tI(x)[12,12][x12,x+12].t\in I(x)\equiv [-\tfrac12,\tfrac12]\cap[x-\tfrac12,x+\tfrac12].

卷积值等于交集 I(x)I(x) 的长度(Lebesgue measure):

B1(x)=length(I(x)).B_1(x)=\operatorname{length}(I(x)).

2. 求交集长度(分段分析)

交集区间上下界是

下界=max ⁣(12,  x12),上界=min ⁣(12,  x+12).\text{下界}= \max\!\big(-\tfrac12,\; x-\tfrac12\big),\qquad \text{上界}= \min\!\big(\tfrac12,\; x+\tfrac12\big).

所以长度为

B1(x)=min ⁣(12,  x+12)max ⁣(12,  x12),B_1(x)=\min\!\big(\tfrac12,\; x+\tfrac12\big)-\max\!\big(-\tfrac12,\; x-\tfrac12\big),

当上界 ≤ 下界 时长度为 0(表示无重叠)。

下面按 xx 的范围分情况计算。

情况 A: x1x\le -1

此时区间 [x12,x+12][x-\tfrac12,x+\tfrac12][1.5,0.5][-1.5,-0.5] 之类,完全落在 [,12][-∞,-\tfrac12] 左侧,与 [1/2,1/2][-1/2,1/2] 无交集 → B1(x)=0B_1(x)=0.

情况 B: 1<x<0-1 < x < 0

因此

B1(x)=(x+12)(12)=x+1.B_1(x)=(x+\tfrac12)-(-\tfrac12)=x+1.

注意 x+1=1xx+1=1-|x| 在此区间(因为 x=x|x|=-x)。

情况 C: 0x<10 \le x < 1

因此

B1(x)=12(x12)=1x,B_1(x)=\tfrac12-(x-\tfrac12)=1-x,

也就是 1x1-|x| (在此区间 x=x|x|=x)。

情况 D: x1x\ge 1

无重叠,B1(x)=0B_1(x)=0.

3. 合并分段结果

把上面的各段合并:

B1(x)={0,x1,1x,x<1.B_1(x)=\begin{cases} 0, & |x|\ge1,\\[4pt] 1-|x|, & |x|<1. \end{cases}

这就是著名的 三角形核(tent function)

4. 性质检验(可选)

  1. 支持:supp(B1)=[1,1]\mathrm{supp}(B_1)=[-1,1](宽度为 2)。
  2. 最大值:B1(0)=1B_1(0)=1
  3. 积分(面积):

B1(x)dx=11(1x)dx=201(1x)dx=2[xx22]01=212=1,\int_{-\infty}^{\infty} B_1(x)\,dx = \int_{-1}^{1} (1-|x|)\,dx = 2\int_{0}^{1} (1-x)\,dx =2\left[x-\tfrac{x^2}{2}\right]_0^1=2\cdot\tfrac12=1,

与单个 box 的面积一致(卷积保持总能量/面积)。

  1. 导数(分段):

B1(x)={1,1<x<0,1,0<x<1,0,elsewhere (in distribution sense at ±1,0有不连续点).B_1'(x)=\begin{cases} 1, & -1<x<0,\\ -1, & 0<x<1,\\ 0, & \text{elsewhere (in distribution sense at }\pm1,0\text{有不连续点)}. \end{cases}

说明 B1B_1 连续但在 x=0x=0 可导性改变(一次可导不连续)。

5. 直观解释(补充)


二 2 阶和 3 阶 B-spline 核函数的形状

1. 设定与回顾

定义 0 阶(box)函数

b(x)=1[12,12](x)={1,x<12,0,otherwise.b(x)=\mathbf{1}_{[-\tfrac12,\tfrac12]}(x)= \begin{cases}1,&|x|<\tfrac12,\\0,&\text{otherwise.}\end{cases}

已知

B1(x)=(bb)(x)={1x,x<1,0,x1,B_1(x)=(b*b)(x)=\begin{cases}1-|x|,&|x|<1,\\0,&|x|\ge1,\end{cases}

即“三角形核”。

接下来

B2(x)=B1b=(bb)b,B3(x)=B2b.B_2(x)=B_1*b=(b*b)*b, \qquad B_3(x)=B_2*b.

卷积定义(1D):

(fg)(x)=f(t)g(xt)dt.(f*g)(x)=\int_{-\infty}^{\infty} f(t)\,g(x-t)\,dt.


2. 详细推导:二阶 B2(x)=B1bB_2(x)=B_1*b

写成积分:

B2(x)=B1(t)b(xt)dt.B_2(x)=\int_{-\infty}^\infty B_1(t)\, b(x-t)\,dt.

由于 B1(t)B_1(t)t<1|t|<1 有值,b(xt)b(x-t)xt<12|x-t|<\tfrac12 有值,因此被积项非零当且仅当

t[1,1][x12,x+12].t\in[-1,1]\cap[x-\tfrac12,\,x+\tfrac12].

所以积分的上下限是

下界=L(x)=max(1,x12),上界=U(x)=min(1,x+12),\text{下界}=L(x)=\max(-1,\,x-\tfrac12),\qquad \text{上界}=U(x)=\min(1,\,x+\tfrac12),

且在这些 tt 上被积函数为 B1(t)=1tB_1(t)=1-|t|.

因此

B2(x)=L(x)U(x)(1t)dt.B_2(x)=\int_{L(x)}^{U(x)} (1-|t|)\,dt.

观察交集何时为空:当 [x12,x+12][x-\tfrac12,x+\tfrac12][1,1][-1,1] 无交集时(即 x1.5|x|\ge 1.5),积分为 0。所以 B2B_2 的支撑是 [3/2,3/2][-3/2,\,3/2]

关键断点来自使上下界转变的点:x=32,  12,  12,  32x=-\tfrac32,\; -\tfrac12,\; \tfrac12,\; \tfrac32。因此分四段讨论(对称性可只做 x0x\ge0 再镜像,但这里写出三段中心表达):

情形 A: 32x<12-\tfrac32 \le x < -\tfrac12

在此区间,x+12<0x+\tfrac12<0x12<1x-\tfrac12<-1,因此

L(x)=1,U(x)=x+12,并且区间内 t0,L(x)=-1,\quad U(x)=x+\tfrac12,\quad\text{并且区间内 }t\le0,

所以 t=t|t|=-t,被积项 1t=1+t1-|t|=1+t

计算:

B2(x)=1x+12(1+t)dt=[t+t22]1x+12.B_2(x)=\int_{-1}^{\,x+\tfrac12}(1+t)\,dt =\Big[t+\tfrac{t^2}{2}\Big]_{-1}^{x+\tfrac12}.

u=x+12u=x+\tfrac12,则

B2(x)=(u+u22)(1+12)=u+u22+12.B_2(x)=\Big(u+\tfrac{u^2}{2}\Big)-\Big(-1+\tfrac{1}{2}\Big) = u+\tfrac{u^2}{2}+\tfrac12.

化简(把 u=x+12u=x+\tfrac12 代回):

B2(x)=(x+32)22,32x<12.B_2(x)=\frac{(x+\tfrac32)^2}{2}, \qquad -\tfrac32\le x<-\tfrac12.

情形 B: 12x12-\tfrac12 \le x \le \tfrac12

此时交集为 [x12,x+12][x-\tfrac12,\,x+\tfrac12],并且区间跨过 t=0t=0。于是分两段积分(从 x12x-\tfrac12 到 0,再到 x+12x+\tfrac12):

B2(x)=x120(1+t)dt+0x+12(1t)dt.B_2(x)=\int_{x-\tfrac12}^{0} (1+t)\,dt + \int_{0}^{x+\tfrac12} (1-t)\,dt.

计算:

x120(1+t)dt=(x12)(x12)22,\int_{x-\tfrac12}^{0}(1+t)\,dt = -\big(x-\tfrac12\big) - \tfrac{(x-\tfrac12)^2}{2}, 0x+12(1t)dt=(x+12)(x+12)22.\int_{0}^{x+\tfrac12}(1-t)\,dt = \big(x+\tfrac12\big) - \tfrac{(x+\tfrac12)^2}{2}.

相加并化简(省略代数中间行)结果为

B2(x)=34x2,x12.B_2(x)=\tfrac34 - x^2,\qquad |x|\le \tfrac12.

(可以验算:在 x=0x=0B2(0)=3/4B_2(0)=3/4,在 x=±1/2x=\pm 1/2 时为 3/41/4=1/23/4 - 1/4 = 1/2,与相邻段连续。)

情形 C: 12<x32\tfrac12 < x \le \tfrac32

对称于 A 段,可写出:

B2(x)=(32x)22,12<x32.B_2(x)=\frac{(\tfrac32-x)^2}{2},\qquad \tfrac12<x\le\tfrac32.

合并写成分段函数(即最终结果)

B2(x)={(x+32)22,32x<12,34x2,x12,(32x)22,12<x32,0,x>32.\boxed{\,B_2(x)=\begin{cases} \frac{(x+\tfrac32)^2}{2}, & -\tfrac32\le x< -\tfrac12,\\[6pt] \tfrac34 - x^2, & |x|\le \tfrac12,\\[6pt] \frac{(\tfrac32-x)^2}{2}, & \tfrac12< x\le \tfrac32,\\[4pt] 0,& |x|>\tfrac32. \end{cases}\,}

3. 三阶 B3B_3(结果与说明)

三阶可以继续用卷积 B3=B2bB_3 = B_2 * b。它的支撑是 [2,2][-2,2]。分段点出现在 x=0,1,2|x|=0,1,2。最终的、常见且标准化的闭式分段多项式为:

B3(x)={16(2x)3,1<x<2,16(46x2+3x3),x1,0,x2.\boxed{\,B_3(x)=\begin{cases} \displaystyle\frac{1}{6}\,(2-|x|)^3, & 1<|x|<2,\\[8pt] \displaystyle\frac{1}{6}\big(4-6x^2+3|x|^3\big), & |x|\le 1,\\[6pt] 0, & |x|\ge 2. \end{cases}\,}

(注意公式中出现 x3|x|^3 保证了关于原点的偶对称性。)

这可以等价写成两段(利用偶性):

性质检验 / 由卷积得到的理由(概述推导路线):


4. 小结(直观与对应表)

每升一阶,等价于对 box filter 做一次卷积——函数更宽、平滑度提高,且每段都是对应次数的多项式。


形状绘制

下面通过绘制代码来具体观察一下形状。

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import convolve

def box_filter_1d(resolution=0.01):
    x = np.arange(-0.5, 0.5 + resolution, resolution)
    y = np.ones_like(x)
    return x, y / np.sum(y)

def bspline_1d(order, resolution=0.01):
    x, b = box_filter_1d(resolution=resolution)
    y = b.copy()
    for _ in range(order):
        y = convolve(y, b, mode='full') * resolution
    x_full = np.linspace(-(order + 1) / 2, (order + 1) / 2, len(y))
    return x_full, y / np.max(y)

# 绘制从 box filter 到 cubic B-spline 的演化
plt.figure(figsize=(8, 5))
colors = ['red', 'orange', 'green', 'blue']
labels = ['B0 (box filter)', 'B1 (triangular)', 'B2 (quadratic)', 'B3 (cubic)']

for n, color, label in zip(range(4), colors, labels):
    x, y = bspline_1d(n)
    plt.plot(x, y, color=color, label=label, linewidth=2)

plt.title("B-spline Evolution from Box Filter (B0 → B3)")
plt.xlabel("x")
plt.ylabel("Normalized amplitude")
plt.legend()
plt.grid(True)
plt.show()

alt text

阶数 形状 连续性
0 方形 不连续
1 三角形 C⁰ 连续
2 光滑凸曲线 C¹ 连续
3 更平滑的凸曲线 C² 连续

这张图展示了从 box filter(B₀)三次 B-spline(B₃) 的平滑演化过程:

你可以看到——每卷积一次,曲线就更光滑、支撑范围更宽。
这就是 B-spline 的核心思想:通过 box filter 的多次卷积实现平滑基函数。


📘 附录 B-spline 的中文术语常见翻译

英文术语 中文翻译 说明
B-spline B 样条 最常见、标准译法
Basis spline 基样条 强调是“基函数”
Cubic B-spline 三次 B 样条 在 Poisson 重建中常用
Uniform B-spline 均匀 B 样条 节点均匀分布
Non-uniform B-spline (NURBS) 非均匀有理 B 样条 用于 CAD、曲面建模