Lecture 3

Introduction to VTK

(includes text and images from the 3rd edition of the VTK User's Guide)



First lets talk about Project 1



VTK
- what is it, what does it do, and why are we using it.

There are quite a few packages that can do visualization (scientific, information, medical, etc) out there. VTK is available for free for all major computer platforms, and there are a lot of vtk examples available on the web, The basic visualization concepts remain the same though the terminology and capabilities may change from package to package.



vtk can be obtained from http://www.vtk.org/

After you grab the source you should build yourself a copy. In the past we have found that doing an out-of-source build is better ...

install cmake if you don't already have it (you can use macports to install it under os-x)

mkdir VTK-build
cd VTK-build
ccmake ../VTK


BUILD_EXAMPLES ON, VTK_WRAP_TCL ON
and maybe VTK_WRAP_JAVA and VTK_WRAP_PYTHON if you plan to use either of those langauges.

and then you should have a makefile that you can make install - this might take a while

and then you may need to set your paths to make sure that the VTK-build/bin directory is included, e.g under OS-X I need to make sure that DYLD_LIBRARY_PATH includes /usr/local/VTK-build/bin



You can use vtk with C++, Tcl, Python, and Java.

VTK comes with an example directory and one of the simplest ways to test your vtk install is to try running the Tcl examples. For example you can cd to VTK/Examples/Medical/tcl and then vtk Medical1.tcl

To compile the cxx examples you should be able to go into the cxx directory and type "cmake ." and then "make". 

Then you can run medical example 1 in VTK/Examples/Medical/Cxx with ./Medical1 /usr/local/vtkData/DATA/headsq/quarter

Medical2, 3, and 4 show progressively more complicated visualizations

  // The following reader is used to read a series of 2D slices (images)
  // that compose the volume. The slice dimensions are set, and the
:/*=========================================================================

  Program:   Visualization Toolkit
  Module:    Medical1.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
//
// This example reads a volume dataset, extracts an isosurface that
// represents the skin and displays it.
//

#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVolume16Reader.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkOutlineFilter.h>
#include <vtkCamera.h>
#include <vtkProperty.h>
#include <vtkPolyDataNormals.h>
#include <vtkContourFilter.h>
#include <vtkSmartPointer.h>

int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    cout << "Usage: " << argv[0] << " DATADIR/headsq/quarter" << endl;
    return EXIT_FAILURE;
    }

  // Create the renderer, the render window, and the interactor. The renderer
  // draws into the render window, the interactor enables mouse- and
  // keyboard-based interaction with the data within the render window.
  //
  vtkSmartPointer<vtkRenderer> aRenderer =
    vtkSmartPointer<vtkRenderer>::New();
  vtkSmartPointer<vtkRenderWindow> renWin =
    vtkSmartPointer<vtkRenderWindow>::New();
  renWin->AddRenderer(aRenderer);

  vtkSmartPointer<vtkRenderWindowInteractor> iren =
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
  iren->SetRenderWindow(renWin);

  // The following reader is used to read a series of 2D slices (images)
  // that compose the volume. The slice dimensions are set, and the
  // pixel spacing. The data Endianness must also be specified. The reader
  // uses the FilePrefix in combination with the slice number to construct
  // filenames using the format FilePrefix.%d. (In this case the FilePrefix
  // is the root name of the file: quarter.)
  vtkSmartPointer<vtkVolume16Reader> v16 =
    vtkSmartPointer<vtkVolume16Reader>::New();
  v16->SetDataDimensions (64,64);
  v16->SetImageRange (1,93);
  v16->SetDataByteOrderToLittleEndian();
  v16->SetFilePrefix (argv[1]);
  v16->SetDataSpacing (3.2, 3.2, 1.5);

  // An isosurface, or contour value of 500 is known to correspond to the
  // skin of the patient. Once generated, a vtkPolyDataNormals filter is
  // is used to create normals for smooth surface shading during rendering.
  vtkSmartPointer<vtkContourFilter> skinExtractor =
    vtkSmartPointer<vtkContourFilter>::New();
  skinExtractor->SetInputConnection(v16->GetOutputPort());
  skinExtractor->SetValue(0, 500);

  vtkSmartPointer<vtkPolyDataNormals> skinNormals =
    vtkSmartPointer<vtkPolyDataNormals>::New();
  skinNormals->SetInputConnection(skinExtractor->GetOutputPort());
  skinNormals->SetFeatureAngle(60.0);

  vtkSmartPointer<vtkPolyDataMapper> skinMapper =
    vtkSmartPointer<vtkPolyDataMapper>::New();
  skinMapper->SetInputConnection(skinNormals->GetOutputPort());
  skinMapper->ScalarVisibilityOff();

  vtkSmartPointer<vtkActor> skin =
    vtkSmartPointer<vtkActor>::New();
  skin->SetMapper(skinMapper);

  // An outline provides context around the data.
  //
  vtkSmartPointer<vtkOutlineFilter> outlineData =
    vtkSmartPointer<vtkOutlineFilter>::New();
  outlineData->SetInputConnection(v16->GetOutputPort());

  vtkSmartPointer<vtkPolyDataMapper> mapOutline =
    vtkSmartPointer<vtkPolyDataMapper>::New();
  mapOutline->SetInputConnection(outlineData->GetOutputPort());

  vtkSmartPointer<vtkActor> outline =
    vtkSmartPointer<vtkActor>::New();
  outline->SetMapper(mapOutline);
  outline->GetProperty()->SetColor(0,0,0);

  // It is convenient to create an initial view of the data. The FocalPoint
  // and Position form a vector direction. Later on (ResetCamera() method)
  // this vector is used to position the camera to look at the data in
  // this direction.
  vtkSmartPointer<vtkCamera> aCamera =
    vtkSmartPointer<vtkCamera>::New();
  aCamera->SetViewUp (0, 0, -1);
  aCamera->SetPosition (0, 1, 0);
  aCamera->SetFocalPoint (0, 0, 0);
  aCamera->ComputeViewPlaneNormal();
  aCamera->Azimuth(30.0);
  aCamera->Elevation(30.0);

  // Actors are added to the renderer. An initial camera view is created.
  // The Dolly() method moves the camera towards the FocalPoint,
  // thereby enlarging the image.
  aRenderer->AddActor(outline);
  aRenderer->AddActor(skin);
  aRenderer->SetActiveCamera(aCamera);
  aRenderer->ResetCamera ();
  aCamera->Dolly(1.5);

  // Set a background color for the renderer and set the size of the
  // render window (expressed in pixels).
  aRenderer->SetBackground(.2, .3, .4);
  renWin->SetSize(640, 480);

  // Note that when camera movement occurs (as it does in the Dolly()
  // method), the clipping planes often need adjusting. Clipping planes
  // consist of two planes: near and far along the view direction. The
  // near plane clips out objects in front of the plane; the far plane
  // clips out objects behind the plane. This way only what is drawn
  // between the planes is actually rendered.
  aRenderer->ResetCameraClippingRange ();

  // Initialize the event loop and then start it.
  iren->Initialize();
  iren->Start();

  return EXIT_SUCCESS;
}


To make a new C project (adapted from https://visualization.hpc.mil/wiki/VTK_in_CPP)

1. Create a file called CMakeLists.txt in the source directory (in this case next to Rendering, Medical, Modelling), containing the following, where Test is replaced with your project name.


cmake_minimum_required(VERSION 2.6)
PROJECT(Test)
 
FIND_PACKAGE(VTK REQUIRED)
INCLUDE(${VTK_USE_FILE})
 
ADD_EXECUTABLE(Test Test.cpp)
TARGET_LINK_LIBRARIES(Test vtkHybrid)

e.g.

cmake_minimum_required(VERSION 2.6)
 
PROJECT(Cone)
 
FIND_PACKAGE(VTK REQUIRED)
INCLUDE(${VTK_USE_FILE})
 
ADD_EXECUTABLE(Cone Cone.cxx)
TARGET_LINK_LIBRARIES(Cone vtkHybrid)

2.  Create a sample cpp file, or copy one form the examples directory like Cone.cxx

#include <vtkConeSource.h>
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
 
int main(int, char *[])
{
  //Create a cone
  vtkSmartPointer<vtkConeSource> coneSource =
    vtkSmartPointer<vtkConeSource>::New();
  coneSource->Update();
 
  //Create a mapper and actor
  vtkSmartPointer<vtkPolyDataMapper> mapper =
    vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper->SetInputConnection(coneSource->GetOutputPort());
 
  vtkSmartPointer<vtkActor> actor =
    vtkSmartPointer<vtkActor>::New();
  actor->SetMapper(mapper);
 
  //Create a renderer, render window, and interactor
  vtkSmartPointer<vtkRenderer> renderer =
    vtkSmartPointer<vtkRenderer>::New();
  vtkSmartPointer<vtkRenderWindow> renderWindow =
    vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->AddRenderer(renderer);
  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
  renderWindowInteractor->SetRenderWindow(renderWindow);
 
  //Add the actors to the scene
  renderer->AddActor(actor);
  renderer->SetBackground(.3, .2, .1); // Background color brown
 
  //Render and interact
  renderWindow->Render();
  renderWindowInteractor->Start();
 
  return EXIT_SUCCESS;
}

3. type "cmake ." to create the Makefile.

4. "make" to build the application

5. run the application (./Test) and you should see a grey cone on a brown background

6. make changes as needed



another useful piece of code for dealing with image data, and in particular doing segmentation and registration, is itk at http://www.itk.org/

which also uses cmake to build itself

similarly you can create ITK-build and do a ccmake ../InsightToolkit-3.20.0 and then make. It will be somewhat faster than building vtk

there is a nice introduction to itk from 2007 at http://www.itk.org/ITK/help/tutorials.html


Visualization Pipeline/Network (Chap 4 vtk book)

In general a visualization tool accesses an external dataset, maps it into an internal representation, processes the data, and generates images from the processed data.


Pretty much all of these packages have a pipeline architecture made up of Data Objects and Process Objects

Data Objects - information plus methods to create, access, delete information. The same data may be represented by different data objects depending on storage size, access times, ase of conversion to graphical primitives.

Process Objects - Operate on input data to generate output data


It takes time to set up a pipeline, and its only valuable if the pipeline will be run many times, typically with different inputs or parameters.

Pipelines can be demand driven (like VTK) or event driven

Demand Driven

Event Driven


Since these pipelines involve networks of objects, which may be running on separate machines, there is a need for communication and synchronization and the main two options are Explicit Execution and Implicit Execution

Explicit Execution

Implicit Execution


Memory Models


So, lets get a little more specific with an example of how we can use the vtk pipeline to go from data to a visualization.

Here is a brief demonstration of ParaView (which is built on top of vtk and available from www.paraview.org) with the Visible Woman dataset

From the virual human web page (http://www.nlm.nih.gov/research/visible/) "The Visible Human Male data set consists of MRI, CT and anatomical images. Axial MRI images of the head and neck and longitudinal sections of the rest of the body were obtained at 4 mm intervals. The MRI images are 256 pixel by 256 pixel resolution. Each pixel having 12 bits of grey tone resolution. The CT data consists of axial CT scans of the entire body taken at 1 mm intervals at a resolution of 512 pixels by 512 pixels with each pixel made up of 12 bits of grey tone. The approximately 7.5 megabyte axial anatomical images are 2048 pixels by 1216 pixels, with each pixel defined by 24 bits of color. The anatomical cross-sections are at 1 mm intervals to coincide with the CT axial images. There are 1871 cross-sections for both CT and anatomy. The complete male data set, 15 gigabytes in size, was made publicly available in November, 1994.

The Visible Human Female data set, released in November, 1995, has the same characteristics as the The Visible Human Male with one exception, the axial anatomical images were obtained at 0.33 mm intervals. This resulted in 5,189 anatomical images, and a data set of about 40 gigabytes. Spacing in the "Z" direction was reduced to 0.33mm in order to match the 0.33mm pixel sizing in the "X-Y" plane, thus enabling developers interested in three-dimensional reconstructions to work with cubic voxels."

(and let us remember that in 1995 personal computers were running at 200Mhz, with 16MB of RAM and 1 GB hard drives. Now the visible woman dataset fits on an iPod)

Here are some small jpeg images of a very few of the slices of the visible woman.

and here is ParaView viewing the 75MB version of the Visible Woman dataset. This version of the dataset is made up of 577 slices, 1 slide every 3mm, where each slice is 256 x 256 16-bit values. Once the data is loaded in we can set up two contours - one for the skin and one for the bones.

In paraview 3.8.1 you can read in the visible woman data as raw data then set:

and then create two contours of the Female.raw data - one transparent aqua one at 850 for skin, and one opaque white one at 1200 for bone, and you will see something similar to this.

Paraview visualization of Visible Female

If you think polygons are a little too 20th century then paraview can also do some simple volume rendering
Paraview visualization of Visible Female with volume rendering


Discrete nature of data (Chap 5 vtk)

(possibly) continuous data represented by discrete sampling

Interpolation

A familiar example is a weather map showing the current temperature across the state or country.

Surface temperature exists at every point in the state, so its a continuous data value, but the data is only sampled at certain scattered stations which is then interpolated to form a continuous representation. On the website given below you can click on the map to gain access to the data files and to see how the data is interpolated across the state.

 http://www.sws.uiuc.edu/warm/icnstationmap.asp

Illinois interpolated temperatures

Illinois interpolated precipitation

Here is another nice example of interpolating winds from the San Francisco Bay area

http://sfports.wr.usgs.gov/wind

In particular, click on the 'Observed Wind over S.F. Bay' link to see the observed data, then go back to the modelled data. Also click on the streakline version to see the modelled data in motion.


Structured or Unstructured Data


Dimension (number of independent variables)


Abstract Dataset



Some More examples with ParaView

canyon_elev.raw (which can be found at ftp.evl.uic.edu in pub/INcoming/andy) contains a height map of the grand canyon as a 1024x512 greyscale image.

ParaView can load this image by setting:
data type to 'unsigned char'
File Dimensionality to 2,
extents as 0-1023, 0-511

This will bring up a colour image. You can use the information tab to bring up information on the dataset. In this case that the values in the image range from 68 to 242.

Then you can use the Contour filter to define a series of contours. As the scalar values in the file range from 68 to 242 you could set up a set of separate contours at 125, 150, and 175 for example. These contours would initially all be in the same plane, but you can also translate them in Z.

PAraview visualization of grant canyon height map as contours


You can also take the data and extract a surface from it. Once you have the surface you can tetrahedralize it and then apply warp (scalar) to convert it into 3D and view it as a surface.

Paraview visualization of Grand Canyon height map as surface


A dataset that comes with vtk is VTKData/Data is ironProt.vtk. Since it is a .vtk file, the header of this file contains the information that vtk needs to properly load the data in. Like the visible woman dataset we can use the contour tool to generate contours. In the image below there is a contour at 50 shown as wireframe, another contour at 200 shown as a surface, and a slice showing the data values in a plane (you need to Map Scalars in the Display tab for the contents of the plane to show up).

PAraview visualiation or iron protein

Paraview is also useful in learning which filters accept which kinds of data as input, what kinds of visualizations are possible, and what kinds of interactors are built in to vtk,


Coming Next Time

VTK -  A Programmer's Perspective

Dennis created a page for setting up VTK / C++ / QT on Snow Leopard

Paul found this page on setting up vtk in visual studio 2008


last revision 1/19/11 - updated the paraview snapshots and added the link to compiling vtk in VS 2008