【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")returnthin_image = srclandmarks_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|^2ddmc = (endX - startX) ** 2 + (endY - startY) ** 2H, W, C = srcImg.shapefor i in range(W):for j in range(H):# 计算该点是否在形变圆的范围之内# 优化,第一步,直接判断是会在(startX,startY)的矩阵框中if math.fabs(i - startX) > radius and math.fabs(j - startY) > radius:continue  # 不在 continuedistance = (i - startX) ** 2 + (j - startY) ** 2if (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] = valuereturn copyImg

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

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

def BilinearInsert(src, ux, uy):w, h, c = src.shapeif c == 3:x1 = int(ux)x2 = x1 + 1y1 = int(uy)y2 = y1 + 1part1 = 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 + part4return 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 mathpredictor_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|^2ddmc = (endX - startX) ** 2 + (endY - startY) ** 2H, W, C = srcImg.shapefor i in range(W):for j in range(H):# 计算该点是否在形变圆的范围之内# 优化,第一步,直接判断是会在(startX,startY)的矩阵框中if math.fabs(i - startX) > radius and math.fabs(j - startY) > radius:continue  # 不在 continuedistance = (i - startX) ** 2 + (j - startY) ** 2if (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] = valuereturn copyImg# 双线性插值法
def BilinearInsert(src, ux, uy):w, h, c = src.shapeif c == 3:x1 = int(ux)x2 = x1 + 1y1 = int(uy)y2 = y1 + 1part1 = 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 + part4return 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")returnthin_image = srclandmarks_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实现

  • 双线性插值法

  • 双线性插值

  • 在这里插入图片描述

  • 在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

灵活妙想学数学

灵活妙想学数学 题1&#xff1a;海星有几只&#xff1f; 一共有12只海洋生物&#xff0c;分别是5只脚的海星&#xff0c;8只脚的章鱼和10只脚的鱿鱼&#xff0c;这些海洋动物的脚一共有87只&#xff0c;每种生物至少有1只&#xff0c;问海星有几只&#xff1f; 解&#xff1a…

STM32-笔记40-BKP(备份寄存器)

一、什么是BKP&#xff08;备份寄存器&#xff09;&#xff1f; 备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备份域里&#xff0c;当VDD电源被切断&#xff0c;他们仍然由VBAT维持供电。当系统在待机模式下被唤醒&#xff0c;或…

Sprint Boot教程之五十八:动态启动/停止 Kafka 监听器

Spring Boot – 动态启动/停止 Kafka 监听器 当 Spring Boot 应用程序启动时&#xff0c;Kafka Listener 的默认行为是开始监听某个主题。但是&#xff0c;有些情况下我们不想在应用程序启动后立即启动它。 要动态启动或停止 Kafka Listener&#xff0c;我们需要三种主要方法…

编译pytorch——cuda-toolkit-nvcc

链接 https://blog.csdn.net/wjinjie/article/details/108997692https://docs.nvidia.com/cuda/cuda-installation-guide-linux/#switching-between-driver-module-flavorshttps://forums.developer.nvidia.com/t/can-not-load-nvidia-drivers-on-ubuntu-22-10/239750https://…

如何发布自己的第一个Chrome扩展程序

如何发布自己的Chrome扩展程序 只需要六步即可完成Chrome扩展程序的发布 &#xff08;1&#xff09;首先打开google chrome 应用商城注册开发者账号的页面 &#xff08;2&#xff09;现在进行一个绑卡支付5美元的一次性注册费用即可。【不知道如何绑卡的支付的&#xff0c;文…

SpringBoot入门实现简单增删改查

本例子的依赖 要实现的内容 通过get、post、put和delete接口,对数据库中的trade.categories表进行增删改查操作。 目录结构 com.test/ │ ├── controller/ │ ├── CateController.java │ ├── pojo/ │ ├── dto/ │ │ └── CategoryDto.java │ ├─…

electron 如何申请 Mac 系统权限

对于一些使用 Electron开发的app, 需要获取一些系统权限,比如录屏权限, 获取摄像头权限,麦克风等等,类似于以下界面: 那么Electron App 应该如何申请呢? 首先我们明确一下macOS中基础权限的分类,可以分为以下几种: 隐私权限(Private Permissions) : <!-- entitlements.ma…

浅谈云计算02 | 云计算模式的演进

云计算计算模式的演进 一、云计算计算模式的起源追溯1.2 个人计算机与桌面计算 二、云计算计算模式的发展阶段2.1 效用计算的出现2.2 客户机/服务器模式2.3 集群计算2.4 服务计算2.5 分布式计算2.6 网格计算 三、云计算计算模式的成熟与多元化3.1 主流云计算服务模式的确立3.1.…

An FPGA-based SoC System——RISC-V On PYNQ项目复现

本文参考&#xff1a; &#x1f449; 1️⃣ 原始工程 &#x1f449; 2️⃣ 原始工程复现教程 &#x1f449; 3️⃣ RISCV工具链安装教程 1.准备工作 &#x1f447;下面以LOCATION代表本地源存储库的安装目录&#xff0c;以home/xilinx代表在PYNQ-Z2开发板上的目录 ❗ 下载Vivad…

AI智能体实战|使用扣子Coze搭建AI智能体,看这一篇就够了(新手必读)

有朋友看到我使用Coze搭建的AI智能体蛮实用的&#xff0c;也想自己尝试一下。那今天我就分享一下如何使用Coze&#xff08;扣子&#xff09;搭建AI智能体&#xff0c;手把手教学&#xff0c;流程超级详细&#xff0c;学会了的话&#xff0c;欢迎分享转发&#xff01; 一、搭建A…

.NET8.0多线程编码结合异步编码示例

1、创建一个.NET8.0控制台项目来演示多线程的应用 2、快速创建一个线程 3、多次运行程序&#xff0c;可以得到输出结果 这就是多线程的特点 - 当多个线程并行执行时&#xff0c;它们的具体执行顺序是不确定的&#xff0c;除非我们使用同步机制&#xff08;如 lock、信号量等&am…

nginx 实现 正向代理、反向代理 、SSL(证书配置)、负载均衡 、虚拟域名 ,使用其他中间件监控

我们可以详细地配置 Nginx 来实现正向代理、反向代理、SSL、负载均衡和虚拟域名。同时&#xff0c;我会介绍如何使用一些中间件来监控 Nginx 的状态和性能。 1. 安装 Nginx 如果你还没有安装 Nginx&#xff0c;可以通过以下命令进行安装&#xff08;以 Ubuntu 为例&#xff0…

《数据思维》之数据可视化_读书笔记

文章目录 系列文章目录前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 数据之道&#xff0c;路漫漫其修远兮&#xff0c;吾将上下而求索。 一、数据可视化 最基础的数据可视化方法就是统计图。一个好的统计图应该满足四个标准&#xff1a;准确、有…

Linux之进程

Linux之进程 一.进程进程之形ps命令进程状态特殊进程孤儿进程守护进程 进程创建之创建子进程进程特性优先级进程切换&#xff08;分时操作系统&#xff09; 二.环境变量三.进程地址空间四.进程终止&进程等待五.进程替换六.自定义shell 本篇博客希望简略的介绍进程&#xff…

漫话架构师|什么是系统架构设计师(开篇)

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 关注犬余&#xff0c;共同进步 技术从此不孤单

在AI智能中有几种重要的神经网络类型?6种重要的神经网络类型分享!

神经网络今天已经变得非常流行&#xff0c;但仍然缺乏对它们的了解。一方面&#xff0c;我们已经看到很多人无法识别各种类型的神经网络及其解决的问题&#xff0c;更不用说区分它们中的每一个了。其次&#xff0c;在某种程度上更糟糕的是&#xff0c;当人们在谈论任何神经网络…

业务幂等性技术架构体系之消息幂等深入剖析

在系统中当使用消息队列时&#xff0c;无论做哪种技术选型&#xff0c;有很多问题是无论如何也不能忽视的&#xff0c;如&#xff1a;消息必达、消息幂等等。本文以典型的RabbitMQ为例&#xff0c;讲解如何保证消息幂等的可实施解决方案&#xff0c;其他MQ选型均可参考。 一、…

【C语言】线程----同步、互斥、条件变量

目录 3. 同步 3.1 概念 3.2 同步机制 3.3 函数接口 1. 同步 1.1 概念 同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情 1.2 同步机制 通过信号量实现线程间的同步 信号量&#xff1a;通过信号量实现同步操作&#xff1b;由信号量来决定…

Linux内核的启动

一、需求 Linux系统中内核处于硬件和应用层之间。整个系统启动和初始化的过程&#xff0c;Linux内核是在主处理器启动之后才会执行。不同的处理器启动流程并不相同&#xff0c;这就要求内核能支持各种处理器的初始化操作。Liux内核各个模块&#xff0c;大部分设计时做到了体系…

[手机Linux] ubuntu 错误解决

Ubuntu: 1,ttyname failed: Inappropriate ioctl for device 将 /root/.profile 文件中的 mesg n || true 改为如下内容。 vim /root/.profile tty -s && mesg n || true 2,Errors were encountered while processing: XXX XXXX sudo apt-get --purge remove xxx…