VTK例子--使用不同的vtkActor同时显示灰度图、体渲染、多边形

在实际项目中,常遇到不同类型的数据在同一个渲染窗口显示;如网格多边形与灰度图像的显示、体渲染与多边形的显示、体渲染与灰度图像的显示,如下面几张图的效果;

多边形+灰度图像

体渲染+多边形

体渲染+灰度图像

如何实现这种混合显示的效果呢?在初接触VTK时,就认为是多个Actor在同一个Renderer下渲染到同一个Window下就可以了,至于使用哪一种Actor没有仔细验证;

前段时间从阿兵-AI医疗处看到一个例子VTK交互之vtkVolumePicker,是用vtkVolumePicker做体数据光线投射的拾取器;例子中展示的效果图里包含了体渲染、多边形和灰度图像,就是我想要实现的效果,写个笔记记录一下;

vtkImageActor的介绍VTK笔记-图像相关-vtkImageActor类

使用vtkImageActor和vtkActor实现,图像和网格多边形的展示;

auto cyl = vtkSmartPointer<vtkCylinderSource>::New();
cyl->SetResolution(32);
cyl->SetHeight(1000);
cyl->SetRadius(10);
cyl->SetCenter(0, .5, 0);
cyl->Update();

vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(cyl->GetOutput());
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
actor->GetProperty()->SetAmbient(0.5);
actor->GetProperty()->SetDiffuse(0.5);
actor->GetProperty()->SetSpecular(0.1);

vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New();
reader->SetFileName("G:/Data/lena.bmp");
reader->Update();

vtkSmartPointer<vtkImageActor> imgActor = vtkSmartPointer<vtkImageActor>::New();
imgActor->GetMapper()->SetInputConnection(reader->GetOutputPort());

vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(imgActor);
renderer->AddActor(actor);

renderer->SetBackground(0.4, 0.5, 0.6);

vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->SetSize(500, 500);
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
//renderWindowInteractor->SetInteractorStyle(style);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Initialize();
renderWindowInteractor->Start();

展示效果如下,一个旗杆上飘着的蕾娜:

vtkVolumePicker的例子

具体步骤:

  1. 使用vtkImageReader2类读取一个图像序列,设置相关参数,可以获取vtkImageData对象;

  1. 使用vtkMarchingCubes从vtkImageData对象中提取面渲染,这里可参考VTK笔记-CT图像获取皮肤等值面-vtkContourFilter类的使用

  1. 灰度图像则是直接由vtkImageActor设置图像范围,取得一个矢状位的图像;

  1. 创建了两个圆锥体,用于指示拾取点位置;

  1. 使用方法AddViewProp将前面创建的vtkImageActor、vtkActor、vtkVolume设置到render中进行渲染;

#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkImageReader2.h>
#include <vtkGPUVolumeRayCastMapper.h>
#include <vtkColorTransferFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkVolumeProperty.h>
#include <vtkMarchingCubes.h>
#include <vtkPolyDataNormals.h>
#include <vtkStripper.h>
#include <vtkCellLocator.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkActor.h>
#include <vtkLookupTable.h>
#include <vtkImageMapToColors.h>
#include <vtkImageActor.h>
#include <vtkImageMapper3D.h>
#include <vtkTransform.h>
#include <vtkPlane.h>
#include <vtkCamera.h>
#include <vtkConeSource.h>
#include <vtkDataSetMapper.h>
#include <vtkVolumePicker.h>
#include <vtkCommand.h>

class vtkMyMouseCommand : public vtkCommand
{
public:
    static vtkMyMouseCommand *New()
    {
        return new vtkMyMouseCommand;
    }

    //A function to point an actor along a vector
    void PointCone(vtkActor *actor, double nx, double ny, double nz)
    {
        actor->SetOrientation(0.0, 0.0, 0.0);
        double n = std::sqrt(nx*nx + ny * ny + nz * nz);
        if (nx < 0.0)
        {
            actor->RotateWXYZ(180, 0, 1, 0);
            n = -n;

        }
        actor->RotateWXYZ(180, (nx + n)*0.5, ny*0.5, nz*0.5);
    }

    virtual void Execute(vtkObject *caller, unsigned long eventId, void *callData)
    {
        //renWin->HideCursor();
        int x, y;
        iren->GetEventPosition(x, y);
        picker->Pick(x, y, 0, ren);
        double p[3];
        picker->GetPickPosition(p);
        std::cout << p[0] << " " << p[1] << " " << p[2] << std::endl;
        double n[3];
        picker->GetPickNormal(n);
        redCone->SetPosition(p[0], p[1], p[2]);
        PointCone(redCone, n[0], n[1], n[2]);
        greenCone->SetPosition(p[0], p[1], p[2]);
        PointCone(greenCone, -n[0], -n[1], -n[2]);
        iren->Render();
    }

    vtkRenderWindow *renWin;
    vtkRenderer *ren;
    vtkRenderWindowInteractor *iren;
    vtkVolumePicker *picker;
    vtkActor *redCone;
    vtkActor *greenCone;
};

int main(int, char*[])
{
    vtkNew<vtkRenderer> ren;
    vtkNew<vtkRenderWindow> renWin;
    renWin->AddRenderer(ren);
    vtkNew<vtkRenderWindowInteractor> iren;
    iren->SetRenderWindow(renWin);

    //read the volume
    vtkNew<vtkImageReader2> reader;
    reader->SetDataExtent(0, 63, 0, 63, 0, 92);
    reader->SetFileNameSliceOffset(1);
    reader->SetDataScalarTypeToUnsignedShort();
    reader->SetDataByteOrderToLittleEndian();
    reader->SetFilePrefix("G:/Data/headsq/quarter");
    reader->SetDataSpacing(3.2, 3.2, 1.5);

    //set up the volume rendering
    vtkNew<vtkGPUVolumeRayCastMapper> volumeMapper;
    volumeMapper->SetInputConnection(reader->GetOutputPort());
    volumeMapper->CroppingOn();
    double croppingRegionPlanes[6] = { 0.0, 141.6, 0.0, 201.6, 0.0, 138.0 };
    volumeMapper->SetCroppingRegionPlanes(croppingRegionPlanes);

    vtkNew<vtkColorTransferFunction> volumeColor;
    volumeColor->AddRGBPoint(0, 0.0, 0.0, 0.0);
    volumeColor->AddRGBPoint(180, 0.3, 0.1, 0.2);
    volumeColor->AddRGBPoint(1000, 1.0, 0.7, 0.6);
    volumeColor->AddRGBPoint(2000, 1.0, 1.0, 0.9);

    vtkNew<vtkPiecewiseFunction> volumeScalarOpacity;
    volumeScalarOpacity->AddPoint(0, 0.0);
    volumeScalarOpacity->AddPoint(180, 0.0);
    volumeScalarOpacity->AddPoint(1000, 0.2);
    volumeScalarOpacity->AddPoint(2000, 0.8);

    vtkNew<vtkPiecewiseFunction> volumeGradientOpacity;
    volumeGradientOpacity->AddPoint(0, 0.0);
    volumeGradientOpacity->AddPoint(90, 0.5);
    volumeGradientOpacity->AddPoint(100, 1.0);

    vtkNew<vtkVolumeProperty> volumeProperty;
    volumeProperty->SetColor(volumeColor);
    volumeProperty->SetScalarOpacity(volumeScalarOpacity);
    volumeProperty->SetGradientOpacity(volumeGradientOpacity);
    volumeProperty->SetInterpolationTypeToLinear();
    volumeProperty->ShadeOff();
    volumeProperty->SetAmbient(0.6);
    volumeProperty->SetDiffuse(0.6);
    volumeProperty->SetSpecular(0.1);

    vtkNew<vtkVolume> volume;
    volume->SetMapper(volumeMapper);
    volume->SetProperty(volumeProperty);

    // Do the surface rendering
    vtkNew<vtkMarchingCubes> boneExtractor;
    boneExtractor->SetInputConnection(reader->GetOutputPort());
    boneExtractor->SetValue(0, 1150);
    boneExtractor->Update();

    vtkNew<vtkPolyDataNormals>boneNormals;
    boneNormals->SetInputConnection(boneExtractor->GetOutputPort());
    boneNormals->SetFeatureAngle(60.0);

    vtkNew<vtkStripper> boneStripper;
    boneStripper->SetInputConnection(boneNormals->GetOutputPort());

    vtkNew<vtkCellLocator> boneLocator;
    boneLocator->SetDataSet(boneExtractor->GetOutput());
    boneLocator->LazyEvaluationOn();

    vtkNew<vtkPolyDataMapper> boneMapper;
    boneMapper->SetInputConnection(boneStripper->GetOutputPort());
    boneMapper->ScalarVisibilityOff();

    vtkNew<vtkProperty> boneProperty;
    boneProperty->SetColor(1.0, 1.0, 0.9);

    vtkNew<vtkActor> bone;
    bone->SetMapper(boneMapper);
    bone->SetProperty(boneProperty);

    //Create an image actor
    vtkNew<vtkLookupTable> table;
    table->SetRange(0, 2000);
    table->SetRampToLinear();
    table->SetValueRange(0, 1);
    table->SetHueRange(0, 0);
    table->SetSaturationRange(0, 0);

    vtkNew<vtkImageMapToColors> mapToColors;
    mapToColors->SetInputConnection(reader->GetOutputPort());
    mapToColors->SetLookupTable(table);
    mapToColors->Update();

    vtkNew<vtkImageActor> imageActor;
    imageActor->GetMapper()->SetInputConnection(mapToColors->GetOutputPort());
    imageActor->SetDisplayExtent(32, 32, 0, 63, 0, 92);

    //make a transform and some clipping planes
    vtkNew<vtkTransform> transform;
    transform->RotateWXYZ(-20, 0.0, -0.7, 0.7);
    volume->SetUserTransform(transform);
    bone->SetUserTransform(transform);
    imageActor->SetUserTransform(transform);

    auto c = volume->GetCenter();

    vtkNew<vtkPlane> volumeClip;
    volumeClip->SetNormal(0, 1, 0);
    volumeClip->SetOrigin(c[0], c[1], c[2]);

    vtkNew<vtkPlane> boneClip;
    boneClip->SetNormal(1, 0, 0);
    boneClip->SetOrigin(c[0], c[1], c[2]);

    volumeMapper->AddClippingPlane(volumeClip);
    boneMapper->AddClippingPlane(boneClip);

    ren->AddViewProp(volume);
    ren->AddViewProp(bone);
    ren->AddViewProp(imageActor);

    auto camera = ren->GetActiveCamera();
    camera->SetFocalPoint(c[0], c[1], c[2]);
    camera->SetPosition(c[0] + 500, c[1] - 100, c[2] - 100);
    camera->SetViewUp(0, 0, -1);

    //the cone points along the - x axis
    vtkNew<vtkConeSource> coneSource;
    coneSource->CappingOn();
    coneSource->SetHeight(12);
    coneSource->SetRadius(5);
    coneSource->SetResolution(31);
    coneSource->SetCenter(6, 0, 0);
    coneSource->SetDirection(-1, 0, 0);

    vtkNew<vtkDataSetMapper> coneMapper;
    coneMapper->SetInputConnection(coneSource->GetOutputPort());

    vtkNew<vtkActor> redCone;
    redCone->PickableOff();
    redCone->SetMapper(coneMapper);
    redCone->GetProperty()->SetColor(1, 0, 0);

    vtkNew<vtkActor> greenCone;
    greenCone->PickableOff();
    greenCone->SetMapper(coneMapper);
    greenCone->GetProperty()->SetColor(0, 1, 0);

    //Add the two cones(or just one, if you want);
    ren->AddViewProp(redCone);
    ren->AddViewProp(greenCone);

    //the picker
    vtkNew<vtkVolumePicker> picker;
    picker->SetTolerance(1e-6);
    picker->SetVolumeOpacityIsovalue(0.1);
    //locator is optional, but improves performance for large polydata
    picker->AddLocator(boneLocator);

    renWin->Render();
    vtkNew<vtkMyMouseCommand> mouseCommand;
    mouseCommand->renWin = renWin;
    mouseCommand->ren = ren;
    mouseCommand->iren = iren;
    mouseCommand->picker = picker;
    mouseCommand->redCone = redCone;
    mouseCommand->greenCone = greenCone;
    iren->AddObserver(vtkCommand::MouseMoveEvent, mouseCommand);
    iren->Start();
    
    return 0;
}

下面两张图是渲染效果,从两张图上也发现了问题,第一张图灰度图和体渲染和多边形渲染是有遮挡关系的;第二张图明显可以发现从灰度图可以透视到体渲染;可能是VTK中对image图像和体渲染的遮挡关系的bug,之前也发现了在一个平面上纹理映射一张图像,也存在这种透明关系的错误;

猜你喜欢

转载自blog.csdn.net/liushao1031177/article/details/129279609
今日推荐