基于opencv+dlib的face morph


本实验使用pycharm
目标:实现两张人脸图像的融合
就不讲原理了,直接放实现流程

一、安装dlib和python-opencv库

利用anaconda在python环境下
使用命令:pip install dlib==19.8.1
pip install python-opencv
注意:安装dlib时容易出错,不行的话可以尝试新建环境

二、提取人脸特征点

需要先下载dlib官方训练好的模型(shape_predictor_68_face_landmarks.dat)。下载链接:http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
然后将其解压到目标文件夹(与提取特征点的代码在同一文件夹)
目标:将人脸特征点保存到txt文档

landmark.py

import dlib
import cv2

# 与人脸检测相同,使用dlib自带的frontal_face_detector作为人脸检测器
detector = dlib.get_frontal_face_detector()

# 使用官方提供的模型构建特征提取器
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
# cv2读取图片
img = cv2.imread("nan.jpg")

# 与人脸检测程序相同,使用detector进行人脸检测 dets为返回的结果
dets = detector(img, 1)
# 使用enumerate 函数遍历序列中的元素以及它们的下标
# 下标k即为人脸序号
# left:人脸左边距离图片左边界的距离 ;right:人脸右边距离图片左边界的距离
# top:人脸上边距离图片上边界的距离 ;bottom:人脸下边距离图片上边界的距离
for k, d in enumerate(dets):
    print("dets{}".format(d))
    print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format(
        k, d.left(), d.top(), d.right(), d.bottom()))

    # 使用predictor进行人脸关键点识别 shape为返回的结果
    shape = predictor(img, d)
    # 获取第一个和第二个点的坐标(相对于图片而不是框出来的人脸)
    print("Part 0: {}, Part 1: {} ...".format(shape.part(0), shape.part(1)))

    # 绘制特征点
    with open( 'results.txt', 'w' ) as f:
        for index, pt in enumerate(shape.parts()):
            #将特征点坐标写入txt文档
            f.write('Part {}: {}'.format(index, pt))
            #画圈
            pt_pos = (pt.x, pt.y)
            cv2.circle(img, pt_pos, 1, (255, 0, 0), 2)
            # 利用cv2.putText输出1-68
            font = cv2.FONT_HERSHEY_SIMPLEX
            cv2.putText(img, str(index + 1), pt_pos, font, 0.3, (0, 0, 255), 1, cv2.LINE_AA)


cv2.imshow('img', img)
k = cv2.waitKey()
cv2.destroyAllWindows()

结果:
在这里插入图片描述
并生成文档
在这里插入图片描述
此文档不符合下一步实验的格式,要改成如下格式
在这里插入图片描述
注意:本人所用图像素均改为600*800,此程序只能得到68个特征点坐标,对下一步来说,缺少几处特征点的的坐标,通过手动添加(也可通过程序得到,但我不会)

缺少点的位置如图中红色圆圈
在这里插入图片描述
之后在txt文档中添加图片边框的8个点的坐标(依旧手动)
在这里插入图片描述
最后将txt文档名字改为”图片名.jpg

三、face morph并保存图片

face_morph.py

import numpy as np
import cv2
import sys

# 从文本文件中读取点
def readPoints(file):
    # 创建数组存储点
    points = []
    with open(file) as file:
        for line in file:
            x, y = line.split()
            points.append((int(x), int(y)))
    return points


# 将使用srcTri和dstTri计算的仿射变换应用于src并返回结果图像。
def applyAffineTransform(src, srcTri, dstTri, size):
    # 给定一对三角形,找到仿射变换
    warpMat = cv2.getAffineTransform(np.float32(srcTri), np.float32(dstTri))

    # 将仿射变换应用于src图片
    dst = cv2.warpAffine(src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR,
                         borderMode=cv2.BORDER_REFLECT_101)

    return dst


# Warp和alpha将img1和img2的三角形区域混合到img中
def morphTriangle(img1, img2, img, t1, t2, t, alpha):
    # 找到每个三角形区域的包络矩形
    r1 = cv2.boundingRect(np.float32([t1]))
    r2 = cv2.boundingRect(np.float32([t2]))
    r = cv2.boundingRect(np.float32([t]))

    # 各个矩形左上角的偏移点
    t1Rect = []
    t2Rect = []
    tRect = []

    for i in range(0, 3):
        tRect.append(((t[i][0] - r[0]), (t[i][1] - r[1])))
        t1Rect.append(((t1[i][0] - r1[0]), (t1[i][1] - r1[1])))
        t2Rect.append(((t2[i][0] - r2[0]), (t2[i][1] - r2[1])))

    # 填充三角形来获得掩码
    mask = np.zeros((r[3], r[2], 3), dtype=np.float32)
    cv2.fillConvexPoly(mask, np.int32(tRect), (1.0, 1.0, 1, 0), 16, 0)

    # 将warpImage应用于小矩形块
    img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
    img2Rect = img2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]]

    size = (r[2], r[3])
    warpImage1 = applyAffineTransform(img1Rect, t1Rect, tRect, size)
    warpImage2 = applyAffineTransform(img2Rect, t2Rect, tRect, size)

    # Alpha混合矩形补丁
    imgRect = (1.0 - alpha) * warpImage1 + alpha * warpImage2

    # 将矩形块的三角形区域复制到输出图像
    img[r[1]:r[1] + r[3], r[0]:r[0] + r[2]] = img[r[1]:r[1] + r[3], r[0]:r[0] + r[2]] * (1 - mask) + imgRect * mask


if __name__ == '__main__':
    filename1 = 'nv.jpg'
    filename2 = 'nan.jpg'
    
    #两幅图的融合的比率,范围0到1
    alpha = 0.5

    # 读取图片
    img1 = cv2.imread(filename1)
    img2 = cv2.imread(filename2)

    # 将矩阵转换为浮点数据
    img1 = np.float32(img1)
    img2 = np.float32(img2)

    # 读取相关点
    points1 = readPoints(filename1 + '.txt')
    points2 = readPoints(filename2 + '.txt')
    points = []

    # 计算加权平均点坐标
    for i in range(0, len(points1)):
        x = (1 - alpha) * points1[i][0] + alpha * points2[i][0]
        y = (1 - alpha) * points1[i][1] + alpha * points2[i][1]
        points.append((x, y))

    # 为最后的输出分配空间
    imgMorph = np.zeros(img1.shape, dtype=img1.dtype)
    with open("tri.txt") as file:
        for line in file:
            x, y, z = line.split()

            x = int(x)
            y = int(y)
            z = int(z)

            t1 = [points1[x], points1[y], points1[z]]
            t2 = [points2[x], points2[y], points2[z]]
            t = [points[x], points[y], points[z]]

            # 一次合成一个三角形
            morphTriangle(img1, img2, imgMorph, t1, t2, t, alpha)

    # 输出结果
    cv2.imshow("Morphed Face.jpg", np.uint8(imgMorph))
    cv2.imwrite("video/001.jpg",np.uint8(imgMorph))
    cv2.waitKey(0)

通过逐渐改变融合比率(alpha),输出并保存(imwrite)融合后的图片,图片名为001,002等。我的生成如下图在这里插入图片描述

四、合成人脸渐变视频

avi.py

import cv2
import os

#图片路径
im_dir = 'video'
#输出视频路径
video_dir = 'video/result.avi'
#帧率
fps =6
#图片数,图片数+1,因为后面的循环是从1开始
num = 22
#图片尺寸,若和图片本身尺寸不匹配,输出视频是空的
img_size = (600,800)

#fourcc = cv2.cv.CV_FOURCC('M','J','P','G')#opencv2.4
fourcc = cv2.VideoWriter_fourcc(*'MJPG') #opencv3.0
#读取所有帧,生成视频
videoWriter = cv2.VideoWriter(video_dir, fourcc, fps, img_size)

for i in range(1,num):
    im_name = os.path.join(im_dir,str(i).zfill(3)+'.jpg')
    frame = cv2.imread(im_name)
    videoWriter.write(frame)
    print(im_name)

这一步是将上一步生成的图片合成视频(我这里不会上传视频,就先这样,哈哈)。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值