其他分享
首页 > 其他分享> > 《Poisson Image Editing》论文理解与复现

《Poisson Image Editing》论文理解与复现

作者:互联网

1. 导读

本报告的主要内容是阅读《Poisson Image Editing》论文之后对原理进行理解并利用python复现论文中的每个功能。

2. 引言

图像融合是图像处理的一个基本问题,目的是将源图像中一个物体或者一个区域嵌入到目标图像生成一个新的图像。在对图像进行合成的过程中,为了使合成后的图像更自然,合成边界应当保持无缝。但如果源图像和目标图像有着明显不同的纹理特征,则直接合成后的图像会存在明显的边界。

针对这个问题,论文提出了一种利用构造泊松方程求解像素最优值的方法,在保留了源图像梯度信息的同时,融合源图像与目标图像。泊松图像编辑(Poisson Image Editing)的核心就是根据用户指定的边界条件求解一个泊松方程,实现了梯度域上的连续,从而达到边界处的无缝融合

总的来说,将泊松方程引入图像融合后,接下来的操作很容易在梯度域中进行应用,并且可以通过局部的图像编辑得到全局融合的效果。

3. 技术贡献

利用基于求解泊松方程的通用插值机制,引入了各种新的图像区域无缝编辑工具。允许将不透明和透明的源图像区域无缝地输入到目标区域。并可以基于类似的数学思想,允许用户在一个选定的区域内无缝地修改图像的外观,具体为区域内对象的纹理、照明、颜色、边界无缝处理。

4. 方法介绍——导向插值求解泊松方程

总结来说,图像融合下面两点一定要满足:

(1)边缘过渡要平滑,就是说梯度要小;

(2)感兴趣区域内部的自身纹理要最大程度的保留。

4.1 导向插值原理

图片名称

图中,\(g\)是源图像中要合成的部分,\(V\)是引导梯度场,\(S\)是合并后的图像,\(\Omega\)是合并后被覆盖的区域,\(\partial \Omega\)是边界,\(\Omega\)内的像素用\(f\)表示(\(f\)未知),\(\Omega\)外的像素用\(f^*\)表示(已知)。

  1. 合并后的图像看上去尽可能的平滑,没有明显的边界,所以\(\Omega\)内的梯度值要尽可能的小。因此,\(f\)的值由下面的公式来确定:

    \[\underset {f}{min} \underset{\Omega}{\iint} || \nabla f||^2,f|_{\partial \Omega}=f^*|_{\partial \Omega} \]

    这个的解是以下具有狄利克雷边界条件的拉普拉斯方程的唯一解:\(\Delta f=0\) over \(\Omega\) with $ f|{\partial \Omega}=f^*|{\partial \Omega}$。

  2. 虽然最后的结果很平滑,但是太模糊了,缺失了纹理。因此引入了引导场的进一步约束来修正问题。

    \[\underset {f}{min} \underset{\Omega}{\iint} || \nabla f-\nabla V||^2,f|_{\partial \Omega}=f^*|_{\partial \Omega} \]

    • 在这里,\(V\)可以是保守场也可以是非保守场。更好的理解就是\(V\)作保守场的时候,看成是源图像,即\(V=g\)。即让\(\Omega\)内的梯度尽可能与源图像相似。后面有优化出非保守场,在无缝融合那一块。

    • 这样做的目的是,为了让融合结果中的\(\Omega\)内无限趋近源图像\(g\),这里需要有指引的梯度场\(V\),当融合后的图像内的\(f\)与\(g\)的梯度信息越相似,而边界处等于\(f^*\)的值,就说明原始纹理保持的越好,融合的效果越好。

    • 其解是以下具有狄利克雷边界条件的泊松方程的唯一解:\(\Delta f= div(V)\) over \(\Omega\) with $ f|{\partial \Omega}=f^*|{\partial \Omega}$。

4.2 应用于图像中——离散化

由于图像可以看做是二元函数,其中 x,y 的值是离散的,最小变化量为 1。因此可以用差分代替微分的计算\(\Delta f\)。论文中的数学表达比较抽象,我通过查阅资料之后推导出更直观的表达。

如果\(f\)是关于x的函数,那么\(f\)在某个点的导数就是:

\[f^\prime(x_0)=lim_{\Delta x \rightarrow 1}\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x} \]

将\(\Delta x\)代入1,并用于一阶偏导函数,可得:

\[\frac{\partial f}{\partial x} = f(x+1,y)-f(x,y) \\ \frac{\partial f}{\partial y} = f(x,y+1)-f(x,y) \]

在一阶偏导函数的基础上求二阶偏导函数。在求一阶偏导函数的时候是通过\(x+1\)的形式来近似求\(x\)点的偏导的,直观上来讲取值有点偏右了。那么在求二阶导函数的时候,让\(x = x - 1\)来近似\(x\)点,更为恰当。所以,二阶偏导函数为:

\[\frac{\partial^2 f}{\partial x^2}=f(x+1,y)-f(x,y)-(f((x-1)+1,y)-f(x-1,y))\\ = f(x+1,y)+f(x−1,y)−2f(x,y)\\ \frac{\partial^2 f}{\partial y^2}=f(x,y+1)−f(x,y)−(f(x,(y−1)+1)−f(x,y−1))\\=f(x,y+1)+f(x,y−1)−2f(x,y) \]

由拉普拉斯算子的定义,两式求和可得:

\[\Delta f= f(x+1,y)+f(x−1,y)+f(x,y+1)+f(x,y−1)−4f(x,y) \]

上面的式子,用在图像里可以看做上,下,左,右的点求和,减去四倍的中心点。

因此,许多资料都说可以用如下拉普拉斯卷积核对图像做卷积:

\[\left[ \begin{matrix} 0 & 1 & 0\\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{matrix} \right] \]

5. 应用

5.1 无缝融合(SeamlessClone)

无缝融合是泊松方程求解合成图像像素值的最直观应用。即实现前景图像和背景图像的融合。无缝融合主要分为以下三种:

\[ \begin{cases} \mathbf v(\mathbf x) = \nabla g(\mathbf x)\\ \Delta f = \Delta g \quad over \quad \Omega, with \quad f|_{\partial \Omega}=f^*|_{\partial \Omega} \end{cases} \]

\[ for \,\,\, all \,\,\, \mathbf x \in \Omega, \mathbf v(\mathbf x) = \begin{cases} \nabla f^*(\mathbf x) \quad if \,\,|\nabla f^*(\mathbf x)| > |\nabla g(\mathbf x)|\\ \nabla g(\mathbf x) \quad \,\,\, otherwise \end{cases} \]

通过适当地将源图像的梯度与目标图像的梯度混合,也可以令人信服地添加透明对象。此外,具有复杂轮廓的物体,包括孔,可以自动添加,而不需要艰苦的切割。

图片名称

5.2 选区编辑(Selection editing)

无缝融合处理的问题是将源图像中的区域融合到目标图像中,引导场部分或完全取决于源图像\(g\)的梯度。而选区编辑处理的问题是在单一图像上取出一块区域,对这个区域进行处理,使用完全依赖于原始图像的引导场来定义选区图像的变换。主要形式包括:纹理扁平化、局部明亮变化、局部色彩变化以及无缝拼接。

纹理扁平化和局部明亮变化是通过对原始梯度场\(\nabla f^*\)进行非线性修改实现;背景或前景色修改和无缝拼接是通过就地(in-place)无缝融合实现,原始图片经域内(提供新的源图像)或域外(提供新的边界值)修改后作为源。

图片名称

(1)修改背景色:给定$ \Omega\(域外目标函数\)f*$,例下图中间:通过将选区外图像进行去色处理,得到的图像作为$f*$

(2)修改前景色:给定$ \Omega\(域内源函数\)g\(,例下图右侧:将选区内图像**各通道灰度值**分别乘以1.5、0.5、0.5,得到的图像作为\)g$(后面三个参数是r、g、b三个通道的乘数因子,在0.5-2.5之间。值越大起到锐化的作用)

图片名称
图片名称

6. 代码复现——基于python的opencv实现

6.1 常规融合

1. 思路

2. 代码实现

我学习了opencv中seamlessClone函数的源码,但是源码是基于C++的openGL实现的,我借鉴了一些实现方法,并结合我的思路用python的opencv实现seamlessClone函数

3. 实现结果

输入图像:

原图
图片名称
目标图
图片名称

mask图像

图片名称

输出结果:

图片名称

cv.seamlessClone函数结果:

图片名称

6.2 混合融合

1. 思路

  1. 在这里不能直接用laplacian算子求出散度然后再进行比较,因为公式里面是要求比较的是梯度,因此需要在梯度上进行融合
  2. 梯度有两个方向:x方向和y方向,每个方向有左、右两个方向的梯度。因此需要对四个方向的梯度进行比较

2. 代码实现

3. 实现结果

输入图像:

原图
图片名称
目标图
图片名称

mask图像

图片名称

输出结果:

图片名称

cv.seamlessClone函数实现结果:

图片名称

6.3 单色迁移

1. 思路

2. 代码实现

3. 实现结果

输入图:
输入图像:

原图
图片名称
目标图
图片名称

mask图像

图片名称

输出结果:

图片名称

cv.seamlessClone函数实现结果:

图片名称

6.4 纹理扁平化

1. 思路

def textureFlattening(src, mask, low, high): # low、high分别是Canny边缘检测器低阈值和高阈值
    # 1. 利用canny算子检测边缘。边缘点值为255,非边缘点为0
    canny = cv.Canny(src, low, high)
    # 2. 计算src的散度
	laplacian = cv.Laplacian(np.float64(src),ddepth=-1,ksize=1)
    # 3. 当像素不是边缘点时,修改其散度值为0
    for i in range(canny.shape[0]):
        for j in range(canny.shape[1]):
            if canny[i][j] == 0:
                laplacian[i][j] = 0
    # 4. 逐通道求解,目标图像也是src
    ret = [getX(d, mask, l) for d, l in zip(cv.split(src), cv.split(laplacian))]
    retImg = cv.merge(ret)
    return retImg

如上图为实现的结果。可以看出这个方法不能实现纹理扁平化,通过观察结果矩阵发现像素的变化很大,我猜测是直接利用边缘点则直接将散度赋为0导致的。边缘点的影响可能逐步影响到梯度,从而影响散度;某个方向梯度为0,不代表散度就为0。

2. 代码实现

3. 实现结果

输入图像:
输入图像:

原图
图片名称

mask图像

图片名称

输出结果:

图片名称

cv.textureFlattening函数实现结果:

图片名称

6.5 局部明亮变化

1. 思路

2. 代码实现

3. 实现结果

输入图像:

原图
图片名称

mask图像

图片名称

输出结果:

图片名称

cv.illuminationChange函数实现结果:

图片名称

6.6 局部色彩变化

1. 思路

2. 代码实现

图片名称

3. 实现结果

输入图像:

原图
图片名称

mask图像

图片名称

输出结果:

图片名称

cv.colorChange函数实现结果:

图片名称

6.7 无缝拼接

1. 思路

2. 代码实现

3. 实现结果

输入图像:

原图
图片名称

输出结果:

图片名称

直接拼接结果:

图片名称

7. 思考的问题

  1. 为什么要用梯度?

    图像梯度的最重要性质是梯度可以用来反映图像中亮度改变最明显的区域,也就是说可以用梯度来捕捉图像上的亮度变化,梯度的方向在图像灰度的最大变化率上,它恰好可以反映出图像边缘的灰度变化。

  2. 拉普拉斯算子\(\Delta f\)对图片的影响有什么?

  1. \(f\)和\(f^*\)是什么函数?

    是关于x和y的函数

  2. 为什么最小值问题的解是具有狄利克雷边界条件的拉普拉斯方程的唯一解?(以\(\underset {f}{min} \underset{\Omega}{\iint} || \nabla f||^2,f|_{\partial \Omega}=f^*|_{\partial \Omega}\)为例,其解是\(\Delta f=0\) over \(\Omega\) with $ f|{\partial \Omega}=f^*|{\partial \Omega}$的唯一解)

\[\frac{\partial F}{\partial f}-\frac{d}{dx}(\frac{\partial F}{\partial f_x})-\frac{d}{dy}(\frac{\partial F}{\partial f_y}) = 0 \]

​ 化简得:

\[\frac{d}{dx}(\frac{\partial F}{\partial f_x})=\frac{d}{dx}(2f_x)=2\frac{\partial^2 f}{\partial x^2} \]

​ 因此,

\[\frac{\partial^2 f}{\partial x^2}+\frac{\partial^2 f}{\partial y^2}=\Delta f=0 \]

  1. 泊松方程与拉普拉斯方程

    有点像热方程,就是钳住了边界,并且知道边界发生了什么,然后想让它扩散到中心的过程,并且使这些梯度的约束看起来尽可能好

  1. 已知图像点二阶微分值(散度div(V)),求解各个图像点的像素值——泊松方程\(\Delta f = div(V)\)求解过程

参考文献

  1. cv2.Laplacian与cv2.filter2d -不同的结果 https://cloud.tencent.com/developer/ask/sof/904647

  2. OpenCV-Python 图像平滑处理1:卷积函数filter2D详解及用于均值滤波的案例 https://blog.csdn.net/LaoYuanPython/article/details/123333112

  3. 文献阅读 - Poisson Image Editing https://blog.csdn.net/zhaoyin214/article/details/88196575

  4. 图像处理(十二)图像融合(1)Seamless cloning泊松克隆-Siggraph 2004 https://blog.csdn.net/hjimce/article/details/45716603

  5. 拉普拉斯算子 https://xudeyu.github.io/2019/03/31/laplace-operator.html

  6. opencv的API文档

https://docs.opencv.org/4.x/dc/d23/samples_2cpp_2tutorial_code_2photo_2seamless_cloning_2cloning_demo_8cpp-example.html#a14

https://docs.opencv.org/4.x/dc/d81/photo_8hpp.html

https://docs.opencv.org/4.x/df/da0/group__photo__clone.html#gad55df6aa53797365fa7cc23959a54004

  1. opencv seamlessClone源码 https://github.com/opencv

标签:src,Image,mask,像素,Poisson,图像,np,Editing,cv
来源: https://www.cnblogs.com/3-louise-wang/p/16671316.html