本文是利用opencv python 的美颜(磨皮,大眼)实现。

1 磨皮

1.1 导向滤波

磨皮使用的是导向滤波进行磨皮。关于导向滤波的介绍,可以看我的另一篇文章导向滤波与opencv python实现

2.2 椭圆肤色模型

可以先使用椭圆肤色模型对皮肤部分进行提取,得到掩膜数组,然后利用位运算将滤波后的图像与原图结合,这样就做到了对背景的保护。

def YCrCb_ellipse_model(img):skinCrCbHist = np.zeros((256,256), dtype= np.uint8)cv2.ellipse(skinCrCbHist, (113,155),(23,25), 43, 0, 360, (255,255,255), -1) #绘制椭圆弧线YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB) #转换至YCrCb空间(Y,Cr,Cb) = cv2.split(YCrCb) #拆分出Y,Cr,Cb值skin = np.zeros(Cr.shape, dtype = np.uint8) #掩膜(x,y) = Cr.shapefor i in range(0, x):for j in range(0, y):if skinCrCbHist [Cr[i][j], Cb[i][j]] > 0: #若不在椭圆区间中skin[i][j] = 255res = cv2.bitwise_and(img,img, mask = skin)return skin,res

可以对掩膜数组进行一次开运算,消除细小区域。

def mopi(img):skin,_ = ellipse_skin_model.YCrCb_ellipse_model(img)#获得皮肤的掩膜数组#进行一次开运算kernel = np.ones((3,3),dtype=np.uint8)skin = cv2.erode(skin,kernel=kernel)skin = cv2.dilate(skin,kernel=kernel)img1 = guided_filter(img/255.0,img/255.0,10,eps=0.001)*255img1 = np.array(img1,dtype=np.uint8)img1 = cv2.bitwise_and(img1,img1,mask=skin)#将皮肤与背景分离skin = cv2.bitwise_not(skin)img1 = cv2.add(img1,cv2.bitwise_and(img,img,mask=skin))#磨皮后的结果与背景叠加fig, ax = plt.subplots(1, 2)# ax[0].imshow(skin)# ax[1].imshow(img1[:, :, ::-1])# plt.show()return img1

得到的结果如下图

2 人脸关键点提取

在美颜中,要想进行大眼,瘦脸等操作,需要首先提取出人脸的关键点才可以做坐标变换。
这里,使用了Google的mediapipe进行检测。一般来说应该使用dlib的,但我折腾一个小时没装上。。。

import cv2
import mediapipe as mp
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
def get_face_key_point(img):with mp_face_detection.FaceDetection(model_selection=1, min_detection_confidence=0.5) as face_detection:results = face_detection.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))if not results.detections:return Noneannotated_image = img.copy()r,w,c =img.shapefor detection in results.detections:left_eye =mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.LEFT_EYE)right_eye =mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.RIGHT_EYE)left_eye_pos = [int(left_eye.x * w), int(left_eye.y * r)]right_eye_pos = [int(right_eye.x * w), int(right_eye.y * r)]return  left_eye_pos,right_eye_pos

3 大眼​

3.1 原理

这里参考了论文Interactive Image Warping的局部缩放算法。
局部缩放公式:


由上公式可得,当rrr等于rmaxr_{max}rmax​时,该式等于rrr。

如果a>0a>0a>0,即r乘上一个0~1的数,即变换后的点相较于变换前的点相对于圆心外扩了。也就是局部放大。
如果a<0a<0a<0,即r乘上一个大于1的数,即变换后的点相较于变换前的点相对于圆心内缩了。也就是局部缩小。

根据上述公式,我们可以得到一个原图与输出图的像素坐标映射,通过双线性插值可以得到输出图的对应像素值。

3.2 代码

局部放大函数:

def Local_scaling_warps(img,cx,cy,r_max,a):img1 = np.copy(img)for y in range(cy-r_max,cy+r_max+1):d = int(math.sqrt(r_max**2-(y-cy)**2))x0 = cx-dx1 = cx+dfor x in range(x0,x1+1):r = math.sqrt((x-cx)**2 + (y-cy)**2)for c in range(3):if r<=r_max:vector_c = np.array([cx, cy])vector_r =np.array([x,y])-vector_cf_s = (1-((r/r_max-1)**2)*a)vector_u = vector_c+f_s*vector_r#原坐标img1[y][x][c] = bilinear_interpolation(img,vector_u,c)return img1

双线性插值:

def bilinear_interpolation(img,vector_u,c):ux,uy=vector_ux1,x2 = int(ux),int(ux+1)y1,y2 = int(uy),int(uy+1)# f_x_y1 = (x2-ux)/(x2-x1)*img[x1][y1]+(ux-x1)/(x2-x1)*img[x2][y1]# f_x_y2 = (x2 - ux) / (x2 - x1) * img[x1][y2] + (ux - x1) / (x2 - x1) * img[x2][y2]f_x_y1 = (x2-ux)/(x2-x1)*img[y1][x1][c]+(ux-x1)/(x2-x1)*img[y1][x2][c]f_x_y2 = (x2 - ux) / (x2 - x1) * img[y2][x1][c] + (ux - x1) / (x2 - x1) * img[y2][x2][c]f_x_y = (y2-uy)/(y2-y1)*f_x_y1+(uy-y1)/(y2-y1)*f_x_y2return int(f_x_y)

4 简单界面

这里利用pyqt5简单设计了一个界面。
其中下方的两个滑块分别对应放缩的系数aaa和放缩范围rrr。
左边显示的是原图,右边是处理后的图像。可见小姐姐皮肤变好了,眼睛也变大了。

将参数稍微调下,会变得有点阴间哈哈哈。

# -*- coding: utf-8 -*-
# @Time : 2022/10/14 19:05
# @Author : shuoshuo
# @File : main.py
# @Project : 美颜
import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import *
from PyQt5.QtCore  import Qt
from PyQt5.QtGui import QPixmap, QImage
from PIL import Image,ImageQt
from qtpy import QtGui
import beautify
import face_detect
class My_win(QWidget):def __init__(self,img):super().__init__()self.initUI(img)def initUI(self,img):self.r_max = 40self.a = 0.5self.left_eye_pos,self.right_eye_pos = face_detect.get_face_key_point(img)self.img = img.copy()self.mopi_btn = QPushButton("磨皮",self)self.big_eye_btn  = QPushButton("大眼",self)self.save_btn = QPushButton("保存",self)self.a_slider =QSlider(Qt.Horizontal)self.a_slider.setRange(1,99)self.a_slider.setSingleStep(5)self.a_slider.setToolTip('大眼程度')self.r_slider = QSlider(Qt.Horizontal)self.r_slider.setRange(1,99)self.r_slider.setSingleStep(5)self.r_slider.setToolTip('大眼范围')self.r_slider.valueChanged.connect(self.r_change)self.a_slider.valueChanged.connect(self.a_change)self.big_eye_btn.clicked.connect(self.big_eye)self.mopi_btn.clicked.connect(self.mopi)self.save_btn.clicked.connect(self.save)self.original_pic = QLabel(self)self.result_pic = QLabel(self)self.button_box = QHBoxLayout()# self.button_box.addStretch(1)self.button_box.addWidget(self.mopi_btn)self.button_box.addWidget(self.big_eye_btn)self.button_box.addWidget(self.save_btn)self.slider_box = QHBoxLayout()self.slider_box.addWidget(self.a_slider)self.slider_box.addWidget(self.r_slider)self.hbox  = QHBoxLayout()self.hbox.addStretch(1)self.hbox.addWidget(self.original_pic)self.hbox.addWidget(self.result_pic)self.vbox = QVBoxLayout()self.vbox.addStretch(1)self.vbox.addLayout(self.button_box)self.vbox.addLayout(self.slider_box)self.vbox.addLayout(self.hbox)self.setLayout(self.vbox )self.show_img(img,frame=self.original_pic)self.show_img(self.img,frame=self.result_pic)# self.setGeometry(100,100,800,600)self.setWindowTitle("拉闸美颜")self.show()def show_img(self,img,frame):r,w,c = img.shapeimage = QtGui.QImage(img.data, w, r,w*3, QtGui.QImage.Format_RGB888).rgbSwapped()frame.setPixmap(QtGui.QPixmap.fromImage(image))# frame.setGeometry(pos[0],pos[1],r,w)def big_eye(self):self.result = beautify.big_eye(self.img,r_max=self.r_max,a=self.a,left_eye_pos=self.left_eye_pos,right_eye_pos=self.right_eye_pos)self.result = np.array(self.result, dtype=np.uint8)self.show_img(self.result, frame=self.result_pic)def mopi(self):self.img = beautify.mopi(self.img)# self.img = np.array(self.img*255,dtype=np.uint8)self.show_img(self.img, frame=self.result_pic)def a_change(self):self.a = self.a_slider.value()/100self.big_eye()def r_change(self):self.r_max = self.r_slider.value()self.big_eye()def save(self):cv2.imwrite('output.jpg',self.result)
if __name__ == '__main__':app = QApplication(sys.argv)img = cv2.imread('img.png')# img = cv2.resize(img,(400,300))ex = My_win(img)sys.exit(app.exec_())

4 完整代码

# -*- coding: utf-8 -*-
# @Time : 2022/10/5 22:48
# @Author : shuoshuo
# @File : beautify.py
# @Project : 美颜
import mathimport cv2
import numpy as np
import matplotlib.pyplot as plt
import face_detect
import ellipse_skin_model
def bilinear_interpolation(img,vector_u,c):ux,uy=vector_ux1,x2 = int(ux),int(ux+1)y1,y2 = int(uy),int(uy+1)# f_x_y1 = (x2-ux)/(x2-x1)*img[x1][y1]+(ux-x1)/(x2-x1)*img[x2][y1]# f_x_y2 = (x2 - ux) / (x2 - x1) * img[x1][y2] + (ux - x1) / (x2 - x1) * img[x2][y2]f_x_y1 = (x2-ux)/(x2-x1)*img[y1][x1][c]+(ux-x1)/(x2-x1)*img[y1][x2][c]f_x_y2 = (x2 - ux) / (x2 - x1) * img[y2][x1][c] + (ux - x1) / (x2 - x1) * img[y2][x2][c]f_x_y = (y2-uy)/(y2-y1)*f_x_y1+(uy-y1)/(y2-y1)*f_x_y2return int(f_x_y)
def Local_scaling_warps(img,cx,cy,r_max,a):img1 = np.copy(img)for y in range(cy-r_max,cy+r_max+1):d = int(math.sqrt(r_max**2-(y-cy)**2))x0 = cx-dx1 = cx+dfor x in range(x0,x1+1):r = math.sqrt((x-cx)**2 + (y-cy)**2) #求出当前位置的半径for c in range(3):vector_c = np.array([cx, cy])vector_r =np.array([x,y])-vector_cf_s = (1-((r/r_max-1)**2)*a)vector_u = vector_c+f_s*vector_r#原坐标img1[y][x][c] = bilinear_interpolation(img,vector_u,c)return img1def big_eye(img,r_max,a,left_eye_pos=None,right_eye_pos=None):img0 = img.copy()if left_eye_pos==None or right_eye_pos==None:left_eye_pos,right_eye_pos=face_detect.get_face_key_point(img)img0 = cv2.circle(img0,left_eye_pos,radius=10,color=(0,0,255))img0 = cv2.circle(img0,right_eye_pos,radius=10,color=(0,0,255))img= Local_scaling_warps(img,left_eye_pos[0],left_eye_pos[1],r_max=r_max,a=a)img = Local_scaling_warps(img,right_eye_pos[0],right_eye_pos[1],r_max=r_max,a=a)return img
def guided_filter(I,p,win_size,eps):assert I.any() <=1 and p.any()<=1mean_I = cv2.blur(I,(win_size,win_size))mean_p = cv2.blur(p,(win_size,win_size))corr_I = cv2.blur(I*I,(win_size,win_size))corr_Ip = cv2.blur(I*p,(win_size,win_size))var_I = corr_I-mean_I*mean_Icov_Ip = corr_Ip - mean_I*mean_pa = cov_Ip/(var_I+eps)b = mean_p-a*mean_Imean_a = cv2.blur(a,(win_size,win_size))mean_b = cv2.blur(b,(win_size,win_size))q = mean_a*I + mean_breturn q
def mopi(img):skin,_ = ellipse_skin_model.YCrCb_ellipse_model(img)#获得皮肤的掩膜数组#进行一次开运算kernel = np.ones((3,3),dtype=np.uint8)skin = cv2.erode(skin,kernel=kernel)skin = cv2.dilate(skin,kernel=kernel)img1 = guided_filter(img/255.0,img/255.0,10,eps=0.001)*255img1 = np.array(img1,dtype=np.uint8)img1 = cv2.bitwise_and(img1,img1,mask=skin)#将皮肤与背景分离skin = cv2.bitwise_not(skin)img1 = cv2.add(img1,cv2.bitwise_and(img,img,mask=skin))#磨皮后的结果与背景叠加# fig, ax = plt.subplots(1, 2)# ax[0].imshow(skin)# ax[1].imshow(img1[:, :, ::-1])# plt.show()return img1
if __name__ == '__main__':img = cv2.imread('img.png')img1 = mopi(img)big_eye(img1,r_max=40,a=0.8)
# -*- coding: utf-8 -*-
# @Time : 2022/10/18 21:54
# @Author : shuoshuo
# @File : ellipse_skin_model.py
# @Project : 美颜
import numpy as np
import cv2def YCrCb_ellipse_model(img):skinCrCbHist = np.zeros((256,256), dtype= np.uint8)cv2.ellipse(skinCrCbHist, (113,155),(23,25), 43, 0, 360, (255,255,255), -1) #绘制椭圆弧线YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB) #转换至YCrCb空间(Y,Cr,Cb) = cv2.split(YCrCb) #拆分出Y,Cr,Cb值skin = np.zeros(Cr.shape, dtype = np.uint8) #掩膜(x,y) = Cr.shapefor i in range(0, x):for j in range(0, y):if skinCrCbHist [Cr[i][j], Cb[i][j]] > 0: #若不在椭圆区间中skin[i][j] = 255res = cv2.bitwise_and(img,img, mask = skin)return skin,res
if __name__ == '__main__':pass
import cv2
import mediapipe as mp
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utilsdef get_face_key_point(img):with mp_face_detection.FaceDetection(model_selection=1, min_detection_confidence=0.5) as face_detection:results = face_detection.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))if not results.detections:return Noneannotated_image = img.copy()r,w,c =img.shapefor detection in results.detections:left_eye =mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.LEFT_EYE)right_eye =mp_face_detection.get_key_point(detection, mp_face_detection.FaceKeyPoint.RIGHT_EYE)left_eye_pos = [int(left_eye.x * w), int(left_eye.y * r)]right_eye_pos = [int(right_eye.x * w), int(right_eye.y * r)]return  left_eye_pos,right_eye_pos
# -*- coding: utf-8 -*-
# @Time : 2022/10/14 19:05
# @Author : shuoshuo
# @File : main.py
# @Project : 美颜
import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import *
from PyQt5.QtCore  import Qt
from PyQt5.QtGui import QPixmap, QImage
from PIL import Image,ImageQt
from qtpy import QtGui
import beautify
import face_detect
class My_win(QWidget):def __init__(self,img):super().__init__()self.initUI(img)def initUI(self,img):self.r_max = 40self.a = 0.5self.left_eye_pos,self.right_eye_pos = face_detect.get_face_key_point(img)self.img = img.copy()self.mopi_btn = QPushButton("磨皮",self)self.big_eye_btn  = QPushButton("大眼",self)self.save_btn = QPushButton("保存",self)self.a_slider =QSlider(Qt.Horizontal)self.a_slider.setRange(1,99)self.a_slider.setSingleStep(5)self.a_slider.setToolTip('大眼程度')self.r_slider = QSlider(Qt.Horizontal)self.r_slider.setRange(1,99)self.r_slider.setSingleStep(5)self.r_slider.setToolTip('大眼范围')self.r_slider.valueChanged.connect(self.r_change)self.a_slider.valueChanged.connect(self.a_change)self.big_eye_btn.clicked.connect(self.big_eye)self.mopi_btn.clicked.connect(self.mopi)self.save_btn.clicked.connect(self.save)self.original_pic = QLabel(self)self.result_pic = QLabel(self)self.button_box = QHBoxLayout()# self.button_box.addStretch(1)self.button_box.addWidget(self.mopi_btn)self.button_box.addWidget(self.big_eye_btn)self.button_box.addWidget(self.save_btn)self.slider_box = QHBoxLayout()self.slider_box.addWidget(self.a_slider)self.slider_box.addWidget(self.r_slider)self.hbox  = QHBoxLayout()self.hbox.addStretch(1)self.hbox.addWidget(self.original_pic)self.hbox.addWidget(self.result_pic)self.vbox = QVBoxLayout()self.vbox.addStretch(1)self.vbox.addLayout(self.button_box)self.vbox.addLayout(self.slider_box)self.vbox.addLayout(self.hbox)self.setLayout(self.vbox )self.show_img(img,frame=self.original_pic)self.show_img(self.img,frame=self.result_pic)# self.setGeometry(100,100,800,600)self.setWindowTitle("拉闸美颜")self.show()def show_img(self,img,frame):r,w,c = img.shapeimage = QtGui.QImage(img.data, w, r,w*3, QtGui.QImage.Format_RGB888).rgbSwapped()frame.setPixmap(QtGui.QPixmap.fromImage(image))# frame.setGeometry(pos[0],pos[1],r,w)def big_eye(self):self.result = beautify.big_eye(self.img,r_max=self.r_max,a=self.a,left_eye_pos=self.left_eye_pos,right_eye_pos=self.right_eye_pos)self.result = np.array(self.result, dtype=np.uint8)self.show_img(self.result, frame=self.result_pic)def mopi(self):self.img = beautify.mopi(self.img)# self.img = np.array(self.img*255,dtype=np.uint8)self.show_img(self.img, frame=self.result_pic)def a_change(self):self.a = self.a_slider.value()/100self.big_eye()def r_change(self):self.r_max = self.r_slider.value()self.big_eye()def save(self):cv2.imwrite('output.jpg',self.result)
if __name__ == '__main__':app = QApplication(sys.argv)img = cv2.imread('img.png')# img = cv2.resize(img,(400,300))ex = My_win(img)sys.exit(app.exec_())

5 参考文献

Interactive Image Warping
Guided Image Filtering

美颜(磨皮,大眼)opencv python实现相关推荐

  1. OpenCV+python:Canny边缘检测算法

    1,边缘处理 图像边缘信息主要集中在高频段,通常说图像锐化或检测边缘,实质就是高频滤波.我们知道微分运算是求信号的变化率,具有加强高频分量的作用. 在空域运算中来说,对图像的锐化就是计算微分.由于数字 ...

  2. OpenCV Python在计算机视觉中的应用

    OpenCV Python教程 在这篇文章中,我们将使用Python中的OpenCv来涵盖计算机视觉的各个方面.OpenCV长期以来一直是软件开发的重要组成部分. 什么是计算机视觉? 我们考虑一个场景 ...

  3. OpenCV Python教程(2、图像元素的访问、通道分离与合并)

    OpenCV Python教程之图像元素的访问.通道分离与合并 转载请详细注明原作者及出处,谢谢! 访问像素 像素的访问和访问numpy中ndarray的方法完全一样,灰度图为: [python] v ...

  4. python中import cv2遇到的错误及安装方法_独家利用OpenCV,Python和Ubidots来构建行人计数器程序(附代码amp;解析)...

    作者:Jose Garcia 翻译:吴振东 校对:张一豪 本文约4000字,建议阅读14分钟. 本文将利用OpenCV,Python和Ubidots来编写一个行人计数器程序,并对代码进行了较为详细的讲 ...

  5. 如何把OpenCV Python获取的图像传递到C层处理

    原文:https://blog.csdn.net/yushulx/article/details/52788051 用OpenCV Python来开发,如果想要用到一些C/C++的图像处理库,就需要创 ...

  6. openCV—Python(6)—— 图像算数与逻辑运算

    openCV-Python(6)-- 图像算数与逻辑运算 一.函数简介 1.add-图像矩阵相加 函数原型:add(src1, src2, dst=None, mask=None, dtype=Non ...

  7. opencv python 图像去噪

    opencv python 图像去噪 文章目录: https://blog.csdn.net/Annihilation7/article/details/82718470 https://segmen ...

  8. opencv python 中cv2.putText()函数的用法

    opencv python 中cv2.putText()函数的用法 文章目录: 一.快速使用 二.官方文档 三.使用举例 虽然用啦很多次,还是决定记录一下 一.快速使用 cv2.putText(ima ...

  9. opencv python全屏显示、置窗口大小和位置

    opencv python全屏显示.设置窗口大小和位置 文章目录: 一.全屏显示图片或视频 二.设置窗口的大小和位置 1.设置窗口的大小 2.设置窗口的位置 一.全屏显示图片或视频 有时我们需要显示图 ...

最新文章

  1. 关于scrollTop为0以及解决方法
  2. DNS_ARP_DHCP协议
  3. 配合OAuth2进行单设备登录拦截
  4. 广度优先搜索——字串变换(洛谷 P1032)
  5. js获取两个数组不同的元素并返回不同元素组成的数组,并对不同的元素添加一个新的属性
  6. 谷歌波浪推行受阻 “医疗云”生不逢时
  7. oracle减法函数mius_Oracle常用函数及其用法
  8. 一款开源且超好用的网站克隆机 HTTrack
  9. 名企真题-警察抓小偷游戏——数学
  10. WPS、EXCEL中输入公式F4插入绝对引用无反应的解决方法
  11. Data Service相关概念
  12. STM32MP157C-DK2->Develop on Arm® Cortex®-A7之 C语言开发LED例程
  13. emuelec 镜像太大无法写入U盘解决方法
  14. 【超详细】使用Oracle VM VirtualBox 搭建一个Linux虚拟机
  15. IE if注释判断( [if gte IE 8] )的解释网上的完全乱七八糟啊!
  16. GoogLeNet: Going deeper with convolutions
  17. 如何把JAR发布到maven中央仓库
  18. 高级语言程序设计 实验报告一:数据文件的读出和数据统计
  19. 【论文翻译】 BMN: Boundary-Matching Network for Temporal Action Proposal Generation
  20. mysql数据库表缩表_不会写复杂的SQL,该怎么学习?

热门文章

  1. HTML5期末大作业:生活类购物商城网站设计——生活类购物商城模板(2页)学生商店网页作品
  2. 小米手机liveplayer安装包_小米直播助手手机版官方下载
  3. CDR X8(CorelDRAW)安装下载不了,由于您已安装了另一版本图文详解教程
  4. simulink仿真 短路分析 含三相直流逆差的电力系统三相短路仿真,程序完整
  5. 南京信息工程大学计算机科学与技术怎么样,作为全国重点的大学,南京信息工程大学是个怎么样的学校?...
  6. wsl安装gpu版mindspore(一)
  7. ppt护理文书流程图_降低护理文书品管圈ppt
  8. HighCharts结构及详细配置(中文对比)
  9. 云计算就业前景怎么样 学完后能胜任什么岗位
  10. Mac使用Boot Camp安装win10(不用U盘)