【python】OpenCV—Local Translation Warps

server/2025/1/16 2:08:42/

在这里插入图片描述

文章目录

  • 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、代码实现

导入必要的库函数

python">import dlib
import cv2
import numpy as np
import math

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

python">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 函数,实现瘦脸

python">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 函数的实现细节

python">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 检测人脸和人脸关键点,实现如下

python">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瘦脸后的图片,

python">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 范围内的像素点,像素点的坐标求法代入公式计算,值用插值求出

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

python">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)

我们看看

python">        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、完整代码

python">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.ppmy.cn/server/158713.html

相关文章

互联网全景消息(11)之Kafka深度剖析(下)

一、Kafka底层架构 1.1 存储架构 在前面讲过kafka每个主题可以有多个分区&#xff0c;每个分区在它所在的broker上创建一个文件夹每个分区又分为多个段&#xff0c;每个段两个文件&#xff0c;log文件存储顺序消息&#xff0c;index文件里存消息的索引&#xff0c;然后每一个段…

toJSON使用中遇到的问题

目录 为什么要使用JSON使用JSON遇到的问题最后 为什么要使用JSON 在我的项目中&#xff0c;我想使用layui的模版来渲染我的页面&#xff0c;而这个页面我是想通过将它配置成参数来渲染的 具体实现是这样 var laytpl layui.laytpl laytpl(html).render(data, function (strin…

27年《海贼王》:动漫停更,游戏加更

12月的尾声&#xff0c;今年最后一款二游《航海王壮志雄心》正式上线。 2024年&#xff0c;对于新上的二游而言&#xff0c;并不是一个友好的时间段。 由于《原神》带动二游研发浪潮&#xff0c;海量的二游项目在2023年和2024年涌现&#xff0c;导致市场彻底沦为买方市场&…

java基础概念55-不可变集合

一、定义 不可变集合&#xff1a;不可以被修改的集合&#xff0c;只能查询。&#xff08;长度、内容均不可被修改&#xff09; 二、创建不可变集合 【注意】&#xff1a; 此方法是在JDK9中引入的。 2-1、list不可变集合 示例&#xff1a; import java.util.List;public cla…

C#,入门教程(27)——应用程序(Application)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(26)——数据的基本概念与使用方法https://blog.csdn.net/beijinghorn/article/details/124952589 一、什么是应用程序 Application&#xff1f; 应用程序是编程的结果。一般把代码经过编译&#xff08;等&#xff09;过程&#…

碰一碰发视频源码搭建技术剖析,支持OEM

在当下数字化信息高速传播的时代&#xff0c;碰一碰发视频功能以其便捷性和创新性&#xff0c;为信息交互带来了全新的体验。本文将深入探讨该功能的源码搭建技术&#xff0c;助力开发者实现这一充满创意的应用。 一、技术选型 移动端开发&#xff1a;选用React Native作为移动…

C#语言的数据结构

C#语言的数据结构探讨 数据结构是计算机科学中一种用于组织、存储和管理数据的方式。有效地使用数据结构能使算法更加高效&#xff0c;并提高程序的性能。在C#语言中&#xff0c;我们可以构建和使用多种数据结构&#xff0c;以满足不同的需求。本文将介绍C#中的常用数据结构&a…

CentOS 9 Stream 中查看 Python 版本并升级 Python

CentOS 9 Stream 中查看 Python 版本并升级 Python 1. 查看当前 Python 版本2. 升级 Python 版本&#xff08;1&#xff09;安装开发工具&#xff08;2&#xff09;安装必要的依赖包&#xff08;3&#xff09;下载和安装新版本的 Python&#xff08;4&#xff09;验证安装 3. …