首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >VTK中DICOM到点云的转换

VTK中DICOM到点云的转换
EN

Stack Overflow用户
提问于 2017-10-21 18:33:11
回答 2查看 803关注 0票数 1

有没有使用VTK将DICOM (ct扫描)图像转换为点云的方法?

VTK允许读取DICOM和DICOM系列和体渲染,但是可以从一系列DICOM图像生成点云吗?

如果这在VTK中是不可能的,有没有其他的库可以用来实现这个目的呢?

EN

回答 2

Stack Overflow用户

发布于 2018-01-19 06:30:13

这是一个dicom到点云的演示。根据图像的采集方式,Dicom文件是非常不同的,但这是我们一段时间以来一直用于CT扫描的文件。这是“手动版本”,即你需要与终端交互才能浏览dicom目录。这是可以自动化的,但它高度依赖于您的应用程序。

我安装了PCL8.0和vtkdicom。(我可以在没有vtkdicom的情况下进行有限的实现,但它的特性使应用程序在处理不同的dicom目录结构时更加健壮)。

您需要将main中的函数指向计算机上的相应目录(应该是包含DICOMDIR文件的文件)。加载dicom后,可视化工具具有键盘输入m和n,用于控制要可视化的强度目标。(您可以轻松地更改代码以过滤任何参数: x、y、z、强度),并可以根据需要更改宽度或步长。

代码语言:javascript
运行
复制
#include <pcl/common/common_headers.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/filters/passthrough.h>

#include <boost/thread/thread.hpp>

#include <vtkSmartPointer.h>
#include <vtkDICOMImageReader.h>
#include "vtkImageData.h"
#include "vtkDICOMDirectory.h"
#include "vtkDICOMItem.h"
#include "vtkStringArray.h"
#include "vtkIntArray.h"
#include "vtkDICOMReader.h"


bool loadDICOM(pcl::PointCloud<pcl::PointXYZI>::Ptr outCloud, std::string fullPathToDicomDir)
{
    // load DICOM dir file
    vtkSmartPointer<vtkDICOMDirectory> ddir =
        vtkSmartPointer<vtkDICOMDirectory>::New();
    ddir->SetDirectoryName(fullPathToDicomDir.c_str());
    ddir->Update();

    //select patient
    int n = ddir->GetNumberOfPatients();
    int patientSelection = 0;
    if (n > 1)
    {
        std::cout << "Select Patient number, total count: " << n << std::endl;
        std::string userInput;
        std::getline(std::cin, userInput);
        patientSelection = std::stoi(userInput);
    }
    const vtkDICOMItem& patientItem = ddir->GetPatientRecord(patientSelection);
    std::cout << "Patient " << patientSelection << ": " << patientItem.Get(DC::PatientID).AsString() << "\n";

    //select study
    vtkIntArray* studies = ddir->GetStudiesForPatient(patientSelection);
    vtkIdType m = studies->GetMaxId() + 1;
    int studySelection = 0;
    if (m > 1)
    {
        std::cout << "Select study, total count: " << m << std::endl;
        std::string userInput;
        std::getline(std::cin, userInput);
        studySelection = std::stoi(userInput);
    }

    int j = studies->GetValue(studySelection);
    const vtkDICOMItem& studyItem = ddir->GetStudyRecord(j);
    const vtkDICOMItem& studyPItem = ddir->GetPatientRecordForStudy(j);
    cout << " Study " << j << ": \""
        << studyItem.Get(DC::StudyDescription).AsString() << "\" \""
        << studyPItem.Get(DC::PatientName).AsString() << "\" "
        << studyItem.Get(DC::StudyDate).AsString() << "\n";
    int k0 = ddir->GetFirstSeriesForStudy(j);
    int k1 = ddir->GetLastSeriesForStudy(j);

    int seriesSelection;
    std::cout << "Select series, range: " << k0 << " to " << k1 << std::endl;
    for (int i = k0; i <= k1; i++)
    {
        const vtkDICOMItem& seriesItem = ddir->GetSeriesRecord(i);
        vtkStringArray* a = ddir->GetFileNamesForSeries(i);

        cout << "  Series " << i << ": \""
            << seriesItem.Get(DC::SeriesDescription).AsString() << "\" "
            << seriesItem.Get(DC::SeriesNumber).AsString() << " "
            << seriesItem.Get(DC::Modality).AsString() << ", Images: "
            << a->GetNumberOfTuples() << "\n";
    }

    std::string userInput;
    std::getline(std::cin, userInput);
    seriesSelection = std::stoi(userInput);

    const vtkDICOMItem& seriesItem = ddir->GetSeriesRecord(seriesSelection);
    cout << "  Series " << seriesSelection << ": \""
        << seriesItem.Get(DC::SeriesDescription).AsString() << "\" "
        << seriesItem.Get(DC::SeriesNumber).AsString() << " "
        << seriesItem.Get(DC::Modality).AsString() << "\n";

    vtkStringArray* a = ddir->GetFileNamesForSeries(seriesSelection);
    vtkDICOMReader* reader = vtkDICOMReader::New();
    reader->SetFileNames(a);
    reader->Update();



    vtkSmartPointer<vtkImageData> sliceData = reader->GetOutput();

    int numberOfDims = sliceData->GetDataDimension();
    int* dims = sliceData->GetDimensions();
    std::cout << "Cloud dimensions: ";
    int totalPoints = 1;
    for (int i = 0; i < numberOfDims; i++)
    {
        std::cout << dims[i] << " , ";
        totalPoints = totalPoints * dims[i];
    }
    std::cout << std::endl;

    std::cout << "Number of dicom points: " << totalPoints << std::endl;


    //read data into grayCloud

    double* dataRange = sliceData->GetScalarRange();
    double* spacingData = reader->GetDataSpacing();
    std::cout << "Data intensity bounds... min: " << dataRange[0] << ", max: " << dataRange[1] << std::endl;
    if (numberOfDims != 3)
    {
        std::cout << "Incorrect number of dimensions in dicom file, generation failed..." << std::endl;
        return false;
    }
    else
    {
        Eigen::RowVector3f spacing = Eigen::RowVector3f(spacingData[0], spacingData[1], spacingData[2]);
        Eigen::RowVector3i dimensions = Eigen::RowVector3i(dims[0], dims[1], dims[2]);

        outCloud->points.clear();

        std::cout << "x spacing: " << spacing(0) << std::endl;
        std::cout << "y spacing: " << spacing(1) << std::endl;
        std::cout << "z spacing: " << spacing(2) << std::endl;

        for (int z = 0; z < dims[2]; z++)
        {
            if (z % 50 == 0)
            {
                double percentageComplete = (double)z / (double)dims[2];
                std::cout << "Dicom Read Progress: " << (int)(100.0 * percentageComplete) << "%" << std::endl;
            }
            for (int y = 0; y < dims[1]; y++)
            {
                for (int x = 0; x < dims[0]; x++)
                {
                    double tempIntensity = sliceData->GetScalarComponentAsDouble(x, y, z, 0);

                    int tempX = x;

                    pcl::PointXYZI tempPt = pcl::PointXYZI();
                    

                    if (!isinf(tempIntensity) && !isnan(tempIntensity))
                    {
                        //map value into positive realm
                        //tempIntensity = ((tempIntensity - dataRange[0]) / (dataRange[1] - dataRange[0]));
                        if (tempIntensity > SHRT_MAX) { tempIntensity = SHRT_MAX; }
                        else if (tempIntensity < SHRT_MIN) { tempIntensity = SHRT_MIN; }
                    }
                    else
                    {
                        tempIntensity = 0;
                    }

                    tempPt.x = tempX;
                    tempPt.y = y;
                    tempPt.z = z;
                    tempPt.intensity = tempIntensity;
                    outCloud->points.push_back(tempPt);
                }
            }
        }
    }
    std::cout << "Load Dicom Cloud Complete!" << std::endl;
    return true;
}


int indexSlice = 0;
void keyboardEventOccurred(const pcl::visualization::KeyboardEvent& event, void* viewer)
{
    if (event.getKeySym() == "n" && event.keyDown())
    {
        indexSlice -= 1;
    }
    else if (event.getKeySym() == "m" && event.keyDown())
    {
        indexSlice += 1;
    }
}

void displayCloud(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, std::string field, int step, int width, std::string window_name = "default")
{
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer(window_name));

    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "id");

    viewer->registerKeyboardCallback(keyboardEventOccurred, (void*)viewer.get());

    
    pcl::PointCloud<pcl::PointXYZI>::Ptr tempCloud(new pcl::PointCloud<pcl::PointXYZI>);

    pcl::PassThrough<pcl::PointXYZI> pass;
    pass.setInputCloud(cloud);
    pass.setFilterFieldName(field); //could gate this on intensity if u preferred

    int lastIndex = indexSlice-1; //proc first cycle
    while (!viewer->wasStopped()) {
        
        if (indexSlice != lastIndex)
        {
            int low = step * indexSlice - width / 2;
            int high = step * indexSlice + width / 2;
            pass.setFilterLimits(low, high);
            pass.filter(*tempCloud);
            lastIndex = indexSlice;
            std::cout << field<< " range: " <<low<<" , "<<high<< std::endl;

            viewer->removeAllPointClouds();
            pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointXYZI> point_cloud_color_handler(tempCloud, "intensity");
            viewer->addPointCloud< pcl::PointXYZI >(tempCloud, point_cloud_color_handler, "id");
        }
        
        viewer->spinOnce(50);
    }
    viewer->close();
}

// --------------
// -----Main-----
// --------------
int main(int argc, char** argv)
{
    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);
    loadDICOM(cloud, "C:/Local Software/voyDICOM/resources/DICOM_Samples/2021APR14 MiniAchors_V0");
    displayCloud(cloud,"intensity",100,50);
    return 0;
}

请注意,在大多数情况下,dicom文件在原始维度方面相对较大,因此我很少(从不?)我已经将整个dicom文件加载到点云中(直到此代码)。通常,我所做的是以密集的格式(短数组)处理它,然后根据从该数据中选择的内容创建云。这样,在进入稀疏数据集(点云)之前,您可以执行某些从锁定的数据网格中受益的映像操作(打开、关闭等),因为在稀疏数据集上,一切都变得非常昂贵。

它与我的一个调试dicom集一起工作的美丽画面:

票数 2
EN

Stack Overflow用户

发布于 2017-10-23 22:37:19

我想我可能已经找到了一种方法。我还没有尝试过,但理论上它应该是可行的。

首先,需要使用VTK将DICOM图像转换为.vtk格式。在将DICOM图像转换为.vtk格式之后,可以使用点云库将其转换为.pcd (点云格式)。

票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46862348

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档