【三维几何学习】网格上低分辨率的分割结果到高分辨率的投影与可视化

news2024/9/24 7:21:17

网格上低分辨率的分割结果到高分辨率的投影与可视化

  • 引言
  • 一、到高分辨率的投影
    • 1.1 准确率
    • 1.2 主要代码
    • 1.3 投影核心代码
  • 二、可视化代码

引言

三角网格的结构特性决定了其仅用少量三角形即可表示一个完整的3D模型。增加其分辨率可以展示更多模型的形状细节。对于网格分割来说,并不需要很多模型细节,只需要知晓其数据元素所属部分(类别)即可。
在这里插入图片描述

  • 上图分别为低分辨率分割结果、高分辨率投影结果以及Ground truth

在简化网格上进行预测,然后投影到高分辨率网格上一个可行的方案。例如:

MeshWalker1使用的的边界平滑
A Spectral Segmentation Method for Large Meshes2的feature-aware的网格简化

一、到高分辨率的投影

1.1 准确率

以面标签版本的COSEG外星人数据集为例,可参考三角网格(Triangular Mesh)分割数据集
在这里插入图片描述
简化网格上的准确率:96.94 到高分辨率网格投影:95.53
时间上也会快很多,毕竟计算高分辨率网格的输入特征较为费时

1.2 主要代码

部分代码来自3:MeshCNN
TriTransNet是对简化三角网格进行分割的网络,可替换为其它神经网络

import potpourri3d as pp3d
import numpy as np
import os
import pickle
from scipy.spatial import cKDTree
import time
import torch
from config.config import Config
from network.TriTransNet import TriTransNet
from postprocessing.mesh_project import get_faces_BorderPoints


def is_mesh_file(filename):
    return any(filename.endswith(extension) for extension in ['.obj', 'off'])


def fix_vertices(vs):
    z = vs[:, 2].copy()
    vs[:, 2] = vs[:, 1]
    vs[:, 1] = z
    max_range = 0
    for i in range(3):
        min_value = np.min(vs[:, i])
        max_value = np.max(vs[:, i])
        max_range = max(max_range, max_value - min_value)
        vs[:, i] -= min_value
    scale_by = max_range
    vs /= scale_by
    return vs


def get_seg_files(paths, seg_dir, seg_ext='.eseg'):
    segs = []
    for path in paths:
        segfile = os.path.join(seg_dir, os.path.splitext(os.path.basename(path))[0] + seg_ext)
        assert (os.path.isfile(segfile))
        segs.append(segfile)
    return segs


def make_dataset(path):
    meshes = []
    assert os.path.isdir(path), '%s is not a valid directory' % path
    for root, _, fnames in sorted(os.walk(path)):
        for fname in fnames:
            if is_mesh_file(fname):
                path = os.path.join(root, fname)
                meshes.append(path)

    return meshes


if __name__ == '__main__':
    # 简化网格
    sim_root = '../../../datasets/face_label/coseg_aliens'
    sim_paths = make_dataset(os.path.join(sim_root, 'test'))
    # sim_labels = get_seg_files(sim_paths, seg_dir=os.path.join(sim_root, 'seg'))
    # 原始网格
    org_root = '../../../datasets/aliens'  # '../../datasets/vases'
    org_paths = make_dataset(os.path.join(org_root, 'test'))   # shapes  or seg
    org_labels = get_seg_files(org_paths, seg_dir=os.path.join(org_root, 'seg'), seg_ext='.seg')

    # 网络读取
    cfg = Config()
    cfg.class_n = 4
    cfg.mode = 'seg'
    net = TriTransNet(cfg)
    state_dict = torch.load('../../../results/aliens_1500/model/latest_xyz_net.pth')  # latest_xyz_net 95.53432
    if hasattr(state_dict, '_metadata'):
        del state_dict._metadata
    net.load_state_dict(state_dict)
    net.eval()

    # 准确率统计
    all_acc = 0
    sim_acc = 0
    are_acc = 0
    for i in range(len(sim_paths)):
        # 获取网格数据
        sim_name = sim_paths[i]
        filename, _ = os.path.splitext(sim_name)
        prefix = os.path.basename(filename)
        cache = os.path.join('../../../results/aliens_1500/cache/', prefix + '.pkl')
        with open(cache, 'rb') as f:   # 不再计算 读取缓存
            meta = pickle.load(f)

        # 获取网格数据
        sim_mesh = meta['mesh']
        sim_label = meta['label']
        vs = fix_vertices(sim_mesh.vs)

        # 获取预测标签
        with torch.no_grad():
            face_features = np.concatenate([sim_mesh.face_features, sim_mesh.xyz], axis = 0)  # sim_mesh.hks[0:3]
            face_features = torch.from_numpy(face_features).float().unsqueeze(0)
            out = net(face_features, [sim_mesh])
            label = out.data.max(1)[1]
            sim_correct = label.eq(torch.from_numpy(sim_label).long()).sum().float() / sim_mesh.faces_num
            sim_acc += sim_correct
            # 面积
            # idex = label.eq(torch.from_numpy(sim_label).long()).numpy().reshape(-1)
            # face_area = sim_mesh.face_features[6, :]
            # sum_area = face_area.sum()
            # are_acc += face_area[idex].sum() / sum_area

        # 时间
        t = time.time()
        # 投影准备
        label = label.numpy().reshape(-1)
        BorderPoints_xyz, BorderPoints_label = get_faces_BorderPoints(vs, sim_mesh.faces, label, border_k=0.01, border_num=10)
        # 0.01 10 95.53432
        # 0.5 1  退化成最简单的最近邻  94.02
        kdt = cKDTree(BorderPoints_xyz)

        # 读取高分辨率网格
        org_vs, org_faces = pp3d.read_mesh(org_paths[i])
        org_vs = fix_vertices(org_vs)
        org_label = np.loadtxt(open(org_labels[i], 'r'), dtype='float64') -1
        # 原始网格中心点
        mean_vs = org_vs[org_faces]
        mean_vs = mean_vs.sum(axis=1) / 3.0

        dist, indices = kdt.query(mean_vs, workers=-1)
        # 准确率计算
        org_prolabels = BorderPoints_label[indices].reshape(-1)
        pro_cnt = np.equal(org_prolabels, org_label).sum()
        pro_acc = pro_cnt / len(org_label)
        all_acc += pro_acc
        print(filename, ':', pro_acc, ' time:', time.time()-t)
    print(all_acc / len(sim_paths))
    print(sim_acc / len(sim_paths))
    # print(are_acc / len(sim_paths))

1.3 投影核心代码

def get_faces_BorderPoints(vs, faces, labels, border_k=0.1, border_num=1):
    """
        border_k:   远离边的系数
        border_num: 每条边的边缘点数
        首先 默认简化是不会过分破坏分割边界 简化后的网格和原网格基本对齐
        1.简化后的面更大 以一个面为例 均匀采样其边界部分形成边缘点 边缘点的标签赋值为面的标签
        2.赋值原网格面标签为 距离其重心最近的简化网格边缘点标签
    """
    BorderPoints_xyz = -np.ones((len(faces) * 3 * border_num, 3), np.float64)
    BorderPoints_label = -np.ones((len(faces) * 3 * border_num, 1), np.int32)
    cnt = 0
    for face_id in range(len(faces)):
        face = faces[face_id]
        label = labels[face_id]
        for i in range(3):
            if border_num > 1:
                p1, p2, p = vs[face[i]], vs[face[(i + 1) % 3]], vs[face[(i + 2) % 3]]
                for j in range(border_num):
                    center_p = p1 + (p2 - p1) / (border_num + 1) * (j + 1)
                    border_p = center_p + (p - center_p) * border_k
                    BorderPoints_xyz[cnt] = border_p
                    BorderPoints_label[cnt] = label
                    cnt = cnt + 1
            else:
                p1, p2, p = vs[face[i]], vs[face[(i + 1) % 3]], vs[face[(i + 2) % 3]]
                center_p = (p1 + p2) / 2
                border_p = center_p + (p - center_p) * border_k
                BorderPoints_xyz[cnt] = border_p
                BorderPoints_label[cnt] = label
                cnt = cnt + 1

    return BorderPoints_xyz, BorderPoints_label

二、可视化代码

减小可视化网格边的边长,查看模型细节:
在这里插入图片描述

import potpourri3d as pp3d
import numpy as np
import os
import pickle
from scipy.spatial import cKDTree
import time
import pylab as pl
import torch
from config.config import Config
from network.TriTransNet import TriTransNet
from postprocessing.mesh_project import get_faces_BorderPoints
import mpl_toolkits.mplot3d as a3
import matplotlib.colors as colors
from scipy import linalg


def rot_vs_axis_z(vs, radian, scale):
    bias = np.mean(vs)
    vs = vs - bias
    vs *= scale
    rot_matrix = linalg.expm(np.cross(np.eye(3), [0, 0, 1] / linalg.norm([0, 0, 1]) * radian))
    vs = np.dot(rot_matrix, vs.T)
    vs = vs.T + bias
    return vs


def init_ax(ax):
    # hide axis, thank to
    # https://stackoverflow.com/questions/29041326/3d-plot-with-matplotlib-hide-axes-but-keep-axis-labels/
    ax.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
    ax.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
    ax.w_zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
    # Get rid of the spines
    ax.w_xaxis.line.set_color((1.0, 1.0, 1.0, 0.0))
    ax.w_yaxis.line.set_color((1.0, 1.0, 1.0, 0.0))
    ax.w_zaxis.line.set_color((1.0, 1.0, 1.0, 0.0))
    # Get rid of the ticks
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_zticks([])
    return ax


def is_mesh_file(filename):
    return any(filename.endswith(extension) for extension in ['.obj', 'off'])


def fix_vertices(vs):
    z = vs[:, 2].copy()
    vs[:, 2] = vs[:, 1]
    vs[:, 1] = z
    max_range = 0
    for i in range(3):
        min_value = np.min(vs[:, i])
        max_value = np.max(vs[:, i])
        max_range = max(max_range, max_value - min_value)
        vs[:, i] -= min_value
    scale_by = max_range
    vs /= scale_by
    return vs


def get_seg_files(paths, seg_dir, seg_ext='.eseg'):
    segs = []
    for path in paths:
        segfile = os.path.join(seg_dir, os.path.splitext(os.path.basename(path))[0] + seg_ext)
        assert (os.path.isfile(segfile))
        segs.append(segfile)
    return segs


def make_dataset(path):
    meshes = []
    assert os.path.isdir(path), '%s is not a valid directory' % path
    for root, _, fnames in sorted(os.walk(path)):
        for fname in fnames:
            if is_mesh_file(fname):
                path = os.path.join(root, fname)
                meshes.append(path)

    return meshes


if __name__ == '__main__':
    # 简化网格
    sim_root = '../../../datasets/face_label/coseg_aliens'
    sim_paths = make_dataset(os.path.join(sim_root, 'test'))
    # sim_labels = get_seg_files(sim_paths, seg_dir=os.path.join(sim_root, 'seg'))
    # 原始网格
    org_root = '../../../datasets/aliens'  # '../../datasets/vases'
    org_paths = make_dataset(os.path.join(org_root, 'test'))   # shapes  or seg
    org_labels = get_seg_files(org_paths, seg_dir=os.path.join(org_root, 'seg'), seg_ext='.seg')

    # 网络读取
    cfg = Config()
    cfg.class_n = 4
    cfg.mode = 'seg'
    net = TriTransNet(cfg)
    state_dict = torch.load('../../../results/aliens_1500/model/latest_xyz_net.pth')  # latest_xyz_net 95.53432
    if hasattr(state_dict, '_metadata'):
        del state_dict._metadata
    net.load_state_dict(state_dict)
    net.eval()

    # 准确率统计
    all_acc = 0
    sim_acc = 0
    are_acc = 0
    for i in range(len(sim_paths)):
        # 获取网格数据
        sim_name = sim_paths[i]
        filename, _ = os.path.splitext(sim_name)
        prefix = os.path.basename(filename)

        # 选择某一个网格可视化
        #if prefix != '132':
        #    continue
        if i != 3:
            continue

        cache = os.path.join('../../../results/aliens_1500/cache/', prefix + '.pkl')
        with open(cache, 'rb') as f:   # 不再计算 读取缓存
            meta = pickle.load(f)

        # 获取网格数据
        sim_mesh = meta['mesh']
        sim_label = meta['label']
        vs = fix_vertices(sim_mesh.vs)

        # 获取预测标签
        with torch.no_grad():
            face_features = np.concatenate([sim_mesh.face_features, sim_mesh.xyz], axis = 0)  # sim_mesh.hks[0:3]
            face_features = torch.from_numpy(face_features).float().unsqueeze(0)
            out = net(face_features, [sim_mesh])
            label = out.data.max(1)[1]
            sim_correct = label.eq(torch.from_numpy(sim_label).long()).sum().float() / sim_mesh.faces_num
            sim_acc += sim_correct

        # 时间
        t = time.time()
        # 投影准备
        label = label.numpy().reshape(-1)
        BorderPoints_xyz, BorderPoints_label = get_faces_BorderPoints(vs, sim_mesh.faces, label, border_k=0.01, border_num=10)
        # 0.01 10 95.53432
        # 0.5 1  退化成最简单的最近邻  94.02
        kdt = cKDTree(BorderPoints_xyz)

        # 读取高分辨率网格
        org_vs, org_faces = pp3d.read_mesh(org_paths[i])
        org_vs = fix_vertices(org_vs)
        org_label = np.loadtxt(open(org_labels[i], 'r'), dtype='float64') -1
        # 原始网格中心点
        mean_vs = org_vs[org_faces]
        mean_vs = mean_vs.sum(axis=1) / 3.0

        dist, indices = kdt.query(mean_vs, workers=-1)
        # 准确率计算
        org_prolabels = BorderPoints_label[indices].reshape(-1)
        pro_cnt = np.equal(org_prolabels, org_label).sum()
        pro_acc = pro_cnt / len(org_label)
        all_acc += pro_acc
        print(filename, ':', pro_acc, ' time:', time.time()-t)

        # 可视化
        f = pl.figure()
        ax = f.add_subplot(1, 1, 1, projection='3d')
        ax = init_ax(ax)
        r2h = lambda x: colors.rgb2hex(tuple(map(lambda y: y / 255., x)))
        f_colors = [r2h((0, 0, 255)), r2h((0, 255, 255)), r2h((255, 0, 255)), r2h((0, 255, 0))]
        vis_bias = 0.3 #

        # 简化网格
        faces_color = []
        for l in label:
            faces_color.append(f_colors[l - 1])
        vs = rot_vs_axis_z(vs, 0.95, 1)
        tri = a3.art3d.Poly3DCollection(vs[sim_mesh.faces],
                                        facecolors=faces_color,
                                        edgecolors=r2h((0, 0, 0)),
                                        linewidths=0.1,  # 0.1
                                        # linestyles='dashdot',
                                        alpha=1)
        ax.add_collection3d(tri)

        # 高分辨率网格
        org_vs = rot_vs_axis_z(org_vs, 0.95, 1)
        faces_color = []
        for l in org_prolabels.astype(int):
            faces_color.append(f_colors[l - 1])
        org_vs[:, 0] += vs[:, 0].max()/2 + vis_bias
        tri1 = a3.art3d.Poly3DCollection(org_vs[org_faces],
                                        facecolors=faces_color,
                                        edgecolors=r2h((0, 0, 0)),
                                        linewidths=0.1,
                                        # linestyles='dashdot',
                                        alpha=1)
        ax.add_collection3d(tri1)
        max_x = org_vs[:, 0].max()

        # 高分辨率网格Ground truth
        faces_color = []
        for l in org_label.astype(int):
            faces_color.append(f_colors[l - 1])
        org_vs[:, 0] += vs[:, 0].max() / 2 + vis_bias
        tri2 = a3.art3d.Poly3DCollection(org_vs[org_faces],
                                         facecolors=faces_color,
                                         edgecolors=r2h((0, 0, 0)),
                                         linewidths=0.1,
                                         # linestyles='dashdot',
                                         alpha=1)
        ax.add_collection3d(tri2)
        max_x = org_vs[:, 0].max()

        ax.auto_scale_xyz([0, max_x], [0, max_x], [0, max_x])
        ax.view_init(0, -90)
        pl.tight_layout()
        pl.savefig('corr.png', dpi=1000)  # i
        pl.show()
        break
    print(all_acc / len(sim_paths))
    print(sim_acc / len(sim_paths))


  1. MeshWalker: Deep Mesh Understanding by Random Walks ↩︎

  2. A Spectral Segmentation Method for Large Meshes ↩︎

  3. MeshCNN ↩︎

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

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

相关文章

可复用测试用例描述要素

测试用例的输入、操作、预期结果和评估标准、前提条件是测试用例不可少的要素,但对于可复用测试用例而言,这是不够的。本文在文献规定的测试用例要素基础上,增加了新的内容。从而从多个角度完整地对可复用测试用例进行了描述,为可…

从0开始学IntelliJ Plugin开发:一、配置环境

前言 作为一个javaer,相信大家平时开发都多多少少使用了一些idea的插件,那么在享受插件便利的同时,有没有好奇插件是如何开发的 笔者怀着这份好奇开始了idea插件开发学习之路,同时把学习的心得体会整理成系列文章作为学习笔记供…

探访人工智能领跑者:纷享销客携手30+TOP高科技企业走进旷视科技

拥有全球规模领先的计算机视觉研究院; 揽获28项世界顶级AI竞赛冠军; 世界级人工智能公司; 没错,它就是人工智能行业的务实者和领跑者,旷视科技。 3月3日,北京软件和信息服务业协会联合纷享销客,…

macOS 13.3 Beta 3 (22E5236f)With OpenCore 0.9.1开发版 and winPE双引导分区原版镜像

原文地址:http://www.imacosx.cn/112494.html(转载请注明出处)镜像特点完全由黑果魏叔官方制作,针对各种机型进行默认配置,让黑苹果安装不再困难。系统镜像设置为双引导分区,全面去除clover引导分区&#x…

ViT:Transformer在CV领域的开山之作

ViT引发的变革 Transformer最开始是作为自然语言处理(英语: Natural Language Processing ,缩写作 NLP)领域的模型框架,在该领域其可谓大放异彩,然而自始至终都有人在不断尝试将Transformer应用到视觉领域…

快速入门深度学习1(用时1h)

速通《动手学深度学习》1写在最前面0.内容与结构1.深度学习简介1.1 问题引入1.2 思路:逆向思考1.3 跳过1.4 特点1.5 小结2.预备知识(MXNet版本,学错了。。。。)2.1 获取和运行本书的代码2.2 数据操作2.2.1 略过2.2.2 小结2.3 自动…

c/c++开发,无可避免的模板编程实践(篇九)-c++11的新顺序容器

一、std::array数组容器 1.1 数组的适配器-std::array std::array 是封装固定大小数组的容器&#xff0c;是c11标准库新引入的顺序容器&#xff0c;定义于头文件 <array>。 template <class T,std::size_t N > struct array; 此容器是一个聚合类型&#xff0c;其…

ChatPDF解放双手帮你解读PDF文档

一、先介绍一下吧 chatPDF是一个解读pdf文档的AI模型&#xff0c;然后封装出来的工具。如论文、合同、文书、书籍等&#xff0c;只要是PDF都能搞定&#xff0c;可支持120页【2023.3.9】的文件。据说之前支持200页&#xff0c;反正在变 最新爆火的ChatPDF&#xff0c;短短5天就…

nginx 主动健康检查搭建详解(nginx_upstream_check_module)

版本信息 nginx: 1.21 1.下载nginx_upstream_check_module模块 nginx_upstream_check_module-master.zip wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master 解压到 2. 安装nginx 略 3. 补丁安装 由于我这边安装nginx版本为nginx1.21…

你是使用什么工具调试 golang 程序的?

写过 C/C 的都是到&#xff0c;调试程序的时候通常使用 gdb 工具来进行调试&#xff0c;用起来可爽了&#xff0c;那么 gdb 是否也适合 golang 程序的调试的 我个人到是通常使用 dlv 来进行 golang 程序的调试&#xff0c;分享一波 dlv 是什么&#xff0c;全称 Delve Delve …

KiCad 编译

KiCad 编译 因为最新项目需要&#xff0c;所以看了一下KiCad的编译&#xff0c;这里介绍的是64位电脑的编译&#xff0c;32位小伙伴请绕道官网看教程呦。 您可以在KiCad内查看基本的编译教程。 我这里也是参考的官网编译教程进行的编译&#xff0c;接下来让我们一起看看吧。…

论文 | 期刊 | 专业名词解释

文章目录1. EI2. IEEE Xplore3. CN期刊3.2 CN期刊后面的数字代表什么3. SCI3.1 影响因子先立个帖子&#xff0c;后续用到的话随时更新1. EI 工程索引(EI)是由美国工程信息公司(Engineering information Inc.)编辑出版&#xff0c;历史上最悠久的一部大型综合性检索工具。 《工…

03 SWMM快速入门案例的设施参数设置与批量设置

文章目录1 雨量计1.1 雨量计基础设置1.2 雨量计数据来源2 汇水区2.1 参数讲解2.2 设置结果3 检查井3.1 参数讲解3.2 批量设置4 管道4.1 参数讲解4.2 设置结果5 出水口上一篇博客中我们已经完成了各类设施的绘制&#xff0c;本节对他们的参数进行设置1 雨量计 1.1 雨量计基础设…

第一章 C语言:数据存储

一、大小端存储大端存储&#xff1a;数据的低位字节存储在高地址小端存储&#xff1a;数据的低位字节存储在低地址不同编译器有不同的存储方式int a 10; char* p (char*)&a; printf("%x\n", *p); // a ---> 0000000a //0000 0000 0000 0000 0000 0…

教学场景应用视频试看预览功能

html5播放器视频预览功能效果 - 视频预览代码示例预播放一小段时间的视频内容&#xff0c;比如3分钟&#xff0c;然后引导用户付费观看或注册会员观看完整视频。原理&#xff1a;视频播放结束&#xff0c;执行s2j_onPlayOver()函数&#xff0c;显示提示信息或对话框&#xff0c…

Altium Designer(AD)软件使用记录03-AD软件中各层定义

Altium Designer(AD)软件使用记录03-AD软件中各层定义 重点&#xff1a; 1、常用的信号层&#xff1a;顶层&#xff0c;底层层&#xff0c;中间正片层&#xff0c;中间负片层 2、机械1层作为板框层&#xff0c;机械13层作为3D防止层&#xff0c;其他的机械层很少用 3、顶层阻焊…

AVL树详解+模拟实现

1&#xff1a;概念当数据有序&#xff0c;二叉搜索树将趋近于单叉树&#xff0c;查找元素相当于在顺序表中查找元素&#xff0c;效率低下&#xff0c;两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis创建了AVL树。特性如下&#xff1a; 左右子树高度差的绝对值不超过1左右子树…

Django/Vue实现在线考试系统-06-开发环境搭建-Visual Studio Code安装

1.0 VS Code下载和安装 Visual Studio Code,简称 VS Code,是由微软公司开发的 IDE 工具。与微软其他 IDE(如 Visual Studio)不同的是,Visual Studio Code 是跨平台的,可以安装在 Windows、Linux 和 macOS平台上运行。不仅如此, Visual Studio Code 没有限定只能开发特定…

Revit中如何添加一个新的管道直径

有些时候项目当中会遇到一些管径比较小的管道&#xff0c;但是在直径中又没有适合的&#xff0c;怎么办?很简单&#xff0c;跟紧以下几个步理就可以了。 首先&#xff0c;我们拿一个管段为“铁&#xff0c;铸铁30”的为例子&#xff0c;如图1所示&#xff0c;系统中这管段是没…

1.数据结构的研究

数据结构很重要&#xff01; 数据结构很重要&#xff01;! 数据结构很重要&#xff01;! ! 思考 1.数据结构研究的内容有哪些&#xff1f;&#xff08;What&#xff09; 2.为什么要研究数据结构? ? (Why) 3.如何更好的研究数据结构? ? &#xff1f;(How) 注&#xff1a;特别…