【SMPL简介】SMPL: A Skinned Multi-Person Linear Model【源码复现】

news2024/9/21 4:34:15

【SMPL简介】SMPL: A Skinned Multi-Person Linear Model【源码复现】

  • 一、前言
  • 环境搭建
    • 运行demo.py
  • 参考链接

一、前言

SMPL是一种3D人体建模方法.在数字人或者人物角色三维重建领域有着广泛应用
支持人体的各种形状及动作
可以简单理解为通过训练获取的人物模型
常用的模型有 smpl(身体模型),mano(手部模型),smplh(身体+手部),flame(脸部),smplx(身体+手部+脸部) 官网:https://smpl-x.is.tue.mpg.de/index.html https://smpl.is.tue.mpg.de

SMPL 输入是:姿态shape vector β \beta β + 影响动作Pose的参数 θ \theta θ
输出是: 各种变化后vertex顶点的数据
作者:Loper, Matthew and Mahmood, Naureen and Romero, Javier and Pons-Moll, Gerard and Black, Michael J.

【Paper】 > 【Code】 > 【Project】
SMPL 是基于蒙皮和混合形状的真实人体 3D 模型,是从数千个 3D 人体扫描中学习得到的。
在这里插入图片描述
a. T-pose姿态.常用的模版Pose有T-Pose,A-Pose,Da-Pose 颜色表示身体每个部位受到身体关节点影响的权重 颜色表示身体每个部位受到身体关节点影响的权重 颜色表示身体每个部位受到身体关节点影响的权重
b. 改变β参数的姿态,J关节点的变化 β参数可以理解为影响人体高矮胖瘦的一个参数
c. 改变β跟θ参数的姿态,此时还没有发生变化
d. 加上W权重通过W的LBS蒙皮算法的姿态

SMPL-X

【Paper】 > 【Code】 > 【Project】
感兴趣的可以看看原文

环境搭建

  1. 代码下载
git clone https://github.com/vchoutas/smplx.git 
cd smplx
  1. conda 虚拟环境搭建
conda create -n smplx python=3.8 -y 
conda activate smplx
  1. torch
conda install -y pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 pytorch-cuda=11.7 -c pytorch -c nvidia

pip install smplx[all]
  1. matplotlib y轴朝上
ax.view_init(azim=-90, elev=100)
  1. 下载models,先要注册登录
    SMPL下载:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    将这三个模型上传到项目中,需要改名,KaTeX parse error: Double subscript at position 13: basicmodel_f_̲lbs_10_207_0_v1…,其余类似
    SMPL-X 下载:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    将所需6个文件上传到项目中
models
├── smpl
│   ├── SMPL_FEMALE.pkl
│   └── SMPL_MALE.pkl
│   └── SMPL_NEUTRAL.pkl
├── smplh
│   ├── SMPLH_FEMALE.pkl
│   └── SMPLH_MALE.pkl
├── mano
|   ├── MANO_RIGHT.pkl
|   └── MANO_LEFT.pkl
└── smplx
    ├── SMPLX_FEMALE.npz
    ├── SMPLX_FEMALE.pkl
    ├── SMPLX_MALE.npz
    ├── SMPLX_MALE.pkl
    ├── SMPLX_NEUTRAL.npz
    └── SMPLX_NEUTRAL.pkl

在这里插入图片描述

运行demo.py

安装 smplx 包并下载模型参数后,您应该能够运行 demo.py 脚本来可视化结果。对于此步骤,您必须安装pyrender 和trimesh 软件包。

python examples/demo.py --model-folder $SMPLX_FOLDER --plot-joints=True --gender="neutral"

我们可以先用 --plotting-module matplotlib 来先用matplotlib渲染。pip install matplotlib
可以得到对应的输出,远程环境可以保存为png图像查看

elif plotting_module == 'matplotlib':
        from matplotlib import pyplot as plt
        from mpl_toolkits.mplot3d import Axes3D
        from mpl_toolkits.mplot3d.art3d import Poly3DCollection

        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')

        mesh = Poly3DCollection(vertices[model.faces], alpha=0.1)
        face_color = (1.0, 1.0, 0.9)
        edge_color = (0, 0, 0)
        mesh.set_edgecolor(edge_color)
        mesh.set_facecolor(face_color)
        ax.add_collection3d(mesh)
        ax.scatter(joints[:, 0], joints[:, 1], joints[:, 2], color='r')

        ax.view_init(azim=-90, elev=100) #  y轴朝上
        if plot_joints:
            ax.scatter(joints[:, 0], joints[:, 1], joints[:, 2], alpha=0.1)
        plt.show()
        plt.savefig("./outputs_zzk/ok"+".png") # 保存观看
        plt.close()

图像是:(y轴朝上)
在这里插入图片描述
没改代码之前是:
在这里插入图片描述
pyrender 离线渲染包安装
然后就可以跑demo.py了,改为–plotting-module pyrender
需要改demo.py的代码,加上renderer.py,在examples下面:【直接复制过去即可】结果在最下面
demo.py

# -*- coding: utf-8 -*-
import os
# Max-Planck-Gesellschaft zur Förderung der Wissenschaften e.V. (MPG) is
# holder of all proprietary rights on this computer program.
# You can only use this computer program if you have closed
# a license agreement with MPG or you get the right to use the computer
# program from someone who is authorized to grant you that right.
# Any use of the computer program without a valid license is prohibited and
# liable to prosecution.
#
# Copyright©2019 Max-Planck-Gesellschaft zur Förderung
# der Wissenschaften e.V. (MPG). acting on behalf of its Max Planck Institute
# for Intelligent Systems. All rights reserved.
#
# Contact: ps-license@tuebingen.mpg.de

import os.path as osp
import argparse

import numpy as np
import torch

import smplx
from renderer import Renderer
import cv2

def main(model_folder,
         model_type='smplx',
         ext='npz',
         gender='neutral',
         plot_joints=False,
         num_betas=10,
         sample_shape=True,
         sample_expression=True,
         num_expression_coeffs=10,
         plotting_module='pyrender',
         use_face_contour=False):

    model = smplx.create(model_folder, model_type=model_type,
                         gender=gender, use_face_contour=use_face_contour,
                         num_betas=num_betas,
                         num_expression_coeffs=num_expression_coeffs,
                         ext=ext)
    print(model)

    betas, expression = None, None
    if sample_shape:
        betas = torch.randn([1, model.num_betas], dtype=torch.float32)
    if sample_expression:
        expression = torch.randn(
            [1, model.num_expression_coeffs], dtype=torch.float32)

    pose = torch.zeros([1,21*3], dtype=torch.float32)
    pose[:,2] = 1
    pose[:,5] = -1
    output = model(betas=betas, expression=expression, body_pose=pose,
                   return_verts=True)
    # output = model(betas=betas, expression=expression,
    #                return_verts=True)
    vertices = output.vertices.detach().cpu().numpy().squeeze()
    joints = output.joints.detach().cpu().numpy().squeeze()

    print('Vertices shape =', vertices.shape)
    print('Joints shape =', joints.shape)

    if plotting_module == 'pyrender':
        import pyrender
        import trimesh

        renderer = Renderer(resolution=(512, 512), orig_img=True, faces=model.faces)
        img = renderer.render(
            vertices,
        )
        cv2.imwrite('demo.jpg',img)
    elif plotting_module == 'matplotlib':
        from matplotlib import pyplot as plt
        from mpl_toolkits.mplot3d import Axes3D
        from mpl_toolkits.mplot3d.art3d import Poly3DCollection

        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')

        mesh = Poly3DCollection(vertices[model.faces], alpha=0.1)
        face_color = (1.0, 1.0, 0.9)
        edge_color = (0, 0, 0)
        mesh.set_edgecolor(edge_color)
        mesh.set_facecolor(face_color)
        ax.add_collection3d(mesh)
        ax.scatter(joints[:, 0], joints[:, 1], joints[:, 2], color='r')
        ax.view_init(azim=-90, elev=100)
        if plot_joints:
            ax.scatter(joints[:, 0], joints[:, 1], joints[:, 2], alpha=0.1)
        plt.show()
    elif plotting_module == 'open3d':
        import open3d as o3d

        mesh = o3d.geometry.TriangleMesh()
        mesh.vertices = o3d.utility.Vector3dVector(
            vertices)
        mesh.triangles = o3d.utility.Vector3iVector(model.faces)
        mesh.compute_vertex_normals()
        mesh.paint_uniform_color([0.3, 0.3, 0.3])

        geometry = [mesh]
        if plot_joints:
            joints_pcl = o3d.geometry.PointCloud()
            joints_pcl.points = o3d.utility.Vector3dVector(joints)
            joints_pcl.paint_uniform_color([0.7, 0.3, 0.3])
            geometry.append(joints_pcl)

        o3d.visualization.draw_geometries(geometry)
    else:
        raise ValueError('Unknown plotting_module: {}'.format(plotting_module))


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='SMPL-X Demo')

    parser.add_argument('--model-folder', required=True, type=str,
                        help='The path to the model folder')
    parser.add_argument('--model-type', default='smplx', type=str,
                        choices=['smpl', 'smplh', 'smplx', 'mano', 'flame'],
                        help='The type of model to load')
    parser.add_argument('--gender', type=str, default='neutral',
                        help='The gender of the model')
    parser.add_argument('--num-betas', default=10, type=int,
                        dest='num_betas',
                        help='Number of shape coefficients.')
    parser.add_argument('--num-expression-coeffs', default=10, type=int,
                        dest='num_expression_coeffs',
                        help='Number of expression coefficients.')
    parser.add_argument('--plotting-module', type=str, default='pyrender',
                        dest='plotting_module',
                        choices=['pyrender', 'matplotlib', 'open3d'],
                        help='The module to use for plotting the result')
    parser.add_argument('--ext', type=str, default='npz',
                        help='Which extension to use for loading')
    parser.add_argument('--plot-joints', default=False,
                        type=lambda arg: arg.lower() in ['true', '1'],
                        help='The path to the model folder')
    parser.add_argument('--sample-shape', default=True,
                        dest='sample_shape',
                        type=lambda arg: arg.lower() in ['true', '1'],
                        help='Sample a random shape')
    parser.add_argument('--sample-expression', default=True,
                        dest='sample_expression',
                        type=lambda arg: arg.lower() in ['true', '1'],
                        help='Sample a random expression')
    parser.add_argument('--use-face-contour', default=False,
                        type=lambda arg: arg.lower() in ['true', '1'],
                        help='Compute the contour of the face')

    args = parser.parse_args()

    model_folder = osp.expanduser(osp.expandvars(args.model_folder))
    model_type = args.model_type
    plot_joints = args.plot_joints
    use_face_contour = args.use_face_contour
    gender = args.gender
    ext = args.ext
    plotting_module = args.plotting_module
    num_betas = args.num_betas
    num_expression_coeffs = args.num_expression_coeffs
    sample_shape = args.sample_shape
    sample_expression = args.sample_expression

    main(model_folder, model_type, ext=ext,
         gender=gender, plot_joints=plot_joints,
         num_betas=num_betas,
         num_expression_coeffs=num_expression_coeffs,
         sample_shape=sample_shape,
         sample_expression=sample_expression,
         plotting_module=plotting_module,
         use_face_contour=use_face_contour)

renderer.py

import os
#os.environ['PYOPENGL_PLATFORM'] = 'egl'
import math
import trimesh
import pyrender
import numpy as np
from pyrender.constants import RenderFlags


class WeakPerspectiveCamera(pyrender.Camera):
    def __init__(self,
                 scale,
                 translation,
                 znear=pyrender.camera.DEFAULT_Z_NEAR,
                 zfar=None,
                 name=None):
        super(WeakPerspectiveCamera, self).__init__(
            znear=znear,
            zfar=zfar,
            name=name,
        )
        self.scale = scale
        self.translation = translation

    def get_projection_matrix(self, width=None, height=None):
        P = np.eye(4)
        P[0, 0] = self.scale[0]
        P[1, 1] = self.scale[1]
        P[0, 3] = self.translation[0] * self.scale[0]
        P[1, 3] = -self.translation[1] * self.scale[1]
        P[2, 2] = -1
        return P


class PerspectiveCamera(pyrender.Camera):
    def __init__(self,
                 yfov=None,
                 aspectRatio=1):
        super(PerspectiveCamera, self).__init__(
            znear=0.05,
            zfar=100.0
        )
        self.znear = 0.05
        self.zfar = 100.0
        self.yfov = yfov
        self.aspectRatio = aspectRatio

    def get_projection_matrix(self, width=None, height=None):
        znear = 0.05
        zfar = 100.0
        fovX = self.yfov
        tanHalfFovY = math.tan((self.yfov / 2))
        tanHalfFovX = math.tan((fovX / 2))

        top = tanHalfFovY * znear
        bottom = -top
        right = tanHalfFovX * znear
        left = -right

        P = np.zeros((4, 4))

        z_sign = 1.0

        P[0, 0] = 2.0 * znear / (right - left)
        P[1, 1] = 2.0 * znear / (top - bottom)
        P[0, 2] = (right + left) / (right - left)
        P[1, 2] = (top + bottom) / (top - bottom)
        P[3, 2] = -z_sign
        P[2, 2] = z_sign * zfar / (znear-zfar)
        P[2, 3] = 2*(zfar * znear) / (znear-zfar)
        return P





class Renderer:
    def __init__(self, resolution=(224,224), orig_img=False, wireframe=False, faces=None):
        self.resolution = resolution

        self.faces = faces
        self.orig_img = orig_img
        self.wireframe = wireframe
        self.renderer = pyrender.OffscreenRenderer(
            viewport_width=self.resolution[0],
            viewport_height=self.resolution[1],
            point_size=1.0
        )

        # set the scene
        self.scene = pyrender.Scene(bg_color=[0.0, 0.0, 0.0, 0.0], ambient_light=(0.3, 0.3, 0.3))

        # light = pyrender.PointLight(color=[1.0, 1.0, 1.0], intensity=0.8)
        light = pyrender.DirectionalLight(color=[1.0, 1.0, 1.0], intensity=0.8)

        light_pose = np.eye(4)
        light_pose[:3, 3] = [0, -1, 1]
        self.scene.add(light, pose=light_pose)

        light_pose[:3, 3] = [0, 1, 1]
        self.scene.add(light, pose=light_pose)

        light_pose[:3, 3] = [1, 1, 2]
        self.scene.add(light, pose=light_pose)

    def render(self, verts, cam=None, angle=None, axis=None, mesh_filename=None, color=[1.0, 1.0, 0.9], rotate=False):
        mesh = trimesh.Trimesh(vertices=verts, faces=self.faces, process=False)

        Rx = trimesh.transformations.rotation_matrix(math.radians(180), [1, 0, 0])
        mesh.apply_transform(Rx)

        if rotate:
            rot = trimesh.transformations.rotation_matrix(
                np.radians(60), [0, 1, 0])
            mesh.apply_transform(rot)

        if mesh_filename is not None:
            mesh.export(mesh_filename)
        angle = 180
        axis = [1, 0, 0]
        if angle and axis:
            R = trimesh.transformations.rotation_matrix(math.radians(angle), axis)
            mesh.apply_transform(R)



        camera = PerspectiveCamera(yfov=0.5, aspectRatio=1)

        material = pyrender.MetallicRoughnessMaterial(
            metallicFactor=0.0,
            alphaMode='OPAQUE',
            smooth=True,
            wireframe=True,
            roughnessFactor=1.0,
            emissiveFactor=(0.1, 0.1, 0.1),
            baseColorFactor=(color[0], color[1], color[2], 1.0)
        )

        mesh = pyrender.Mesh.from_trimesh(mesh, material=material)

        mesh_node = self.scene.add(mesh, 'mesh')

        camera_pose = np.eye(4)
        camera_pose[:3, 3] = np.array([0, 0, 5])
        cam_node = self.scene.add(camera, pose=camera_pose)

        if self.wireframe:
            render_flags = RenderFlags.RGBA | RenderFlags.ALL_WIREFRAME
        else:
            render_flags = RenderFlags.RGBA

        rgb, _ = self.renderer.render(self.scene, flags=render_flags)
        # valid_mask = (rgb[:, :, -1] > 0)[:, :, np.newaxis]
        # #output_img = rgb[:, :, :-1] * valid_mask + (1 - valid_mask) * img
        # output_img = rgb * valid_mask + (1 - valid_mask) * img
        # image = output_img.astype(np.uint8)

        self.scene.remove_node(mesh_node)
        self.scene.remove_node(cam_node)

        return rgb

在这里插入图片描述

在这里插入图片描述

参考链接

  1. https://www.bilibili.com/video/BV1jkVeeJEDn/?spm_id_from=333.788&vd_source=5413f4289a5882463411525768a1ee27
  2. https://github.com/heawon-yoon/smpl_gaussian_tutorial/blob/main/smpl.ipynb

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1907368.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信息技术课堂纪律管理:从混乱到秩序的智慧转型

引言: 在信息爆炸的时代,信息技术课程如同一把开启未来世界大门的钥匙,为学生们搭建起探索科技奥秘的桥梁。然而,面对着屏幕背后的无限诱惑,维持课堂纪律,确保学生们专注于学习,成为了每位信息…

Flask项目搭建及部署 —— Python

flask搭建及部署 pip 19.2.3 python 3.7.5 Flask 1.1.1 Flask-SQLAlchemy 2.4.1 Pika 1.1.0 Redis 3.3.11 flask-wtf 0.14.2 1、创建flask项目: 创建完成后整个项目结构树: app.py: 项⽬管理⽂件,通过它管理项⽬。 static: 存放静态…

使用tcpdump抓取本本机的所有icmp包

1、抓取本机所有icmp包 tcpdump -i any icmp -vv 图中上半部分,是源主机tmp179无法ping通目标主机192.168.10.79(因为把该主机关机了)的状态,注意看,其中有unreachable 图中下半部分,是源主机tmp179可以p…

张量分解(2)——张量运算(内积、外积、直积、范数)

🍅 写在前面 👨‍🎓 博主介绍:大家好,这里是hyk写算法了吗,一枚致力于学习算法和人工智能领域的小菜鸟。 🔎个人主页:主页链接(欢迎各位大佬光临指导) ⭐️近…

国内仅存的3个完美替代Google安卓商店的APP与网站

Google安卓商店在国内访问限制,部分谷歌商店镜像站点也相继受限,现分享目前仍可在国内顺畅使用的应用程序商店与网站资源,请大家且用且珍惜。 2024年7月8日国内验证有效的资源 F-Droid 简介:F-Droid,专注于开源软件的…

独立站爆款产品的选品思路及底层逻辑拆解

在这个竞争激烈的跨境电商市场,有一件事情比网站设计、营销策略、物流服务都更重要。那就是选品。跨境独立站选品是独立站成功的第一步,如果选错了产品,那么所有努力都可能白费。可能会面临库存积压、利润低迷、客户流失等问题。但是如果选对…

从数据到洞察:DataOps加速AI模型开发的秘密实践大公开!

作者 | 代立冬,白鲸开源科技联合创始人&CTO 引言 在AI驱动的商业世界中,DataOps作为连接数据与洞察的桥梁,正迅速成为企业数据战略的核心。 在WOT全球技术创新大会2024北京站,白鲸开源联合创始人&CTO 代立冬 在「大数据…

NFT 技术在艺术领域的应用

NFT (Non-Fungible Token) 技术在艺术领域有着广泛的应用,为艺术家和艺术品收藏家带来了新的机遇和挑战。以下是 NFT 技术在艺术领域的一些主要应用。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1. 数字艺术品确权和交…

【Spring Boot】Spring AOP动态代理,以及静态代理

目录 Spring AOP代理一. 代理的概念二. 静态代理三. JDK代理3.1 重写 invoke 方法进⾏功能增强3.2 通过Proxy类随机生成代理对象 四. CGLIB代理4.1 自定义类来重写intercept方法4.2 通过Enhancer类的create方法来创建代理类 五. AOP源码剖析 总结(重中之重,精华) Sp…

【人工智能】—基于成都市各区(市)县租房价格预测建模研究

引言 随着城市化进程的加速,人口流动日益频繁,租房市场作为城市生活的重要组成部分,其价格波动对居民生活质量和城市经济发展具有显著影响。成都市,作为中国西部地区的经济、文化、交通和科技中心,近年来吸引了大量人…

昇思25天学习打卡营第17天|linchenfengxue

RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务,是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型,实现如下的效果: 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This fil…

1.8.0-矩阵乘法的反向传播-简单推导

1相关资料 之前分享过一个博客里面写的,我们大致了解并记住结论的博客:【深度学习】7-矩阵乘法运算的反向传播求梯度_矩阵梯度公式-CSDN博客;这里再分享一下自然语言处理书上关于这部分的推导过程:3-矩阵相乘-梯度反向传播的计算…

开源模型应用落地-FastAPI-助力模型交互-进阶篇(一)

一、前言 FastAPI 的高级用法可以为开发人员带来许多好处。它能帮助实现更复杂的路由逻辑和参数处理,使应用程序能够处理各种不同的请求场景,提高应用程序的灵活性和可扩展性。 在数据验证和转换方面,高级用法提供了更精细和准确的控制&#…

上海慕尼黑电子展开展,启明智显携物联网前沿方案亮相

随着科技创新的浪潮不断涌来,上海慕尼黑电子展在万众瞩目中盛大开幕。本次展会汇聚了全球顶尖的电子产品与技术解决方案,成为业界瞩目的焦点。启明智显作为物联网彩屏显示领域的佼佼者携产品亮相展会,为参展者带来了RTOS、LINUX全系列方案及A…

【见刊通知】MVIPIT 2023机器视觉、图像处理与影像技术国际会议

MVIPIT 2023:https://ieeexplore.ieee.org/xpl/conhome/10578343/proceeding 入库Ei数据库需等20-50天左右 第二届会议征稿启动(MVIPIT 2024) The 2nd International Conference on Machine Vision, Image Processing & Imaging Techn…

java —— tomcat 部署项目

一、通过 war 包部署 1、将项目导出为 war 包; 2、将 war 包放置在 tomcat 目录下的 webapps 文件夹下,该 war 包稍时便自动解析为项目文件夹; 3、启动 tomcat 的 /bin 目录下的 startup.bat 文件,此时即可从浏览器访问项目首页…

3.Python学习:模块\包\yaml

1.模块与包–互相引用 (1)一个模块就是一个.py文件 (2)有模块的目录–文件夹 (3)包:文件夹包含__init__.py文件 (4)导入包时,init.py文件里的内容会执行一次 …

聚鼎装饰画:装饰画喊个与现在是什么情况

回眸历史长河,装饰画以其独特的魅力一直为人类生活环境添彩增趣。从古埃及的壁画到文艺复兴时期的油画,再到现代简约的线条画,装饰画如同时代的缩影,映射出不同历史阶段的文化特征与审美趣味。 在现代社会,装饰画的现状…

C语言学习笔记[21]:分支语句if...else

C语言是结构化的程序设计语言 顺序结构选择结构循环结构 分支语句对应的就是选择结构,循环语句对应的就是循环结构 分支语句 if...elseswitch 循环语句 whilefordo...while goto语句 语句 C语言中由分号隔开的就是一条语句,比如: #…

猫咪浮毛多怎么办?一分钟推荐性价比高的养猫空气净化器排名

作为一名猫咖店老板,我发现很多铲屎官来店里咨询,在春夏换季时会频繁打喷嚏、全身过敏红肿。这是因为猫咪在换季时会大量掉毛,家里就像下雪一样,空气中充满了猫毛。这些猫毛上附带的细菌会随浮毛被人吸入,从而引发打喷…