【python】OpenCV—Local Translation Warps

在这里插入图片描述

文章目录

  • 1、功能描述
  • 2、原理分析
  • 3、代码实现
  • 4、效果展示
  • 5、完整代码
  • 6、参考

1、功能描述

利用液化效果实现瘦脸美颜

交互式的液化效果原理来自 Gustafsson A. Interactive image warping[D]. , 1993.

在这里插入图片描述

2、原理分析

在这里插入图片描述

在这里插入图片描述

上面描述很清晰了,鼠标初始在 C,也即形变范围的圆心在 C,形变半径 r m a x r_{max} rmax,形变方向 C→M,

圆圈内原始 U 位置会被形变到 X,可以简单直白理解为拉伸后 U 位置的值给了 X 位置,此时 U 位置空置了,需要插值

插值公示 93 年的论文中直接给出了,我们尝试 coding

这里还涉及到插值,我们回顾下比较常见的双线性插值原理

在这里插入图片描述

在这里插入图片描述

3、代码实现

导入必要的库函数

import dlib
import cv2
import numpy as np
import math

载入人脸检测器和人脸关键点检测模型

predictor_path = "./shape_predictor_68_face_landmarks.dat"

# 使用dlib自带的frontal_face_detector作为我们的特征提取器
detector = dlib.get_frontal_face_detector()  # 人脸检测器
predictor = dlib.shape_predictor(predictor_path)  # 关键点检测模型

读入图片,调用 face_thin_auto 函数,实现瘦脸

def main():
    src = cv2.imread(r'./1.jpg')  # (1546, 1236, 3)
    # cv2.imshow('src', src)
    face_thin_auto(src)
    cv2.waitKey(0)


if __name__ == '__main__':
    main()

在这里插入图片描述

看看 face_thin_auto 函数的实现细节

def face_thin_auto(src):
    landmarks = landmark_dec_dlib_fun(src)

    point_img = src.copy()
    for index, landmark in enumerate(landmarks[0]):
        cv2.circle(point_img, center=np.array(landmark)[0], radius=5, color=(255, 0, 0), thickness=-1)
        cv2.putText(point_img, str(index), org=(landmark[0,0]-30, landmark[0,1]),
                    fontFace=cv2.FONT_HERSHEY_TRIPLEX,fontScale=0.5, color=(0,255,0))
    cv2.imwrite("point.jpg", point_img)

    # 如果未检测到人脸关键点,就不进行瘦脸
    if len(landmarks) == 0:
        print("not detect face keypoint")
        return

    thin_image = src
    landmarks_node = landmarks[0]
    endPt = landmarks_node[16]  # matrix([[753, 450]])
    for index in range(3, 14, 2):
        start_landmark = landmarks_node[index]
        end_landmark = landmarks_node[index + 2]
        r = math.sqrt((start_landmark[0, 0] - end_landmark[0, 0]) **2 +
                      (start_landmark[0, 1] - end_landmark[0, 1]) **2)
        thin_image = localTranslationWarp(thin_image, start_landmark[0, 0],
                                          start_landmark[0, 1], endPt[0, 0], endPt[0, 1], r)

    # 显示
    # cv2.imshow('thin', thin_image)
    cv2.imwrite(r'./thin.jpg', thin_image)

landmark_dec_dlib_fun 检测人脸和人脸关键点,实现如下

def landmark_dec_dlib_fun(img_src):
    img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
    cv2.imwrite("gray.jpg", img_gray)
    land_marks = []

    rects = detector(img_gray, 0)  # 人脸检测,[[(336, 286) (782, 732)]]
    plot_img = img_src.copy()
    for i in range(len(rects)):  # 遍历检测到的人脸
        cv2.rectangle(plot_img, (rects[i].left(), rects[i].top()),  (rects[i].right(), rects[i].bottom()),
                      color=(0,255,0), thickness=10)
        land_marks_node = np.matrix([[p.x, p.y] for p in predictor(img_gray, rects[i]).parts()])
        land_marks.append(land_marks_node)
    cv2.imwrite("face_det.jpg", plot_img)
    return land_marks

先把图片变成灰度图,然后人脸检测,绘制人脸检测结果,人脸关键点检测,返回关键点坐标

在这里插入图片描述
在这里插入图片描述

face_thin_auto 函数接下来绘制人脸关键点,一共 68 个

在这里插入图片描述

遍历关键点,3,5,7,9,11,13

也即 C = 3,5,7,9,11,13,M = 16

r m a x r_{max} rmax 为关键点 3-5 的距离,5-7 的距离,7-9 的距离,9-11 的距离,11-13 的距离,13-15 的距离

调用 localTranslationWarp 求瘦脸后的图片,

def localTranslationWarp(srcImg, startX, startY, endX, endY, radius):
    ddradius = float(radius * radius)
    copyImg = srcImg.copy()

    # 计算公式中的|m-c|^2
    ddmc = (endX - startX) ** 2 + (endY - startY) ** 2
    H, W, C = srcImg.shape
    for i in range(W):
        for j in range(H):
            # 计算该点是否在形变圆的范围之内
            # 优化,第一步,直接判断是会在(startX,startY)的矩阵框中
            if math.fabs(i - startX) > radius and math.fabs(j - startY) > radius:
                continue  # 不在 continue

            distance = (i - startX) ** 2 + (j - startY) ** 2

            if (distance < ddradius):
                # 计算出(i,j)坐标的原坐标
                # 计算公式中右边平方号里的部分
                ratio = (ddradius - distance) / (ddradius - distance + ddmc)
                ratio = ratio ** 2

                # 映射原位置
                UX = i - ratio * (endX - startX)
                UY = j - ratio * (endY - startY)

                # 根据双线性插值法得到UX,UY的值
                value = BilinearInsert(srcImg, UX, UY)
                # 改变当前 i ,j的值
                copyImg[j, i] = value

    return copyImg

localTranslationWarp 仅作用与以 C 为圆心, r m a x r_{max} rmax 范围内的像素点,像素点的坐标求法代入公式计算,值用插值求出

在这里插入图片描述
双线性插值实现

def BilinearInsert(src, ux, uy):
    w, h, c = src.shape
    if c == 3:
        x1 = int(ux)
        x2 = x1 + 1
        y1 = int(uy)
        y2 = y1 + 1

        part1 = src[y1, x1].astype(float) * (float(x2) - ux) * (float(y2) - uy)
        part2 = src[y1, x2].astype(float) * (ux - float(x1)) * (float(y2) - uy)
        part3 = src[y2, x1].astype(float) * (float(x2) - ux) * (uy - float(y1))
        part4 = src[y2, x2].astype(float) * (ux - float(x1)) * (uy - float(y1))

        insertValue = part1 + part2 + part3 + part4

        return insertValue.astype(np.int8)

我们看看

        part1 = src[y1, x1].astype(float) * (float(x2) - ux) * (float(y2) - uy)
        part2 = src[y1, x2].astype(float) * (ux - float(x1)) * (float(y2) - uy)
        part3 = src[y2, x1].astype(float) * (float(x2) - ux) * (uy - float(y1))
        part4 = src[y2, x2].astype(float) * (ux - float(x1)) * (uy - float(y1))

对应

f ( Q 11 ) ∗ x 2 − x x 2 − x 1 ∗ y 2 − y y 2 − y 1 = f ( Q 11 ) ∗ ( x 2 − x ) ∗ ( y 2 − y ) f(Q_{11}) * \frac{x_2 - x}{x_2- x_1} * \frac{y_2 - y}{y_2- y_1} = f(Q_{11}) * (x_2 - x) * (y_2 - y) f(Q11)x2x1x2xy2y1y2y=f(Q11)(x2x)(y2y)

f ( Q 21 ) ∗ x − x 1 x 2 − x 1 ∗ y 2 − y y 2 − y 1 = f ( Q 21 ) ∗ ( x − x 1 ) ∗ ( y 2 − y ) f(Q_{21}) * \frac{x - x_1}{x_2- x_1} * \frac{y_2 - y}{y_2- y_1} = f(Q_{21}) * (x - x_1) * (y_2 - y) f(Q21)x2x1xx1y2y1y2y=f(Q21)(xx1)(y2y)

f ( Q 12 ) ∗ x 2 − x x 2 − x 1 ∗ y − y 1 y 2 − y 1 = f ( Q 12 ) ∗ ( x 2 − x ) ∗ ( y − y 1 ) f(Q_{12}) * \frac{x_2 - x}{x_2- x_1} * \frac{y - y_1}{y_2- y_1} = f(Q_{12}) * (x_2 - x) * (y - y_1) f(Q12)x2x1x2xy2y1yy1=f(Q12)(x2x)(yy1)

f ( Q 22 ) ∗ x − x 1 x 2 − x 1 ∗ y − y 1 y 2 − y 1 = f ( Q 22 ) ∗ ( x − x 1 ) ∗ ( y − y 1 ) f(Q_{22}) * \frac{x - x_1}{x_2- x_1} * \frac{y - y_1}{y_2- y_1} = f(Q_{22}) * (x - x_1) * (y - y_1) f(Q22)x2x1xx1y2y1yy1=f(Q22)(xx1)(yy1)

4、效果展示

输入
在这里插入图片描述

输出

在这里插入图片描述

再明显一点试试

在这里插入图片描述

在这里插入图片描述

输入

在这里插入图片描述

输出

在这里插入图片描述

肉眼看不太明显,对比工具看比较明显

缩小下图片的输入分辨率

在这里插入图片描述
在这里插入图片描述
效果会明显一些

5、完整代码

import dlib
import cv2
import numpy as np
import math

predictor_path = "./shape_predictor_68_face_landmarks.dat"

# 使用dlib自带的frontal_face_detector作为我们的特征提取器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(predictor_path)


def landmark_dec_dlib_fun(img_src):
    img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
    cv2.imwrite("gray.jpg", img_gray)
    land_marks = []

    rects = detector(img_gray, 0)  # 人脸检测,[[(336, 286) (782, 732)]]
    plot_img = img_src.copy()
    for i in range(len(rects)):  # 遍历检测到的人脸
        cv2.rectangle(plot_img, (rects[i].left(), rects[i].top()),  (rects[i].right(), rects[i].bottom()),
                      color=(0,255,0), thickness=10)
        land_marks_node = np.matrix([[p.x, p.y] for p in predictor(img_gray, rects[i]).parts()])
        land_marks.append(land_marks_node)
    cv2.imwrite("face_det.jpg", plot_img)
    return land_marks


'''
方法: Interactive Image Warping 局部平移算法
'''
def localTranslationWarp(srcImg, startX, startY, endX, endY, radius):
    ddradius = float(radius * radius)
    copyImg = srcImg.copy()

    # 计算公式中的|m-c|^2
    ddmc = (endX - startX) ** 2 + (endY - startY) ** 2
    H, W, C = srcImg.shape
    for i in range(W):
        for j in range(H):
            # 计算该点是否在形变圆的范围之内
            # 优化,第一步,直接判断是会在(startX,startY)的矩阵框中
            if math.fabs(i - startX) > radius and math.fabs(j - startY) > radius:
                continue  # 不在 continue

            distance = (i - startX) ** 2 + (j - startY) ** 2

            if (distance < ddradius):
                # 计算出(i,j)坐标的原坐标
                # 计算公式中右边平方号里的部分
                ratio = (ddradius - distance) / (ddradius - distance + ddmc)
                ratio = ratio ** 2

                # 映射原位置
                UX = i - ratio * (endX - startX)
                UY = j - ratio * (endY - startY)

                # 根据双线性插值法得到UX,UY的值
                value = BilinearInsert(srcImg, UX, UY)
                # 改变当前 i ,j的值
                copyImg[j, i] = value

    return copyImg


# 双线性插值法
def BilinearInsert(src, ux, uy):
    w, h, c = src.shape
    if c == 3:
        x1 = int(ux)
        x2 = x1 + 1
        y1 = int(uy)
        y2 = y1 + 1

        part1 = src[y1, x1].astype(float) * (float(x2) - ux) * (float(y2) - uy)
        part2 = src[y1, x2].astype(float) * (ux - float(x1)) * (float(y2) - uy)
        part3 = src[y2, x1].astype(float) * (float(x2) - ux) * (uy - float(y1))
        part4 = src[y2, x2].astype(float) * (ux - float(x1)) * (uy - float(y1))

        insertValue = part1 + part2 + part3 + part4

        return insertValue.astype(np.int8)


def face_thin_auto(src):
    landmarks = landmark_dec_dlib_fun(src)

    point_img = src.copy()
    for index, landmark in enumerate(landmarks[0]):
        cv2.circle(point_img, center=np.array(landmark)[0], radius=5, color=(255, 0, 0), thickness=-1)
        cv2.putText(point_img, str(index), org=(landmark[0,0]-30, landmark[0,1]),
                    fontFace=cv2.FONT_HERSHEY_TRIPLEX,fontScale=0.5, color=(0,255,0))
    cv2.imwrite("point.jpg", point_img)

    # 如果未检测到人脸关键点,就不进行瘦脸
    if len(landmarks) == 0:
        print("not detect face keypoint")
        return

    thin_image = src
    landmarks_node = landmarks[0]
    endPt = landmarks_node[16]  # matrix([[753, 450]])
    for index in range(3, 14, 2):
        start_landmark = landmarks_node[index]
        end_landmark = landmarks_node[index + 2]
        r = math.sqrt((start_landmark[0, 0] - end_landmark[0, 0]) **2 +
                      (start_landmark[0, 1] - end_landmark[0, 1]) **2)
        thin_image = localTranslationWarp(thin_image, start_landmark[0, 0],
                                          start_landmark[0, 1], endPt[0, 0], endPt[0, 1], r)

    # 显示
    # cv2.imshow('thin', thin_image)
    cv2.imwrite(r'./thin.jpg', thin_image)


def main():
    src = cv2.imread(r'./1.jpg')  # (1546, 1236, 3)
    # cv2.imshow('src', src)
    face_thin_auto(src)
    cv2.waitKey(0)


if __name__ == '__main__':
    main()

6、参考

  • http://dlib.net/files/
    shape_predictor_68_face_landmarks.dat.bz2

  • 链接: https://pan.baidu.com/s/1gO_wqRAtWndGkUhZOSBw2Q?pwd=4enn
    提取码: 4enn

  • 图像变形算法:实现Photoshop液化工具箱中向前变形工具

  • http://www.gson.org/thesis/warping-thesis.pdf

  • 图像处理算法之瘦脸及放大眼睛

  • 图像瘦脸算法

  • 简易版“美颜”来了!肝了一夜!用Python做一个高瘦脸神器!

  • OpenCV图像处理|Python OpenCV实现人脸瘦脸功能

  • pytorch 液态算法实现瘦脸效果

  • 双线性插值算法原理 python实现

  • 双线性插值法

  • 双线性插值

  • 在这里插入图片描述

  • 在这里插入图片描述

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

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

相关文章

大疆上云API基于源码部署

文章目录 大疆上云API基于源码部署注意事项1、学习官网2、环境准备注意事项3、注册成为DJI开发者4、下载前后端运行所需要的包/依赖前端依赖下载后端所需要的Maven依赖包 用到的软件可以在这里下载5、MySQL数据库安装安装MySQL启动MySQL服务在IDEA中配置MySQL的连接信息 6、Red…

AI学习路线图-邱锡鹏-神经网络与深度学习

1 需求 神经网络与深度学习 2 接口 3 示例 4 参考资料

行业案例:高德服务单元化方案和架构实践

目录 为什么要做单元化 高德单元化的特点 高德单元化实践 服务单元化架构 就近接入实现方案 路由表设计 路由计算 服务端数据驱动的单元化场景 总结 系列阅读 为什么要做单元化 单机房资源瓶颈 随着业务体量和服务用户群体的增长,单机房或同城双机房无法支持服…

【计算机网络】lab7 TCP协议

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;计算机网络_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 实验目的…

docker中jenkins流水线式部署GitLab中springboot项目

本质就是将java项目拉取下来&#xff0c;并自动打包成docker镜像&#xff0c;运行 首先启动一个docker的jenkins 如果没有镜像使用我的镜像 通过网盘分享的文件&#xff1a;jenkins.tar 链接: https://pan.baidu.com/s/1VJOMf6RSIQbvW_V1zFD7eQ?pwd6666 提取码: 6666 放入服…

【初识扫盲】厚尾分布

厚尾分布&#xff08;Fat-tailed distribution&#xff09;是一种概率分布&#xff0c;其尾部比正态分布更“厚”&#xff0c;即尾部的概率密度更大&#xff0c;极端值出现的概率更高。 一、厚尾分布的特征 尾部概率大 在正态分布中&#xff0c;极端值&#xff08;如距离均值很…

小程序租赁系统

内容概要 小程序租赁系统&#xff0c;听起来很复杂&#xff0c;但其实就是为那些想要快速搭建业务的人提供一个便捷的工具。随着移动互联网的迅猛发展&#xff0c;越来越多的企业和创业者开始寻找效率和灵活性&#xff0c;而小程序正好满足了这种需求。据统计&#xff0c;过去…

高可用虚拟IP-keepalived

个人觉得华为云这个文档十分详细&#xff1a;使用虚拟IP和Keepalived搭建高可用Web集群_弹性云服务器 ECS_华为云 应用场景&#xff1a;虚拟IP技术。虚拟IP&#xff0c;就是一个未分配给真实主机的IP&#xff0c;也就是说对外提供数据库服务器的主机除了有一个真实IP外还有一个…

工厂人员定位管理系统方案(二)人员精确定位系统架构设计,适用于工厂智能管理

哈喽~这里是维小帮&#xff0c;提供多个场所的定位管理方案&#xff0c;如需获取工厂人员定位管理系统解决方案可前往文章最下方获取&#xff0c;如有项目合作及技术交流欢迎私信我们哦~撒花 在上一篇文章中&#xff0c;我们初步探讨了工厂人员定位管理系统的需求背景以及定位方…

虚假星标:GitHub上的“刷星”乱象与应对之道

在开源软件的世界里&#xff0c;GitHub无疑是最重要的平台之一。它不仅是一个代码托管平台&#xff0c;也是一个社交网络&#xff0c;允许开发者通过“点赞”&#xff08;即加星&#xff09;来表达对某个项目的喜爱和支持&#xff0c;“星标”&#xff08;Star&#xff09;则成…

RK3568 Android 13 内置搜狗输入法小计

问&#xff1a;为什么写&#xff1f; 答&#xff1a;网上搜出来的都试过了&#xff0c;不行&#xff01;下面直接上代码和注意事项&#xff01; 首先到这个目录&#xff08;/RK3568/Rockchip_Android13_SDK_Release/device/rockchip/rk356x/tl3568_evm/preinstall&#xff09…

GO语言实现KMP算法

前言 本文结合朱战立教授编著的《数据结构—使用c语言&#xff08;第五版&#xff09;》&#xff08;以下简称为《数据结构&#xff08;第五版&#xff09;朱站立》&#xff09;中4.4.2章节内容编写&#xff0c;KMP的相关概念可参考此书4.4.2章节内容。原文中代码是C语言&…

基于springboot的疫情网课管理系统

作者&#xff1a;学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等 文末获取“源码数据库万字文档PPT”&#xff0c;支持远程部署调试、运行安装。 项目包含&#xff1a; 完整源码数据库功能演示视频万字文档PPT 项目编码&#xff1…

FFmpeg硬件解码

使用FFmpeg进行硬件解码时&#xff0c;通常需要结合FFmpeg的API和硬件加速API&#xff08;如CUDA、VAAPI、DXVA2等&#xff09;。以下是一个简单的C代码示例&#xff0c;展示如何使用FFmpeg进行硬件解码。这个示例使用了CUDA作为硬件加速的后端。 1. 安装FFmpeg和CUDA 确保你…

unity如何在urp管线下合并spine的渲染批次

对于导入unity的spine来说,他会对每个spine生成独有的材质,虽然他们使用的是同一个shader,但由于附带独有的贴图,这样在项目使用中会由于材质贴图不同而导致无法合批. 而为什么选用urp,因为在built-in管线中,对于GPU-instancing,即使通过使用图集的方式统一了贴图,也会由于spi…

【Elasticsearch】批量操作:优化性能

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s,热衷于探…

深入 Flutter 和 Compose 在 UI 渲染刷新时 Diff 实现对比

众所周知&#xff0c;不管是什么框架&#xff0c;在前端 UI 渲染时&#xff0c;都会有构造出一套相关的渲染树&#xff0c;并且在 UI 更新时&#xff0c;为了尽可能提高性能&#xff0c;一般都只会进行「差异化」更新&#xff0c;而不是对整个 UI Tree 进行刷新&#xff0c;所以…

Docker 的安装和基本使用[SpringBoot之Docker实战系列] - 第535篇

历史文章&#xff08;文章累计530&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 《…

介绍下不同语言的异常处理机制

Golang 在Go语言中&#xff0c;有两种用于处于异常的机制&#xff0c;分别是error和panic&#xff1b; panic panic 是 Go 中处理异常情况的机制&#xff0c;用于表示程序遇到了无法恢复的错误&#xff0c;需要终止执行。 使用场景 程序出现严重的不符合预期的问题&#x…

车联网安全--TLS握手过程详解

目录 1. TLS协议概述 2. 为什么要握手 2.1 Hello 2.2 协商 2.3 同意 3.总共握了几次手&#xff1f; 1. TLS协议概述 车内各ECU间基于CAN的安全通讯--SecOC&#xff0c;想必现目前多数通信工程师们都已经搞的差不多了&#xff08;不要再问FvM了&#xff09;&#xff1b;…