最近一个“最强变脸术”又火爆抖音啦,还不知道的朋友建议先打开抖音,搜索“最强变脸术”看个十来个视频再回来看这篇文章。视频看起来炫酷,其实本质就是图像的各种变换组合到一块的结果。那我们能不能也搞出一个来玩玩?我利用周末刷了两天抖音,不停的暂停、继续… 最终在尝试了仿射变换
和透视变换
两种方案后,搞出了一个“低配版最强变脸术”。首先先来看看最终实现的效果(忽略gif颜色问题),也可以到http://www.iqiyi.com/w_19saz1z92h.html查看完整视频,然后从数学原理、opencv代码实现入手一步步的搞一个“最强变脸术”。
人脸关键点识别
看过“最强变脸术”的都知道,这个效果最基础的技术就是人脸识别。都2020年了,人脸识别当然不是多难的事了,可以选择的技术也很多,比如可以利用深度学习自己训练一个,也可以和我一样使用dlib
这个三方库。
dlib
用起来很简单,下面直接上代码了。
img = cv2.imread("./imgs/2.jpg")
dets = detector(img, 1)
shape = predictor(img, dets[0])
landmarks = []
for p in shape.parts():
landmarks.append(np.array([p.x, p.y]))
for idx, point in enumerate(landmarks):
cv2.putText(img, str(idx), (point[0], point[1]), fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
fontScale=0.3, color=(0, 255, 0))
cv2.imshow("--", img)
cv2.waitKey()
运行上面的代码可以看到这样的结果:
请注意上面图中36
、45
、29
三个数字的位置,因为在下面仿射变换
的版本中我们要用到。
版本一:仿射变换实现
人脸关键点搞定后的第一次尝试,我是用的图像仿射变换
来实现的。通过不断观察,我拆解出了一下三种变换方式:
- 平移
- 缩放
- 旋转
平移
需要平移,是因为我们需要把两张图片上的人脸叠放到一块。平移的变换操作矩阵是:
[ 1 0 t x 0 1 t y ] \left[ \begin{matrix} 1 & 0 & tx \\ 0 & 1 & ty \end{matrix} \right] [1001txty]
例如我们要向右平移100个像素,向下平移50个像素,那么变换矩阵就应该是:
[ 1 0 100 0 1 50 ] \left[ \begin{matrix} 1 & 0 & 100 \\ 0 & 1 & 50 \end{matrix} \right] [100110050]
对应的运算是:
[ x ′ y ′ ] = [ 1 0 100 0 1 50 ] ∗ [ x y 1 ] \left[ \begin{matrix} x' \\ y' \\ \end{matrix} \right]=\left[ \begin{matrix} 1 & 0 & 100 \\ 0 & 1 & 50 \end{matrix} \right]*\left[ \begin{matrix} x \\ y \\ 1 \end{matrix} \right] [x′y′]=[100110050]∗⎣⎡xy1⎦⎤
即
{ x ′ = 1 ∗ x + 0 ∗ y + 100 ∗ 1 y ′ = 0 ∗ x + 1 ∗ y + 50 ∗ 1 \begin{cases} x'=1*x+0*y+100*1 \\ y'=0*x+1*y+50*1 \end{cases} {
x′=1∗x+0∗y+100∗1y′=0∗x+1∗y+50∗1
所以平移操作的本质就是对每个像素加上一个偏移量。下面是使用opencv对图像进行平移操作的代码:
img = cv2.imread("./imgs/2.jpg")
M = np.float32(
[
[1, 0, 100],
[0, 1, 50]
]
)
dst = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))
cv2.imshow("", dst)
cv2.waitKey()
运行上面的代码可以看到这样的结果:
缩放
需要缩放,是因为我们在人脸对齐的时候需要尽可能的保证两张人脸大小一致。缩放的变换操作矩阵是:
[ f x 0 0 0