├── For show.png ├── data ├── man1.jpg ├── man2.jpg ├── woman1.jpg └── woman2.jpg ├── save ├── save.jpg └── save.png ├── .gitattributes ├── README.md └── Openimg ├── first.py ├── first.ui └── head.py /For show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailaisun/face_gui/HEAD/For show.png -------------------------------------------------------------------------------- /data/man1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailaisun/face_gui/HEAD/data/man1.jpg -------------------------------------------------------------------------------- /data/man2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailaisun/face_gui/HEAD/data/man2.jpg -------------------------------------------------------------------------------- /save/save.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailaisun/face_gui/HEAD/save/save.jpg -------------------------------------------------------------------------------- /save/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailaisun/face_gui/HEAD/save/save.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /data/woman1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailaisun/face_gui/HEAD/data/woman1.jpg -------------------------------------------------------------------------------- /data/woman2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailaisun/face_gui/HEAD/data/woman2.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # face_gui 2 | ## 项目介绍 3 | 该项目能够从给定的正面照片中,自动识别脸部区域,并对图像前景进行提取分割,然后替换背景,并按照证件照的规格进行规范化处理。一共由5个模块组成:
4 | 5 | ### 1. 头部局部照识别与截取模块设计 6 | 利用Vahid Kazemi 和 Josephine Sullivan提出的基于gradient boosting的回归树算法检测面部的68个关键点位置.
7 | [论文:One Millisecond Face Alignment with an Ensemble of Regression Trees](http://www.nada.kth.se/~vahidk/face_ert.html)
8 | 9 | ### 2. 图像前景分割
10 | 利用opencv中的grabcut方法对人脸周围区域进行分割,将人头和衣服等前景分离出来.
11 | [论文:GrabCut” — Interactive Foreground Extraction using Iterated Graph Cuts](https://wenku.baidu.com/view/4b8db16a58fafab069dc0292.html)
12 | 13 | ### 3. 证件照规范化 14 | 按照规格进行图片处理:分辨率:361×381,分辨率96dpi,位深度24,大小30k左右.
15 | 16 | ### 4. 背景替换 17 | 根据图片的背景颜色特征进行替换背景(蓝-红-白),如蓝背景变为红背景:将BGR图像转为HSV图像,蓝颜色H通道在78和110之间,然后转换通道将这些像素替换为(0,0,255)即可。
18 | 19 | ### 5. 界面设计 20 | 利用pyqt5进行界面设计.
21 | 22 | ## 项目配置 23 | ### 环境平台
24 | python3
25 | 26 | ### 模块安装
27 | pip install opencv-python 28 | pip install pyqt5 29 | pip install dlib 30 | 31 | ### 文件下载
32 | [shape_predictor_68_face_landmarks.dat.bz2](http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2)下载后放入data目录中
33 | 34 | ### 脚本运行
35 | python head.py 36 | 37 | ## 项目展示 38 | ![image](https://github.com/kailaisun/face_gui/blob/master/For%20show.png) 39 | -------------------------------------------------------------------------------- /Openimg/first.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'first.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9.2 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | # from PyQt5.QtCore import QCoreApplication 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(1127, 879) 16 | self.pushButton = QtWidgets.QPushButton(Form) 17 | self.pushButton.setGeometry(QtCore.QRect(80, 90, 100, 60)) 18 | self.pushButton.setObjectName("pushButton") 19 | self.label = QtWidgets.QLabel(Form) 20 | self.label.setGeometry(QtCore.QRect(230, 40, 361, 381)) 21 | self.label.setStyleSheet("QLabel{\n" 22 | " border-color: rgb(255, 170,0);\n" 23 | " border-width: 1px;\n" 24 | " border-style: solid;\n" 25 | "}") 26 | self.label.setText("") 27 | self.label.setObjectName("label") 28 | self.label_2 = QtWidgets.QLabel(Form) 29 | self.label_2.setGeometry(QtCore.QRect(610, 40, 361, 381)) 30 | self.label_2.setStyleSheet("QLabel{\n" 31 | " border-color: rgb(255, 170,0);\n" 32 | " border-width: 1px;\n" 33 | " border-style: solid;\n" 34 | "}") 35 | self.label_2.setText("") 36 | self.label_2.setObjectName("label_2") 37 | self.pushButton_2 = QtWidgets.QPushButton(Form) 38 | self.pushButton_2.setGeometry(QtCore.QRect(80, 420, 100, 60)) 39 | self.pushButton_2.setObjectName("pushButton_2") 40 | self.pushButton_3 = QtWidgets.QPushButton(Form) 41 | self.pushButton_3.setGeometry(QtCore.QRect(80, 530, 100, 60)) 42 | self.pushButton_3.setObjectName("pushButton_3") 43 | self.pushButton_4 = QtWidgets.QPushButton(Form) 44 | self.pushButton_4.setGeometry(QtCore.QRect(80, 640, 100, 60)) 45 | self.pushButton_4.setObjectName("pushButton_4") 46 | self.pushButton_5 = QtWidgets.QPushButton(Form) 47 | self.pushButton_5.setGeometry(QtCore.QRect(80, 200, 100, 60)) 48 | self.pushButton_5.setObjectName("pushButton_5") 49 | self.pushButton_6 = QtWidgets.QPushButton(Form) 50 | self.pushButton_6.setGeometry(QtCore.QRect(80, 310, 100, 60)) 51 | self.pushButton_6.setObjectName("pushButton_6") 52 | 53 | self.pushButton_7 = QtWidgets.QPushButton(Form) 54 | self.pushButton_7.setGeometry(QtCore.QRect(80, 750, 100, 60)) 55 | self.pushButton_7.setObjectName("pushButton_7") 56 | 57 | self.label_3 = QtWidgets.QLabel(Form) 58 | self.label_3.setGeometry(QtCore.QRect(230, 460, 361, 381)) 59 | self.label_3.setStyleSheet("QLabel{\n" 60 | " border-color: rgb(255, 170,0);\n" 61 | " border-width: 1px;\n" 62 | " border-style: solid;\n" 63 | "}") 64 | self.label_3.setText("") 65 | self.label_3.setObjectName("label_3") 66 | self.label_4 = QtWidgets.QLabel(Form) 67 | self.label_4.setGeometry(QtCore.QRect(610, 460, 361, 381)) 68 | self.label_4.setStyleSheet("QLabel{\n" 69 | " border-color: rgb(255, 170,0);\n" 70 | " border-width: 1px;\n" 71 | " border-style: solid;\n" 72 | "}") 73 | self.label_4.setText("") 74 | self.label_4.setObjectName("label_4") 75 | self.label_5 = QtWidgets.QLabel(Form) 76 | self.label_5.setGeometry(QtCore.QRect(380, 430, 72, 15)) 77 | self.label_5.setObjectName("label_5") 78 | self.label_6 = QtWidgets.QLabel(Form) 79 | self.label_6.setGeometry(QtCore.QRect(760, 430, 72, 15)) 80 | self.label_6.setObjectName("label_6") 81 | self.label_7 = QtWidgets.QLabel(Form) 82 | self.label_7.setGeometry(QtCore.QRect(380, 850, 72, 15)) 83 | self.label_7.setObjectName("label_7") 84 | self.label_8 = QtWidgets.QLabel(Form) 85 | self.label_8.setGeometry(QtCore.QRect(750, 850, 72, 15)) 86 | self.label_8.setObjectName("label_8") 87 | 88 | self.retranslateUi(Form) 89 | self.pushButton.clicked.connect(Form.openimage) 90 | self.pushButton_5.clicked.connect(Form.image_crop) 91 | self.pushButton_6.clicked.connect(Form.change) 92 | self.pushButton_2.clicked.connect(Form.generate) 93 | self.pushButton_3.clicked.connect(Form.backchange) 94 | self.pushButton_4.clicked.connect(Form.save) 95 | self.pushButton_7.clicked.connect(Form.close) 96 | QtCore.QMetaObject.connectSlotsByName(Form) 97 | 98 | def retranslateUi(self, Form): 99 | _translate = QtCore.QCoreApplication.translate 100 | Form.setWindowTitle(_translate("Form", "打开图片")) 101 | self.pushButton.setText(_translate("Form", "打开图片")) 102 | self.pushButton_2.setText(_translate("Form", "生成证件照")) 103 | self.pushButton_3.setText(_translate("Form", "背景转换")) 104 | self.pushButton_4.setText(_translate("Form", "保存图片")) 105 | self.pushButton_5.setText(_translate("Form", "截取")) 106 | self.pushButton_6.setText(_translate("Form", "轮廓提取")) 107 | self.pushButton_7.setText(_translate("Form", "退出")) 108 | self.label_5.setText(_translate("Form", "原始图像")) 109 | self.label_6.setText(_translate("Form", "截取图片")) 110 | self.label_7.setText(_translate("Form", "轮廓图像")) 111 | self.label_8.setText(_translate("Form", "最终图像")) 112 | 113 | -------------------------------------------------------------------------------- /Openimg/first.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1127 10 | 879 11 | 12 | 13 | 14 | 打开图片 15 | 16 | 17 | 18 | 19 | 80 20 | 110 21 | 100 22 | 60 23 | 24 | 25 | 26 | 打开图片 27 | 28 | 29 | 30 | 31 | 32 | 230 33 | 40 34 | 361 35 | 381 36 | 37 | 38 | 39 | QLabel{ 40 | border-color: rgb(255, 170,0); 41 | border-width: 1px; 42 | border-style: solid; 43 | } 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 610 53 | 40 54 | 361 55 | 381 56 | 57 | 58 | 59 | QLabel{ 60 | border-color: rgb(255, 170,0); 61 | border-width: 1px; 62 | border-style: solid; 63 | } 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 80 73 | 440 74 | 100 75 | 60 76 | 77 | 78 | 79 | 生成证件照 80 | 81 | 82 | 83 | 84 | 85 | 80 86 | 550 87 | 100 88 | 60 89 | 90 | 91 | 92 | 背景转换 93 | 94 | 95 | 96 | 97 | 98 | 80 99 | 660 100 | 100 101 | 60 102 | 103 | 104 | 105 | 保存图片 106 | 107 | 108 | 109 | 110 | 111 | 80 112 | 220 113 | 100 114 | 60 115 | 116 | 117 | 118 | 截取 119 | 120 | 121 | 122 | 123 | 124 | 80 125 | 330 126 | 100 127 | 60 128 | 129 | 130 | 131 | 轮廓提取 132 | 133 | 134 | 135 | 136 | 137 | 230 138 | 460 139 | 361 140 | 381 141 | 142 | 143 | 144 | QLabel{ 145 | border-color: rgb(255, 170,0); 146 | border-width: 1px; 147 | border-style: solid; 148 | } 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 610 158 | 460 159 | 361 160 | 381 161 | 162 | 163 | 164 | QLabel{ 165 | border-color: rgb(255, 170,0); 166 | border-width: 1px; 167 | border-style: solid; 168 | } 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 380 178 | 430 179 | 72 180 | 15 181 | 182 | 183 | 184 | 原始图像 185 | 186 | 187 | 188 | 189 | 190 | 760 191 | 430 192 | 72 193 | 15 194 | 195 | 196 | 197 | 截取图片 198 | 199 | 200 | 201 | 202 | 203 | 380 204 | 850 205 | 72 206 | 15 207 | 208 | 209 | 210 | 轮廓图像 211 | 212 | 213 | 214 | 215 | 216 | 750 217 | 850 218 | 72 219 | 15 220 | 221 | 222 | 223 | 最终图像 224 | 225 | 226 | 227 | 228 | 229 | 230 | pushButton 231 | clicked() 232 | Form 233 | openimage() 234 | 235 | 236 | 83 237 | 322 238 | 239 | 240 | 161 241 | 315 242 | 243 | 244 | 245 | 246 | 247 | openimage() 248 | 249 | 250 | -------------------------------------------------------------------------------- /Openimg/head.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Skl 4 | @file:head.py 5 | @time:2018/5/322:01 6 | """ 7 | from PyQt5 import QtWidgets, QtGui 8 | import sys 9 | from first import Ui_Form # 导入生成first.py里生成的类 10 | from PyQt5.QtWidgets import QFileDialog 11 | 12 | import cv2 13 | import numpy as np 14 | import dlib 15 | 16 | image_path = ' ' 17 | image_save = '../save/save.jpg' 18 | detector = dlib.get_frontal_face_detector() 19 | landmark_predictor = dlib.shape_predictor( 20 | '../data/shape_predictor_68_face_landmarks.dat') 21 | Back_t = 0 22 | 23 | 24 | class mywindow(QtWidgets.QWidget, Ui_Form): 25 | def __init__(self): 26 | super(mywindow, self).__init__() 27 | self.setupUi(self) 28 | # 定义槽函数 29 | 30 | def bule2red(self, img): 31 | global Back_t 32 | # print(type(img)) 33 | rows, cols, channels = img.shape 34 | hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 35 | lower_blue = np.array([78, 43, 46]) 36 | upper_blue = np.array([110, 255, 255]) 37 | mask = cv2.inRange(hsv, lower_blue, upper_blue) 38 | # cv2.imshow('Mask', mask) 39 | # 腐蚀膨胀 40 | erode = cv2.erode(mask, None, iterations=1) 41 | dilate = cv2.dilate(erode, None, iterations=1) 42 | # 遍历替换 43 | Back_t += 1 44 | if Back_t % 3 == 1: 45 | for i in range(rows): 46 | for j in range(cols): 47 | if dilate[i, j] == 255: 48 | img[i, j] = (0, 0, 255) # BGR 49 | if Back_t % 3 == 2: 50 | for i in range(rows): 51 | for j in range(cols): 52 | if dilate[i, j] == 255: 53 | img[i, j] = (225, 166, 23) # BGR 54 | if Back_t % 3 == 0: 55 | for i in range(rows): 56 | for j in range(cols): 57 | if dilate[i, j] == 255: 58 | img[i, j] = (255, 255, 255) # BGR 59 | return img 60 | 61 | def openimage(self): 62 | global image_path 63 | # 打开文件路径 64 | # 设置文件扩展名过滤,注意用双分号间隔 65 | imgName, imgType = QFileDialog.getOpenFileName( 66 | self, "打开图片", "", " *.jpg;;*.png;;*.jpeg;;*.bmp;;All Files (*)") 67 | 68 | # imgName='../data/1.jpg' 69 | # 利用qlabel显示图片 70 | print('open:' + imgName) 71 | self.png = QtGui.QPixmap(imgName).scaled( 72 | self.label.width(), self.label.height()) 73 | self.label.setPixmap(self.png) 74 | image_path = str(imgName) 75 | 76 | img = cv2.imread(image_path) # 读取 77 | self.imgg = img.copy() 78 | t = img.shape 79 | faces = detector(img, 1) 80 | 81 | mask = np.zeros(img.shape[:2], np.uint8) 82 | bgdModel = np.zeros((1, 65), np.float64) 83 | fgdModel = np.zeros((1, 65), np.float64) 84 | 85 | if (len(faces) > 0): 86 | for k, d in enumerate(faces): 87 | left = max(int((3 * d.left() - d.right()) / 2), 1) 88 | top = max(int((3 * d.top() - d.bottom()) / 2) - 50, 1) 89 | right = min(int((3 * d.right() - d.left()) / 2), t[1]) 90 | bottom = min(int((3 * d.bottom() - d.top()) / 2), t[0]) 91 | self.rect = (left, top, right, bottom) 92 | rect_reg = (d.left(), d.top(), d.right(), d.bottom()) 93 | shape = landmark_predictor(img, d) 94 | xx, yy = 0, 0 95 | for i in range(68): 96 | xx += shape.part(i).x 97 | yy += shape.part(i).y 98 | # cv2.circle(img, (shape.part(i).x, shape.part(i).y),5,(0,255,0), -1, 8) 99 | # cv2.putText(img,str(i),(shape.part(i).x,shape.part(i).y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,255,0)) 100 | xx, yy = int(xx / 68), int(yy / 68) 101 | else: 102 | exit(0) 103 | cv2.grabCut(img, mask, self.rect, bgdModel, fgdModel, 5, 104 | cv2.GC_INIT_WITH_RECT) # 函数返回值为mask,bgdModel,fgdModel 105 | mask2 = np.where( 106 | (mask == 2) | ( 107 | mask == 0), 108 | 0, 109 | 1).astype('uint8') # 0和2做背景 110 | 111 | # cv2.imshow('b',img) 112 | img = img * mask2[:, :, np.newaxis] # 使用蒙板来获取前景区域 113 | erode = cv2.erode(img, None, iterations=1) 114 | dilate = cv2.dilate(erode, None, iterations=1) 115 | for i in range(t[0]): # 高 116 | for j in range(t[1]): 117 | if max(dilate[i, j]) <= 0: 118 | # if (abs(j - xx) * abs(j - xx)) < 100: # 距离调整 119 | # continue 120 | # else: 121 | dilate[i, j] = (225, 166, 23) # BGR 122 | img = img[self.rect[1]:self.rect[3], self.rect[0]:self.rect[2]] 123 | dilate = dilate[self.rect[1]:self.rect[3], self.rect[0]:self.rect[2]] 124 | self.output_im = cv2.resize(dilate, (361, 381)) 125 | self.output_imreg = cv2.resize(img, (361, 381)) 126 | 127 | def image_crop(self): 128 | # 截取图片,预处理 129 | self.imgg = self.imgg[self.rect[1] 130 | :self.rect[3], self.rect[0]:self.rect[2]] 131 | self.imgg = cv2.resize(self.imgg, (361, 381)) 132 | cv2.imwrite('output_crop.jpg', self.imgg) 133 | self.label_2.setPixmap( 134 | QtGui.QPixmap('output_crop.jpg').scaled( 135 | self.label.width(), 136 | self.label.height())) 137 | pass 138 | 139 | def change(self): 140 | # 显示轮廓 141 | cv2.imwrite('output.jpg', self.output_im) 142 | cv2.imwrite('output_reg.jpg', self.output_imreg) 143 | self.label_3.setPixmap( 144 | QtGui.QPixmap('output_reg.jpg').scaled( 145 | self.label.width(), 146 | self.label.height())) 147 | pass 148 | 149 | def generate(self): 150 | # 生成证件照 151 | self.label_4.setPixmap( 152 | QtGui.QPixmap('output.jpg').scaled( 153 | self.label.width(), 154 | self.label.height())) 155 | pass 156 | 157 | def backchange(self): 158 | # 更换背景 159 | self.back_img = cv2.imread('output.jpg') 160 | self.back_img = self.bule2red(self.back_img) 161 | cv2.imwrite('output1.jpg', self.back_img) 162 | self.label_4.setPixmap( 163 | QtGui.QPixmap('output1.jpg').scaled( 164 | self.label.width(), 165 | self.label.height())) 166 | pass 167 | 168 | def save(self): 169 | file_path, file_p = QFileDialog.getSaveFileName( 170 | self, "save file", "../save/save.jpg", "*.jpg;;*.png;;*.jpeg;;*.bmp;;All Files (*)") 171 | print('save:' + file_path) 172 | cv2.imwrite(file_path, self.back_img, None) 173 | pass 174 | 175 | 176 | app = QtWidgets.QApplication(sys.argv) 177 | window = mywindow() 178 | window.show() 179 | sys.exit(app.exec_()) 180 | --------------------------------------------------------------------------------