70行python代码实现qq视频加特效效果

python实现qq视频加特效

使用过qq视频聊天功能的朋友应该都知道它有一个自动为我们加特效的功能,虽然自带中二 ヽ(ー_ー)ノ,但是能够将挂饰精准加到适应的位置,尚且值得我辈学习。废话不多说,直接开正题:

先上一下效果:

在这里插入图片描述在这里插入图片描述

from PIL import Image
import numpy
import cv2

一、识别器官(本例以眼睛为例)

大象装入冰箱,分为三个步骤。要在我们的脸上糊一个挂饰,第一步必须找到眼睛,这一步本菜用的是OpenCV的级联分类器CascadeClassifier。
这个CascadeClassifier类,及其牛逼而强大,本菜这一步直接用大神训练好的分类器,在视频帧中找到眼睛的位置。如下:

old_data = None
detector = cv2.CascadeClassifier("haarcascade_lefteye_2splits.xml")
cap = cv2.VideoCapture(0)
while True:
	_, frame = cap.read()
	# 4.png是要糊在眼睛上的挂饰
	pendant = Image.open("4.png")

	frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
	rects = detector.detectMultiScale(frame_gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

	eye_x, eye_y, eye_w, eye_h = get_rect(rects)

detectMultiScale()方法返回的是一个二维的numpy向量,通俗讲,就是返回一个存放所有识别结果坐标和长宽的列表,这里就是我们眼睛所在位置以及眼睛的大小。为使挂饰糊的稳准狠,我们还需要对这个列表处理一下,算出挂饰的坐标和长宽。

def get_rect(rects):
	pen_x = 0
	pen_y = 0
	pen_w = 0
	pen_h = 0
	for i, (x, y, w, h) in enumerate(rects):
		# 俩眼睛一般人都是竖直方向位置一直,高度也大概相同
		pen_y = y
		pen_h = h
		# 挂饰的水平坐标需要时左边那个,宽度的计算为:x2-x1+w2
		if not i:
			pen_x = x
			pen_w -= x
		else:
			if x > pen_x:
				pen_w += (x + w)
			else:
				pen_x = x
				pen_w = -pen_w - x + w
	return pen_x, pen_y, pen_w, pen_h

二、挂饰糊脸

OK,当我们已经把眼睛得位置找到,试问还有什么能够阻挡我们把东西糊在脸上的冲动,接下来我们只需要安安静静的做一个没有感情的糊脸机器,代码如下:
为了让挂饰糊的稳准狠,我们这里对参数进行了一次"微"调整。

frame = add_pendant(pen_x-int(pen_w*0.25), pen_y-int(pen_h*0.25), frame, pendant)

PIL的paste()方法可以把一张图片糊在另外一张图片上,very契合我们的需求~但是要注意,我们这里双向操作,一会儿用PIL,一会儿用CV2,为了避免这两个大大之间打架报错,必须要通过numpy大佬中间调停,把两者的格式转换。
其次,RGBA格式的A层为图片的透明层。paste()方法的mask参数能给我们要糊上去的挂饰加一层蒙版,把透明层给去掉,否则透明层会被自动识别为黑色,那么我们糊的东西就不再beautiful了。

def add_pendant(x, y, frame, pendant):
	frame = numpy.array(frame)
	frame_img = Image.fromarray(frame)
	pendant = pendant.convert('RGBA')
	frame_img.paste(pendant, (x, y), mask=pendant.split()[3])
	frame = numpy.asarray(frame_img)
	return frame

三、装饰精修

完成上面两步,基本就可以实现挂饰糊脸的功能,但…当代码跑起来了,本菜也傻球了…因为通过分类器识别,时而会出现只识别出一只眼睛的情况,当这种情况出现时,根据我们的坐标换算方法,挂饰会变成超级巨无霸,就不是把挂饰糊眼睛上了,真真是直接糊在脸上,么得一丝感情那种。
因而,我们还要对挂饰的坐标进行一次修正,如下:

pen_x, pen_y, pen_w, pen_h = get_rect(rects)
if old_data is not None:
	pen_x, pen_y, pen_w, pen_h = check_data(pen_x, pen_y, pen_w, pen_h, old_data)
try:
	pendant = pendant.resize((int(pen_w*1.5), int(pen_h*1.5)))
except Exception as e:
	pass
frame = add_pendant(pen_x-int(pen_w*0.25), pen_y-int(pen_h*0.25), frame, pendant)
old_data = [pen_x, pen_y, pen_w, pen_h]
def check_data(eye_x, eye_y, eye_w, eye_h, old_data):
	scale_factor = 0.5
	if (abs(eye_x - old_data[0]) > scale_factor * old_data[0] or abs(eye_y - old_data[1]) > scale_factor * old_data[1] or abs(eye_w - old_data[2]) >= scale_factor * old_data[2] or abs(eye_h - old_data[3]) >= scale_factor * old_data[3]):
		return old_data[0], old_data[1], old_data[2], old_data[3]
	return eye_x, eye_y, eye_w, eye_h

OK,接下来,就可以开开心心的糊脸了~望大家糊的开心。
全代码见这里,可以点个星再走哦~

发布了8 篇原创文章 · 获赞 19 · 访问量 547

猜你喜欢

转载自blog.csdn.net/evil126126/article/details/105699311