切换到具有不同数量的标量组件的图像会导致访问冲突

时间:2014-01-31 13:08:01

标签: c++ vtk

迁移到vtk6.1,应用程序中存在最后一个问题。它使用vtk进行体积渲染。应用程序应该能够(在某些用户交互中)在通过传递函数仅渲染每个体素的一个标量分量到渲染多个之间进行切换。从理论上讲,据我所知,vtk应该可以渲染最多4个这样的通道的音量。迁移到vtk 6.1后,代码中存在访问冲突,这在vtk 5.10.1中有效。

这是我能想出的最简单的例子,可以重现问题(MultiChannelTest.cxx):

#include <vtkColorTransferFunction.h>
#include <vtkFixedPointVolumeRayCastMapper.h>
#include <vtkImageData.h>
#include <vtkImageAppendComponents.h>
#include <vtkMetaImageReader.h>
#include <vtkOpenGLRenderer.h>
#include <vtkPiecewiseFunction.h>
#include <vtkProperty2D.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkSmartPointer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVolumeProperty.h>

int main (int, char *[])
{
    vtkSmartPointer<vtkColorTransferFunction> colorTransferFunction =
        vtkSmartPointer<vtkColorTransferFunction>::New();
    colorTransferFunction->AddRGBPoint(0, 0.0, 0.0, 0.0);
    colorTransferFunction->AddRGBPoint(308, 1.0, 1.0, 1.0);
    colorTransferFunction->Build();
    vtkSmartPointer<vtkPiecewiseFunction> piecewiseFunction =
        vtkSmartPointer<vtkPiecewiseFunction>::New();
    piecewiseFunction->AddPoint(0, 0.0);
    piecewiseFunction->AddPoint(308, 1.0);

    vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> fixedPointVolumeRayCastMapper =
        vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New();
    // to disable nagging warning about thread number:
    fixedPointVolumeRayCastMapper->SetNumberOfThreads(1);

    vtkSmartPointer<vtkMetaImageReader> reader =
        vtkSmartPointer<vtkMetaImageReader>::New();
    reader->SetFileName("some-test-volume.mhd");
    reader->Update();
    vtkImageData* imageData = reader->GetOutput();
#if VTK_MAJOR_VERSION <= 5
    fixedPointVolumeRayCastMapper->SetInput(imageData);
#else
    fixedPointVolumeRayCastMapper->SetInputData(imageData);
#endif
    fixedPointVolumeRayCastMapper->Update();

    vtkSmartPointer<vtkVolumeProperty> volumeProperty =
        vtkSmartPointer<vtkVolumeProperty>::New();
    volumeProperty->SetColor(0, colorTransferFunction);
    volumeProperty->SetScalarOpacity(0, piecewiseFunction);

    vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
    volume->SetMapper(fixedPointVolumeRayCastMapper);
    volume->SetProperty(volumeProperty);

    vtkSmartPointer<vtkOpenGLRenderer> renderer =
        vtkSmartPointer<vtkOpenGLRenderer>::New();
    renderer->AddVolume(volume);

    vtkSmartPointer<vtkRenderWindow> renWin =
        vtkSmartPointer<vtkRenderWindow>::New();
    renWin->AddRenderer(renderer);

    renWin->Render(); // when I comment this line out there is no access violation

    // now add multichannel image instead:

    vtkSmartPointer<vtkImageAppendComponents> append =
        vtkSmartPointer<vtkImageAppendComponents>::New();
#if VTK_MAJOR_VERSION <= 5
    append->SetInput(imageData);
    append->AddInput(imageData);
#else
    append->SetInputData(imageData);
    append->AddInputData(imageData);
#endif
    append->Update();

    vtkSmartPointer<vtkImageData> multiChannelImageData =
        vtkSmartPointer<vtkImageData>::New();
    multiChannelImageData->DeepCopy(append->GetOutput());

#if VTK_MAJOR_VERSION <= 5
    fixedPointVolumeRayCastMapper->SetInput(multiChannelImageData);
#else
    fixedPointVolumeRayCastMapper->SetInputData(multiChannelImageData);
#endif

    volumeProperty->SetColor(1, colorTransferFunction);
    volumeProperty->SetScalarOpacity(1, piecewiseFunction);

    volume->SetProperty(volumeProperty);

    volume->Update();
    fixedPointVolumeRayCastMapper->Update();

    renWin->Render(); // <--- The access violation happens in here

    return EXIT_SUCCESS;
}

从我的调试会话中,我可以在vtkVolumeRayCastSpaceLeapingImageFilter第172行告诉vtk失败:

    for ( c = 0; c < nComponents; ++c )
      {
      *(tmpPtr++) = 0xffff;  // Min Scalar  // <-- this is line 172
      *(tmpPtr++) = 0;       // Max Scalar

在那里,它通过tmpPtr引用一些输出图像。它似乎缓存的图像;并且第二次调用Render时,它仍然从第一次渲染运行中获取一个(现在它太小了,因为它实际上需要的空间是第二次组件的第一次运行的两倍),因此tmpPtr超出为图像分配的空间大小,并导致访问冲突。

我完全不知道如何告诉vtk丢弃此缓存,并重新分配它。整个渲染逻辑非常复杂,我还没有真正完全包围它。神经学点似乎是void vtkVolumeRayCastSpaceLeapingImageFilter ::AllocateOutputData(vtkImageData *output, ...方法,它检查范围和组件数量是否已经改变:

if (extent[0] == uExtent[0] && extent[1] == uExtent[1] &&
    extent[2] == uExtent[2] && extent[3] == uExtent[3] &&
    extent[4] == uExtent[4] && extent[5] == uExtent[5] &&
    this->Cache->GetNumberOfScalarComponents() ==
                output->GetNumberOfScalarComponents())
  {

由于某种原因,output->GetNumberOfScalarComponents()仍然返回3(即使将输入图像更改为具有2个标量组件的输入图像,此“空间跳跃过滤器”也需要每个原始图像的每个组件3个组件) (所以它应该实际返回6,这将使它不等于3,然后触发重新分配)。

我需要做些什么才能让vtk使用适当的尺寸重新创建此缓存?即我需要更改什么才能使output->GetNumberOfScalarComponents()第二次返回正确的数字?

不幸的是我还没有确定这个output变量的设置位置(或者忘了设置?),这将是我调试的下一步。它可能是一个vtk bug吗?

这绝对是一个缓存问题而不是多通道图像的一般问题 - 在上面的程序中,当我注释掉对Render的第一次调用时,整个过程没有任何问题(因为{{ 1}}然后返回6,因此分配了更大的缓冲区。)

对于vtk6,这是与上面的代码一起使用的cmake文件:

output->GetNumberOfScalarComponents()

1 个答案:

答案 0 :(得分:0)

我还没有找到关于为什么上述情况正在发生的直接答案,但我现在有一个解决方法:

缓存似乎发生在Mapper或其某些超类或连接类中。重新创建映射器时,不再发生访问冲突。

因此,如果不是将multiChannelImage设置为现有的映射器,而是执行此操作:

    vtkSmartPointer<vtkImageData> multiChannelImageData =
        vtkSmartPointer<vtkImageData>::New();
    multiChannelImageData->DeepCopy(append->GetOutput());
    fixedPointVolumeRayCastMapper =
        vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New();
    // to disable nagging warning about thread number:
    fixedPointVolumeRayCastMapper->SetNumberOfThreads(1);
#if VTK_MAJOR_VERSION <= 5
    fixedPointVolumeRayCastMapper->SetInput(multiChannelImageData);
#else
    fixedPointVolumeRayCastMapper->SetInputData(multiChannelImageData);
#endif
volumeProperty->SetColor(1, colorTransferFunction);
volumeProperty->SetScalarOpacity(1, piecewiseFunction);

volume->SetProperty(volumeProperty);
    volume->SetMapper(fixedPointVolumeRayCastMapper);

    fixedPointVolumeRayCastMapper->Update();

    renWin->Render();

然后不再存在访问冲突。可能不是一个理想的解决方案,但至少它是有效的。