使用python将dicom格式的文件转换为点云PLY

2024-02-19  本文已影响0人  小黄不头秃

有时候我们会需要将CBCT扫描出来的dicom序列文件转换为点云格式,并进行下一步的处理。
那么这里我提供了一种思路,将dicom数据转换成了点云,但是这种转换仅保留了点的位置信息。可用于进一步的特征提取或者配准等工作

import pydicom 
import numpy as np 
import os 
from tqdm import tqdm 
import time 

# 这里是使用生成一个ASCII格式的PLY文件,包含了点的XYZ坐标以及可选的颜色信息。如果你的点云没有颜色信息,只需要调用create_ply_file(points)即可。
# 注意:对于大型点云数据集,通常会考虑使用二进制PLY格式以减小文件大小和提高读写效率。上述例子仅针对ASCII PLY格式,转换到二进制格式需要修改写入部分,并使用struct.pack()进行数据打包。

# 这里寻找点云的点
def get_pointlist_from_dicom(dicom_dir, thresh_hold=0):
    print("Loading dicom series".center(120, "="))
    point_list = np.empty([0,3])

    for dcm_f in tqdm(os.listdir(dicom_dir)):
        file_path = dicom_dir + dcm_f 
        dcm = pydicom.dcmread(file_path)
        dcm_array = dcm.pixel_array
        # 获取窗宽和窗位(如果DICOM头中有这些信息)  
        window_width = dcm.WindowWidth if 'WindowWidth' in dcm else 1500  
        window_center = dcm.WindowCenter if 'WindowCenter' in dcm else 50  
        series_number = float(dcm.InstanceNumber)

        # 窗宽窗位调整  
        min_value = window_center - window_width // 2  # -1024
        max_value = window_center + window_width // 2  # 3072 
        scaled_pixel_data = np.clip(dcm_array, min_value, max_value)
        scaled_pixel_data = (scaled_pixel_data - min_value) / (max_value - min_value) * 255  
        scaled_pixel_data = scaled_pixel_data.astype(np.uint8)  

        # 使用numpy
        x_index, y_index = np.where(scaled_pixel_data>thresh_hold)
        z_index = np.array([series_number]*len(x_index))
        layer_points = np.concatenate([[x_index], [y_index], [z_index]], axis=0).T
        point_list = np.concatenate([point_list, layer_points], axis=0)

        # # 垃圾写法 比上面的写法慢了 34 倍
        # for x in range(len(scaled_pixel_data)):
        #     for y in range(len(scaled_pixel_data)):
        #         if scaled_pixel_data[x][y] > thresh_hold:
        #                 point_list.append([x, y, layer_index])
    print("\n")
    return point_list
                         

# 将点写成点云文件
def write_ply_file(point_list, point_cloud_file):
    print("Writting PLY file".center(120, "="))
    with open(point_cloud_file, "w+") as f:
        # 写入PLY文件头
        f.write("ply\n")
        f.write("format ascii 1.0\n")
        f.write("element vertex %d\n" % len(point_list))
        f.write("property float x\n")
        f.write("property float y\n")
        f.write("property float z\n")

        f.write("property uchar red\n")
        f.write("property uchar green\n")
        f.write("property uchar blue\n")

        f.write("end_header\n")

        for (x,y,z) in tqdm(point_list):
            f.write("%f %f %f %d %d %d\n" % (x, y, z, 0, 255, 0))

    print("Finished!\n")


if __name__ == "__main__":
    dicom_dir = "./publick/" 
    point_cloud_file = f"./output/tooth_{str(int(time.time()))}.ply" 

    if not os.path.exists("./output/"):
        os.mkdir("./output/")

    point_list = get_pointlist_from_dicom(dicom_dir, thresh_hold=125)  
    write_ply_file(point_list,point_cloud_file)

结果展示:


上一篇下一篇

猜你喜欢

热点阅读