- 前言
- 一、系统整体结构
- 二、运行环境
- 1.预训练模型下载
- 2.提取面部标记
- 3.混合图像
- 5.校正颜色
- 6.转换函数
- 三、系统测试
前言
通过 Dlib 提供的机器学习、数值计算、图模型算法、图像处理等相关功能,采
用人脸识别预训练,实现两张照片的换脸功能。。
一、系统整体结构
1.采用dlib的预训练模型实现主体功能
2.设计交互界面
3.系统测试
需要 Python 3.6 及以上配置,完成该项目所需要的库文件有 OpenCV、dlib、numpy、sys、PIL、thikter、matplotlib。
1.预训练模型下载采用官方提供的预训练模型,帮助开发者节省时间,从 Dlib sourseforge 库下载:
http://sourceforge.net/projects/dclib/files/dlib/v18.10/shape_predictor_68_face_landmarks.dat.bz2
构建有 68 个特征点组成的人脸特征提取器。相关代码如下:
PREDICTOR_PATH = "./shape_predictor_68_face_landmarks.dat" FACE_POINTS = list(range(17, 68)) MOUTH_POINTS = list(range(48, 61)) RIGHT_BROW_POINTS = list(range(17, 22)) LEFT_BROW_POINTS = list(range(22, 27)) RIGHT_EYE_POINTS = list(range(36, 42)) LEFT_EYE_POINTS = list(range(42, 48)) NOSE_POINTS = list(range(27, 35)) JAW_POINTS = list(range(0, 17)) #划分 68 个点的每个点意味着什么部位,如第 27~35 的点(xi,yi)就是鼻子 ALIGN_POINTS = (LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS + RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS) #对齐图片的点,即五官 OVERLAY_POINTS = [LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS,NOSE_POINTS + MOUTH_POINTS,] detector = dlib.get_frontal_face_detector() #人脸检测器 predictor = dlib.shape_predictor(PREDICTOR_PATH) #特征提取器2.提取面部标记
在预训练获取特征提取器后用户输入两个图像的人脸特征点,函数将一个图像转化成
numpy 数组,并返回 68*2 元素矩阵,输入图像一张脸有 68 个特征点,每个特征点对应每行
的 x、y 坐标
代码如下(示例):
class TooManyFaces(Exception):#设置检测到太多脸的类 pass class NoFaces(Exception):#设置没有检测到脸的类 Pass def get_landmarks(im):#获取人脸特征点,将图像转化成 numpy 数组,返回 68*2 元素矩阵 #输入图像的每个特征点对应每行的 x、y 坐标 rects = detector(im, 1)#每个矩形列表在图像中对应一个脸 #rects 表示人脸框的位置信息 if len(rects) > 1: #如果识别的人脸数大于一个,引发 TooManyFaces 异常 raise TooManyFaces if len(rects) == 0:#如果图片没人脸,引发 NoFaces 异常 raise NoFaces return numpy.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()]) #为加快计算,把得到的特征点转换成 numpy 矩阵 def read_im_and_landmarks(fname):#从计算机中读取用户所选的图片并提取特征点 im = cv2.imread(fname, cv2.IMREAD_COLOR)#opencv 读取图片并显示 im = cv2.resize(im, (im.shape[1] * SCALE_FACTOR, im.shape[0] * SCALE_FACTOR)) s = get_landmarks(im) return im, s3.混合图像
使用掩模(mask)表示不同区域,属于人脸区域像素值为 1,不属于人脸区域像素值为 0。
在提取时直接将原图片乘以掩模,得到人脸,而其余区域像素值为 0;如果将原图片乘以
1−mask,即人脸区域是 0,保留其余区域。上面两个结果相加,实现初步换脸
代码如下:
def draw_convex_hull(im, points, color):#绘制凸包 points = cv2.convexHull(points) #寻找图像的凸包,points 就是输入的一组点 cv2.fillConvexPoly(im, points, color=color) 105 #cv2.fillConvexPoly()函数可以填充凸多边形,由凸包得到的轮廓点作为顶点 3.遮罩实现 def get_face_mask(im, landmarks):#为一张图像和一个标记矩阵生成一个遮罩 #画出了两个凸多边形:一个是眼睛周围的区域,一个是鼻子和嘴部周围的区域 im = numpy.zeros(im.shape[:2], dtype=numpy.float64) #numpy.zeros 返回给定形状和类型的新数组,用 0 填充 #img.shape[:2] 取彩色图片的高、宽 for group in OVERLAY_POINTS: #OVERLAY_POINTS 定义为[LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS , NOSE_POINTS + MOUTH_POINTS,] #分为[眼睛周围、鼻子和嘴]两个区域,是第二张图片中要覆盖第一张图片的点 draw_convex_hull(im,landmarks[group],color=1) #对图片中 68 特征点集里是 OVERLAY_POINTS 的点进行凸包绘制 im = numpy.array([im, im, im]).transpose((1, 2, 0)) im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT),0)>0)*1.0 #高斯滤波 im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) return im5.校正颜色
def correct_colours(im1, im2, landmarks1): 106 #修正两幅图像之间不同肤色和光线造成的覆盖区域边缘不连续 blur_amount = COLOUR_CORRECT_BLUR_FRAC * numpy.linalg.norm( numpy.mean(landmarks1[LEFT_EYE_POINTS], axis=0) - numpy.mean(landmarks1[RIGHT_EYE_POINTS],axis=0)) #numpy.mean 求左右眼点集均值,其中 axis=0,压缩行,对各列求均值,返回 1*n 矩阵 #从左眼点集、右眼点集分别得到一个代表左眼和右眼的点,两者相减是左右眼横,纵相对距离 #用 numpy.linalg.norm 得到矩阵所有元素平方和开根号,勾股定理,得到了两眼之间的距离 #COLOUR_CORRECT_BLUR_FRAC*两眼距离作为高斯内核大小 #内核太小,第一个图像的面部特征将显示在第二个图像中 #内核过大,内核之外区域像素被覆盖并发生变色,COLOUR_CORRECT_BLUR_FRAC 设置为 0.6 blur_amount = int(blur_amount) if blur_amount % 2 == 0: blur_amount += 1 #高斯内核大小不能是偶数 im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0) #用模板扫描图像中的每一个像素,确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值 #高斯矩阵的长与宽是高斯内核(blur_amount)的大小,标准差取 0,返回高斯滤波后的图像 im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0) im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype) #防止除零,将高斯滤波后 im2 元素的数据类型返回,强制类型转换成数据类型 return (im2.astype(numpy.float64) * im1_blur.astype(numpy.float64) / im2_blur.astype(numpy.float64)) #试图改变图像 2 的颜色来匹配图像 1,通过用 im2*im1/im2 的高斯模糊6.转换函数
def main():
global image1,image2#将两张照片设置为全局变量,方便在各个函数中直接使用
im1, landmarks1 = read_im_and_landmarks(image1)#提取图片 1 的特征点
im2, landmarks2 = read_im_and_landmarks(image2)#提取图片 2 的特征点
M = transformation_from_points(landmarks1[ALIGN_POINTS],
landmarks2[ALIGN_POINTS])
#普氏分析(Procrustes analysis)调整脸部,相同变换使两张照片面部特征的相对距离尽可能小
mask = get_face_mask(im2, landmarks2)#为一张图像和一个标记矩阵生成一个遮罩
warped_mask = warp_im(mask, M, im1.shape)
#把图像 2 遮罩通过仿射矩阵 M 映射到图像 1 上
combined_mask = numpy.max([get_face_mask(im1, landmarks1),
warped_mask], axis=0)
warped_im2 = warp_im(im2, M, im1.shape)#把图像 2 映射到遮好的图像 1 上
warped_corrected_im2 = correct_colours(im1, warped_im2, landmarks1)#校
正颜色
output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 *
combined_mask
cv2.imwrite('output.jpg', output_im) #输出换脸后的照片
三、系统测试
第一步,单击进入浏览页面,自行浏览计算机内的文件;第二步,选择换脸的照片;第三步,右边个框,在图片 1 下方框内显示出自己刚选择的照片。同理,打开图片 2 功能和打开图片 1 一样;第四步,单击左边最下方的换脸按钮,进行换脸。待换脸界面如图 9-5 所示。单击换脸后,在当前文件目录下生成换脸后的图片
融合结果图片:



