使用open3d进行人体配准和重建学习记录

news2024/11/26 9:36:24

使用open3d进行人体配准和重建学习记录

  • 一、使用kinectv2捕捉人体rgb和depth图
  • 二、重建部分
    • 2.1 泼松重建
    • 2.2 滚球重建
    • 2.3 alpha重建

一、使用kinectv2捕捉人体rgb和depth图

# -*- coding: utf-8 -*-
# @Time    : 2024/3/20 17:26
# @Author  : sjh
# @Site    : 
# @File    : main.py
# @Comment : 采用RGBD点云重建
import ctypes
import os
import sys
import time

import cv2
import loguru
import mapper
import matplotlib.pyplot as plt
import numpy as np
import open3d as o3d
from pykinect2 import PyKinectRuntime
from pykinect2 import PyKinectV2
from pykinect2.PyKinectV2 import *
import shutil
import os
import shutil
def make_clean_folder(path_folder):
    if not os.path.exists(path_folder):
        os.makedirs(path_folder)
    else:
        shutil.rmtree(path_folder)
        os.makedirs(path_folder)
def RGBD2cloud(file_path):
    file_path = file_path
    make_clean_folder("./data/pcd_data")
    # 获取文件列表
    file_list = os.listdir(os.path.join(file_path, "RGB"))

    # 定义排序键函数,提取文件名中的数字部分作为排序依据
    def extract_number(filename):
        return int(''.join(filter(str.isdigit, filename)))

    # 使用 sorted() 函数对文件列表进行排序,以文件名中的数字部分作为排序依据
    sorted_file_list = sorted(file_list, key=extract_number)
    for i, name in enumerate(sorted_file_list):
        color_raw = o3d.io.read_image(os.path.join(file_path, "RGB", name))
        depth_raw = o3d.io.read_image(os.path.join(file_path, "DEPTH", name.replace(".jpg", ".png")))
        # 将Open3D图像转换为NumPy数组
        depth_raw_np = np.asarray(depth_raw)
        # 应用双边滤波
        depth_filtered_np = cv2.bilateralFilter(depth_raw_np.astype(np.float32), 3, 3, 3)
        # 如果需要,将NumPy数组转换回Open3D图像
        depth_raw = o3d.geometry.Image(depth_filtered_np)
        rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth(color_raw, depth_raw,
                                                                        convert_rgb_to_intensity=False)

        plt.subplot(1, 2, 1)
        plt.title('Redwood grayscale image')
        plt.imshow(rgbd_image.color)
        plt.subplot(1, 2, 2)
        plt.title('Redwood depth image')
        plt.imshow(rgbd_image.depth)
        # plt.show()

        inter = o3d.camera.PinholeCameraIntrinsic()
        inter.set_intrinsics(1920, 1080, 365.939, 365.939, 256.530, 207.09)
        pcd = o3d.geometry.PointCloud().create_from_rgbd_image(
            rgbd_image, inter)

        # Flip it, otherwise the pointcloud will be upside down
        pcd.transform([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
        o3d.io.write_point_cloud(f"./data/pcd_data/test{i + 1}.pcd", pcd)
        # o3d.visualization.draw_geometries([pcd])


def seg_body(pcd_path, show=False):
    def display_inlier_outlier(cloud, ind, show):
        inlier_cloud = cloud.select_by_index(ind)
        outlier_cloud = cloud.select_by_index(ind, invert=True)
        # 选中的点为灰色,未选中点为红色
        outlier_cloud.paint_uniform_color([1, 0, 0])
        inlier_cloud.paint_uniform_color([0.8, 0.8, 0.8])
        # 可视化
        if show: o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud], window_name="统计滤波1")
        return inlier_cloud

    def plane_filter(pcd):
        # 分割点云
        plane_model, inliers = pcd.segment_plane(distance_threshold=0.01,
                                                 ransac_n=3,
                                                 num_iterations=1000)
        [plane_a, plane_b, plane_c, plane_d] = plane_model
        print(f"Plane equation: {plane_a}x + {plane_b}y + {plane_c}z + {plane_d} = 0")
        inlier_cloud = pcd.select_by_index(inliers)
        outlier_cloud = pcd.select_by_index(inliers, invert=True)
        # 选中的点为灰色,未选中点为红色
        outlier_cloud.paint_uniform_color([1, 0, 0])
        inlier_cloud.paint_uniform_color([0.8, 0.8, 0.8])
        # 可视化结果
        o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud], window_name="Inlier Cloud")
        o3d.visualization.draw_geometries([outlier_cloud], window_name="Outlier Cloud")
        return outlier_cloud

    def statistical_filter(pcd, show):
        # 统计滤波
        num_neighbors = 20  # K邻域点的个数
        std_ratio = 2.0  # 标准差乘数
        # 执行统计滤波,返回滤波后的点云sor_pcd和对应的索引ind
        sor_pcd, ind = pcd.remove_statistical_outlier(num_neighbors, std_ratio)
        print("统计式离群点移除2,统计滤波后的点云:", sor_pcd)
        sor_pcd.paint_uniform_color([0, 0, 1])
        # 提取噪声点云
        sor_noise_pcd = pcd.select_by_index(ind, invert=True)
        print("噪声点云:", sor_noise_pcd)
        sor_noise_pcd.paint_uniform_color([1, 0, 0])
        # 可视化滤波结果
        if show: o3d.visualization.draw_geometries([sor_pcd, sor_noise_pcd], window_name="统计滤波2",
                                                   width=800,  # 窗口宽度
                                                   height=600)  # 窗口高度

        # 半径式离群点剔除
        print("半径式离群点剔除")
        sor_pcd, ind = sor_pcd.remove_radius_outlier(nb_points=16, radius=0.05)
        sor_pcd.paint_uniform_color([0, 0, 1])
        # 提取噪声点云
        sor_noise_pcd = pcd.select_by_index(ind, invert=True)
        print("噪声点云:", sor_noise_pcd)
        sor_noise_pcd.paint_uniform_color([1, 0, 0])

        # 可视化滤波结果
        if show: o3d.visualization.draw_geometries([sor_pcd, sor_noise_pcd], window_name="统计滤波2",
                                                   width=800,  # 窗口宽度
                                                   height=600)  # 窗口高度
        return sor_pcd

    def filter_points_by_distance(cloud, max_distance=3.0):
        # 将点云转换为NumPy数组
        points = np.asarray(cloud.points)
        # 找到满足距离条件的点的索引
        valid_indices = np.where((np.abs(points[:, 2]) <= max_distance) & (points[:, 1] >= -0.55) & (-0.85 <= points[:, 0] ) & (  points[:, 0] <= 0.55)
                                 & (np.abs(points[:, 2]) >= 1))[0]
        # 使用select_by_index方法基于索引过滤点云
        filtered_cloud = cloud.select_by_index(valid_indices)
        return filtered_cloud

    file_list = os.listdir(pcd_path)
    # 定义一个自定义的排序函数,根据文件名中 "test" 的最后一个数字进行排序
    def sort_by_last_number(file_name):
        return int(file_name.split("test")[-1].split(".")[0])

    # 使用 sorted() 函数对文件列表进行排序
    sorted_file_list = sorted(file_list, key=sort_by_last_number, reverse=False)
    # 删除文件夹及其内容
    shutil.rmtree("./data/seg_pcd_data/")
    # 创建空文件夹
    os.makedirs("./data/seg_pcd_data/")
    make_clean_folder("./data/seg_pcd_data")
    for i, name in enumerate(sorted_file_list):
        print("read file:", os.path.join(pcd_path, name))
        # 加载点云文件
        pcd = o3d.io.read_point_cloud(os.path.join(pcd_path, name))
        # 使用PassThrough滤波器去除超出指定范围的点云
        pcd = filter_points_by_distance(pcd, max_distance=2.5)
        if show: o3d.visualization.draw_geometries([pcd], window_name="Filtered Point Cloud")
        pcd = pcd.voxel_down_sample(voxel_size=0.02)

        # 统计式离群点移除
        print("统计式离群点移除1")
        cl, ind = pcd.remove_statistical_outlier(nb_neighbors=50,
                                                 std_ratio=0.05)
        # 可视化
        pcd = display_inlier_outlier(pcd, ind, show)

        # nb_neighbors:最近k个点    std_ratio:基于标准差的阈值,越小滤除点越多
        pcd = statistical_filter(pcd, show)

        # 保存人体点云
        if show: o3d.visualization.draw_geometries([pcd], window_name="Outlier Cloud")

        o3d.io.write_point_cloud(f"./data/seg_pcd_data/human_body{i + 1}.pcd", pcd)


def mutilway_registration(bunny_path):
    def load_bunnypoint_clouds(voxel_size=0.0, bunny_path=''):
        pcds = []

        demo_icp_pcds = os.listdir(bunny_path)
        for path in demo_icp_pcds:
            pcd = o3d.io.read_point_cloud(os.path.join(bunny_path, path))
            pcd_down = pcd.voxel_down_sample(voxel_size=voxel_size)
            pcds.append(pcd_down)
        return pcds

    voxel_size = 0.005
    pcds_down = load_bunnypoint_clouds(voxel_size, bunny_path)
    o3d.visualization.draw_geometries(pcds_down, )

    def pairwise_registration(source, target):
        # 估计目标点云的法线
        target.estimate_normals(
            search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)
        )

        print("Apply point-to-plane ICP")
        icp_coarse = o3d.pipelines.registration.registration_icp(
            source, target, max_correspondence_distance_coarse, np.identity(4),
            o3d.pipelines.registration.TransformationEstimationPointToPlane())
        icp_fine = o3d.pipelines.registration.registration_icp(
            source, target, max_correspondence_distance_fine,
            icp_coarse.transformation,
            o3d.pipelines.registration.TransformationEstimationPointToPlane())
        transformation_icp = icp_fine.transformation
        information_icp = o3d.pipelines.registration.get_information_matrix_from_point_clouds(
            source, target, max_correspondence_distance_fine,
            icp_fine.transformation)
        return transformation_icp, information_icp

    def full_registration(pcds, max_correspondence_distance_coarse,
                          max_correspondence_distance_fine):
        pose_graph = o3d.pipelines.registration.PoseGraph()
        odometry = np.identity(4)
        pose_graph.nodes.append(o3d.pipelines.registration.PoseGraphNode(odometry))
        n_pcds = len(pcds)
        for source_id in range(n_pcds):
            for target_id in range(source_id + 1, n_pcds):
                transformation_icp, information_icp = pairwise_registration(
                    pcds[source_id], pcds[target_id])
                print("Build o3d.pipelines.registration.PoseGraph")
                if target_id == source_id + 1:  # odometry case
                    odometry = np.dot(transformation_icp, odometry)
                    pose_graph.nodes.append(
                        o3d.pipelines.registration.PoseGraphNode(
                            np.linalg.inv(odometry)))
                    pose_graph.edges.append(
                        o3d.pipelines.registration.PoseGraphEdge(source_id,
                                                                 target_id,
                                                                 transformation_icp,
                                                                 information_icp,
                                                                 uncertain=False))
                else:  # loop closure case
                    pose_graph.edges.append(
                        o3d.pipelines.registration.PoseGraphEdge(source_id,
                                                                 target_id,
                                                                 transformation_icp,
                                                                 information_icp,
                                                                 uncertain=True))
        return pose_graph

    print("完整注册 ...")
    max_correspondence_distance_coarse = voxel_size * 15
    max_correspondence_distance_fine = voxel_size * 1.5
    with o3d.utility.VerbosityContextManager(
            o3d.utility.VerbosityLevel.Debug) as cm:
        pose_graph = full_registration(pcds_down,
                                       max_correspondence_distance_coarse,
                                       max_correspondence_distance_fine)
    print("优化姿势图...")
    option = o3d.pipelines.registration.GlobalOptimizationOption(
        max_correspondence_distance=max_correspondence_distance_fine,
        edge_prune_threshold=0.25,
        reference_node=0)
    with o3d.utility.VerbosityContextManager(
            o3d.utility.VerbosityLevel.Debug) as cm:
        o3d.pipelines.registration.global_optimization(
            pose_graph,
            o3d.pipelines.registration.GlobalOptimizationLevenbergMarquardt(),
            o3d.pipelines.registration.GlobalOptimizationConvergenceCriteria(),
            option)
    voxel_size = 0.005
    print("Transform points and display")
    for point_id in range(len(pcds_down)):
        print(pose_graph.nodes[point_id].pose)
        pcds_down[point_id].transform(pose_graph.nodes[point_id].pose)
    o3d.visualization.draw_geometries(pcds_down, )

    pcds = load_bunnypoint_clouds(voxel_size, bunny_path)
    pcd_combined = o3d.geometry.PointCloud()
    for point_id in range(len(pcds)):
        pcds[point_id].transform(pose_graph.nodes[point_id].pose)
        pcd_combined += pcds[point_id]
    pcd_combined_down = pcd_combined.voxel_down_sample(voxel_size=voxel_size)
    o3d.io.write_point_cloud("multiway_registration.pcd", pcd_combined_down)
    o3d.visualization.draw_geometries([pcd_combined_down], )


class Cloud:
    def __init__(self, file="", dynamic=False, color=False, depth=False, body=False, skeleton=False,
                 simultaneously=False, color_overlay=False):
        # Initialize Kinect object
        self._kinect = PyKinectRuntime.PyKinectRuntime(
            PyKinectV2.FrameSourceTypes_Color | PyKinectV2.FrameSourceTypes_Depth | PyKinectV2.FrameSourceTypes_Body | PyKinectV2.FrameSourceTypes_BodyIndex)
        self._body_index = None  # save body index image
        self._body_index_points = None  # save body index points
        self._cloud = False  # Flag to break loop when creating a pointcloud
        self._depth = None  # Store last depth frame
        self._color_frame = None  # store the last color frame
        self._red = 0  # Store red value from cv2 track bar
        self._green = 0  # Store green value from cv2 track bar
        self._blue = 0  # Store blue value from cv2 track bar
        self._size = 0.5  # Store value of point size from cv2 track bar
        self._opacity = 0  # store opacity value of colors from cv2 track bar
        self._dt = .0  # Store time value since kinect started from cv2 track bar
        self._skeleton_points = None  # store multiple skeleton points
        self._color_point_cloud = color  # Flag to show dynamic point cloud using the color frame
        self._depth_point_cloud = depth  # Flag to show dynamic point cloud using the depth frame
        self._simultaneously_point_cloud = simultaneously  # Flag for simultaneously showing the point clouds
        self._skeleton_point_cloud = skeleton  # Flag for showing the skeleton cloud
        self._dynamic = dynamic  # Flag for initializing a dynamic pointcloud
        self._cloud_file = file  # Store the file name
        self._body_index_cloud = body  # save body flag
        self._color_overlay = color_overlay  # flag to display the rgb image color up to the pointcloud
        self._dir_path = os.path.dirname(os.path.realpath(__file__))  # Store the absolute path of the file
        self._body_frame = None  # store body frame data
        self._joints = None  # save skeleton joints
        self._bodies_indexes = None  # save tracked skeleton indexes
        self._world_points = None  # Store world points
        self._color_point_cloud_points = None  # store color cloud points for simultaneously showing
        self._depth_point_cloud_points = None  # store depth cloud points for simultaneously showing
        self._body_point_cloud_points = None  # store body cloud points for simultaneously showing
        self._skeleton_point_cloud_points = None  # store skeleton cloud points for simultaneously showing
        self._simultaneously_point_cloud_points = None  # stack all the points
        self._skeleton_colors = np.asarray([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [0, 1, 1], [1, 0, 1]],
                                           dtype=np.float32)  # skeleton color pallet
        self._scatter = None  # Store GL Scatter handler
        self._color = None  # Store color for each point
        self._t = None  # Store starting time for pointcloud
        self._dynamic_point_cloud = None  # Store the calculated point cloud points

    def main(self):
        cv2.namedWindow("Press Space to Continue", cv2.WINDOW_NORMAL)  # 创建一个窗口以便捕捉按键
        vis = o3d.visualization.Visualizer()  # 启动可视化工具
        # vis.create_window()  # 初始化窗口
        frame_num = 0
        while True:
            # 检测空格键,继续下一次循环
            key = cv2.waitKey(0)  # 0表示无限等待直到有键盘输入

            if key == 32:  # 空格键的ASCII码是32
                self._cloud_file = f'data/pcd_data/test_cloud_{frame_num}.pcd'
                self.create_points()  # 创建点云数据
                # 根据文件类型导出点云
                if self._cloud_file[-4:] == '.ply':
                    self.export_to_ply()
                if self._cloud_file[-4:] == '.pcd':
                    self.export_to_pcd()
                loguru.logger.info(f"已保存第{frame_num}帧pcd")
                frame_num += 1
                # # add file geometry
                # vis.add_geometry(o3d.io.read_point_cloud(os.path.join(self._dir_path, self._cloud_file)))
                # opt = vis.get_render_option()  # get options
                # opt.background_color = np.asarray([0, 0, 0])  # background to black
                # view_control = vis.get_view_control()
                # view_control.rotate(0, -360)
                # vis.poll_events()
                # vis.update_renderer()
            elif key == 27:  # 如果按下ESC键,则退出循环
                break

    def create_points(self):
        """
        Check if the file exists and if not create the point cloud points and the file
        :return None
        """
        # Check if the file exists in the folder
        if self._depth_point_cloud or self._color_point_cloud:
            t = time.time()  # starting time
            while not self._cloud:
                self._depth = self._kinect.get_last_depth_frame()
                self._color_frame = self._kinect.get_last_color_frame()
                # ----- Get Depth Frame
                if not self._kinect.has_new_depth_frame():
                    # store depth frame
                    continue
                # ----- Get Color Frame
                if not self._kinect.has_new_color_frame():
                    # store color frame
                    continue
                # wait for kinect to grab at least one depth frame
                if self._kinect.has_new_depth_frame() and self._color_frame is not None:

                    # use mapper to get world points
                    if self._depth_point_cloud:
                        world_points = mapper.depth_2_world(self._kinect, self._kinect._depth_frame_data,
                                                            _CameraSpacePoint)
                        world_points = ctypes.cast(world_points, ctypes.POINTER(ctypes.c_float))
                        world_points = np.ctypeslib.as_array(world_points, shape=(
                        self._kinect.depth_frame_desc.Height * self._kinect.depth_frame_desc.Width, 3))
                        world_points *= 1000  # transform to mm
                        self._dynamic_point_cloud = np.ndarray(shape=(len(world_points), 3), dtype=np.float32)
                        # transform to mm
                        self._dynamic_point_cloud[:, 0] = world_points[:, 0]
                        self._dynamic_point_cloud[:, 1] = world_points[:, 2]
                        self._dynamic_point_cloud[:, 2] = world_points[:, 1]

                        if self._cloud_file[-4:] == '.ply' or self._cloud_file[-4:] == '.pcd':
                            # update color for .ply file only
                            self._color = np.zeros((len(self._dynamic_point_cloud), 3), dtype=np.float32)
                            # map color to depth frame
                            Xs, Ys = mapper.color_2_depth_space(self._kinect, _ColorSpacePoint,
                                                                self._kinect._depth_frame_data, show=False)
                            color_img = self._color_frame.reshape(
                                (self._kinect.color_frame_desc.Height, self._kinect.color_frame_desc.Width, 4)).astype(
                                np.uint8)
                            # make align rgb/d image
                            align_color_img = np.zeros(
                                (self._kinect.depth_frame_desc.Height, self._kinect.depth_frame_desc.Width, 4),
                                dtype=np.uint8)
                            align_color_img[:, :] = color_img[Ys, Xs, :]
                            align_color_img = align_color_img.reshape(
                                (self._kinect.depth_frame_desc.Height * self._kinect.depth_frame_desc.Width, 4)).astype(
                                np.uint8)
                            align_color_img = align_color_img[:, :3:]  # remove the fourth opacity channel
                            align_color_img = align_color_img[..., ::-1]  # transform from bgr to rgb
                            self._color[:, 0] = align_color_img[:, 0]
                            self._color[:, 1] = align_color_img[:, 1]
                            self._color[:, 2] = align_color_img[:, 2]

                    if self._color_point_cloud:
                        # use mapper to get world points from color sensor
                        world_points = mapper.color_2_world(self._kinect, self._kinect._depth_frame_data,
                                                            _CameraSpacePoint)
                        world_points = ctypes.cast(world_points, ctypes.POINTER(ctypes.c_float))
                        world_points = np.ctypeslib.as_array(world_points, shape=(
                        self._kinect.color_frame_desc.Height * self._kinect.color_frame_desc.Width, 3))
                        world_points *= 1000  # transform to mm
                        # transform the point cloud to np (424*512, 3) array
                        self._dynamic_point_cloud = np.ndarray(shape=(len(world_points), 3), dtype=np.float32)
                        self._dynamic_point_cloud[:, 0] = world_points[:, 0]
                        self._dynamic_point_cloud[:, 1] = world_points[:, 2]
                        self._dynamic_point_cloud[:, 2] = world_points[:, 1]

                        if self._cloud_file[-4:] == '.ply' or self._cloud_file[-4:] == '.pcd':
                            # update color for .ply file only
                            self._color = np.zeros((len(self._dynamic_point_cloud), 3), dtype=np.float32)
                            # get color image
                            color_img = self._color_frame.reshape(
                                (self._kinect.color_frame_desc.Height, self._kinect.color_frame_desc.Width, 4)).astype(
                                np.uint8)
                            color_img = color_img.reshape(
                                (self._kinect.color_frame_desc.Height * self._kinect.color_frame_desc.Width, 4))
                            color_img = color_img[:, :3:]  # remove the fourth opacity channel
                            color_img = color_img[..., ::-1]  # transform from bgr to rgb
                            # update color with rgb color
                            self._color[:, 0] = color_img[:, 0]
                            self._color[:, 1] = color_img[:, 1]
                            self._color[:, 2] = color_img[:, 2]

                    self._cloud = True  # break loop
                self._dt = time.time() - t  # running time
        else:
            print('[CloudPoint] No sensor flag checked')
            print('Example 1 :\n pcl = Cloud(file=filename, color=True) \n pcl.visualize()')
            print('Example 2 :\n pcl = Cloud(file=filename, depth=True) \n pcl.visualize()')
            sys.exit()

    def export_to_ply(self):
        """
        Inspired by https://github.com/bponsler/kinectToPly
        Writes a kinect point cloud into a .ply file
        return None
        """
        # assert that the points have been created
        assert self._dynamic_point_cloud is not None, "Point Cloud has not been initialized"
        assert self._cloud_file != "", "Specify text filename"
        # stack data
        data = np.column_stack((self._dynamic_point_cloud, self._color))
        data = data[np.all(data != float('-inf'), axis=1)]  # remove -inf
        # header format of ply file
        header_lines = ["ply",
                        "format ascii 1.0",
                        "comment generated by: python",
                        "element vertex {}".format(int(len(data))),
                        "property float x",
                        "property float y",
                        "property float z",
                        "property uchar red",
                        "property uchar green",
                        "property uchar blue",
                        "end_header"]
        # convert to string
        data = '\n'.join(
            '{} {} {} {} {} {}'.format('%.2f' % x[0], '%.2f' % x[1], '%.2f' % x[2], int(x[3]), int(x[4]), int(x[5])) for
            x in data)
        header = '\n'.join(line for line in header_lines) + '\n'
        # write file
        file = open(os.path.join(self._dir_path, self._cloud_file), 'w')
        file.write(header)
        file.write(data)
        file.close()

    def export_to_pcd(self):
        # assert that the points have been created
        assert self._dynamic_point_cloud is not None, "Point Cloud has not been initialized"
        assert self._cloud_file != "", "Specify text filename"
        # pack r/g/b to rgb
        rgb = np.asarray([[int(int(r_g_b[0]) << 16 | int(r_g_b[1]) << 8 | int(r_g_b[0]))] for r_g_b in self._color])
        # stack data
        data = np.column_stack((self._dynamic_point_cloud, rgb))
        data = data[np.all(data != float('-inf'), axis=1)]  # remove -inf
        # header format of pcd file
        header_lines = ["# .PCD v0.7 - Point Cloud Data file format",
                        "VERSION 0.7",
                        "FIELDS x y z rgb",
                        "SIZE 4 4 4 4",
                        "TYPE F F F U",
                        "COUNT 1 1 1 1",
                        "WIDTH {}".format(int(len(data))),
                        "HEIGHT 1",
                        "VIEWPOINT 0 0 0 1 0 0 0",
                        "POINTS {}".format(int(len(data))),
                        "DATA ascii"]
        # convert to string
        data = '\n'.join('{} {} {} {}'.format('%.2f' % x[0], '%.2f' % x[1], '%.2f' % x[2], int(x[3])) for x in data)
        header = '\n'.join(line for line in header_lines) + '\n'
        # write file
        file = open(os.path.join(self._dir_path, self._cloud_file), 'w')
        file.write(header)
        file.write(data)
        file.close()


if __name__ == "__main__":
    # pcl = Cloud(dynamic=True, simultaneously=True, color=True, depth=True, body=False, skeleton=False, color_overlay=True)
    # pcl.main() # step1 得到点云
    # RGBD2cloud(file_path=r"./data/RGBD4")  # step1 得到点云
    seg_body(pcd_path=r"./data/pcd_data", show=False)  # #step2
    # mutilway_registration(bunny_path=r"./data/seg_pcd_data/")  # #step3

其中RGBD2cloud可以将rgb和depth转化为点云图,
seg_body可以裁剪出人体上半部分点云图,目前裁剪出的点云图失去了彩色部分
在这里插入图片描述

二、重建部分

2.1 泼松重建

def Poisson_rec(pcd_path):
    import open3d as o3d
    import numpy as np

    # 加载点云
    pcd = o3d.io.read_point_cloud(pcd_path)
    pcd = pcd.voxel_down_sample(voxel_size=0.05)
    pcd.paint_uniform_color([1.0, 0.0, 1.0])
    # 法线估计
    radius = 0.5  # 调整为更适合你数据的值
    max_nn = 30 # 调整为更适合你数据的值
    pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius, max_nn))

    # 使用不依赖于相机位置的方法调整法线方向
    pcd.orient_normals_consistent_tangent_plane(k=max_nn)

    # Poisson重建
    with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug) as cm:
        mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=9)  # 调整深度参数

    # 可视化重建结果
    o3d.visualization.draw_geometries([mesh], window_name="点云重建")
    # 可视化
    print('visualize densities')
    densities = np.asarray(densities)
    density_colors = plt.get_cmap('plasma')(
        (densities - densities.min()) / (densities.max() - densities.min()))
    density_colors = density_colors[:, :3]
    density_mesh = o3d.geometry.TriangleMesh()
    density_mesh.vertices = mesh.vertices
    density_mesh.triangles = mesh.triangles
    density_mesh.triangle_normals = mesh.triangle_normals
    density_mesh.vertex_colors = o3d.utility.Vector3dVector(density_colors)
    o3d.visualization.draw_geometries([density_mesh],)

    print('remove low density vertices')
    vertices_to_remove = densities < np.quantile(densities, 0.01)
    mesh.remove_vertices_by_mask(vertices_to_remove)
    print(mesh)
    o3d.visualization.draw_geometries([mesh],)
    o3d.io.write_triangle_mesh("mesh.ply", mesh)

在这里插入图片描述

2.2 滚球重建

def Ball_pivoting(pcd_path):
    # 读取你的点云数据
    pcd_path = pcd_path  # 将这里的路径改为你的点云文件路径
    pcd = o3d.io.read_point_cloud(pcd_path)
    pcd = o3d.io.read_point_cloud(pcd_path)
    pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.05, max_nn=50))
    pcd.orient_normals_consistent_tangent_plane(100)
    # 可视化原始点云
    o3d.visualization.draw_geometries([pcd])

    # 定义球枢转算法的球半径
    radii = [0.05, 0.1, 0.2]

    # 使用球枢转算法从点云中创建三角网格
    rec_mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
        pcd, o3d.utility.DoubleVector(radii))

    # 可视化重建的网格和原始点云
    o3d.visualization.draw_geometries([rec_mesh])

在这里插入图片描述

2.3 alpha重建

    import open3d as o3d
    import numpy as np

    # --------------------------- 加载点云 ---------------------------
    print("->正在加载点云... ")
    pcd = o3d.io.read_point_cloud(pcd_path)
    pcd = pcd.voxel_down_sample(voxel_size=0.005)
    print("原始点云:", pcd)
    # ==============================================================

    # ------------------------- Alpha shapes -----------------------
    alpha = 0.05
    print(f"alpha={alpha:.3f}")
    mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(pcd, alpha)
    mesh.compute_vertex_normals()
    o3d.visualization.draw_geometries([mesh], mesh_show_back_face=True)
    # 保存三角网格而不是点云
    o3d.io.write_triangle_mesh("./mesh.ply", mesh)

在这里插入图片描述
现在存在以下问题:

  1. 人体点云分割后缺少了彩色信息,icp配准后就无法使用彩色icp
  2. 配准后的点云如何有效重建

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

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

相关文章

外贸建站:WordPress搭建外贸独立站零基础自建站完整教程(2024)

对于做外贸来说&#xff0c;拥有自己的外贸独立网站真的非常重要。在外贸领域&#xff0c;如今各平台竞争激烈&#xff0c;规则多&#xff0c;成本高&#xff0c;价格战、政策变化快&#xff0c;还存在封店风险等等因素。在这种情况下&#xff0c;拥有外贸独立站就能很好规避上…

保卫蓝天:持续努力,任重道远|中联环保圈

经过长达十年的不懈努力&#xff0c;我国在大气污染防治领域取得了令人瞩目的历史性成就。空气质量明显改善&#xff0c;蓝天保卫战初见成效。但是也必须清醒地认识到&#xff0c;目前仍面临着诸多严峻挑战&#xff0c;保卫蓝天的任务依然任重道远。 2023 年&#xff0c;我国空…

使用pip安装Jupyter

通过pip安装Jupyter Notebook. pip install jupyter通过pip安装Jupyter Lab pip install jupyterlab启动Jupyter jupyter notebook

人人都离不开的算法:AI 时代的生存指南

文章目录 一、算法在生活中的“无处不在”二、算法在工作学习中的“智慧助力”三、算法在社会发展中的“驱动力量”四、算法带来的“双刃剑”效应五、应对算法挑战的策略《人人都离不开的算法——图解算法应用》编辑推荐1、通俗易懂2、技术科普3、贴近时代、贴近生活4、启发思考…

pygame--坦克大战(二)

加载敌方坦克 敌方坦克的方向是随机的&#xff0c;使用随机数生成。 初始化敌方坦克。 class EnemyTank(Tank):def __init__(self,left,top,speed):self.images {U: pygame.image.load(img/enemy1U.gif),D: pygame.image.load(img/enemy1D.gif),L: pygame.image.load(img/e…

僵死进程(僵尸进程)

1.僵死进程产生的原因或者条件: 什么是僵死进程?当子进程先于父进程结束,父进程没有获取子进程的退出码,此时子进程变成僵死进程. 简而言之,就是子进程先结束,并且父进程没有获取它的退出码; 那么僵死进程产生的原因或者条件就是:子进程先于父进程结束,并且父进程没有获取子进…

HAL STM32 硬件I2C方式读取AS5600磁编码器获取角度例程

HAL STM32 硬件I2C方式读取AS5600磁编码器获取角度例程 &#x1f4cd;相关篇《STM32 软件I2C方式读取AS5600磁编码器获取角度例程》 ✨stm32使用硬件I2C去读取角度数据&#xff0c;通过STM32CubeMX工具配置工程&#xff0c;读取角度数据&#xff0c;只需要调用一个函数&#xf…

vulhub打靶记录——healthcare

文章目录 主机发现端口扫描FTP—21search ProPFTd EXPFTP 匿名用户登录 web服务—80目录扫描search openemr exp登录openEMR 后台 提权总结 主机发现 使用nmap扫描局域网内存活的主机&#xff0c;命令如下&#xff1a; netdiscover -i eth0 -r 192.168.151.0/24192.168.151.1…

空间数据结构(四叉树,八叉树,BVH树,BSP树,K-d树)

下文参考&#xff1a;https://www.cnblogs.com/KillerAery/p/10878367.html 游戏编程知识课程 - 四分树(quadtree)_哔哩哔哩_bilibili 利用空间数据结构可以加速计算&#xff0c;是重要的优化思想。空间数据结构常用于场景管理&#xff0c;渲染&#xff0c;物理&#xff0c;游…

云计算对象存储服务

对象存储服务&#xff08;OSS&#xff09;中的存储桶(Bucket)叫做‘OBS桶 存储桶&#xff08;Bucket&#xff09;&#xff1a;存储桶式对象存储服务中用于存储对象的基本容器&#xff0c;类似于文件系统中的文件夹。每个存储桶具有唯一的名称&#xff0c;并且可以在桶中存储任…

用于自动驾驶,无人驾驶领域的IMU六轴陀螺仪传感器:M-G370

用于自动驾驶,无人驾驶的IMU惯导模块六轴陀螺仪传感器:M-G370。自2020年&#xff0c;自动驾驶,无人驾驶已经迎来新突破&#xff0c;自动驾驶汽车作为道路交通体系的一员&#xff0c;要能做到的就是先判断周边是否有障碍物&#xff0c;自身的行驶是否会对其他交通参与成员产生危…

STM32F4系列单片机的定时器讲解和计数器,PWM信号输出,PWM信号捕获的实现对电机进行控制和监测功能

1.定时器功能介绍&#xff1a; 在控制领域里面&#xff0c;我们可以用信号输出定时器&#xff0c;进行PWM的控制&#xff0c;从而达到控制电机的目的&#xff0c;通过输入捕获功能可以用来接收外部的数字信号&#xff0c;用于测量脉冲宽度、频率或周期等。在这里给大家介绍下&…

二维相位解包理论算法和软件【全文翻译-将相位分解为 “非旋转 “和 “旋转 “(2.4)】

2.4 将相位分解为 "非旋转 "和 "旋转 "部分 借用电磁场理论,可以用发散和卷曲来指定矢量场[9][10]。当且仅当矢量函数 F(r)(以及由其描述的场)在整个域 D 中不旋转或无旋转时,我们称之为矢量函数 F(r)、 因此,如果等式 2.30(也是第 2.2 节关于路径…

面对汽车充电桩隐私泄露威胁,应该怎么做?

想必各位车主在第一次扫码或刷卡使用汽车充电桩时&#xff0c;都会出现类似于上图的请求&#xff0c;除了上述的定位权限外&#xff0c;运营商还会索要你的网络权限、相机权限、通知权限、设备信息权限、存储权限、电话权限等。 那么你知道这些权限充电桩获取后到底用于什么吗&…

DELL服务器使用iDRAC升级BIOS等固件版本

前言 正值DELL推出DELL R730XD服务器最新的BIOS固件&#xff08;2.19.0 2024/3/18&#xff09;之际&#xff0c;本人也有合适的时间将手头的服务器BIOS固件进行升级操作。 本文博将DELL R730xd 的iDRAC8版本为例&#xff0c;介绍整个升级过程。其他DELL类型的服务器操作类似&…

黄金票据攻击

黄金票据攻击——域内横向移动技术 一、黄金票据攻击介绍&#xff1a; 黄金票据攻击是一种滥用Kerberos身份认证协议的攻击方式&#xff0c;它允许攻击者伪造域控krbtgt用户的TGT&#xff08;Ticket-Granting Ticket&#xff09;。通过这种方法&#xff0c;攻击者可以生成有效…

【C语言】2048小游戏【附源码】

一、游戏描述&#xff1a; 2048是一款数字益智类游戏&#xff0c;玩家需要使用键盘控制数字方块的移动&#xff0c;合并相同数字的方块&#xff0c;最终达到数字方块上出现“2048”的目标。 每次移动操作&#xff0c;所有数字方块会朝着指定方向同时滑动&#xff0c;并在靠近边…

supersqli-攻防世界

题目 加个报错 1 and 11 #没报错判断为单引号字符注入 爆显位 1 order by 2#回显正常 1 order by 3#报错 说明列数是2 尝试联合查询 -1 union select 1,2# 被过滤了 return preg_match("/select|update|delete|drop|insert|where|\./i",$inject); select|update|d…

vCenter Server出现no healthy upstream的解决方法

https://blog.51cto.com/wangchunhai/4907250 访问vCenter 7.0 地址后&#xff0c;页面出现“no healthy upstream”,无法正常登录vCenter&#xff0c;重启后依旧如此&#xff0c;该故障的前提是没有对vCenter做过任何配置&#xff0c;如下图所示。 尝试登录"VMware vCen…

快速排渣器与矿用快速除渣器

你不想虚度光阴&#xff0c;你想用自己的努力&#xff0c;书写自己人生的精彩&#xff0c;那么&#xff0c;你脚下的路不能回头&#xff0c;铺满掌声和鲜花的人生路上&#xff0c;脚下没有那么平坦&#xff0c;或是泥泞&#xff0c;或是陷阱&#xff0c;别管多么艰难的路程&…