本次目标比较简单,当时学习代码的时候是其他博主的教程,但是找不到博主链接了, 因为时间有点久远了。
1.学习Python语言和OpenCV,构建开发环境;
2.学习人脸识别算法,能在图片中自动识别人脸;
3.利用图像锐化算法,使得皮肤和头发细节完美呈现;
4.利用图像平滑算法,实现自动磨皮、美白等效果;
5.实现人体增高、瘦脸、美化眼睛等效果;
这里主要使用的有dlib/opencv等第三方库
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(np.float)*(float(x2)-ux)*(float(y2)-uy)
part2 = src[y1, x2].astype(np.float)*(ux-float(x1))*(float(y2)-uy)
part3 = src[y2, x1].astype(np.float) * (float(x2) - ux)*(uy-float(y1))
part4 = src[y2, x2].astype(np.float) * \
(ux-float(x1)) * (uy - float(y1))
insertValue = part1+part2+part3+part4
return insertValue.astype(np.int8)
def landmark_dec_dlib_fun(img_src, detector, predictor):
img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
land_marks = []
rects = detector(img_gray, 0)
for i in range(len(rects)):
land_marks_node = np.matrix(
[[p.x, p.y] for p in predictor(img_gray, rects[i]).parts()])
land_marks.append(land_marks_node)
return land_marks
def face_thin_auto(src, detector, predictor):
landmarks = landmark_dec_dlib_fun(src, detector, predictor)
# 如果未检测到人脸关键点,就不进行瘦脸
if len(landmarks) == 0:
return src
for landmarks_node in landmarks:
left_landmark = landmarks_node[3]
left_landmark_down = landmarks_node[5]
right_landmark = landmarks_node[13]
right_landmark_down = landmarks_node[15]
endPt = landmarks_node[30]
# 计算第4个点到第6个点的距离作为瘦脸距离
r_left = math.sqrt((left_landmark[0, 0]-left_landmark_down[0, 0])*(left_landmark[0, 0]-left_landmark_down[0, 0]) +
(left_landmark[0, 1] - left_landmark_down[0, 1]) * (left_landmark[0, 1] - left_landmark_down[0, 1]))
# 计算第14个点到第16个点的距离作为瘦脸距离
r_right = math.sqrt((right_landmark[0, 0]-right_landmark_down[0, 0])*(right_landmark[0, 0]-right_landmark_down[0, 0]) +
(right_landmark[0, 1] - right_landmark_down[0, 1]) * (right_landmark[0, 1] - right_landmark_down[0, 1]))
# 瘦左边脸
thin_image = localTranslationWarp(
src, left_landmark[0, 0], left_landmark[0, 1], endPt[0, 0], endPt[0, 1], r_left)
# 瘦右边脸
thin_image = localTranslationWarp(
thin_image, right_landmark[0, 0], right_landmark[0, 1], endPt[0, 0], endPt[0, 1], r_right)
return thin_image
AIMakeup.py代码实现
import cv2
import dlib
import numpy as np
import imutils
predictor_path = "./data/shape_predictor_68_face_landmarks.dat"
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(predictor_path)
class NoFace(Exception):
'''
没脸
'''
pass
class Organ():
def __init__(self, im_bgr, im_hsv, temp_bgr, temp_hsv, landmark, name, ksize=None):
'''
五官部位类
'''
self.im_bgr, self.im_hsv, self.landmark, self.name = im_bgr, im_hsv, landmark, name
self.get_rect()
self.shape = (int(self.bottom-self.top), int(self.right-self.left))
self.size = self.shape[0]*self.shape[1]*3
self.move = int(np.sqrt(self.size/3)/20)
self.ksize = self.get_ksize()
self.patch_bgr, self.patch_hsv = self.get_patch(
self.im_bgr), self.get_patch(self.im_hsv)
self.set_temp(temp_bgr, temp_hsv)
self.patch_mask = self.get_mask_re()
pass
def set_temp(self, temp_bgr, temp_hsv):
self.im_bgr_temp, self.im_hsv_temp = temp_bgr, temp_hsv
self.patch_bgr_temp, self.patch_hsv_temp = self.get_patch(
self.im_bgr_temp), self.get_patch(self.im_hsv_temp)
def confirm(self):
'''
确认操作
'''
self.im_bgr[:], self.im_hsv[:] = self.im_bgr_temp[:], self.im_hsv_temp[:]
def update_temp(self):
'''
更新临时图片
'''
self.im_bgr_temp[:], self.im_hsv_temp[:] = self.im_bgr[:], self.im_hsv[:]
def get_ksize(self, rate=15):
size = max([int(np.sqrt(self.size/3)/rate), 1])
size = (size if size % 2 == 1 else size+1)
return (size, size)
def get_rect(self):
'''
获得定位方框
'''
ys, xs = self.landmark[:, 1], self.landmark[:, 0]
self.top, self.bottom, self.left, self.right = np.min(
ys), np.max(ys), np.min(xs), np.max(xs)
def get_patch(self, im):
'''
截取局部切片
'''
shape = im.shape
x = im[np.max([self.top-self.move, 0]):np.min([self.bottom+self.move, shape[0]]),
np.max([self.left-self.move, 0]):np.min([self.right+self.move, shape[1]])]
return x
def _draw_convex_hull(self, im, points, color):
'''
勾画多凸边形
'''
points = cv2.convexHull(points)
cv2.fillConvexPoly(im, points, color=color)
def get_mask_re(self, ksize=None):
'''
获得局部相对坐标遮罩
'''
if ksize == None:
ksize = self.ksize
landmark_re = self.landmark.copy()
landmark_re[:, 1] -= np.max([self.top-self.move, 0])
landmark_re[:, 0] -= np.max([self.left-self.move, 0])
mask = np.zeros(self.patch_bgr.shape[:2], dtype=np.float64)
self._draw_convex_hull(mask,
landmark_re,
color=1)
mask = np.array([mask, mask, mask]).transpose((1, 2, 0))
mask = (cv2.GaussianBlur(mask, ksize, 0) > 0) * 1.0
return cv2.GaussianBlur(mask, ksize, 0)[:]
def get_mask_abs(self, ksize=None):
'''
获得全局绝对坐标遮罩
'''
if ksize == None:
ksize = self.ksize
mask = np.zeros(self.im_bgr.shape, dtype=np.float64)
patch = self.get_patch(mask)
patch[:] = self.patch_mask[:]
return mask
def whitening(self, rate=0.15, confirm=True):
'''
提亮美白
'''
if confirm:
self.confirm()
self.patch_hsv[:, :, -1] = np.minimum(self.patch_hsv[:, :, -1]+self.patch_hsv[:, :, -1]
* self.patch_mask[:, :, -1]*rate, 255).astype('uint8')
self.im_bgr[:] = cv2.cvtColor(self.im_hsv, cv2.COLOR_HSV2BGR)[:]
self.update_temp()
else:
self.patch_hsv_temp[:] = cv2.cvtColor(
self.patch_bgr_temp, cv2.COLOR_BGR2HSV)[:]
self.patch_hsv_temp[:, :, -1] = np.minimum(self.patch_hsv_temp[:, :, -1] +
self.patch_hsv_temp[:, :, -1]*self.patch_mask[:, :, -1]*rate, 255).astype('uint8')
self.patch_bgr_temp[:] = cv2.cvtColor(
self.patch_hsv_temp, cv2.COLOR_HSV2BGR)[:]
def brightening(self, rate=0.3, confirm=True):
'''
提升鲜艳度
'''
patch_mask = self.get_mask_re((1, 1))
if confirm:
self.confirm()
patch_new = self.patch_hsv[:, :, 1]*patch_mask[:, :, 1]*rate
patch_new = cv2.GaussianBlur(patch_new, (3, 3), 0)
self.patch_hsv[:, :, 1] = np.minimum(
self.patch_hsv[:, :, 1]+patch_new, 255).astype('uint8')
self.im_bgr[:] = cv2.cvtColor(self.im_hsv, cv2.COLOR_HSV2BGR)[:]
self.update_temp()
else:
self.patch_hsv_temp[:] = cv2.cvtColor(
self.patch_bgr_temp, cv2.COLOR_BGR2HSV)[:]
patch_new = self.patch_hsv_temp[:, :, 1]*patch_mask[:, :, 1]*rate
patch_new = cv2.GaussianBlur(patch_new, (3, 3), 0)
self.patch_hsv_temp[:, :, 1] = np.minimum(
self.patch_hsv[:, :, 1]+patch_new, 255).astype('uint8')
self.patch_bgr_temp[:] = cv2.cvtColor(
self.patch_hsv_temp, cv2.COLOR_HSV2BGR)[:]
def smooth(self, rate=0.6, ksize=(7, 7), confirm=True):
'''
磨皮
'''
if ksize == None:
ksize = self.get_ksize(80)
index = self.patch_mask > 0
if confirm:
self.confirm()
patch_new = cv2.GaussianBlur(cv2.bilateralFilter(
self.patch_bgr, 3, *ksize), ksize, 0)
self.patch_bgr[index] = np.minimum(
rate*patch_new[index]+(1-rate)*self.patch_bgr[index], 255).astype('uint8')
self.im_hsv[:] = cv2.cvtColor(self.im_bgr, cv2.COLOR_BGR2HSV)[:]
self.update_temp()
else:
patch_new = cv2.GaussianBlur(cv2.bilateralFilter(
self.patch_bgr_temp, 3, *ksize), ksize, 0)
self.patch_bgr_temp[index] = np.minimum(
rate*patch_new[index]+(1-rate)*self.patch_bgr_temp[index], 255).astype('uint8')
self.patch_hsv_temp[:] = cv2.cvtColor(
self.patch_bgr_temp, cv2.COLOR_BGR2HSV)[:]
def sharpen(self, rate=0.3, confirm=True):
'''
锐化
'''
patch_mask = self.get_mask_re((3, 3))
kernel = np.zeros((9, 9), np.float32)
kernel[4, 4] = 2.0 # Identity, times two!
# Create a box filter:
boxFilter = np.ones((9, 9), np.float32) / 81.0
# Subtract the two:
kernel = kernel - boxFilter
index = patch_mask > 0
if confirm:
self.confirm()
sharp = cv2.filter2D(self.patch_bgr, -1, kernel)
self.patch_bgr[index] = np.minimum(
((1-rate)*self.patch_bgr)[index]+sharp[index]*rate, 255).astype('uint8')
self.update_temp()
else:
sharp = cv2.filter2D(self.patch_bgr_temp, -1, kernel)
self.patch_bgr_temp[:] = np.minimum(
self.patch_bgr_temp+self.patch_mask*sharp*rate, 255).astype('uint8')
self.patch_hsv_temp[:] = cv2.cvtColor(
self.patch_bgr_temp, cv2.COLOR_BGR2HSV)[:]
class Forehead(Organ):
def __init__(self, im_bgr, im_hsv, temp_bgr, temp_hsv, landmark, mask_organs, name, ksize=None):
self.mask_organs = mask_organs
super(Forehead, self).__init__(im_bgr, im_hsv,
temp_bgr, temp_hsv, landmark, name, ksize)
def get_mask_re(self, ksize=None):
'''
获得局部相对坐标遮罩
'''
if ksize == None:
ksize = self.ksize
landmark_re = self.landmark.copy()
landmark_re[:, 1] -= np.max([self.top-self.move, 0])
landmark_re[:, 0] -= np.max([self.left-self.move, 0])
mask = np.zeros(self.patch_bgr.shape[:2], dtype=np.float64)
self._draw_convex_hull(mask,
landmark_re,
color=1)
mask = np.array([mask, mask, mask]).transpose((1, 2, 0))
mask = (cv2.GaussianBlur(mask, ksize, 0) > 0) * 1.0
patch_organs = self.get_patch(self.mask_organs)
mask = cv2.GaussianBlur(mask, ksize, 0)[:]
mask[patch_organs > 0] = (1-patch_organs[patch_organs > 0])
return mask
class Face(Organ):
'''
脸类
'''
def __init__(self, im_bgr, img_hsv, temp_bgr, temp_hsv, landmarks, index):
self.index = index
# 五官名称
self.organs_name = ['jaw', 'mouth', 'nose',
'left eye', 'right eye', 'left brow', 'right brow']
# 五官等标记点
self.organs_points = [list(range(0, 17)), list(range(48, 61)), list(range(27, 35)), list(
range(42, 48)), list(range(36, 42)), list(range(22, 27)), list(range(17, 22))]
# 实例化脸对象和五官对象
self.organs = {name: Organ(im_bgr, img_hsv, temp_bgr, temp_hsv, landmarks[points], name) for name, points in zip(
self.organs_name, self.organs_points)}
# 获得额头坐标,实例化额头
mask_nose = self.organs['nose'].get_mask_abs()
mask_organs = (self.organs['mouth'].get_mask_abs()+mask_nose+self.organs['left eye'].get_mask_abs(
)+self.organs['right eye'].get_mask_abs()+self.organs['left brow'].get_mask_abs()+self.organs['right brow'].get_mask_abs())
forehead_landmark = self.get_forehead_landmark(
im_bgr, landmarks, mask_organs, mask_nose)
self.organs['forehead'] = Forehead(
im_bgr, img_hsv, temp_bgr, temp_hsv, forehead_landmark, mask_organs, 'forehead')
mask_organs += self.organs['forehead'].get_mask_abs()
# 人脸的完整标记点
self.FACE_POINTS = np.concatenate([landmarks, forehead_landmark])
super(Face, self).__init__(im_bgr, img_hsv,
temp_bgr, temp_hsv, self.FACE_POINTS, 'face')
mask_face = self.get_mask_abs()-mask_organs
self.patch_mask = self.get_patch(mask_face)
pass
def get_forehead_landmark(self, im_bgr, face_landmark, mask_organs, mask_nose):
'''
计算额头坐标
'''
# 画椭圆
radius = (np.linalg.norm(
face_landmark[0]-face_landmark[16])/2).astype('int32')
center_abs = tuple(
((face_landmark[0]+face_landmark[16])/2).astype('int32'))
angle = np.degrees(np.arctan(
(lambda l: l[1]/l[0])(face_landmark[16]-face_landmark[0]))).astype('int32')
mask = np.zeros(mask_organs.shape[:2], dtype=np.float64)
cv2.ellipse(mask, center_abs, (radius, radius), angle, 180, 360, 1, -1)
# 剔除与五官重合部分
mask[mask_organs[:, :, 0] > 0] = 0
# 根据鼻子的肤色判断真正的额头面积
index_bool = []
for ch in range(3):
mean, std = np.mean(im_bgr[:, :, ch][mask_nose[:, :, ch] > 0]), np.std(
im_bgr[:, :, ch][mask_nose[:, :, ch] > 0])
up, down = mean+0.5*std, mean-0.5*std
index_bool.append((im_bgr[:, :, ch] < down)
| (im_bgr[:, :, ch] > up))
index_zero = (
(mask > 0) & index_bool[0] & index_bool[1] & index_bool[2])
mask[index_zero] = 0
index_abs = np.array(np.where(mask > 0)[::-1]).transpose()
landmark = cv2.convexHull(index_abs).squeeze()
return landmark
class Makeup():
'''
化妆器
'''
def __init__(self, predictor_path="./data/shape_predictor_68_face_landmarks.dat"):
self.photo_path = []
self.PREDICTOR_PATH = predictor_path
self.faces = {}
# 人脸定位、特征提取器,来自dlib
self.detector = detector
self.predictor = predictor
def get_faces(self, im_bgr, im_hsv, temp_bgr, temp_hsv, name, n=1):
'''
人脸定位和特征提取,定位到两张及以上脸或者没有人脸将抛出异常
im:
照片的numpy数组
fname:
照片名字的字符串
返回值:
人脸特征(x,y)坐标的矩阵
'''
rects = self.detector(im_bgr, 1)
if len(rects) < 1:
raise NoFace('Too many faces in '+name)
return {name: [Face(im_bgr, im_hsv, temp_bgr, temp_hsv, np.array([[p.x, p.y] for p in self.predictor(im_bgr, rect).parts()]), i) for i, rect in enumerate(rects)]}
def read_im(self, fname, scale=1):
'''
读取图片
'''
im = cv2.imdecode(np.fromfile(fname, dtype=np.uint8), -1)
im = imutils.resize(im, width=600)
if type(im) == type(None):
raise ValueError(
'Opencv error reading image "{}" , got None'.format(fname))
return im
def read_and_mark(self, fname):
im_bgr = self.read_im(fname)
im_hsv = cv2.cvtColor(im_bgr, cv2.COLOR_BGR2HSV)
temp_bgr, temp_hsv = im_bgr.copy(), im_hsv.copy()
return im_bgr, temp_bgr, self.get_faces(im_bgr, im_hsv, temp_bgr, temp_hsv, fname)
utils.py代码实现
import dlib
import cv2
import numpy as np
import math
def SharpenImage(src):
blur_img = cv2.GaussianBlur(src, (0, 0), 5)
usm = cv2.addWeighted(src, 1.5, blur_img, -0.5, 0)
return usm
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(np.float)*(float(x2)-ux)*(float(y2)-uy)
part2 = src[y1, x2].astype(np.float)*(ux-float(x1))*(float(y2)-uy)
part3 = src[y2, x1].astype(np.float) * (float(x2) - ux)*(uy-float(y1))
part4 = src[y2, x2].astype(np.float) * \
(ux-float(x1)) * (uy - float(y1))
insertValue = part1+part2+part3+part4
return insertValue.astype(np.int8)
def localTranslationWarp(srcImg, startX, startY, endX, endY, radius):
ddradius = float(radius * radius)
copyImg = np.zeros(srcImg.shape, np.uint8)
copyImg = srcImg.copy()
# 计算公式中的|m-c|^2
ddmc = (endX - startX) * (endX - startX) + \
(endY - startY) * (endY - startY)
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
distance = (i - startX) * (i - startX) + \
(j - startY) * (j - startY)
if(distance < ddradius):
# 计算出(i,j)坐标的原坐标
# 计算公式中右边平方号里的部分
ratio = (ddradius-distance) / (ddradius - distance + ddmc)
ratio = ratio * ratio
# 映射原位置
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 landmark_dec_dlib_fun(img_src, detector, predictor):
img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
land_marks = []
rects = detector(img_gray, 0)
for i in range(len(rects)):
land_marks_node = np.matrix(
[[p.x, p.y] for p in predictor(img_gray, rects[i]).parts()])
land_marks.append(land_marks_node)
return land_marks
def face_thin_auto(src, detector, predictor):
landmarks = landmark_dec_dlib_fun(src, detector, predictor)
# 如果未检测到人脸关键点,就不进行瘦脸
if len(landmarks) == 0:
return src
for landmarks_node in landmarks:
left_landmark = landmarks_node[3]
left_landmark_down = landmarks_node[5]
right_landmark = landmarks_node[13]
right_landmark_down = landmarks_node[15]
endPt = landmarks_node[30]
# 计算第4个点到第6个点的距离作为瘦脸距离
r_left = math.sqrt((left_landmark[0, 0]-left_landmark_down[0, 0])*(left_landmark[0, 0]-left_landmark_down[0, 0]) +
(left_landmark[0, 1] - left_landmark_down[0, 1]) * (left_landmark[0, 1] - left_landmark_down[0, 1]))
# 计算第14个点到第16个点的距离作为瘦脸距离
r_right = math.sqrt((right_landmark[0, 0]-right_landmark_down[0, 0])*(right_landmark[0, 0]-right_landmark_down[0, 0]) +
(right_landmark[0, 1] - right_landmark_down[0, 1]) * (right_landmark[0, 1] - right_landmark_down[0, 1]))
# 瘦左边脸
thin_image = localTranslationWarp(
src, left_landmark[0, 0], left_landmark[0, 1], endPt[0, 0], endPt[0, 1], r_left)
# 瘦右边脸
thin_image = localTranslationWarp(
thin_image, right_landmark[0, 0], right_landmark[0, 1], endPt[0, 0], endPt[0, 1], r_right)
return thin_image
MakupGUI.py代码实现
import sys
sys.path.append('.')
sys.path.append('raw')
import os
import numpy as np
import cv2
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QFileDialog, QMessageBox
from AIMakeup import Makeup, detector, predictor
from utils import face_thin_auto, SharpenImage
# 1.学习Python语言和OpenCV,构建开发环境;
# 2.学习人脸识别算法,能在图片中自动识别人脸;
# 3.利用图像锐化算法,使得皮肤和头发细节完美呈现;
# 4.利用图像平滑算法,实现自动磨皮、美白等效果;
# 5.实现人体增高、瘦脸、美化眼睛等效果;
class Ui_MainWindow(object):
def __init__(self, MainWindow):
self.window = MainWindow
self._setupUi()
# 控件分组
self.bg_edit = [self.bt_brightening,
self.bt_whitening, self.bt_sharpen, self.bt_smooth,
self.bt_Laplace, self.bt_Thin]
self.bg_op = [self.bt_confirm, self.bt_cancel, self.bt_reset]
self.bg_result = [self.bt_view_compare,
self.bt_save, self.bt_save_compare]
self.sls = [self.sl_brightening, self.sl_sharpen,
self.sl_whitening, self.sl_smooth,
self.sl_Laplace, self.sl_Thin]
# 用于显示图片的标签
self.label = QtWidgets.QLabel(self.window)
self.sa.setWidget(self.label)
# 批量设置状态
self._set_statu(self.bg_edit, False)
self._set_statu(self.bg_op, False)
self._set_statu(self.bg_result, False)
self._set_statu(self.sls, False)
# 导入dlib模型文件
if os.path.exists("./data/shape_predictor_68_face_landmarks.dat"):
self.path_predictor = os.path.abspath(
"./data/shape_predictor_68_face_landmarks.dat")
else:
QMessageBox.warning(self.centralWidget, '警告', '默认的dlib模型文件路径不存在,请指定文件位置。\
\n或从http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2下载')
self.path_predictor, _ = QFileDialog.getOpenFileName(
self.centralWidget, '选择dlib模型文件', './', 'Data Files(*.dat)')
# 实例化化妆器
self.mu = Makeup(self.path_predictor)
self.path_img = ''
self._set_connect()
def _set_connect(self):
'''
设置程序逻辑
'''
self.bt_open.clicked.connect(self._open_img)
OPs = ['sharpen', 'whitening', 'smooth', 'brightening',
'Laplace', 'Thin', 'cancel', 'confirm', 'reset',
'save', 'save_compare', 'view_compare']
for op in OPs:
self.__getattribute__(
'bt_'+op).clicked.connect(self.__getattribute__('_'+op))
def _open_img(self):
'''
打开图片
'''
self.path_img, _ = QFileDialog.getOpenFileName(
self.centralWidget, '打开图片文件', './', 'Image Files(*.png *.jpg *.bmp)')
if self.path_img and os.path.exists(self.path_img):
print(self.path_img)
self.im_bgr, self.temp_bgr, self.faces = self.mu.read_and_mark(
self.path_img)
self.im_ori, self.previous_bgr = self.im_bgr.copy(), self.im_bgr.copy()
self._set_statu(self.bg_edit, True)
self._set_statu(self.bg_op, True)
self._set_statu(self.bg_result, True)
self._set_statu(self.sls, True)
self._set_img()
else:
QMessageBox.warning(self.centralWidget, '无效路径', '无效路径,请重新选择!')
def _cv2qimg(self, cvImg):
'''
将opencv的图片转换为QImage
'''
height, width, channel = cvImg.shape
bytesPerLine = 3 * width
image2show = QImage(cv2.cvtColor(cvImg, cv2.COLOR_BGR2RGB).data,
width, height, bytesPerLine, QImage.Format_RGB888)
return image2show
def _set_img(self):
'''
显示pixmap
'''
self.label.setPixmap(QPixmap.fromImage(self._cv2qimg(self.temp_bgr)))
def _set_statu(self, group, value):
'''
批量设置状态
'''
[item.setEnabled(value) for item in group]
def _confirm(self):
'''
确认操作
'''
self.im_bgr[:] = self.temp_bgr[:]
def _cancel(self):
'''
还原到上一步
'''
self.temp_bgr[:] = self.previous_bgr[:]
self._set_img()
def _reset(self):
'''
重置为原始图片
'''
self.temp_bgr[:] = self.im_ori[:]
self._set_img()
def _mapfaces(self, fun, value):
'''
对每张脸进行迭代操作
'''
self.previous_bgr[:] = self.temp_bgr[:]
for face in self.faces[self.path_img]:
fun(face, value)
self._set_img()
def _Laplace(self):
value = min(1, max(self.sl_Laplace.value()/200, 0))
kernel = np.array([[0, -1, 0], [0, 5, 0], [0, -1, 0]])
print('-[INFO] laplace:', value)
self.previous_bgr[:] = self.temp_bgr[:]
self.temp_bgr = SharpenImage(self.temp_bgr)
# self.temp_bgr = cv2.filter2D(self.temp_bgr, -1, kernel)
self.temp_bgr = np.minimum(self.temp_bgr, 255).astype('uint8')
self.im_bgr = self.temp_bgr
self._set_img()
def _Thin(self):
value = min(1, max(self.sl_Thin.value()/100, 0))
print('-[INFO] thin:', value)
self.previous_bgr[:] = self.temp_bgr[:]
self.temp_bgr = face_thin_auto(self.temp_bgr, detector, predictor)
self.im_bgr = self.temp_bgr
self._set_img()
def _sharpen(self):
value = min(1, max(self.sl_sharpen.value()/200, 0))
print('-[INFO] sharpen:', value)
def fun(face, value):
face.organs['left eye'].sharpen(value, confirm=False)
face.organs['right eye'].sharpen(value, confirm=False)
self._mapfaces(fun, value)
def _whitening(self):
value = min(1, max(self.sl_whitening.value()/200, 0))
print('-[INFO] whitening:', value)
def fun(face, v):
face.organs['left eye'].whitening(value, confirm=False)
face.organs['right eye'].whitening(value, confirm=False)
face.organs['left brow'].whitening(value, confirm=False)
face.organs['right brow'].whitening(value, confirm=False)
face.organs['nose'].whitening(value, confirm=False)
face.organs['forehead'].whitening(value, confirm=False)
face.organs['mouth'].whitening(value, confirm=False)
face.whitening(value, confirm=False)
self._mapfaces(fun, value)
def _brightening(self):
value = min(1, max(self.sl_brightening.value()/200, 0))
print('-[INFO] brightening:', value)
def fun(face, value):
face.organs['mouth'].brightening(value, confirm=False)
self._mapfaces(fun, value)
def _smooth(self):
value = min(1, max(self.sl_smooth.value()/100, 0))
print('-[INFO] smooth:', value)
def fun(face, value):
face.smooth(value, confirm=False)
face.organs['nose'].smooth(value*2/3, confirm=False)
face.organs['forehead'].smooth(value*3/2, confirm=False)
face.organs['mouth'].smooth(value, confirm=False)
self._mapfaces(fun, value)
def _save(self):
output_path, _ = QFileDialog.getSaveFileName(
self.centralWidget, '选择保存位置', './', 'Image Files(*.png *.jpg *.bmp)')
if output_path:
self.save(output_path, self.im_bgr)
else:
QMessageBox.warning(self.centralWidget, '无效路径', '无效路径,请重新选择!')
def _save_compare(self):
output_path, _ = QFileDialog.getSaveFileName(
self.centralWidget, '选择保存位置', './', 'Image Files(*.png *.jpg *.bmp)')
if output_path:
self.save(output_path, np.concatenate(
[self.im_ori, self.im_bgr], 1))
else:
QMessageBox.warning(self.centralWidget, '无效路径', '无效路径,请重新选择!')
def _view_compare(self):
cv2.imshow('Compare', np.concatenate([self.im_ori, self.im_bgr], 1))
cv2.waitKey()
def _setupUi(self):
self.window.setObjectName("MainWindow")
self.window.resize(837, 838)
self.centralWidget = QtWidgets.QWidget(self.window)
self.centralWidget.setObjectName("centralWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralWidget)
self.verticalLayout.setObjectName("verticalLayout")
self.sa = QtWidgets.QScrollArea(self.centralWidget)
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sa.sizePolicy().hasHeightForWidth())
self.sa.setSizePolicy(sizePolicy)
self.sa.setWidgetResizable(True)
self.sa.setObjectName("sa")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 813, 532))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.sa.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout.addWidget(self.sa)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.bt_whitening = QtWidgets.QPushButton(self.centralWidget)
self.bt_whitening.setObjectName("bt_whitening")
self.gridLayout.addWidget(self.bt_whitening, 0, 0, 1, 1)
self.sl_whitening = QtWidgets.QSlider(self.centralWidget)
self.sl_whitening.setOrientation(QtCore.Qt.Horizontal)
self.sl_whitening.setObjectName("sl_whitening")
self.gridLayout.addWidget(self.sl_whitening, 0, 1, 1, 1)
spacerItem = QtWidgets.QSpacerItem(
40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem, 0, 2, 1, 1)
self.bt_smooth = QtWidgets.QPushButton(self.centralWidget)
self.bt_smooth.setObjectName("bt_smooth")
self.gridLayout.addWidget(self.bt_smooth, 1, 0, 1, 1)
self.sl_smooth = QtWidgets.QSlider(self.centralWidget)
self.sl_smooth.setOrientation(QtCore.Qt.Horizontal)
self.sl_smooth.setObjectName("sl_smooth")
self.gridLayout.addWidget(self.sl_smooth, 1, 1, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(
40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem1, 1, 2, 1, 1)
self.bt_sharpen = QtWidgets.QPushButton(self.centralWidget)
self.bt_sharpen.setObjectName("bt_sharpen")
self.gridLayout.addWidget(self.bt_sharpen, 2, 0, 1, 1)
self.sl_sharpen = QtWidgets.QSlider(self.centralWidget)
self.sl_sharpen.setOrientation(QtCore.Qt.Horizontal)
self.sl_sharpen.setObjectName("sl_sharpen")
self.gridLayout.addWidget(self.sl_sharpen, 2, 1, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(
40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem2, 2, 2, 1, 1)
self.bt_brightening = QtWidgets.QPushButton(self.centralWidget)
self.bt_brightening.setObjectName("bt_brightening")
self.gridLayout.addWidget(self.bt_brightening, 3, 0, 1, 1)
self.sl_brightening = QtWidgets.QSlider(self.centralWidget)
self.sl_brightening.setOrientation(QtCore.Qt.Horizontal)
self.sl_brightening.setObjectName("sl_brightening")
self.gridLayout.addWidget(self.sl_brightening, 3, 1, 1, 1)
spacerItem3 = QtWidgets.QSpacerItem(
40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem3, 3, 2, 1, 1)
self.bt_Laplace = QtWidgets.QPushButton(self.centralWidget)
self.bt_Laplace.setObjectName("bt_brightening")
self.gridLayout.addWidget(self.bt_Laplace, 4, 0, 1, 1)
self.sl_Laplace = QtWidgets.QSlider(self.centralWidget)
self.sl_Laplace.setOrientation(QtCore.Qt.Horizontal)
self.sl_Laplace.setObjectName("sl_Laplace")
self.gridLayout.addWidget(self.sl_Laplace, 4, 1, 1, 1)
spacerItem4 = QtWidgets.QSpacerItem(
40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem4, 4, 2, 1, 1)
self.bt_Thin = QtWidgets.QPushButton(self.centralWidget)
self.bt_Thin.setObjectName("bt_brightening")
self.gridLayout.addWidget(self.bt_Thin, 5, 0, 1, 1)
self.sl_Thin = QtWidgets.QSlider(self.centralWidget)
self.sl_Thin.setOrientation(QtCore.Qt.Horizontal)
self.sl_Thin.setObjectName("sl_Thin")
self.gridLayout.addWidget(self.sl_Thin, 5, 1, 1, 1)
spacerItem5 = QtWidgets.QSpacerItem(
40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem5, 5, 2, 1, 1)
self.bt_open = QtWidgets.QPushButton(self.centralWidget)
self.bt_open.setObjectName("bt_open")
self.gridLayout.addWidget(self.bt_open, 6, 0, 1, 1)
self.bt_confirm = QtWidgets.QPushButton(self.centralWidget)
self.bt_confirm.setObjectName("bt_confirm")
self.gridLayout.addWidget(self.bt_confirm, 7, 0, 1, 1)
self.bt_cancel = QtWidgets.QPushButton(self.centralWidget)
self.bt_cancel.setObjectName("bt_cancel")
self.gridLayout.addWidget(self.bt_cancel, 7, 1, 1, 1)
self.bt_reset = QtWidgets.QPushButton(self.centralWidget)
self.bt_reset.setObjectName("bt_reset")
self.gridLayout.addWidget(self.bt_reset, 7, 2, 1, 1)
self.bt_view_compare = QtWidgets.QPushButton(self.centralWidget)
self.bt_view_compare.setObjectName("bt_view_compare")
self.gridLayout.addWidget(self.bt_view_compare, 8, 0, 1, 1)
self.bt_save = QtWidgets.QPushButton(self.centralWidget)
self.bt_save.setObjectName("bt_save")
self.gridLayout.addWidget(self.bt_save, 9, 1, 1, 1)
self.bt_save_compare = QtWidgets.QPushButton(self.centralWidget)
self.bt_save_compare.setObjectName("bt_save_compare")
self.gridLayout.addWidget(self.bt_save_compare, 9, 2, 1, 1)
self.verticalLayout.addLayout(self.gridLayout)
self.window.setCentralWidget(self.centralWidget)
self.retranslateUi()
QtCore.QMetaObject.connectSlotsByName(self.window)
def save(self, output_path, output_im):
'''
保存图片
'''
cv2.imencode('.jpg', output_im)[1].tofile(output_path)
def retranslateUi(self):
_translate = QtCore.QCoreApplication.translate
self.window.setWindowTitle(_translate("MainWindow", "AI美颜"))
self.bt_whitening.setText(_translate("MainWindow", "美白"))
self.bt_smooth.setText(_translate("MainWindow", "磨皮"))
self.bt_sharpen.setText(_translate("MainWindow", "亮眼"))
self.bt_brightening.setText(_translate("MainWindow", "红唇"))
self.bt_Laplace.setText(_translate("MainWindow", "锐化"))
self.bt_Thin.setText(_translate("MainWindow", "瘦脸"))
self.bt_open.setText(_translate("MainWindow", "打开文件"))
self.bt_confirm.setText(_translate("MainWindow", "确认更改"))
self.bt_cancel.setText(_translate("MainWindow", "撤销更改"))
self.bt_reset.setText(_translate("MainWindow", "还原"))
self.bt_view_compare.setText(_translate("MainWindow", "查看对比"))
self.bt_save.setText(_translate("MainWindow", "保存"))
self.bt_save_compare.setText(_translate("MainWindow", "保存对比图"))
if __name__ == "__main__":
import qdarkstyle
app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow(MainWindow)
ui.window.show()
sys.exit(app.exec_())