Python图像处理【24】面部变形(face morphing)

面部变形

    • 0. 前言
    • 1. 网格变形算法
    • 2. 实现面部变形
    • 小结
    • 系列链接

0. 前言

面部变形 (face morphing) 的目的是在计算图像中两个面部之间的平均值,它并不是计算两张图像的平均值,而是计算面部区域的平均值。可以将该过程分解为以下两步:

  • 对齐两个面部图像(使用仿射变换)
  • 应用交叉溶解(利用 alpha 融合执行图像线性组合)创建输出图像

但是该方法通常无法正确应用,我们通常需要使用(局部)特征匹配。例如,要执行面部变形,可以在关键点之间(例如鼻子与鼻子,眼睛与眼睛)进行匹配,这称为局部(非参数)扭曲。

1. 网格变形算法

使用网格变形算法实现面部变形的步骤如下:

  • 定义对应关系:面部变形算法使用两个面部共同的一组特征点将源面部转换为目标面部,这些特征点可以手动创建,也可以使用面部特征检测算法自动生成的面部特征点,我们需要找到对齐面部之间的点对应关系(例如,使用两个面部上相同的关键点顺序以一致的方式标记特征点)
  • Delaunay 三角剖分:需要提供用于变形的点的三角剖分;Delaunay 三角剖分不会输出过细的三角形
  • 计算中间(变形)面部:在计算整个变形序列之前,计算源和目的地图像的中间面部,这需要计算平均形状,即两个面部中每个关键点位置的平均值
  • 计算变形图像 M 中特征点的位置:对于源图像中的每个三角形,计算将三角形的三个角映射到变形图像中三角形的对应角的仿射变换,然后使用刚刚计算的仿射变换将三角形内的所有像素变换到变形图像
  • 最后,使用 alpha 混合算法将两个图像混合在一起,得到最终变形图像

面部标志 (Facial landmarks) 是面部关键点/特征,用于标记/识别关键面部属性,并定位/表示面部的突出区域,如左/右眼、左/右眉毛、鼻子、嘴巴和下颌线等。面部关键点检测已经成功地应用于面部对齐、头部姿态估计、面部交换、眨眼检测等。

2. 实现面部变形

(1) 首先导入所有必需的库:

from scipy.spatial import Delaunay
from skimage.io import imread
import scipy.misc
import cv2
import dlib
import numpy as np
from matplotlib import pyplot as plt

(2) 使用 dlib 库的面部检测器和形状预测器,通过实现函数 extract_landmarks() 自动计算面部关键点。该函数获取输入图像,使用布尔标志指示是否添加边界点,并需要 dlib 库的 shape_predictor 模型的路径(即 dat 文件的路径,该文件是序列化的预训练的面部特征点提取器模型)。函数使用 dlibshape_predictor() 函数(内部使用回归树集合)来估算 68 个关键点的位置 (x,y)

def extract_landmarks(img, add_boundary_points=True, predictor_path = 'models/shape_predictor_68_face_landmarks.dat'):
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(predictor_path)
    try:
        #points = stasm.search_single(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY))
        dets = detector(img, 1)
        points = np.zeros((68, 2))
        for k, d in enumerate(dets):
            print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format(
                k, d.left(), d.top(), d.right(), d.bottom()))
            # Get the landmarks/parts for the face in box d.
            shape = predictor(img, d)
            for i in range(68):
                points[i, 0] = shape.part(i).x
                points[i, 1] = shape.part(i).y
    except Exception as e:
        print('Failed finding face points: ', e)
        return []
    points = points.astype(np.int32)
    return points

可以在 GitCode 中下载 shape_predictor_68_face_landmarks.dat 文件。

(3) 使用函数 extract_landmarks() 提取源和目标面部图像的关键点:

src_path = '1.png'
dst_path = '2.png'

src_img = imread(src_path)
dst_img = imread(dst_path)

src_points = extract_landmarks(src_img)
dst_points = extract_landmarks(dst_img)

(4) 使用函数 weighted_average_points() 计算两组点和两个图像的 alpha 混合结果:

def weighted_average_points(start_points, end_points, percent=0.5):
    if percent <= 0:
        return end_points
    elif percent >= 1:
        return start_points
    else:
        return np.asarray(start_points*percent + end_points*(1-percent), np.int32)

(5) 实现函数 bilinear_interpolate() 对每个图像通道进行插值:

def bilinear_interpolate(img, coords):
    int_coords = np.int32(coords)
    x0, y0 = int_coords
    dx, dy = coords - int_coords

    # 4 Neighour pixels
    q11 = img[y0, x0]
    q21 = img[y0, x0+1]
    q12 = img[y0+1, x0]
    q22 = img[y0+1, x0+1]

    btm = q21.T * dx + q11.T * (1 - dx)
    top = q22.T * dx + q12.T * (1 - dx)
    inter_pixel = top * dy + btm * (1 - dy)

    return inter_pixel.T

(6) 实现函数 get_grid_coordinates() 生成输入点感兴趣区域 (region of interest, ROI) 内所有可能 (x,y) 网格坐标的数组:

def get_grid_coordinates(points):
    xmin = np.min(points[:, 0])
    xmax = np.max(points[:, 0]) + 1
    ymin = np.min(points[:, 1])
    ymax = np.max(points[:, 1]) + 1
    return np.asarray([(x, y) for y in range(ymin, ymax)
                        for x in range(xmin, xmax)], np.uint32)

(7) 实现函数 process_warp()result_imgROI 内扭曲 src_img 中的每个三角形:

def process_warp(src_img, result_img, tri_affines, dst_points, delaunay):
    roi_coords = get_grid_coordinates(dst_points)
    # indices to vertices. -1 if pixel is not in any triangle
    roi_tri_indices = delaunay.find_simplex(roi_coords)

    for simplex_index in range(len(delaunay.simplices)):
        coords = roi_coords[roi_tri_indices == simplex_index]
        num_coords = len(coords)
        out_coords = np.dot(tri_affines[simplex_index], np.vstack((coords.T, np.ones(num_coords))))
        x, y = coords.T
        result_img[y, x] = bilinear_interpolate(src_img, out_coords)
    return None

(8) 实现生成器函数 gen_triangular_affine_matrics() 计算从 dest_pointssrc_points 的每个三角形顶点 (x,y) 的仿射变换矩阵:

def get_triangular_affine_matrices(vertices, src_points, dest_points):
    ones = [1, 1, 1]
    for tri_indices in vertices:
        src_tri = np.vstack((src_points[tri_indices, :].T, ones))
        dst_tri = np.vstack((dest_points[tri_indices, :].T, ones))
        mat = np.dot(src_tri, np.linalg.inv(dst_tri))[:2, :]
        yield mat

(9) 实现函数 warp_image() 获取源/目标图像和相应的关键点,并使用定义的函数计算变形的输出图像和关键点的相应 Delaunay 三角测量:

def warp_image(src_img, src_points, dest_points, dest_shape, dtype=np.uint8):
    num_chans = 3
    src_img = src_img[:, :, :3]
    rows, cols = dest_shape[:2]
    result_img = np.zeros((rows, cols, num_chans), dtype)
    delaunay = Delaunay(dest_points)
    tri_affines = np.asarray(list(get_triangular_affine_matrices(delaunay.simplices, src_points, dest_points)))
    process_warp(src_img, result_img, tri_affines, dest_points, delaunay)
    return result_img, delaunay

(10) 绘制源和目标人脸图像以及面部标志(由形状预测器预测):

fig = plt.figure(figsize=(20,10))
plt.subplot(121)
plt.imshow(src_img)
for i in range(68):
    plt.plot(src_points[i,0], src_points[i,1], 'r.', markersize=20)
plt.title('Source image', size=10)
plt.axis('off')
plt.subplot(122)
plt.imshow(dst_img)
for i in range(68):
    plt.plot(dst_points[i,0], dst_points[i,1], 'g.', markersize=20)
plt.title('Destination image', size=10)
plt.axis('off')
plt.suptitle('Facial Landmarks computed for the images', size=12)
fig.subplots_adjust(wspace=0.01, left=0.1, right=0.9)
plt.show()

面部关键点

(11) 计算 alpha 值增加的变形图像,获得具有不同混合比例的变形图像,以观察从源图像到目标图像的平滑过渡:

fig = plt.figure(figsize=(20,20))
i = 1
for percent in np.linspace(1, 0, 9):
    points = weighted_average_points(src_points, dst_points, percent)
    src_face, src_d = warp_image(src_img, src_points, points, (600,600))
    end_face, end_d = warp_image(dst_img, dst_points, points, (600,600))
    average_face = weighted_average_points(src_face, end_face, percent)
    plt.subplot(3, 3, i)
    plt.imshow(average_face)
    plt.title('alpha=' + str(percent), size=10)
    plt.axis('off')
    i += 1
plt.suptitle('Face morphing', size=12)
fig.subplots_adjust(top=0.92, bottom=0, left=0.075, right=0.925, wspace=0.01, hspace=0.05)
plt.show()

面部变形过程

(12) 可视化具有使用 Delaunay 三角测量获得的三角剖分面部,并将面部标志作为三角形的顶点:

fig = plt.figure(figsize=(20,10))
plt.subplot(121)
plt.imshow(src_img)
plt.triplot(src_points[:,0], src_points[:,1], src_d.simplices.copy())
plt.plot(src_points[:,0], src_points[:,1], 'o', color='red')
plt.title('Source image', size=10)
plt.axis('off')
plt.subplot(122)
plt.imshow(dst_img)
plt.triplot(dst_points[:,0], dst_points[:,1], end_d.simplices.copy())
plt.plot(dst_points[:,0], dst_points[:,1], 'o')
plt.title('Destination image', size=10)
plt.axis('off')
plt.suptitle('Delaunay triangulation of the images', size=12)
fig.subplots_adjust(wspace=0.01, left=0.1, right=0.9)
plt.show()

三角剖分结果

小结

面部图像处理是提取和分析人类面部信息的研究领域,人脸是图像处理中的最重要的对象之一。因此,在过去的几十年中,面部图像的自动处理和识别已经得到了研究人员的极大关注。在本节中,我们学习了如何使用 dlib 库执行面部变形操作。

系列链接

Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【4】图像线性变换
Python图像处理【5】图像扭曲/逆扭曲
Python图像处理【6】通过哈希查找重复和类似的图像
Python图像处理【7】采样、卷积与离散傅里叶变换
Python图像处理【8】使用低通滤波器模糊图像
Python图像处理【9】使用高通滤波器执行边缘检测
Python图像处理【10】基于离散余弦变换的图像压缩
Python图像处理【11】利用反卷积执行图像去模糊
Python图像处理【12】基于小波变换执行图像去噪
Python图像处理【13】使用PIL执行图像降噪
Python图像处理【14】基于非线性滤波器的图像去噪
Python图像处理【15】基于非锐化掩码锐化图像
Python图像处理【16】OpenCV直方图均衡化
Python图像处理【17】指纹增强和细节提取
Python图像处理【18】边缘检测详解
Python图像处理【19】基于霍夫变换的目标检测
Python图像处理【20】图像金字塔
Python图像处理【21】基于卷积神经网络增强微光图像
Python图像处理【22】基于卷积神经网络的图像去雾
Python图像处理【23】分布式图像处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/563332.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker容器化部署(企业版)

大家好&#xff0c;webfunny前端监控埋点系统&#xff0c;已经正式发布了webfunny的官方镜像&#xff1a; Webfunny镜像目录&#xff1a;https://hub.docker.com/r/webfunny/webfunny_monitor_cluster/tags 部署前提是你的服务器已经安装了Docker环境&#xff0c;没有安装doc…

车载电子电器架构 —— 售后诊断开发

车载电子电器架构 —— 售后诊断开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己…

OpenCV——Niblack局部阈值二值化方法

目录 一、Niblack算法1、算法概述2、参考文献二、代码实现三、结果展示OpenCV——Niblack局部阈值二值化方法由CSDN点云侠原创,爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、Niblack算法 1、算法概述 Niblack 算法是一种典型的局部阈值…

SpringMVC深解--一起学习吧之架构

SpringMVC的工作原理主要基于请求驱动&#xff0c;它采用了前端控制器模式来进行设计。以下是SpringMVC工作原理的详细解释&#xff1a; 请求接收与分发&#xff1a; 当用户发送一个请求到Web服务器时&#xff0c;这个请求首先会被SpringMVC的前端控制器&#xff08;Dispatche…

VUE识别图片文字OCR(tesseract.js)

效果:1&#xff1a; 效果图2&#xff1a; 一、安装tesseract.js npm i tesseract.js 二、静态页面实现 <template><div><div style"marginTop:100px"><input change"handleChage" type"file" id"image-input"…

【数据结构项目】通讯录

个人主页点这里~ 原文件在gitee里~ 通讯录的实现 基于动态顺序表实现通讯录项目1、功能要求2、代码实现file.hfile.cList.hList.ctest.c 基于动态顺序表实现通讯录项目 准备&#xff1a;结构体、动态内存管理、顺序表、文件操作 1、功能要求 ①能够存储100个人的通讯信息 ②…

Xilinx 7系列FPGA全局时钟缓冲器基本单元

Global Clock Buffer Primitives&#xff08;全局时钟缓冲器基本单元&#xff09;在FPGA&#xff08;现场可编程门阵列&#xff09;和其他数字系统中扮演着至关重要的角色。这些基本单元被设计用于处理、分配和增强时钟信号&#xff0c;以确保系统中的各个组件都能以精确和同步…

【Hadoop】- YARN架构[7]

前言 Yarn架构是一个用于管理和调度Hadoop集群资源的系统。它是Hadoop生态系统的一部分&#xff0c;主要用于解决Hadoop中的资源管理问题。 通过使用Yarn架构&#xff0c;Hadoop集群中的不同应用程序可以共享集群资源&#xff0c;并根据需要动态分配和回收资源。这种灵活的资…

4.21java聊天室项目小结

基本完成了用户的登录注册功能&#xff0c;可以实现用户账号登录和邮箱登录功能&#xff0c;忘记密码通过邮箱发送验证码找回&#xff0c;注册账号功能&#xff0c;并传递给客户端更新数据库的表内容 注册功能&#xff1a; 注册成功后密码进行MD5加密并通过服务器保存到数据库…

【STM32F4】STM32CUMX相关环境配置

一、环境配置 我们需要以下两个软件 &#xff08;一&#xff09;keil5 最正统&#xff0c;最经典的嵌入式MCU开发环境。 该环境的配置可以看看之前的文章 所需文件如下&#xff1a; 当时配置的是STC8H的环境&#xff0c;现在基于此&#xff0c;重新给STM32配置环境。能让STC…

Tricentis测试生成式人工智能系统和红队:入门指南

Tricentis测试生成式人工智能系统和红队:入门指南 测试人工智能并确保其责任、安全和保障的话题从未如此紧迫。自 2021 年以来,人工智能滥用的争议和事件增加了26 倍,凸显了日益增长的担忧。用户很快就会发现,人工智能工具并非万无一失。他们可能会表现出过度自信,并且缺…

ChatGPT研究论文提示词集合2-【形成假设、设计研究方法】

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 目录 1.形成假设 2.设计研究方法 3.书籍介绍 AIPaperPass智能论文写作平台 近期小编按照学术论文的流程&#xff0c;精心准备一套学术研究各个流程的提示词集合。总共14个步骤&#…

11.接口自动化学习-Yaml学习

1.配置文件作用 配置文件项目角度&#xff1a; &#xff08;1&#xff09;现成的应用–第三方组件 mysql–数据库–my.conf tomcat–web服务器–server.xml 修改&#xff1a;连接数/端口 redis–缓存服务器–redis.conf 修改配置 jemeter–压测工具–jemeter.properties–修改…

PHP反序列化漏洞原理(附带pikachu靶场演示)

1.反序列化概念 序列化:是将变量转换为可保存或传输的字符串的过程;实现函数是serialize()反序列化:就是在适当的时候把这个字符串再转化成原来的变量使用&#xff0c;就是序列化的逆过程。实现函数是unserialize() 直白一点就是&#xff1a;序列化是把对象转换成字节流&#…

SpringMVC--RESTful

1. RESTful 1.1. RESTful简介 REST&#xff1a;Representational State Transfer&#xff0c;表现层资源状态转移。 RESTful是一种网络架构风格&#xff0c;它定义了如何通过网络进行数据的交互。这种风格基于HTTP协议&#xff0c;使得网络应用之间的通信变得更加简洁和高效。…

python爬虫--------requests案列(二十七天)

兄弟姐们&#xff0c;大家好哇&#xff01;我是喔的嘛呀。今天我们一起来学习requests案列。 一、requests____cookie登录古诗文网 1、首先想要模拟登录&#xff0c;就必须要获取登录表单数据 登录完之后点f12&#xff0c;然后点击network&#xff0c;最上面那个就是登录接口…

数据结构––kmp算法(串)

kmp算法作为串的一个重要内容&#xff0c;必然有一定的难度&#xff0c;而在看到各类教辅书里的概念与解释后&#xff0c;其晦涩难懂的内容直接劝退一部分人&#xff0c;现在&#xff0c;让我们来看看吧 KMP解决的问题类型 KMP算法的作用就是在一个已知的字符串中查找子串的位…

布控球——防爆监控设备

控球&#xff0c;作为一种专为特定场景设计的防爆监控设备&#xff0c;主要用于在高风险、易燃易爆等特殊环境中提供实时、安全、高效的视频监控服务。其主要特点和功能如下&#xff1a; 防爆性能&#xff1a;布控球首先具备出色的防爆能力&#xff0c;外壳通常采用高强度、耐高…

解开Intel ECI 的面纱

前言 Intel ECI是一个用于工业领域边缘控制的软硬件平台&#xff0c;我们今天主要探索的是软件部分&#xff0c;也就是系统镜像。区别于传统的Ubuntu或者Debian&#xff0c;ECI的强大之处在于它的实时性以及对于Intel自家芯片的缓存优化能力极强。 那么让我们来探索一下 编译…

学习51单片机 C语言知识

一、数据类型 C 语言包含的数据类型如下图所示 C51 的数据类型分为基本数据类型和组合数据类型&#xff0c;情况与标准 C 中的数据类型基本相同&#xff0c;但其中 char 型与 short 型相同&#xff0c;float 型与 double 型相同&#xff0c;另外&#xff0c;C51 中还有专门针…