使用飞桨PaddleHub实现皮影戏创作

该博客介绍了如何利用飞桨PaddlePaddle平台的PaddleHub进行人体骨骼关键点检测,结合皮影戏素材,实现将人物动作映射到皮影上,进而制作出皮影戏效果。首先,通过PaddleHub安装所需库并检测人体关键点,然后解析骨骼信息,将皮影素材映射到对应位置。接着,将连续帧处理成皮影动画,最终将处理后的图片合并成视频,呈现皮影戏动态效果。提供了详细的代码示例和视频链接展示最终成果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

飞桨(PaddlePaddle)以百度多年的深度学习技术研究和业务应用为基础,是中国首个开源开放、技术领先、功能完备的产业级深度学习平台,集深度学习核心训练和推理框架、基础模型库、端到端开发套件和丰富的工具组件于一体。
皮影戏(Shadow Puppets),又称“影子戏”或“灯影戏”,是一种以兽皮或纸板做成的人物剪影以表演故事的民间戏剧,有浓厚的乡土气息,是中国民间古老的传统艺术,流行范围极为广泛。先辈通过手艺演绎着皮影戏,同样我们也可以通过AI方式来实现。
本项目通过PaddleHub完成人体骨骼关键点检测,将人体骨骼关键点进行连接,获取到人体的肢体骨骼,在骨骼肢体上覆盖皮影素材,从而得到皮影人。将视频中连续帧进行转换,就可以实现“皮影戏”的效果。
本项目参考以下文章:
AI 实现皮影戏,传承正在消失的艺术:https://aistudio.baidu.com/aistudio/projectdetail/764130?fromQRCode=1&shared=1

一、 前期准备

以下是我所用到的工具和环境:

工具&环境
Python 3.7.4
PyCharm 2019.3.3

二、实现步骤

1、安装所需库

通过pip安装PaddlePaddle和PaddleHub:

pip install PaddlePaddle
pip install PaddleHub

通过PaddleHub来安装人体骨骼关键点检测模型 human_pose_estimation_resnet50_mpii:

hub install human_pose_estimation_resnet50_mpii==1.1.1

2、目录和素材

创建work文件夹:
在这里插入图片描述

在work目录下新建imgs、mp4_img、mp4_img_analysis、output_pose、shadow_play_material五个文件夹,分别存放图片资源、视频按帧导出的图片、视频图片分析结果、人体骨骼关键点识别后的图片、皮影的素材图片:在这里插入图片描述
shadow_play_material中的图片素材需要从上述参考文章的“文件” -“work/shadow_play_material” 中获取,同时,也要把皮影背景图 “background.jpg” 下载下来,这两个素材分别是合成皮影形象及背景的关键素材,不可或缺。
在这里插入图片描述

3、人体骨骼关键点检测

完整代码如下:

import os
import cv2
import paddlehub as hub
import matplotlib.pyplot as plt
from matplotlib.image import imread
import numpy as np


def show_img(img_path, size=8):
    '''
        文件读取图片显示
    '''
    im = imread(img_path)
    plt.figure(figsize=(size, size))
    plt.axis("off")
    plt.imshow(im)


def img_show_bgr(image, size=8):
    '''
        cv读取的图片显示
    '''
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    plt.figure(figsize=(size, size))
    plt.imshow(image)

    plt.axis("off")
    plt.show()

show_img('work/imgs/3.jpg')
#通过代码获取图片中的结果
pose_estimation = hub.Module(name="human_pose_estimation_resnet50_mpii")
result = pose_estimation.keypoint_detection(paths=['work/imgs/3.jpg'], visualization=True, output_dir="work/output_pose/")
show_img('work/output_pose/3.jpg')
print(result)

实现效果:
在这里插入图片描述

4、将动作映射到皮影戏中

要实现皮影戏的效果我们首先要解析人体各个骨骼关键点的位置信息,通过关节点的信息计算皮影的肢体位置,和旋转方向,从而达到肢体同步。
通过2个骨骼关键点可以确认肢体的长度和旋转角度,根据长度就可以对素材进行缩放,根据旋转角度,可以先对素材进行中心旋转,再计算旋转后图片的位移信息,就可以得到最终映射骨骼关键点位置。将各个素材图片映射到对应的肢体上,便可以达到动作映射的效果。
完整代码:

import os
import cv2
import paddlehub as hub
import matplotlib.pyplot as plt
from matplotlib.image import imread
import numpy as np


def show_img(img_path, size=8):
    '''
        文件读取图片显示
    '''
    im = imread(img_path)
    plt.figure(figsize=(size, size))
    plt.axis("off")
    plt.imshow(im)


def img_show_bgr(image, size=8):
    '''
        cv读取的图片显示
    '''
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    plt.figure(figsize=(size, size))
    plt.imshow(image)

    plt.axis("off")
    plt.show()

show_img('work/imgs/3.jpg')
#通过代码获取图片中的结果
pose_estimation = hub.Module(name="human_pose_estimation_resnet50_mpii")
result = pose_estimation.keypoint_detection(paths=['work/imgs/3.jpg'], visualization=True, output_dir="work/output_pose/")
show_img('work/output_pose/3.jpg')

def get_true_angel(value):
    '''
    转转得到角度值
    '''
    return value / np.pi * 180


def get_angle(x1, y1, x2, y2):
    '''
    计算旋转角度
    '''
    dx = abs(x1 - x2)
    dy = abs(y1 - y2)
    result_angele = 0
    if x1 == x2:
        if y1 > y2:
            result_angele = 180
    else:
        if y1 != y2:
            the_angle = int(get_true_angel(np.arctan(dx / dy)))
        if x1 < x2:
            if y1 > y2:
                result_angele = -(180 - the_angle)
            elif y1 < y2:
                result_angele = -the_angle
            elif y1 == y2:
                result_angele = -90
        elif x1 > x2:
            if y1 > y2:
                result_angele = 180 - the_angle
            elif y1 < y2:
                result_angele = the_angle
            elif y1 == y2:
                result_angele = 90

    if result_angele < 0:
        result_angele = 360 + result_angele
    return result_angele


def rotate_bound(image, angle, key_point_y):
    '''
    旋转图像,并取得关节点偏移量
    '''
    # 获取图像的尺寸
    (h, w) = image.shape[:2]
    # 旋转中心
    (cx, cy) = (w / 2, h / 2)
    # 关键点必须在中心的y轴上
    (kx, ky) = cx, key_point_y
    d = abs(ky - cy)

    # 设置旋转矩阵
    M = cv2.getRotationMatrix2D((cx, cy), -angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])

    # 计算图像旋转后的新边界
    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))

    # 计算旋转后的相对位移
    move_x = nW / 2 + np.sin(angle / 180 * np.pi) * d
    move_y = nH / 2 - np.cos(angle / 180 * np.pi) * d

    # 调整旋转矩阵的移动距离(t_{x}, t_{y})
    M[0, 2] += (nW / 2) - cx
    M[1, 2] += (nH / 2) - cy

    return cv2.warpAffine(image, M, (nW, nH)), int(move_x), int(move_y)


def get_distences(x1, y1, x2, y2):
    return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5


def append_img_by_sk_points(img, append_img_path, key_point_y, first_point, second_point, append_img_reset_width=None,
                            append_img_max_height_rate=1, middle_flip=False, append_img_max_height=None):
    '''
    将需要添加的肢体图片进行缩放
    '''
    append_image = cv2.imdecode(np.fromfile(append_img_path, dtype=np.uint8), cv2.IMREAD_UNCHANGED)

    # 根据长度进行缩放
    sk_height = int(
        get_distences(first_point[0], first_point[1], second_point[0], second_point[1]) * append_img_max_height_rate)
    # 缩放制约
    if append_img_max_height:
        sk_height = min(sk_height, append_img_max_height)

    sk_width = int(
        sk_height / append_image.shape[0] * append_image.shape[1]) if append_img_reset_width is None else int(
        append_img_reset_width)
    if sk_width <= 0:
        sk_width = 1
    if sk_height <= 0:
        sk_height = 1

    # 关键点映射
    key_point_y_new = int(key_point_y / append_image.shape[0] * append_image.shape[1])
    # 缩放图片
    append_image = cv2.resize(append_image, (sk_width, sk_height))

    img_height, img_width, _ = img.shape
    # 是否根据骨骼节点位置在 图像中间的左右来控制是否进行 左右翻转图片
    # 主要处理头部的翻转, 默认头部是朝左
    if middle_flip:
        middle_x = int(img_width / 2)
        if first_point[0] < middle_x and second_point[0] < middle_x:
            append_image = cv2.flip(append_image, 1)

    # 旋转角度
    angle = get_angle(first_point[0], first_point[1], second_point[0], second_point[1])
    append_image, move_x, move_y = rotate_bound(append_image, angle=angle, key_point_y=key_point_y_new)
    app_img_height, app_img_width, _ = append_image.shape

    zero_x = first_point[
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jzqs_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值