文章目录
- 前言
- 一、系统整体结构
- 二、运行环境
- 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, s
3.混合图像
使用掩模(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 im
5.校正颜色
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 所示。单击换脸后,在当前文件目录下生成换脸后的图片
融合结果图片: