融合/形变技术
Image Morphing的原理是十分简单的。有两幅图像 I 和 J ,我们希望通过融合图像 I 和 J 来创建一幅新的图像 M. 图像 I 和 J 的融合过程是由参数 alpha 来控制,参数 alpha 介于0和1之间。当alpha = 0,新的图像 M看起来更接近 I;当alpha = 1,新的图像 M看起来更接近 J。可以通过以下的方程来进行融合:
然而,令alpha = 1 利用以上的方程融合的结果如下图所示,结果并不令人满意:
以上图片中中间的是最终融合结果,效果不容乐观。我们希望在进行融合之前将眼睛和嘴巴等相似部位进行一定程度的对齐。
所以,为了将图像 I 形变到图像 J,我们首先需要在两幅图片之间建立一些像素的对应关系。换句话说,在图像 I 中的每一个像素(xi,yi),我们希望在图像 J 中找到对应的像素点(xj,yj)。如果我们呢可以找到这种像素间的对应关系,那么我们就可以通过两个步骤来进行融合。第一:计算图像 M 中像素位置坐标(xm,ym),可通过以下方程进行:
第二我们需要找到像素点(xm,ym)处的像素灰度值:
通过以上两步,就可以实现我们的目标。然而找到两幅图片中一一对应的像素是非常困难的,我们可以通过找到少量的对应点来替代。
脸部变形步骤
1. 利用面部特征检测找到对应点
首先利用面部特征检测来自动找到许多对应点。在此利用dlib 来监测到68个对应点。下一步,额外添加四个点(一个在左耳,一个在脖子,两个在肩膀);最终再添加一些图像的角点和这些角点之间的点作为对应点。
2. 三角剖分
经过以上的步骤之后,我们一共得到两个集合A, B(每个图像对应一个集合),每个集合包含80个点的坐标信息。每个点对表示当前图像中对应点的坐标信息。A, B中的点都是一一对应的(比如希拉里左侧眼角左边的点在A中的位置索引和参*泰德左侧眼角左边的点在B中的位置索引是一样的)。下图分别是集合A和B中的点,每行表示一个点。
利用前述方程可以计算出一个新的集合C,新的集合表示融合以后的图像中这些点所在的坐标位置,新集合中每个元素表示一个点。在这个新的集合C运用Delaunay三角剖分,Delaunay三角剖分的结果是新的集合中所有点构成的三角形,三角剖分中每个元素暗示了构成一个三角形的三个点在C中的位置索引。以下是三角剖分的结果,每一行表示一个三角形。
因为集合C的点的坐标位置是根据集合A,B中坐标位置计算出来的,所以三者之间的点有对应关系,利用三角剖分的结果在集合A和b上显示如下图。可以注意到三角形之间区域近似相似。现在我们有了对应区域,就可以进行融合。
图片扭曲和alpha融合
图片融合的效果将由参数alpha来控制,并通过以下步骤实现:
1. 在融合的图片中找到特征点的位置:
在新的图像M中,根据方程1可以找到所有80个点的位置信息。
2. 计算仿射变换:
目前我们在图像 I中有80个点(集合A),图像 J中有80个点(集合B),融合的图像 M中有80个点。由80个点所组成的三角形信息也存储在三角剖分的结果中。我们在图像I中取出一个三角形,与图像M中对应的三角形之间计算仿射变换,OpenCV中函数getAffineTransform可以实现这个操作。每个图像共被分为总共149个,为 I和 M之间的每一对三角形计算仿射变换。最终在J和 M之间重复这一步骤。
3. 扭曲三角形
对于图像 I中的每一个三角形,利用上一步中得到的仿射变换矩阵,将三角形中的所有像素扭曲到目标图像中的对应三角形中,重复所有三角形得到一个图像I的扭曲版本。同样的操作得到一个图像J的扭曲版本。在OpenCV中可以利用 warpAffine函数来实现。然而,warpAffine是对一幅图像进行操作而不是三角形,一个可行的办法是为每个三角形求一个最小包围矩形,对包围矩形利用warpAffine进行扭曲,然后利用掩模操作将三角形以外的区域去掉。三角形淹没可以利用fillConvexPoly(函数FillConverExpoly根据多边形顶点绘制一个填充的凸多边形)创建。在利用函数warpAffine 的时候要使用荣火热模式 BORDER_REFLECT_101,该模式可以很好的隐藏缝隙。
4. 对扭曲的图像进行Aihpa融合
上一步骤得到了图像I和图像J的扭曲版本,利用方程2对他们继续融合,就能得到我们的结果M.