核心目标:把一张图的一部分无缝地粘贴到另一张图上
比如,把一只猫的脸(源图像 g
)贴到一个人的脸上(目标图像 f*
),让它们看起来自然融合,没有生硬的边界。
🧩 第一部分:泊松图像编辑(基本版)
💡 基本思想:
- 我们不直接复制源图像的颜色 ,而是复制它的颜色变化趋势(梯度)。
- 也就是说,我们更关心图像中明暗变化的模式,而不是具体的颜色值。
📐 公式解释:
我们要求解一个方程组,确保在融合区域(Ω)内:
- 新图像的梯度(颜色变化)尽量接近源图像的梯度。
- 新图像的边界颜色和目标图像的边界颜色一致。
🔢 怎么做(离散情况):
对融合区域内的每一个像素 p
,我们建立一个方程:
|N_p| * f_p - Σ(f_q) = Σ(f*_q) + Σ(g_p - g_q)
其中:
f_p
是我们要求的新图像在像素p
的颜色N_p
是p
的上下左右四个邻居像素f*_q
是目标图像在边界像素q
的颜色g_p - g_q
是源图像在p
和q
之间的颜色变化
最终我们得到一个线性方程组 Ax = b
,解出 x
就得到了融合后的图像。
🌈 第二部分:混合梯度
💡 改进思想:
有时候我们希望在融合区域保留两个图像中最明显的边缘。
比如:
- 源图像有一个很锐利的物体边缘
- 目标图像有一个重要的纹理背景
📐 公式解释:
对于每个像素对 (p, q)
,我们比较:
- 源图像的梯度大小
|g_p - g_q|
- 目标图像的梯度大小
|f*_p - f*_q|
我们选择梯度值更大的那个作为引导。
🎨 第三部分:纹理扁平化
💡 核心思想:
只保留图像中最重要的边缘,去掉细微的纹理,创造出类似卡通画的效果。
📐 公式解释:
我们用一个边缘检测器(如Canny)生成二值掩码 M
:
- 如果
p
或q
是边缘点(M_p = 1
或M_q = 1
),就保留梯度 - 否则,梯度设为0
这样只有显著的边缘被保留,其他区域变得平坦。
💻 技术实现关键点:
- 需要建立一个稀疏线性系统
Ax = b
- 对融合区域内的像素进行编号索引
- 使用稀疏矩阵求解器(如
scipy.sparse.linalg.spsolve
)
各个公式
1. 基本泊松图像编辑公式
连续情况公式:
min₍ƒ₎ ∬Ω |∇f - ∇g|², 满足 f|∂Ω = f*|∂Ω
逐部分解释:
f
:我们要计算的融合后的图像函数g
:源图像(要粘贴的图像)函数f*
:目标图像(背景图像)函数Ω
:融合区域(我们要替换的区域)∂Ω
:融合区域的边界∇
:梯度算子,表示图像的变化率|∇f - ∇g|²
:衡量新图像梯度与源图像梯度的差异f|∂Ω = f*|∂Ω
:边界条件,确保融合区域边界的颜色与目标图像一致
直观理解: 在融合区域内,我们希望新图像的"明暗变化模式"与源图像一致,但边界颜色要与背景图像平滑连接。
离散情况公式:
min₍ƒ|Ω₎ ∑₍p∈Ω₎ ∑₍q∈Nₚ∩Ω₎ ((fₚ - fₑ) - (gₚ - gₑ))², 满足 f|∂Ω = f*|∂Ω
逐部分解释:
p ∈ Ω
:融合区域内的每一个像素q ∈ Nₚ ∩ Ω
:像素p在融合区域内的邻居像素(上下左右)fₚ - fₑ
:新图像在p和q之间的颜色变化gₚ - gₑ
:源图像在p和q之间的颜色变化((fₚ - fₑ) - (gₚ - gₑ))²
:衡量两个变化的差异
线性方程组形式:
|Nₚ|fₚ - ∑₍q∈Nₚ∩Ω₎ fₑ = ∑₍q∈Nₚ∩∂Ω₎ f*ₑ + ∑₍q∈Nₚ₎ (gₚ - gₑ), ∀p ∈ Ω
逐部分解释:
|Nₚ|
:像素p在融合区域内的邻居数量(2, 3, 或4个)|Nₚ|fₚ
:p点颜色值乘以邻居个数- ∑₍q∈Nₚ∩Ω₎ fₑ
:减去所有在融合区域内的邻居的颜色值∑₍q∈Nₚ∩∂Ω₎ f*ₑ
:加上所有在边界上的邻居在目标图像中的颜色值∑₍q∈Nₚ₎ (gₚ - gₑ)
:源图像在p点与所有邻居的梯度之和
为什么是这个形式? 这实际上是从拉普拉斯算子推导出来的离散形式,确保在每个像素点处颜色变化平滑。
2. 混合梯度公式
连续情况:
v(x) = { ∇f*(x), 如果 |∇f*(x)| ≥ |∇g(x)|
∇g(x), 如果 |∇f*(x)| < |∇g(x)| }
离散情况:
vₚₑ = { f*ₚ - f*ₑ, 如果 |f*ₚ - f*ₑ| ≥ |gₚ - gₑ|
gₚ - gₑ, 如果 |f*ₚ - f*ₑ| < |gₚ - gₑ| }
逐部分解释:
|f*ₚ - f*ₑ|
:目标图像在p和q之间的梯度大小(颜色变化强度)|gₚ - gₑ|
:源图像在p和q之间的梯度大小- 选择逻辑:哪个图像的梯度更大就选用哪个
应用场景: 当你想保留源图像的明显边缘,同时又不丢失目标图像中的重要纹理时。
3. 纹理扁平化公式
连续情况:
v(x) = M(x)∇f*(x)
离散情况:
vₚₑ = { f*ₚ - f*ₑ, 如果 Mₚ = 1 或 Mₑ = 1
0, 否则 }
逐部分解释:
M
:二值掩码,通常由边缘检测器生成Mₚ = 1
:像素p是边缘点Mₑ = 1
:像素q是边缘点- 逻辑:只有当p或q至少有一个是边缘点时,才保留梯度;否则梯度为0
效果: 只有显著的边缘被保留,其他区域的梯度为零,意味着这些区域颜色平坦,产生卡通化效果。
🔍 关键理解要点:
- 梯度 (∇) 在图像中表示"颜色变化的方向和强度"
- 拉普拉斯算子 是梯度的散度,描述每个点与其周围点的平均差异
- 边界条件 确保融合区域与背景自然衔接
- 稀疏线性系统 因为每个方程只涉及少量像素(最多5个:自己+4个邻居)
详细解释离散情况公式到底在求什么:
🎯 最终目标是什么?
我们要求的是:融合区域 Ω 内每一个像素点的新颜色值 fₚ
也就是说,我们要求一个颜色值的数组,这个数组能让融合区域既保持源图像的纹理变化,又能与背景图像无缝衔接。
🔍 逐层分解离散公式
原始最小化问题:
min₍ƒ|Ω₎ ∑₍p∈Ω₎ ∑₍q∈Nₚ∩Ω₎ ((fₚ - fₑ) - (gₚ - gₑ))²
理解这个最小化:
- 对于融合区域内的每个像素p ,以及p的每个邻居q
- 我们希望:
(新图像中p和q的差异)
尽可能接近(源图像中p和q的差异)
- 也就是希望:
(fₚ - fₑ) ≈ (gₚ - gₑ)
为什么这样能无缝融合?
因为我们在融合区域内复制了源图像的相对变化模式,而不是绝对颜色值。
🧩 转化为线性方程组
最小化问题最终导出了这个方程组:
|Nₚ|fₚ - ∑₍q∈Nₚ∩Ω₎ fₑ = ∑₍q∈Nₚ∩∂Ω₎ f*ₑ + ∑₍q∈Nₚ₎ (gₚ - gₑ)
让我用一个具体例子来解释:
假设场景:
背景图像 f*: [10, 20, 30] ← 边界像素
融合区域 Ω: [ ?, ?, ?] ← 要求解的像素
源图像 g: [50, 60, 70] ← 参考梯度
对于中间像素p,方程展开:
|Nₚ| = 2 (左右两个邻居)
左边邻居在边界:f*_left = 10
右边邻居在边界:f*_right = 30
源图像梯度:
gₚ - g_left = 60 - 50 = 10
gₚ - g_right = 60 - 70 = -10
梯度总和 = 10 + (-10) = 0
方程:
2fₚ - (f_left + f_right) = (10 + 30) + 0
2fₚ - (f_left + f_right) = 40
🔢 更直观的理解方式
把每个像素的方程重写为:
|Nₚ|fₚ = ∑₍q∈Nₚ∩Ω₎ fₑ + ∑₍q∈Nₚ∩∂Ω₎ f*ₑ + ∑₍q∈Nₚ₎ (gₚ - gₑ)
这实际上是在说:
每个像素的新颜色值,应该是它周围像素颜色的加权平均,再加上源图像提供的"修正项"。
具体来说:
- 左边部分
|Nₚ|fₚ - ∑₍q∈Nₚ∩Ω₎ fₑ
:确保颜色平滑变化 - 右边第一部分
∑₍q∈Nₚ∩∂Ω₎ f*ₑ
:确保与边界颜色衔接 - 右边第二部分
∑₍q∈Nₚ₎ (gₚ - gₑ)
:注入源图像的纹理信息
💻 实际要求解什么?
我们要求解的是一个线性方程组:
Ax = b
其中:
x
:包含所有未知fₚ
的向量(就是我们要的最终结果)A
:系数矩阵,由每个像素的邻居关系构成b
:由边界条件和源图像梯度构成的常数向量
举例说明:
假设融合区域有3个像素,方程组可能是:
方程1: 4f₁ - f₂ - f₃ = 100
方程2: -f₁ + 3f₂ - f₃ = 80
方程3: -f₁ - f₂ + 4f₃ = 120
解这个方程组就得到 [f₁, f₂, f₃]
的值。
❓ 简单总结:
"离散情况公式是要求什么?"
答: 要求融合区域内每一个像素点的最佳颜色值,使得:
- ✅ 区域内保持源图像的纹理变化模式
- ✅ 区域边界与背景图像自然过渡
- ✅ 整体看起来无缝融合