【3维视觉】mesh采样成sdf代码分析,sample_SDF_points

2023-10-23 23:10

本文主要是介绍【3维视觉】mesh采样成sdf代码分析,sample_SDF_points,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

sample_SDF_points的完整代码

gpu_id = '5'
source_dir = 'demo_data/input'
target_dir = 'demo_data/output'
all_classes = ["02691156","04090263"
]
num_samples_and_method = [(100000, 'uniformly'), (100000, 'near')]
################################################import os
os.environ['CUDA_VISIBLE_DEVICES'] = gpu_id
from datetime import datetime
import numpy as np
import torch
from utils import *for c in all_classes:input_dir = os.path.join(source_dir, c)output_dir = os.path.join(target_dir, c)os.makedirs(output_dir, exist_ok=True)all_shapes = os.listdir(input_dir)all_shapes = [f.split('.')[0] for f in all_shapes]for i, shape_id in enumerate(all_shapes):print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), c, 'processing: %d/%d'%(i,len(all_shapes)))in_path = os.path.join(input_dir, shape_id+'.obj')out_path = os.path.join(output_dir, shape_id+'.npy')vertices, faces = load_obj(in_path)mesh = obj2nvc(vertices, faces).cuda()mesh_normals = face_normals(mesh)distrib = area_weighted_distribution(mesh, mesh_normals)xyz = sample_points(mesh, num_samples_and_method, mesh_normals, distrib)sd = points_mesh_signed_distance(xyz, mesh)xyz_sd = torch.cat([xyz, sd.unsqueeze(1)], dim=1)rand_idx = torch.randperm(xyz_sd.shape[0])xyz_sd = xyz_sd[rand_idx].cpu().numpy()np.save(out_path, xyz_sd)

导入obj读点面:vertices, faces = load_obj(in_path)

def load_obj(filename):"""Args:filename: str, path to .obj fileReturns:vertices: tensor(float), shape (num_vertices, 3)faces: tensor(long), shape (num_faces, 3)"""assert os.path.exists(filename), 'File \''+filename+'\' does not exist.'reader = tinyobjloader.ObjReader() # 构造tinyobjloader的ObjReader类config = tinyobjloader.ObjReaderConfig()config.triangulate = Truereader.ParseFromFile(filename, config) # 从文件filename中按配置config读objattrib = reader.GetAttrib()  # 得到逐点的属性vertices = torch.FloatTensor(attrib.vertices).reshape(-1, 3) # 得到n*3维的张量shapes = reader.GetShapes()faces = []for shape in shapes:faces += [idx.vertex_index for idx in shape.mesh.indices]faces = torch.LongTensor(faces).reshape(-1, 3)return vertices, faces

attrib = reader.GetAttrib()

返回的是attrib_t类型
其中attrib.vertices是将每个点的3个坐标依次排放在一个list中
比如有n个点,得到的attrib.vertices的维度就是3n
在这里插入图片描述

class attrib_t(__pybind11_builtins.pybind11_object):# no docdef numpy_vertices(self): # real signature unknown; restored from __doc__""" numpy_vertices(self: tinyobjloader.attrib_t) -> numpy.ndarray[float64] """passdef __init__(self): # real signature unknown; restored from __doc__""" __init__(self: tinyobjloader.attrib_t) -> None """passcolors = property(lambda self: object(), lambda self, v: None, lambda self: None)  # defaultnormals = property(lambda self: object(), lambda self, v: None, lambda self: None)  # defaulttexcoords = property(lambda self: object(), lambda self, v: None, lambda self: None)  # defaultvertices = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

vertices = torch.FloatTensor(attrib.vertices).reshape(-1, 3)

torch.FloatTensor()

类型转换, 将list ,numpy转化为tensor,其中的每个数据是float类型

reshape(-1,3)

维度转换,将tensor转换3列的维度,(-1)表示转换后行的维度需要计算得到。
在这里插入图片描述

shapes = reader.GetShapes()

shapes的数据结构
在这里插入图片描述
shape.mesh.indices的数据结构
在这里插入图片描述

class shape_t(__pybind11_builtins.pybind11_object):# no docdef __init__(self): # real signature unknown; restored from __doc__""" __init__(self: tinyobjloader.shape_t) -> None """passlines = property(lambda self: object(), lambda self, v: None, lambda self: None)  # defaultmesh = property(lambda self: object(), lambda self, v: None, lambda self: None)  # defaultname = property(lambda self: object(), lambda self, v: None, lambda self: None)  # defaultpoints = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

faces += [idx.vertex_index for idx in shape.mesh.indices]

得到的faces的维度为:301,656, 把每个面的三个点索引依次排放到list中
在这里插入图片描述

faces = torch.LongTensor(faces).reshape(-1, 3)

得到的faces的维度为:100,552x3,表示的就是有100552个面,每行代表组成这个面的三个顶点的索引

转换数据格式为N面-3点-xyz: mesh = obj2nvc(vertices, faces).cuda()

def obj2nvc(vertices, faces):"""Args:vertices: tensor(float), shape (num_vertices, 3)faces: tensor(long), shape (num_faces, 3)Returns:mesh: tensor(float), shape (num_faces, 3, 3), (num_faces, 3 vertices, xyz coordinates)"""mesh = vertices[faces.flatten()].reshape(faces.size()[0], 3, 3)return mesh.contiguous()

mesh = vertices[faces.flatten()].reshape(faces.size()[0], 3, 3)

得到的mesh的维度是100,552x3x3,即每个面对应的3个点,每一个点对应的3个坐标值
在这里插入图片描述

求每个面的法线:mesh_normals = face_normals(mesh)

def face_normals(mesh):"""Args:mesh: tensor(float), shape (num_faces, 3, 3)Returns:normals: tensor(float), shape (num_faces, 3)"""vec_a = mesh[:, 0] - mesh[:, 1]  # 每个三角形面的第0个顶点坐标-第1个顶点坐标=矢量10vec_b = mesh[:, 1] - mesh[:, 2]  # 每个三角形面的第1个顶点坐标-第2个顶点坐标=矢量21normals = torch.cross(vec_a, vec_b)  # 计算两个矢量的叉乘得到法线return normals

分析面的分布:distrib = area_weighted_distribution(mesh, mesh_normals)

def area_weighted_distribution(mesh, normals=None):"""Args:mesh: tensor(float), shape (num_faces, 3, 3)normals: tensor(float), shape (num_faces, 3)Returns:distrib: distribution"""if normals is None:normals = face_normals(mesh)areas = torch.norm(normals, p=2, dim=1) * 0.5areas /= torch.sum(areas) + 1e-10distrib = torch.distributions.Categorical(areas.view(-1))return distrib

torch.norm()

对输入张量input在给定维度dim上求p范数

def norm(input, p="fro", dim=None, keepdim=False, out=None, dtype=None)

参考:
torch.norm()函数的用法

范数理解

重头戏1:xyz = sample_points(mesh, num_samples_and_method, mesh_normals, distrib)

num_samples_and_method = [(100000, 'uniformly'), (100000, 'near')]

从mesh模型上采样点

def sample_points(mesh, num_samples_and_method, normals=None, distrib=None):"""Args:mesh: tensor(float), shape (num_faces, 3, 3)num_samples_and_method: [tuple(int, str)]normals: tensor(float), shape (num_faces, 3)distrib: distributionReturns:samples: tensor(float), shape (num_samples, 3)"""if normals is None:normals = face_normals(mesh)if distrib is None:distrib = area_weighted_distribution(mesh, normals)samples = []for num_samples, method in num_samples_and_method:if method == 'uniformly':samples.append(sample_uniformly(mesh, num_samples))elif method == 'surface':samples.append(sample_on_surface(mesh, num_samples, normals, distrib)[0])elif method == 'near':samples.append(sample_near_surface(mesh, num_samples, normals, distrib))samples = torch.cat(samples, dim=0)return samples

均匀采样:sample_uniformly(mesh, num_samples)

def sample_uniformly(mesh, num_samples):"""sample uniformly in [-1,1] bounding volume.Args:mesh: tensor(float), shape (num_faces, 3, 3)num_samples: intReturns:samples: tensor(float), shape (num_samples, 3)"""samples = (torch.rand(num_samples, 3) - 0.5) * 1.1samples = samples.to(mesh.device)return samples

每个面上采样:sample_on_surface(mesh, num_samples, normals, distrib)

squeeze:压缩维度,
unsqueeze:膨胀维度,unsqueeze(-1)在最后一维增加一个维度

def sample_on_surface(mesh, num_samples, normals=None, distrib=None):"""Args:mesh: tensor(float), shape (num_faces, 3, 3)num_samples: intnormals: tensor(float), shape (num_faces, 3)distrib: distributionReturns:samples: tensor(float), shape (num_samples, 3)normals: tensor(float), shape (num_samples, 3)"""if normals is None:normals = face_normals(mesh)if distrib is None:distrib = area_weighted_distribution(mesh, normals)idx = distrib.sample([num_samples])selected_faces = mesh[idx]selected_normals = normals[idx]u = torch.sqrt(torch.rand(num_samples)).to(mesh.device).unsqueeze(-1)v = torch.rand(num_samples).to(mesh.device).unsqueeze(-1)samples = (1 - u) * selected_faces[:,0,:] + (u * (1 - v)) * selected_faces[:,1,:] + u * v * selected_faces[:,2,:]  # 每个面上采样了一个点return samples, selected_normals

sample_near_surface(mesh, num_samples, normals, distrib)

def sample_near_surface(mesh, num_samples, normals=None, distrib=None):"""Args:mesh: tensor(float), shape (num_faces, 3, 3)num_samples: intnormals: tensor(float), shape (num_faces, 3)distrib: distributionReturns:samples: tensor(float), shape (num_samples, 3)"""samples = sample_on_surface(mesh, num_samples, normals, distrib)[0] #  面上采样samples += torch.randn_like(samples) * 0.01 # 面上采样完后再对坐标进行一些偏移return samples

torch.randn为从标准正太分布(均值为0,方差为1)中随机选择的一组数

重头戏2:sd = points_mesh_signed_distance(xyz, mesh)

求采样点到mesh表面的距离

def points_mesh_signed_distance(points, mesh):"""Args:points: tensor(float), shape (num_points, 3)mesh: tensor(float), shape (num_faces, 3, 3)Returns:sd: tensor(float), shape (num_points,)"""sd = mesh2sdf.mesh2sdf_gpu(points, mesh)[0]  # nvidia实现的C++,gpu版本的mesh2sdfreturn sd

将坐标xyz和到表面距离sd拼接起来: xyz_sd = torch.cat([xyz, sd.unsqueeze(1)], dim=1)

sd.unsqueeze(1),比如原来
sd.shape 为 torch.Size([num_samples])
sd.unsqueeze(1).shape为 torch.Size([num_samples,1])

这篇关于【3维视觉】mesh采样成sdf代码分析,sample_SDF_points的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/271064

相关文章

Java List排序实例代码详解

《JavaList排序实例代码详解》:本文主要介绍JavaList排序的相关资料,Java排序方法包括自然排序、自定义排序、Lambda简化及多条件排序,实现灵活且代码简洁,文中通过代码介绍的... 目录一、自然排序二、自定义排序规则三、使用 Lambda 表达式简化 Comparator四、多条件排序五、

Java 压缩包解压实现代码

《Java压缩包解压实现代码》Java标准库(JavaSE)提供了对ZIP格式的原生支持,通过java.util.zip包中的类来实现压缩和解压功能,本文将重点介绍如何使用Java来解压ZIP或RA... 目录一、解压压缩包1.zip解压代码实现:2.rar解压代码实现:3.调用解压方法:二、注意事项三、总

Linux实现简易版Shell的代码详解

《Linux实现简易版Shell的代码详解》本篇文章,我们将一起踏上一段有趣的旅程,仿照CentOS–Bash的工作流程,实现一个功能虽然简单,但足以让你深刻理解Shell工作原理的迷你Sh... 目录一、程序流程分析二、代码实现1. 打印命令行提示符2. 获取用户输入的命令行3. 命令行解析4. 执行命令

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

uniapp小程序中实现无缝衔接滚动效果代码示例

《uniapp小程序中实现无缝衔接滚动效果代码示例》:本文主要介绍uniapp小程序中实现无缝衔接滚动效果的相关资料,该方法可以实现滚动内容中字的不同的颜色更改,并且可以根据需要进行艺术化更改和自... 组件滚动通知只能实现简单的滚动效果,不能实现滚动内容中的字进行不同颜色的更改,下面实现一个无缝衔接的滚动

利用Python实现可回滚方案的示例代码

《利用Python实现可回滚方案的示例代码》很多项目翻车不是因为不会做,而是走错了方向却没法回头,技术选型失败的风险我们都清楚,但真正能提前规划“回滚方案”的人不多,本文从实际项目出发,教你如何用Py... 目录描述题解答案(核心思路)题解代码分析第一步:抽象缓存接口第二步:实现两个版本第三步:根据 Fea

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

QT6中绘制UI的两种方法详解与示例代码

《QT6中绘制UI的两种方法详解与示例代码》Qt6提供了两种主要的UI绘制技术:​​QML(QtMeta-ObjectLanguage)​​和​​C++Widgets​​,这两种技术各有优势,适用于不... 目录一、QML 技术详解1.1 QML 简介1.2 QML 的核心概念1.3 QML 示例:简单按钮

Java进行日期解析与格式化的实现代码

《Java进行日期解析与格式化的实现代码》使用Java搭配ApacheCommonsLang3和Natty库,可以实现灵活高效的日期解析与格式化,本文将通过相关示例为大家讲讲具体的实践操作,需要的可以... 目录一、背景二、依赖介绍1. Apache Commons Lang32. Natty三、核心实现代

基于Go语言实现Base62编码的三种方式以及对比分析

《基于Go语言实现Base62编码的三种方式以及对比分析》Base62编码是一种在字符编码中使用62个字符的编码方式,在计算机科学中,,Go语言是一种静态类型、编译型语言,它由Google开发并开源,... 目录一、标准库现状与解决方案1. 标准库对比表2. 解决方案完整实现代码(含边界处理)二、关键实现细