赞
踩
在做数字人时,会遇到一个问题,如何把头部说话的视频和原始图片/视频合在一起,这里就涉及到很有意思的图像无缝融合的技术, 我们一起来学习下吧
目录
1. 融合的效果
2. 拉普拉斯金字塔
3. 泊松融合
4. 参考资料
一、融合的效果
拉普拉斯金字塔进行图像融合 基本看不出来是有两类水果图片融合的
使用泊松融合,对生成的头部视频和原视频进行融合
对原图第一帧进行人脸检测和裁剪(具体实现可参考上一篇文章),然后以这个点对人脸进行裁剪,然后根据音频驱动嘴型说话,以及原来的视频眨眼和头部动作作为参考,生成新的头部视频, 最终把生成的头部视频逐帧和源视频进行融合,生成最终带身体和手部动作的视频.
二、拉普拉斯金字塔(Laplacian Pyramid)
2.1 介绍
对图片先逐级进行下采样,然后再逐级进行上采样,通过从高斯金字塔的连续两层之间计算差值来构建的,用于捕获图像在不同尺度下的细节信息
图片来自: 拉普拉斯金字https://zhuanlan.zhihu.com/p/454085730
拉普拉斯的用处可以分为两大类, 对图像进行压缩 和对图像进行融合
通过先下采样再上采样,然后对上采样的,得到拉普拉斯金字塔,然后再和逐级上采样的图片进行相加即可恢复为原图, 即是无损的. 如果我们对图片中变化比较小的低频分量进行剔除,只保留更多细节的高频分量(即边缘轮廓),损失一部分不明显的信息,再进行恢复 可以实现图片压缩的效果.
下面我们看下如何通过拉普拉斯金字塔实现图像融合
2.2 图像融合原理以及代码实现
对两张图片进行融合需要一个Mask图,指定从图片中哪些区域需要进行融合,比如文章开头的两种水果的融合,取左边一半进行融合(用全1 白色表示),右边一半进行保留(用全0 黑色表示)
完整代码实现如下
import numpy as np
import cv2
# 修复尺寸不匹配的问题
def fix_size(img, target):
if img.shape[0] != target.shape[0] or img.shape[1] != target.shape[1]:
img = cv2.resize(img, (target.shape[1], target.shape[0]), interpolation=cv2.INTER_LINEAR)
return img
# 拉普拉斯金字塔图像融合实现
def Laplacian_Pyramid_Blending_with_mask(A, B, m, num_levels=6):
gpA = [A.copy()]
gpB = [B.copy()]
gpM = [m.copy()]
#先机型下采样,获取高斯金字塔
for i in range(num_levels):
gpA.append(cv2.pyrDown(gpA[i]))
gpB.append(cv2.pyrDown(gpB[i]))
gpM.append(cv2.pyrDown(gpM[i]))
#再逐层根据上一层的高斯图以及当前层上采样结果想减获取拉普拉斯金字塔
lpA = [gpA[num_levels]]
lpB = [gpB[num_levels]]
for i in range(num_levels, 0, -1):
LA = cv2.subtract(gpA[i-1], fix_size(cv2.pyrUp(gpA[i]), gpA[i-1]))
LB = cv2.subtract(gpB[i-1], fix_size(cv2.pyrUp(gpB[i]), gpB[i-1]))
lpA.append(LA)
lpB.append(LB)
#根据mask进行融合
LS = []
for la, lb, gm in zip(lpA, lpB, gpM):
gm = fix_size(gm, la)
#通过np.newaxis添加一个维度, 把二维的mask图转为和AB图一样三通道的图片
print(f"gm.ndim:{gm.ndim},gm:{gm.shape},la:{la.shape},lb:{lb.shape}")
gm = gm[..., np.newaxis] if gm.ndim == 2 else gm
la = fix_size(la, gm)
lb = fix_size(lb, gm)
ls = la * gm + lb * (1.0 - gm)
LS.append(ls)
#根据拉普拉斯金字塔重建图片
ls_ = LS[0]
for i in range(1, num_levels + 1):
ls_ = fix_size(cv2.pyrUp(ls_), LS[i])
ls_ = cv2.add(ls_, LS[i])
ls_ = fix_size(ls_, A)
ls_ = np.clip(ls_, 0, 255).astype('uint8')
return ls_
根据不同的层数融合效果如下,层级过低边界还是很清晰没有很好的融合,随着层数加大效果效果会好很多。
需要注意的是:基于 laplace 金字塔的图像拼接限制挺大的,对两张图片匹配性有很高的要求(尺寸大小以及融合位置)
三、泊松融合(Poisson Blending)
3.1 介绍
泊松融合可以将一个图像区域无缝地融合到另一个图像中,在保持图像渐变的前提下调整图像的像素值,不仅能够保留源图像的细节和结构,又能自然地适应目标图像的颜色和亮度,使得融合区域与周围环境看起来更自热协调。比如下面的融合效果
图片来自:https://github.com/Erkaman/poisson_blend
3.2 原理和代码实现
泊松融合处理图像的梯度域而非直接在像素值上操作,具体来说,它通过解泊松方程来编辑图像的一个区域,让选择的图像区域无缝地融入到目标图像中,使得融合区域内的梯度与源图像相匹配,同时边界与目标图像平滑对接
泊松融合通常涉及以下步骤:
选择源图像(要复制的图像)和目标图像(要粘贴到的图像)。
在源图像上定义一个蒙版,标记出需要融合的区域。
计算源图像在蒙版区域内的梯度(颜色变化)。
解决泊松方程,使得融合区域内的梯度与源图像相匹配,同时边界与目标图像平滑对接。
opencv中封装了seamlessClone接口来实现泊松融合
cv.seamlessClone(src, dst, mask, p, flags[, blend]) ->blend
src:输入8位3通道图像(截取目标的大图)
dst:输入8位3通道图像(待粘贴融合的目标背景图)
mask:输入8位1或3通道图像(目标掩码区域图像)
p:对象被放置在目标图像dst中的位置
blend:输出图像,与dst具有相同大小和类型。
flags:克隆方法可以是NORMAL_CLONE,MIXED_CLONE或MONOCHROME_TRANSFER
NORMAL_CLONE: 不保留dst 图像的texture细节。目标区域的梯度只由源图像决定。
MIXED_CLONE: 保留dest图像的texture 细节。目标区域的梯度是由原图像和目的图像的组合计算出来(计算dominat gradient)。
MONOCHROME_TRANSFER: 不保留src图像的颜色细节,只有src图像的质地,颜色和目标图像一样,可以用来进行皮肤质地填充
使用如下
#resize人脸裁剪并且根据音频驱动生成的视频帧
oy1, oy2, ox1, ox2 = crop_info[1]
p = cv2.resize(crop_frame.astype(np.uint8), (ox2-ox1, oy2 - oy1))
#生成裁剪人脸大小的mask,赋值为白色(即融合区域全部使用crop_frame)
mask = 255*np.ones(p.shape, p.dtype)
#融合的中心点
location = ((ox1+ox2) // 2, (oy1+oy2) // 2)
#使用seamlessClone(通过柏松融合实现)的NORMAL_CLONE的类型进行crop_frame和原始图片进行融合
gen_img = cv2.seamlessClone(p, full_img, mask, location, cv2.NORMAL_CLONE)
四. 参考资料
1. 拉普拉斯金字塔 https://zhuanlan.zhihu.com/p/454085730
2. OpenCV图像无缝融合-seamlessClone介绍与使用 https://blog.csdn.net/stq054188/article/details/114271822
3.计算机视觉:泊松融合 https://drustz.com/posts/2015/10/17/poisson/
4.poisson_blend:https://github.com/Erkaman/poisson_blend
感谢你的阅读
接下来我们继续学习输出AIGC相关内容,关注公众号“音视频开发之旅”,回复:“数字人” 获取资料,一起学习成长。
欢迎交流
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。