Skip to content

VoxelsOnBoundary

Repository source: VoxelsOnBoundary


Description

This example uses vtkMultiThreshold to extract voxels that are inside an isosurface and on the boundary of the isosurface. The result is a vtkUnstructuredGrid for each set of voxels. Before processing, vtkImageShrink3D reduces the resolution by a factor of 4.

Compare these results with MedicalDemo1 that extracts the surface using vtkFlyingEdges3D or vtkMarchingCubes to extract an interpolated isosurface.

Info

The example uses src/Testing/Data/FullHead.mhd which references src/Testing/Data/FullHead.raw.gz.

Other languages

See (PythonicAPI)

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

VoxelsOnBoundary.cxx

#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkDataSetMapper.h>
#include <vtkImageDataToPointSet.h>
#include <vtkImageShrink3D.h>
#include <vtkMetaImageReader.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkMultiThreshold.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPlane.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkUnstructuredGrid.h>

#include <iostream>
#include <string>
#include <vector>

int main(int argc, char* argv[])
{
  if (argc < 2)
  {
    cout << "Usage: " << argv[0] << " file.mhd e.g. FullHead.mhd" << endl;
    return EXIT_FAILURE;
  }

  vtkNew<vtkMetaImageReader> reader;
  reader->SetFileName(argv[1]);
  reader->Update();

  vtkNew<vtkImageShrink3D> shrink;
  shrink->SetShrinkFactors(4, 4, 4);
  shrink->SetInputConnection(reader->GetOutputPort());
  shrink->Update();

  // Create a PointSet from the ImageData
  vtkNew<vtkImageDataToPointSet> imageDataToPointSet;
  imageDataToPointSet->SetInputConnection(reader->GetOutputPort());
  imageDataToPointSet->SetInputConnection(shrink->GetOutputPort());
  imageDataToPointSet->Update();

  // Extract voxels on the border between the inside and outside.
  vtkNew<vtkMultiThreshold> threshold;
  // Inside points have one or more points above the isosurface
  int insideId = threshold->AddIntervalSet(
      501, 20000, vtkMultiThreshold::CLOSED, vtkMultiThreshold::CLOSED,
      vtkDataObject::FIELD_ASSOCIATION_POINTS, "ImageScalars", 0, 0);
  // Border points have points that straddle the boundary
  int borderId = threshold->AddIntervalSet(
      499.9999, 501.0000, vtkMultiThreshold::OPEN, vtkMultiThreshold::OPEN,
      vtkDataObject::FIELD_ASSOCIATION_POINTS, "ImageScalars", 0, 0);

  threshold->SetInputData(imageDataToPointSet->GetOutput());

  // Select the intervals to be output
  threshold->OutputSet(insideId);
  threshold->OutputSet(borderId);
  threshold->Update();

  // Visualize
  vtkNew<vtkNamedColors> colors;
  // vtkColor3d outsideColor    = colors->GetColor3d("Crimson");
  vtkColor3d insideColor = colors->GetColor3d("Banana");
  vtkColor3d borderColor = colors->GetColor3d("Mint");
  // vtkColor3d surfaceColor    = colors->GetColor3d("Peacock");
  vtkColor3d backgroundColor = colors->GetColor3d("Silver");

  vtkNew<vtkPlane> plane;
  plane->SetOrigin(dynamic_cast<vtkUnstructuredGrid*>(
                       vtkMultiBlockDataSet::SafeDownCast(
                           threshold->GetOutput()->GetBlock(insideId))
                           ->GetBlock(0))
                       ->GetCenter());
  plane->SetNormal(1.0, -1.0, -1.0);

  // Inside
  vtkNew<vtkDataSetMapper> insideMapper;
  insideMapper->SetInputData(dynamic_cast<vtkUnstructuredGrid*>(
      vtkMultiBlockDataSet::SafeDownCast(
          threshold->GetOutput()->GetBlock(insideId))
          ->GetBlock(0)));
  insideMapper->ScalarVisibilityOff();

  vtkNew<vtkActor> insideActor;
  insideActor->SetMapper(insideMapper);
  insideActor->GetProperty()->SetDiffuseColor(insideColor.GetData());
  insideActor->GetProperty()->SetSpecular(.6);
  insideActor->GetProperty()->SetSpecularPower(30);
  insideActor->GetProperty()->EdgeVisibilityOn();

  // Border
  vtkNew<vtkDataSetMapper> borderMapper;
  borderMapper->SetInputData(dynamic_cast<vtkUnstructuredGrid*>(
      vtkMultiBlockDataSet::SafeDownCast(
          threshold->GetOutput()->GetBlock(borderId))
          ->GetBlock(0)));
  borderMapper->ScalarVisibilityOff();

  vtkNew<vtkActor> borderActor;
  borderActor->SetMapper(borderMapper);
  borderActor->GetProperty()->SetDiffuseColor(borderColor.GetData());
  borderActor->GetProperty()->SetSpecular(.6);
  borderActor->GetProperty()->SetSpecularPower(30);
  borderActor->GetProperty()->EdgeVisibilityOn();

  vtkNew<vtkRenderer> renderer;
  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->AddRenderer(renderer);
  renderWindow->SetSize(640, 480);

  vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  renderWindowInteractor->SetRenderWindow(renderWindow);

  renderer->SetBackground(backgroundColor.GetData());
  renderer->UseHiddenLineRemovalOn();

  renderer->AddActor(insideActor);
  renderer->AddActor(borderActor);

  renderWindow->SetWindowName("CellsOnBoundary");
  renderWindow->Render();

  // Setup a good view
  renderer->GetActiveCamera()->SetViewUp(0, 0, -1);
  renderer->GetActiveCamera()->SetPosition(0, -1, 0);
  renderer->GetActiveCamera()->SetFocalPoint(0, 0, 0);
  renderer->GetActiveCamera()->Azimuth(30.0);
  renderer->GetActiveCamera()->Elevation(30.0);
  renderer->ResetCamera();
  renderer->GetActiveCamera()->Dolly(1.5);

  renderWindowInteractor->Start();

  return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)

project(VoxelsOnBoundary)

find_package(VTK COMPONENTS 
  CommonColor
  CommonCore
  CommonDataModel
  FiltersGeneral
  IOImage
  ImagingCore
  InteractionStyle
  RenderingContextOpenGL2
  RenderingCore
  RenderingFreeType
  RenderingGL2PSOpenGL2
  RenderingOpenGL2
)

if (NOT VTK_FOUND)
  message(FATAL_ERROR "VoxelsOnBoundary: Unable to find the VTK build folder.")
endif()

# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
add_executable(VoxelsOnBoundary MACOSX_BUNDLE VoxelsOnBoundary.cxx )
  target_link_libraries(VoxelsOnBoundary PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
  TARGETS VoxelsOnBoundary
  MODULES ${VTK_LIBRARIES}
)

Download and Build VoxelsOnBoundary

Click here to download VoxelsOnBoundary and its CMakeLists.txt file. Once the tarball VoxelsOnBoundary.tar has been downloaded and extracted,

cd VoxelsOnBoundary/build

If VTK is installed:

cmake ..

If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:

cmake -DVTK_DIR:PATH=/home/me/vtk_build ..

Build the project:

make

and run it:

./VoxelsOnBoundary

WINDOWS USERS

Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.