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

该文章介绍了一个使用Python和OpenCV库进行脸部识别和图像处理的项目,包括了人脸关键点检测、双线性插值法、瘦脸算法以及图像美白、磨皮等美颜效果的实现。通过dlib库进行人脸检测,然后应用特定的图像变换来达到美容效果,如自动磨皮、美白和瘦脸。
摘要由CSDN通过智能技术生成


前言

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

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

效果如下:
在这里插入图片描述

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
人脸识别门禁系统是一种基于人脸识别技术的智能门禁系统,其可通过对人脸进行采集、识别和比对,实现对门禁的控制和管理。本文将详细阐述基于python+openCV+dlib+mysql的人脸识别门禁系统的设计与实现。 一、技术选型 本系统主要采用以下技术: 1. Python:作为主要编程语言,用于实现整个系统的逻辑控制和算法设计。 2. OpenCV:作为图像处理库,用于实现人脸检测、特征提取和人脸识别等核心功能。 3. Dlib:作为人脸识别库,用于实现人脸特征点检测和人脸识别等功能。 4. MySQL:作为数据库系统,用于存储人脸特征和相关信息。 二、系统设计 本系统主要包括以下功能模块: 1. 人脸采集模块:用于采集用户的人脸图像,并将其存储到本地或远程数据库中。 2. 人脸检测模块:用于检测人脸区域,提取人脸特征,并将其存储到数据库中。 3. 人脸识别模块:用于识别用户的人脸特征,并与数据库中的人脸特征进行比对,以确定用户身份。 4. 门禁控制模块:根据用户身份结果,控制门禁的开关。 5. 数据库管理模块:用于管理数据库中的人脸特征和相关信息。 三、系统实现 1. 人脸采集模块 人脸采集模块主要是通过摄像头对用户的人脸进行拍摄和保存。代码如下: ```python import cv2 cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() cv2.imshow("capture", frame) if cv2.waitKey(1) & 0xFF == ord('q'): #按q键退出 cv2.imwrite("face.jpg", frame) #保存人脸图像 break cap.release() cv2.destroyAllWindows() ``` 2. 人脸检测模块 人脸检测模块主要是通过OpenCV中的CascadeClassifier类进行人脸检测,再通过Dlib中的shape_predictor类进行人脸特征点检测和特征提取。代码如下: ```python import cv2 import dlib detector = dlib.get_frontal_face_detector() #人脸检测器 predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") #特征点检测器 img = cv2.imread("face.jpg") #读取人脸图像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #转换为灰度图像 faces = detector(gray, 0) #检测人脸 for face in faces: landmarks = predictor(gray, face) #检测特征点 for n in range(68): x = landmarks.part(n).x y = landmarks.part(n).y cv2.circle(img, (x, y), 2, (0, 255, 0), -1) #绘制特征点 cv2.imshow("face", img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 3. 人脸识别模块 人脸识别模块主要是通过Dlib中的face_recognition类进行人脸特征提取和比对。代码如下: ```python import face_recognition known_image = face_recognition.load_image_file("known_face.jpg") #读取已知的人脸图像 unknown_image = face_recognition.load_image_file("unknown_face.jpg") #读取待识别的人脸图像 known_encoding = face_recognition.face_encodings(known_image)[0] #提取已知人脸的特征 unknown_encoding = face_recognition.face_encodings(unknown_image)[0] #提取待识别人脸的特征 results = face_recognition.compare_faces([known_encoding], unknown_encoding) #比对人脸特征 if results[0]: print("Match") else: print("No match") ``` 4. 门禁控制模块 门禁控制模块主要是通过GPIO控制门禁的开关。代码如下: ```python import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) GPIO.output(11, GPIO.HIGH) #开门 time.sleep(5) #等待5秒 GPIO.output(11, GPIO.LOW) #关门 GPIO.cleanup() #清理GPIO资源 ``` 5. 数据库管理模块 数据库管理模块主要是通过MySQLdb模块实现对MySQL数据库的连接和操作,包括新建数据库、新建表、插入数据、查询数据等。代码如下: ```python import MySQLdb #连接数据库 conn = MySQLdb.connect(host="localhost", user="root", passwd="123456", db="test", charset="utf8") #新建表 cursor = conn.cursor() sql = "CREATE TABLE `face` (`id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `encoding` text NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;" cursor.execute(sql) #插入数据 name = "张三" encoding = "0.1,0.2,0.3,0.4" sql = "INSERT INTO `face` (`name`, `encoding`) VALUES (%s, %s)" cursor.execute(sql, (name, encoding)) conn.commit() #查询数据 sql = "SELECT * FROM `face` WHERE `name`=%s" cursor.execute(sql, (name,)) result = cursor.fetchone() print(result) cursor.close() conn.close() ``` 四、总结 本文主要介绍了基于python+openCV+dlib+mysql的人脸识别门禁系统的设计与实现。该系统主要采用了Python作为主要编程语言,OpenCVDlib作为图像处理和人脸识别库,MySQL作为数据库系统。通过对这些技术的应用,实现人脸采集、检测、识别和门禁控制等核心功能。该系统可以应用于各类场景的门禁控制和身份验证,具有较高的实用价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的广东仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值