AnatomicalOrientation
VTKExamples/Cxx/VisualizationAlgorithms/AnatomicalOrientation
Description¶
This depicts a human figure transected by the three commonly used anatomical planes:
- Sagittal plane – is perpendicular to the ground divides the body into a left section and a right section.
- Coronal plane – is perpendicular to the ground and divides the body into a front (anterior) section and back (posterior) section.
- Transverse plane – or axial plane is parallel to the ground divides the body into an upper (superior) section and a bottom (inferior) section.
Four annotated cube actors are also provided demonstrating different coordinate systems. The annotations on the faces of the cube actors are:
- Sagittal plane
- L - left.
- R - right.
- Coronal plane
- A - anterior.
- P - posterior.
- Transverse plane
- S - superior.
- I - inferior.
The annotated cube actors demonstrate the various coordinate systems: - The anatomical coordinate system forming a 3D basis is defined along the anatomical axes of anterior-posterior, inferior-superior, and left-right. These are the positive directions. In a Cartesian system this is RPS (Right, Posterior, Superior). The top-left annotated cube actor shows this basis, this is a left-handed system. - RAS (Right, Anterior, Superior), left-right, posterior-anterior, and inferior-superior. This is the usual right-handed system used by VTK and Slicer. The bottom left annotated cube actor shows this basis. - LPS (Left, Posterior, Superior), right-left, anterior-posterior, and inferior-superior. This is used in DICOM images and by the ITK toolkit. The bottom right annotated cube actor shows this basis. - The upper right cube actor has no axes and simply shows the planes.
RPS is a left-handed system whilst RAS and LPS are right-handed.
Note that the text for the planes is carefully placed to avoid obstructing the figure and it also sits slightly above the plane.
Code¶
AnatomicalOrientation.cxx
/* * The human data file is taken from: * https://github.com/Slicer/Slicer/blob/master/Base/Logic/Resources/OrientationMarkers/Human.vtp * Thanks to the Slicer people for providing this. * */ #include <vtkAnnotatedCubeActor.h> #include <vtkAxesActor.h> #include <vtkCamera.h> #include <vtkCaptionActor2D.h> #include <vtkNamedColors.h> #include <vtkOrientationMarkerWidget.h> #include <vtkPlaneSource.h> #include <vtkPolyDataMapper.h> #include <vtkPropAssembly.h> #include <vtkProperty.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRenderer.h> #include <vtkSmartPointer.h> #include <vtkTextProperty.h> #include <vtkTransform.h> #include <vtkTransformPolyDataFilter.h> #include <vtkVectorText.h> #include <vtkXMLPolyDataReader.h> #include <algorithm> #include <array> #include <iomanip> #include <iostream> #include <vector> namespace { /** * @param scale Sets the scale and direction of the axes. * @param xyzLabels Labels for the axes. * @return The axes actor. */ vtkSmartPointer<vtkAxesActor> MakeAxesActor( std::array<double, 3>& scale, std::array<std::string, 3>& xyzLabels); /** * @param colors Used to set the colors of the cube faces. * @return The annotated cube actor. */ vtkSmartPointer<vtkAnnotatedCubeActor> MakeAnnotatedCubeActor( vtkNamedColors* colors); /** * @param scale Sets the scale and direction of the axes. * @param xyzLabels Labels for the axes. * @param colors Used to set the colors of the cube faces. * @return The combined axes and annotated cube prop. */ vtkSmartPointer<vtkPropAssembly> MakeCubeActor( std::array<double, 3>& scale, std::array<std::string, 3>& xyzLabels, vtkNamedColors* colors); /** * Make the traverse, coronal and saggital planes. * * @param colors Used to set the color of the planes. * @return The planes actors. */ std::vector<vtkSmartPointer<vtkActor>> MakePlanesActors(vtkNamedColors* colors); /** * Generate text to place on the planes. * Careful placement is needed here. * @return The text actors. */ std::vector<vtkSmartPointer<vtkActor>> AddTextToPlanes(); } int main(int argc, char* argv[]) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " fileName" << std::endl; std::cout << "where: fileName is Human.vtp." << std::endl; return EXIT_FAILURE; } std::string fileName = argv[1]; vtkSmartPointer<vtkNamedColors> colors = vtkSmartPointer<vtkNamedColors>::New(); // Create the rendering window, renderer, and interactive renderer. vtkSmartPointer<vtkRenderer> ren = vtkSmartPointer<vtkRenderer>::New(); vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New(); renWin->SetSize(780, 780); renWin->AddRenderer(ren); vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New(); iren->SetRenderWindow(renWin); // Make an annotated cube actor with axes and then add it into an orientation // marker widget. // Three of these need to be made. // Right Posterior Superior std::array<std::string, 3> xyzLabels{{"X", "Y", "Z"}}; std::array<double, 3> scale{{1.5, -1.5, 1.5}}; vtkSmartPointer<vtkPropAssembly> axes = MakeCubeActor(scale, xyzLabels, colors); vtkSmartPointer<vtkOrientationMarkerWidget> om = vtkSmartPointer<vtkOrientationMarkerWidget>::New(); om->SetOrientationMarker(axes); // Position upper left in the viewport. om->SetViewport(0.0, 0.8, 0.2, 1.0); om->SetInteractor(iren); om->EnabledOn(); om->InteractiveOn(); // Right, Anterior, Superior. std::array<double, 3> scale1{{1.5, 1.5, 1.5}}; vtkSmartPointer<vtkPropAssembly> axes1 = MakeCubeActor(scale1, xyzLabels, colors); vtkSmartPointer<vtkOrientationMarkerWidget> om1 = vtkSmartPointer<vtkOrientationMarkerWidget>::New(); om1->SetOrientationMarker(axes1); // Position lower left in the viewport. om1->SetViewport(0, 0, 0.2, 0.2); om1->SetInteractor(iren); om1->EnabledOn(); om1->InteractiveOn(); // Left, Posterior, Superior. std::array<double, 3> scale2{{-1.5, -1.5, 1.5}}; vtkSmartPointer<vtkPropAssembly> axes2 = MakeCubeActor(scale2, xyzLabels, colors); vtkSmartPointer<vtkOrientationMarkerWidget> om2 = vtkSmartPointer<vtkOrientationMarkerWidget>::New(); om2->SetOrientationMarker(axes2); // Position lower right in the viewport. om2->SetViewport(0.8, 0, 1.0, 0.2); om2->SetInteractor(iren); om2->EnabledOn(); om2->InteractiveOn(); // Finally create an annotated cube actor adding it into an orientation marker // widget. vtkSmartPointer<vtkAnnotatedCubeActor> axes3 = MakeAnnotatedCubeActor(colors); vtkSmartPointer<vtkOrientationMarkerWidget> om3 = vtkSmartPointer<vtkOrientationMarkerWidget>::New(); om3->SetOrientationMarker(axes3); // Position upper right in the viewport. om3->SetViewport(0.8, 0.8, 1.0, 1.0); om3->SetInteractor(iren); om3->EnabledOn(); om3->InteractiveOn(); // Read in the model. vtkSmartPointer<vtkXMLPolyDataReader> reader = vtkSmartPointer<vtkXMLPolyDataReader>::New(); reader->SetFileName(fileName.c_str()); reader->Update(); vtkSmartPointer<vtkPolyDataMapper> humanMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); humanMapper->SetInputConnection(reader->GetOutputPort()); humanMapper->SetScalarModeToUsePointFieldData(); humanMapper->SelectColorArray("Color"); humanMapper->SetColorModeToDirectScalars(); vtkSmartPointer<vtkActor> humanActor = vtkSmartPointer<vtkActor>::New(); humanActor->SetMapper(humanMapper); std::vector<double> bounds(6, 0); humanActor->GetBounds(&bounds[0]); std::vector<double>::iterator maxElt = std::max_element(bounds.begin(), bounds.end()); // Scale the actor humanActor->SetScale(1.0 / *maxElt); ren->AddActor(humanActor); // Make the planes. std::vector<vtkSmartPointer<vtkActor>> actors = MakePlanesActors(colors); for (auto actor : actors) { ren->AddViewProp(actor); } // Label them. std::vector<vtkSmartPointer<vtkActor>> textActors = AddTextToPlanes(); for (auto actor : textActors) { ren->AddViewProp(actor); } // Interact ren->SetBackground2(colors->GetColor3d("OldLace").GetData()); ren->SetBackground(colors->GetColor3d("MistyRose").GetData()); ren->GradientBackgroundOn(); ren->ResetCamera(); ren->GetActiveCamera()->Zoom(1.6); ren->GetActiveCamera()->SetPosition(-2.3, 4.1, 4.2); ren->GetActiveCamera()->SetViewUp(0.0, 0.0, 1.0); ren->ResetCameraClippingRange(); renWin->Render(); // Call SetWindowName after renWin.Render() is called. renWin->SetWindowName("Anatomical Orientation"); iren->Initialize(); iren->Start(); return EXIT_SUCCESS; } namespace { vtkSmartPointer<vtkAxesActor> MakeAxesActor( std::array<double, 3>& scale, std::array<std::string, 3>& xyzLabels) { vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New(); axes->SetScale(scale[0], scale[1], scale[2]); axes->SetShaftTypeToCylinder(); axes->SetXAxisLabelText(xyzLabels[0].c_str()); axes->SetYAxisLabelText(xyzLabels[1].c_str()); axes->SetZAxisLabelText(xyzLabels[2].c_str()); axes->SetCylinderRadius(0.5 * axes->GetCylinderRadius()); axes->SetConeRadius(1.025 * axes->GetConeRadius()); axes->SetSphereRadius(1.5 * axes->GetSphereRadius()); vtkTextProperty* tprop = axes->GetXAxisCaptionActor2D()->GetCaptionTextProperty(); tprop->ItalicOn(); tprop->ShadowOn(); tprop->SetFontFamilyToTimes(); // Use the same text properties on the other two axes. axes->GetYAxisCaptionActor2D()->GetCaptionTextProperty()->ShallowCopy(tprop); axes->GetZAxisCaptionActor2D()->GetCaptionTextProperty()->ShallowCopy(tprop); return axes; } vtkSmartPointer<vtkAnnotatedCubeActor> MakeAnnotatedCubeActor( vtkNamedColors* colors) { // A cube with labeled faces. vtkSmartPointer<vtkAnnotatedCubeActor> cube = vtkSmartPointer<vtkAnnotatedCubeActor>::New(); cube->SetXPlusFaceText("R"); // Right cube->SetXMinusFaceText("L"); // Left cube->SetYPlusFaceText("A"); // Anterior cube->SetYMinusFaceText("P"); // Posterior cube->SetZPlusFaceText("S"); // Superior/Cranial cube->SetZMinusFaceText("I"); // Inferior/Caudal cube->SetFaceTextScale(0.5); cube->GetCubeProperty()->SetColor(colors->GetColor3d("Gainsboro").GetData()); cube->GetTextEdgesProperty()->SetColor( colors->GetColor3d("LightSlateGray").GetData()); // Change the vector text colors. cube->GetXPlusFaceProperty()->SetColor( colors->GetColor3d("Tomato").GetData()); cube->GetXMinusFaceProperty()->SetColor( colors->GetColor3d("Tomato").GetData()); cube->GetYPlusFaceProperty()->SetColor( colors->GetColor3d("DeepSkyBlue").GetData()); cube->GetYMinusFaceProperty()->SetColor( colors->GetColor3d("DeepSkyBlue").GetData()); cube->GetZPlusFaceProperty()->SetColor( colors->GetColor3d("SeaGreen").GetData()); cube->GetZMinusFaceProperty()->SetColor( colors->GetColor3d("SeaGreen").GetData()); return cube; } vtkSmartPointer<vtkPropAssembly> MakeCubeActor( std::array<double, 3>& scale, std::array<std::string, 3>& xyzLabels, vtkNamedColors* colors) { // We are combining a vtk.vtkAxesActor and a vtk.vtkAnnotatedCubeActor // into a vtk.vtkPropAssembly vtkSmartPointer<vtkAnnotatedCubeActor> cube = MakeAnnotatedCubeActor(colors); vtkSmartPointer<vtkAxesActor> axes = MakeAxesActor(scale, xyzLabels); // Combine orientation markers into one with an assembly. vtkSmartPointer<vtkPropAssembly> assembly = vtkSmartPointer<vtkPropAssembly>::New(); assembly->AddPart(axes); assembly->AddPart(cube); return assembly; } vtkSmartPointer<vtkTransformPolyDataFilter> MakePlane( std::array<int, 2>& resolution, std::array<double, 3>& origin, std::array<double, 3>& point1, std::array<double, 3>& point2, std::array<double, 4>& wxyz, std::array<double, 3>& translate) { vtkSmartPointer<vtkPlaneSource> plane = vtkSmartPointer<vtkPlaneSource>::New(); plane->SetResolution(resolution[0], resolution[1]); plane->SetOrigin(origin.data()); plane->SetPoint1(point1.data()); plane->SetPoint2(point2.data()); vtkSmartPointer<vtkTransform> trnf = vtkSmartPointer<vtkTransform>::New(); trnf->RotateWXYZ(wxyz[0], wxyz[1], wxyz[2], wxyz[3]); trnf->Translate(translate.data()); vtkSmartPointer<vtkTransformPolyDataFilter> tpdPlane = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); tpdPlane->SetTransform(trnf); tpdPlane->SetInputConnection(plane->GetOutputPort()); return tpdPlane; } std::vector<vtkSmartPointer<vtkActor>> MakePlanesActors(vtkNamedColors* colors) { std::vector<vtkSmartPointer<vtkTransformPolyDataFilter>> planes; std::vector<vtkSmartPointer<vtkPolyDataMapper>> mappers; std::vector<vtkSmartPointer<vtkActor>> actors; // Parameters for a plane lying in the x-y plane. std::array<int, 2> resolution{{10, 10}}; std::array<double, 3> origin{{0.0, 0.0, 0.0}}; std::array<double, 3> point1{{1, 0, 0}}; std::array<double, 3> point2{{0, 1, 0}}; std::array<double, 4> wxyz0{{0, 0, 0, 0}}; std::array<double, 3> translate{{-0.5, -0.5, 0}}; std::array<double, 4> wxyz1{{-90, 1, 0, 0}}; std::array<double, 4> wxyz2{{-90, 0, 1, 0}}; planes.push_back(MakePlane(resolution, origin, point1, point2, wxyz0, translate)); // x-y plane planes.push_back(MakePlane(resolution, origin, point1, point2, wxyz1, translate)); // x-z plane planes.push_back(MakePlane(resolution, origin, point1, point2, wxyz2, translate)); // y-z plane for (size_t i = 0; i < planes.size(); ++i) { mappers.push_back(vtkSmartPointer<vtkPolyDataMapper>::New()); mappers[i]->SetInputConnection(planes[i]->GetOutputPort()); actors.push_back(vtkSmartPointer<vtkActor>::New()); actors[i]->SetMapper(mappers[i]); } actors[0]->GetProperty()->SetColor( colors->GetColor3d("SeaGreen").GetData()); // Transverse plane actors[1]->GetProperty()->SetColor( colors->GetColor3d("DeepSkyBlue").GetData()); // Coronal plane actors[2]->GetProperty()->SetColor( colors->GetColor3d("Tomato").GetData()); // Saggital plane return actors; } std::vector<vtkSmartPointer<vtkActor>> AddTextToPlanes() { std::vector<vtkSmartPointer<vtkActor>> textActors; std::array<double, 3> scale{{0.04, 0.04, 0.04}}; vtkSmartPointer<vtkVectorText> text1 = vtkSmartPointer<vtkVectorText>::New(); text1->SetText("Transverse\nPlane\n\nSuperior\nCranial"); vtkSmartPointer<vtkTransform> trnf1 = vtkSmartPointer<vtkTransform>::New(); trnf1->RotateZ(-90); vtkSmartPointer<vtkTransformPolyDataFilter> tpdPlane1 = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); tpdPlane1->SetTransform(trnf1); tpdPlane1->SetInputConnection(text1->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> textMapper1 = vtkSmartPointer<vtkPolyDataMapper>::New(); textMapper1->SetInputConnection(tpdPlane1->GetOutputPort()); vtkSmartPointer<vtkActor> textActor1 = vtkSmartPointer<vtkActor>::New(); textActor1->SetMapper(textMapper1); textActor1->SetScale(scale.data()); textActor1->AddPosition(0.4, 0.49, 0.01); textActors.push_back(textActor1); vtkSmartPointer<vtkVectorText> text2 = vtkSmartPointer<vtkVectorText>::New(); text2->SetText("Transverse\nPlane\n\nInferior\n(Caudal)"); vtkSmartPointer<vtkTransform> trnf2 = vtkSmartPointer<vtkTransform>::New(); trnf2->RotateZ(270); trnf2->RotateWXYZ(180, 0, 1, 0); vtkSmartPointer<vtkTransformPolyDataFilter> tpdPlane2 = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); tpdPlane2->SetTransform(trnf2); tpdPlane2->SetInputConnection(text2->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> textMapper2 = vtkSmartPointer<vtkPolyDataMapper>::New(); textMapper2->SetInputConnection(tpdPlane2->GetOutputPort()); vtkSmartPointer<vtkActor> textActor2 = vtkSmartPointer<vtkActor>::New(); textActor2->SetMapper(textMapper2); textActor2->SetScale(scale.data()); textActor2->AddPosition(0.4, -0.49, -0.01); textActors.push_back(textActor2); vtkSmartPointer<vtkVectorText> text3 = vtkSmartPointer<vtkVectorText>::New(); text3->SetText("Sagittal\nPlane\n\nLeft"); vtkSmartPointer<vtkTransform> trnf3 = vtkSmartPointer<vtkTransform>::New(); trnf3->RotateX(90); trnf3->RotateWXYZ(-90, 0, 1, 0); vtkSmartPointer<vtkTransformPolyDataFilter> tpdPlane3 = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); tpdPlane3->SetTransform(trnf3); tpdPlane3->SetInputConnection(text3->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> textMapper3 = vtkSmartPointer<vtkPolyDataMapper>::New(); textMapper3->SetInputConnection(tpdPlane3->GetOutputPort()); vtkSmartPointer<vtkActor> textActor3 = vtkSmartPointer<vtkActor>::New(); textActor3->SetMapper(textMapper3); textActor3->SetScale(scale.data()); textActor3->AddPosition(-0.01, 0.49, 0.4); textActors.push_back(textActor3); vtkSmartPointer<vtkVectorText> text4 = vtkSmartPointer<vtkVectorText>::New(); text4->SetText("Sagittal\nPlane\n\nRight"); vtkSmartPointer<vtkTransform> trnf4 = vtkSmartPointer<vtkTransform>::New(); trnf4->RotateX(90); trnf4->RotateWXYZ(-270, 0, 1, 0); vtkSmartPointer<vtkTransformPolyDataFilter> tpdPlane4 = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); tpdPlane4->SetTransform(trnf4); tpdPlane4->SetInputConnection(text4->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> textMapper4 = vtkSmartPointer<vtkPolyDataMapper>::New(); textMapper4->SetInputConnection(tpdPlane4->GetOutputPort()); vtkSmartPointer<vtkActor> textActor4 = vtkSmartPointer<vtkActor>::New(); textActor4->SetMapper(textMapper4); textActor4->SetScale(scale.data()); textActor4->AddPosition(0.01, -0.49, 0.4); textActors.push_back(textActor4); vtkSmartPointer<vtkVectorText> text5 = vtkSmartPointer<vtkVectorText>::New(); text5->SetText("Coronal\nPlane\n\nAnterior"); vtkSmartPointer<vtkTransform> trnf5 = vtkSmartPointer<vtkTransform>::New(); trnf5->RotateY(-180); trnf5->RotateWXYZ(-90, 1, 0, 0); vtkSmartPointer<vtkTransformPolyDataFilter> tpdPlane5 = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); tpdPlane5->SetTransform(trnf5); tpdPlane5->SetInputConnection(text5->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> textMapper5 = vtkSmartPointer<vtkPolyDataMapper>::New(); textMapper5->SetInputConnection(tpdPlane5->GetOutputPort()); vtkSmartPointer<vtkActor> textActor5 = vtkSmartPointer<vtkActor>::New(); textActor5->SetMapper(textMapper5); textActor5->SetScale(scale.data()); textActor5->AddPosition(0.49, 0.01, 0.20); textActors.push_back(textActor5); vtkSmartPointer<vtkVectorText> text6 = vtkSmartPointer<vtkVectorText>::New(); text6->SetText("Coronal\nPlane\n\nPosterior"); vtkSmartPointer<vtkTransform> trnf6 = vtkSmartPointer<vtkTransform>::New(); trnf6->RotateWXYZ(90, 1, 0, 0); vtkSmartPointer<vtkTransformPolyDataFilter> tpdPlane6 = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); tpdPlane6->SetTransform(trnf6); tpdPlane6->SetInputConnection(text6->GetOutputPort()); vtkSmartPointer<vtkPolyDataMapper> textMapper6 = vtkSmartPointer<vtkPolyDataMapper>::New(); textMapper6->SetInputConnection(tpdPlane6->GetOutputPort()); vtkSmartPointer<vtkActor> textActor6 = vtkSmartPointer<vtkActor>::New(); textActor6->SetMapper(textMapper6); textActor6->SetScale(scale.data()); textActor6->AddPosition(-0.49, -0.01, 0.3); textActors.push_back(textActor6); return textActors; } }
CMakeLists.txt¶
cmake_minimum_required(VERSION 2.8) PROJECT(AnatomicalOrientation) find_package(VTK REQUIRED) include(${VTK_USE_FILE}) add_executable(AnatomicalOrientation MACOSX_BUNDLE AnatomicalOrientation.cxx ) target_link_libraries(AnatomicalOrientation ${VTK_LIBRARIES})
Download and Build AnatomicalOrientation¶
Click here to download AnatomicalOrientation and its CMakeLists.txt file. Once the tarball AnatomicalOrientation.tar has been downloaded and extracted,
cd AnatomicalOrientation/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:
./AnatomicalOrientation
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.