基于python+opencv+dlib实现人脸修正效果【附源码】

文章目录

  • 前言
  • 1.目录结构
  • 2.具体功能代码
    • 1.双线性插值法
    • 2.人脸关键点检测
    • 3.瘦脸代码
  • 总结【完整代码】


前言

本次目标比较简单,当时学习代码的时候是其他博主的教程,但是找不到博主链接了, 因为时间有点久远了。

1.学习Python语言和OpenCV,构建开发环境;
2.学习人脸识别算法,能在图片中自动识别人脸;
3.利用图像锐化算法,使得皮肤和头发细节完美呈现;
4.利用图像平滑算法,实现自动磨皮、美白等效果;
5.实现人体增高、瘦脸、美化眼睛等效果;

这里主要使用的有dlib/opencv等第三方库


1.目录结构

在这里插入图片描述

2.具体功能代码

1.双线性插值法

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)

2.人脸关键点检测

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

3.瘦脸代码

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_())

效果如下:
基于python+opencv+dlib实现人脸修正效果【附源码】_第1张图片

你可能感兴趣的:(人工智能-人脸识别,python,opencv,计算机视觉)