python实现人脸识别抓取人脸并做成熊猫头表情包

前几天在浏览器上看到推送的文章用人脸识别做表情包。当时并不为意,没过多久就想弄一下了。无奈浏览器开了无痕找不着历史记录,上网搜也搜不到。CSDN里有过半都是简单地把两张图拼一起(你别说,经过我这么一找发现CSDN里面很多文章都是一模一样的,一个字都没动。真是我有句粗口不知当讲不当讲)。
思路是这样的,首先用脸部识别把脸的轮廓描下来,然后生成脸的掩膜。接着利用掩膜把脸抠出来放到熊猫头上
首先先导入包

import face_recognition
import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont

这里要插一句,导入的face_recognition需要依赖dlib库,dlib这玩意儿超难装。我是下了cmake然后自己按照教程手动编译了一次才装上的。如果没有cmake+boost环境不建议pip install这两个库,网上很多跟你说把轮子下下来装就可以的都是骗人的,归根结底还是要有C++环境来编译这两个库才能用。不信装上以后按Ctrl+鼠标点击你会发现它的源码全是C++。
OK,扯远了。 然后就是导入带抓取的表情了。

image = face_recognition.load_image_file(r"myWife.jpg")

这里写图片描述
恩用了我们家小老婆的丑照所以打码了(/手动再见)。不过做脸部识别的时候不能打码哦,不然上哪找脸去
接着我们使用face_recognition.face_landmarks来提取脸部特征,并用下巴,两个眉毛把脸的轮廓描出来

face_line = list()#用于记录脸部轮廓信息,之后做掩膜要用
for face_landmarks in face_landmarks_list:

   #打印此图像中眉毛和脸型组成的轮廓
    facial_features = [
        'chin',
        'left_eyebrow',
        'right_eyebrow',
    ]

    for facial_feature in facial_features:
        print("The {} in this face has the following points: {}".format(facial_feature, face_landmarks[facial_feature]))

   #让我们在图像中描绘出每个人脸特征!
    pil_image = Image.fromarray(image)
    d = ImageDraw.Draw(pil_image)

    chin = face_landmarks['chin']
    left_eyebrow = face_landmarks['left_eyebrow']
    right_eyebrow = face_landmarks['right_eyebrow']
    chin.reverse()
    list_all = left_eyebrow + right_eyebrow + chin + [left_eyebrow[0]]
    face_line.append(list_all)
    d.line(list_all, width=5)
    pil_image.save("Facial_contour.jpg")

这里写图片描述
把脸部轮廓描出来就要做一个掩膜用来把感兴趣区域(ROI)选出来。这个掩膜用cv2的水漫填充把不感兴趣的地方填充起来,再用对掩膜进行腐蚀膨胀出来,让掩膜框出来的地方更符合要求

#新建一个和原图一样大小的全白图片,用ImageDraw在上面勾出人脸轮廓,作为掩膜的模板
mask = np.ones(image.shape, dtype=np.uint8)*255
mask = Image.fromarray(mask)
q = ImageDraw.Draw(mask)
q.line(face_line[0], width=5, fill=(0, 0, 0))
mask.save("mask.jpg")#将图片写出是为了交给OpenCV处理

#生成掩膜
mask = cv2.imread('mask.jpg')
h, w = mask.shape[:2]  # 读取图像的宽和高
mask_flood = np.zeros([h + 2, w + 2], np.uint8)  # 新建图像矩阵  +2是官方函数要求
cv2.floodFill(mask, mask_flood, (75, 75), (0, 0, 0))#这里使用OpenCV的水漫填充,把轮廓外部涂成黑色,内部为白色

#用一个5*5的卷积核对掩膜进行闭运算,去掉噪声
#erosion的迭代次数2次是我试出来的,感觉效果比较好
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(mask, kernel, iterations=2)
dilation = cv2.dilate(erosion, kernel, iterations=1)
mask = dilation

轮廓做出来是这样的
这里写图片描述
然后就要把ROI框出来了,这里我直接将原图位置对应掩膜的像素涂成黑的,而ROI保持原状。掩膜就是下面那样,大家自己想象一下有个人头在白色区域
掩膜就是这样的了

#重新读入原图,框出RIO,交给OpenCV处理
image = cv2.imread("myWife.jpg")
image[mask == 0] = 0

接着就是对表情的处理了,怎么样把表情和谐地提出来是一个大问题。如果提太过了很可能会缺很多部分,但是处理少了又会让整个表情黑成一坨。这里主要是用膨胀腐蚀来消除噪声以及强化表情部分。

#将处理过的图片变为灰度图
GrayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#二值化处理
ret, image = cv2.threshold(GrayImage, 80, 255, cv2.THRESH_BINARY)

#再来一次水漫填充,这次把轮廓之外的地方变成白色
h, w = image.shape[:2]
mask_flood = np.zeros([h + 2, w + 2], np.uint8)
cv2.floodFill(image, mask_flood, (1, 1), (255, 255, 255))

#用一个2*2的卷积核对RIO进行多次腐蚀膨胀处理,去掉噪声,这里进行多少次膨胀腐蚀要结合图片效果来进行
kernel = np.ones((2,2),np.uint8)
dilation = cv2.dilate(image,kernel,iterations=2)
erosion = cv2.erode(dilation,kernel,iterations=1)
dilation = cv2.dilate(image,kernel,iterations=1)
image = cv2.erode(dilation,kernel,iterations=1)

#这是表情需要截取的部分,不同图这个地方的参数不同
image = image[130:410, 170:505]
cv2.imwrite("last.png",image)#这里输出图片是为了给Image做处理

这里写图片描述
提出来的表情就是这样的了。
之后,就是要对背景下手了,也就是熊猫头(/括弧笑)。
这里写图片描述
首先要先确定哪一部分是要放入表情的地方,我下的这个图的插入位置在box中,box的四个值分别是[左上角顶点坐标的x,左上角顶点坐标的y,右下角顶点坐标的x,右下角顶点坐标的y]。

box = (160, 155, 465, 405)#背景图要被替换的部分
base_img = Image.open('background.png')
image = Image.open('last.png')
image = image.resize((box[2] - box[0], box[3] - box[1]))#缩放表情,贴入的表情必须和背景被替换的地方大小相同
base_img.paste(image, box)
base_img.show()
base_img.save('out.png')#这里输出图片是为了给cv2做处理

这里写图片描述
这时候输出的图就是这样的,ps痕迹非常明显。要是是这样的话我是拿不出手的。这样我们就要继续对这个图进行处理了。这时候又是我们的膨胀腐蚀大法。

#这是为了让图片看起来更自然,而且本身表情就是黑白的,所以对图片再进行一次二值化处理
image = cv2.imread(r'out.png')
GrayImage = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
ret,image = cv2.threshold(GrayImage,85,255,cv2.THRESH_BINARY)

#这里继续膨胀腐蚀,让图片更好看一点
erosion = cv2.erode(image,kernel,iterations=1)
dilation = cv2.dilate(erosion,kernel,iterations=2)
erosion = cv2.erode(dilation,kernel,iterations=1)
image = erosion
cv2.imwrite("last_out.jpg", image)
cv2.waitKey(0)

这里写图片描述
到这里这个表情包就算是完成了。后面什么加文字吖什么的CSDN有很多一模一样的文章都会说怎么做,我就不做了。然后就是发图给小老婆看看了。小老婆喊我拿着键盘去不知道要干什么呢。

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值