VTK如何通过c ++上的imageImport导入3D图像数据以进行体积渲染

时间:2016-10-11 11:54:34

标签: 3d vtk volume-rendering

通过VT​​K Python API,我们可以导入/制作3D图像数据和体积渲染,如下所示:

enter image description here

Python代码是:

# coding=utf-8
import vtk
from numpy import *
import random

num = 74
# We begin by creating the data we want to render.
# For this tutorial, we create a 3D-image containing three overlapping cubes.
# This data can of course easily be replaced by data from a medical CT-scan or anything else three dimensional.
# The only limit is that the data must be reduced to unsigned 8 bit or 16 bit integers.
data_matrix = zeros([num, num, num], dtype=uint8)
# data_matrix[0:35, 0:35, 0:35] = 50
# data_matrix[25:55, 25:55, 25:55] = 100
# data_matrix[45:74, 45:74, 45:74] = 150

for i in range(0, num - 1):
    for j in range(0, num - 1):
        for k in range(0, num - 1):
            data_matrix[i:i + 1, j:j + 1, k:k + 1] = 100

# For VTK to be able to use the data, it must be stored as a VTK-image. This can be done by the vtkImageImport-class which
# imports raw data and stores it.
dataImporter = vtk.vtkImageImport()
# The previously created array is converted to a string of chars and imported.
data_string = data_matrix.tostring()
# dataImporter.CopyImportVoidPointer(data_string, len(data_string))
dataImporter.SetImportVoidPointer(data_matrix)
# The type of the newly imported data is set to unsigned char (uint8)
dataImporter.SetDataScalarTypeToUnsignedChar()
# Because the data that is imported only contains an intensity value (it isn't RGB-coded or something similar), the importer
# must be told this is the case.
dataImporter.SetNumberOfScalarComponents(1)
# The following two functions describe how the data is stored and the dimensions of the array it is stored in. For this
# simple case, all axes are of length 75 and begins with the first element. For other data, this is probably not the case.
# I have to admit however, that I honestly don't know the difference between SetDataExtent() and SetWholeExtent() although
# VTK complains if not both are used.
dataImporter.SetDataExtent(0, 74, 0, 74, 0, 74)
dataImporter.SetWholeExtent(0, 74, 0, 74, 0, 74)

# The following class is used to store transparency-values for later retrieval. In our case, we want the value 0 to be
# completely opaque whereas the three different cubes are given different transparency-values to show how it works.
alphaChannelFunc = vtk.vtkPiecewiseFunction()
alphaChannelFunc.AddPoint(0, 0.0)
# alphaChannelFunc.AddPoint(50, 0.001)
# alphaChannelFunc.AddPoint(100, 0.02)
alphaChannelFunc.AddPoint(150, 0.02)

# This class stores color data and can create color tables from a few color points. For this demo, we want the three cubes
# to be of the colors red green and blue.
colorFunc = vtk.vtkColorTransferFunction()
colorFunc.AddRGBPoint(50, 0.0, 0.2, 0.2)
colorFunc.AddRGBPoint(100, 0.0, 1.0, 0.0)
colorFunc.AddRGBPoint(150, 0.0, 0.0, 1.0)

# The previous two classes stored properties. Because we want to apply these properties to the volume we want to render,
# we have to store them in a class that stores volume properties.
volumeProperty = vtk.vtkVolumeProperty()
volumeProperty.SetColor(colorFunc)
volumeProperty.SetScalarOpacity(alphaChannelFunc)

# This class describes how the volume is rendered (through ray tracing).
compositeFunction = vtk.vtkVolumeRayCastCompositeFunction()
# We can finally create our volume. We also have to specify the data for it, as well as how the data will be rendered.
volumeMapper = vtk.vtkVolumeRayCastMapper()
volumeMapper.SetVolumeRayCastFunction(compositeFunction)
volumeMapper.SetInputConnection(dataImporter.GetOutputPort())

# The class vtkVolume is used to pair the previously declared volume as well as the properties to be used when rendering that volume.
volume = vtk.vtkVolume()
volume.SetMapper(volumeMapper)
volume.SetProperty(volumeProperty)

# With almost everything else ready, its time to initialize the renderer and window, as well as creating a method for exiting the application
renderer = vtk.vtkRenderer()
renderWin = vtk.vtkRenderWindow()
renderWin.AddRenderer(renderer)
renderInteractor = vtk.vtkRenderWindowInteractor()
renderInteractor.SetRenderWindow(renderWin)

# We add the volume to the renderer ...
renderer.AddVolume(volume)
# ... set background color to white ...
renderer.SetBackground(1, 1, 1)
# ... and set window size.
renderWin.SetSize(400, 400)


# A simple function to be called when the user decides to quit the application.
def exitCheck(obj, event):
    if obj.GetEventPending() != 0:
        obj.SetAbortRender(1)


# Tell the application to use the function as an exit check.
# renderWin.AddObserver("AbortCheckEvent", exitCheck)

renderInteractor.Initialize()
# Because nothing will be rendered without any input, we order the first render manually before control is handed over to the main-loop.
renderWin.Render()
renderInteractor.Start()

但对于c ++ API,我无法通过ImageImport获得相同的体积渲染。

以下代码是我编写的C ++代码,参考上面的Python代码。 但结果如下:

enter image description here

我编写的C ++代码引用了上面的Python代码:

#include <iostream>
#include <vtkStructuredPointsReader.h>
#include <vtkVolumeRayCastCompositeFunction.h>
#include <vtkVolumeRayCastMapper.h>
#include <vtkRendererCollection.h>
#include <vtkSmartPointer.h>
#include <vtkImageImport.h>
#include <stdlib.h>
// VTK includes
#include "vtkBoxWidget.h"
#include "vtkCamera.h"
#include "vtkCommand.h"
#include "vtkColorTransferFunction.h"
#include "vtkDICOMImageReader.h"
#include "vtkImageData.h"
#include "vtkImageResample.h"
#include "vtkMetaImageReader.h"
#include "vtkPiecewiseFunction.h"
#include "vtkPlanes.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"
#include "vtkXMLImageDataReader.h"
#include "vtkFixedPointVolumeRayCastMapper.h"

int main() {
    std::cout << "Hello, World!" << std::endl;
    // create data
    // Create a c-style image
    const int width = 4;
    const int height = 4;
    const int depth = 4;

    unsigned char cImage[width * height * depth];
    unsigned char value = 0;
    for (unsigned int row = 0; row < height; ++row) {
        for (unsigned int col = 0; col < width; ++col) {
            for (unsigned int z = 0; z < depth; ++z) {
                cImage[row * width * depth + col * depth + z] = 100;
            }
        }
    }
    // Convert the c-style image to a vtkImageData
    vtkSmartPointer<vtkImageImport> imageImport =
            vtkSmartPointer<vtkImageImport>::New();

    imageImport->SetImportVoidPointer(cImage);
    imageImport->SetDataScalarTypeToUnsignedChar();
    imageImport->SetNumberOfScalarComponents(1);
    imageImport->SetDataSpacing(1, 1, 1);
    imageImport->SetDataOrigin(0, 0, 0);
    imageImport->SetDataExtent(0, width - 1, 0, height - 1, 0, depth - 1);
    imageImport->SetWholeExtent(0, width - 1, 0, height - 1, 0, depth - 1);
//    imageImport->SetDataExtentToWholeExtent();
//    imageImport->SetDataScalarTypeToUnsignedChar();
//    imageImport->SetNumberOfScalarComponents(1);
    imageImport->Update();

    // Create the standard ren, render window and interactor
    vtkRenderer *ren = vtkRenderer::New();
    vtkRenderWindow *renWin = vtkRenderWindow::New();
    renWin->AddRenderer(ren);
    vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();

    iren->SetRenderWindow(renWin);

    // Create the reader for the data
//    vtkStructuredPointsReader *reader = vtkStructuredPointsReader::New();
//    reader->SetFileName("/home/honhe/ClionProjects/Demo_vtk_SimpleRayCast/ironProt.vtk");

    // Create transfer mapping scalar value to opacity
    vtkPiecewiseFunction *opacityTransferFunction = vtkPiecewiseFunction::New();
    opacityTransferFunction->AddPoint(0, 0.0);
    opacityTransferFunction->AddPoint(150, 0.2);

    // Create transfer mapping scalar value to color
    vtkColorTransferFunction *colorTransferFunction = vtkColorTransferFunction::New();
    colorTransferFunction->AddRGBPoint(0.0, 0.0, 0.0, 0.0);
    colorTransferFunction->AddRGBPoint(100.0, 0.0, 1.0, 0.0);

    // The property describes how the data will look
    vtkVolumeProperty *volumeProperty = vtkVolumeProperty::New();
    volumeProperty->SetColor(colorTransferFunction);
    volumeProperty->SetScalarOpacity(opacityTransferFunction);
    volumeProperty->ShadeOn();
    volumeProperty->SetInterpolationTypeToLinear();

    // The mapper / ray cast function know how to render the data
    vtkVolumeRayCastCompositeFunction *compositeFunction = vtkVolumeRayCastCompositeFunction::New();
    vtkVolumeRayCastMapper *volumeMapper = vtkVolumeRayCastMapper::New();
    volumeMapper->SetVolumeRayCastFunction(compositeFunction);
    volumeMapper->SetInputConnection(imageImport->GetOutputPort());

    // The volume holds the mapper and the property and
    // can be used to position/orient the volume
    vtkVolume *volume = vtkVolume::New();
    volume->SetMapper(volumeMapper);
    volume->SetProperty(volumeProperty);

    ren->AddVolume(volume);
    ren->SetBackground(1, 1, 1);
    renWin->SetSize(400, 400);

    iren->Initialize();
    renWin->Render();
    iren->Start();
    return 0;
}

我搜索示例代码,但只找到以下内容,它们无法使用3d图像数据。

任何帮助欣赏!

1 个答案:

答案 0 :(得分:0)

感谢您的节目,我学到了很多东西。 我用你的程序得到了相同的结果,发现原因是选中的光线投射函数---复合函数。 我将此功能更改为MIP功能并且运行良好,如下所示:

 //The mapper/ray cast fuction know how to render the data
 //    vtkSmartPointer<vtkVolumeRayCastCompositeFunction> compositeFunc
 //            = vtkSmartPointer<vtkVolumeRayCastCompositeFunction>::New();
    vtkSmartPointer<vtkVolumeRayCastMIPFunction> compositeFunc
            = vtkSmartPointer<vtkVolumeRayCastMIPFunction>::New();
    vtkSmartPointer<vtkVolumeRayCastMapper> volumeMapper = vtkSmartPointer<vtkVolumeRayCastMapper>::New();

我无法理解这两个功能。在VTKTextbook中,它有很好的描述。