Commit e4ffa1f6 authored by lassoan's avatar lassoan
Browse files

ENH: Use vtkMRMLLabelMapVolumeNode class for labelmap volumes

Before vtkMRMLScalarVolumeNode was used for both scalar and label map
volumes and the LabelMap custom MRML node attribute was used for
distinguishing between them (0=scalar; 1=label map volume).
This made conversion between labelmap/scalar volumes very easy but made
it difficult to customize behavior, display, processing of segmentation
information.

Now a new vtkMRMLLabelMapVolumeNode class is used for storing segmentation
information (still using vtkMRMLScalarVolume used as base class for backward
compatibility; but in the future the base class may be changed to reflect
that segmentation can be represented in various ways, not just as volumes).

Notes:

A new "segmentation" node has been developed that is based on vtkMRMLLabelMapVolumeNode,
which can store segmentation information in various ways (not just labelmaps but planar
contours, closed surfaces, etc). To allow using of this new segmentation node,
first the vtkMRMLLabelMapVolumeNode has to be integrated into the Slicer core.

When reading a scene that was saved with an old version of Slicer, label maps are stored
in vtkMRMLScalarVolumeNode with label map attribute set to 1. Added a check for this case
and handle it by converting the vtkMRMLScalarVolumeNode to a vtkMRMLLabelMapVolumeNode during import.


git-svn-id: http://svn.slicer.org/Slicer4/trunk@24291 3bd1e089-480b-0410-8dfb-8563597acbee
parent 55215ab9
......@@ -204,7 +204,6 @@ if(Slicer_USE_QtTesting AND Slicer_USE_PYTHONQT)
slicer_add_python_unittest(SCRIPT KneeAtlasTest.py)
slicer_add_python_unittest(SCRIPT fiber_visibility_crash2438.py)
slicer_add_python_unittest(SCRIPT test_tractography_display.py)
slicer_add_python_unittest(SCRIPT labelToggleBug2049.py)
slicer_add_python_unittest(SCRIPT sceneImport2428.py)
slicer_add_python_unittest(SCRIPT SlicerMRBTest.py)
slicer_add_python_unittest(SCRIPT Slicer4Minute.py)
......@@ -227,7 +226,6 @@ if(Slicer_USE_QtTesting AND Slicer_USE_PYTHONQT)
DTINotReproducibleIssue3977.py
fiber_visibility_crash2438.py
test_tractography_display.py
labelToggleBug2049.py
sceneImport2428.py
SlicerMRBTest.py
Slicer4Minute.py
......
......@@ -367,9 +367,8 @@ class SubjectHierarchyGenericSelfTestTest(ScriptedLoadableModuleTest):
self.assertTrue( volumeNode.IsA('vtkMRMLScalarVolumeNode') )
self.assertTrue( label > 0 )
sampleLabelmapNode = slicer.vtkMRMLScalarVolumeNode()
sampleLabelmapNode = slicer.vtkMRMLLabelMapVolumeNode()
sampleLabelmapNode.SetName(name)
sampleLabelmapNode.SetLabelMap(1)
sampleLabelmapNode = slicer.mrmlScene.AddNode(sampleLabelmapNode)
sampleLabelmapNode.Copy(volumeNode)
imageData = vtk.vtkImageData()
......@@ -393,6 +392,8 @@ class SubjectHierarchyGenericSelfTestTest(ScriptedLoadableModuleTest):
self.assertTrue( colorNode != None )
labelmapVolumeDisplayNode.SetAndObserveColorNodeID(colorNode.GetID())
labelmapVolumeDisplayNode.VisibilityOn()
sampleLabelmapNodeName = slicer.mrmlScene.GenerateUniqueName(name)
sampleLabelmapNode.SetName(sampleLabelmapNodeName)
sampleLabelmapNode.SetAndObserveDisplayNodeID(labelmapVolumeDisplayNode.GetID())
return sampleLabelmapNode
......
import os
import unittest
from __main__ import vtk, qt, ctk, slicer
#
# labelToggleBug2049
#
class labelToggleBug2049:
def __init__(self, parent):
parent.title = "labelToggleBug2049" # TODO make this more human readable by adding spaces
parent.categories = ["Testing.TestCases"]
parent.dependencies = []
parent.contributors = ["Jean-Christophe Fillion-Robin (Kitware), Steve Pieper (Isomics)"] # replace with "Firstname Lastname (Org)"
parent.helpText = """
This is an example of scripted loadable module bundled in an extension.
"""
parent.acknowledgementText = """
This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. and Steve Pieper, Isomics, Inc. and was partially funded by NIH grant 3P41RR013218-12S1.
""" # replace with organization, grant and thanks.
self.parent = parent
# Add this test to the SelfTest module's list for discovery when the module
# is created. Since this module may be discovered before SelfTests itself,
# create the list if it doesn't already exist.
try:
slicer.selfTests
except AttributeError:
slicer.selfTests = {}
slicer.selfTests['labelToggleBug2049'] = self.runTest
def runTest(self):
tester = labelToggleBug2049Test()
tester.runTest()
#
# qlabelToggleBug2049Widget
#
class labelToggleBug2049Widget:
def __init__(self, parent = None):
if not parent:
self.parent = slicer.qMRMLWidget()
self.parent.setLayout(qt.QVBoxLayout())
self.parent.setMRMLScene(slicer.mrmlScene)
else:
self.parent = parent
self.layout = self.parent.layout()
if not parent:
self.setup()
self.parent.show()
def setup(self):
# Instantiate and connect widgets ...
# reload button
# (use this during development, but remove it when delivering
# your module to users)
self.reloadButton = qt.QPushButton("Reload")
self.reloadButton.toolTip = "Reload this module."
self.reloadButton.name = "labelToggleBug2049 Reload"
self.layout.addWidget(self.reloadButton)
self.reloadButton.connect('clicked()', self.onReload)
# reload and test button
# (use this during development, but remove it when delivering
# your module to users)
self.reloadAndTestButton = qt.QPushButton("Reload and Test")
self.reloadAndTestButton.toolTip = "Reload this module and then run the self tests."
self.layout.addWidget(self.reloadAndTestButton)
self.reloadAndTestButton.connect('clicked()', self.onReloadAndTest)
# Collapsible button
dummyCollapsibleButton = ctk.ctkCollapsibleButton()
dummyCollapsibleButton.text = "A collapsible button"
self.layout.addWidget(dummyCollapsibleButton)
# Layout within the dummy collapsible button
dummyFormLayout = qt.QFormLayout(dummyCollapsibleButton)
# HelloWorld button
helloWorldButton = qt.QPushButton("Hello world")
helloWorldButton.toolTip = "Print 'Hello world' in standard ouput."
dummyFormLayout.addWidget(helloWorldButton)
helloWorldButton.connect('clicked(bool)', self.onHelloWorldButtonClicked)
# Add vertical spacer
self.layout.addStretch(1)
# Set local var as instance attribute
self.helloWorldButton = helloWorldButton
def onHelloWorldButtonClicked(self):
print "Hello World !"
def onReload(self,moduleName="labelToggleBug2049"):
"""Generic reload method for any scripted module.
ModuleWizard will subsitute correct default moduleName.
"""
globals()[moduleName] = slicer.util.reloadScriptedModule(moduleName)
def onReloadAndTest(self,moduleName="labelToggleBug2049"):
self.onReload()
evalString = 'globals()["%s"].%sTest()' % (moduleName, moduleName)
tester = eval(evalString)
tester.runTest()
#
# labelToggleBug2049Logic
#
class labelToggleBug2049Logic:
"""This class should implement all the actual
computation done by your module. The interface
should be such that other python code can import
this class and make use of the functionality without
requiring an instance of the Widget
"""
def __init__(self):
pass
def hasImageData(self,volumeNode):
"""This is a dummy logic method that
returns true if the passed in volume
node has valid image data
"""
if not volumeNode:
print('no volume node')
return False
if volumeNode.GetImageData() == None:
print('no image data')
return False
return True
class labelToggleBug2049Test(unittest.TestCase):
"""
This is the test case for your scripted module.
"""
def delayDisplay(self,message,msec=1000):
print(message)
self.info = qt.QDialog()
self.infoLayout = qt.QVBoxLayout()
self.info.setLayout(self.infoLayout)
self.label = qt.QLabel(message,self.info)
self.infoLayout.addWidget(self.label)
qt.QTimer.singleShot(msec, self.info.close)
self.info.exec_()
def setUp(self):
""" Do whatever is needed to reset the state - typically a scene clear will be enough.
"""
slicer.mrmlScene.Clear(0)
def runTest(self):
"""Run as few or as many tests as needed here.
"""
self.setUp()
self.test_labelToggleBug20491()
def test_labelToggleBug20491(self):
""" Ideally you should have several levels of tests. At the lowest level
tests sould exercise the functionality of the logic with different inputs
(both valid and invalid). At higher levels your tests should emulate the
way the user would interact with your code and confirm that it still works
the way you intended.
One of the most important features of the tests is that it should alert other
developers when their changes will have an impact on the behavior of your
module. For example, if a developer removes a feature that you depend on,
your test should break so they know that the feature is needed.
"""
#
# this module exists to implement a test for bug #2409
# http://na-mic.org/Bug/view.php?id=2409
#
self.delayDisplay("Running the test")
#
# first, get the data
#
import urllib
downloads = (
('http://www.slicer.org/slicerWiki/images/4/43/MR-head.nrrd', 'MR-head.nrrd', slicer.util.loadVolume),
('http://www.slicer.org/slicerWiki/images/4/43/MR-head.nrrd', 'MR-head_1.nrrd', slicer.util.loadVolume),
)
for url,name,loader in downloads:
filePath = slicer.app.temporaryPath + '/' + name
if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
print('Requesting download %s from %s...\n' % (name, url))
urllib.urlretrieve(url, filePath)
if loader:
print('Loading %s...\n' % (name,))
loader(filePath)
self.delayDisplay('Finished with download and loading\n')
# check the data
logic = labelToggleBug2049Logic()
volumeNode = slicer.util.getNode(pattern="MR-head")
self.assertTrue( logic.hasImageData(volumeNode) )
volumeNode_1 = slicer.util.getNode(pattern="MR-head_1")
self.assertTrue( logic.hasImageData(volumeNode_1) )
self.delayDisplay("Volumes check out okay")
# use the volumes module to replicate the bug report
m = slicer.util.mainWindow()
m.moduleSelector().selectModule('Volumes')
self.delayDisplay("Entered Volumes module")
selector = slicer.util.findChildren(name='ActiveVolumeNodeSelector')[0]
checkBox = slicer.util.findChildren(name='LabelMapCheckBox')[0]
selector.setCurrentNode(volumeNode)
checkBox.checked = True
self.delayDisplay("Made the volumeNode into a label map")
# set the label map layer
appLogic = slicer.app.applicationLogic()
selectionNode = appLogic.GetSelectionNode()
selectionNode.SetReferenceActiveVolumeID( volumeNode_1.GetID() )
selectionNode.SetReferenceActiveLabelVolumeID( volumeNode.GetID() )
appLogic.PropagateVolumeSelection(0)
self.delayDisplay("Put volumes into layers")
# turn off the label map attribue
selector.setCurrentNode(volumeNode)
checkBox.checked = False
self.delayDisplay("Made the volumeNode into a non-label map")
self.delayDisplay('Did we crash?')
self.delayDisplay('Test passed!')
......@@ -28,6 +28,7 @@
#include <vtkMRMLFreeSurferModelOverlayStorageNode.h>
#include <vtkMRMLFreeSurferModelStorageNode.h>
#include <vtkMRMLLabelMapVolumeDisplayNode.h>
#include <vtkMRMLLabelMapVolumeNode.h>
#include <vtkMRMLTransformNode.h>
#include <vtkMRMLModelNode.h>
#include <vtkMRMLModelHierarchyNode.h>
......@@ -857,6 +858,7 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
vtkMRMLNode *nd = 0;
vtkSmartPointer<vtkMRMLDisplayNode> disp;
vtkMRMLStorageNode *storageNode = 0;
vtkMRMLLabelMapVolumeNode *lmvnd = 0;
vtkMRMLScalarVolumeNode *svnd = 0;
vtkMRMLVectorVolumeNode *vvnd = 0;
vtkMRMLDiffusionTensorVolumeNode *dtvnd = 0;
......@@ -888,6 +890,10 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
{
vvnd = vtkMRMLVectorVolumeNode::SafeDownCast(nd);
}
else if (vtkMRMLLabelMapVolumeNode::SafeDownCast(nd))
{
lmvnd = vtkMRMLLabelMapVolumeNode::SafeDownCast(nd);
}
else if (vtkMRMLScalarVolumeNode::SafeDownCast(nd))
{
svnd = vtkMRMLScalarVolumeNode::SafeDownCast(nd);
......@@ -949,9 +955,9 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
// Read the data into the referenced node
if (itksys::SystemTools::FileExists( req.GetFilename().c_str() ))
{
if (svnd || vvnd)
if (svnd || lmvnd || vvnd)
{
// Load a scalar or vector volume node
// Load a scalar, label map, or vector volume node
storageNode = storableNode->CreateDefaultStorageNode();
}
else if (dtvnd || dwvnd)
......@@ -1154,20 +1160,18 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
if (!displayableNode->GetDisplayNode())
{
if ((svnd && !svnd->GetDisplayNode())
|| (lmvnd && !lmvnd->GetDisplayNode())
|| (vvnd && !vvnd->GetDisplayNode()))
{
// Scalar or vector volume node
if (svnd)
// Scalar, label map or vector volume node
if (lmvnd)
{
if (svnd->GetLabelMap())
{
disp.TakeReference(vtkMRMLLabelMapVolumeDisplayNode::New());
}
else
else if (svnd)
{
disp.TakeReference(vtkMRMLScalarVolumeDisplayNode::New());
}
}
else
{
disp.TakeReference(vtkMRMLVectorVolumeDisplayNode::New());
......@@ -1185,7 +1189,7 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
// dtvdn->SetUpperThreshold(0);
// dtvdn->SetLowerThreshold(0);
// dtvdn->SetAutoWindowLevel(1);
disp.TakeReference(dtvdn); // assign to superclass pointer
disp = dtvdn; // assign to superclass pointer
}
else
{
......@@ -1237,20 +1241,16 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
vtkMRMLNode *dnode = this->GetMRMLScene()->AddNode( disp );
disp->SetAndObserveColorNodeID(this->GetColorLogic()->GetDefaultVolumeColorNodeID());
disp = vtkMRMLDisplayNode::SafeDownCast(dnode);
int isLabelMap = 0;
vtkMRMLVolumeDisplayNode *displayNode = NULL;
if (svnd)
if (lmvnd)
{
isLabelMap = svnd->GetLabelMap();
if (isLabelMap)
{
displayNode = vtkMRMLLabelMapVolumeDisplayNode::SafeDownCast(disp);
}
else
else if (svnd)
{
displayNode = vtkMRMLScalarVolumeDisplayNode::SafeDownCast(disp);
}
}
else
{
displayNode = vtkMRMLVolumeDisplayNode::SafeDownCast(disp);
......@@ -1259,7 +1259,12 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
{
displayNode->SetDefaultColorMap();
}
if (svnd)
if (lmvnd)
{
lmvnd->SetAndObserveDisplayNodeID( disp->GetID() );
}
else if (svnd)
{
svnd->SetAndObserveDisplayNodeID( disp->GetID() );
}
......@@ -1302,36 +1307,17 @@ void vtkSlicerApplicationLogic::ProcessReadNodeData(ReadDataRequest& req)
// Tensors? Vectors?
if (req.GetDisplayData())
{
if (vtkMRMLScalarVolumeNode::SafeDownCast(nd) != NULL)
{
svnd = vtkMRMLScalarVolumeNode::SafeDownCast(nd);
}
else
if (lmvnd)
{
svnd = NULL;
this->GetSelectionNode()->SetActiveLabelVolumeID( req.GetNode().c_str() );
}
if (svnd)
else if (svnd)
{
if (svnd->GetLabelMap())
{
this->GetSelectionNode()
->SetActiveLabelVolumeID( req.GetNode().c_str() );
}
else
{
this->GetSelectionNode()
->SetActiveVolumeID( req.GetNode().c_str() );
}
if (vtkMRMLScalarVolumeDisplayNode::SafeDownCast(svnd->GetDisplayNode()) != NULL)
{
this->GetSelectionNode()->SetActiveVolumeID( req.GetNode().c_str() );
// make sure win/level gets calculated
svnd->GetDisplayNode()->Modified();
}
// if (!strcmp(svnd->GetClassName(), "vtkMRMLScalarVolumeNode"))
{
this->PropagateVolumeSelection();
}
}
}
}
......
......@@ -117,7 +117,11 @@ def PushToSlicer(sitkimage, NodeName, compositeView=0, overwrite=False):
else:
raise Exception('Unknown view option given: {0}. See help'.format(compositeView))
EnsureRegistration()
if imageType == 'label':
newNode = CreateNewVolumeNode(NodeName,'vtkMRMLLabelMapVolumeNode', overwrite)
else:
newNode = CreateNewVolumeNode(NodeName,'vtkMRMLScalarVolumeNode', overwrite)
if not overwrite:
newDisplayNode = CreateNewDisplayNode(NodeName)
else:
......@@ -139,8 +143,6 @@ def PushToSlicer(sitkimage, NodeName, compositeView=0, overwrite=False):
elif imageType == 'background':
selectionNode.SetReferenceActiveVolumeID(writtenNode.GetID())
elif imageType == 'label':
vl = slicer.modules.volumes.logic()
vl.SetVolumeAsLabelMap(writtenNode, True)
selectionNode.SetReferenceActiveLabelVolumeID(writtenNode.GetID())
applicationLogic = slicer.app.applicationLogic()
......
......@@ -495,12 +495,13 @@ def array(pattern = "", index = 0):
console for quick debugging/testing. More specific API should be
used in scripts to be sure you get exactly what you want.
"""
scalarTypes = ('vtkMRMLScalarVolumeNode', 'vtkMRMLLabelMapVolumeNode')
vectorTypes = ('vtkMRMLVectorVolumeNode', 'vtkMRMLMultiVolumeNode')
tensorTypes = ('vtkMRMLDiffusionTensorVolumeNode',)
pointTypes = ('vtkMRMLModelNode',)
import vtk.util.numpy_support
n = getNode(pattern=pattern, index=index)
if n.GetClassName() == 'vtkMRMLScalarVolumeNode':
if n.GetClassName() in scalarTypes:
i = n.GetImageData()
shape = list(n.GetImageData().GetDimensions())
shape.reverse()
......
......@@ -254,7 +254,7 @@ void qSlicerCLIModuleUIHelperPrivate::initializeMaps()
// Image type attribute mapping
Self::ImageTypeAttributeToNodeType["scalar"] = "vtkMRMLScalarVolumeNode";
Self::ImageTypeAttributeToNodeType["label"] = "vtkMRMLScalarVolumeNode";
Self::ImageTypeAttributeToNodeType["label"] = "vtkMRMLLabelMapVolumeNode";
Self::ImageTypeAttributeToNodeType["vector"] = "vtkMRMLVectorVolumeNode";
Self::ImageTypeAttributeToNodeType["tensor"] = "vtkMRMLDiffusionTensorVolumeNode";
Self::ImageTypeAttributeToNodeType["diffusion-weighted"] = "vtkMRMLDiffusionWeightedVolumeNode";
......@@ -616,11 +616,6 @@ QWidget* qSlicerCLIModuleUIHelperPrivate::createImageTagWidget(const ModuleParam
QObject::connect(this->CLIModuleWidget, SIGNAL(mrmlSceneChanged(vtkMRMLScene*)),
widget, SLOT(setMRMLScene(vtkMRMLScene*)));
// Specify factory attributes
if (type == "label")
{
widget->addAttribute(nodeType, "LabelMap",QString("1"));
}
INSTANCIATE_WIDGET_VALUE_WRAPPER(Image, imageName, imageLabel, widget);
......
......@@ -990,6 +990,7 @@ void vtkSlicerCLIModuleLogic::ApplyTask(void *clientdata)
std::set<std::string> MemoryTransferPossible;
MemoryTransferPossible.insert("vtkMRMLScalarVolumeNode");
MemoryTransferPossible.insert("vtkMRMLLabelMapVolumeNode");
MemoryTransferPossible.insert("vtkMRMLVectorVolumeNode");
MemoryTransferPossible.insert("vtkMRMLDiffusionWeightedVolumeNode");
MemoryTransferPossible.insert("vtkMRMLDiffusionTensorVolumeNode");
......
......@@ -452,6 +452,13 @@ void qSlicerSlicer2SceneReaderPrivate::importVolumeNode(NodeType& node)
QString volumeNodeID;
vtkSmartPointer<vtkMRMLVolumeDisplayNode> volumeDisplayNode;
// set labelMap 0
// if { [info exists n(labelMap)] && ($n(labelMap) == "yes" || $n(labelMap) == "true") } {
// set labelMap 1
// }
bool labelMap = node.contains("labelMap") &&
(node["labelMap"] == "yes" ||
node["labelMap"] == "true");
//switch [string tolower $n(fileType)] {
// "nrrd" -
......@@ -485,14 +492,6 @@ void qSlicerSlicer2SceneReaderPrivate::importVolumeNode(NodeType& node)
fileName = node["fileName"];
}
// set labelMap 0
// if { [info exists n(labelMap)] && ($n(labelMap) == "yes" || $n(labelMap) == "true") } {
// set labelMap 1
// }
bool labelMap = node.contains("labelMap") &&
(node["labelMap"] == "yes" ||
node["labelMap"] == "true");
//set logic [$::slicer3::VolumesGUI GetLogic]
//set loadingOptions $labelMap
//set volumeNode [$logic AddArchetypeVolume $fileName $n(name) $loadingOptions ""]
......@@ -682,11 +681,8 @@ void qSlicerSlicer2SceneReaderPrivate::importVolumeNode(NodeType& node)
// } else {
// set volumeDisplayNode [vtkMRMLScalarVolumeDisplayNode New]
// }
if (node.contains("labelMap") &&
(node["labelMap"] == "yes" ||
node["labelMap"] == "true"))
if (labelMap)
{
vtkMRMLScalarVolumeNode::SafeDownCast(volumeNode)->SetLabelMap(1);
volumeDisplayNode = vtkSmartPointer<vtkMRMLLabelMapVolumeDisplayNode>::New();
}
else
......
......@@ -56,7 +56,6 @@ class ScriptedLoadableModuleTemplateWidget(ScriptedLoadableModuleWidget):
#
self.inputSelector = slicer.qMRMLNodeComboBox()
self.inputSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
self.inputSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 0 )
self.inputSelector.selectNodeUponCreation = True
self.inputSelector.addEnabled = False
self.inputSelector.removeEnabled = False
......@@ -72,7 +71,6 @@ class ScriptedLoadableModuleTemplateWidget(ScriptedLoadableModuleWidget):
#
self.outputSelector = slicer.qMRMLNodeComboBox()
self.outputSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
self.outputSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 0 )
self.outputSelector.selectNodeUponCreation = True
self.outputSelector.addEnabled = True
self.outputSelector.removeEnabled = True
......
......@@ -138,6 +138,7 @@ set(MRMLCore_SRCS
vtkMRMLDisplayableHierarchyNode.cxx
vtkMRMLInteractionNode.cxx
vtkMRMLLabelMapVolumeDisplayNode.cxx
vtkMRMLLabelMapVolumeNode.cxx
vtkMRMLLinearTransformNode.cxx
vtkMRMLModelDisplayNode.cxx
vtkMRMLModelHierarchyNode.cxx
......
/*==============================================================================
Copyright (c) Laboratory for Percutaneous Surgery (PerkLab)
Queen's University, Kingston, ON, Canada. All Rights Reserved.
See COPYRIGHT.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file was originally developed by Andras Lasso, PerkLab, Queen's University
and was supported through the Applied Cancer Research Unit program of Cancer Care
Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care
==============================================================================*/
// MRML includes
#include "vtkMRMLLabelMapVolumeNode.h"
#include "vtkMRMLLabelMapVolumeDisplayNode.h"
#include "vtkMRMLScene.h"
// VTK includes
#include <vtkDataArray.h>
#include <vtkImageData.h>
#include <vtkNew.h>
#include <vtkObjectFactory.h>
#include <vtkPointData.h>
//----------------------------------------------------------------------------
vtkMRMLNodeNewMacro(vtkMRMLLabelMapVolumeNode);
//----------------------------------------------------------------------------
vtkMRMLLabelMapVolumeNode::vtkMRMLLabelMapVolumeNode()
{