densepose与SMPL之IUV坐标转XYZ坐标

article/2025/9/28 4:55:33

具体流程

一、SMPL模型

SMPL模型拥有6890个XYZ坐标的3D人体点,目前第一步需要将这6890个人体点进行分析,并将不同部位的点位进行归并,具体分为以下几个部分:头部,胸部,腰部,左臂,右臂,左腿,右腿。因6890个XYZ坐标并非连续的坐标,是先描述左半边身体,然后再描述右半边身体,所以需花费较长时间分类。

上述分类区域的部署是为了方便找到具体点位的XYZ坐标范围,方便对应IUV坐标。

二、具体流程

实现具体流程

推理Densepose得到IUV坐标
将IUV坐标转换成XYZ坐标
根据预先找到各个点位的XYZ坐标范围保存IUV坐标范围
于原图中匹配到IUV坐标范围的位置索引
用位置索引在原图中画出点位

若找到了各个点位的IUV坐标范围后,后期推理流程会省略将IUV坐标转换成XYZ坐标去匹配点位的XYZ坐标这个步骤,演变成下面流程:

推理Densepose得到IUV坐标
匹配当前点位IUV坐标并直接得到位置索引
用位置索引在原图中画出点位

其中SMPL人体XYZ坐标点充当中间变量,转换得到人体的IUV坐标后就不需用到了。

三、目前实现左眼位置代码

总体代码可以参考densepose的notebook示例,以及一个densepose的IUV坐标转换XYZ进行贴图的notebook示例。

1、下载SMPL人体模型:

请添加图片描述

2、安装依赖:

pip install chumpy
pip install tqdm

3、具体代码

首先加载SMPL模型,并绘制具体人体模型:

# 导入包
import pickle
import numpy as np
import matplotlib.pyplot as pltfrom tqdm import tqdm
from PIL import Image
from mpl_toolkits.mplot3d import axes3d, Axes3D # Now read the smpl model.
with open('./models/basicmodel_m_lbs_10_207_0_v1.1.0.pkl', 'rb') as f:data = pickle.load(f, encoding='iso-8859-1')Vertices = data['v_template']  ##  Loaded vertices of size (6890, 3)X,Y,Z = [Vertices[:,0], Vertices[:,1],Vertices[:,2]]fig = plt.figure(figsize=[150,30])ax = fig.add_subplot(141, projection='3d')
ax.scatter(Z, X, Y, s=0.02, c='k')
smpl_view_set_axis_full_body(ax)ax = fig.add_subplot(142, projection='3d')
ax.scatter(Z,X,Y,s=0.02,c='k')
smpl_view_set_axis_full_body(ax,45)ax = fig.add_subplot(143, projection='3d')
ax.scatter(Z,X,Y,s=0.02,c='k')
smpl_view_set_axis_full_body(ax,90)ax = fig.add_subplot(144, projection='3d')
ax.scatter(Z,X,Y, s=1, c='k')
smpl_view_set_axis_full_body(ax,0)plt.show()

然后画出每几个连续点位然后区分人体区域:头部,胸部,腰部,左臂,右臂,左腿,右腿。目前左眼的部分点位区域为:2778~2800。

def plot_points(begin, end, b, e, body=False, face=True):Z_ft, X_ft, Y_ft = Z[begin:end], X[begin:end], Y[begin:end]fig = plt.figure(figsize=[40,10])if body:ax = fig.add_subplot(141, projection='3d')ax.scatter(Z_ft, X_ft, Y_ft, s=0.02, c='k')ax.scatter(Z_ft[b:e], X_ft[b:e], Y_ft[b:e], s=15, c='r')smpl_view_set_axis_full_body(ax)plt.show()if face:ax = fig.add_subplot(144, projection='3d')ax.scatter(Z_ft, X_ft, Y_ft, s=1, c='k')ax.scatter(Z_ft[b:e], X_ft[b:e], Y_ft[b:e], s=15, c='r')smpl_view_set_axis_face(ax,0)plt.show()# 画出左眼点位图        
plot_points(0, -1, 2778, 2800, False, True)     # 得到左眼位置的XYZ坐标范围
Z_ft, X_ft, Y_ft = Z[0:6800], X[0:6800], Y[0:6800]
Z_part, X_part, Y_part = Z_ft[2780:2800], X_ft[2780:2800], Y_ft[2780:2800]Z_leye = []
X_leye = []
Y_leye = []
for i in range(len(Z_ft)):if Z_ft[i] >= min(Z_part) and Z_ft[i] <= max(Z_part) and X_ft[i] >= min(X_part) and X_ft[i] <= max(X_part) and Y_ft[i] >= min(Y_part) and Y_ft[i] <= max(Y_part):Z_leye.append(Z_ft[i])X_leye.append(X_ft[i])Y_leye.append(Y_ft[i])

定义densepose的IUV坐标转换为XYZ的代码:

import numpy as np
import copy
import cv2
from scipy.io  import loadmat
import scipy.spatial.distance
import os class DensePoseMethods:def __init__(self):#ALP_UV = loadmat( './UV_Processed.mat'  ) # Use your own pathself.FaceIndices = np.array( ALP_UV['All_FaceIndices']).squeeze()self.FacesDensePose = ALP_UV['All_Faces']-1self.U_norm = ALP_UV['All_U_norm'].squeeze()self.V_norm = ALP_UV['All_V_norm'].squeeze()self.All_vertices =  ALP_UV['All_vertices'][0]def barycentric_coordinates_fast(self, P0, P1, P2, P):# This is a merge of barycentric_coordinates_exists & barycentric_coordinates.# Inputs are (n, 3) shaped.u = P1 - P0   #u is (n,3)v = P2 - P0   #v is (n,3)w = P.T - P0    #w is (n,3)#vCrossW = np.cross(v, w) #result is (n,3)vCrossU = np.cross(v, u) #result is (n,3)A = np.einsum('nd,nd->n', vCrossW, vCrossU) # vector-wise dot product. Result shape is (n,)#uCrossW = np.cross(u, w)uCrossV = - vCrossUB = np.einsum('nd,nd->n', uCrossW, uCrossV)#sq_denoms = np.einsum('nd,nd->n', uCrossV, uCrossV) #result shape is  (n,)sq_rs = np.einsum('nd,nd->n', vCrossW, vCrossW)sq_ts = np.einsum('nd,nd->n', uCrossW, uCrossW)rs = np.sqrt(sq_rs / sq_denoms)  #result shape is  (n,)ts = np.sqrt(sq_ts / sq_denoms)#results = [None] * P0.shape[0]for i in range(len(results)):if not (A[i] < 0 or B[i] < 0):if ((rs[i] <= 1) and (ts[i] <= 1) and (rs[i] + ts[i] <= 1)):results[i] = (1 - (rs[i] + ts[i]) , rs[i], ts[i])return resultsdef IUV2FBC_fast( self, I_point , U_point, V_point):P = np.array([ U_point , V_point , 0 ])FaceIndicesNow  = np.where( self.FaceIndices == I_point )FacesNow = self.FacesDensePose[FaceIndicesNow]#P_0 = np.vstack( (self.U_norm[FacesNow][:,0], self.V_norm[FacesNow][:,0], np.zeros(self.U_norm[FacesNow][:,0].shape))).transpose()P_1 = np.vstack( (self.U_norm[FacesNow][:,1], self.V_norm[FacesNow][:,1], np.zeros(self.U_norm[FacesNow][:,1].shape))).transpose()P_2 = np.vstack( (self.U_norm[FacesNow][:,2], self.V_norm[FacesNow][:,2], np.zeros(self.U_norm[FacesNow][:,2].shape))).transpose()#bcs = self.barycentric_coordinates_fast(P_0, P_1, P_2, P)for i, bc in enumerate(bcs):if bc is not None:bc1,bc2,bc3 = bcreturn(FaceIndicesNow[0][i],bc1,bc2,bc3)## If the found UV is not inside any faces, select the vertex that is closest!#D1 = scipy.spatial.distance.cdist( np.array( [U_point,V_point])[np.newaxis,:] , P_0[:,0:2]).squeeze()D2 = scipy.spatial.distance.cdist( np.array( [U_point,V_point])[np.newaxis,:] , P_1[:,0:2]).squeeze()D3 = scipy.spatial.distance.cdist( np.array( [U_point,V_point])[np.newaxis,:] , P_2[:,0:2]).squeeze()#minD1 = D1.min()minD2 = D2.min()minD3 = D3.min()#if((minD1< minD2) & (minD1< minD3)):return(  FaceIndicesNow[0][np.argmin(D1)] , 1.,0.,0. )elif((minD2< minD1) & (minD2< minD3)):return(  FaceIndicesNow[0][np.argmin(D2)] , 0.,1.,0. )else:return(  FaceIndicesNow[0][np.argmin(D3)] , 0.,0.,1. )def FBC2PointOnSurface( self, FaceIndex, bc1,bc2,bc3,Vertices ):##Vert_indices = self.All_vertices[self.FacesDensePose[FaceIndex]]-1##p = Vertices[Vert_indices[0],:] * bc1 +  \Vertices[Vert_indices[1],:] * bc2 +  \Vertices[Vert_indices[2],:] * bc3 ##return(p)

读取照片face1.png的IUV保存结果,并将IUV坐标转换为XYZ坐标并将在左眼位置的IUV坐标都保存下来。

iuv_arr = np.load('./saved/iuv_gao1.npy')
print('iuv_arr.shape: ', iuv_arr.shape)
INDS = iuv_arr[0,:,:]
pick_idx = 1   # PICK PERSON INDEX!C = np.where(INDS >= pick_idx)iuv0 = iuv_arr[0,:,:]
iuv1 = iuv_arr[1,:,:]
iuv2 = iuv_arr[2,:,:]
IUV = np.concatenate((iuv0[:,:,np.newaxis], iuv1[:,:,np.newaxis], iuv2[:,:,np.newaxis]), axis=2)
print('IUV shape:', IUV.shape)print('num pts on picked person:', C[0].shape)
IUV_pick = IUV[C[0], C[1], :]  # boolean indexing
IUV_pick = IUV_pick.astype(np.float)
IUV_pick[:, 1:3] = IUV_pick[:, 1:3] / 255.0
collected_x = np.zeros(C[0].shape)
collected_y = np.zeros(C[0].shape)
collected_z = np.zeros(C[0].shape)# 开始将IUV坐标转换为XYZ坐标,并将对应位置的IUV坐标保存下来为iuv_list。
DP = DensePoseMethods()
pbar = tqdm(total=IUV_pick.shape[0])iuv_list = []
for i in range(IUV_pick.shape[0]):pbar.update(1) # Use tqdm to visualize the converting process# Convert IUV to FBC (faceIndex and barycentric coordinates.)FaceIndex,bc1,bc2,bc3 = DP.IUV2FBC_fast(IUV_pick[i, 0], IUV_pick[i, 1], IUV_pick[i, 2])# Use FBC to get 3D coordinates on the surface.p = DP.FBC2PointOnSurface( FaceIndex, bc1,bc2,bc3,Vertices )#collected_x[i] = p[0]collected_y[i] = p[1]collected_z[i] = p[2]if p[0] >= min(X_leye) and p[0] <= max(X_leye) and p[1] >= min(Y_leye) and p[1] <= max(Y_leye) and p[2] >= min(Z_leye) and p[2] <= max(Z_leye):save_iuv = [IUV_pick[i, 0], IUV_pick[i, 1], IUV_pick[i, 2]]iuv_list.append(save_iuv)pbar.close()
print('IUV to PointOnSurface done')# 得到iuv单独的范围list
print(len(iuv_list))
i_leye_list = []
u_leye_list = []
v_leye_list = []
for cur_iuv_arr in iuv_list:i_leye_list.append(cur_iuv_arr[0])u_leye_list.append(cur_iuv_arr[1])v_leye_list.append(cur_iuv_arr[2])

在原图中画出左眼区域:

img = cv2.imread('./saved/face1.png')
bbox_xywh = [4.8355975, 0., 462.35657, 667.8104]  # densepose推理得到的人体区域
x, y, w, h = int(bbox_xywh[0]), int(bbox_xywh[1]), int(bbox_xywh[2]), int(bbox_xywh[3])
crop_img = img[y:y+h, x:x+w]   # 人体区域图像
i_face1 = iuv_arr[0,:,:]
u_face1 = iuv_arr[1,:,:]
v_face1 = iuv_arr[2,:,:]
U = u_face1.astype(float) / 255.0
V = v_face1.astype(float) / 255.0# 显示cv2格式的图
def show_pic_hor(pic_list):nums = len(pic_list)plt_nums = np.ceil(np.sqrt(nums))for i in range(nums):plt.subplot(1, nums, i + 1)if pic_list[i].ndim == 3:try:cur_pic_rgb = cv2.cvtColor(pic_list[i], cv2.COLOR_BGR2RGB)except:cur_pic_rgb = pic_list[i]plt.imshow(cur_pic_rgb)elif pic_list[i].ndim == 2:plt.imshow(pic_list[i], 'gray')plt.show()# 画左眼的函数
def plot_left_eye(crop_img, iuv_list, i_face1, U, V):img_cut = crop_img.copy()k = 0for i in range(img_cut.shape[0]):for j in range(img_cut.shape[1]):if i_face1[i,j] == iuv_list[k][0] and U[i,j] == iuv_list[k][1] and V[i,j] == iuv_list[k][2]:cv2.circle(img_cut, (j,i), 1, (0,0,255), 0)k += 1if k == len(iuv_list):return img_cutreturn img_cutimg_cut = plot_left_eye(crop_img, iuv_list, i_face1, U, V)
show_pic_hor([img_cut])

参考项目:
(1)Densepose:https://github.com/facebookresearch/DensePose/blob/main/notebooks/DensePose-COCO-on-SMPL.ipynb
(2)Detectron2:https://github.com/facebookresearch/detectron2
(2)Densepose IUV-XYZ:https://github.com/linjunyu/Detectron2-Densepose-IUV2XYZ


http://chatgpt.dhexx.cn/article/FAmDDXmV.shtml

相关文章

人体捕捉:《SMPL》

《SMPL: A Skinned Multi-Person Linear Model》 作者&#xff1a;Matthew Loper 主页&#xff1a;https://smpl.is.tue.mpg.de/ 时间&#xff1a;2015 文章目录 Table of NotationModel generation functionsModel input parameters(controls)Model parameters(parameters le…

python3跑通smpl模型_Python smpl-pytorch包_程序模块 - PyPI - Python中文网

Pythorch的SMPL层 SMPL人体[1]层为PyTorch(用v0.4和v1.x测试) 是一个可微的pytorch层,它确定地从姿势和形状参数映射到人体关节和顶点。 它可以作为可微层集成到任何体系结构中,以预测实体网格。 代码由Yana Hasson改编自manopth存储库。 安装 您可以从PyPI:安装smpl pytorc…

SMPL STAR人体模型系列

SCAPE: 人体模型 SMPL: 人体模型 SMPL-H: 人体手 SMPL-X: 人体手人脸&#xff08;FLAME&#xff09; STAR: SMPL的改进版 人体模型主要思想是将pose, shape解耦&#xff0c;用参数化的方式描述人体表面的信息。 SCAPE&#xff1a;基于三角面片deformable的人体模型。 SMPL…

SMPL模型及源码解读

Contents Preface一、模型解读二、源码解读Citation Preface SMPL主要是人体三维重建常用模型&#xff0c;本文主要对模型及源码进行了解读&#xff08;自己的理解不一定正确&#xff09;&#xff0c;为以后更好的利用此模型进行人体重建打好基础&#xff01; 一、模型解读 二…

SMPL:数据增强之处理pose和3d点

SMPL 是一个低维度的参数化人体模型&#xff0c;SMPL系数有pose和shape&#xff0c;在训练基于SMPL的3Dmesh重建任务时候&#xff0c;一般需要进行数据增强 例如旋转&#xff0c;镜像等&#xff0c;那当GT 中有pose和3d点的时候&#xff0c;怎么处理&#xff1f; 一 &#xff1…

SMPL源码实现及相关问题

SMPL源码实现及相关问题 SMPL模型代码结构SMPL实现具体步骤一、环境的配置二、相关库的安装三、运行SMPL CITATION SMPL模型 SMPL模型官网http://smpl.is.tue.mpg.de/&#xff0c;里面可以下载模型和观看演示视频&#xff0c;只不过要先注册一个账号。SMPL由Michael J. Black团…

smpl-x论文学习-部分翻译

论文地址&#xff1a;Expressive Body Capture: 3D Hands, Face, and Body from a Single Image 知乎大佬的讲解&#xff1a;https://zhuanlan.zhihu.com/p/137235901 另一位大佬的讲解&#xff1a;https://posts.careerengine.us/p/5f23a5898988c12b4302afb6 1. 定性结果 和 …

人体动作捕捉与SMPL模型 (mocap and SMPL model)

人体动作捕捉与SMPL模型 (mocap and SMPL model) FesianXu 2020.7.5 前言 笔者最近在做和motion capture动作捕捉相关的项目&#xff0c;学习了一些关于人体3D mesh模型的知识&#xff0c;其中以SMPL模型最为常见&#xff0c;笔者特在此进行笔记&#xff0c;希望对大家有帮助&a…

blender 绘制离散顶点, SMPL骨架绘制

给定一些点&#xff0c;如何绘制出来&#xff0c;借助 blender 看下效果。纠结于 unity 还是 blender&#xff0c; 最终还是 blender 了。 目前还都不太满意&#xff0c;思路一比较靠谱&#xff0c;但是需要更复杂的计算 思路一&#xff0c;第二版&#xff0c;已完成&#xff…

SMPL-CN

paper-reading 为方便理解smpl文章的主要实现思想&#xff0c;此文为论文中文解读&#xff0c;资料来源zju。 日后有空&#xff0c;会写出论文的主要推导过程以及值得注意的重点。 摘要&#xff1a; 我们提出了一个人体形状和姿势相关的形状变化的学习模型&#xff0c;它比以前…

SMPL源码解读

这是源码的整体结构&#xff0c;先简单说一下各个文件里面是什么。 一、models文件 包含3个模型的.pkl文件&#xff0c;.pkl文件是python提供的可以以字符串的形式记录对象、变量的文件格式。这个模型里面包括了: 1.J_regressor_prior:关节回归矩阵的先验&#xff0c;保存形…

SMPL 人体模型简要

smpl是指2015 马普的一篇文章“SMPL: a skinned multi-person linear model”中构建的人体参数化三维模型&#xff0c;人体可以理解为是一个基础模型和在该模型基础上进行形变的总和&#xff0c;在形变基础上进行PCA&#xff0c;得到刻画形状的低维参数——形状参数&#xff08…

SMPL-论文解读

文章目录 创新点算法shape blend shape&#xff1a;pose blend shapeJoint locationSMPL model 训练过程Pose Parameter Trainingjoint regressorShape Parameter TrainingOptimization summary DMPL实验结论 论文&#xff1a; 《SMPL: A skinned multi-person linear model》…

SMPL模型进阶

SMPL模型是一种参数化人体模型&#xff0c;是马普所提出的一种人体建模方法&#xff0c;该方法可以进行任意的人体建模和动画驱动。这种方法与传统的LBS的最大的不同在于其提出的人体姿态影像体表形貌的方法&#xff0c;这种方法可以模拟人的肌肉在肢体运动过程中的凸起和凹陷。…

SMPL源代码实现和模型解读

对于SLAM的工作已经告一段落了&#xff0c;传统的人体动态三维重建也要告一段落了&#xff0c;由于课题研究的方向是基于图像\视频的人体三维重建&#xff0c;三维shape、pose的恢复&#xff1a;所以今天和大家交流的是SMPL模型&#xff0c;对于SMPL模型的理论部分欢迎大家交流…

SMPL简析

SMPL模型是一种参数化人体模型&#xff0c;是马普所提出的一种人体建模方法。这种方法可以模拟人的肌肉在肢体运动过程中的凸起和凹陷。因此可以避免人体在运动过程中的表面失真&#xff0c;可以精准的刻画人的肌肉拉伸以及收缩运动的形貌。 该模型可以通过身体形状参数和姿势参…

SMPL学习笔记

文章目录 前言一、SMPL概述1.形状参数( β \beta β)2.姿态参数( θ \theta θ) 二、体姿转换过程原理1.基于形状的混合成形(Shape Blend Shapes)2.基于姿态的混合成形 (Pose Blend Shapes)3.蒙皮 (Skinning) 三、具体过程分析1.基于形状的混合成形2.基于姿态的混合成形3.骨骼点…

Java的生产者消费者模型

前言 学完了线程后&#xff0c;我又去找了一些线程相关的练习题来练手&#xff0c;其中印象最深的就是生产者消费者模型这一块&#xff0c;为什么呢&#xff0c;因为它每一篇练习题里都有&#xff0c;开始没看懂&#xff0c;后面就去仔细研究了一下&#xff0c;哦&#xff0c;…

多线程之生产者消费者模型

生产者消费者模型 1.为什么要使用生产者和消费者模式2.案例 1.为什么要使用生产者和消费者模式 在线程开发中&#xff0c;生产者就是生产线程的线程&#xff0c;消费者就是消费线程的线程。在多线程开发中&#xff0c;如果生产者如理数据很快&#xff0c;消费者处理数据很慢&am…

生产者消费者模型(多线程工作)

目录 1.模型前提 2.阻塞队列&#xff08;消费场所&#xff09; 3. 实验 4.有关效率 1.模型前提 以单生产者对单消费者为例子&#xff1a; 前提一&#xff1a;有一个缓冲区作为消费场所。 前提二&#xff1a;有两种功能不同的线程分别具有消费与生产的能力。 前提三&…