[med-svn] [itksnap] 02/07: New upstream version 3.6.0
Gert Wollny
gewo at moszumanska.debian.org
Fri Apr 7 06:37:16 UTC 2017
This is an automated email from the git hooks/post-receive script.
gewo pushed a commit to branch master
in repository itksnap.
commit 103873188389613f328d5f742b1db83a878904bf
Author: Gert Wollny <gewo at debian.org>
Date: Thu Apr 6 16:14:32 2017 +0000
New upstream version 3.6.0
---
.gitmodules | 6 +
CMake/standalone.cmake | 5 -
CMakeLists.txt | 208 +-
CTestConfig.cmake | 6 +-
Common/CommandLineArgumentParser.cxx | 2 +-
Common/HistoryManager.cxx | 31 +
Common/HistoryManager.h | 4 +
Common/IPCHandler.cxx | 2 +-
Common/IRISVectorTypesToITKConversion.h | 26 +
...tkBSplineScatteredDataPointSetToImageFilter.txx | 4 +-
.../itkMorphologicalContourInterpolator.h | 413 +++
.../itkMorphologicalContourInterpolator.hxx | 1723 ++++++++++++
...arallelSparseFieldLevelSetImageFilterBugFix.txx | 3 +-
...orTypesToITKConversion.h => ImageFunctions.cxx} | 58 +-
Common/ImageFunctions.h | 93 +
Common/PresetManager.hxx | 3 +-
Common/Registry.cxx | 72 +-
Common/Registry.h | 19 +
Common/SNAPCommon.h | 9 +
Common/SystemInterface.cxx | 8 +-
Common/SystemInterface.h | 2 +-
Common/ThreadSpecificData.cxx | 90 +-
Common/ThreadSpecificData.h | 36 +
GUI/Model/AnnotationModel.cxx | 119 +-
GUI/Model/AnnotationModel.h | 16 +-
GUI/Model/Generic3DModel.cxx | 72 +-
GUI/Model/GenericSliceModel.cxx | 269 +-
GUI/Model/GenericSliceModel.h | 105 +-
GUI/Model/GlobalUIModel.cxx | 22 +-
GUI/Model/GlobalUIModel.h | 24 +
GUI/Model/ImageIOWizardModel.cxx | 158 +-
GUI/Model/ImageIOWizardModel.h | 90 +-
GUI/Model/ImageInfoModel.cxx | 4 +-
GUI/Model/ImageRegistrationManager.cxx | 175 --
GUI/Model/ImageRegistrationManager.h | 70 -
GUI/Model/InteractiveRegistrationModel.cxx | 259 ++
GUI/Model/InteractiveRegistrationModel.h | 101 +
GUI/Model/InterpolateLabelModel.cxx | 119 +
GUI/Model/InterpolateLabelModel.h | 103 +
GUI/Model/LabelEditorModel.cxx | 28 +
GUI/Model/LabelEditorModel.h | 6 +
GUI/Model/LayerGeneralPropertiesModel.cxx | 24 +-
GUI/Model/LayerGeneralPropertiesModel.h | 2 +-
GUI/Model/LayerTableRowModel.cxx | 22 +-
GUI/Model/OrthogonalSliceCursorNavigationModel.cxx | 28 +-
GUI/Model/OrthogonalSliceCursorNavigationModel.h | 12 +-
GUI/Model/PaintbrushModel.cxx | 156 +-
GUI/Model/PaintbrushModel.h | 14 +-
GUI/Model/PolygonDrawingModel.cxx | 46 +-
GUI/Model/PolygonDrawingModel.h | 34 +-
GUI/Model/RegistrationModel.cxx | 1090 ++++++++
GUI/Model/RegistrationModel.h | 270 ++
GUI/Model/SaveModifiedLayersModel.cxx | 14 +-
GUI/Model/SaveModifiedLayersModel.h | 6 +-
GUI/Model/SliceWindowCoordinator.cxx | 151 +-
GUI/Model/SliceWindowCoordinator.h | 44 +-
GUI/Model/SnakeROIModel.cxx | 73 +-
GUI/Model/SnakeROIModel.h | 20 +-
GUI/Model/SnakeROIResampleModel.cxx | 6 +-
GUI/Model/SnakeROIResampleModel.h | 7 +-
GUI/Model/SnakeWizardModel.cxx | 81 +-
GUI/Model/SynchronizationModel.cxx | 10 +-
GUI/Qt/Components/DICOMListingTable.cxx | 68 +
GUI/Qt/Components/DICOMListingTable.h | 21 +
GUI/Qt/Components/FileChooserPanelWithHistory.cxx | 78 +-
GUI/Qt/Components/FileChooserPanelWithHistory.h | 8 +
GUI/Qt/Components/HistoryQListModel.cxx | 2 +-
GUI/Qt/Components/LayerInspectorRowDelegate.ui | 2 +-
GUI/Qt/Components/ProcessEventsITKCommand.h | 41 +
GUI/Qt/Components/QtReporterDelegates.cxx | 9 +
GUI/Qt/Components/SNAPQApplication.cxx | 115 +
GUI/Qt/Components/SNAPQApplication.h | 58 +
GUI/Qt/Components/SNAPQtCommon.cxx | 10 +-
GUI/Qt/Components/SliceViewPanel.cxx | 10 +
GUI/Qt/Components/SliceViewPanel.ui | 9 +
GUI/Qt/Components/ViewPanel3D.ui | 24 +-
GUI/Qt/Components/ZoomInspector.cxx | 8 +-
GUI/Qt/Components/ZoomInspector.ui | 2 +-
GUI/Qt/Coupling/QtRadioButtonCoupling.h | 11 +
GUI/Qt/Resources/SNAPResources.qrc | 2 +
GUI/Qt/Resources/reslice_16.png | Bin 0 -> 726 bytes
GUI/Qt/Resources/reslice_16 at 2x.png | Bin 0 -> 1764 bytes
GUI/Qt/View/CrosshairsInteractionMode.cxx | 55 +-
GUI/Qt/View/GenericView3D.cxx | 9 +-
GUI/Qt/View/PaintbrushInteractionMode.cxx | 8 +-
GUI/Qt/View/QtAbstractOpenGLBox.cxx | 21 +-
GUI/Qt/View/QtVTKRenderWindowBox.cxx | 28 +-
GUI/Qt/View/RegistrationInteractionMode.cxx | 98 +
GUI/Qt/View/RegistrationInteractionMode.h | 65 +
.../View/SliceWindowInteractionDelegateWidget.cxx | 4 +-
GUI/Qt/View/TestOpenGLDialog.h | 246 ++
GUI/Qt/Windows/DropActionDialog.cxx | 81 +-
GUI/Qt/Windows/DropActionDialog.h | 15 +-
GUI/Qt/Windows/DropActionDialog.ui | 6 +-
GUI/Qt/Windows/ImageIOWizard.cxx | 198 +-
GUI/Qt/Windows/ImageIOWizard.h | 19 +-
GUI/Qt/Windows/ImageIOWizard/RegistrationPage.cxx | 92 -
GUI/Qt/Windows/ImageIOWizard/RegistrationPage.h | 58 -
GUI/Qt/Windows/ImageIOWizard/RegistrationPage.ui | 154 --
GUI/Qt/Windows/InterpolateLabelsDialog.cxx | 63 +
GUI/Qt/Windows/InterpolateLabelsDialog.h | 36 +
GUI/Qt/Windows/InterpolateLabelsDialog.ui | 290 +++
GUI/Qt/Windows/LabelEditorDialog.cxx | 30 +
GUI/Qt/Windows/LabelEditorDialog.h | 8 +
GUI/Qt/Windows/LabelEditorDialog.ui | 20 +
GUI/Qt/Windows/LayerInspectorDialog.cxx | 22 +-
GUI/Qt/Windows/MainControlPanel.cxx | 6 +-
GUI/Qt/Windows/MainControlPanel.ui | 50 +-
GUI/Qt/Windows/MainImageWindow.cxx | 364 ++-
GUI/Qt/Windows/MainImageWindow.h | 37 +-
GUI/Qt/Windows/MainImageWindow.ui | 112 +-
GUI/Qt/Windows/PreferencesDialog.cxx | 5 +
GUI/Qt/Windows/PreferencesDialog.ui | 8 +-
GUI/Qt/Windows/Registration/RegistrationDialog.cxx | 309 +++
GUI/Qt/Windows/Registration/RegistrationDialog.h | 68 +
GUI/Qt/Windows/Registration/RegistrationDialog.ui | 931 +++++++
GUI/Qt/Windows/ReorientImageDialog.ui | 88 +-
GUI/Qt/Windows/ResampleDialog.cxx | 2 +-
GUI/Qt/Windows/SaveModifiedLayersDialog.cxx | 2 +-
GUI/Qt/Windows/SimpleFileDialogWithHistory.cxx | 23 +-
GUI/Qt/Windows/SimpleFileDialogWithHistory.h | 10 +-
GUI/Qt/Windows/SpeedImageDialog.cxx | 7 +-
GUI/Qt/Windows/SpeedImageDialog.ui | 177 +-
GUI/Qt/Windows/SplashPanel.ui | 2 +-
GUI/Qt/main.cxx | 277 +-
GUI/Renderer/AnnotationRenderer.cxx | 40 +-
GUI/Renderer/AnnotationRenderer.h | 4 +-
GUI/Renderer/CrosshairsRenderer.cxx | 12 +-
GUI/Renderer/Generic3DRenderer.cxx | 2 +-
GUI/Renderer/GenericSliceRenderer.cxx | 397 ++-
GUI/Renderer/GenericSliceRenderer.h | 59 +-
GUI/Renderer/IntensityCurveVTKRenderer.cxx | 6 +-
GUI/Renderer/OpenGLSliceTexture.cxx | 99 +-
GUI/Renderer/OpenGLSliceTexture.h | 45 +-
GUI/Renderer/OptimizationProgressRenderer.cxx | 60 +-
GUI/Renderer/OptimizationProgressRenderer.h | 15 +-
GUI/Renderer/OrientationGraphicRenderer.cxx | 2 +-
.../OrientationWidget/Reorient/ScanningROI.cxx | 16 +-
.../OrientationWidgetGUI.cxx | 24 +-
GUI/Renderer/PaintbrushRenderer.cxx | 2 +-
GUI/Renderer/PolygonDrawingRenderer.cxx | 48 +-
GUI/Renderer/PolygonDrawingRenderer.h | 10 +-
GUI/Renderer/RegistrationRenderer.cxx | 161 ++
GUI/Renderer/RegistrationRenderer.h | 29 +
GUI/Renderer/SliceWindowDecorationRenderer.cxx | 6 +-
GUI/Renderer/SnakeModeRenderer.cxx | 8 +-
GUI/Renderer/SnakeParameterPreviewRenderer.cxx | 3 +-
GUI/Renderer/SnakeParameterPreviewRenderer.h | 3 +-
GUI/Renderer/SnakeROIRenderer.cxx | 8 +-
GUI/Renderer/Window3DPicker.cxx | 10 +-
GUI/Renderer/Window3DPicker.h | 2 +-
Logic/Common/ImageCoordinateGeometry.cxx | 24 +-
Logic/Common/ImageCoordinateGeometry.h | 16 +-
Logic/Common/ImageCoordinateTransform.cxx | 116 +-
Logic/Common/ImageCoordinateTransform.h | 43 +-
Logic/Common/ImageRayIntersectionFinder.h | 20 +-
Logic/Common/ImageRayIntersectionFinder.txx | 24 +-
Logic/Common/MetaDataAccess.cxx | 5 +
Logic/Common/MetaDataAccess.h | 2 +
Logic/Common/SNAPAppearanceSettings.cxx | 39 +-
Logic/Common/SNAPAppearanceSettings.h | 1 +
Logic/Common/SNAPRegistryIO.cxx | 14 +-
Logic/Common/SNAPRegistryIO.h | 4 +-
Logic/Common/SNAPSegmentationROISettings.h | 4 -
Logic/Framework/GenericImageData.cxx | 219 +-
Logic/Framework/GenericImageData.h | 44 +-
Logic/Framework/GlobalState.h | 3 +-
Logic/Framework/IRISApplication.cxx | 845 ++++--
Logic/Framework/IRISApplication.h | 140 +-
Logic/Framework/ImageAnnotationData.cxx | 18 +-
Logic/Framework/ImageAnnotationData.h | 20 +-
Logic/Framework/ImageIODelegates.cxx | 43 +-
Logic/Framework/ImageIODelegates.h | 37 +-
Logic/Framework/SNAPImageData.cxx | 5 +-
Logic/Framework/SegmentationUpdateIterator.h | 287 ++
Logic/Framework/UndoDataManager.h | 90 +-
Logic/Framework/UndoDataManager.txx | 137 +-
Logic/ImageWrapper/DisplayMappingPolicy.cxx | 89 +-
Logic/ImageWrapper/DisplayMappingPolicy.h | 20 +-
Logic/ImageWrapper/GuidedNativeImageIO.cxx | 227 +-
Logic/ImageWrapper/GuidedNativeImageIO.h | 68 +-
Logic/ImageWrapper/ImageWrapper.cxx | 683 ++++-
Logic/ImageWrapper/ImageWrapper.h | 108 +-
Logic/ImageWrapper/ImageWrapperBase.h | 85 +-
Logic/ImageWrapper/ImageWrapperTraits.h | 12 +-
Logic/ImageWrapper/ScalarImageWrapper.cxx | 265 +-
Logic/ImageWrapper/ScalarImageWrapper.h | 27 +-
Logic/ImageWrapper/VectorImageWrapper.cxx | 70 +-
Logic/ImageWrapper/VectorImageWrapper.h | 7 +-
Logic/ImageWrapper/VectorToScalarImageAccessor.h | 32 +-
Logic/LevelSet/LevelSetExtensionFilter.h | 22 +-
Logic/LevelSet/SNAPAdvectionFieldImageFilter.h | 22 +-
Logic/LevelSet/SNAPAdvectionFieldImageFilter.txx | 22 +-
Logic/LevelSet/SNAPLevelSetDriver.h | 25 +-
Logic/LevelSet/SNAPLevelSetDriver.txx | 25 +-
Logic/LevelSet/SNAPLevelSetFunction.h | 32 +-
Logic/LevelSet/SNAPLevelSetFunction.txx | 23 +-
Logic/LevelSet/SignedDistanceFilter.h | 131 -
Logic/LevelSet/SignedDistanceFilter.txx | 108 -
Logic/LevelSet/SnakeParameters.cxx | 22 +-
Logic/LevelSet/SnakeParameters.h | 135 +-
Logic/LevelSet/SnakeParametersPreviewPipeline.cxx | 64 +-
Logic/LevelSet/SnakeParametersPreviewPipeline.h | 1 -
Logic/Mesh/MultiLabelMeshPipeline.cxx | 61 +-
Logic/Mesh/MultiLabelMeshPipeline.h | 55 +-
Logic/Preprocessing/ImageCollectionToImageFilter.h | 2 +-
.../PreprocessingFilterConfigTraits.cxx | 2 +-
.../PreprocessingFilterConfigTraits.h | 8 +-
.../{RandomForest => }/RFClassificationEngine.cxx | 71 +-
.../{RandomForest => }/RFClassificationEngine.h | 20 +-
.../RandomForest/RandomForestClassifier.cxx | 37 -
.../RandomForest/RandomForestClassifier.h | 93 -
Logic/Preprocessing/SlicePreviewFilterWrapper.h | 8 +-
Logic/Preprocessing/SlicePreviewFilterWrapper.txx | 4 +-
Logic/RLEImage/RLEImage.h | 253 ++
Logic/RLEImage/RLEImage.txx | 260 ++
Logic/RLEImage/RLEImageConstIterator.h | 407 +++
Logic/RLEImage/RLEImageIterator.h | 154 ++
Logic/RLEImage/RLEImageRegionConstIterator.h | 220 ++
Logic/RLEImage/RLEImageRegionIterator.h | 139 +
Logic/RLEImage/RLEImageScanlineConstIterator.h | 144 +
Logic/RLEImage/RLEImageScanlineIterator.h | 80 +
Logic/RLEImage/RLERegionOfInterestImageFilter.h | 282 ++
Logic/RLEImage/RLERegionOfInterestImageFilter.txx | 533 ++++
Logic/Slicing/AdaptiveSlicingPipeline.h | 187 ++
Logic/Slicing/AdaptiveSlicingPipeline.txx | 287 ++
Logic/Slicing/FastLinearInterpolator.h | 886 +++++++
Logic/Slicing/IRISSlicer.h | 180 +-
Logic/Slicing/IRISSlicer.txx | 268 +-
Logic/Slicing/IRISSlicer_RLE.txx | 353 +++
.../ImageRegionConstIteratorWithIndexOverride.h | 74 +
.../IntensityToColorLookupTableImageFilter.cxx | 10 +
.../IntensityToColorLookupTableImageFilter.h | 9 +-
.../Slicing/LookupTableIntensityMappingFilter.cxx | 81 +-
Logic/Slicing/LookupTableIntensityMappingFilter.h | 3 +
Logic/Slicing/NonOrthogonalSlicer.h | 285 ++
Logic/Slicing/NonOrthogonalSlicer.txx | 431 +++
.../RGBALookupTableIntensityMappingFilter.cxx | 75 +-
.../RGBALookupTableIntensityMappingFilter.h | 4 +
README.git | 9 +
README.html | 98 +-
README.md | 67 +
ReleaseNotes.txt => ReleaseNotes.md | 241 +-
Submodules/c3d/.gitignore | 16 +
Submodules/c3d/CMakeLists.txt | 219 ++
Submodules/c3d/COPYING.txt | 674 +++++
.../c3d/CTestConfig.cmake | 13 +-
Submodules/c3d/Convert2DMain.cxx | 39 +
Submodules/c3d/Convert3DMain.cxx | 38 +
Submodules/c3d/Convert4DMain.cxx | 38 +
Submodules/c3d/ConvertException.h | 63 +
Submodules/c3d/ConvertImageND.cxx | 2609 +++++++++++++++++++
Submodules/c3d/ConvertImageND.h | 204 ++
Submodules/c3d/ConvertImageVersion.cxx.in | 2 +
Submodules/c3d/ConvertNDLibrary.cmake | 145 ++
Submodules/c3d/ImageStack.h | 103 +
Submodules/c3d/README.txt | 19 +
Submodules/c3d/adapters/.ResliceImage.cxx.swp | Bin 0 -> 16384 bytes
Submodules/c3d/adapters/AddImages.cxx | 65 +
Submodules/c3d/adapters/AddImages.h | 48 +
Submodules/c3d/adapters/AlignByLandmarks.cxx | 243 ++
Submodules/c3d/adapters/AlignByLandmarks.h | 54 +
Submodules/c3d/adapters/AntiAliasImage.cxx | 64 +
Submodules/c3d/adapters/AntiAliasImage.h | 48 +
Submodules/c3d/adapters/ApplyMetric.cxx | 470 ++++
Submodules/c3d/adapters/ApplyMetric.h | 73 +
Submodules/c3d/adapters/BiasFieldCorrectionN4.cxx | 220 ++
Submodules/c3d/adapters/BiasFieldCorrectionN4.h | 48 +
Submodules/c3d/adapters/BinaryHoleFill.cxx | 58 +
Submodules/c3d/adapters/BinaryHoleFill.h | 48 +
Submodules/c3d/adapters/BinaryImageCentroid.cxx | 68 +
Submodules/c3d/adapters/BinaryImageCentroid.h | 48 +
Submodules/c3d/adapters/BinaryMathOperation.cxx | 99 +
Submodules/c3d/adapters/BinaryMathOperation.h | 60 +
Submodules/c3d/adapters/Canny.cxx | 86 +
Submodules/c3d/adapters/Canny.h | 48 +
Submodules/c3d/adapters/ClipImageIntensity.cxx | 55 +
Submodules/c3d/adapters/ClipImageIntensity.h | 48 +
Submodules/c3d/adapters/ComputeFFT.cxx | 85 +
Submodules/c3d/adapters/ComputeFFT.h | 48 +
Submodules/c3d/adapters/ComputeMoments.cxx | 101 +
Submodules/c3d/adapters/ComputeMoments.h | 48 +
Submodules/c3d/adapters/ComputeOverlaps.cxx | 97 +
Submodules/c3d/adapters/ComputeOverlaps.h | 48 +
Submodules/c3d/adapters/ConnectedComponents.cxx | 95 +
Submodules/c3d/adapters/ConnectedComponents.h | 48 +
Submodules/c3d/adapters/ConvertAdapter.h | 63 +
Submodules/c3d/adapters/CoordinateMap.cxx | 83 +
Submodules/c3d/adapters/CoordinateMap.h | 48 +
Submodules/c3d/adapters/CopyTransform.cxx | 67 +
Submodules/c3d/adapters/CopyTransform.h | 48 +
Submodules/c3d/adapters/CreateImage.cxx | 59 +
Submodules/c3d/adapters/CreateImage.h | 48 +
Submodules/c3d/adapters/CreateInterpolator.cxx | 96 +
Submodules/c3d/adapters/CreateInterpolator.h | 61 +
Submodules/c3d/adapters/DicomSeriesList.cxx | 124 +
Submodules/c3d/adapters/DicomSeriesList.h | 48 +
Submodules/c3d/adapters/ExtractRegion.cxx | 61 +
Submodules/c3d/adapters/ExtractRegion.h | 48 +
Submodules/c3d/adapters/ExtractSlice.cxx | 216 ++
Submodules/c3d/adapters/ExtractSlice.h | 51 +
Submodules/c3d/adapters/FlipImage.cxx | 65 +
Submodules/c3d/adapters/FlipImage.h | 48 +
Submodules/c3d/adapters/GeneralLinearModel.cxx | 91 +
Submodules/c3d/adapters/GeneralLinearModel.h | 48 +
Submodules/c3d/adapters/HessianObjectness.cxx | 87 +
Submodules/c3d/adapters/HessianObjectness.h | 48 +
Submodules/c3d/adapters/HistogramMatch.cxx | 69 +
Submodules/c3d/adapters/HistogramMatch.h | 48 +
Submodules/c3d/adapters/ImageERF.cxx | 58 +
Submodules/c3d/adapters/ImageERF.h | 48 +
Submodules/c3d/adapters/ImageGradient.cxx | 84 +
Submodules/c3d/adapters/ImageGradient.h | 48 +
Submodules/c3d/adapters/ImageLaplacian.cxx | 55 +
Submodules/c3d/adapters/ImageLaplacian.h | 48 +
Submodules/c3d/adapters/LabelOverlapMeasures.cxx | 135 +
Submodules/c3d/adapters/LabelOverlapMeasures.h | 48 +
Submodules/c3d/adapters/LabelStatistics.cxx | 106 +
Submodules/c3d/adapters/LabelStatistics.h | 48 +
Submodules/c3d/adapters/LandmarksToSpheres.cxx | 175 ++
Submodules/c3d/adapters/LandmarksToSpheres.h | 48 +
Submodules/c3d/adapters/LaplacianSharpening.cxx | 53 +
Submodules/c3d/adapters/LaplacianSharpening.h | 48 +
Submodules/c3d/adapters/LevelSetSegmentation.cxx | 192 ++
Submodules/c3d/adapters/LevelSetSegmentation.h | 60 +
Submodules/c3d/adapters/MRFVote.cxx | 189 ++
Submodules/c3d/adapters/MRFVote.h | 48 +
Submodules/c3d/adapters/MathematicalMorphology.cxx | 81 +
Submodules/c3d/adapters/MathematicalMorphology.h | 48 +
Submodules/c3d/adapters/MeanFilter.cxx | 61 +
Submodules/c3d/adapters/MeanFilter.h | 48 +
Submodules/c3d/adapters/MedianFilter.cxx | 58 +
Submodules/c3d/adapters/MedianFilter.h | 48 +
Submodules/c3d/adapters/MixtureModel.cxx | 124 +
Submodules/c3d/adapters/MixtureModel.h | 48 +
Submodules/c3d/adapters/MultiplyImages.cxx | 66 +
Submodules/c3d/adapters/MultiplyImages.h | 48 +
Submodules/c3d/adapters/NormalizeLocalWindow.cxx | 128 +
Submodules/c3d/adapters/NormalizeLocalWindow.h | 48 +
.../c3d/adapters/NormalizedCrossCorrelation.cxx | 131 +
.../c3d/adapters/NormalizedCrossCorrelation.h | 48 +
Submodules/c3d/adapters/OverlayLabelImage.cxx | 90 +
Submodules/c3d/adapters/OverlayLabelImage.h | 48 +
Submodules/c3d/adapters/PadImage.cxx | 78 +
Submodules/c3d/adapters/PadImage.h | 48 +
Submodules/c3d/adapters/PeronaMalik.cxx | 57 +
Submodules/c3d/adapters/PeronaMalik.h | 48 +
Submodules/c3d/adapters/PrintImageInfo.cxx | 260 ++
Submodules/c3d/adapters/PrintImageInfo.h | 49 +
Submodules/c3d/adapters/RFApply.cxx | 82 +
Submodules/c3d/adapters/RFApply.h | 48 +
Submodules/c3d/adapters/RFTrain.cxx | 176 ++
Submodules/c3d/adapters/RFTrain.h | 68 +
Submodules/c3d/adapters/Rank.cxx | 86 +
Submodules/c3d/adapters/Rank.h | 48 +
Submodules/c3d/adapters/ReadImage.cxx | 201 ++
Submodules/c3d/adapters/ReadImage.h | 58 +
Submodules/c3d/adapters/ReciprocalImage.cxx | 63 +
Submodules/c3d/adapters/ReciprocalImage.h | 48 +
Submodules/c3d/adapters/ReorderStack.cxx | 62 +
Submodules/c3d/adapters/ReorderStack.h | 48 +
Submodules/c3d/adapters/ReplaceIntensities.cxx | 79 +
Submodules/c3d/adapters/ReplaceIntensities.h | 48 +
Submodules/c3d/adapters/ResampleImage.cxx | 90 +
Submodules/c3d/adapters/ResampleImage.h | 48 +
Submodules/c3d/adapters/ResliceImage.cxx | 182 ++
Submodules/c3d/adapters/ResliceImage.h | 48 +
Submodules/c3d/adapters/SLICSuperVoxel.cxx | 79 +
Submodules/c3d/adapters/SLICSuperVoxel.h | 48 +
Submodules/c3d/adapters/SampleImage.cxx | 60 +
Submodules/c3d/adapters/SampleImage.h | 52 +
Submodules/c3d/adapters/ScalarToRGB.cxx | 96 +
Submodules/c3d/adapters/ScalarToRGB.h | 48 +
Submodules/c3d/adapters/ScaleShiftImage.cxx | 63 +
Submodules/c3d/adapters/ScaleShiftImage.h | 48 +
Submodules/c3d/adapters/SetOrientation.cxx | 86 +
Submodules/c3d/adapters/SetOrientation.h | 48 +
Submodules/c3d/adapters/SetSform.cxx | 89 +
Submodules/c3d/adapters/SetSform.h | 48 +
.../c3d/adapters/SignedDistanceTransform.cxx | 68 +
Submodules/c3d/adapters/SignedDistanceTransform.h | 48 +
.../c3d/adapters/SimpleElasticRegistration.cxx | 733 ++++++
.../c3d/adapters/SimpleElasticRegistration.h | 133 +
Submodules/c3d/adapters/SmoothImage.cxx | 88 +
Submodules/c3d/adapters/SmoothImage.h | 48 +
Submodules/c3d/adapters/SplitMultilabelImage.cxx | 85 +
Submodules/c3d/adapters/SplitMultilabelImage.h | 48 +
Submodules/c3d/adapters/StapleAlgorithm.cxx | 71 +
Submodules/c3d/adapters/StapleAlgorithm.h | 48 +
Submodules/c3d/adapters/TestImage.cxx | 94 +
Submodules/c3d/adapters/TestImage.h | 48 +
Submodules/c3d/adapters/ThresholdImage.cxx | 59 +
Submodules/c3d/adapters/ThresholdImage.h | 48 +
Submodules/c3d/adapters/TileImages.cxx | 94 +
Submodules/c3d/adapters/TileImages.h | 48 +
Submodules/c3d/adapters/TrimImage.cxx | 104 +
Submodules/c3d/adapters/TrimImage.h | 52 +
Submodules/c3d/adapters/UnaryMathOperation.cxx | 48 +
Submodules/c3d/adapters/UnaryMathOperation.h | 48 +
Submodules/c3d/adapters/UpdateMetadataKey.cxx | 53 +
Submodules/c3d/adapters/UpdateMetadataKey.h | 48 +
Submodules/c3d/adapters/Vote.cxx | 104 +
Submodules/c3d/adapters/Vote.h | 48 +
.../c3d/adapters/VoxelwiseComponentFunction.cxx | 186 ++
.../c3d/adapters/VoxelwiseComponentFunction.h | 51 +
Submodules/c3d/adapters/VoxelwiseRegression.cxx | 73 +
Submodules/c3d/adapters/VoxelwiseRegression.h | 48 +
Submodules/c3d/adapters/WarpImage.cxx | 105 +
Submodules/c3d/adapters/WarpImage.h | 48 +
Submodules/c3d/adapters/WarpLabelImage.cxx | 154 ++
Submodules/c3d/adapters/WarpLabelImage.h | 48 +
Submodules/c3d/adapters/WeightedSum.cxx | 76 +
Submodules/c3d/adapters/WeightedSum.h | 48 +
Submodules/c3d/adapters/WeightedSumVoxelwise.cxx | 74 +
Submodules/c3d/adapters/WeightedSumVoxelwise.h | 48 +
Submodules/c3d/adapters/WrapDimension.cxx | 90 +
Submodules/c3d/adapters/WrapDimension.h | 48 +
Submodules/c3d/adapters/WriteImage.cxx | 247 ++
Submodules/c3d/adapters/WriteImage.h | 58 +
.../generator/ConvertAdapterTemplate.cxx.in | 47 +
.../adapters/generator/ConvertAdapterTemplate.h.in | 48 +
Submodules/c3d/adapters/generator/runme.sh | 4 +
Submodules/c3d/api/ConvertAPI.cxx | 220 ++
Submodules/c3d/api/ConvertAPI.h | 98 +
Submodules/c3d/cmake/Package.cmake | 124 +
Submodules/c3d/doc/c3d.md | 1503 +++++++++++
Submodules/c3d/gui/CMake/DeployQt5.cmake | 337 +++
Submodules/c3d/gui/CommandEditor.cxx | 503 ++++
Submodules/c3d/gui/CommandEditor.h | 91 +
Submodules/c3d/gui/ConvertSyntaxHighlighter.cxx | 91 +
Submodules/c3d/gui/ConvertSyntaxHighlighter.h | 50 +
Submodules/c3d/gui/HistoryDialog.cxx | 177 ++
Submodules/c3d/gui/HistoryDialog.h | 51 +
Submodules/c3d/gui/HistoryDialog.ui | 107 +
Submodules/c3d/gui/MainWindow.cxx | 499 ++++
Submodules/c3d/gui/MainWindow.h | 92 +
Submodules/c3d/gui/MainWindow.ui | 210 ++
Submodules/c3d/gui/SettingsDialog.cxx | 88 +
Submodules/c3d/gui/SettingsDialog.h | 35 +
Submodules/c3d/gui/SettingsDialog.ui | 115 +
.../c3d/gui/Utilities/Forwarding/CMakeLists.txt | 19 +
.../gui/Utilities/Forwarding/SharedForwardExe.c.in | 27 +
Submodules/c3d/gui/main.cxx | 109 +
Submodules/c3d/gui/resources/macos/Info.plist | 29 +
Submodules/c3d/gui/resources/macos/c3dgui.icns | Bin 0 -> 142873 bytes
Submodules/c3d/gui/resources/macos/icon.graffle | 331 +++
.../c3d/gui/resources/macos/icon_256x256.png | Bin 0 -> 25300 bytes
.../c3d/gui/resources/macos/icon_512x512.png | Bin 0 -> 53630 bytes
Submodules/c3d/itkextras/CMakeLists.txt | 3 +
.../c3d/itkextras}/ImageCollectionToImageFilter.h | 1 +
.../ImageRegionConstIteratorWithIndexOverride.h | 78 +
.../OneDimensionalInPlaceAccumulateFilter.h | 123 +
.../OneDimensionalInPlaceAccumulateFilter.txx | 565 ++++
Submodules/c3d/itkextras/PovRayIO/CMakeLists.txt | 7 +
.../c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.cxx | 121 +
.../c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.h | 95 +
.../PovRayIO/itkPovRayDF3ImageIOFactory.cxx | 52 +
.../PovRayIO/itkPovRayDF3ImageIOFactory.h | 67 +
.../c3d/itkextras/RandomForest/CMakeLists.txt | 1 +
.../RandomForest/Library/classification.h | 0
.../itkextras}/RandomForest/Library/classifier.h | 1 +
.../c3d/itkextras}/RandomForest/Library/data.h | 0
.../c3d/itkextras}/RandomForest/Library/forest.h | 0
.../c3d/itkextras}/RandomForest/Library/imageio.h | 3 +-
.../RandomForest/Library/linearalgebra.h | 0
.../c3d/itkextras}/RandomForest/Library/node.h | 0
.../c3d/itkextras}/RandomForest/Library/random.h | 0
.../itkextras}/RandomForest/Library/statistics.h | 5 +-
.../c3d/itkextras}/RandomForest/Library/trainer.h | 0
.../RandomForest/Library/trainingcontext.h | 0
.../c3d/itkextras}/RandomForest/Library/tree.h | 6 +-
.../c3d/itkextras}/RandomForest/Library/type.h | 0
.../c3d/itkextras}/RandomForest/Library/utility.h | 0
.../RandomForest/RandomForestClassifier.cxx | 0
.../RandomForest/RandomForestClassifier.h | 197 ++
.../RandomForestClassifyImageFilter.h | 28 +-
.../RandomForestClassifyImageFilter.txx | 177 +-
Submodules/c3d/itkextras/VectorImageTools.h | 59 +
Submodules/c3d/itkextras/VoxBoIO/CMakeLists.txt | 7 +
.../c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.cxx | 792 ++++++
.../c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.h | 128 +
.../VoxBoIO/itkVoxBoCUBImageIOFactory.cxx | 52 +
.../itkextras/VoxBoIO/itkVoxBoCUBImageIOFactory.h | 67 +
Submodules/c3d/itkextras/gsGSAffine3DTransform.h | 159 ++
Submodules/c3d/itkextras/gsGSAffine3DTransform.txx | 501 ++++
.../itkGaussianInterpolateImageFunction.h | 267 ++
.../itkHessianToObjectnessMeasureImageFilter.h | 173 ++
.../itkHessianToObjectnessMeasureImageFilter.hxx | 206 ++
.../c3d/itkextras/itkLabelContourImageFilter.h | 226 ++
.../c3d/itkextras/itkLabelContourImageFilter.txx | 449 ++++
...itkLabelImageGaussianInterpolateImageFunction.h | 262 ++
.../itkextras/itkLabelOverlapMeasuresImageFilter.h | 208 ++
.../itkLabelOverlapMeasuresImageFilter.txx | 437 ++++
.../itkMultiScaleHessianBasedMeasureImageFilter.h | 225 ++
...itkMultiScaleHessianBasedMeasureImageFilter.hxx | 435 ++++
Submodules/c3d/itkextras/itkOrientedRASImage.h | 271 ++
.../c3d/itkextras/itkSLICSuperVoxelImageFilter.h | 111 +
.../c3d/itkextras/itkSLICSuperVoxelImageFilter.hxx | 285 ++
Submodules/c3d/utilities/AffineTransformTool.cxx | 785 ++++++
Submodules/c3d/utilities/CMakeLists.txt | 5 +
Submodules/c3d/utilities/bashcomp.sh | 120 +
Submodules/c3d/utilities/doc/Documentation.cxx | 194 ++
Submodules/c3d/utilities/doc/Documentation.h | 93 +
Submodules/c3d/utilities/hexdump.c | 2109 +++++++++++++++
Submodules/c3d/utilities/hexdump.h | 215 ++
Submodules/greedy/.CMakeLists.txt.un~ | Bin 0 -> 30805 bytes
Submodules/greedy/.gitignore | 5 +
Submodules/greedy/CMake/FindFFTW.cmake | 68 +
Submodules/greedy/CMake/Package.cmake | 123 +
Submodules/greedy/CMakeLists.txt | 88 +
Submodules/greedy/src/.SimpleWarpImageFilter.h.un~ | Bin 0 -> 609 bytes
.../greedy/src/.SimpleWarpImageFilter.txx.un~ | Bin 0 -> 555 bytes
Submodules/greedy/src/.greedy_main.cxx.un~ | Bin 0 -> 9909 bytes
Submodules/greedy/src/.lddmm_data.cxx.un~ | Bin 0 -> 195729 bytes
Submodules/greedy/src/.lddmm_data.h.un~ | Bin 0 -> 35112 bytes
Submodules/greedy/src/CommandLineHelper.h | 316 +++
Submodules/greedy/src/FastLinearInterpolator.h | 893 +++++++
.../greedy/src/FastWarpCompositeImageFilter.h | 142 +
.../greedy/src/FastWarpCompositeImageFilter.txx | 147 ++
Submodules/greedy/src/GreedyAPI.cxx | 2745 ++++++++++++++++++++
Submodules/greedy/src/GreedyAPI.h | 318 +++
Submodules/greedy/src/GreedyException.h | 60 +
Submodules/greedy/src/GreedyParameters.cxx | 69 +
Submodules/greedy/src/GreedyParameters.h | 221 ++
.../ImageRegionConstIteratorWithIndexOverride.h | 78 +
.../greedy/src/LinearTransformToWarpFilter.h | 106 +
.../greedy/src/LinearTransformToWarpFilter.txx | 104 +
.../src/MultiComponentApproximateNCCImageMetric.h | 242 ++
.../MultiComponentApproximateNCCImageMetric.txx | 496 ++++
.../greedy/src/MultiComponentImageMetricBase.h | 378 +++
.../greedy/src/MultiComponentImageMetricBase.txx | 512 ++++
.../src/MultiComponentMutualInfoImageMetric.h | 367 +++
.../src/MultiComponentMutualInfoImageMetric.txx | 592 +++++
.../greedy/src/MultiComponentNCCImageMetric.h | 271 ++
.../greedy/src/MultiComponentNCCImageMetric.txx | 641 +++++
.../greedy/src/MultiImageAffineMSDMetricFilter.h | 220 ++
.../greedy/src/MultiImageAffineMSDMetricFilter.txx | 258 ++
.../greedy/src/MultiImageOpticalFlowImageFilter.h | 142 +
.../src/MultiImageOpticalFlowImageFilter.txx | 174 ++
.../greedy/src/MultiImageRegistrationHelper.h | 238 ++
.../greedy/src/MultiImageRegistrationHelper.txx | 1210 +++++++++
.../greedy/src/MultiImageSimpleWarpImageFilter.h | 903 +++++++
.../greedy/src/MultiImageSimpleWarpImageFilter.txx | 1429 ++++++++++
.../src/OneDimensionalInPlaceAccumulateFilter.h | 123 +
.../src/OneDimensionalInPlaceAccumulateFilter.txx | 585 +++++
Submodules/greedy/src/SimpleWarpImageFilter.h | 208 ++
Submodules/greedy/src/SimpleWarpImageFilter.txx | 246 ++
Submodules/greedy/src/bk/SimpleWarpImageFilter.h | 195 ++
Submodules/greedy/src/bk/SimpleWarpImageFilter.txx | 236 ++
Submodules/greedy/src/bk/greedy_main.cxx | 295 +++
.../src/bk/itkGaussianInterpolateImageFunction.h | 267 ++
.../src/bk/itkOptLinearInterpolateImageFunction.h | 520 ++++
.../bk/itkOptLinearInterpolateImageFunction.txx | 160 ++
.../itkOptVectorLinearInterpolateImageFunction.h | 520 ++++
.../itkOptVectorLinearInterpolateImageFunction.txx | 160 ++
Submodules/greedy/src/bk/lddmm_common.h | 10 +
Submodules/greedy/src/bk/lddmm_data.cxx | 871 +++++++
Submodules/greedy/src/bk/lddmm_data.h | 172 ++
Submodules/greedy/src/bk/lddmm_main.cxx | 326 +++
Submodules/greedy/src/bk/lddmm_temp.cxx | 0
Submodules/greedy/src/greedy_main.cxx | 473 ++++
.../src/itkGaussianInterpolateImageFunction.h | 290 +++
.../itkOptVectorLinearInterpolateImageFunction.h | 520 ++++
.../itkOptVectorLinearInterpolateImageFunction.txx | 160 ++
.../itkVectorImageCentralDifferenceImageFunction.h | 150 ++
...tkVectorImageCentralDifferenceImageFunction.hxx | 93 +
Submodules/greedy/src/lddmm_common.h | 74 +
Submodules/greedy/src/lddmm_data.cxx | 1433 ++++++++++
Submodules/greedy/src/lddmm_data.h | 285 ++
Submodules/greedy/src/lddmm_main.cxx | 354 +++
.../greedy/src/macf/macf_fixed_point_inverse.cxx | 75 +
Submodules/greedy/src/macf/macf_gradient.cxx | 126 +
.../greedy/src/macf/macf_objective_and_delta.cxx | 108 +
.../src/macf/macf_objective_and_gradient.cxx | 94 +
Submodules/greedy/src/macf/macf_pyramid.cxx | 86 +
.../data/lddmm/test01_expected_result.nii.gz | Bin 0 -> 120766 bytes
.../greedy/testing/data/lddmm/test01_input.nii.gz | Bin 0 -> 18580 bytes
.../greedy/testing/data/phantom01_fixed.nii.gz | Bin 0 -> 7086 bytes
.../greedy/testing/data/phantom01_mask.nii.gz | Bin 0 -> 3689 bytes
.../greedy/testing/data/phantom01_moving.nii.gz | Bin 0 -> 60132 bytes
.../greedy/testing/data/phantom01_readme.txt | 9 +
Submodules/greedy/testing/data/phantom01_rigid.mat | 4 +
.../greedy/testing/data/phantom01_source.nii.gz | Bin 0 -> 3073 bytes
.../greedy/testing/data/phantom02_fixed.nii.gz | Bin 0 -> 7573 bytes
.../greedy/testing/data/phantom02_moving.nii.gz | Bin 0 -> 61666 bytes
.../greedy/testing/data/phantom03_fixed.nii.gz | Bin 0 -> 379368 bytes
.../greedy/testing/data/phantom03_moving.nii.gz | Bin 0 -> 7059886 bytes
Submodules/greedy/testing/data/runpair.sh | 32 +
Submodules/greedy/testing/data/src/phantoms.sh | 35 +
.../TestOneDimensionalInPlaceAccumulateFilter.cxx | 68 +
Testing/GUI/Qt/SNAPTestQt.cxx | 45 +-
Testing/GUI/Qt/SNAPTestQt.h | 6 +
Testing/GUI/Qt/Scripts/test_DiffSpace.js | 49 +
Testing/GUI/Qt/Scripts/test_Library.js | 17 +-
Testing/GUI/Qt/Scripts/test_Workspace.js | 2 +-
Testing/GUI/Qt/TestingScripts.qrc | 11 +-
Testing/Logic/SNAPTestDriver.cxx | 256 --
Testing/Logic/SNAPTestDriver.h | 63 -
Testing/Logic/SlicingPerformanceTest.cxx | 331 +++
Testing/Logic/TestBase.h | 100 -
Testing/Logic/TestCompareLevelSets.cxx | 850 ------
Testing/Logic/TestCompareLevelSets.h | 73 -
Testing/Logic/TestImageWrapper.h | 93 -
Testing/Logic/TestMain.cxx | 25 -
Testing/Logic/TutorialTest.cxx | 373 ---
Testing/Logic/iteratorTests.cxx | 25 +
Testing/Logic/itkImageIteratorTest.cxx | 280 ++
Testing/Logic/itkImageIteratorWithIndexTest.cxx | 399 +++
.../Logic/itkImageIteratorsForwardBackwardTest.cxx | 172 ++
...tkImageRegionConstIteratorWithOnlyIndexTest.cxx | 230 ++
Testing/Logic/itkImageRegionIteratorTest.cxx | 246 ++
Testing/Logic/itkImageScanlineIteratorTest1.cxx | 235 ++
Testing/Logic/itkIteratorTests.cxx | 124 +
.../Logic/itkRegionOfInterestImageFilterTest.cxx | 144 +
Testing/Logic/testRLE.cxx | 145 ++
Testing/TestData/X300.mha | Bin 0 -> 1684 bytes
Testing/TestData/X39.nii.gz | Bin 0 -> 465 bytes
Testing/TestData/Y300.mha | Bin 0 -> 1132 bytes
Testing/TestData/Y55.nii.gz | Bin 0 -> 284 bytes
Testing/TestData/Z150.mha | Bin 0 -> 1522 bytes
Testing/TestData/Z32.nii.gz | Bin 0 -> 426 bytes
Testing/TestData/diffspace.itksnap | 328 +++
Testing/TestData/fixed_image.nii.gz | Bin 0 -> 1268 bytes
Testing/TestData/moving_image.nii.gz | Bin 0 -> 1069 bytes
Testing/TestData/multi_chunk.nii.gz | Bin 0 -> 277681 bytes
Testing/TestData/reslice_image.nii.gz | Bin 0 -> 7479 bytes
Testing/TestData/t1_chunk.nii.gz | Bin 0 -> 97937 bytes
Testing/TestData/t2_chunk.nii.gz | Bin 0 -> 62091 bytes
Testing/TestData/vb-seg.mha | Bin 0 -> 180716 bytes
Testing/TestData/warp_image.nii.gz | Bin 0 -> 163728 bytes
Testing/TestData/warpfield.itksnap | 349 +++
Utilities/MacOS/BundleResources/Info.plist | 15 +-
632 files changed, 77698 insertions(+), 6213 deletions(-)
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..a43531d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "Submodules/c3d"]
+ path = Submodules/c3d
+ url = http://git.code.sf.net/p/c3d/git
+[submodule "Submodules/greedy"]
+ path = Submodules/greedy
+ url = https://github.com/pyushkevich/greedy.git
diff --git a/CMake/standalone.cmake b/CMake/standalone.cmake
index c73b292..9a76358 100644
--- a/CMake/standalone.cmake
+++ b/CMake/standalone.cmake
@@ -63,8 +63,3 @@ ENDIF(NOT SNAP_USE_QT4)
# Look for OpenGL.
FIND_PACKAGE(OpenGL REQUIRED)
-
-# Link libraries from the parent CMAKE file
-#LINK_LIBRARIES(ITKAlgorithms ITKCommon ITKBasicFilters)
-
-
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e061b18..df2b695 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,13 +54,13 @@ endforeach()
# These four fields should be modified when versions change
SET(SNAP_VERSION_MAJOR 3)
-SET(SNAP_VERSION_MINOR 4)
+SET(SNAP_VERSION_MINOR 6)
SET(SNAP_VERSION_PATCH 0)
SET(SNAP_VERSION_QUALIFIER "")
# These fields should also be modified each time
-SET(SNAP_VERSION_RELEASE_DATE "20151130")
-SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "Nov 30, 2015")
+SET(SNAP_VERSION_RELEASE_DATE "20170401")
+SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "Apr 1, 2017")
# This field should only change when the format of the settings files changes
# in a non backwards-compatible way
@@ -86,6 +86,17 @@ get_git_branch(SNAP_VERSION_GIT_BRANCH)
MESSAGE(STATUS " GIT Info: BRANCH ${SNAP_VERSION_GIT_BRANCH}, SHA: ${SNAP_VERSION_GIT_SHA1}")
#--------------------------------------------------------------------------------
+# ENSURE THAT SUBMODULES HAVE BEEN INITIALIZED AND UPDATED
+#--------------------------------------------------------------------------------
+if(NOT EXISTS "${SNAP_SOURCE_DIR}/Submodules/c3d/CMakeLists.txt")
+ MESSAGE(SEND_ERROR "Submodule c3d has not been initialized/updated. Git users, see README.git")
+endif()
+
+if(NOT EXISTS "${SNAP_SOURCE_DIR}/Submodules/greedy/CMakeLists.txt")
+ MESSAGE(SEND_ERROR "Submodule greedy has not been initialized/updated. Git users, see README.git")
+endif()
+
+#--------------------------------------------------------------------------------
# FIND PACKAGES IF BUILDING OUTSIDE INSIGHTAPPLICATIONS
#--------------------------------------------------------------------------------
@@ -236,8 +247,7 @@ SET(LOGIC_CXX
Logic/Preprocessing/GMM/GaussianMixtureModel.cxx
Logic/Preprocessing/GMM/KMeansPlusPlus.cxx
Logic/Preprocessing/GMM/UnsupervisedClustering.cxx
- Logic/Preprocessing/RandomForest/RFClassificationEngine.cxx
- Logic/Preprocessing/RandomForest/RandomForestClassifier.cxx
+ Logic/Preprocessing/RFClassificationEngine.cxx
Logic/Preprocessing/Texture/MomentTextures.cxx
Logic/Slicing/IntensityCurveVTK.cxx
Logic/Slicing/IntensityToColorLookupTableImageFilter.cxx
@@ -253,6 +263,7 @@ SET(LOGIC_HEADERS
Common/CommandLineArgumentParser.h
Common/Credits.h
Common/HistoryManager.h
+ Common/ImageFunctions.h
Common/IPCHandler.h
Common/IRISException.h
Common/IRISVectorTypes.h
@@ -264,6 +275,8 @@ SET(LOGIC_HEADERS
Common/ITKExtras/itkBinaryDiamondStructuringElement.txx
Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.h
Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.txx
+ Common/ITKExtras/itkMorphologicalContourInterpolator.h
+ Common/ITKExtras/itkMorphologicalContourInterpolator.hxx
Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.h
Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx
Common/ITKExtras/itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.h
@@ -307,6 +320,7 @@ SET(LOGIC_HEADERS
Logic/Framework/LayerAssociation.h
Logic/Framework/LayerAssociation.txx
Logic/Framework/LayerIterator.h
+ Logic/Framework/SegmentationUpdateIterator.h
Logic/Framework/SNAPImageData.h
Logic/Framework/UndoDataManager.h
Logic/Framework/UndoDataManager.txx
@@ -316,6 +330,17 @@ SET(LOGIC_HEADERS
Logic/ImageWrapper/ImageWrapper.h
Logic/ImageWrapper/ImageWrapperBase.h
Logic/ImageWrapper/ImageWrapperTraits.h
+ Logic/ImageWrapper/VectorToScalarImageAccessor.h
+ Logic/RLEImage/RLEImage.h
+ Logic/RLEImage/RLEImage.txx
+ Logic/RLEImage/RLEImageConstIterator.h
+ Logic/RLEImage/RLEImageIterator.h
+ Logic/RLEImage/RLEImageRegionConstIterator.h
+ Logic/RLEImage/RLEImageRegionIterator.h
+ Logic/RLEImage/RLEImageScanlineConstIterator.h
+ Logic/RLEImage/RLEImageScanlineIterator.h
+ Logic/RLEImage/RLERegionOfInterestImageFilter.h
+ Logic/RLEImage/RLERegionOfInterestImageFilter.txx
Logic/ImageWrapper/InputSelectionImageFilter.h
Logic/ImageWrapper/LabelToRGBAFilter.h
Logic/ImageWrapper/NativeIntensityMappingPolicy.h
@@ -336,8 +361,6 @@ SET(LOGIC_HEADERS
Logic/LevelSet/SNAPLevelSetFunction.txx
Logic/LevelSet/SNAPLevelSetStopAndGoFilter.h
Logic/LevelSet/SNAPLevelSetStopAndGoFilter.txx
- Logic/LevelSet/SignedDistanceFilter.h
- Logic/LevelSet/SignedDistanceFilter.txx
Logic/LevelSet/SnakeParameters.h
Logic/Mesh/AllPurposeProgressAccumulator.h
Logic/Mesh/GuidedMeshIO.h
@@ -352,8 +375,6 @@ SET(LOGIC_HEADERS
Logic/Preprocessing/GMMClassifyImageFilter.h
Logic/Preprocessing/GMMClassifyImageFilter.txx
Logic/Preprocessing/PreprocessingFilterConfigTraits.h
- Logic/Preprocessing/RandomForestClassifyImageFilter.h
- Logic/Preprocessing/RandomForestClassifyImageFilter.txx
Logic/Preprocessing/SlicePreviewFilterWrapper.h
Logic/Preprocessing/SlicePreviewFilterWrapper.txx
Logic/Preprocessing/SmoothBinaryThresholdImageFilter.h
@@ -364,15 +385,18 @@ SET(LOGIC_HEADERS
Logic/Preprocessing/GMM/GaussianMixtureModel.h
Logic/Preprocessing/GMM/KMeansPlusPlus.h
Logic/Preprocessing/GMM/UnsupervisedClustering.h
- Logic/Preprocessing/RandomForest/RFClassificationEngine.h
- Logic/Preprocessing/RandomForest/RandomForestClassifier.h
Logic/Preprocessing/Texture/MomentTextures.h
+ Logic/Slicing/ImageRegionConstIteratorWithIndexOverride.h
+ Logic/Slicing/FastLinearInterpolator.h
Logic/Slicing/IRISSlicer.h
Logic/Slicing/IRISSlicer.txx
+ Logic/Slicing/IRISSlicer_RLE.txx
Logic/Slicing/IntensityCurveInterface.h
Logic/Slicing/IntensityCurveVTK.h
Logic/Slicing/IntensityToColorLookupTableImageFilter.h
Logic/Slicing/LookupTableIntensityMappingFilter.h
+ Logic/Slicing/NonOrthogonalSlicer.h
+ Logic/Slicing/NonOrthogonalSlicer.txx
Logic/Slicing/RGBALookupTableIntensityMappingFilter.h
)
@@ -390,8 +414,9 @@ SET(UI_GENERIC_CXX
GUI/Model/GlobalUIModel.cxx
GUI/Model/ImageIOWizardModel.cxx
GUI/Model/ImageInfoModel.cxx
- GUI/Model/ImageRegistrationManager.cxx
GUI/Model/IntensityCurveModel.cxx
+ GUI/Model/InteractiveRegistrationModel.cxx
+ GUI/Model/InterpolateLabelModel.cxx
GUI/Model/LabelEditorModel.cxx
GUI/Model/LayerGeneralPropertiesModel.cxx
GUI/Model/LayerTableRowModel.cxx
@@ -402,6 +427,7 @@ SET(UI_GENERIC_CXX
GUI/Model/PaintbrushSettingsModel.cxx
GUI/Model/PolygonDrawingModel.cxx
GUI/Model/PolygonSettingsModel.cxx
+ GUI/Model/RegistrationModel.cxx
GUI/Model/ReorientImageModel.cxx
GUI/Model/SaveModifiedLayersModel.cxx
GUI/Model/SliceWindowCoordinator.cxx
@@ -431,6 +457,7 @@ SET(UI_GENERIC_CXX
GUI/Renderer/PaintbrushRenderer.cxx
GUI/Renderer/PolygonDrawingRenderer.cxx
GUI/Renderer/PolygonScanConvert.cxx
+ GUI/Renderer/RegistrationRenderer.cxx
GUI/Renderer/SliceWindowDecorationRenderer.cxx
GUI/Renderer/SnakeParameterPreviewRenderer.cxx
GUI/Renderer/SnakeROIRenderer.cxx
@@ -460,8 +487,9 @@ SET(UI_GENERIC_HEADERS
GUI/Model/GlobalUIModel.h
GUI/Model/ImageInfoModel.h
GUI/Model/ImageIOWizardModel.h
- GUI/Model/ImageRegistrationManager.h
GUI/Model/IntensityCurveModel.h
+ GUI/Model/InteractiveRegistrationModel.h
+ GUI/Model/InterpolateLabelModel.h
GUI/Model/LabelEditorModel.h
GUI/Model/LayerGeneralPropertiesModel.h
GUI/Model/LayerSelectionModel.h
@@ -472,6 +500,7 @@ SET(UI_GENERIC_HEADERS
GUI/Model/PaintbrushSettingsModel.h
GUI/Model/PolygonSettingsModel.h
GUI/Model/PolygonDrawingModel.h
+ GUI/Model/RegistrationModel.h
GUI/Model/ReorientImageModel.h
GUI/Model/SaveModifiedLayersModel.h
GUI/Model/SNAPUIFlag.h
@@ -504,6 +533,7 @@ SET(UI_GENERIC_HEADERS
GUI/Renderer/PaintbrushRenderer.h
GUI/Renderer/PolygonDrawingRenderer.h
GUI/Renderer/PolygonScanConvert.h
+ GUI/Renderer/RegistrationRenderer.h
GUI/Renderer/SliceWindowDecorationRenderer.h
GUI/Renderer/SnakeParameterPreviewRenderer.h
GUI/Renderer/SnakeROIRenderer.h
@@ -527,6 +557,7 @@ SET(UI_QT_CXX
GUI/Qt/Components/ContrastInspector.cxx
GUI/Qt/Components/CursorInspector.cxx
GUI/Qt/Components/DisplayLayoutInspector.cxx
+ GUI/Qt/Components/DICOMListingTable.cxx
GUI/Qt/Components/FileChooserPanelWithHistory.cxx
GUI/Qt/Components/HistoryQListModel.cxx
GUI/Qt/Components/ImageInfoInspector.cxx
@@ -553,6 +584,7 @@ SET(UI_QT_CXX
GUI/Qt/Components/SnakeToolROIPanel.cxx
GUI/Qt/Components/SnakeWizardPanel.cxx
GUI/Qt/Components/SNAPComponent.cxx
+ GUI/Qt/Components/SNAPQApplication.cxx
GUI/Qt/Components/SNAPQtCommon.cxx
GUI/Qt/Components/SliceViewPanel.cxx
GUI/Qt/Components/SynchronizationInspector.cxx
@@ -574,6 +606,7 @@ SET(UI_QT_CXX
GUI/Qt/View/QtVTKRenderWindowBox.cxx
GUI/Qt/View/PaintbrushInteractionMode.cxx
GUI/Qt/View/PolygonDrawingInteractionMode.cxx
+ GUI/Qt/View/RegistrationInteractionMode.cxx
GUI/Qt/View/SliceWindowInteractionDelegateWidget.cxx
GUI/Qt/View/SnakeROIInteractionMode.cxx
GUI/Qt/View/ThumbnailInteractionMode.cxx
@@ -582,7 +615,7 @@ SET(UI_QT_CXX
GUI/Qt/Windows/ImageIODialog.cxx
GUI/Qt/Windows/ImageIOWizard.cxx
GUI/Qt/Windows/ImageIOWizard/OverlayRolePage.cxx
- GUI/Qt/Windows/ImageIOWizard/RegistrationPage.cxx
+ GUI/Qt/Windows/InterpolateLabelsDialog.cxx
GUI/Qt/Windows/LabelEditorDialog.cxx
GUI/Qt/Windows/LayerInspectorDialog.cxx
GUI/Qt/Windows/LabelSelectionPopup.cxx
@@ -601,6 +634,7 @@ SET(UI_QT_CXX
GUI/Qt/Windows/MeshExportWizard/MeshExportWizard.cxx
GUI/Qt/Windows/MeshExportWizard/MeshExportModePage.cxx
GUI/Qt/Windows/MeshExportWizard/MeshExportBrowsePage.cxx
+ GUI/Qt/Windows/Registration/RegistrationDialog.cxx
Testing/GUI/Qt/SNAPTestQt.cxx
)
@@ -612,6 +646,7 @@ SET(UI_MOC_HEADERS
GUI/Qt/Components/ColorMapInspector.h
GUI/Qt/Components/ContrastInspector.h
GUI/Qt/Components/CursorInspector.h
+ GUI/Qt/Components/DICOMListingTable.h
GUI/Qt/Components/DisplayLayoutInspector.h
GUI/Qt/Components/FileChooserPanelWithHistory.h
GUI/Qt/Components/HistoryQListModel.h
@@ -637,6 +672,7 @@ SET(UI_MOC_HEADERS
GUI/Qt/Components/SnakeToolROIPanel.h
GUI/Qt/Components/SnakeWizardPanel.h
GUI/Qt/Components/SNAPComponent.h
+ GUI/Qt/Components/SNAPQApplication.h
GUI/Qt/Components/SliceViewPanel.h
GUI/Qt/Components/SynchronizationInspector.h
GUI/Qt/Components/ViewPanel3D.h
@@ -657,15 +693,17 @@ SET(UI_MOC_HEADERS
GUI/Qt/View/QtVTKRenderWindowBox.h
GUI/Qt/View/PaintbrushInteractionMode.h
GUI/Qt/View/PolygonDrawingInteractionMode.h
+ GUI/Qt/View/RegistrationInteractionMode.h
GUI/Qt/View/SliceWindowInteractionDelegateWidget.h
GUI/Qt/View/SnakeROIInteractionMode.h
+ GUI/Qt/View/TestOpenGLDialog.h
GUI/Qt/View/ThumbnailInteractionMode.h
GUI/Qt/Windows/AboutDialog.h
GUI/Qt/Windows/DropActionDialog.h
GUI/Qt/Windows/ImageIODialog.h
GUI/Qt/Windows/ImageIOWizard.h
GUI/Qt/Windows/ImageIOWizard/OverlayRolePage.h
- GUI/Qt/Windows/ImageIOWizard/RegistrationPage.h
+ GUI/Qt/Windows/InterpolateLabelsDialog.h
GUI/Qt/Windows/LabelEditorDialog.h
GUI/Qt/Windows/LayerInspectorDialog.h
GUI/Qt/Windows/LabelSelectionPopup.h
@@ -683,15 +721,17 @@ SET(UI_MOC_HEADERS
GUI/Qt/Windows/MeshExportWizard/MeshExportWizard.h
GUI/Qt/Windows/MeshExportWizard/MeshExportModePage.h
GUI/Qt/Windows/MeshExportWizard/MeshExportBrowsePage.h
+ GUI/Qt/Windows/Registration/RegistrationDialog.h
Testing/GUI/Qt/SNAPTestQt.h
)
# These UI headers don't need to be MOC'd
SET(UI_NONMOC_HEADERS
- GUI/Qt/Components/SNAPQtCommon.h
+ GUI/Qt/Components/ProcessEventsITKCommand.h
GUI/Qt/Components/QtCursorOverride.h
GUI/Qt/Components/QtRendererPlatformSupport.h
GUI/Qt/Components/QtReporterDelegates.h
+ GUI/Qt/Components/SNAPQtCommon.h
GUI/Qt/Coupling/QtAbstractButtonCoupling.h
GUI/Qt/Coupling/QtAbstractItemViewCoupling.h
GUI/Qt/Coupling/QtActionCoupling.h
@@ -741,7 +781,7 @@ SET(UI_FORMS
GUI/Qt/Windows/DropActionDialog.ui
GUI/Qt/Windows/ImageIODialog.ui
GUI/Qt/Windows/ImageIOWizard/OverlayRolePage.ui
- GUI/Qt/Windows/ImageIOWizard/RegistrationPage.ui
+ GUI/Qt/Windows/InterpolateLabelsDialog.ui
GUI/Qt/Windows/LabelEditorDialog.ui
GUI/Qt/Windows/LayerInspectorDialog.ui
GUI/Qt/Windows/LabelSelectionPopup.ui
@@ -759,6 +799,7 @@ SET(UI_FORMS
GUI/Qt/Windows/MeshExportWizard/MeshExportWizard.ui
GUI/Qt/Windows/MeshExportWizard/MeshExportModePage.ui
GUI/Qt/Windows/MeshExportWizard/MeshExportBrowsePage.ui
+ GUI/Qt/Windows/Registration/RegistrationDialog.ui
)
SET(UI_RESOURCES
@@ -831,9 +872,12 @@ ELSE( CMAKE_GENERATOR MATCHES "Visual Studio 6" )
${SNAP_SOURCE_DIR}/Logic/Mesh
${SNAP_SOURCE_DIR}/Logic/Preprocessing
${SNAP_SOURCE_DIR}/Logic/Preprocessing/GMM
- ${SNAP_SOURCE_DIR}/Logic/Preprocessing/RandomForest
${SNAP_SOURCE_DIR}/Logic/Preprocessing/Texture
+ ${SNAP_SOURCE_DIR}/Logic/RLEImage
${SNAP_SOURCE_DIR}/Logic/Slicing
+ ${SNAP_SOURCE_DIR}/Submodules/c3d/itkextras/RandomForest
+ ${SNAP_SOURCE_DIR}/Submodules/c3d/api
+ ${SNAP_SOURCE_DIR}/Submodules/greedy/src
${SNAP_SOURCE_DIR}/GUI
${SNAP_SOURCE_DIR}/GUI/Model
${SNAP_SOURCE_DIR}/GUI/Renderer
@@ -848,6 +892,7 @@ ELSE( CMAKE_GENERATOR MATCHES "Visual Studio 6" )
${SNAP_SOURCE_DIR}/GUI/Qt/View
${SNAP_SOURCE_DIR}/GUI/Qt/Windows
${SNAP_SOURCE_DIR}/GUI/Qt/Windows/MeshExportWizard
+ ${SNAP_SOURCE_DIR}/GUI/Qt/Windows/Registration
${SNAP_SOURCE_DIR}/Testing/Logic
${SNAP_SOURCE_DIR}/Testing/GUI/Qt
${SNAP_BINARY_DIR}
@@ -861,10 +906,12 @@ ENDIF( CMAKE_GENERATOR MATCHES "Visual Studio 6" )
# Compiler-specific warinings
#--------------------------------------------------------------------------------
-# Get rid of this ridiculous warning in VS8
-IF( CMAKE_GENERATOR MATCHES "Visual Studio 8" OR CMAKE_GENERATOR MATCHES "Visual Studio 9" OR CMAKE_GENERATOR MATCHES "Visual Studio 10" )
+# Get rid of this ridiculous warning in VS8+
+IF( MSVC )
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
-ENDIF( CMAKE_GENERATOR MATCHES "Visual Studio 8" OR CMAKE_GENERATOR MATCHES "Visual Studio 9" OR CMAKE_GENERATOR MATCHES "Visual Studio 10" )
+ ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
+ ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)
+ENDIF( MSVC )
IF( CMAKE_GENERATOR MATCHES "^NMake" OR CMAKE_GENERATOR MATCHES "^Visual Studio" )
ADD_DEFINITIONS(-DNOMINMAX)
@@ -878,24 +925,14 @@ ENDIF( CMAKE_GENERATOR MATCHES "^NMake" OR CMAKE_GENERATOR MATCHES "^Visual Stud
SET(SNAP_ITK_LIBS ${ITK_LIBRARIES})
# Core VTK libraries
-SET(SNAP_VTK_LIBS
- vtkChartsCore
- vtkCommonCore
- vtkRenderingCore
- vtkRenderingFreeType
- vtkRenderingFreeTypeOpenGL
- vtkRenderingOpenGL
- vtkRenderingVolume
- vtkRenderingVolumeOpenGL
- vtkFiltersCore
- vtkImagingCore
- vtkViewsCore
- vtkViewsContext2D
- vtkIOCore
- vtkIOExport
- vtkIOGeometry
- vtkIOLegacy
-)
+SET(SNAP_VTK_LIBS ${VTK_LIBRARIES})
+
+# Libraries provided by the submodules
+SET(SNAP_SUBMODULE_LIBS
+ cnd_api
+ cnd_driver
+ cnd_adapters
+ greedyapi)
# System libraries
SET(SNAP_SYSTEM_LIBS
@@ -906,12 +943,32 @@ SET(SNAP_SYSTEM_LIBS
# Designate the external libraries used by SNAP
SET(SNAP_EXTERNAL_LIBS
+ ${SNAP_SUBMODULE_LIBS}
${SNAP_ITK_LIBS}
${SNAP_VTK_LIBS}
${SNAP_SYSTEM_LIBS}
)
#--------------------------------------------------------------------------------
+# Include C3D Library as submodule
+#--------------------------------------------------------------------------------
+SET(CONVERT3D_SOURCE_DIR ${SNAP_SOURCE_DIR}/Submodules/c3d)
+SET(CONVERT3D_BINARY_DIR ${SNAP_BINARY_DIR}/Submodules/c3d)
+
+SET(CONVERT3D_BUILD_AS_SUBPROJECT ON)
+ADD_SUBDIRECTORY(${CONVERT3D_SOURCE_DIR})
+
+#--------------------------------------------------------------------------------
+# Include Greedy Library as submodule
+#--------------------------------------------------------------------------------
+SET(GREEDY_TOOL_SOURCE_DIR ${SNAP_SOURCE_DIR}/Submodules/greedy)
+SET(GREEDY_TOOL_BINARY_DIR ${SNAP_BINARY_DIR}/Submodules/greedy)
+
+SET(GREEDY_TOOL_BUILD_AS_SUBPROJECT ON)
+ADD_SUBDIRECTORY(${GREEDY_TOOL_SOURCE_DIR})
+
+
+#--------------------------------------------------------------------------------
# Define SNAP library targets (logic and UI)
#--------------------------------------------------------------------------------
@@ -1009,11 +1066,79 @@ MARK_AS_ADVANCED(SNAP_GUI_TEST_ACCEL)
# The list of Qt animated GUI tests
SET(GUI_TESTS
Workspace
+ DiffSpace
ProbeIntensity
RegionCompetition
RandomForest)
SET(TESTDATA_DIR "${SNAP_SOURCE_DIR}/Testing/TestData")
+set(TEMP ${SNAP_BINARY_DIR}/Testing/Temporary)
+
+ADD_EXECUTABLE(SlicingPerformanceTest Testing/Logic/SlicingPerformanceTest.cxx)
+TARGET_LINK_LIBRARIES(SlicingPerformanceTest ${ITK_LIBRARIES})
+
+ADD_EXECUTABLE(testRLE Testing/Logic/testRLE.cxx)
+TARGET_LINK_LIBRARIES(testRLE ${ITK_LIBRARIES})
+
+ADD_EXECUTABLE(iteratorTests
+ Testing/Logic/itkRegionOfInterestImageFilterTest.cxx
+ Testing/Logic/itkIteratorTests.cxx
+ Testing/Logic/itkImageIteratorsForwardBackwardTest.cxx
+ Testing/Logic/itkImageIteratorTest.cxx
+ Testing/Logic/itkImageIteratorWithIndexTest.cxx
+ Testing/Logic/itkImageRegionConstIteratorWithOnlyIndexTest.cxx
+ Testing/Logic/itkImageRegionIteratorTest.cxx
+ Testing/Logic/itkImageScanlineIteratorTest1.cxx
+ Testing/Logic/iteratorTests.cxx)
+TARGET_LINK_LIBRARIES(iteratorTests ${ITK_LIBRARIES})
+
+add_test(NAME BasicSlicingTestX39 COMMAND itkTestDriver
+ --compare ${TESTDATA_DIR}/X39.nii.gz ${TEMP}/X39.nii.gz
+ $<TARGET_FILE:SlicingPerformanceTest>
+ ${TESTDATA_DIR}/MRIcrop-seg.gipl.gz
+ ${TEMP}/X39.nii.gz
+ X 39 irisRLE
+)
+
+add_test(NAME BasicSlicingTestY55 COMMAND itkTestDriver
+ --compare ${TESTDATA_DIR}/Y55.nii.gz ${TEMP}/Y55.nii.gz
+ $<TARGET_FILE:SlicingPerformanceTest>
+ ${TESTDATA_DIR}/MRIcrop-seg.gipl.gz
+ ${TEMP}/Y55.nii.gz
+ Y 55 irisRLE
+)
+
+add_test(NAME BasicSlicingTestZ32 COMMAND itkTestDriver
+ --compare ${TESTDATA_DIR}/Z32.nii.gz ${TEMP}/Z32.nii.gz
+ $<TARGET_FILE:SlicingPerformanceTest>
+ ${TESTDATA_DIR}/MRIcrop-seg.gipl.gz
+ ${TEMP}/Z32.nii.gz
+ Z 32 irisRLE
+)
+
+add_test(NAME SlicingPerformanceTestX300 COMMAND itkTestDriver
+ --compare ${TESTDATA_DIR}/X300.mha ${TEMP}/X300.mha
+ $<TARGET_FILE:SlicingPerformanceTest>
+ ${TESTDATA_DIR}/vb-seg.mha
+ ${TEMP}/X300.mha
+ X 300 irisRLE
+)
+
+add_test(NAME SlicingPerformanceTestY300 COMMAND itkTestDriver
+ --compare ${TESTDATA_DIR}/Y300.mha ${TEMP}/Y300.mha
+ $<TARGET_FILE:SlicingPerformanceTest>
+ ${TESTDATA_DIR}/vb-seg.mha
+ ${TEMP}/Y300.mha
+ Y 300 irisRLE
+)
+
+add_test(NAME SlicingPerformanceTestZ150 COMMAND itkTestDriver
+ --compare ${TESTDATA_DIR}/Z150.mha ${TEMP}/Z150.mha
+ $<TARGET_FILE:SlicingPerformanceTest>
+ ${TESTDATA_DIR}/vb-seg.mha
+ ${TEMP}/Z150.mha
+ Z 150 irisRLE
+)
# Set up a test for each GUI test
FOREACH(GUI_TEST ${GUI_TESTS})
@@ -1232,10 +1357,13 @@ ELSE()
IF(SNAP_PACKAGE_QT_PLUGINS)
get_property(QT_XCB_PLUGIN TARGET Qt5::QXcbIntegrationPlugin PROPERTY LOCATION_RELEASE)
+ get_property(QT_XCB_GLX_PLUGIN TARGET Qt5::QXcbEglIntegrationPlugin PROPERTY LOCATION_RELEASE)
+ get_property(QT_XCB_EGL_PLUGIN TARGET Qt5::QXcbGlxIntegrationPlugin PROPERTY LOCATION_RELEASE)
get_property(QT_GIF_PLUGIN TARGET Qt5::QGifPlugin PROPERTY LOCATION_RELEASE)
# Install the plugin
- install_qt5_executable(${SNAP_MAIN_INSTALL_DIR}/${SNAP_EXE} "${QT_XCB_PLUGIN};${QT_GIF_PLUGIN}")
+ install_qt5_executable(${SNAP_MAIN_INSTALL_DIR}/${SNAP_EXE}
+ "${QT_XCB_PLUGIN};${QT_GIF_PLUGIN};${QT_XCB_GLX_PLUGIN};${QT_XCB_EGL_PLUGIN}")
ELSE(SNAP_PACKAGE_QT_PLUGINS)
diff --git a/CTestConfig.cmake b/CTestConfig.cmake
index 2a7898b..1915c65 100644
--- a/CTestConfig.cmake
+++ b/CTestConfig.cmake
@@ -6,10 +6,10 @@
## ENABLE_TESTING()
## INCLUDE(CTest)
-set(CTEST_PROJECT_NAME "ITK-SNAP 3.4")
-set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC")
+set(CTEST_PROJECT_NAME "ITK-SNAP master")
+set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "itksnap.org")
-set(CTEST_DROP_LOCATION "/cdash/submit.php?project=ITK-SNAP+3.4")
+set(CTEST_DROP_LOCATION "/cdash/submit.php?project=ITK-SNAP+master")
set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Common/CommandLineArgumentParser.cxx b/Common/CommandLineArgumentParser.cxx
index b8488f8..19d7088 100644
--- a/Common/CommandLineArgumentParser.cxx
+++ b/Common/CommandLineArgumentParser.cxx
@@ -162,7 +162,7 @@ CommandLineArgumentParseResult
::GetNumberOfOptionParameters(const char *option)
{
assert(IsOptionPresent(option));
- return m_OptionMap[string(option)].size();
+ return (int) m_OptionMap[string(option)].size();
}
void
diff --git a/Common/HistoryManager.cxx b/Common/HistoryManager.cxx
index f2b3182..7a98694 100644
--- a/Common/HistoryManager.cxx
+++ b/Common/HistoryManager.cxx
@@ -76,6 +76,37 @@ void HistoryManager
model->SetValue(array);
}
+void HistoryManager::PrintHistory(std::ostream &sout)
+{
+ // Print global history
+ sout << "GLOBAL HISTORY" << std::endl;
+ for(HistoryMap::const_iterator it = m_GlobalHistory.begin();
+ it != m_GlobalHistory.end(); ++it)
+ {
+ sout << " " << it->first << std::endl;
+ HistoryListType hlist = it->second->GetValue();
+ for(HistoryListType::const_iterator hit = hlist.begin();
+ hit != hlist.end(); ++hit)
+ {
+ sout << " " << *hit << std::endl;
+ }
+ }
+
+ sout << "LOCAL HISTORY" << std::endl;
+ for(HistoryMap::const_iterator it = m_LocalHistory.begin();
+ it != m_LocalHistory.end(); ++it)
+ {
+ sout << " " << it->first << std::endl;
+ HistoryListType hlist = it->second->GetValue();
+ for(HistoryListType::const_iterator hit = hlist.begin();
+ hit != hlist.end(); ++hit)
+ {
+ sout << " " << *hit << std::endl;
+ }
+ }
+
+}
+
void HistoryManager::UpdateHistory(
const std::string &category,
const std::string &filename,
diff --git a/Common/HistoryManager.h b/Common/HistoryManager.h
index f0649fb..e00b607 100644
--- a/Common/HistoryManager.h
+++ b/Common/HistoryManager.h
@@ -109,6 +109,9 @@ public:
HistoryManager();
+ void PrintHistory(std::ostream &sout);
+
+
protected:
static const unsigned int HISTORY_SIZE_LOCAL, HISTORY_SIZE_GLOBAL;
@@ -128,6 +131,7 @@ protected:
void UpdateHistoryList(
ConcreteHistoryModel *model, const std::string &file, unsigned int maxsize);
+
// The system interface class
SystemInterface *m_SystemInterface;
diff --git a/Common/IPCHandler.cxx b/Common/IPCHandler.cxx
index 7f3b665..8a93758 100644
--- a/Common/IPCHandler.cxx
+++ b/Common/IPCHandler.cxx
@@ -43,7 +43,7 @@ void IPCHandler::Attach(const char *path, short version, size_t message_size)
#ifdef WIN32
// Create a shared memory block (key based on the preferences file)
m_Handle = CreateFileMapping(
- INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, msize, path);
+ INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD) msize, path);
// If the return value is NULL, something failed
if(m_Handle)
diff --git a/Common/IRISVectorTypesToITKConversion.h b/Common/IRISVectorTypesToITKConversion.h
index f5d4409..5eb2e2d 100644
--- a/Common/IRISVectorTypesToITKConversion.h
+++ b/Common/IRISVectorTypesToITKConversion.h
@@ -39,6 +39,8 @@
#include "itkSize.h"
#include "itkIndex.h"
+#include "itkContinuousIndex.h"
+#include "itkPoint.h"
/**
* Convert a VectorNi to itk::Size
@@ -70,4 +72,28 @@ to_itkIndex(const vnl_vector_fixed<T,VSize> &x)
return z;
}
+template <class T, unsigned int VSize>
+inline itk::ContinuousIndex<double, VSize>
+to_itkContinuousIndex(const vnl_vector_fixed<T,VSize> &x)
+{
+ itk::ContinuousIndex<double, VSize> z;
+
+ for(unsigned int i=0;i<VSize;i++)
+ z[i] = static_cast<double>(x(i));
+
+ return z;
+}
+
+template <class T, unsigned int VSize>
+inline itk::Point<double, VSize>
+to_itkPoint(const vnl_vector_fixed<T,VSize> &x)
+{
+ itk::Point<double, VSize> z;
+
+ for(unsigned int i=0;i<VSize;i++)
+ z[i] = static_cast<double>(x(i));
+
+ return z;
+}
+
#endif // __IRISVectorTypesToITKConversion_h_
diff --git a/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.txx b/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.txx
index 3887e3c..d4d28a7 100644
--- a/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.txx
+++ b/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.txx
@@ -19,9 +19,7 @@
#define __itkBSplineScatteredDataPointSetToImageFilter_txx
#include "itkBSplineScatteredDataPointSetToImageFilter.h"
-#include "itkImageRegionConstIteratorWithIndex.h"
-#include "itkImageRegionIterator.h"
-#include "itkImageRegionIteratorWithIndex.h"
+#include "RLEImageRegionIterator.h"
#include "itkImageDuplicator.h"
#include "itkCastImageFilter.h"
#include "itkNumericTraits.h"
diff --git a/Common/ITKExtras/itkMorphologicalContourInterpolator.h b/Common/ITKExtras/itkMorphologicalContourInterpolator.h
new file mode 100755
index 0000000..7bdd021
--- /dev/null
+++ b/Common/ITKExtras/itkMorphologicalContourInterpolator.h
@@ -0,0 +1,413 @@
+/*=========================================================================
+*
+* Copyright Insight Software Consortium
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*
+* 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.
+*
+*=========================================================================*/
+#ifndef itkMorphologicalContourInterpolator_h
+#define itkMorphologicalContourInterpolator_h
+
+#include "itkBinaryThresholdImageFilter.h"
+#include "itkConnectedComponentImageFilter.h"
+#include "itkExtractImageFilter.h"
+#include "itkImageToImageFilter.h"
+#include "itksys/hash_map.hxx"
+
+namespace itk
+{
+/** \class MorphologicalContourInterpolator
+ *
+ * \brief Interpolates contours between slices. Based on a paper by Albu et al.
+ *
+ * \par Inputs and Outputs
+ * This is an image-to-image filter. The dimensionality is 3D or higher.
+ * Input contains an image with some slices segmented, usually manually.
+ * The output has all in-between slices also segmented.
+ *
+ * \par Parameters
+ * Slices are detected at positions where a pixel exists with same labeled
+ * neighbors in slice and only clear (0) neighbors perpendicular to the slice.
+ * If default behaviour is unwanted, contour indices can be set
+ * by enabling UseCustomSlicePositions and calling SetLabeledSliceIndices.
+ *
+ * Filter can be restricted to run along only one axis, and/or to interpolate
+ * just one label.
+ *
+ * Since optimal alignment between slices would require exhaustive search,
+ * the default is to use heuristic (breadth first search starting from centroid).
+ *
+ * There is also an alternative algorithm based on distance transform approach.
+ * It is slightly faster, but it can jump across a twisty shape (not geodesic).
+ *
+ * Reference:
+ * Albu AB, Beugeling T, Laurendeau D. "A morphology-based approach for
+ * interslice interpolation of anatomical slices from volumetric images."
+ * IEEE Trans Biomed Eng. 2008 Aug;55(8):2022-38. DOI:10.1109/TBME.2008.921158
+ *
+ * Acknowledgement:
+ * This work is supported by NIH grant R01 EB014346, "Continued development
+ * and maintenance of the ITK-SNAP 3D image segmentation software."
+ *
+ * \ingroup MorphologicalContourInterpolation
+ */
+template< typename TImage >
+class MorphologicalContourInterpolator:
+ public ImageToImageFilter< TImage, TImage >
+{
+ template< typename T >
+ friend class MorphologicalContourInterpolatorParallelInvoker;
+
+public:
+ /** Standard class typedefs. */
+ typedef MorphologicalContourInterpolator Self;
+ typedef ImageToImageFilter< TImage, TImage > Superclass;
+ typedef SmartPointer< Self > Pointer;
+ typedef Image< typename TImage::PixelType, TImage::ImageDimension - 1 > SliceType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro( Self );
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro( MorphologicalContourInterpolator, ImageToImageFilter );
+
+ /** Interpolate only this label. Interpolates all labels if set to 0 (default). */
+ itkSetMacro( Label, typename TImage::PixelType );
+
+ /** Which label is interpolated. 0 means all labels (default). */
+ itkGetMacro( Label, typename TImage::PixelType );
+
+ /** Which label is interpolated. 0 means all labels (default). */
+ itkGetConstMacro( Label, typename TImage::PixelType );
+
+ /** Interpolate only along this axis. Interpolates along all axes if set to -1 (default). */
+ itkSetMacro( Axis, int );
+
+ /** Axis of interpolation. -1 means interpolation along all axes (default). */
+ itkGetMacro( Axis, int );
+
+ /** Axis of interpolation. -1 means interpolation along all axes (default). */
+ itkGetConstMacro( Axis, int );
+
+ /** Heuristic alignment of regions for interpolation is faster than optimal alignment.
+ * Default is heuristic. */
+ itkSetMacro( HeuristicAlignment, bool );
+
+ /** Heuristic alignment of regions for interpolation is faster than optimal alignment.
+ * Default is heuristic. */
+ itkGetMacro( HeuristicAlignment, bool );
+
+ /** Heuristic alignment of regions for interpolation is faster than optimal alignment.
+ * Default is heuristic. */
+ itkGetConstMacro( HeuristicAlignment, bool );
+
+ /** Using distance transform instead of repeated dilations to calculate
+ * the median contour is slightly faster, but produces lower quality interpolations.
+ * Default is OFF (that is, use repeated dilations). */
+ itkSetMacro( UseDistanceTransform, bool );
+
+ /** Using distance transform instead of repeated dilations to calculate
+ * the median contour is slightly faster, but produces lower quality interpolations.
+ * Default is OFF (that is, use repeated dilations). */
+ itkGetMacro( UseDistanceTransform, bool );
+
+ /** Using distance transform instead of repeated dilations to calculate
+ * the median contour is slightly faster, but produces lower quality interpolations.
+ * Default is OFF (that is, use repeated dilations). */
+ itkGetConstMacro( UseDistanceTransform, bool );
+
+ /** Use custom slice positions (not slice auto-detection).
+ * SetLabeledSliceIndices has to be called prior to Update(). */
+ itkSetMacro( UseCustomSlicePositions, bool );
+
+ /** Use custom slice positions (not slice auto-detection).
+ * SetLabeledSliceIndices has to be called prior to Update(). */
+ itkGetMacro( UseCustomSlicePositions, bool );
+
+ /** Use custom slice positions (not slice auto-detection).
+ * SetLabeledSliceIndices has to be called prior to Update(). */
+ itkGetConstMacro( UseCustomSlicePositions, bool );
+
+ /** Use ball instead of default cross structuring element for repeated dilations. */
+ void
+ SetUseBallStructuringElement( bool useBall )
+ {
+ if ( useBall != m_UseBallStructuringElement )
+ {
+ m_UseBallStructuringElement = useBall;
+ m_ConnectedComponents->SetFullyConnected( useBall );
+ this->Modified();
+ }
+ }
+
+ /** Use ball instead of default cross structuring element for repeated dilations. */
+ itkGetMacro( UseBallStructuringElement, bool );
+
+ /** Use ball instead of default cross structuring element for repeated dilations. */
+ itkGetConstMacro( UseBallStructuringElement, bool );
+
+ /** If there is a pixel whose all 4-way neighbors belong the the same label
+ except along one axis, and along that axis its neighbors are 0 (background),
+ then that axis should be interpolated along. Interpolation is possible
+ along more than one axis. Updates LabeledSliceIndices.*/
+ void
+ DetermineSliceOrientations();
+
+ /** An std::set of slice indices which need to be interpolated. */
+ typedef std::set< typename TImage::IndexValueType > SliceSetType;
+
+ /** Clears all custom slice positions. */
+ void
+ ClearLabeledSliceIndices()
+ {
+ m_LabeledSlices.clear();
+ m_LabeledSlices.resize( TImage::ImageDimension );
+ this->Modified();
+ }
+
+ /** If default slice detection is not wanted, slice indices
+ * between which interpolation is done can be set using this method. */
+ void
+ SetLabeledSliceIndices( unsigned int axis,
+ typename TImage::PixelType label,
+ const std::vector< typename TImage::IndexValueType >& indices )
+ {
+ m_LabeledSlices[axis][label] = SliceSetType().insert( indices.begin(), indices.end() );
+ this->Modified();
+ }
+
+ /** If default slice detection is not wanted, slice indices
+ * between which interpolation is done can be set using this method. */
+ void
+ SetLabeledSliceIndices( unsigned int axis, typename TImage::PixelType label, const SliceSetType& indices )
+ {
+ m_LabeledSlices[axis][label] = indices;
+ this->Modified();
+ }
+
+ /** Slice indices between which interpolation is done. */
+ SliceSetType
+ GetLabeledSliceIndices( unsigned int axis, typename TImage::PixelType label )
+ {
+ return m_LabeledSlices[axis][label];
+ }
+
+ // each label gets a set of slices in which it is present
+ typedef itksys::hash_map< typename TImage::PixelType, SliceSetType > LabeledSlicesType;
+ typedef std::vector< LabeledSlicesType > SliceIndicesType;
+
+ /** Slice indices between which interpolation is done. */
+ SliceIndicesType
+ GetLabeledSliceIndices()
+ {
+ return m_LabeledSlices;
+ }
+
+protected:
+ MorphologicalContourInterpolator();
+ ~MorphologicalContourInterpolator() {}
+ typename TImage::PixelType m_Label;
+ int m_Axis;
+ bool m_HeuristicAlignment;
+ bool m_UseDistanceTransform;
+ bool m_UseBallStructuringElement;
+ bool m_UseCustomSlicePositions;
+ IdentifierType m_MinAlignIters; // minimum number of iterations in align method
+ IdentifierType m_MaxAlignIters; // maximum number of iterations in align method
+ IdentifierType m_ThreadCount; // for thread local instances
+ SliceIndicesType m_LabeledSlices; // one for each axis
+
+ /** Derived image typedefs. */
+ typedef Image< bool, TImage::ImageDimension > BoolImageType;
+ typedef Image< float, TImage::ImageDimension - 1 > FloatSliceType;
+ typedef Image< bool, TImage::ImageDimension - 1 > BoolSliceType;
+
+ /** Are these two slices equal? */
+ bool
+ ImagesEqual( typename BoolSliceType::Pointer& a, typename BoolSliceType::Pointer& b );
+
+ /** Does the real work. */
+ virtual void
+ GenerateData() ITK_OVERRIDE;
+
+ /** Determines correspondances between two slices and calls apropriate methods. */
+ void
+ InterpolateBetweenTwo( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iconn,
+ typename SliceType::Pointer& jconn,
+ ThreadIdType threadId );
+
+ /** If interpolation is done along more than one axis,
+ the interpolations are merged using a modified "or" rule:
+ -if all interpolated images have 0 for a given pixel, the output is 0
+ -if just one image has a non-zero label, then that label is chosen
+ -if more than one image has a non-zero label, last written label is chosen */
+ void
+ InterpolateAlong( int axis, TImage* out );
+
+ /** Slice i has a region, slice j does not */
+ void
+ Extrapolate( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ ThreadIdType threadId );
+
+ /** Creates a signed distance field image. */
+ typename FloatSliceType::Pointer
+ MaurerDM( typename BoolSliceType::Pointer& inImage, ThreadIdType threadId );
+
+ /** A sequence of conditional dilations starting with begin and reaching end.
+ begin and end must cover the same region (size and index the same) */
+ std::vector< typename BoolSliceType::Pointer >
+ GenerateDilationSequence( typename BoolSliceType::Pointer& begin,
+ typename BoolSliceType::Pointer& end,
+ ThreadIdType threadId );
+
+ /** Finds an interpolating mask for these two aligned masks */
+ typename BoolSliceType::Pointer
+ FindMedianImageDilations( typename BoolSliceType::Pointer& intersection,
+ typename BoolSliceType::Pointer& iMask,
+ typename BoolSliceType::Pointer& jMask,
+ ThreadIdType threadId );
+
+ /** Finds an interpolating mask for these two aligned masks */
+ typename BoolSliceType::Pointer
+ FindMedianImageDistances( typename BoolSliceType::Pointer& intersection,
+ typename BoolSliceType::Pointer& iMask,
+ typename BoolSliceType::Pointer& jMask,
+ ThreadIdType threadId );
+
+ /** Build transition sequence and pick the median */
+ void
+ Interpolate1to1( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ typename TImage::PixelType jRegionId,
+ const typename SliceType::IndexType& translation,
+ bool recursive,
+ ThreadIdType threadId );
+
+ typedef std::vector< typename TImage::PixelType > PixelList;
+
+ /** Splits the bigger region and does N 1-to-1 interpolations */
+ void
+ Interpolate1toN( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ const PixelList& jRegionIds,
+ const typename SliceType::IndexType& translation,
+ ThreadIdType threadId );
+
+ /** Crates a translated copy of part of the image which fits in the newRegion. */
+ typename SliceType::Pointer
+ TranslateImage( typename SliceType::Pointer& image,
+ const typename SliceType::IndexType& translation,
+ typename SliceType::RegionType newRegion );
+
+ /** The returns cardingal of the symmetric distance between images.
+ The images must cover the same region */
+ IdentifierType
+ CardSymDifference( typename BoolSliceType::Pointer& shape1, typename BoolSliceType::Pointer& shape2 );
+
+ /** Copied from ImageSource and changed to allocate a cleared buffer. */
+ virtual void
+ AllocateOutputs() ITK_OVERRIDE;
+
+ /** Returns the centroid of given regions */
+ typename SliceType::IndexType
+ Centroid( typename SliceType::Pointer& conn, const PixelList& regionIds );
+
+ /** Calculates maximum intersection region for both slices given a translation.
+ Both inputs are modified so that jRegion is a translated version of iRegion. */
+ void
+ IntersectionRegions( const typename SliceType::IndexType& translation,
+ typename SliceType::RegionType& iRegion,
+ typename SliceType::RegionType& jRegion );
+
+ /** Returns number of intersecting pixels */
+ IdentifierType
+ Intersection( typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ const PixelList& jRegionIds,
+ const typename SliceType::IndexType& translation );
+
+ /** How much j needs to be translated to best align with i */
+ typename SliceType::IndexType
+ Align( typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ const PixelList& jRegionIds );
+
+ typedef itksys::hash_map< typename TImage::PixelType, typename TImage::RegionType > BoundingBoxesType;
+ BoundingBoxesType m_BoundingBoxes; // bounding box for each label
+
+ /** Calculates a bounding box of non-zero pixels. */
+ typename SliceType::RegionType
+ BoundingBox( itk::SmartPointer< SliceType > image );
+
+ /** Expands a region to incorporate the provided index.
+ * Assumes both a valid region and a valid index.
+ * It can be invoked with 2D or 3D region, hence the additional template parameter. */
+ template< typename T2 >
+ void
+ ExpandRegion( typename T2::RegionType& region, const typename T2::IndexType& index );
+
+ /** Connected components of a specified region. */
+ typename SliceType::Pointer
+ RegionedConnectedComponents( const typename TImage::RegionType& region,
+ typename TImage::PixelType label,
+ IdentifierType& objectCount );
+
+ /** Seed and mask must cover the same region (size and index the same). */
+ typename BoolSliceType::Pointer
+ Dilate1( typename BoolSliceType::Pointer& seed, typename BoolSliceType::Pointer& mask, ThreadIdType threadId );
+
+ typedef ExtractImageFilter< TImage, SliceType > RoiType;
+ typename RoiType::Pointer m_RoI;
+
+ typedef BinaryThresholdImageFilter< SliceType, BoolSliceType > BinarizerType;
+ typename BinarizerType::Pointer m_Binarizer;
+
+ typedef ConnectedComponentImageFilter< BoolSliceType, SliceType > ConnectedComponentsType;
+ typename ConnectedComponentsType::Pointer m_ConnectedComponents;
+
+private:
+ MorphologicalContourInterpolator( const Self & );
+ void operator=( const Self & );
+};
+} // namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkMorphologicalContourInterpolator.hxx"
+#endif
+
+#endif // itkMorphologicalContourInterpolator_h
diff --git a/Common/ITKExtras/itkMorphologicalContourInterpolator.hxx b/Common/ITKExtras/itkMorphologicalContourInterpolator.hxx
new file mode 100755
index 0000000..70b4042
--- /dev/null
+++ b/Common/ITKExtras/itkMorphologicalContourInterpolator.hxx
@@ -0,0 +1,1723 @@
+/*=========================================================================
+*
+* Copyright Insight Software Consortium
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*
+* 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.
+*
+*=========================================================================*/
+#ifndef itkMorphologicalContourInterpolator_hxx
+#define itkMorphologicalContourInterpolator_hxx
+
+#include "itkAndImageFilter.h"
+#include "itkBinaryBallStructuringElement.h"
+#include "itkBinaryCrossStructuringElement.h"
+#include "itkBinaryDilateImageFilter.h"
+#include "itkCastImageFilter.h"
+#include "itkImageAlgorithm.h"
+#include "itkImageRegionConstIteratorWithIndex.h"
+#include "itkImageRegionIterator.h"
+#include "itkMorphologicalContourInterpolator.h"
+#include "itkMultiThreader.h"
+#include "itkObjectFactory.h"
+#include "itkOrImageFilter.h"
+#include "itkSignedMaurerDistanceMapImageFilter.h"
+#include "itkSimpleFastMutexLock.h"
+#include "itkThreadedIndexedContainerPartitioner.h"
+#include "itkUnaryFunctorImageFilter.h"
+#include <algorithm>
+#include <climits>
+#include <queue>
+#include <utility>
+#include <vector>
+
+namespace itk
+{
+template< typename TImage >
+struct SegmentBetweenTwo
+{
+ int axis;
+ TImage* out;
+ int label, i, j;
+ typename MorphologicalContourInterpolator< TImage >::SliceType::Pointer iconn, jconn;
+};
+
+template< typename TImage >
+class MorphologicalContourInterpolatorParallelInvoker:
+ public itk::DomainThreader< itk::ThreadedIndexedContainerPartitioner, MorphologicalContourInterpolator< TImage > >
+{
+public:
+ // Standard ITK typedefs.
+ typedef MorphologicalContourInterpolatorParallelInvoker Self;
+ typedef itk::DomainThreader< itk::ThreadedIndexedContainerPartitioner,
+ MorphologicalContourInterpolator< TImage > > Superclass;
+ typedef itk::SmartPointer< Self > Pointer;
+ typedef itk::SmartPointer< const Self > ConstPointer;
+
+ // The domain is an index range.
+ typedef typename Superclass::DomainType DomainType;
+
+ // This creates the ::New() method for instantiating the class.
+ itkNewMacro( Self );
+
+ /** Array of segments which need to be interpolated. */
+ void
+ SetWorkArray( std::vector< SegmentBetweenTwo< TImage > >& workArray )
+ {
+ m_WorkArray = workArray;
+ }
+
+ /** Array of segments which need to be interpolated. */
+ void
+ ClearWorkArray()
+ {
+ m_WorkArray.clear();
+ }
+
+protected:
+ // We need a constructor for the itkNewMacro.
+ MorphologicalContourInterpolatorParallelInvoker() {}
+
+private:
+ virtual void
+ ThreadedExecution( const DomainType& subDomain, const ThreadIdType threadId )
+ {
+ // Look only at the range of cells by the set of indices in the subDomain.
+ for ( itk::IndexValueType ii = subDomain[0]; ii <= subDomain[1] && ii < IndexValueType( m_WorkArray.size() ); ++ii )
+ {
+ this->m_Associate->InterpolateBetweenTwo(
+ m_WorkArray[ii].axis,
+ m_WorkArray[ii].out,
+ m_WorkArray[ii].label,
+ m_WorkArray[ii].i,
+ m_WorkArray[ii].j,
+ m_WorkArray[ii].iconn,
+ m_WorkArray[ii].jconn,
+ threadId );
+ }
+ } // ThreadedExecution
+
+ std::vector< SegmentBetweenTwo< TImage > > m_WorkArray;
+};
+
+template< typename TImage >
+bool
+MorphologicalContourInterpolator< TImage >
+::ImagesEqual( typename BoolSliceType::Pointer& a, typename BoolSliceType::Pointer& b )
+{
+ ImageRegionConstIterator< BoolSliceType > ita( a, a->GetLargestPossibleRegion() );
+ ImageRegionConstIterator< BoolSliceType > itb( b, b->GetLargestPossibleRegion() );
+
+ while ( !ita.IsAtEnd() )
+ {
+ if ( ita.Get() != itb.Get() )
+ {
+ break;
+ }
+ ++ita;
+ ++itb;
+ }
+
+ if ( ita.IsAtEnd() )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+} // >::ImagesEqual
+
+template< typename TImage >
+MorphologicalContourInterpolator< TImage >
+::MorphologicalContourInterpolator()
+ : m_Label( 0 ),
+ m_Axis( -1 ),
+ m_HeuristicAlignment( true ),
+ m_UseDistanceTransform( true ),
+ m_UseBallStructuringElement( false ),
+ m_UseCustomSlicePositions( false ),
+ m_MinAlignIters( pow( 2, (int) TImage::ImageDimension ) ), // smaller of this and pixel count of the search image
+ m_MaxAlignIters( pow( 6, (int) TImage::ImageDimension ) ), // bigger of this and root of pixel count of the search image
+ m_ThreadCount( MultiThreader::GetGlobalDefaultNumberOfThreads() ),
+ m_LabeledSlices( TImage::ImageDimension ) // initialize with empty sets
+{
+ // set up pipeline for regioned connected components
+ m_RoI = RoiType::New();
+ m_RoI->SetDirectionCollapseToIdentity();
+ m_Binarizer = BinarizerType::New();
+ m_Binarizer->SetInput( m_RoI->GetOutput() );
+ m_ConnectedComponents = ConnectedComponentsType::New();
+ m_ConnectedComponents->SetInput( m_Binarizer->GetOutput() );
+ // FullyConnected is related to structuring element used
+ // true for ball, false for cross
+ m_ConnectedComponents->SetFullyConnected( m_UseBallStructuringElement );
+}
+
+template< typename TImage >
+template< typename T2 >
+void
+MorphologicalContourInterpolator< TImage >
+::ExpandRegion( typename T2::RegionType& region, const typename T2::IndexType& index )
+{
+ for ( unsigned int a = 0; a < T2::ImageDimension; ++a )
+ {
+ if ( region.GetIndex( a ) > index[a] )
+ {
+ region.SetSize( a, region.GetSize( a ) + region.GetIndex( a ) - index[a] );
+ region.SetIndex( a, index[a] );
+ }
+ else if ( region.GetIndex( a ) + (typename T2::IndexValueType)region.GetSize( a ) <= index[a] )
+ {
+ region.SetSize( a, index[a] - region.GetIndex( a ) + 1 );
+ }
+ // else it is already within
+ }
+}
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::DetermineSliceOrientations()
+{
+ m_LabeledSlices.clear();
+ m_LabeledSlices.resize( TImage::ImageDimension ); // initialize with empty sets
+ m_BoundingBoxes.clear();
+
+ typename TImage::ConstPointer m_Input = this->GetInput();
+ typename TImage::Pointer m_Output = this->GetOutput();
+
+ typename TImage::RegionType region = m_Output->GetRequestedRegion();
+ ImageRegionConstIteratorWithIndex< TImage > it( m_Input, region );
+ while ( !it.IsAtEnd() )
+ {
+ typename TImage::IndexType indPrev, indNext;
+ const typename TImage::IndexType ind = it.GetIndex();
+ const typename TImage::PixelType val = m_Input->GetPixel( ind );
+ if ( val != 0 )
+ {
+ typename TImage::RegionType boundingBox1;
+ boundingBox1.SetIndex( ind );
+ for ( unsigned int a = 0; a < TImage::ImageDimension; ++a )
+ {
+ boundingBox1.SetSize( a, 1 );
+ }
+ std::pair< typename BoundingBoxesType::iterator, bool > resBB
+ = m_BoundingBoxes.insert( std::make_pair( val, boundingBox1 ) );
+ if ( !resBB.second ) // include this index in existing BB
+ {
+ ExpandRegion< TImage >( resBB.first->second, ind );
+ }
+
+ unsigned int cTrue = 0;
+ unsigned int cAdjacent = 0;
+ unsigned int axis = 0;
+ for ( unsigned int a = 0; a < TImage::ImageDimension; ++a )
+ {
+ indPrev = ind;
+ indPrev[a]--;
+ indNext = ind;
+ indNext[a]++;
+ typename TImage::PixelType prev = 0;
+ if ( region.IsInside( indPrev ) )
+ {
+ prev = m_Input->GetPixel( indPrev );
+ }
+ typename TImage::PixelType next = 0;
+ if ( region.IsInside( indNext ) )
+ {
+ next = m_Input->GetPixel( indNext );
+ }
+ if ( prev == 0 && next == 0 ) // && - isolated slices only, || - flat edges too
+ {
+ axis = a;
+ ++cTrue;
+ }
+ else if ( prev == val && next == val )
+ {
+ ++cAdjacent;
+ }
+ }
+ if ( cTrue == 1 && cAdjacent == TImage::ImageDimension - 1 )
+ // slice has empty adjacent space only along one axis
+ {
+ if ( m_Axis == -1 || m_Axis == int(axis) )
+ {
+ m_LabeledSlices[axis][val].insert( ind[axis] );
+ }
+ }
+ }
+ ++it;
+ }
+} // >::DetermineSliceOrientations
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::Extrapolate( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ ThreadIdType threadId )
+{
+ PixelList jRegionIds;
+
+ jRegionIds.push_back( iRegionId );
+ typename SliceType::IndexType centroid = Centroid( iConn, jRegionIds );
+
+ typename SliceType::RegionType reg3;
+ typename SliceType::SizeType size;
+ size.Fill( 3 );
+ reg3.SetSize( size );
+
+ typename SliceType::IndexType phIndex;
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ phIndex[d] = centroid.GetIndex()[d] - 1;
+ }
+ reg3.SetIndex( phIndex );
+
+ // create a phantom small slice centered around centroid
+ typename SliceType::Pointer phSlice = SliceType::New();
+ phSlice->CopyInformation( iConn );
+ phSlice->SetRegions( reg3 );
+ phSlice->Allocate( true );
+
+ // add a phantom point to the center of a newly constructed slice
+ phSlice->SetPixel( centroid, iRegionId );
+
+ // do alignment in case the iShape is concave and centroid is not within the iShape
+ typename SliceType::IndexType translation = Align( iConn, iRegionId, phSlice, jRegionIds );
+ // now translate the phantom slice for best alignment
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ phIndex[d] -= translation[d];
+ }
+ reg3.SetIndex( phIndex );
+ phSlice->SetRegions( reg3 );
+ typename SliceType::IndexType t0;
+ t0.Fill( 0 );
+ Interpolate1to1( axis, out, label, i, j, iConn, iRegionId, phSlice, iRegionId, t0, false, threadId );
+} // >::Extrapolate
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::BoolSliceType::Pointer
+MorphologicalContourInterpolator< TImage >
+::Dilate1( typename BoolSliceType::Pointer& seed, typename BoolSliceType::Pointer& mask, ThreadIdType threadId )
+{
+ // set up structuring element for dilation
+ typedef BinaryCrossStructuringElement< typename BoolSliceType::PixelType,
+ BoolSliceType::ImageDimension > CrossStructuringElementType;
+ typedef BinaryBallStructuringElement< typename BoolSliceType::PixelType,
+ BoolSliceType::ImageDimension > BallStructuringElementType;
+ typedef BinaryDilateImageFilter< BoolSliceType, BoolSliceType,
+ CrossStructuringElementType > CrossDilateType;
+ typedef BinaryDilateImageFilter< BoolSliceType, BoolSliceType,
+ BallStructuringElementType > BallDilateType;
+
+ static std::vector< bool > initialized( m_ThreadCount ); // default: false
+ static std::vector< typename CrossDilateType::Pointer > m_CrossDilator( m_ThreadCount );
+ static std::vector< typename BallDilateType::Pointer > m_BallDilator( m_ThreadCount );
+ static std::vector< CrossStructuringElementType > m_CrossStructuringElement( m_ThreadCount );
+ static std::vector< BallStructuringElementType > m_BallStructuringElement( m_ThreadCount );
+ typedef AndImageFilter< BoolSliceType, BoolSliceType, BoolSliceType > AndFilterType;
+ static std::vector< typename AndFilterType::Pointer > m_And( m_ThreadCount );
+
+ if ( !initialized[threadId] ) // make sure these non-trivial operations are executed only once per thread
+ {
+ m_CrossDilator[threadId] = CrossDilateType::New();
+ m_BallDilator[threadId] = BallDilateType::New();
+ m_And[threadId] = AndFilterType::New();
+ m_And[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ typedef Size< BoolSliceType::ImageDimension > SizeType;
+ SizeType size;
+ size.Fill( 1 );
+
+ m_CrossDilator[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ m_CrossStructuringElement[threadId].SetRadius( size );
+ m_CrossStructuringElement[threadId].CreateStructuringElement();
+ m_CrossDilator[threadId]->SetKernel( m_CrossStructuringElement[threadId] );
+
+ m_BallDilator[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ m_BallStructuringElement[threadId].SetRadius( size );
+ m_BallStructuringElement[threadId].CreateStructuringElement();
+ m_BallDilator[threadId]->SetKernel( m_BallStructuringElement[threadId] );
+
+ initialized[threadId] = true;
+ }
+
+ typename BoolSliceType::Pointer temp;
+ if ( m_UseBallStructuringElement )
+ {
+ m_BallDilator[threadId]->SetInput( seed );
+ m_BallDilator[threadId]->GetOutput()->SetRegions( seed->GetRequestedRegion() );
+ m_BallDilator[threadId]->Update();
+ temp = m_BallDilator[threadId]->GetOutput();
+ }
+ else
+ {
+ m_CrossDilator[threadId]->SetInput( seed );
+ m_CrossDilator[threadId]->GetOutput()->SetRegions( seed->GetRequestedRegion() );
+ m_CrossDilator[threadId]->Update();
+ temp = m_CrossDilator[threadId]->GetOutput();
+ }
+ temp->DisconnectPipeline();
+ // temp->SetRegions(mask->GetLargestPossibleRegion()); //not needed when seed and mask have same regions
+
+ m_And[threadId]->SetInput( 0, mask );
+ m_And[threadId]->SetInput( 1, temp );
+ m_And[threadId]->GetOutput()->SetRegions( seed->GetRequestedRegion() );
+ m_And[threadId]->Update();
+ typename BoolSliceType::Pointer result = m_And[threadId]->GetOutput();
+ result->DisconnectPipeline();
+ return result;
+} // >::Dilate1
+
+template< typename TImage >
+std::vector< typename MorphologicalContourInterpolator< TImage >::BoolSliceType::Pointer >
+MorphologicalContourInterpolator< TImage >
+::GenerateDilationSequence( typename BoolSliceType::Pointer& begin,
+ typename BoolSliceType::Pointer& end,
+ ThreadIdType threadId )
+{
+ std::vector< typename BoolSliceType::Pointer > seq;
+ seq.push_back( Dilate1( begin, end, threadId ) );
+
+ do
+ {
+ seq.back()->DisconnectPipeline();
+ seq.push_back( Dilate1( seq.back(), end, threadId ) );
+ }
+ while ( !ImagesEqual( seq.back(), seq[seq.size() - 2] ) );
+
+ seq.pop_back(); // remove duplicate image
+ return seq;
+}
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::BoolSliceType::Pointer
+MorphologicalContourInterpolator< TImage >
+::FindMedianImageDilations( typename BoolSliceType::Pointer& intersection,
+ typename BoolSliceType::Pointer& iMask,
+ typename BoolSliceType::Pointer& jMask,
+ ThreadIdType threadId )
+{
+ std::vector< typename BoolSliceType::Pointer > iSeq = GenerateDilationSequence( intersection, iMask, threadId );
+ std::vector< typename BoolSliceType::Pointer > jSeq = GenerateDilationSequence( intersection, jMask, threadId );
+ std::reverse( iSeq.begin(), iSeq.end() ); // we want to start from i and end at intersection
+ if ( iSeq.size() < jSeq.size() )
+ {
+ iSeq.swap( jSeq ); // swap so iSeq.size() >= jSeq.size()
+ }
+ float ratio = float( jSeq.size() ) / iSeq.size();
+
+ // generate union of transition sequences
+ typedef OrImageFilter< BoolSliceType > OrType;
+ static std::vector< bool > initialized( m_ThreadCount ); // default: false
+ static std::vector< typename OrType::Pointer > m_Or( m_ThreadCount );
+ if ( !initialized[threadId] )
+ {
+ m_Or[threadId] = OrType::New();
+ m_Or[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ initialized[threadId] = true;
+ }
+
+ std::vector< typename BoolSliceType::Pointer > seq;
+ for ( unsigned x = 0; x < iSeq.size(); x++ )
+ {
+ m_Or[threadId]->SetInput( 0, iSeq[x] );
+ unsigned xj = ratio * x;
+ m_Or[threadId]->SetInput( 1, jSeq[xj] );
+ m_Or[threadId]->GetOutput()->SetRegions( iMask->GetRequestedRegion() );
+ m_Or[threadId]->Update();
+ seq.push_back( m_Or[threadId]->GetOutput() );
+ seq.back()->DisconnectPipeline();
+ }
+
+ // find median
+ unsigned minIndex = 0;
+ IdentifierType min = iMask->GetRequestedRegion().GetNumberOfPixels();
+ for ( unsigned x = 0; x < iSeq.size(); x++ )
+ {
+ IdentifierType iS = CardSymDifference( seq[x], iMask );
+ IdentifierType jS = CardSymDifference( seq[x], jMask );
+ IdentifierType xScore = iS >= jS ? iS - jS : jS - iS; // abs(iS-jS)
+ if ( xScore < min )
+ {
+ min = xScore;
+ minIndex = x;
+ }
+ }
+ return seq[minIndex];
+} // >::FindMedianImageDilations
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::FloatSliceType::Pointer
+MorphologicalContourInterpolator< TImage >
+::MaurerDM( typename BoolSliceType::Pointer& mask, ThreadIdType threadId )
+{
+ typedef itk::SignedMaurerDistanceMapImageFilter< BoolSliceType, FloatSliceType > FilterType;
+ static std::vector< bool > initialized( m_ThreadCount ); // default: false
+ static std::vector< typename FilterType::Pointer > filter( m_ThreadCount );
+ if ( !initialized[threadId] )
+ {
+ filter[threadId] = FilterType::New();
+ filter[threadId]->SetUseImageSpacing( false ); // interpolation algorithm calls for working in index space
+ filter[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ initialized[threadId] = true;
+ }
+ filter[threadId]->SetInput( mask );
+ filter[threadId]->GetOutput()->SetRequestedRegion( mask->GetRequestedRegion() );
+ filter[threadId]->Update();
+ return filter[threadId]->GetOutput();
+}
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::BoolSliceType::Pointer
+MorphologicalContourInterpolator< TImage >
+::FindMedianImageDistances( typename BoolSliceType::Pointer& intersection,
+ typename BoolSliceType::Pointer& iMask,
+ typename BoolSliceType::Pointer& jMask,
+ ThreadIdType threadId )
+{
+ // calculate distance field
+ typename FloatSliceType::Pointer sdf = MaurerDM( intersection, threadId );
+
+ // create histograms of distances and union
+ typename BoolSliceType::Pointer orImage = BoolSliceType::New();
+ orImage->CopyInformation( intersection );
+ orImage->SetRegions( iMask->GetRequestedRegion() );
+ orImage->Allocate( true );
+ std::vector< long long > iHist;
+ std::vector< long long > jHist;
+ ImageRegionConstIterator< BoolSliceType > iti( iMask, iMask->GetRequestedRegion() );
+ ImageRegionConstIterator< BoolSliceType > itj( jMask, iMask->GetRequestedRegion() );
+ ImageRegionIterator< BoolSliceType > ito( orImage, iMask->GetRequestedRegion() );
+ ImageRegionConstIterator< FloatSliceType > itsdf( sdf, iMask->GetRequestedRegion() );
+ const short fractioning = 10; // how many times more precise distance than rounding to int
+ while ( !itsdf.IsAtEnd() )
+ {
+ bool iM = iti.Get();
+ bool jM = itj.Get();
+ typename TImage::PixelType dist = fractioning * itsdf.Get();
+ if ( iM && !jM )
+ {
+ if ( size_t(dist) >= iHist.size() )
+ {
+ iHist.resize( dist + 1, 0 );
+ }
+ iHist[dist]++;
+ ito.Set( true );
+ }
+ else if ( jM && !iM )
+ {
+ if ( size_t(dist) >= jHist.size() )
+ {
+ jHist.resize( dist + 1, 0 );
+ }
+ jHist[dist]++;
+ ito.Set( true );
+ }
+ else if ( iM && jM )
+ {
+ ito.Set( true );
+ }
+
+ ++iti;
+ ++itj;
+ ++ito;
+ ++itsdf;
+ }
+
+ // sum of histogram bins for i and j and
+ std::vector< long long >::size_type maxSize = std::max( iHist.size(), jHist.size() );
+ if ( maxSize == 0 )
+ {
+ return intersection;
+ }
+ iHist.resize( maxSize, 0 );
+ jHist.resize( maxSize, 0 );
+ assert( iHist[0] == 0 );
+ assert( jHist[0] == 0 );
+ std::vector< long long > iSum( maxSize, 0 );
+ std::vector< long long > jSum( maxSize, 0 );
+ for ( unsigned b = 1; b < maxSize; b++ )
+ {
+ iSum[b] = iSum[b - 1] + iHist[b];
+ jSum[b] = jSum[b - 1] + jHist[b];
+ }
+ long long iTotal = iSum[maxSize - 1];
+ long long jTotal = jSum[maxSize - 1];
+
+ // find minimum of differences of sums
+ int bestBin = 0;
+ long long bestDiff = LLONG_MAX;
+ for ( unsigned b = 0; b < maxSize; b++ )
+ {
+ long long iS = std::abs( iTotal - iSum[b] + jSum[b] );
+ long long jS = std::abs( jTotal - jSum[b] + iSum[b] );
+ long long diff = std::abs( iS - jS );
+ if ( diff < bestDiff )
+ {
+ bestDiff = diff;
+ bestBin = b;
+ }
+ }
+
+ // threshold at distance bestBin is the median intersection
+ typedef BinaryThresholdImageFilter< FloatSliceType, BoolSliceType > FloatBinarizerType;
+ typedef AndImageFilter< BoolSliceType, BoolSliceType, BoolSliceType > AndFilterType;
+ static std::vector< bool > initialized( m_ThreadCount ); // default: false
+ static std::vector< typename FloatBinarizerType::Pointer > threshold( m_ThreadCount );
+ static std::vector< typename AndFilterType::Pointer > m_And( m_ThreadCount );
+ if ( !initialized[threadId] )
+ {
+ threshold[threadId] = FloatBinarizerType::New();
+ threshold[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ m_And[threadId] = AndFilterType::New();
+ m_And[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ initialized[threadId] = true;
+ }
+ threshold[threadId]->SetInput( sdf );
+ threshold[threadId]->SetUpperThreshold( float( bestBin ) / fractioning );
+ threshold[threadId]->GetOutput()->SetRequestedRegion( sdf->GetRequestedRegion() );
+ threshold[threadId]->Update();
+
+ m_And[threadId]->SetInput( threshold[threadId]->GetOutput() );
+ m_And[threadId]->SetInput( 1, orImage );
+ m_And[threadId]->GetOutput()->SetRequestedRegion( orImage->GetRequestedRegion() );
+ m_And[threadId]->Update();
+ typename BoolSliceType::Pointer median = m_And[threadId]->GetOutput();
+ return median;
+} // >::FindMedianImageDistances
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::SliceType::RegionType
+MorphologicalContourInterpolator< TImage >
+::BoundingBox( itk::SmartPointer< SliceType > image )
+{
+ typename SliceType::RegionType newRegion = image->GetLargestPossibleRegion();
+ typename SliceType::IndexType minInd = newRegion.GetIndex() + newRegion.GetSize();
+ typename SliceType::IndexType maxInd = newRegion.GetIndex();
+ ImageRegionConstIteratorWithIndex< SliceType > iIt( image, newRegion );
+
+ while ( !iIt.IsAtEnd() )
+ {
+ if ( iIt.Get() )
+ {
+ typename SliceType::IndexType ind = iIt.GetIndex();
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ if ( ind[d] < minInd[d] )
+ {
+ minInd[d] = ind[d];
+ }
+ if ( ind[d] > maxInd[d] )
+ {
+ maxInd[d] = ind[d];
+ }
+ }
+ }
+ ++iIt;
+ }
+
+ newRegion.SetIndex( minInd );
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ newRegion.SetSize( d, maxInd[d] - minInd[d] + 1 );
+ }
+ return newRegion;
+} // >::BoundingBox
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::Interpolate1to1( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ typename TImage::PixelType jRegionId,
+ const typename SliceType::IndexType& translation,
+ bool recursive,
+ ThreadIdType threadId )
+{
+ // translate iConn by t/2 and jConn by -t/2
+ typename SliceType::IndexType iTrans;
+ typename SliceType::IndexType jTrans;
+ typename SliceType::RegionType iRegion = iConn->GetLargestPossibleRegion();
+ typename SliceType::RegionType jRegion = jConn->GetLargestPossibleRegion();
+ typename SliceType::IndexType jBottom = jRegion.GetIndex();
+ bool carry = false;
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ if ( !carry )
+ {
+ iTrans[d] = translation[d] / 2;
+ carry = translation[d] % 2;
+ }
+ else if ( translation[d] % 2 == 0 )
+ {
+ iTrans[d] = translation[d] / 2;
+ }
+ else // use carry
+ {
+ if ( translation[d] > 0 )
+ {
+ iTrans[d] = translation[d] / 2 + 1;
+ }
+ else
+ {
+ iTrans[d] = translation[d] / 2 - 1;
+ }
+ carry = false;
+ }
+ jTrans[d] = iTrans[d] - translation[d];
+ iRegion.SetIndex( d, iRegion.GetIndex()[d] + iTrans[d] );
+ jRegion.SetIndex( d, jRegion.GetIndex()[d] + jTrans[d] );
+ jBottom[d] = jRegion.GetIndex()[d] + jRegion.GetSize( d ) - 1;
+ }
+ typename SliceType::RegionType newRegion = iRegion;
+ ExpandRegion< SliceType >( newRegion, jRegion.GetIndex() );
+ ExpandRegion< SliceType >( newRegion, jBottom );
+ typename SliceType::IndexValueType mid = ( i + j + ( carry ? 1 : 0 ) ) / 2; // index of middle slice
+
+ typename SliceType::Pointer iConnT = TranslateImage( iConn, iTrans, newRegion );
+ typename SliceType::Pointer jConnT = TranslateImage( jConn, jTrans, newRegion );
+
+ if ( !recursive ) // reduce newRegion to bounding box so we deal with less pixels
+ {
+ newRegion = BoundingBox( iConnT );
+ typename SliceType::RegionType jBB = BoundingBox( jConnT );
+ typename SliceType::IndexType i2 = jBB.GetIndex();
+ ExpandRegion< SliceType >( newRegion, i2 );
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ i2[d] += jBB.GetSize( d ) - 1;
+ }
+ ExpandRegion< SliceType >( newRegion, i2 );
+ }
+
+ // create and allocate slice binary masks
+ typename BoolSliceType::Pointer iSlice = BoolSliceType::New();
+ iSlice->CopyInformation( iConnT );
+ iSlice->SetRegions( newRegion );
+ iSlice->Allocate( true );
+ typename BoolSliceType::Pointer jSlice = BoolSliceType::New();
+ jSlice->CopyInformation( jConnT );
+ jSlice->SetRegions( newRegion );
+ jSlice->Allocate( true );
+
+ // convert to binary by iteration
+ ImageRegionConstIterator< SliceType > iIt( iConnT, newRegion );
+ ImageRegionConstIterator< SliceType > jIt( jConnT, newRegion );
+ ImageRegionIterator< BoolSliceType > ibIt( iSlice, newRegion );
+ ImageRegionIterator< BoolSliceType > jbIt( jSlice, newRegion );
+ while ( !iIt.IsAtEnd() )
+ {
+ if ( iIt.Get() == iRegionId )
+ {
+ ibIt.Set( true );
+ }
+ if ( jIt.Get() == jRegionId )
+ {
+ jbIt.Set( true );
+ }
+ ++iIt;
+ ++jIt;
+ ++ibIt;
+ ++jbIt;
+ }
+
+ // create intersection
+ typedef AndImageFilter< BoolSliceType > AndSliceType;
+ static std::vector< bool > initialized( m_ThreadCount ); // default: false
+ static std::vector< typename AndSliceType::Pointer > sAnd( m_ThreadCount );
+ if ( !initialized[threadId] )
+ {
+ sAnd[threadId] = AndSliceType::New();
+ sAnd[threadId]->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ initialized[threadId] = true;
+ }
+ sAnd[threadId]->SetInput( 0, iSlice );
+ sAnd[threadId]->SetInput( 1, jSlice );
+ sAnd[threadId]->GetOutput()->SetRegions( iSlice->GetRequestedRegion() );
+ sAnd[threadId]->Update();
+ typename BoolSliceType::Pointer intersection = sAnd[threadId]->GetOutput();
+ intersection->DisconnectPipeline();
+
+ typename BoolSliceType::Pointer median;
+ if ( m_UseDistanceTransform )
+ {
+ median = FindMedianImageDistances( intersection, iSlice, jSlice, threadId );
+ }
+ else
+ {
+ median = FindMedianImageDilations( intersection, iSlice, jSlice, threadId );
+ }
+
+ // finally write it out into the output image pointer
+ typename TImage::RegionType outRegion = this->GetOutput()->GetRequestedRegion();
+ typename SliceType::RegionType sliceRegion;
+ for ( int d = 0; d < int(TImage::ImageDimension) - 1; d++ )
+ {
+ if ( d < axis )
+ {
+ sliceRegion.SetIndex( d, outRegion.GetIndex( d ) );
+ sliceRegion.SetSize( d, outRegion.GetSize( d ) );
+ }
+ else
+ {
+ sliceRegion.SetIndex( d, outRegion.GetIndex( d + 1 ) );
+ sliceRegion.SetSize( d, outRegion.GetSize( d + 1 ) );
+ }
+ }
+ typename SliceType::IndexType t0;
+ t0.Fill( 0 );
+ IntersectionRegions( t0, sliceRegion, newRegion ); // clips new region to output region
+ // sliceRegion possibly shrunk, copy it into outRegion
+ for ( int d = 0; d < int(TImage::ImageDimension) - 1; d++ )
+ {
+ if ( d < axis )
+ {
+ outRegion.SetIndex( d, sliceRegion.GetIndex( d ) );
+ outRegion.SetSize( d, sliceRegion.GetSize( d ) );
+ }
+ else
+ {
+ outRegion.SetIndex( d + 1, sliceRegion.GetIndex( d ) );
+ outRegion.SetSize( d + 1, sliceRegion.GetSize( d ) );
+ }
+ }
+ outRegion.SetIndex( axis, mid );
+ outRegion.SetSize( axis, 1 );
+
+ typename SliceType::Pointer midConn = SliceType::New();
+ midConn->CopyInformation( iConnT );
+ midConn->SetRegions( newRegion );
+ midConn->Allocate( true );
+
+ ImageRegionConstIterator< BoolSliceType > seqIt( median, newRegion );
+ ImageRegionIterator< SliceType > midIt( midConn, newRegion );
+ while ( !seqIt.IsAtEnd() )
+ {
+ if ( seqIt.Get() )
+ {
+ midIt.Set( 1 );
+ }
+ ++seqIt;
+ ++midIt;
+ }
+
+ bool withinReq = true;
+ typename TImage::RegionType reqRegion = this->GetOutput()->GetRequestedRegion();
+ for ( unsigned d = 0; d < TImage::ImageDimension; d++ )
+ {
+ if ( outRegion.GetIndex( d ) < reqRegion.GetIndex( d ) ||
+ outRegion.GetIndex( d ) + outRegion.GetSize( d ) >
+ reqRegion.GetIndex( d ) + reqRegion.GetSize( d ) )
+ {
+ withinReq = false;
+ break;
+ }
+ }
+
+ static SimpleFastMutexLock mutex;
+ if ( withinReq ) // else we should not write it
+ {
+ seqIt.GoToBegin();
+ // writing through one RLEImage iterator invalidates all the others
+ // so this whole writing loop needs to be serialized
+ mutex.Lock();
+ ImageRegionIterator< TImage > outIt( out, outRegion );
+ while ( !outIt.IsAtEnd() )
+ {
+ if ( seqIt.Get() && outIt.Get() < label )
+ {
+ outIt.Set( label );
+ }
+ ++seqIt;
+ ++outIt;
+ }
+
+ mutex.Unlock();
+ } // iterator destroyed here
+
+ // recurse if needed
+ if ( abs( (int) (i - j) ) > 2 )
+ {
+ PixelList regionIDs;
+ regionIDs.push_back( 1 );
+
+ int iReq = i < reqRegion.GetIndex( axis ) ? -1 :
+ ( i > reqRegion.GetIndex( axis ) + IndexValueType( reqRegion.GetSize( axis ) ) ? +1 : 0 );
+ int jReq = j < reqRegion.GetIndex( axis ) ? -1 :
+ ( j > reqRegion.GetIndex( axis ) + IndexValueType( reqRegion.GetSize( axis ) ) ? +1 : 0 );
+ int mReq = mid < reqRegion.GetIndex( axis ) ? -1 :
+ ( mid > reqRegion.GetIndex( axis ) + IndexValueType( reqRegion.GetSize( axis ) ) ? +1 : 0 );
+ bool first = abs( (int) (i - mid) ) > 1 && abs( (int)(iReq + mReq) ) <= 1; // i-mid?
+ bool second = abs( (int) (j - mid) ) > 1 && abs( (int)(jReq + mReq) ) <= 1; // j-mid?
+
+ if ( first )
+ {
+ Interpolate1to1( axis, out, label, i, mid, iConn, iRegionId, midConn, 1, iTrans, true, threadId );
+ }
+ if ( second )
+ {
+ Interpolate1to1( axis, out, label, j, mid, jConn, jRegionId, midConn, 1, jTrans, true, threadId );
+ }
+ }
+} // >::Interpolate1to1
+
+template< typename TImage >
+class MatchesID
+{
+ typename TImage::PixelType m_ID;
+
+public:
+ MatchesID() {}
+ MatchesID( typename TImage::PixelType id )
+ : m_ID( id )
+ {}
+ bool
+ operator!=( const MatchesID& other )
+ {
+ return m_ID != other.m_ID;
+ }
+
+ bool
+ operator==( const MatchesID& other )
+ {
+ return m_ID == other.m_ID;
+ }
+
+ inline bool
+ operator()( const typename TImage::PixelType& val ) const
+ {
+ return val == m_ID;
+ }
+};
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::Interpolate1toN( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ const PixelList& jRegionIds,
+ const typename SliceType::IndexType& translation,
+ ThreadIdType threadId )
+{
+ // first convert iConn into binary mask
+ MatchesID< TImage > matchesID( iRegionId );
+
+ typedef UnaryFunctorImageFilter< SliceType, BoolSliceType, MatchesID< TImage > > CastType;
+ typename CastType::Pointer caster = CastType::New();
+ caster->SetNumberOfThreads( 1 ); // excessive threading is counterproductive
+ caster->SetFunctor( matchesID );
+ caster->SetInput( iConn );
+ caster->Update();
+ typename BoolSliceType::Pointer mask = caster->GetOutput();
+
+ typename SliceType::RegionType iRegion, jRegion, newjRegion;
+ iRegion = iConn->GetLargestPossibleRegion();
+ jRegion = jConn->GetLargestPossibleRegion();
+ newjRegion = jRegion;
+ newjRegion.SetSize( iRegion.GetSize() );
+
+ // construct n empty images
+ std::vector< typename BoolSliceType::Pointer > blobs;
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ typename BoolSliceType::Pointer temp = BoolSliceType::New();
+ temp->CopyInformation( jConn );
+ temp->SetRegions( iRegion );
+ temp->Allocate( true );
+ blobs.push_back( temp );
+ }
+
+ // fill the n images with intersections - these are seeds
+ typename SliceType::Pointer belongs = SliceType::New();
+ belongs->CopyInformation( mask );
+ belongs->SetRegions( iRegion );
+ belongs->Allocate( true ); // initialize to zero (false)
+ ImageRegionIterator< SliceType > belongIt( belongs, iRegion );
+ IntersectionRegions( translation, iRegion, jRegion );
+ ImageRegionConstIterator< BoolSliceType > maskIt( mask, iRegion );
+ ImageRegionConstIteratorWithIndex< SliceType > jIt( jConn, jRegion );
+ ImageRegionIterator< SliceType > belongInit( belongs, iRegion );
+
+ // convert jConn into n blobs, translating them into the index space of iConn
+ while ( !maskIt.IsAtEnd() )
+ {
+ if ( maskIt.Get() )
+ {
+ typename TImage::PixelType jVal = jIt.Get();
+ typename PixelList::const_iterator res = std::find( jRegionIds.begin(), jRegionIds.end(), jVal );
+ if ( res != jRegionIds.end() )
+ {
+ blobs[res - jRegionIds.begin()]->SetPixel( maskIt.GetIndex(), true );
+ belongInit.Set( res - jRegionIds.begin() + 1 );
+ }
+ }
+ ++maskIt;
+ ++jIt;
+ ++belongInit;
+ }
+
+ // prepare dilation filter
+ iRegion = iConn->GetLargestPossibleRegion(); // expand to full i image
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ blobs[x]->SetRegions( iRegion );
+ }
+ ImageRegionConstIterator< BoolSliceType > maskIt2( mask, iRegion );
+ ImageRegionConstIteratorWithIndex< BoolSliceType > jIt2( blobs[0], iRegion );
+
+ bool hollowedMaskEmpty;
+
+ do // while hollowed mask is not empty
+ {
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ blobs[x] = Dilate1( blobs[x], mask, threadId );
+ blobs[x]->DisconnectPipeline();
+ }
+
+ hollowedMaskEmpty = true;
+ maskIt2.GoToBegin();
+ jIt2.GoToBegin();
+ belongIt.GoToBegin();
+ while ( !maskIt2.IsAtEnd() ) // hollow out the big mask with dilated seeds while avoiding conflicts
+ {
+ if ( maskIt2.Get() )
+ {
+ if ( !belongIt.Get() )
+ {
+ unsigned x = 0;
+ while ( x < jRegionIds.size() )
+ {
+ if ( blobs[x]->GetPixel( jIt2.GetIndex() ) )
+ {
+ break;
+ }
+ ++x;
+ }
+
+ if ( x < jRegionIds.size() ) // covered by a blob, hollow it out
+ {
+ belongIt.Set( x + 1 );
+ for ( x++; x < jRegionIds.size(); x++ )
+ {
+ // pixel does not belong to this blob
+ blobs[x]->SetPixel( jIt2.GetIndex(), false );
+ }
+ }
+ else // keep it
+ {
+ hollowedMaskEmpty = false;
+ }
+ }
+ else // the pixel already belongs to some blob
+ {
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ if ( unsigned(belongIt.Get()) != x + 1 )
+ {
+ // pixel does not belong to this blob
+ blobs[x]->SetPixel( jIt2.GetIndex(), false );
+ }
+ }
+ }
+ }
+ ++maskIt2;
+ ++jIt2;
+ ++belongIt;
+ }
+ }
+ while ( !hollowedMaskEmpty );
+
+ blobs.clear(); // deallocates the images
+
+ // convert the belongs into n Conn-style images
+ std::vector< typename SliceType::Pointer > conns;
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ typename SliceType::Pointer temp2 = SliceType::New();
+ temp2->CopyInformation( iConn );
+ temp2->SetRegions( iConn->GetLargestPossibleRegion() );
+ temp2->Allocate( true );
+ conns.push_back( temp2 );
+ }
+ ImageRegionConstIteratorWithIndex< SliceType > belongIt2( belongs, iRegion );
+ while ( !belongIt2.IsAtEnd() )
+ {
+ const typename SliceType::PixelType belong = belongIt2.Get();
+ if ( belong > 0 )
+ {
+ conns[belong - 1]->SetPixel( belongIt2.GetIndex(), iRegionId );
+ }
+ ++belongIt2;
+ }
+ // make n 1-to-1 interpolations
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ Interpolate1to1( axis, out, label, i, j, conns[x], iRegionId, jConn, jRegionIds[x], translation, false, threadId );
+ }
+} // >::Interpolate1toN
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::SliceType::Pointer
+MorphologicalContourInterpolator< TImage >
+::TranslateImage( typename SliceType::Pointer& image,
+ const typename SliceType::IndexType& translation,
+ typename SliceType::RegionType newRegion )
+{
+ typename SliceType::Pointer result = SliceType::New();
+ result->CopyInformation( image );
+ result->SetRegions( newRegion );
+ result->Allocate( true ); // initialize to zero (false)
+ typename SliceType::RegionType inRegion = image->GetLargestPossibleRegion();
+ IntersectionRegions( translation, inRegion, newRegion );
+ ImageAlgorithm::Copy< SliceType, SliceType >( image.GetPointer(), result.GetPointer(), inRegion, newRegion );
+ return result;
+}
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::IntersectionRegions( const typename SliceType::IndexType& translation,
+ typename SliceType::RegionType& iRegion,
+ typename SliceType::RegionType& jRegion )
+{
+ typename SliceType::IndexType iBegin = iRegion.GetIndex();
+ typename SliceType::IndexType jBegin = jRegion.GetIndex();
+ for ( IdentifierType d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ IndexValueType iSize = iRegion.GetSize( d );
+ IndexValueType jSize = jRegion.GetSize( d );
+ iBegin[d] += translation[d];
+ IndexValueType t = std::max( iBegin[d], jBegin[d] );
+ iRegion.SetSize( d, std::min( IndexValueType( iSize ) - ( t - iBegin[d] ),
+ IndexValueType( jSize ) - ( t - jBegin[d] ) ) );
+ iRegion.SetIndex( d, t - translation[d] );
+ jRegion.SetIndex( d, t );
+ }
+ jRegion.SetSize( iRegion.GetSize() ); // size is the same
+}
+
+template< typename TImage >
+IdentifierType
+MorphologicalContourInterpolator< TImage >
+::Intersection( typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ const PixelList& jRegionIds,
+ const typename SliceType::IndexType& translation )
+{
+ typename SliceType::RegionType iRegion, jRegion;
+ iRegion = iConn->GetLargestPossibleRegion();
+ jRegion = jConn->GetLargestPossibleRegion();
+ IntersectionRegions( translation, iRegion, jRegion );
+
+ std::vector< IdentifierType > counts( jRegionIds.size() );
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ counts[x] = 0;
+ }
+ ImageRegionConstIterator< SliceType > iIt( iConn, iRegion );
+ ImageRegionConstIterator< SliceType > jIt( jConn, jRegion );
+ while ( !iIt.IsAtEnd() )
+ {
+ if ( iIt.Get() == iRegionId )
+ {
+ typename TImage::PixelType jVal = jIt.Get();
+ typename PixelList::const_iterator res = std::find( jRegionIds.begin(), jRegionIds.end(), jVal );
+ if ( res != jRegionIds.end() )
+ {
+ ++counts[res - jRegionIds.begin()];
+ }
+ }
+ ++iIt;
+ ++jIt;
+ }
+
+ IdentifierType sum = 0;
+ for ( unsigned x = 0; x < jRegionIds.size(); x++ )
+ {
+ if ( counts[x] == 0 )
+ {
+ return 0; // iConn must intersect all subregions of jConn
+ }
+ sum += counts[x];
+ }
+ return sum;
+} // >::Intersection
+
+template< typename TImage >
+IdentifierType
+MorphologicalContourInterpolator< TImage >
+::CardSymDifference( typename BoolSliceType::Pointer& iShape, typename BoolSliceType::Pointer& jShape )
+{
+ typename BoolSliceType::RegionType region = iShape->GetLargestPossibleRegion();
+ IdentifierType count = 0;
+ ImageRegionConstIterator< BoolSliceType > iIt( iShape, region );
+ ImageRegionConstIterator< BoolSliceType > jIt( jShape, region );
+ while ( !iIt.IsAtEnd() )
+ {
+ if ( iIt.Get() != jIt.Get() )
+ {
+ count++;
+ }
+ ++iIt;
+ ++jIt;
+ }
+
+ return count;
+}
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::SliceType::IndexType
+MorphologicalContourInterpolator< TImage >
+::Centroid( typename SliceType::Pointer& conn, const PixelList& regionIds )
+{
+ ImageRegionConstIteratorWithIndex< SliceType > it( conn, conn->GetLargestPossibleRegion() );
+ IndexValueType ind[SliceType::ImageDimension] = { 0 }; // all components are initialized to zero
+ IdentifierType pixelCount = 0;
+ while ( !it.IsAtEnd() )
+ {
+ typename TImage::PixelType val = it.Get();
+ if ( val )
+ {
+ typename PixelList::const_iterator res = std::find( regionIds.begin(), regionIds.end(), val );
+ if ( res != regionIds.end() )
+ {
+ ++pixelCount;
+ typename SliceType::IndexType pInd = it.GetIndex();
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ ind[d] += pInd[d];
+ }
+ }
+ }
+ ++it;
+ }
+
+ typename SliceType::IndexType retVal;
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ retVal[d] = ind[d] / pixelCount;
+ }
+ return retVal;
+} // >::Centroid
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::SliceType::IndexType
+MorphologicalContourInterpolator< TImage >
+::Align( typename SliceType::Pointer& iConn,
+ typename TImage::PixelType iRegionId,
+ typename SliceType::Pointer& jConn,
+ const PixelList& jRegionIds )
+{
+ // calculate centroids
+ PixelList iRegionIds;
+
+ iRegionIds.push_back( iRegionId );
+ typename SliceType::IndexType iCentroid = Centroid( iConn, iRegionIds );
+ typename SliceType::IndexType jCentroid = Centroid( jConn, jRegionIds );
+
+ typename SliceType::IndexType ind;
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ ind[d] = jCentroid[d] - iCentroid[d];
+ }
+
+ // construct an image with all possible translations
+ typename SliceType::RegionType searchRegion;
+ typename SliceType::RegionType iLPR = iConn->GetLargestPossibleRegion();
+ typename SliceType::RegionType jLPR = jConn->GetLargestPossibleRegion();
+ for ( IdentifierType d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ searchRegion.SetIndex( d, jLPR.GetIndex()[d] - iLPR.GetIndex()[d] - iLPR.GetSize( d ) + 1 );
+ searchRegion.SetSize( d, iLPR.GetSize( d ) + jLPR.GetSize( d ) - 1 );
+ }
+
+ typename BoolSliceType::Pointer searched = BoolSliceType::New();
+ searched->SetRegions( searchRegion );
+ searched->Allocate( true ); // initialize to zero (false)
+
+ // breadth first search starting from centroid, implicitly:
+ // when intersection scores are equal, chooses the one closer to centroid
+ std::queue< typename SliceType::IndexType > uncomputed;
+ typename SliceType::IndexType t0;
+ t0.Fill( 0 );
+ uncomputed.push( t0 ); // no translation - guaranteed to find a non-zero intersection
+ uncomputed.push( ind ); // this introduces movement, and possibly has the same score
+ searched->SetPixel( t0, true );
+ searched->SetPixel( ind, true );
+ IdentifierType score, maxScore = 0;
+ typename SliceType::IndexType bestIndex;
+ IdentifierType iter = 0;
+ IdentifierType minIter = std::min( m_MinAlignIters, searchRegion.GetNumberOfPixels() );
+ IdentifierType maxIter = std::max( m_MaxAlignIters, (IdentifierType)sqrt( searchRegion.GetNumberOfPixels() ) );
+
+ while ( !uncomputed.empty() )
+ {
+ ind = uncomputed.front();
+ uncomputed.pop();
+ score = Intersection( iConn, iRegionId, jConn, jRegionIds, ind );
+ if ( score > maxScore )
+ {
+ maxScore = score;
+ bestIndex = ind;
+ }
+
+ // we breadth this search
+ if ( !m_HeuristicAlignment || maxScore == 0 || iter <= minIter || (score > maxScore * 0.9 && iter <= maxIter) )
+ {
+ for ( unsigned d = 0; d < SliceType::ImageDimension; d++ )
+ {
+ ind[d] -= 1; // "left"
+ if ( searchRegion.IsInside( ind ) && !searched->GetPixel( ind ) )
+ {
+ uncomputed.push( ind );
+ searched->SetPixel( ind, true );
+ ++iter;
+ }
+ ind[d] += 2; // "right"
+ if ( searchRegion.IsInside( ind ) && !searched->GetPixel( ind ) )
+ {
+ uncomputed.push( ind );
+ searched->SetPixel( ind, true );
+ ++iter;
+ }
+ ind[d] -= 1; // return to initial
+ }
+ }
+ }
+
+ return bestIndex;
+} // >::Align
+
+template< typename TImage >
+typename MorphologicalContourInterpolator< TImage >::SliceType::Pointer
+MorphologicalContourInterpolator< TImage >
+::RegionedConnectedComponents( const typename TImage::RegionType& region,
+ typename TImage::PixelType label,
+ IdentifierType& objectCount )
+{
+ m_RoI->SetExtractionRegion( region );
+ m_RoI->SetInput( this->GetInput() );
+ m_Binarizer->SetLowerThreshold( label );
+ m_Binarizer->SetUpperThreshold( label );
+ m_ConnectedComponents->Update();
+ objectCount = m_ConnectedComponents->GetObjectCount();
+ return m_ConnectedComponents->GetOutput();
+}
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::InterpolateBetweenTwo( int axis,
+ TImage* out,
+ typename TImage::PixelType label,
+ typename TImage::IndexValueType i,
+ typename TImage::IndexValueType j,
+ typename SliceType::Pointer& iconn,
+ typename SliceType::Pointer& jconn,
+ ThreadIdType threadId )
+{
+ // go through comparison image and create correspondence pairs
+ typedef std::set< std::pair< typename TImage::PixelType, typename TImage::PixelType > > PairSet;
+ PairSet pairs, unwantedPairs, uncleanPairs;
+ typename SliceType::RegionType ri = iconn->GetRequestedRegion();
+ typename SliceType::RegionType rj = jconn->GetRequestedRegion();
+ ImageRegionConstIterator< SliceType > iti( iconn, ri );
+ ImageRegionConstIterator< SliceType > itj( jconn, rj );
+ while ( !iti.IsAtEnd() )
+ {
+ if ( iti.Get() != 0 || itj.Get() != 0 )
+ {
+ uncleanPairs.insert( std::make_pair( iti.Get(), itj.Get() ) );
+ // std::cout << " iti:" << iti.GetIndex() << iti.Get() <<
+ // " itj:" << itj.GetIndex() << itj.Get() << std::endl;
+ if ( iti.Get() != 0 && itj.Get() != 0 )
+ {
+ unwantedPairs.insert( std::make_pair( 0, itj.Get() ) );
+ unwantedPairs.insert( std::make_pair( iti.Get(), 0 ) );
+ }
+ }
+ ++iti;
+ ++itj;
+ }
+
+ std::set_difference( uncleanPairs.begin(), uncleanPairs.end(), unwantedPairs.begin(),
+ unwantedPairs.end(), std::inserter( pairs, pairs.end() ) );
+
+ // first do extrapolation for components without overlaps
+ typename PairSet::iterator p = pairs.begin();
+ while ( p != pairs.end() )
+ {
+ if ( p->second == 0 )
+ {
+ Extrapolate( axis, out, label, i, j, iconn, p->first, threadId );
+ pairs.erase( p++ );
+ }
+ else if ( p->first == 0 )
+ {
+ Extrapolate( axis, out, label, j, i, jconn, p->second, threadId );
+ pairs.erase( p++ );
+ }
+ else
+ {
+ ++p;
+ }
+ }
+
+ // count ocurrances of each component
+ typedef std::map< typename TImage::PixelType, IdentifierType > CountMap;
+ CountMap iCounts, jCounts;
+ for ( p = pairs.begin(); p != pairs.end(); ++p )
+ {
+ iCounts[p->first]++;
+ jCounts[p->second]++;
+ }
+
+ // now handle 1 to 1 correspondences
+ p = pairs.begin();
+ while ( p != pairs.end() )
+ {
+ if ( iCounts[p->first] == 1 && jCounts[p->second] == 1 )
+ {
+ PixelList regionIDs;
+ regionIDs.push_back( p->second );
+ typename SliceType::IndexType translation = Align( iconn, p->first, jconn, regionIDs );
+ Interpolate1to1( axis, out, label, i, j, iconn, p->first, jconn, p->second, translation, false, threadId );
+ iCounts.erase( p->first );
+ jCounts.erase( p->second );
+ pairs.erase( p++ );
+ }
+ else
+ {
+ ++p;
+ }
+ }
+
+ PixelList regionIDs( pairs.size() ); // preallocate
+ // now do 1-to-N and M-to-1 cases
+ p = pairs.begin();
+ while ( p != pairs.end() )
+ {
+ regionIDs.clear();
+
+ if ( iCounts[p->first] == 1 ) // M-to-1
+ {
+ for ( typename PairSet::iterator rest = pairs.begin(); rest != pairs.end(); ++rest )
+ {
+ if ( rest->second == p->second )
+ {
+ regionIDs.push_back( rest->first );
+ }
+ }
+
+ typename SliceType::IndexType translation = Align( jconn, p->second, iconn, regionIDs );
+ Interpolate1toN( axis, out, label, j, i, jconn, p->second, iconn, regionIDs, translation, threadId );
+
+ typename PairSet::iterator rest = pairs.begin();
+ while ( rest != pairs.end() )
+ {
+ if ( rest != p && rest->second == p->second )
+ {
+ --iCounts[rest->first];
+ --jCounts[rest->second];
+ pairs.erase( rest++ );
+ }
+ else
+ {
+ ++rest;
+ }
+ }
+
+ --iCounts[p->first];
+ --jCounts[p->second];
+ pairs.erase( p++ );
+ } // M-to-1
+ else if ( jCounts[p->second] == 1 ) // 1-to-N
+ {
+ for ( typename PairSet::iterator rest = pairs.begin(); rest != pairs.end(); ++rest )
+ {
+ if ( rest->first == p->first )
+ {
+ regionIDs.push_back( rest->second );
+ }
+ }
+
+ typename SliceType::IndexType translation = Align( iconn, p->first, jconn, regionIDs );
+ Interpolate1toN( axis, out, label, i, j, iconn, p->first, jconn, regionIDs, translation, threadId );
+
+ typename PairSet::iterator rest = pairs.begin();
+ ++rest;
+ while ( rest != pairs.end() )
+ {
+ if ( rest != p && rest->first == p->first )
+ {
+ --iCounts[rest->first];
+ --jCounts[rest->second];
+ pairs.erase( rest++ );
+ }
+ else
+ {
+ ++rest;
+ }
+ }
+
+ --iCounts[p->first];
+ --jCounts[p->second];
+ pairs.erase( p++ );
+ } // 1-to-N
+ else
+ {
+ ++p;
+ }
+ } // 1-to-N and M-to-1
+
+ // only M-to-N correspondences remain
+ // we turn each M-to-N case into m 1-to-N cases
+ p = pairs.begin();
+ while ( p != pairs.end() )
+ {
+ regionIDs.clear();
+ for ( typename PairSet::iterator rest = p; rest != pairs.end(); ++rest )
+ {
+ if ( rest->first == p->first )
+ {
+ regionIDs.push_back( rest->second );
+ }
+ }
+
+ typename SliceType::IndexType translation = Align( iconn, p->first, jconn, regionIDs );
+ Interpolate1toN( axis, out, label, i, j, iconn, p->first, jconn, regionIDs, translation, threadId );
+
+ typename PairSet::iterator rest = p;
+ ++rest;
+ while ( rest != pairs.end() )
+ {
+ if ( rest->first == p->first )
+ {
+ pairs.erase( rest++ );
+ }
+ else
+ {
+ ++rest;
+ }
+ }
+
+ // counts no longer matter, do not waste time deleting them
+ pairs.erase( p++ );
+ } // M-to-N
+} // void MorphologicalContourInterpolator::InterpolateBetweenTwo()
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::InterpolateAlong( int axis, TImage* out )
+{
+ // a list of segments which need to be interpolated
+ std::vector< SegmentBetweenTwo< TImage > > segments;
+ typename TImage::RegionType reqRegion = this->GetOutput()->GetRequestedRegion();
+ for ( typename LabeledSlicesType::iterator it = m_LabeledSlices[axis].begin();
+ it != m_LabeledSlices[axis].end();
+ ++it )
+ {
+ if ( m_Label == 0 || m_Label == it->first ) // label needs to be interpolated
+ {
+ typename SliceSetType::iterator prev = it->second.begin();
+ if ( prev == it->second.end() )
+ {
+ continue; // nothing to do for this label
+ }
+
+ typename TImage::RegionType ri = reqRegion;
+ if ( !m_UseCustomSlicePositions )
+ {
+ typename BoundingBoxesType::iterator iBB = m_BoundingBoxes.find( it->first );
+ if ( iBB == m_BoundingBoxes.end() )
+ {
+ continue; // this label not present in requested region
+ }
+ else
+ {
+ ri = iBB->second;
+ }
+ }
+ ri.SetSize( axis, 0 );
+ ri.SetIndex( axis, *prev );
+ IdentifierType xCount;
+ typename SliceType::Pointer iconn = this->RegionedConnectedComponents( ri, it->first, xCount );
+ iconn->DisconnectPipeline();
+ int iReq = *prev < reqRegion.GetIndex( axis ) ? -1 :
+ ( *prev > reqRegion.GetIndex( axis ) + IndexValueType( reqRegion.GetSize( axis ) ) ? +1 : 0 );
+
+ typename SliceSetType::iterator next = it->second.begin();
+ for ( ++next; next != it->second.end(); ++next )
+ {
+ typename TImage::RegionType rj = ri;
+ rj.SetIndex( axis, *next );
+ typename SliceType::Pointer jconn = this->RegionedConnectedComponents( rj, it->first, xCount );
+ jconn->DisconnectPipeline();
+ int jReq = *next < reqRegion.GetIndex( axis ) ? -1 :
+ ( *next > reqRegion.GetIndex( axis ) + IndexValueType( reqRegion.GetSize( axis ) ) ? +1 : 0 );
+
+ if ( *prev + 1 < *next // only if they are not adjacent slices
+ && abs(iReq + jReq) <= 1 ) // and not out of the requested region
+ // unless they are on opposite ends
+ {
+ SegmentBetweenTwo< TImage > s;
+ s.axis = axis;
+ s.out = out;
+ s.label = it->first;
+ s.i = *prev;
+ s.j = *next;
+ s.iconn = iconn;
+ s.jconn = jconn;
+ segments.push_back( s );
+ }
+ iconn = jconn;
+ iReq = jReq;
+ prev = next;
+ }
+ }
+ }
+
+ typedef MorphologicalContourInterpolatorParallelInvoker< TImage > Parallelizer;
+ typename Parallelizer::Pointer parallelizer = Parallelizer::New();
+ parallelizer->SetWorkArray( segments );
+ typename Parallelizer::DomainType completeDomain;
+ completeDomain[0] = 0;
+ completeDomain[1] = std::max( 0, int( segments.size() ) - 1 );
+ parallelizer->Execute( this, completeDomain );
+ parallelizer->ClearWorkArray();
+} // >::InterpolateAlong
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::AllocateOutputs()
+{
+ typedef ImageBase< TImage::ImageDimension > ImageBaseType;
+ typename ImageBaseType::Pointer outputPtr;
+
+ for ( OutputDataObjectIterator it( this ); !it.IsAtEnd(); it++ )
+ {
+ outputPtr = dynamic_cast< ImageBaseType * >( it.GetOutput() );
+ if ( outputPtr )
+ {
+ outputPtr->SetBufferedRegion( outputPtr->GetRequestedRegion() );
+ outputPtr->Allocate( true );
+ }
+ }
+}
+
+template< typename TImage >
+void
+MorphologicalContourInterpolator< TImage >
+::GenerateData()
+{
+ typename TImage::ConstPointer m_Input = this->GetInput();
+ typename TImage::Pointer m_Output = this->GetOutput();
+ this->AllocateOutputs();
+
+ if ( m_UseCustomSlicePositions )
+ {
+ SliceIndicesType t = m_LabeledSlices;
+ this->DetermineSliceOrientations(); // calculates bounding boxes
+ m_LabeledSlices = t; // restore custom positions
+ }
+ else
+ {
+ this->DetermineSliceOrientations();
+ }
+
+ if ( m_BoundingBoxes.size() == 0 && !m_UseCustomSlicePositions )
+ {
+ ImageAlgorithm::Copy< TImage, TImage >( m_Input.GetPointer(), m_Output.GetPointer(),
+ m_Output->GetRequestedRegion(), m_Output->GetRequestedRegion() );
+ return; // no contours detected
+ }
+
+ if ( m_Axis == -1 ) // interpolate along all axes
+ {
+ FixedArray< bool, TImage::ImageDimension > aggregate;
+ aggregate.Fill( false );
+ for ( unsigned i = 0; i < TImage::ImageDimension; i++ )
+ {
+ if ( this->m_Label == 0 ) // examine all labels
+ {
+ for ( unsigned l = 0; l < m_LabeledSlices[i].size(); l++ )
+ {
+ if ( m_LabeledSlices[i][l].size() > 1 )
+ {
+ aggregate[i] = true;
+ }
+ }
+ }
+ else // we only care about this label
+ {
+ if ( m_LabeledSlices[i][m_Label].size() > 1 )
+ {
+ aggregate[i] = true;
+ }
+ }
+ }
+
+ for ( unsigned int a = 0; a < TImage::ImageDimension; ++a )
+ {
+ if ( aggregate[a] )
+ {
+ this->InterpolateAlong( a, m_Output );
+ }
+ }
+ } // interpolate along all axes
+ else // interpolate along the specified axis
+ {
+ this->InterpolateAlong( m_Axis, m_Output );
+ }
+
+ // Overwrites m_Output with non non-zeroes from m_Input
+ ImageRegionIterator< TImage > itO( this->GetOutput(), this->GetOutput()->GetBufferedRegion() );
+ ImageRegionConstIterator< TImage > itI( this->GetInput(), this->GetOutput()->GetBufferedRegion() );
+ while ( !itI.IsAtEnd() )
+ {
+ typename TImage::PixelType val = itI.Get();
+ if ( val != 0 )
+ {
+ itO.Set( val );
+ }
+
+ ++itI;
+ ++itO;
+ }
+} // >::GenerateData
+} // end namespace itk
+
+#endif // itkMorphologicalContourInterpolator_hxx
diff --git a/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx b/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx
index d78a4fb..e501797 100644
--- a/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx
+++ b/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx
@@ -20,8 +20,7 @@ PURPOSE. See the above copyright notices for more information.
#include "itkParallelSparseFieldLevelSetImageFilterBugFix.h"
#include "itkZeroCrossingImageFilter.h"
#include "itkShiftScaleImageFilter.h"
-#include "itkImageRegionIterator.h"
-#include "itkImageRegionConstIterator.h"
+#include "RLEImageRegionIterator.h"
#include "itkNumericTraits.h"
#include "itkNeighborhoodAlgorithm.h"
#include "itkMacro.h"
diff --git a/Common/IRISVectorTypesToITKConversion.h b/Common/ImageFunctions.cxx
similarity index 56%
copy from Common/IRISVectorTypesToITKConversion.h
copy to Common/ImageFunctions.cxx
index f5d4409..fe5ab57 100644
--- a/Common/IRISVectorTypesToITKConversion.h
+++ b/Common/ImageFunctions.cxx
@@ -1,13 +1,13 @@
/*=========================================================================
Program: ITK-SNAP
- Module: $RCSfile: IRISVectorTypesToITKConversion.h,v $
+ Module: $RCSfile: IRISVectorTypes.h,v $
Language: C++
- Date: $Date: 2007/12/30 04:05:12 $
- Version: $Revision: 1.2 $
+ Date: $Date: 2008/11/01 11:32:00 $
+ Version: $Revision: 1.3 $
Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
+
+ This file is part of ITK-SNAP
ITK-SNAP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -29,45 +28,16 @@
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE. See the above copyright notices for more information.
-
-=========================================================================*/
-#ifndef __IRISVectorTypesToITKConversion_h_
-#define __IRISVectorTypesToITKConversion_h_
-
-#include "IRISVectorTypes.h"
-
-#include "itkSize.h"
-#include "itkIndex.h"
-
-/**
- * Convert a VectorNi to itk::Size
- */
-template <class T, unsigned int VSize>
-inline itk::Size<VSize>
-to_itkSize(const vnl_vector_fixed<T,VSize> &x)
-{
- itk::Size<VSize> z;
-
- for(unsigned int i=0;i<VSize;i++)
- z[i] = static_cast<unsigned long>(x(i));
+ PURPOSE. See the above copyright notices for more information.
- return z;
-}
-
-/**
- * Convert a VectorNi to itk::Size
- */
-template <class T, unsigned int VSize>
-inline itk::Index<VSize>
-to_itkIndex(const vnl_vector_fixed<T,VSize> &x)
-{
- itk::Index<VSize> z;
+ -----
- for(unsigned int i=0;i<VSize;i++)
- z[i] = static_cast<unsigned long>(x(i));
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
- return z;
-}
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
-#endif // __IRISVectorTypesToITKConversion_h_
+=========================================================================*/
+#include "ImageFunctions.h"
diff --git a/Common/ImageFunctions.h b/Common/ImageFunctions.h
new file mode 100644
index 0000000..1aef317
--- /dev/null
+++ b/Common/ImageFunctions.h
@@ -0,0 +1,93 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: IRISVectorTypes.h,v $
+ Language: C++
+ Date: $Date: 2008/11/01 11:32:00 $
+ Version: $Revision: 1.3 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef IMAGEFUNCTIONS_H
+#define IMAGEFUNCTIONS_H
+
+#include "IRISVectorTypes.h"
+#include "itkImageBase.h"
+
+namespace itk { template <unsigned int D> class ImageBase; }
+
+/*
+ * Miscellaneous functions involving ITK images
+ */
+
+template <unsigned int VDim, typename TVector>
+void GetImagePhysicalExtents(itk::ImageBase<VDim> *image,
+ TVector &ext_min,
+ TVector &ext_max)
+{
+ // Get the index of each corner of the image
+ itk::ImageRegion<VDim> lpr = image->GetLargestPossibleRegion();
+
+ int n_corners = 1 << VDim;
+ for(int i = 0; i < n_corners; i++)
+ {
+ // Compute the index of this corner
+ itk::ContinuousIndex<double, VDim> index;
+ for(int j = 0; j < VDim; j++)
+ {
+ index[j] = ((i >> j) & 1)
+ ? lpr.GetIndex()[j] - 0.5
+ : lpr.GetIndex()[j] + lpr.GetSize()[j] - 0.5;
+ }
+
+ // Map this corner to physical space
+ itk::Point<double, VDim> point;
+ image->TransformContinuousIndexToPhysicalPoint(index, point);
+
+ // Update min/max with this point
+ for(int j = 0; j < VDim; j++)
+ {
+ if(i == 0 || ext_min[j] > point[j])
+ ext_min[j] = point[j];
+
+ if(i == 0 || ext_max[j] < point[j])
+ ext_max[j] = point[j];
+ }
+ }
+}
+
+
+
+#endif // IMAGEFUNCTIONS_H
diff --git a/Common/PresetManager.hxx b/Common/PresetManager.hxx
index f2b0c74..20ca112 100644
--- a/Common/PresetManager.hxx
+++ b/Common/PresetManager.hxx
@@ -39,7 +39,8 @@ PresetManager<TManagedObjectTraits>
// Load each of the presets from the registry
for(int j = 0; j < m_PresetUser.size(); j++)
{
- Registry reg = m_System->ReadSavedObject(m_Category.c_str(), m_PresetUser[j].c_str());
+ Registry reg;
+ m_System->ReadSavedObject(m_Category.c_str(), m_PresetUser[j].c_str(), reg);
ManagedTypePtr mtp = ManagedType::New();
TManagedObjectTraits::ReadFromRegistry(mtp, reg);
m_PresetMap[m_PresetUser[j]] = mtp;
diff --git a/Common/Registry.cxx b/Common/Registry.cxx
index de1a2d6..f808b11 100644
--- a/Common/Registry.cxx
+++ b/Common/Registry.cxx
@@ -343,7 +343,7 @@ Registry
::SetFlagAddIfNotFound(bool yesno)
{
// Set the internal value
- m_AddIfNotFound = yesno;
+ m_AddIfNotFound = yesno;
// Propagate to all the children folders
for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf)
@@ -352,6 +352,46 @@ Registry
}
}
+void Registry::CleanEmptyFolders()
+{
+ // Iterate over all the subfolders
+ FolderMapType::iterator itf = m_FolderMap.begin();
+ while(itf != m_FolderMap.end())
+ {
+ itf->second->CleanEmptyFolders();
+ if(itf->second->IsEmpty())
+ m_FolderMap.erase(itf++);
+ else
+ itf++;
+ }
+}
+
+bool Registry::IsZeroSizeArray()
+{
+ return
+ this->HasEntry("ArraySize") &&
+ this->Entry("ArraySize")[(unsigned int) 0] == 0 &&
+ this->m_EntryMap.size() == 1 &&
+ this->m_FolderMap.size() == 0;
+}
+
+void Registry::CleanZeroSizeArrays()
+{
+ // Iterate over all the subfolders
+ FolderMapType::iterator itf = m_FolderMap.begin();
+ while(itf != m_FolderMap.end())
+ {
+ // Do the recursive part
+ itf->second->CleanZeroSizeArrays();
+
+ // Check if it has the array size key
+ if(itf->second->IsZeroSizeArray())
+ m_FolderMap.erase(itf++);
+ else
+ itf++;
+ }
+}
+
void
Registry
::CollectKeys(StringListType &keyList,const StringType &prefix)
@@ -434,6 +474,11 @@ Registry
m_FolderMap.clear();
}
+bool Registry::IsEmpty() const
+{
+ return m_EntryMap.size() == 0 && m_FolderMap.size() == 0;
+}
+
Registry::StringType
Registry
::EncodeXML(const StringType &input)
@@ -645,12 +690,25 @@ Registry
ReadFromFile(fname);
}
+Registry::Registry(const Registry &source)
+{
+ *this = source;
+}
+
+void Registry::operator =(const Registry &source)
+{
+ this->Clear();
+ this->Update(source);
+ this->m_AddIfNotFound = source.m_AddIfNotFound;
+}
+
+
Registry::
~Registry()
{
// Delete all the sub-folders
- // for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf)
- // delete itf->second;
+ for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf)
+ delete itf->second;
}
bool Registry::operator == (const Registry &other) const
@@ -663,7 +721,9 @@ bool Registry::operator == (const Registry &other) const
it1 != m_FolderMap.end(); ++it1, ++it2)
{
// Compare keys
- if(it1->first != it2->first)
+ const StringType &key1 = it1->first;
+ const StringType &key2 = it2->first;
+ if(key1 != key2)
return false;
// Compare subfolder contents (recursively)
@@ -679,7 +739,9 @@ bool Registry::operator == (const Registry &other) const
it1 != m_EntryMap.end(); ++it1, ++it2)
{
// Compare keys
- if(it1->first != it2->first)
+ const StringType &key1 = it1->first;
+ const StringType &key2 = it2->first;
+ if(key1 != key2)
return false;
// Compare subfolder contents (recursively)
diff --git a/Common/Registry.h b/Common/Registry.h
index 950151a..7feaae7 100644
--- a/Common/Registry.h
+++ b/Common/Registry.h
@@ -335,6 +335,12 @@ public:
/** Constructor loads a registry from a file */
Registry(const char *fname);
+ /** Copy constructor - copy entire registry by value */
+ Registry(const Registry &source);
+
+ /** Assignment operator - copy entire registry by value */
+ void operator = (const Registry &source);
+
/** Destructor */
virtual ~Registry();
@@ -373,6 +379,9 @@ public:
/** Empty the contents of the registry */
void Clear();
+ /** Check if the folder is empty */
+ bool IsEmpty() const;
+
/** Get a list of all keys that have values contained in this registry
* and all subfolders (recursive operation). Prefix is used internally,
* but can be specified to prepend a string to all keys */
@@ -447,6 +456,15 @@ public:
return result;
}
+ /** Test if the folder contains just an empty array */
+ bool IsZeroSizeArray();
+
+ /** Remove all empty folders recursively (folders with no elements) */
+ void CleanEmptyFolders();
+
+ /** Remove all folders that are zero-length arrays */
+ void CleanZeroSizeArrays();
+
/** An IO exception objects thrown by this class when reading from file*/
class IOException : public StringType {
public:
@@ -460,6 +478,7 @@ public:
};
private:
+
// Hashtable type definition
typedef std::map<StringType, Registry *> FolderMapType;
typedef std::map<StringType, RegistryValue> EntryMapType;
diff --git a/Common/SNAPCommon.h b/Common/SNAPCommon.h
index 216684d..d502ba3 100644
--- a/Common/SNAPCommon.h
+++ b/Common/SNAPCommon.h
@@ -129,6 +129,15 @@ enum AnatomicalDirection
ANATOMY_NONSENSE
};
+// List of available interpolation methods
+enum InterpolationMethod {
+ NEAREST_NEIGHBOR = 0,
+ TRILINEAR,
+ TRICUBIC,
+ SINC_WINDOW_05
+};
+
+
// An atomic data type to represent draw-over state
diff --git a/Common/SystemInterface.cxx b/Common/SystemInterface.cxx
index 61701ad..3f35d35 100644
--- a/Common/SystemInterface.cxx
+++ b/Common/SystemInterface.cxx
@@ -496,9 +496,9 @@ SystemInterface
return names;
}
-Registry
+void
SystemInterface
-::ReadSavedObject(const char *category, const char *name)
+::ReadSavedObject(const char *category, const char *name, Registry &out_folder)
{
// Make sure we have a directory for this category
std::string appdir = GetApplicationDataDirectory();
@@ -515,9 +515,7 @@ SystemInterface
if(!SystemTools::FileExists(fname.c_str(), true))
throw IRISException("Saved object file does not exist");
- Registry reg;
- reg.ReadFromXMLFile(fname.c_str());
- return reg;
+ out_folder.ReadFromXMLFile(fname.c_str());
}
void
diff --git a/Common/SystemInterface.h b/Common/SystemInterface.h
index 2b00492..2cd2726 100644
--- a/Common/SystemInterface.h
+++ b/Common/SystemInterface.h
@@ -130,7 +130,7 @@ public:
* SNAP options are less likely to interfere with each other.
*/
std::vector<std::string> GetSavedObjectNames(const char *category);
- Registry ReadSavedObject(const char *category, const char *name);
+ void ReadSavedObject(const char *category, const char *name, Registry &out_folder);
void UpdateSavedObject(const char *category, const char *name, Registry &folder);
void DeleteSavedObject(const char *category, const char *name);
static std::string DecodeObjectName(std::string fname);
diff --git a/Common/ThreadSpecificData.cxx b/Common/ThreadSpecificData.cxx
index e5ea337..da6abb5 100644
--- a/Common/ThreadSpecificData.cxx
+++ b/Common/ThreadSpecificData.cxx
@@ -1,6 +1,44 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: SNAPLevelSetFunction.h,v $
+ Language: C++
+ Date: $Date: 2007/12/30 04:05:14 $
+ Version: $Revision: 1.2 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
#include "ThreadSpecificData.h"
#include "itkMultiThreader.h"
-#include "IRISException.h"
+#ifndef DONT_USE_IRIS_EXCEPTION
+ #include "IRISException.h"
+#endif
#if defined(ITK_USE_PTHREADS)
@@ -17,7 +55,13 @@ ThreadSpecificDataSupport::ThreadSpecificDataSupport()
// Create the key
int rc = pthread_key_create(pkey, &ThreadSpecificDataSupport::Deleter);
if(rc)
- throw IRISException("pthread_key_create failed with rc = %d", rc);
+ {
+ #ifndef DONT_USE_IRIS_EXCEPTION
+ throw IRISException("pthread_key_create failed with rc = %d", rc);
+ #else
+ std::cerr << "pthread_key_create failed with rc = " << rc;
+ #endif
+ }
// Store the key
m_KeyPointer = pkey;
@@ -29,7 +73,13 @@ ThreadSpecificDataSupport::~ThreadSpecificDataSupport()
pthread_key_t *pkey = (pthread_key_t *) m_KeyPointer;
int rc = pthread_key_delete(pkey[0]);
if(rc)
- throw IRISException("pthread_key_delete failed with rc = %d", rc);
+ {
+ #ifndef DONT_USE_IRIS_EXCEPTION
+ throw IRISException("pthread_key_delete failed with rc = %d", rc);
+ #else
+ std::cerr << "pthread_key_delete failed with rc = " << rc;
+ #endif
+ }
}
void *ThreadSpecificDataSupport::GetPtrCreateIfNeeded(size_t data_size)
@@ -41,7 +91,13 @@ void *ThreadSpecificDataSupport::GetPtrCreateIfNeeded(size_t data_size)
pdata = malloc(data_size);
int rc = pthread_setspecific(pkey[0], pdata);
if(rc)
- throw IRISException("pthread_setspecific failed with rc = %d", rc);
+ {
+ #ifndef DONT_USE_IRIS_EXCEPTION
+ throw IRISException("pthread_setspecific failed with rc = %d", rc);
+ #else
+ std::cerr << "pthread_setspecific failed with rc: " << rc;
+ #endif
+ }
}
return pdata;
}
@@ -64,7 +120,13 @@ ThreadSpecificDataSupport::ThreadSpecificDataSupport()
DWORD *key = new DWORD[1];
key[0] = TlsAlloc();
if(key[0] == TLS_OUT_OF_INDEXES)
- throw IRISException("TlsAlloc failed with error %d", GetLastError());
+ {
+ #ifndef DONT_USE_IRIS_EXCEPTION
+ throw IRISException("TlsAlloc failed with error %d", GetLastError());
+ #else
+ std::cerr << "TlsAlloc failed with error: " << GetLastError();
+ #endif
+ }
m_KeyPointer = key;
}
@@ -89,11 +151,23 @@ void *ThreadSpecificDataSupport::GetPtrCreateIfNeeded(size_t data_size)
if(!pdata)
{
if(GetLastError() != ERROR_SUCCESS)
- throw IRISException("TlsGetValue failed with error %d", GetLastError());
-
+ {
+ #ifndef DONT_USE_IRIS_EXCEPTION
+ throw IRISException("TlsGetValue failed with error %d", GetLastError());
+ #else
+ std::cerr << "TlsGetValue failed with error: " << GetLastError();
+ #endif
+ }
+
pdata = malloc(data_size);
if(!TlsSetValue(key[0], pdata))
- throw IRISException("TlsSetValue failed with error %d", GetLastError());
+ {
+ #ifndef DONT_USE_IRIS_EXCEPTION
+ throw IRISException("TlsSetValue failed with error %d", GetLastError());
+ #else
+ std::cerr << "TlsSetValue failed with error : " << GetLastError();
+ #endif
+ }
}
return pdata;
diff --git a/Common/ThreadSpecificData.h b/Common/ThreadSpecificData.h
index 71ae387..c6cdf69 100644
--- a/Common/ThreadSpecificData.h
+++ b/Common/ThreadSpecificData.h
@@ -1,3 +1,39 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: SNAPLevelSetFunction.h,v $
+ Language: C++
+ Date: $Date: 2007/12/30 04:05:14 $
+ Version: $Revision: 1.2 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
#ifndef THREADSPECIFICDATA_H
#define THREADSPECIFICDATA_H
diff --git a/GUI/Model/AnnotationModel.cxx b/GUI/Model/AnnotationModel.cxx
index 9602118..0719d57 100644
--- a/GUI/Model/AnnotationModel.cxx
+++ b/GUI/Model/AnnotationModel.cxx
@@ -13,10 +13,10 @@ void AnnotationModel::SetParent(GenericSliceModel *model)
ValueChangedEvent(), ModelUpdateEvent());
}
-double AnnotationModel::GetLineLength(const Vector3f &xSliceA, const Vector3f &xSliceB)
+double AnnotationModel::GetLineLength(const Vector3d &xSliceA, const Vector3d &xSliceB)
{
- Vector2f pt1InAna = m_Parent->MapSliceToPhysicalWindow(xSliceA);
- Vector2f pt2InAna = m_Parent->MapSliceToPhysicalWindow(xSliceB);
+ Vector2d pt1InAna = m_Parent->MapSliceToPhysicalWindow(xSliceA);
+ Vector2d pt2InAna = m_Parent->MapSliceToPhysicalWindow(xSliceB);
double length = (pt1InAna[0] - pt2InAna[0]) * (pt1InAna[0] - pt2InAna[0])
+ (pt1InAna[1] - pt2InAna[1]) * (pt1InAna[1] - pt2InAna[1]);
length = sqrt(length);
@@ -30,7 +30,7 @@ double AnnotationModel::GetCurrentLineLength()
double AnnotationModel::GetCurrentLineLengthInPixels()
{
- Vector2f screen_offset =
+ Vector2d screen_offset =
m_Parent->MapSliceToWindow(m_CurrentLine.first) -
m_Parent->MapSliceToWindow(m_CurrentLine.second);
@@ -40,12 +40,12 @@ double AnnotationModel::GetCurrentLineLengthInPixels()
double AnnotationModel::GetAngleWithCurrentLine(const annot::LineSegmentAnnotation *lsa)
{
// Process the current line
- Vector2f v1 = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.first)
+ Vector2d v1 = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.first)
- m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.second);
v1 /= sqrt(v1[0]*v1[0] + v1[1]*v1[1]);
// Process the annotation
- Vector2f v2 = m_Parent->MapSliceToPhysicalWindow(
+ Vector2d v2 = m_Parent->MapSliceToPhysicalWindow(
m_Parent->MapImageToSlice(lsa->GetSegment().first))
-
m_Parent->MapSliceToPhysicalWindow(
@@ -63,15 +63,15 @@ void AnnotationModel::AdjustAngleToRoundDegree(LineSegment &line, int n_degrees)
// Map the line segment from slice coordinates to window physical, where angles are
// computed
- Vector2f p1 = m_Parent->MapSliceToPhysicalWindow(line.first);
- Vector2f p2 = m_Parent->MapSliceToPhysicalWindow(line.second);
+ Vector2d p1 = m_Parent->MapSliceToPhysicalWindow(line.first);
+ Vector2d p2 = m_Parent->MapSliceToPhysicalWindow(line.second);
// Express the vector of the line in polar coordinates
double p_rad = (p2 - p1).magnitude();
double p_phase = atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180.0 / vnl_math::pi;
// Current proposed adjustment
- Vector2f p2_rot_best = p2;
+ Vector2d p2_rot_best = p2;
double rot_best = std::numeric_limits<double>::infinity();
// Loop over all the lines
@@ -83,9 +83,9 @@ void AnnotationModel::AdjustAngleToRoundDegree(LineSegment &line, int n_degrees)
if(lsa && this->IsAnnotationVisible(lsa))
{
// Normalize the annotated line
- Vector2f q1 = m_Parent->MapSliceToPhysicalWindow(
+ Vector2d q1 = m_Parent->MapSliceToPhysicalWindow(
m_Parent->MapImageToSlice(lsa->GetSegment().first));
- Vector2f q2 = m_Parent->MapSliceToPhysicalWindow(
+ Vector2d q2 = m_Parent->MapSliceToPhysicalWindow(
m_Parent->MapImageToSlice(lsa->GetSegment().second));
// Get the phase of the line
@@ -96,7 +96,7 @@ void AnnotationModel::AdjustAngleToRoundDegree(LineSegment &line, int n_degrees)
double p_phase_shift = fabs(p_phase_round - p_phase);
// Map the rounded phase to the new p2 position
- Vector2f p2_prop(
+ Vector2d p2_prop(
p1[0] + p_rad * cos(p_phase_round * vnl_math::pi / 180.0),
p1[1] + p_rad * sin(p_phase_round * vnl_math::pi / 180.0));
@@ -113,32 +113,32 @@ void AnnotationModel::AdjustAngleToRoundDegree(LineSegment &line, int n_degrees)
line.second = m_Parent->MapPhysicalWindowToSlice(p2_rot_best);
}
-Vector3f AnnotationModel::GetAnnotationCenter(const AbstractAnnotation *annot)
+Vector3d AnnotationModel::GetAnnotationCenter(const AbstractAnnotation *annot)
{
const annot::LineSegmentAnnotation *lsa = dynamic_cast<const annot::LineSegmentAnnotation *>(annot);
if(lsa)
{
return (m_Parent->MapImageToSlice(lsa->GetSegment().first)
- + m_Parent->MapImageToSlice(lsa->GetSegment().second)) * 0.5f;
+ + m_Parent->MapImageToSlice(lsa->GetSegment().second)) * 0.5;
}
- return Vector3f(0.f);
+ return Vector3d(0.0);
}
-double AnnotationModel::GetDistanceToLine(const Vector3f &x1, const Vector3f &x2, const Vector3d &point)
+double AnnotationModel::GetDistanceToLine(const Vector3d &x1, const Vector3d &x2, const Vector3d &point)
{
- Vector2f p0 = m_Parent->MapSliceToWindow(x1);
- Vector2f p1 = m_Parent->MapSliceToWindow(x2);
- Vector2f x = m_Parent->MapSliceToWindow(to_float(point));
+ Vector2d p0 = m_Parent->MapSliceToWindow(x1);
+ Vector2d p1 = m_Parent->MapSliceToWindow(x2);
+ Vector2d x = m_Parent->MapSliceToWindow(point);
- float alpha = dot_product(x - p0, p1 - p0) / dot_product(p1 - p0, p1 - p0);
+ double alpha = dot_product(x - p0, p1 - p0) / dot_product(p1 - p0, p1 - p0);
if(alpha < 0)
alpha = 0;
if(alpha > 1)
alpha = 1;
- Vector2f px = p0 * (1.0f - alpha) + p1 * alpha;
+ Vector2d px = p0 * (1.0f - alpha) + p1 * alpha;
return (px - x).magnitude();
}
@@ -175,8 +175,8 @@ double AnnotationModel
if(lsa)
{
const annot::LineSegment &seg = lsa->GetSegment();
- Vector3f s1 = m_Parent->MapImageToSlice(seg.first);
- Vector3f s2 = m_Parent->MapImageToSlice(seg.second);
+ Vector3d s1 = m_Parent->MapImageToSlice(seg.first);
+ Vector3d s2 = m_Parent->MapImageToSlice(seg.second);
return GetDistanceToLine(s1, s2, point);
}
@@ -184,7 +184,7 @@ double AnnotationModel
if(lma)
{
const annot::Landmark &landmark = lma->GetLandmark();
- Vector3f s1, s2;
+ Vector3d s1, s2;
this->GetLandmarkArrowPoints(landmark, s1, s2);
return GetDistanceToLine(s1, s2, point);
}
@@ -234,13 +234,13 @@ bool AnnotationModel::ProcessPushEvent(const Vector3d &xSlice, bool shift_mod)
if(m_FlagDrawingLine)
{
// Complete drawing line
- m_CurrentLine.second = to_float(xSlice);
+ m_CurrentLine.second = xSlice;
handled = true;
}
else
{
- m_CurrentLine.first = to_float(xSlice);
- m_CurrentLine.second = to_float(xSlice);
+ m_CurrentLine.first = xSlice;
+ m_CurrentLine.second = xSlice;
m_FlagDrawingLine = true;
handled = true;
}
@@ -280,8 +280,8 @@ bool AnnotationModel::ProcessPushEvent(const Vector3d &xSlice, bool shift_mod)
if(asel)
{
m_MovingSelection = true;
- m_DragStart = to_float(xSlice);
- m_DragLast = to_float(xSlice);
+ m_DragStart = xSlice;
+ m_DragLast = xSlice;
m_MovingSelectionHandle = handle_idx;
m_MovingSelectionHandleAnnot = asel;
}
@@ -310,7 +310,7 @@ bool AnnotationModel::ProcessMoveEvent(const Vector3d &xSlice, bool shift_mod, b
if(m_FlagDrawingLine)
{
// Accept the second point
- m_CurrentLine.second = to_float(xSlice);
+ m_CurrentLine.second = xSlice;
// If shift pressed, adjust the line to have a integral angle with one of existing
// lines
@@ -326,9 +326,9 @@ bool AnnotationModel::ProcessMoveEvent(const Vector3d &xSlice, bool shift_mod, b
{
// Compute the amount to move the annotation by
- Vector3f p_last = m_Parent->MapSliceToImage(m_DragLast);
- Vector3f p_now = m_Parent->MapSliceToImage(to_float(xSlice));
- Vector3f p_delta = p_now - p_last;
+ Vector3d p_last = m_Parent->MapSliceToImage(m_DragLast);
+ Vector3d p_now = m_Parent->MapSliceToImage(xSlice);
+ Vector3d p_delta = p_now - p_last;
// Process the move command on selected annotations
for(ImageAnnotationData::AnnotationIterator it = adata->GetAnnotations().begin();
@@ -350,7 +350,7 @@ bool AnnotationModel::ProcessMoveEvent(const Vector3d &xSlice, bool shift_mod, b
}
// Store the update point
- m_DragLast = to_float(xSlice);
+ m_DragLast = xSlice;
// Event has been handled
handled = true;
@@ -418,10 +418,10 @@ void AnnotationModel::AcceptLine()
// Set the default offset. The default offset corresponds to 5 screen pixels
// to the top right. We need to map this into image units
- Vector2f xHeadWin = m_Parent->MapSliceToWindow(m_CurrentLine.first);
- Vector2f xTailWin = m_Parent->MapSliceToWindow(m_CurrentLine.second);
- Vector2f xHeadWinPhys = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.first);
- Vector2f xTailWinPhys = m_Parent->MapSliceToPhysicalWindow(m_Parent->MapWindowToSlice(xTailWin));
+ Vector2d xHeadWin = m_Parent->MapSliceToWindow(m_CurrentLine.first);
+ Vector2d xTailWin = m_Parent->MapSliceToWindow(m_CurrentLine.second);
+ Vector2d xHeadWinPhys = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.first);
+ Vector2d xTailWinPhys = m_Parent->MapSliceToPhysicalWindow(m_Parent->MapWindowToSlice(xTailWin));
lm.Offset = xTailWinPhys - xHeadWinPhys;
// Add the line
@@ -514,8 +514,8 @@ void AnnotationModel::GoToNextOrPrevAnnotation(int direction)
if(a->IsVisible(m_Parent->GetSliceDirectionInImageSpace()))
{
// Create a pair for the current annotation
- Vector3f ank_img = a->GetAnchorPoint(m_Parent->GetSliceDirectionInImageSpace());
- Vector3f ank_slice = m_Parent->MapImageToSlice(ank_img);
+ Vector3d ank_img = a->GetAnchorPoint(m_Parent->GetSliceDirectionInImageSpace());
+ Vector3d ank_slice = m_Parent->MapImageToSlice(ank_img);
long hash = ank_slice[2] * 100000000l + ank_slice[1] * 10000l + ank_slice[0];
@@ -618,12 +618,12 @@ void AnnotationModel::GoToNextOrPrevAnnotation(int direction)
this->InvokeEvent(ModelUpdateEvent());
}
-bool AnnotationModel::TestPointInClickRadius(const Vector3f &xClickSlice,
- const Vector3f &xPointSlice,
+bool AnnotationModel::TestPointInClickRadius(const Vector3d &xClickSlice,
+ const Vector3d &xPointSlice,
int logical_pixels)
{
- Vector2f clickW = m_Parent->MapSliceToWindow(xClickSlice);
- Vector2f pointW = m_Parent->MapSliceToWindow(xPointSlice);
+ Vector2d clickW = m_Parent->MapSliceToWindow(xClickSlice);
+ Vector2d pointW = m_Parent->MapSliceToWindow(xPointSlice);
int vppr = m_Parent->GetSizeReporter()->GetViewportPixelRatio();
return
@@ -632,8 +632,7 @@ bool AnnotationModel::TestPointInClickRadius(const Vector3f &xClickSlice,
fabs(clickW[1] - pointW[1]) <= logical_pixels * vppr;
}
-void AnnotationModel::MoveAnnotationHandle(
- AnnotationModel::AbstractAnnotation *ann, int handle, const Vector3f &deltaPhys)
+void AnnotationModel::MoveAnnotationHandle(AnnotationModel::AbstractAnnotation *ann, int handle, const Vector3d &deltaPhys)
{
// Draw all the line segments
annot::LineSegmentAnnotation *lsa =
@@ -660,13 +659,13 @@ void AnnotationModel::MoveAnnotationHandle(
}
else if(handle == 1)
{
- Vector3f headXSlice = m_Parent->MapImageToSlice(lm.Pos);
- Vector3f tailXSlice = m_Parent->MapPhysicalWindowToSlice(
+ Vector3d headXSlice = m_Parent->MapImageToSlice(lm.Pos);
+ Vector3d tailXSlice = m_Parent->MapPhysicalWindowToSlice(
m_Parent->MapSliceToPhysicalWindow(headXSlice) + lm.Offset);
- Vector3f tailXImage = m_Parent->MapSliceToImage(tailXSlice);
- Vector3f tailXImageMoved = tailXImage + deltaPhys;
- Vector3f tailXSliceMoved = m_Parent->MapImageToSlice(tailXImageMoved);
- Vector2f tailXPhysWinMoved = m_Parent->MapSliceToPhysicalWindow(tailXSliceMoved);
+ Vector3d tailXImage = m_Parent->MapSliceToImage(tailXSlice);
+ Vector3d tailXImageMoved = tailXImage + deltaPhys;
+ Vector3d tailXSliceMoved = m_Parent->MapImageToSlice(tailXImageMoved);
+ Vector2d tailXPhysWinMoved = m_Parent->MapSliceToPhysicalWindow(tailXSliceMoved);
lm.Offset = tailXPhysWinMoved - m_Parent->MapSliceToPhysicalWindow(headXSlice);
}
@@ -694,13 +693,13 @@ AnnotationModel::GetSelectedHandleUnderCusror(const Vector3d &xSlice, int &out_h
if(lsa)
{
// Draw the line
- Vector3f p1 = m_Parent->MapImageToSlice(lsa->GetSegment().first);
- Vector3f p2 = m_Parent->MapImageToSlice(lsa->GetSegment().second);
+ Vector3d p1 = m_Parent->MapImageToSlice(lsa->GetSegment().first);
+ Vector3d p2 = m_Parent->MapImageToSlice(lsa->GetSegment().second);
// Test if either of these points is close to the cursor
- if(this->TestPointInClickRadius(to_float(xSlice), p1, 5))
+ if(this->TestPointInClickRadius(xSlice, p1, 5))
out_handle = 0;
- else if(this->TestPointInClickRadius(to_float(xSlice), p2, 5))
+ else if(this->TestPointInClickRadius(xSlice, p2, 5))
out_handle = 1;
}
@@ -708,14 +707,14 @@ AnnotationModel::GetSelectedHandleUnderCusror(const Vector3d &xSlice, int &out_h
dynamic_cast<annot::LandmarkAnnotation *>(it->GetPointer());
if(lma)
{
- Vector3f xHeadSlice, xTailSlice;
+ Vector3d xHeadSlice, xTailSlice;
annot::Landmark lm = lma->GetLandmark();
this->GetLandmarkArrowPoints(lm, xHeadSlice, xTailSlice);
// Test if either of these points is close to the cursor
- if(this->TestPointInClickRadius(to_float(xSlice), xHeadSlice, 5))
+ if(this->TestPointInClickRadius(xSlice, xHeadSlice, 5))
out_handle = 0;
- else if(this->TestPointInClickRadius(to_float(xSlice), xTailSlice, 5))
+ else if(this->TestPointInClickRadius(xSlice, xTailSlice, 5))
out_handle = 1;
}
@@ -756,7 +755,7 @@ bool AnnotationModel::IsHoveringOverAnnotation(const Vector3d &xSlice)
}
void AnnotationModel::GetLandmarkArrowPoints(const annot::Landmark &lm,
- Vector3f &outHeadXSlice, Vector3f &outTailXSlice)
+ Vector3d &outHeadXSlice, Vector3d &outTailXSlice)
{
// Get the head coordinates in slice units
outHeadXSlice = m_Parent->MapImageToSlice(lm.Pos);
diff --git a/GUI/Model/AnnotationModel.h b/GUI/Model/AnnotationModel.h
index 967f573..b86c490 100644
--- a/GUI/Model/AnnotationModel.h
+++ b/GUI/Model/AnnotationModel.h
@@ -55,7 +55,7 @@ public:
irisIsMacro(MovingSelection)
/** Get the physical length of line segment */
- double GetLineLength(const Vector3f &xSliceA, const Vector3f &xSliceB);
+ double GetLineLength(const Vector3d &xSliceA, const Vector3d &xSliceB);
/** Get the physical length of current line */
double GetCurrentLineLength();
@@ -102,9 +102,9 @@ public:
/** Set the text assigned to the current annotation */
irisGetSetMacro(CurrentAnnotationText, std::string)
- Vector3f GetAnnotationCenter(const AbstractAnnotation *annot);
+ Vector3d GetAnnotationCenter(const AbstractAnnotation *annot);
- void GetLandmarkArrowPoints(const annot::Landmark &lm, Vector3f &outHeadXSlice, Vector3f &outTailXSlice);
+ void GetLandmarkArrowPoints(const annot::Landmark &lm, Vector3d &outHeadXSlice, Vector3d &outTailXSlice);
@@ -121,7 +121,7 @@ protected:
LineSegment m_CurrentLine;
// Motion-related variables
- Vector3f m_DragStart, m_DragLast;
+ Vector3d m_DragStart, m_DragLast;
bool m_MovingSelection;
int m_MovingSelectionHandle;
annot::AbstractAnnotation *m_MovingSelectionHandleAnnot;
@@ -129,7 +129,7 @@ protected:
// Text assigned to the currently drawn annotation
std::string m_CurrentAnnotationText;
- double GetDistanceToLine(const Vector3f &x1, const Vector3f &x2, const Vector3d &point);
+ double GetDistanceToLine(const Vector3d &x1, const Vector3d &x2, const Vector3d &point);
double GetDistanceToLine(LineSegment &line, const Vector3d &point);
double GetPixelDistanceToAnnotation(const AbstractAnnotation *annot, const Vector3d &point);
void AdjustAngleToRoundDegree(LineSegment &ls, int n_degrees);
@@ -138,12 +138,12 @@ protected:
annot::AbstractAnnotation *GetSelectedHandleUnderCusror(const Vector3d &xSlice, int &out_handle);
- bool TestPointInClickRadius(const Vector3f &xClickSlice,
- const Vector3f &xPointSlice,
+ bool TestPointInClickRadius(const Vector3d &xClickSlice,
+ const Vector3d &xPointSlice,
int logical_pixels);
// Apply move command to annotation handle
- void MoveAnnotationHandle(AbstractAnnotation *ann, int handle, const Vector3f &deltaPhys);
+ void MoveAnnotationHandle(AbstractAnnotation *ann, int handle, const Vector3d &deltaPhys);
};
#endif // ANNOTATIONMODEL_H
diff --git a/GUI/Model/Generic3DModel.cxx b/GUI/Model/Generic3DModel.cxx
index 3ceb81b..f7b1c6e 100644
--- a/GUI/Model/Generic3DModel.cxx
+++ b/GUI/Model/Generic3DModel.cxx
@@ -13,6 +13,8 @@
#include "vtkPointData.h"
#include "itkMutexLockHolder.h"
#include "MeshOptions.h"
+#include "ImageWrapperTraits.h"
+#include "SegmentationUpdateIterator.h"
// All the VTK stuff
#include "vtkPolyData.h"
@@ -229,7 +231,7 @@ void Generic3DModel::UpdateSegmentationMesh(itk::Command *callback)
InvokeEvent(ModelUpdateEvent());
}
- catch(vtkstd::bad_alloc &)
+ catch(std::bad_alloc &)
{
throw IRISException("Out of memory during mesh computation");
}
@@ -249,28 +251,61 @@ bool Generic3DModel::AcceptAction()
ToolbarMode3DType mode = m_ParentUI->GetGlobalState()->GetToolbarMode3D();
IRISApplication *app = m_ParentUI->GetDriver();
+ // Get the segmentation image
+ LabelImageWrapper::ImageType *imSeg = app->GetCurrentImageData()->GetSegmentation()->GetImage();
+
// Accept the current action
if(mode == SPRAYPAINT_MODE)
{
+ // Anything to update?
+ bool update = false;
+
// Merge all the spray points into the segmentation
- app->BeginSegmentationUpdate("3D spray paint");
for(int i = 0; i < m_SprayPoints->GetNumberOfPoints(); i++)
{
+ // Find the point in image coordinates
double *x = m_SprayPoints->GetPoint(i);
- Vector3ui pos(
- static_cast<unsigned int>(x[0]),
- static_cast<unsigned int>(x[1]),
- static_cast<unsigned int>(x[2]));
- app->UpdateSegmentationVoxel(pos);
+
+ // Create a region around this one voxel (in the future could use other shapes)
+ SegmentationUpdateIterator::RegionType region;
+ region.SetIndex(0, static_cast<unsigned int>(x[0])); region.SetSize(0, 1);
+ region.SetIndex(1, static_cast<unsigned int>(x[1])); region.SetSize(1, 1);
+ region.SetIndex(2, static_cast<unsigned int>(x[2])); region.SetSize(2, 1);
+
+ // Treat each point as a region update
+ SegmentationUpdateIterator it(imSeg, region,
+ app->GetGlobalState()->GetDrawingColorLabel(),
+ app->GetGlobalState()->GetDrawOverFilter());
+
+ for(; !it.IsAtEnd(); ++it)
+ {
+ it.PaintAsForeground();
+ }
+
+ // Store the delta for this update
+ it.Finalize();
+
+ if(it.GetNumberOfChangedVoxels() > 0)
+ {
+ update = true;
+ app->GetCurrentImageData()->StoreIntermediateUndoDelta(it.RelinquishDelta());
+ }
}
- // Clear the spray points
- m_SprayPoints->GetPoints()->Reset();
- m_SprayPoints->Modified();
- InvokeEvent(SprayPaintEvent());
+ // Store the undo point
+ if(update)
+ {
+ app->GetCurrentImageData()->StoreUndoPoint("3D spray paint");
+ app->RecordCurrentLabelUse();
+
+ // Clear the spray points
+ m_SprayPoints->GetPoints()->Reset();
+ m_SprayPoints->Modified();
+ InvokeEvent(SprayPaintEvent());
+ }
// Return true if anything changed
- return app->EndSegmentationUpdate() > 0;
+ return update;
}
else if(mode == SCALPEL_MODE && m_ScalpelStatus == SCALPEL_LINE_COMPLETED)
{
@@ -280,12 +315,13 @@ bool Generic3DModel::AcceptAction()
// Map these properties into the image coordinates
Vector3d xi = affine_transform_point(m_WorldMatrixInverse, xw);
- Vector3d ni = affine_transform_vector(m_WorldMatrixInverse, nw);
+
+ // Normal is a covariant tensor and has to be transformed by (M^-1)'
+ vnl_matrix<double> Madj = m_WorldMatrix.extract(3,3).transpose();
+ Vector3d ni = Madj * nw;
// Use the driver to relabel the plane
- app->BeginSegmentationUpdate("3D scalpel");
- app->RelabelSegmentationWithCutPlane(ni, dot_product(xi, ni));
- int nMod = app->EndSegmentationUpdate();
+ int nMod = app->RelabelSegmentationWithCutPlane(ni, dot_product(xi, ni));
// Reset the scalpel state, but only if the operation was successful
if(nMod > 0)
@@ -375,7 +411,7 @@ bool Generic3DModel::IntersectSegmentation(int vx, int vy, Vector3i &hit)
int result = 0;
if(m_Driver->IsSnakeModeLevelSetActive())
{
- typedef ImageRayIntersectionFinder<float, SnakeImageHitTester> RayCasterType;
+ typedef ImageRayIntersectionFinder<itk::Image<float, 3>, SnakeImageHitTester> RayCasterType;
RayCasterType caster;
result = caster.FindIntersection(
m_ParentUI->GetDriver()->GetSNAPImageData()->GetSnake()->GetImage(),
@@ -383,7 +419,7 @@ bool Generic3DModel::IntersectSegmentation(int vx, int vy, Vector3i &hit)
}
else
{
- typedef ImageRayIntersectionFinder<LabelType, LabelImageHitTester> RayCasterType;
+ typedef ImageRayIntersectionFinder<LabelImageWrapperTraits::ImageType, LabelImageHitTester> RayCasterType;
RayCasterType caster;
LabelImageHitTester tester(m_ParentUI->GetDriver()->GetColorLabelTable());
caster.SetHitTester(tester);
diff --git a/GUI/Model/GenericSliceModel.cxx b/GUI/Model/GenericSliceModel.cxx
index 8eb5b04..da8165a 100644
--- a/GUI/Model/GenericSliceModel.cxx
+++ b/GUI/Model/GenericSliceModel.cxx
@@ -71,6 +71,10 @@ GenericSliceModel::GenericSliceModel()
// Nothing is hovered over
m_HoveredImageLayerIdModel = NewSimpleConcreteProperty(-1ul);
m_HoveredImageIsThumbnailModel = NewSimpleConcreteProperty(false);
+
+ m_ImageToDisplayTransform = ImageCoordinateTransform::New();
+ m_DisplayToImageTransform = ImageCoordinateTransform::New();
+ m_DisplayToAnatomyTransform = ImageCoordinateTransform::New();
}
void GenericSliceModel::Initialize(GlobalUIModel *model, int index)
@@ -94,7 +98,13 @@ void GenericSliceModel::Initialize(GlobalUIModel *model, int index)
Rebroadcast(dlm, DisplayLayoutModel::LayerLayoutChangeEvent(), ModelUpdateEvent());
// Listen to cursor update events and rebroadcast them for the child model
- m_SliceIndexModel->Rebroadcast(model, CursorUpdateEvent(), ValueChangedEvent());
+ m_SliceIndexModel->Rebroadcast(m_Driver, CursorUpdateEvent(), ValueChangedEvent());
+
+ // Also rebroadcast the cursor change events as a model update event
+ Rebroadcast(m_Driver, CursorUpdateEvent(), ModelUpdateEvent());
+
+ // Rebroadcast our own zoom/map events as model update events
+ Rebroadcast(this, SliceModelGeometryChangeEvent(), ModelUpdateEvent());
// Also listen for changes in the selected layer
AbstractSimpleULongProperty *selLayerModel = m_Driver->GetGlobalState()->GetSelectedLayerIdModel();
@@ -161,6 +171,17 @@ void GenericSliceModel::OnUpdate()
}
}
+ if(m_EventBucket->HasEvent(MainImageDimensionsChangeEvent())
+ || m_EventBucket->HasEvent(ViewportSizeReporter::ViewportResizeEvent())
+ || m_EventBucket->HasEvent(DisplayLayoutModel::LayerLayoutChangeEvent())
+ || m_EventBucket->HasEvent(ValueChangedEvent())
+ || m_EventBucket->HasEvent(CursorUpdateEvent())
+ || m_EventBucket->HasEvent(SliceModelGeometryChangeEvent()))
+ {
+ // Viewport geometry pretty much depends on everything!
+ if(m_SliceInitialized && m_ViewZoom > 1.e-7)
+ this->UpdateUpstreamViewportGeometry();
+ }
}
void GenericSliceModel::ComputeOptimalZoom()
@@ -169,18 +190,18 @@ void GenericSliceModel::ComputeOptimalZoom()
assert(IsSliceInitialized());
// Compute slice size in spatial coordinates
- Vector2f worldSize(
+ Vector2d worldSize(
m_SliceSize[0] * m_SliceSpacing[0],
m_SliceSize[1] * m_SliceSpacing[1]);
// Set the view position (position of the center of the image?)
- m_OptimalViewPosition = worldSize * 0.5f;
+ m_OptimalViewPosition = worldSize * 0.5;
// Reduce the width and height of the slice by the margin
Vector2ui szCanvas = this->GetCanvasSize();
// Compute the ratios of window size to slice size
- Vector2f ratios(
+ Vector2d ratios(
szCanvas(0) / worldSize(0),
szCanvas(1) / worldSize(1));
@@ -212,23 +233,24 @@ GenericSliceModel
}
// Store the transforms between the display and image spaces
- m_ImageToDisplayTransform =
- imageData->GetImageGeometry().GetImageToDisplayTransform(m_Id);
- m_DisplayToImageTransform =
- imageData->GetImageGeometry().GetDisplayToImageTransform(m_Id);
- m_DisplayToAnatomyTransform =
- imageData->GetImageGeometry().GetAnatomyToDisplayTransform(m_Id).Inverse();
+ m_ImageToDisplayTransform->SetTransform(
+ imageData->GetImageGeometry().GetImageToDisplayTransform(m_Id));
+ m_DisplayToImageTransform->SetTransform(
+ imageData->GetImageGeometry().GetDisplayToImageTransform(m_Id));
+
+ imageData->GetImageGeometry().GetAnatomyToDisplayTransform(m_Id)->
+ ComputeInverse(m_DisplayToAnatomyTransform);
// Get the volume extents & voxel scale factors
Vector3ui imageSizeInImageSpace = m_ImageData->GetVolumeExtents();
- Vector3f imageScalingInImageSpace = to_float(m_ImageData->GetImageSpacing());
+ Vector3d imageScalingInImageSpace = to_double(m_ImageData->GetImageSpacing());
// Initialize quantities that depend on the image and its transform
for(unsigned int i = 0; i < 3; i++)
{
// Get the direction in image space that corresponds to the i'th
// direction in slice space
- m_ImageAxes[i] = m_DisplayToImageTransform.GetCoordinateIndexZeroBased(i);
+ m_ImageAxes[i] = m_DisplayToImageTransform->GetCoordinateIndexZeroBased(i);
// Record the size and scaling of the slice
m_SliceSize[i] = imageSizeInImageSpace[m_ImageAxes[i]];
@@ -244,6 +266,9 @@ GenericSliceModel
// Compute the optimal zoom for this slice
ComputeOptimalZoom();
+ // Set the zoom to optimal zoom for starters
+ m_ViewZoom = m_OptimalZoom;
+
// Fire a modified event, forcing a repaint of the window
InvokeEvent(ModelUpdateEvent());
}
@@ -273,63 +298,62 @@ GenericSliceModel
SetViewPosition(m_OptimalViewPosition);
}
-Vector3f
-GenericSliceModel
-::MapSliceToImage(const Vector3f &xSlice)
+Vector3d GenericSliceModel::MapSliceToImage(const Vector3d &xSlice)
{
assert(IsSliceInitialized());
// Get corresponding position in display space
- return m_DisplayToImageTransform.TransformPoint(xSlice);
+ return m_DisplayToImageTransform->TransformPoint(xSlice);
+}
+
+Vector3d GenericSliceModel::MapSliceToImagePhysical(const Vector3d &xSlice)
+{
+ Vector3d xImage = this->MapSliceToImage(xSlice);
+ ImageWrapperBase *main = this->GetDriver()->GetCurrentImageData()->GetMain();
+ return main->TransformVoxelCIndexToPosition(xImage);
}
/**
* Map a point in image coordinates to slice coordinates
*/
-Vector3f
-GenericSliceModel
-::MapImageToSlice(const Vector3f &xImage)
+Vector3d GenericSliceModel::MapImageToSlice(const Vector3d &xImage)
{
assert(IsSliceInitialized());
// Get corresponding position in display space
- return m_ImageToDisplayTransform.TransformPoint(xImage);
+ return m_ImageToDisplayTransform->TransformPoint(xImage);
}
-Vector2f
-GenericSliceModel
-::MapSliceToWindow(const Vector3f &xSlice)
+Vector2d GenericSliceModel::MapSliceToWindow(const Vector3d &xSlice)
{
assert(IsSliceInitialized());
// Adjust the slice coordinates by the scaling amounts
- Vector2f uvScaled(
- xSlice(0) * m_SliceSpacing(0),xSlice(1) * m_SliceSpacing(1));
+ Vector2d uvScaled(
+ xSlice(0) * m_SliceSpacing(0), xSlice(1) * m_SliceSpacing(1));
// Compute the window coordinates
Vector2ui size = this->GetCanvasSize();
- Vector2f uvWindow =
+ Vector2d uvWindow =
m_ViewZoom * (uvScaled - m_ViewPosition) +
- Vector2f(0.5f * size[0], 0.5f * size[1]);
+ Vector2d(0.5 * size[0], 0.5 * size[1]);
// That's it, the projection matrix is set up in the scaled-slice coordinates
return uvWindow;
}
-Vector3f
-GenericSliceModel
-::MapWindowToSlice(const Vector2f &uvWindow)
+Vector3d GenericSliceModel::MapWindowToSlice(const Vector2d &uvWindow)
{
assert(IsSliceInitialized() && m_ViewZoom > 0);
// Compute the scaled slice coordinates
Vector2ui size = this->GetCanvasSize();
- Vector2f winCenter(0.5f * size[0],0.5f * size[1]);
- Vector2f uvScaled =
+ Vector2d winCenter(0.5 * size[0],0.5 * size[1]);
+ Vector2d uvScaled =
m_ViewPosition + (uvWindow - winCenter) / m_ViewZoom;
// The window coordinates are already in the scaled-slice units
- Vector3f uvSlice(
+ Vector3d uvSlice(
uvScaled(0) / m_SliceSpacing(0),
uvScaled(1) / m_SliceSpacing(1),
this->GetCursorPositionInSliceCoordinates()[2]);
@@ -338,16 +362,14 @@ GenericSliceModel
return uvSlice;
}
-Vector3f
-GenericSliceModel
-::MapWindowOffsetToSliceOffset(const Vector2f &uvWindowOffset)
+Vector3d GenericSliceModel::MapWindowOffsetToSliceOffset(const Vector2d &uvWindowOffset)
{
assert(IsSliceInitialized() && m_ViewZoom > 0);
- Vector2f uvScaled = uvWindowOffset / m_ViewZoom;
+ Vector2d uvScaled = uvWindowOffset / m_ViewZoom;
// The window coordinates are already in the scaled-slice units
- Vector3f uvSlice(
+ Vector3d uvSlice(
uvScaled(0) / m_SliceSpacing(0),
uvScaled(1) / m_SliceSpacing(1),
0);
@@ -356,27 +378,23 @@ GenericSliceModel
return uvSlice;
}
-Vector2f
-GenericSliceModel
-::MapSliceToPhysicalWindow(const Vector3f &xSlice)
+Vector2d GenericSliceModel::MapSliceToPhysicalWindow(const Vector3d &xSlice)
{
assert(IsSliceInitialized());
// Compute the physical window coordinates
- Vector2f uvPhysical;
+ Vector2d uvPhysical;
uvPhysical[0] = xSlice[0] * m_SliceSpacing[0];
uvPhysical[1] = xSlice[1] * m_SliceSpacing[1];
return uvPhysical;
}
-Vector3f
-GenericSliceModel
-::MapPhysicalWindowToSlice(const Vector2f &uvPhysical)
+Vector3d GenericSliceModel::MapPhysicalWindowToSlice(const Vector2d &uvPhysical)
{
assert(IsSliceInitialized());
- Vector3f xSlice;
+ Vector3d xSlice;
xSlice[0] = uvPhysical[0] / m_SliceSpacing[0];
xSlice[1] = uvPhysical[1] / m_SliceSpacing[1];
xSlice[2] = this->GetCursorPositionInSliceCoordinates()[2];
@@ -389,12 +407,12 @@ GenericSliceModel
::ResetViewPosition()
{
// Compute slice size in spatial coordinates
- Vector2f worldSize(
+ Vector2d worldSize(
m_SliceSize[0] * m_SliceSpacing[0],
m_SliceSize[1] * m_SliceSpacing[1]);
// Set the view position (position of the center of the image?)
- m_ViewPosition = worldSize * 0.5f;
+ m_ViewPosition = worldSize * 0.5;
// Update view
InvokeEvent(SliceModelGeometryChangeEvent());
@@ -402,41 +420,39 @@ GenericSliceModel
void
GenericSliceModel
-::SetViewPositionRelativeToCursor(Vector2f offset)
+::SetViewPositionRelativeToCursor(Vector2d offset)
{
// Get the crosshair position
Vector3ui xCursorInteger = m_Driver->GetCursorPosition();
// Shift the cursor position by by 0.5 in order to have it appear
// between voxels
- Vector3f xCursorImage = to_float(xCursorInteger) + Vector3f(0.5f);
+ Vector3d xCursorImage = to_double(xCursorInteger) + Vector3d(0.5);
// Get the cursor position on the slice
- Vector3f xCursorSlice = MapImageToSlice(xCursorImage);
+ Vector3d xCursorSlice = MapImageToSlice(xCursorImage);
// Subtract from the view position
- Vector2f vp;
+ Vector2d vp;
vp[0] = offset[0] + xCursorSlice[0] * m_SliceSpacing[0];
vp[1] = offset[1] + xCursorSlice[1] * m_SliceSpacing[1];
SetViewPosition(vp);
}
-Vector2f
-GenericSliceModel
-::GetViewPositionRelativeToCursor()
+Vector2d GenericSliceModel::GetViewPositionRelativeToCursor()
{
// Get the crosshair position
Vector3ui xCursorInteger = m_Driver->GetCursorPosition();
// Shift the cursor position by by 0.5 in order to have it appear
// between voxels
- Vector3f xCursorImage = to_float(xCursorInteger) + Vector3f(0.5f);
+ Vector3d xCursorImage = to_double(xCursorInteger) + Vector3d(0.5);
// Get the cursor position on the slice
- Vector3f xCursorSlice = MapImageToSlice(xCursorImage);
+ Vector3d xCursorSlice = MapImageToSlice(xCursorImage);
// Subtract from the view position
- Vector2f offset;
+ Vector2d offset;
offset[0] = m_ViewPosition[0] - xCursorSlice[0] * m_SliceSpacing[0];
offset[1] = m_ViewPosition[1] - xCursorSlice[1] * m_SliceSpacing[1];
@@ -445,11 +461,11 @@ GenericSliceModel
void GenericSliceModel::CenterViewOnCursor()
{
- Vector2f offset; offset.fill(0.0f);
+ Vector2d offset; offset.fill(0.0);
this->SetViewPositionRelativeToCursor(offset);
}
-void GenericSliceModel::SetViewZoom(float zoom)
+void GenericSliceModel::SetViewZoom(double zoom)
{
assert(zoom > 0);
m_ViewZoom = zoom;
@@ -457,10 +473,15 @@ void GenericSliceModel::SetViewZoom(float zoom)
this->InvokeEvent(SliceModelGeometryChangeEvent());
}
-void GenericSliceModel::ZoomInOrOut(float factor)
+void GenericSliceModel::SetViewZoomInLogicalPixels(double zoom)
{
- float oldzoom = m_ViewZoom;
- float newzoom = oldzoom * factor;
+ this->SetViewZoom(zoom * m_SizeReporter->GetViewportPixelRatio());
+}
+
+void GenericSliceModel::ZoomInOrOut(double factor)
+{
+ double oldzoom = m_ViewZoom;
+ double newzoom = oldzoom * factor;
if( (oldzoom < m_OptimalZoom && newzoom > m_OptimalZoom) ||
(oldzoom > m_OptimalZoom && newzoom < m_OptimalZoom) )
@@ -471,6 +492,12 @@ void GenericSliceModel::ZoomInOrOut(float factor)
SetViewZoom(newzoom);
}
+double GenericSliceModel::GetViewZoomInLogicalPixels() const
+{
+ double zoom_physical = this->GetViewZoom();
+ return zoom_physical / m_SizeReporter->GetViewportPixelRatio();
+}
+
/*
GenericSliceModel *
GenericSliceModel
@@ -502,12 +529,12 @@ const SliceViewportLayout::SubViewport *GenericSliceModel::GetHoveredViewport()
return NULL;
}
-Vector3f GenericSliceModel::GetCursorPositionInSliceCoordinates()
+Vector3d GenericSliceModel::GetCursorPositionInSliceCoordinates()
{
Vector3ui cursorImageSpace = m_Driver->GetCursorPosition();
- Vector3f cursorDisplaySpace =
- m_ImageToDisplayTransform.TransformPoint(
- to_float(cursorImageSpace) + Vector3f(0.5f));
+ Vector3d cursorDisplaySpace =
+ m_ImageToDisplayTransform->TransformPoint(
+ to_double(cursorImageSpace) + Vector3d(0.5));
return cursorDisplaySpace;
}
@@ -531,18 +558,18 @@ void GenericSliceModel::ComputeThumbnailProperties()
const GlobalDisplaySettings *gds = m_ParentUI->GetGlobalDisplaySettings();
// The thumbnail will occupy a specified fraction of the target canvas
- float xFraction = 0.01f * gds->GetZoomThumbnailSizeInPercent();
+ double xFraction = 0.01 * gds->GetZoomThumbnailSizeInPercent();
// But it must not exceed a predefined size in pixels in either dimension
- float xThumbMax = gds->GetZoomThumbnailMaximumSize();
+ double xThumbMax = gds->GetZoomThumbnailMaximumSize();
// Recompute the fraction based on maximum size restriction
Vector2ui size = this->GetCanvasSize();
- float xNewFraction = xFraction;
+ double xNewFraction = xFraction;
if( size[0] * xNewFraction > xThumbMax )
- xNewFraction = xThumbMax * 1.0f / size[0];
+ xNewFraction = xThumbMax * 1.0 / size[0];
if( size[1] * xNewFraction > xThumbMax )
- xNewFraction = xThumbMax * 1.0f / size[1];
+ xNewFraction = xThumbMax * 1.0 / size[1];
// Set the position and size of the thumbnail, in pixels
m_ThumbnailZoom = xNewFraction * m_OptimalZoom;
@@ -565,7 +592,7 @@ void GenericSliceModel::OnSourceDataUpdate()
}
*/
-void GenericSliceModel::SetViewPosition(Vector2f pos)
+void GenericSliceModel::SetViewPosition(Vector2d pos)
{
if(m_ViewPosition != pos)
{
@@ -618,7 +645,7 @@ GenericSliceModel
::MergeSliceSegmentation(itk::Image<unsigned char, 2> *drawing)
{
// Z position of slice
- float zpos = this->GetCursorPositionInSliceCoordinates()[2];
+ double zpos = this->GetCursorPositionInSliceCoordinates()[2];
return m_Driver->UpdateSegmentationWithSliceDrawing(
drawing, m_DisplayToImageTransform, zpos, "Polygon Drawing");
}
@@ -632,6 +659,14 @@ Vector2ui GenericSliceModel::GetSize()
return Vector2ui(viewport[0] / cols, viewport[1] / rows);
}
+Vector2ui GenericSliceModel::GetSizeInLogicalPixels()
+{
+ Vector2ui size = this->GetSize();
+ size[0] = (unsigned int) (size[0] / m_SizeReporter->GetViewportPixelRatio());
+ size[1] = (unsigned int) (size[1] / m_SizeReporter->GetViewportPixelRatio());
+ return size;
+}
+
Vector2ui GenericSliceModel::GetCanvasSize()
{
assert(m_ViewportLayout.vpList.size() > 0);
@@ -639,6 +674,11 @@ Vector2ui GenericSliceModel::GetCanvasSize()
return m_ViewportLayout.vpList.front().size;
}
+bool GenericSliceModel::IsTiling() const
+{
+ return m_Driver->GetGlobalState()->GetSliceViewLayerLayout() == LAYOUT_TILED;
+}
+
void GenericSliceModel::GetNonThumbnailViewport(Vector2ui &pos, Vector2ui &size)
{
// Initialize to the entire view
@@ -864,8 +904,8 @@ void GenericSliceModel::UpdateViewportLayout()
}
else
{
- float cell_w = w / ncols;
- float cell_h = h / nrows;
+ double cell_w = w / ncols;
+ double cell_h = h / nrows;
for(int irow = 0; irow < nrows; irow++)
for(int icol = 0; icol < ncols; icol++)
if(m_ViewportLayout.vpList.size() < n_base_layers)
@@ -880,6 +920,83 @@ void GenericSliceModel::UpdateViewportLayout()
}
}
+void GenericSliceModel::UpdateUpstreamViewportGeometry()
+{
+ // In this function, we have to figure out where the active viewport
+ // is located in the physical image space of ITK-SNAP.
+ GenericImageData *gid = this->GetImageData();
+
+ // Get the display image spec corresponding to the current viewport
+ GenericImageData::ImageBaseType *dispimg = gid->GetDisplayViewportGeometry(this->GetId());
+
+ // Get the primary viewport - this is what affects everything else
+ SliceViewportLayout::SubViewport &vp = m_ViewportLayout.vpList.front();
+
+ // The size of the viewport is fairly easy
+ GenericImageData::RegionType region;
+ region.SetSize(0, vp.size[0]); region.SetSize(1, vp.size[1]); region.SetSize(2, 1);
+
+ // The spacing of the viewport in physical units. This refers to the size of each
+ // pixel. The easiest way to determine this is to map the edges of the viewport to
+ // physical image coordinates
+ Vector2d u[3];
+ Vector3d s[4];
+ Vector3d x[4];
+
+ // Define the corners of the viewport in screen pixel units
+ u[0][0] = 0;
+ u[0][1] = 0;
+ u[1][0] = u[0][0] + vp.size[0]; u[1][1] = u[0][1];
+ u[2][0] = u[0][0]; u[2][1] = u[0][1] + vp.size[1];
+
+ // Map into slice coordinates, adding the third dimension
+ s[0] = this->MapWindowToSlice(u[0]);
+ s[1] = this->MapWindowToSlice(u[1]);
+ s[2] = this->MapWindowToSlice(u[2]);
+ s[3] = this->MapWindowToSlice(u[0]);
+ s[3][2] += m_DisplayToImageTransform->GetCoordinateOrientation(2);
+
+ // Map these four points into the physical image space
+ for(int i = 0; i < 4; i++)
+ {
+ // Shift by half-voxel in the in-plane dimensions
+ s[i][0] -= this->GetDisplayToImageTransform()->GetCoordinateOrientation(0) * 0.5;
+ s[i][1] -= this->GetDisplayToImageTransform()->GetCoordinateOrientation(1) * 0.5;
+
+ itk::ContinuousIndex<double, 3> j = to_itkContinuousIndex(this->MapSliceToImage(s[i]));
+ itk::Point<double, 3> px;
+ gid->GetMain()->GetImageBase()->TransformContinuousIndexToPhysicalPoint(j, px);
+ x[i] = Vector3d(px);
+ }
+
+ // Spacing - divide the length of each edge by the size in voxels
+ GenericImageData::ImageBaseType::SpacingType spacing;
+ spacing[0] = (x[1] - x[0]).magnitude() / vp.size[0];
+ spacing[1] = (x[2] - x[0]).magnitude() / vp.size[1];
+ spacing[2] = (x[3] - x[0]).magnitude();
+
+ // Origin - the coordinates of the first point, plus a half-voxel
+ Vector3d origin = x[0];
+ origin += (x[1] - x[0]) / (2.0 * vp.size[0]);
+ origin += (x[2] - x[0]) / (2.0 * vp.size[1]);
+ origin -= (x[3] - x[0]) / 2.0;
+
+ // Direction cosines - these are the normalized directions
+ GenericImageData::ImageBaseType::DirectionType dir;
+ for(int col = 0; col < 3; col++)
+ {
+ Vector3d dirvec = (x[col+1] - x[0]).normalize();
+ for(int row = 0; row < 3; row++)
+ dir(row, col) = dirvec[row];
+ }
+
+ // Set all of the parameters for the reference image
+ dispimg->SetSpacing(spacing);
+ dispimg->SetOrigin(to_itkPoint(origin));
+ dispimg->SetDirection(dir);
+ dispimg->SetRegions(region);
+}
+
ImageWrapperBase *GenericSliceModel::GetLayerForNthTile(int row, int col)
{
// Number of divisions
diff --git a/GUI/Model/GenericSliceModel.h b/GUI/Model/GenericSliceModel.h
index fe232ab..b782c3a 100644
--- a/GUI/Model/GenericSliceModel.h
+++ b/GUI/Model/GenericSliceModel.h
@@ -58,7 +58,8 @@ itkEventMacro(SliceModelGeometryChangeEvent, IRISEvent)
struct SliceViewportLayout
{
public:
- struct SubViewport {
+ struct SubViewport
+ {
// Size and position of the viewport
Vector2ui pos, size;
@@ -84,6 +85,29 @@ public:
information. Ideally, you should be able to store and retrieve the
state of this object between sessions.
+ This class utilizes multiple coordinate systems, described below:
+
+ WINDOW COORDINATE SYSTEM
+ ------------------------
+ This is a 2D coordinate system describing the primary viewport in the 2D
+ slice window. The origin (0, 0) of this coordinate system is the bottom
+ left corner of the active viewport. The upper right corner of the active
+ viewport has window coordinate obtained from GetCanvasSize()
+
+ SLICE COORDINATE SYSTEM
+ -----------------------
+ This is a 3D coordinate system relative to the image slice displayed in
+ the window.
+ x: in units of pixels along the horizontal axis of the window
+ y: in units of pixels along the vertical axis of the window
+ z: slice index of the currently displayed slice
+
+ Coordinate x=0, y=0 corresponds to the (0,0) voxel in the displayed slice
+
+ IMAGE COORDINATE SYSTEM
+ -----------------------
+ This is the ITK image coordinate system
+
This class is meant to be used with arbitrary GUI environments. It is
unaware of Qt, FLTK, etc.
*/
@@ -137,44 +161,50 @@ public:
* Map a point in window coordinates to a point in slice coordinates
* (Window coordinates are the ones stored in FLTKEvent.xSpace)
*/
- Vector3f MapWindowToSlice(const Vector2f &xWindow);
+ Vector3d MapWindowToSlice(const Vector2d &xWindow);
/**
* Map an offset in window coordinates to an offset in slice coordinates
*/
- Vector3f MapWindowOffsetToSliceOffset(const Vector2f &xWindowOffset);
+ Vector3d MapWindowOffsetToSliceOffset(const Vector2d &xWindowOffset);
/**
* Map a point in slice coordinates to a point in window coordinates
* (Window coordinates are the ones stored in FLTKEvent.xSpace)
*/
- Vector2f MapSliceToWindow(const Vector3f &xSlice);
+ Vector2d MapSliceToWindow(const Vector3d &xSlice);
/**
* Map a point in slice coordinates to a point in PHYISCAL window coordinates
*/
- Vector2f MapSliceToPhysicalWindow(const Vector3f &xSlice);
+ Vector2d MapSliceToPhysicalWindow(const Vector3d &xSlice);
/**
* Map a point in PHYISCAL window coordinates to a point in slice coordinates
*/
- Vector3f MapPhysicalWindowToSlice(const Vector2f &uvPhysical);
+ Vector3d MapPhysicalWindowToSlice(const Vector2d &uvPhysical);
/**
* Map a point in slice coordinates to a point in the image coordinates
*/
- Vector3f MapSliceToImage(const Vector3f &xSlice);
+ Vector3d MapSliceToImage(const Vector3d &xSlice);
+
+ /**
+ * Map a point in slice coordinates to a point in image physical coordinates
+ * (the ITK LPS coordinate system)
+ */
+ Vector3d MapSliceToImagePhysical(const Vector3d &xSlice);
/**
* Map a point in image coordinates to slice coordinates
*/
- Vector3f MapImageToSlice(const Vector3f &xImage);
+ Vector3d MapImageToSlice(const Vector3d &xImage);
/**
* Get the cursor position in slice coordinates, shifted to the center
* of the voxel
*/
- Vector3f GetCursorPositionInSliceCoordinates();
+ Vector3d GetCursorPositionInSliceCoordinates();
/**
* Get the slice index for this window
@@ -213,45 +243,51 @@ public:
/** Return the offset from the center of the viewport to the cursor position
* in slice units (#voxels * spacing). This is used to synchronize panning
* across SNAP sessions */
- Vector2f GetViewPositionRelativeToCursor();
+ Vector2d GetViewPositionRelativeToCursor();
/** Set the offset from the center of the viewport to the cursor position */
- void SetViewPositionRelativeToCursor(Vector2f offset);
+ void SetViewPositionRelativeToCursor(Vector2d offset);
/** Center the view on the image crosshairs */
void CenterViewOnCursor();
/** Set the zoom factor (number of pixels on the screen per millimeter in
* image space */
- void SetViewZoom(float zoom);
+ void SetViewZoom(double zoom);
+
+ /** Set the zoom factor in logical units (accounting for Retina displays) */
+ void SetViewZoomInLogicalPixels(double zoom);
/**
* Zoom in/out by a specified factor. This method will 'stop' at the optimal
* zoom if it's between the old zoom and the new zoom
*/
- void ZoomInOrOut(float factor);
+ void ZoomInOrOut(double factor);
/** Get the zoom factor (number of pixels on the screen per millimeter in
* image space */
- irisGetMacro(ViewZoom,float)
+ irisGetMacro(ViewZoom,double)
+
+ /** Get the logical zoom factor (for retina-style displays) */
+ double GetViewZoomInLogicalPixels() const;
/** Computes the zoom that gives the best fit for the window */
void ComputeOptimalZoom();
/** Compute the optimal zoom (best fit) */
- irisGetMacro(OptimalZoom,float)
+ irisGetMacro(OptimalZoom,double)
/** Set the zoom management flag */
irisSetMacro(ManagedZoom,bool)
/** Set the view position **/
- void SetViewPosition(Vector2f);
+ void SetViewPosition(Vector2d pos);
/** Get the view position **/
- irisGetMacro(ViewPosition, Vector2f)
+ irisGetMacro(ViewPosition, Vector2d)
/** Get the slice spacing in the display space orientation */
- irisGetMacro(SliceSpacing,Vector3f)
+ irisGetMacro(SliceSpacing,Vector3d)
/** Get the slice spacing in the display space orientation */
irisGetMacro(SliceSize,Vector3i)
@@ -262,6 +298,9 @@ public:
/** Get the physical size of the window (updated from widget via events) */
Vector2ui GetSize();
+ /** Get the physical size of the window (updated from widget via events) */
+ Vector2ui GetSizeInLogicalPixels();
+
/**
* Get the size of the canvas on which the slice will be rendered. When the
* view is in tiled mode, this reports the size of one of the tiles. When the
@@ -269,6 +308,11 @@ public:
*/
Vector2ui GetCanvasSize();
+ /**
+ * Whether rendering in tiled mode
+ */
+ bool IsTiling() const;
+
/** Has the slice model been initialized with image data? */
irisIsMacro(SliceInitialized)
@@ -279,11 +323,11 @@ public:
irisGetMacro(ZoomThumbnailPosition, Vector2i)
irisGetMacro(ZoomThumbnailSize, Vector2i)
- irisGetMacro(ThumbnailZoom, float)
+ irisGetMacro(ThumbnailZoom, double)
- irisGetMacro(ImageToDisplayTransform, const ImageCoordinateTransform &)
- irisGetMacro(DisplayToAnatomyTransform, const ImageCoordinateTransform &)
- irisGetMacro(DisplayToImageTransform, const ImageCoordinateTransform &)
+ irisGetMacro(ImageToDisplayTransform, const ImageCoordinateTransform *)
+ irisGetMacro(DisplayToAnatomyTransform, const ImageCoordinateTransform *)
+ irisGetMacro(DisplayToImageTransform, const ImageCoordinateTransform *)
irisGetMacro(ViewportLayout, const SliceViewportLayout &)
@@ -368,13 +412,13 @@ protected:
int m_ImageAxes[3];
// The transform from image coordinates to display coordinates
- ImageCoordinateTransform m_ImageToDisplayTransform;
+ SmartPtr<ImageCoordinateTransform> m_ImageToDisplayTransform;
// The transform from display coordinates to image coordinates
- ImageCoordinateTransform m_DisplayToImageTransform;
+ SmartPtr<ImageCoordinateTransform> m_DisplayToImageTransform;
// The transform from display coordinates to patient coordinates
- ImageCoordinateTransform m_DisplayToAnatomyTransform;
+ SmartPtr<ImageCoordinateTransform> m_DisplayToAnatomyTransform;
// Dimensions of the current slice (the third component is the size
// of the image in the slice direction)
@@ -382,19 +426,19 @@ protected:
// Pixel dimensions for the slice. (the third component is the pixel
// width in the slice direction)
- Vector3f m_SliceSpacing;
+ Vector3d m_SliceSpacing;
// Position of visible window in slice space coordinates
- Vector2f m_ViewPosition;
+ Vector2d m_ViewPosition;
// The view position where the slice wants to be
- Vector2f m_OptimalViewPosition;
+ Vector2d m_OptimalViewPosition;
// The number of screen pixels per mm of image
- float m_ViewZoom;
+ double m_ViewZoom;
// The zoom level at which the slice fits snugly into the window
- float m_OptimalZoom;
+ double m_OptimalZoom;
// Flag indicating whether the window's zooming is managed externally
// by the SliceWindowCoordinator
@@ -433,6 +477,7 @@ protected:
/** Update the state of the viewport based on current layout settings */
void UpdateViewportLayout();
+ void UpdateUpstreamViewportGeometry();
};
#endif // GENERICSLICEMODEL_H
diff --git a/GUI/Model/GlobalUIModel.cxx b/GUI/Model/GlobalUIModel.cxx
index f2690d8..7ec6d4d 100644
--- a/GUI/Model/GlobalUIModel.cxx
+++ b/GUI/Model/GlobalUIModel.cxx
@@ -67,6 +67,9 @@
#include "ImageIOWizardModel.h"
#include "IntensityCurveInterface.h"
#include "ColorLabelQuickListModel.h"
+#include "InterpolateLabelModel.h"
+#include "RegistrationModel.h"
+#include "InteractiveRegistrationModel.h"
#include <itksys/SystemTools.hxx>
@@ -97,6 +100,10 @@ GlobalUIModel::GlobalUIModel()
m_PaintbrushSettingsModel = PaintbrushSettingsModel::New();
m_PaintbrushSettingsModel->SetParentModel(this);
+ // Registration model
+ m_RegistrationModel = RegistrationModel::New();
+ m_RegistrationModel->SetParentModel(this);
+
// Create the slice models
for (unsigned int i = 0; i < 3; i++)
{
@@ -117,6 +124,10 @@ GlobalUIModel::GlobalUIModel()
m_AnnotationModel[i] = AnnotationModel::New();
m_AnnotationModel[i]->SetParent(m_SliceModel[i]);
+
+ m_InteractiveRegistrationModel[i] = InteractiveRegistrationModel::New();
+ m_InteractiveRegistrationModel[i]->SetParent(m_SliceModel[i]);
+ m_InteractiveRegistrationModel[i]->SetRegistrationModel(m_RegistrationModel);
}
@@ -195,6 +206,10 @@ GlobalUIModel::GlobalUIModel()
m_ColorLabelQuickListModel = ColorLabelQuickListModel::New();
m_ColorLabelQuickListModel->SetParentModel(this);
+ // Interpolation dialog
+ m_InterpolateLabelModel = InterpolateLabelModel::New();
+ m_InterpolateLabelModel->SetParentModel(this);
+
// Set up the cursor position model
m_CursorPositionModel = wrapGetterSetterPairAsProperty(
this,
@@ -492,9 +507,10 @@ void GlobalUIModel::LoadUserPreferences()
dbs->ReadFromRegistry(
si->Folder("UserInterface.DefaultBehavior"));
- // Read the global display properties
- m_GlobalDisplaySettings->ReadFromRegistry(
- si->Folder("SliceView.DisplaySettings"));
+ // Read the global display properties and apply them
+ SmartPtr<GlobalDisplaySettings> temp_gds = GlobalDisplaySettings::New();
+ temp_gds->ReadFromRegistry(si->Folder("SliceView.DisplaySettings"));
+ this->SetGlobalDisplaySettings(temp_gds);
// Read the 3D mesh options
m_Driver->GetGlobalState()->GetMeshOptions()->ReadFromRegistry(
diff --git a/GUI/Model/GlobalUIModel.h b/GUI/Model/GlobalUIModel.h
index e33af84..f0aa0de 100644
--- a/GUI/Model/GlobalUIModel.h
+++ b/GUI/Model/GlobalUIModel.h
@@ -40,6 +40,7 @@ class GenericSliceModel;
class OrthogonalSliceCursorNavigationModel;
class PolygonDrawingModel;
class AnnotationModel;
+class InteractiveRegistrationModel;
class SliceWindowCoordinator;
class GuidedNativeImageIO;
class SystemInterface;
@@ -72,6 +73,8 @@ class GlobalDisplaySettings;
class ImageIOWizardModel;
class ImageWrapperBase;
class ColorLabelQuickListModel;
+class InterpolateLabelModel;
+class RegistrationModel;
namespace itk
{
@@ -174,6 +177,12 @@ public:
return m_AnnotationModel[i];
}
+ /** Get the interactive registration model for each slice */
+ InteractiveRegistrationModel *GetInteractiveRegistrationModel(unsigned int i) const
+ {
+ return m_InteractiveRegistrationModel[i];
+ }
+
/** Get the model for intensity curve navigation */
irisGetMacro(IntensityCurveModel, IntensityCurveModel *)
@@ -231,6 +240,12 @@ public:
/** Model for the list of recently used color labels */
irisGetMacro(ColorLabelQuickListModel, ColorLabelQuickListModel *)
+ /** Model for the interpolate labels dialog */
+ irisGetMacro(InterpolateLabelModel, InterpolateLabelModel *)
+
+ /** Model for image registration */
+ irisGetMacro(RegistrationModel, RegistrationModel *)
+
/**
Check the state of the system. This class will issue StateChangeEvent()
when one of the flags has changed. This method can be used together with
@@ -343,6 +358,9 @@ protected:
// Models for annotation
SmartPtr<AnnotationModel> m_AnnotationModel[3];
+ // Models for interactive registration
+ SmartPtr<InteractiveRegistrationModel> m_InteractiveRegistrationModel[3];
+
// Window coordinator
SmartPtr<SliceWindowCoordinator> m_SliceCoordinator;
@@ -397,6 +415,12 @@ protected:
// Model for the quick list of recently used labels
SmartPtr<ColorLabelQuickListModel> m_ColorLabelQuickListModel;
+ // Model for the label interpolation dialog
+ SmartPtr<InterpolateLabelModel> m_InterpolateLabelModel;
+
+ // Model for image registration
+ SmartPtr<RegistrationModel> m_RegistrationModel;
+
// Current coordinates of the cursor
SmartPtr<AbstractRangedUIntVec3Property> m_CursorPositionModel;
bool GetCursorPositionValueAndRange(
diff --git a/GUI/Model/ImageIOWizardModel.cxx b/GUI/Model/ImageIOWizardModel.cxx
index ac3f20d..5e71f54 100644
--- a/GUI/Model/ImageIOWizardModel.cxx
+++ b/GUI/Model/ImageIOWizardModel.cxx
@@ -5,13 +5,15 @@
#include "SystemInterface.h"
#include "ImageCoordinateGeometry.h"
#include <itksys/SystemTools.hxx>
-#include "HistoryManager.h"
+
#include "ColorMap.h"
+#include "ImageIODelegates.h"
+#include "HistoryManager.h"
+#include "GenericImageData.h"
#include "IRISException.h"
#include <sstream>
-#include "ImageIODelegates.h"
ImageIOWizardModel::ImageIOWizardModel()
{
@@ -20,9 +22,11 @@ ImageIOWizardModel::ImageIOWizardModel()
m_LoadDelegate = NULL;
m_SaveDelegate = NULL;
m_Overlay = false;
- m_UseRegistration = false;
m_LoadedImage = NULL;
+ // Suggested format is empty
+ m_SuggestedFormat = GuidedNativeImageIO::FORMAT_COUNT;
+
// Initialize various property models
m_StickyOverlayModel = wrapGetterSetterPairAsProperty(
this,
@@ -33,37 +37,6 @@ ImageIOWizardModel::ImageIOWizardModel()
this,
&Self::GetStickyOverlayColorMapValue,
&Self::SetStickyOverlayColorMapValue);
-
- // Initialize the registration models
-
- // Registration mode
- RegistrationModeDomain reg_mode_domain;
- reg_mode_domain[ImageRegistrationManager::RIGID] = "Rigid";
- reg_mode_domain[ImageRegistrationManager::SIMILARITY] = "Rigid with uniform scaling";
- reg_mode_domain[ImageRegistrationManager::AFFINE] = "Affine";
- reg_mode_domain[ImageRegistrationManager::INITONLY] = "Initial alignment only";
- m_RegistrationModeModel = NewConcreteProperty(ImageRegistrationManager::RIGID, reg_mode_domain);
-
- // Registration metric
- RegistrationMetricDomain reg_metric_domain;
- reg_metric_domain[ImageRegistrationManager::NMI] = "Normalized mutual information";
- reg_metric_domain[ImageRegistrationManager::NCC] = "Normalized cross-correlation";
- reg_metric_domain[ImageRegistrationManager::SSD] = "Squared intensity difference";
- m_RegistrationMetricModel = NewConcreteProperty(ImageRegistrationManager::NMI, reg_metric_domain);
-
- // Registration initialization
- RegistrationInitDomain reg_init_domain;
- reg_init_domain[ImageRegistrationManager::HEADERS] = "Align based on image headers";
- reg_init_domain[ImageRegistrationManager::CENTERS] = "Align image centers";
- m_RegistrationInitModel = NewConcreteProperty(ImageRegistrationManager::HEADERS, reg_init_domain);
-
- // Registration manager
- m_RegistrationManager = ImageRegistrationManager::New();
- Rebroadcast(m_RegistrationManager, itk::IterationEvent(), RegistrationProgressEvent());
-
- // Optimization progress renderer
- m_RegistrationProgressRenderer = OptimizationProgressRenderer::New();
- m_RegistrationProgressRenderer->SetModel(this);
}
@@ -81,7 +54,6 @@ ImageIOWizardModel
m_LoadDelegate = NULL;
m_SaveDelegate = delegate;
m_SuggestedFilename = delegate->GetCurrentFilename();
- m_UseRegistration = false;
m_Overlay = false;
m_LoadedImage = NULL;
}
@@ -89,18 +61,15 @@ ImageIOWizardModel
void
ImageIOWizardModel
::InitializeForLoad(GlobalUIModel *parent,
- AbstractLoadImageDelegate *delegate,
- const char *name,
- const char *dispName)
+ AbstractLoadImageDelegate *delegate)
{
m_Parent = parent;
m_Mode = LOAD;
- m_HistoryName = name;
- m_DisplayName = dispName;
+ m_HistoryName = delegate->GetHistoryName();
+ m_DisplayName = delegate->GetDisplayName();
m_GuidedIO = GuidedNativeImageIO::New();
m_LoadDelegate = delegate;
m_SaveDelegate = NULL;
- m_UseRegistration = delegate->GetUseRegistration();
m_Overlay = delegate->IsOverlay();
m_LoadedImage = NULL;
}
@@ -322,7 +291,7 @@ ImageIOWizardModel::FileFormat ImageIOWizardModel::GetSelectedFormat()
return GuidedNativeImageIO::GetFileFormat(m_Registry);
}
-#include "GenericImageData.h"
+
void ImageIOWizardModel::LoadImage(std::string filename)
{
@@ -361,6 +330,9 @@ void ImageIOWizardModel::LoadImage(std::string filename)
regAssoc.Folder("Files.Grey").Update(m_Registry);
si->AssociateRegistryWithFile(
m_GuidedIO->GetFileNameOfNativeImage().c_str(), regAssoc);
+
+ // Also place the IO hints into the layer
+ m_LoadedImage->SetIOHints(m_Registry);
}
catch(IRISException &excIRIS)
{
@@ -399,20 +371,16 @@ void ImageIOWizardModel::Reset()
m_Registry.Clear();
}
-void ImageIOWizardModel::ProcessDicomDirectory(const std::string &filename)
+void ImageIOWizardModel::ProcessDicomDirectory(const std::string &filename,
+ itk::Command *progressCommand)
{
- // Here is a request
- GuidedNativeImageIO::DicomRequest req;
- req.push_back(GuidedNativeImageIO::DicomRequestField(
- 0x0020, 0x0011, "SeriesNumber"));
-
// Get the directory
std::string dir = GetBrowseDirectory(filename);
// Get the registry
try
{
- m_GuidedIO->ParseDicomDirectory(dir, m_DicomContents, req);
+ m_GuidedIO->ParseDicomDirectory(dir, progressCommand);
}
catch (IRISException &ei)
{
@@ -425,13 +393,72 @@ void ImageIOWizardModel::ProcessDicomDirectory(const std::string &filename)
}
}
+std::list<std::string>
+ImageIOWizardModel
+::GetFoundDicomSeriesIds()
+{
+ // Get the DICOM registry from the GuidedIO
+ typedef GuidedNativeImageIO::DicomDirectoryParseResult ParseResult;
+ const ParseResult &pr = m_GuidedIO->GetLastDicomParseResult();
+
+ std::list<std::string> result;
+ for(ParseResult::SeriesMapType::const_iterator it = pr.SeriesMap.begin();
+ it != pr.SeriesMap.end(); ++it)
+ result.push_back(it->first);
+
+ return result;
+}
+
+Registry
+ImageIOWizardModel
+::GetFoundDicomSeriesMetaData(const std::string &series_id)
+{
+ // Get the DICOM registry from the GuidedIO
+ typedef GuidedNativeImageIO::DicomDirectoryParseResult ParseResult;
+ const ParseResult &pr = m_GuidedIO->GetLastDicomParseResult();
+
+ // Registry result
+ Registry r;
+
+ // Find the metadata
+ ParseResult::SeriesMapType::const_iterator it = pr.SeriesMap.find(series_id);
+ if(it != pr.SeriesMap.end())
+ r.Update(it->second.MetaData);
+
+ return r;
+}
+
void ImageIOWizardModel
-::LoadDicomSeries(const std::string &filename, int series)
+::LoadDicomSeries(const std::string &filename,
+ const std::string &series_id)
{
+ // Get the DICOM registry from the GuidedIO
+ typedef GuidedNativeImageIO::DicomDirectoryParseResult ParseResult;
+ const ParseResult &pr = m_GuidedIO->GetLastDicomParseResult();
+
+ // Get the metadata for the current series
+ ParseResult::SeriesMapType::const_iterator itc = pr.SeriesMap.find(series_id);
+ if(itc == pr.SeriesMap.end())
+ throw IRISException("DICOM series id %s not found, logic error",
+ series_id.c_str());
+ Registry meta_current = itc->second.MetaData;
+
// Set up the registry for DICOM IO
- m_Registry["DICOM.SeriesId"] << m_DicomContents[series]["SeriesId"][""];
+ m_Registry["DICOM.SeriesId"] << meta_current["SeriesId"][""];
m_Registry.Folder("DICOM.SeriesFiles").PutArray(
- m_DicomContents[series].Folder("SeriesFiles").GetArray(std::string()));
+ meta_current.Folder("SeriesFiles").GetArray(std::string()));
+
+ // Store information about the entire dicom diretory into a separate subfolder.
+ // This is to allow subsequent quick loading of other DICOM series in the same
+ // directory, e.g., through a menu item on the main menu
+ m_Registry["DICOM.DirectoryInfo.ArraySize"] << pr.SeriesMap.size();
+ int i = 0;
+ for(ParseResult::SeriesMapType::const_iterator it = pr.SeriesMap.begin();
+ it != pr.SeriesMap.end(); ++it, ++i)
+ {
+ Registry &r = m_Registry.Folder(m_Registry.Key("DICOM.DirectoryInfo.Entry[%d]", i));
+ r.Update(it->second.MetaData);
+ }
// Set the format to DICOM
SetSelectedFormat(GuidedNativeImageIO::FORMAT_DICOM_DIR);
@@ -441,6 +468,13 @@ void ImageIOWizardModel
// Call the main load method
this->LoadImage(dir);
+
+ // DICOM filenames are meaningless. Assign a nickname based on series name
+ if(m_LoadedImage->GetCustomNickname().length() == 0)
+ {
+ m_LoadedImage->SetCustomNickname(meta_current["SeriesDescription"][""]);
+ }
+
}
unsigned long ImageIOWizardModel::GetFileSizeInBytes(const std::string &file)
@@ -458,26 +492,6 @@ void ImageIOWizardModel::Finalize()
{
}
-void ImageIOWizardModel::PerformRegistration()
-{
- m_RegistrationManager->PerformRegistration(m_Parent->GetDriver()->GetCurrentImageData(),
- this->GetRegistrationMode(),
- this->GetRegistrationMetric(),
- this->GetRegistrationInit());
-}
-
-
-void ImageIOWizardModel::UpdateImageTransformFromRegistration()
-{
- m_RegistrationManager->UpdateImageTransformFromRegistration(
- m_Parent->GetDriver()->GetCurrentImageData());
-}
-
-double ImageIOWizardModel::GetRegistrationObjective()
-{
- return m_RegistrationManager->GetRegistrationObjective();
-}
-
bool ImageIOWizardModel::GetStickyOverlayValue(bool &value)
{
// Make sure the image has already been loaded
diff --git a/GUI/Model/ImageIOWizardModel.h b/GUI/Model/ImageIOWizardModel.h
index 2ff4bf5..1887fba 100644
--- a/GUI/Model/ImageIOWizardModel.h
+++ b/GUI/Model/ImageIOWizardModel.h
@@ -7,8 +7,6 @@
#include "GuidedNativeImageIO.h"
#include "Registry.h"
#include "ImageIODelegates.h"
-#include "ImageRegistrationManager.h"
-#include "OptimizationProgressRenderer.h"
class GlobalUIModel;
@@ -31,10 +29,6 @@ public:
irisITKObjectMacro(ImageIOWizardModel, AbstractModel)
- itkEventMacro(RegistrationProgressEvent, IRISEvent)
-
- FIRES(RegistrationProgressEvent)
-
typedef GuidedNativeImageIO::FileFormat FileFormat;
enum Mode { LOAD, SAVE };
@@ -48,8 +42,7 @@ public:
// using a smart pointer, so its ownership can be relinquished to the
// wizard.
void InitializeForLoad(GlobalUIModel *parent,
- AbstractLoadImageDelegate *delegate,
- const char *name, const char *dispName);
+ AbstractLoadImageDelegate *delegate);
void InitializeForSave(GlobalUIModel *parent,
AbstractSaveImageDelegate *delegate,
@@ -171,21 +164,30 @@ public:
/**
Load relevant information from DICOM directory
*/
- void ProcessDicomDirectory(const std::string &filename);
+ void ProcessDicomDirectory(const std::string &filename, itk::Command *progressCommand);
/**
- Get the DICOM directory contents
- */
- irisGetMacro(DicomContents, const GuidedNativeImageIO::RegistryArray &)
+ * Get a list of loaded Dicom SeriesIDs. This can be called from the
+ * callback of progressCommand, allowing on the fly updates
+ */
+ std::list<std::string> GetFoundDicomSeriesIds();
+
+ /**
+ * Get the metadata for a single found series Id
+ */
+ Registry GetFoundDicomSeriesMetaData(const std::string &series_id);
/**
Load n-th series from DICOM directory
*/
- void LoadDicomSeries(const std::string &filename, int series);
+ void LoadDicomSeries(const std::string &filename,
+ const std::string &series_id);
irisGetSetMacro(SuggestedFilename, std::string)
+ irisGetSetMacro(SuggestedFormat, GuidedNativeImageIO::FileFormat)
+
/**
Access the registry stored in the model and used for providing hints to
the image IO.
@@ -199,11 +201,6 @@ public:
virtual void Finalize();
/**
- * Set whether the IO module should use registration
- */
- irisGetMacro(UseRegistration, bool)
-
- /**
* Is this an overlay?
*/
irisIsMacro(Overlay)
@@ -214,41 +211,6 @@ public:
/** Which is the colormap of the sticky overlay */
irisSimplePropertyAccessMacro(StickyOverlayColorMap, std::string)
- // Registration mode typedefs
- typedef ImageRegistrationManager::RegistrationMode RegistrationMode;
- typedef ImageRegistrationManager::RegistrationMetric RegistrationMetric;
- typedef ImageRegistrationManager::RegistrationInit RegistrationInit;
-
- // Registration domains
- typedef SimpleItemSetDomain<RegistrationMode, std::string> RegistrationModeDomain;
- typedef SimpleItemSetDomain<RegistrationMetric, std::string> RegistrationMetricDomain;
- typedef SimpleItemSetDomain<RegistrationInit, std::string> RegistrationInitDomain;
-
- // Access to registration models
- irisGenericPropertyAccessMacro(RegistrationMode, RegistrationMode, RegistrationModeDomain)
- irisGenericPropertyAccessMacro(RegistrationMetric, RegistrationMetric, RegistrationMetricDomain)
- irisGenericPropertyAccessMacro(RegistrationInit, RegistrationInit, RegistrationInitDomain)
-
- /**
- * Perform registration between loaded overlay and main image. This operation is meant to
- * be executed in a separate thread. From time to time, it will place the registration
- * results into a thread-safe variable and fire a progress event. Use the method
- * UpdateImageTransformFromRegistration() to apply registration results to the displayed image
- */
- void PerformRegistration();
-
- /**
- * Apply the currently computed transform to the image being loaded - allowing the user to
- * see the registration results on the fly
- */
- void UpdateImageTransformFromRegistration();
-
- /** Get the value of the registration objective function */
- double GetRegistrationObjective();
-
- /** Get the progress renderer object */
- irisGetMacro(RegistrationProgressRenderer, OptimizationProgressRenderer *)
-
protected:
// Standard ITK protected constructors
@@ -280,14 +242,11 @@ protected:
// Whether the layer being loaded is an overlay
bool m_Overlay;
- // Whether registration should be used to load this image
- bool m_UseRegistration;
-
// Suggested filename
std::string m_SuggestedFilename;
- // DICOM support
- GuidedNativeImageIO::RegistryArray m_DicomContents;
+ // Suggested format
+ GuidedNativeImageIO::FileFormat m_SuggestedFormat;
// Overlay display behavior models
SmartPtr<AbstractSimpleBooleanProperty> m_StickyOverlayModel;
@@ -299,21 +258,6 @@ protected:
bool GetStickyOverlayColorMapValue(std::string &value);
void SetStickyOverlayColorMapValue(std::string value);
- // Registration models
- typedef ConcretePropertyModel<RegistrationMode, RegistrationModeDomain> RegistrationModeModel;
- typedef ConcretePropertyModel<RegistrationMetric, RegistrationMetricDomain> RegistrationMetricModel;
- typedef ConcretePropertyModel<RegistrationInit, RegistrationInitDomain> RegistrationInitModel;
-
- SmartPtr<RegistrationModeModel> m_RegistrationModeModel;
- SmartPtr<RegistrationMetricModel> m_RegistrationMetricModel;
- SmartPtr<RegistrationInitModel> m_RegistrationInitModel;
-
- // Registration manager
- SmartPtr<ImageRegistrationManager> m_RegistrationManager;
-
- // Renderer used to plot the metric
- SmartPtr<OptimizationProgressRenderer> m_RegistrationProgressRenderer;
-
// Pointer to the image layer that has been loaded
ImageWrapperBase *m_LoadedImage;
};
diff --git a/GUI/Model/ImageInfoModel.cxx b/GUI/Model/ImageInfoModel.cxx
index 5786f06..ba4b406 100644
--- a/GUI/Model/ImageInfoModel.cxx
+++ b/GUI/Model/ImageInfoModel.cxx
@@ -81,7 +81,7 @@ bool ImageInfoModel
{
if(!this->GetLayer()) return false;
Vector3ui cursor = m_ParentModel->GetDriver()->GetCursorPosition();
- value = GetLayer()->TransformVoxelIndexToPosition(cursor);
+ value = GetLayer()->TransformVoxelIndexToPosition(to_int(cursor));
return true;
}
@@ -90,7 +90,7 @@ bool ImageInfoModel
{
if(!this->GetLayer()) return false;
Vector3ui cursor = m_ParentModel->GetDriver()->GetCursorPosition();
- value = GetLayer()->TransformVoxelIndexToNIFTICoordinates(to_double(cursor));
+ value = GetLayer()->TransformVoxelCIndexToNIFTICoordinates(to_double(cursor));
return true;
}
diff --git a/GUI/Model/ImageRegistrationManager.cxx b/GUI/Model/ImageRegistrationManager.cxx
deleted file mode 100644
index 6fd24ae..0000000
--- a/GUI/Model/ImageRegistrationManager.cxx
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "ImageRegistrationManager.h"
-#include "GenericImageData.h"
-
-#include <itkImageRegistrationMethodv4.h>
-#include <itkAffineTransform.h>
-#include <itkANTSNeighborhoodCorrelationImageToImageMetricv4.h>
-// #include <itkJointHistogramMutualInformationImageToImageMetricv4.h>
-#include <itkMattesMutualInformationImageToImageMetricv4.h>
-#include <itkMeanSquaresImageToImageMetricv4.h>
-#include <itkEuler3DTransform.h>
-#include <itkConjugateGradientLineSearchOptimizerv4.h>
-#include <itkRegistrationParameterScalesFromPhysicalShift.h>
-#include <itkFastMutexLock.h>
-
-#include <itkRecursiveGaussianImageFilter.h>
-
-ImageRegistrationManager::ImageRegistrationManager()
-{
- m_RegistrationResultLock = itk::FastMutexLock::New();
- m_RegistrationResult.Transform = TransformBase::New();
-}
-
-void ImageRegistrationManager::PerformRegistration(GenericImageData *imageData,
- RegistrationMode in_mode,
- RegistrationMetric in_metric,
- RegistrationInit in_init)
-{
- // Get the things we will be registering
- typedef itk::Image<float, 3> ImageType;
-
- // The main image already loaded - this will be the fixed image
- ImageWrapperBase *main = imageData->GetMain();
- ImageWrapperBase *overlay = imageData->GetLastOverlay();
-
- // Cast to floating point format
- SmartPtr<ScalarImageWrapperBase::FloatImageSource> castMain =
- main->GetDefaultScalarRepresentation()->CreateCastToFloatPipeline();
-
- SmartPtr<ScalarImageWrapperBase::FloatImageSource> castOverlay =
- overlay->GetDefaultScalarRepresentation()->CreateCastToFloatPipeline();
-
- // Apply some default smoothing to the images, with standard deviation equal to twice
- // the smallest image spacing
- typedef itk::RecursiveGaussianImageFilter<ImageType, ImageType> SmoothFilter;
- SmartPtr<SmoothFilter> smoothMain = SmoothFilter::New();
- smoothMain->SetInput(castMain->GetOutput());
- smoothMain->SetSigma(castMain->GetOutput()->GetSpacing().GetVnlVector().min_value() * 1.0);
- smoothMain->InPlaceOn();
-
- SmartPtr<SmoothFilter> smoothOverlay = SmoothFilter::New();
- smoothOverlay->SetInput(castOverlay->GetOutput());
- smoothOverlay->SetSigma(castOverlay->GetOutput()->GetSpacing().GetVnlVector().min_value() * 1.0);
- smoothOverlay->InPlaceOn();
-
- // Set up registration (for now, rigid is the default)
- typedef itk::Euler3DTransform<double> TransformType;
- typedef itk::ImageRegistrationMethodv4<ImageType, ImageType, TransformType> RegMethod;
-
- // Set up the registration method
- SmartPtr<RegMethod> method = RegMethod::New();
-
- // Set the fixed and moving images
- method->SetFixedImage(smoothMain->GetOutput());
- method->SetMovingImage(smoothOverlay->GetOutput());
-
- // Set the metric
- typedef itk::MattesMutualInformationImageToImageMetricv4<ImageType, ImageType> MetricType;
- SmartPtr<MetricType> metric = MetricType::New();
-
- metric->SetNumberOfHistogramBins(32);
- metric->SetUseMovingImageGradientFilter( false );
- metric->SetUseFixedImageGradientFilter( false );
- metric->SetUseFixedSampledPointSet( false );
- metric->SetVirtualDomainFromImage(castMain->GetOutput());
-
- method->SetMetric(metric);
- method->SetMetricSamplingStrategy(RegMethod::REGULAR);
- method->SetMetricSamplingPercentage(0.1);
-
- // Set up the number of shrink levels
- RegMethod::ShrinkFactorsArrayType shrinkArray(2);
- shrinkArray[0] = 4;
- shrinkArray[1] = 2;
-
- method->SetNumberOfLevels(2);
- method->SetShrinkFactorsPerLevel(shrinkArray);
-
- RegMethod::SmoothingSigmasArrayType smoothArray(2);
- smoothArray.fill(0.0);
- method->SetSmoothingSigmasPerLevel(smoothArray);
-
- // Set up the scales estimator
- typedef itk::RegistrationParameterScalesFromPhysicalShift<MetricType> ScalesEstimatorType;
- SmartPtr<ScalesEstimatorType> scalesEstimator = ScalesEstimatorType::New();
- scalesEstimator->SetMetric( metric );
- scalesEstimator->SetTransformForward( true );
-
- // Set up the optimizer
- typedef itk::ConjugateGradientLineSearchOptimizerv4Template<double> OptimizerType;
- SmartPtr<OptimizerType> optimizer = OptimizerType::New();
- optimizer->SetLowerLimit( 0 );
- optimizer->SetUpperLimit( 2 );
- optimizer->SetEpsilon( 0.2 );
- optimizer->SetLearningRate( 0.25 );
- optimizer->SetMaximumStepSizeInPhysicalUnits( 0.25 );
- optimizer->SetNumberOfIterations( 1000 );
- optimizer->SetScalesEstimator( scalesEstimator );
- optimizer->SetDoEstimateScales( true );
- optimizer->SetMinimumConvergenceValue( 1e-6 );
- optimizer->SetConvergenceWindowSize( 10 );
- optimizer->SetDoEstimateLearningRateAtEachIteration( true );
- optimizer->SetDoEstimateLearningRateOnce( false );
-
- // Set the optimizer
- method->SetOptimizer(optimizer);
-
- // Print out the optimizer status at every iteration.
- typedef itk::MemberCommand<Self> CommandType;
- SmartPtr<CommandType> command = CommandType::New();
- command->SetCallbackFunction(this, &Self::OnRegistrationUpdate);
- optimizer->AddObserver(itk::IterationEvent(), command);
-
- // Perform registration
- method->Update();
-}
-
-void
-ImageRegistrationManager
-::OnRegistrationUpdate(itk::Object *caller, const itk::EventObject &event)
-{
- // Get the optimizer
- OptimizerType *optimizer = static_cast<OptimizerType *>(caller);
-
- // Obtain a lock on the registration result
- m_RegistrationResultLock->Lock();
-
- // Get the metric value
- m_RegistrationResult.MetricValue = optimizer->GetValue();
-
- // TODO: split by transform type!
-
- // Get the parameters and convert them to a matrix
- typedef itk::Euler3DTransform<double> TransformType;
- SmartPtr<TransformType> transform = TransformType::New();
- transform->SetParameters(optimizer->GetCurrentPosition());
-
- m_RegistrationResult.Transform->SetMatrix(transform->GetMatrix());
- m_RegistrationResult.Transform->SetOffset(transform->GetOffset());
-
- // Release the lock on the registration result
- m_RegistrationResultLock->Unlock();
-
- // Fire an iteration event
- InvokeEvent(itk::IterationEvent());
-}
-
-void ImageRegistrationManager::UpdateImageTransformFromRegistration(GenericImageData *imageData)
-{
- // Make a copy of the registration result
- m_RegistrationResultLock->Lock();
- RegistrationResult result = m_RegistrationResult;
- m_RegistrationResultLock->Unlock();
-
- std::cout << "METRIC Value: " << result.MetricValue << std::endl;
-
- // Apply the registration result to the data
- imageData->GetLastOverlay()->SetITKTransform(
- imageData->GetMain()->GetImageBase(), result.Transform);
-}
-
-double ImageRegistrationManager::GetRegistrationObjective()
-{
- return m_RegistrationResult.MetricValue;
-}
-
diff --git a/GUI/Model/ImageRegistrationManager.h b/GUI/Model/ImageRegistrationManager.h
deleted file mode 100644
index dcfe7a2..0000000
--- a/GUI/Model/ImageRegistrationManager.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef IMAGEREGISTRATIONMANAGER_H
-#define IMAGEREGISTRATIONMANAGER_H
-
-#include "AbstractModel.h"
-#include "itkMatrixOffsetTransformBase.h"
-
-class GenericImageData;
-
-namespace itk {
- class FastMutexLock;
- template <class TFloat> class ConjugateGradientLineSearchOptimizerv4Template;
- template <class TFloat, unsigned int VDim1, unsigned int VDim2> class MatrixOffsetTransformBase;
-}
-
-/**
- * This class encapsulates the logic of rigid/affine image registration. It
- * can perform registration and provide registration results.
- */
-class ImageRegistrationManager : public AbstractModel
-{
-public:
-
- irisITKObjectMacro(ImageRegistrationManager, AbstractModel)
-
- // Registration enums
- enum RegistrationMode { INITONLY = 0, RIGID, SIMILARITY, AFFINE, INVALID_MODE };
- enum RegistrationMetric { NMI = 0, NCC, SSD, INVALID_METRIC };
- enum RegistrationInit { HEADERS=0, CENTERS, INVALID_INIT };
-
- // Perform registration with provided parameters
- void PerformRegistration(GenericImageData *imageData,
- RegistrationMode mode,
- RegistrationMetric metric,
- RegistrationInit init);
-
- // Update image data with the registration progress
- void UpdateImageTransformFromRegistration(GenericImageData *imageData);
-
- // Get the value of the objective function
- double GetRegistrationObjective();
-
-protected:
-
- ImageRegistrationManager();
- ~ImageRegistrationManager() {}
-
- void OnRegistrationUpdate(itk::Object *caller, const itk::EventObject &event);
-
-private:
-
- // Registration typedefs
- typedef itk::ConjugateGradientLineSearchOptimizerv4Template<double> OptimizerType;
- typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformBase;
-
- // Representation of the result of image registration
- struct RegistrationResult {
- double MetricValue;
- SmartPtr<TransformBase> Transform;
- };
-
- // Thread-safe variable for storing registration results
- RegistrationResult m_RegistrationResult;
- SmartPtr<itk::FastMutexLock> m_RegistrationResultLock;
-
- // Initial direction matrix of the image
- vnl_matrix<double> m_InitialDirectionMatrix;
- vnl_vector<double> m_InitialOrigin;
-};
-
-#endif // IMAGEREGISTRATIONMANAGER_H
diff --git a/GUI/Model/InteractiveRegistrationModel.cxx b/GUI/Model/InteractiveRegistrationModel.cxx
new file mode 100644
index 0000000..4024738
--- /dev/null
+++ b/GUI/Model/InteractiveRegistrationModel.cxx
@@ -0,0 +1,259 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: InteractiveRegistrationModel.cxx,v $
+ Language: C++
+ Date: $Date: 2010/10/18 11:25:44 $
+ Version: $Revision: 1.12 $
+ Copyright (c) 2011 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "InteractiveRegistrationModel.h"
+#include "GenericSliceModel.h"
+#include "RegistrationModel.h"
+#include "IRISApplication.h"
+#include "GenericImageData.h"
+#include "ImageWrapperBase.h"
+
+double InteractiveRegistrationModel::GetRotationWidgetRadius()
+{
+ RegistrationModel *rmodel = this->GetRegistrationModel();
+ GenericSliceModel *smodel = this->GetParent();
+ ImageWrapperBase *moving = rmodel->GetMovingLayerWrapper();
+
+ if(!moving)
+ return 0;
+
+ // Get the center of rotation
+ Vector3ui rot_ctr_image = rmodel->GetRotationCenter();
+
+ // Map the center of rotation into the slice coordinates
+ Vector3d rot_ctr_slice = smodel->MapImageToSlice(to_double(rot_ctr_image));
+
+ // Get the corners of the viewport in slice coordinate units
+ const SliceViewportLayout::SubViewport &vp =
+ smodel->GetViewportLayout().vpList.front();
+
+ // We will use a much simplified way of computing the arc. Taking eight points
+ // at the compass directions of the viewport, we will find the one farthest away
+ // from the rotation center, and draw a circle through it
+ int margin = 10;
+ Vector2ui u0(margin, margin);
+ Vector2ui u1(vp.size[0] / 2, vp.size[1] / 2);
+ Vector2ui u2(vp.size[0] - margin, vp.size[1] - margin);
+
+ // Vector2ui u0(vp.pos[0] + margin, vp.pos[1] + margin);
+ // Vector2ui u1(vp.pos[0] + vp.size[0] / 2, vp.pos[1] + vp.size[1] / 2);
+ // Vector2ui u2(vp.pos[0] + vp.size[0] - margin, vp.pos[1] + vp.size[1] - margin);
+
+ std::vector<Vector2ui> xProbe;
+ xProbe.push_back(Vector2ui(u0[0], u0[1]));
+ xProbe.push_back(Vector2ui(u0[0], u1[1]));
+ xProbe.push_back(Vector2ui(u0[0], u2[1]));
+ xProbe.push_back(Vector2ui(u1[0], u2[1]));
+ xProbe.push_back(Vector2ui(u2[0], u2[1]));
+ xProbe.push_back(Vector2ui(u2[0], u1[1]));
+ xProbe.push_back(Vector2ui(u2[0], u0[1]));
+ xProbe.push_back(Vector2ui(u1[0], u0[1]));
+
+ double r = 0;
+ for(int i = 0; i < xProbe.size(); i++)
+ {
+ Vector3d x_probe_slice = smodel->MapWindowToSlice(to_double(xProbe[i]));
+ double dx = (x_probe_slice[0] - rot_ctr_slice[0]) * smodel->GetSliceSpacing()[0];
+ double dy = (x_probe_slice[1] - rot_ctr_slice[1]) * smodel->GetSliceSpacing()[1];
+ double rtest = sqrt(dx * dx + dy * dy);
+ if(rtest > r)
+ r = rtest;
+ }
+
+ // There we have it, we found the r that we want.
+ return r;
+}
+
+void InteractiveRegistrationModel::RotateByTheta(double theta)
+{
+ // Apply the rotation angle to the transform. To do this, we use the axis-angle rotation
+ // formulation. The axis, in this case, is the normal to the slice in physical space of
+ // the reference image.
+ RegistrationModel *rmodel = this->GetRegistrationModel();
+ GenericSliceModel *smodel = this->GetParent();
+
+ // Map a vector in the z direction into image coordinates
+ Vector3d v_image = to_double(smodel->GetDisplayToImageTransform()->TransformVector(Vector3d(0, 0, -1)));
+
+ // Transform this vector into physical space
+ itk::Vector<double, 3> axis_image, axis_phys;
+ axis_image.SetVnlVector(v_image);
+ smodel->GetDriver()->GetCurrentImageData()->GetMain()->GetImageBase()
+ ->TransformLocalVectorToPhysicalVector(axis_image, axis_phys);
+
+ Vector3d v_phys = axis_phys.GetVnlVector();
+ v_phys.normalize();
+
+ // Now we have an actual axis of rotation
+ rmodel->ApplyRotation(v_phys, theta);
+}
+
+InteractiveRegistrationModel::InteractiveRegistrationModel()
+ : m_HoveringOverRotationWidget(false), m_HoveringOverMovingLayer(false)
+{
+
+}
+
+InteractiveRegistrationModel::~InteractiveRegistrationModel()
+{
+
+}
+
+
+bool InteractiveRegistrationModel::ProcessPushEvent(const Vector3d &xSlice)
+{
+ if(m_HoveringOverRotationWidget)
+ {
+ m_LastTheta = 0;
+ return true;
+ }
+ else if(m_HoveringOverMovingLayer)
+ {
+ m_LastDisplacement = 0;
+ return true;
+ }
+ else
+ {
+ // Don't handle events occurring over fixed / other layers, so that the user
+ // can still move the cursor
+ return false;
+ }
+}
+
+bool InteractiveRegistrationModel::ProcessDragEvent(const Vector3d &xSlice, const Vector3d &xDragStart)
+{
+ // The user is moving the mouse. We just want to check if we are close to the rotation widget
+ RegistrationModel *rmodel = this->GetRegistrationModel();
+ GenericSliceModel *smodel = this->GetParent();
+ ImageWrapperBase *moving = rmodel->GetMovingLayerWrapper();
+
+ if(!moving || !m_HoveringOverMovingLayer)
+ return false;
+
+ if(m_HoveringOverRotationWidget)
+ {
+ // Treat this as a rotation event
+
+ // Get the center of rotation
+ Vector3ui rot_ctr_image = rmodel->GetRotationCenter();
+
+ // Map the center of rotation into the slice coordinates
+ Vector3d rot_ctr_slice = smodel->MapImageToSlice(to_double(rot_ctr_image));
+
+ // Compute the rotation angle
+ double dx0 = (xDragStart[0] - rot_ctr_slice[0]) * smodel->GetSliceSpacing()[0];
+ double dy0 = (xDragStart[1] - rot_ctr_slice[1]) * smodel->GetSliceSpacing()[1];
+ double dx1 = (xSlice[0] - rot_ctr_slice[0]) * smodel->GetSliceSpacing()[0];
+ double dy1 = (xSlice[1] - rot_ctr_slice[1]) * smodel->GetSliceSpacing()[1];
+
+ double theta = atan2(dy1, dx1) - atan2(dy0, dx0);
+
+ this->RotateByTheta(theta - m_LastTheta);
+
+ // Set the last theta
+ m_LastTheta = theta;
+ }
+ else
+ {
+ // Find the physical coordinates of the start and end points of the drag
+ Vector3d xDrag2 = smodel->MapSliceToImagePhysical(xSlice);
+ Vector3d xDrag1 = smodel->MapSliceToImagePhysical(xDragStart);
+
+ // Total displacement so far
+ Vector3d xDispTotal = xDrag2 - xDrag1;
+
+ // Apply the delta displacement
+ rmodel->ApplyTranslation(xDispTotal - m_LastDisplacement);
+
+ // Store the displacement
+ m_LastDisplacement = xDispTotal;
+ }
+
+ return true;
+}
+
+bool
+InteractiveRegistrationModel
+::GetDoProcessInteractionOverLayer(unsigned long layer_id)
+{
+ RegistrationModel *rmodel = this->GetRegistrationModel();
+ ImageWrapperBase *moving = rmodel->GetMovingLayerWrapper();
+
+ // If tiling and moving layer is drawing
+ // If stacked and moving layer is drawing
+ // If moving layer is an overlay
+ return moving && (moving->GetUniqueId() == layer_id || moving->IsSticky());
+}
+
+bool
+InteractiveRegistrationModel
+::ProcessMouseMoveEvent(
+ const Vector3d &xSlice, unsigned long hover_layer_id)
+{
+ // The user is moving the mouse. We just want to check if we are close to the rotation widget
+ RegistrationModel *rmodel = this->GetRegistrationModel();
+ GenericSliceModel *smodel = this->GetParent();
+
+ // Does this interaction fall into our provenance?
+ if(!this->GetDoProcessInteractionOverLayer(hover_layer_id))
+ {
+ m_HoveringOverMovingLayer = false;
+ m_HoveringOverRotationWidget = false;
+ return false;
+ }
+
+ // We are hovering over the moving layer
+ m_HoveringOverMovingLayer = true;
+
+ // Get the center of rotation
+ Vector3ui rot_ctr_image = rmodel->GetRotationCenter();
+
+ // Map the center of rotation into the slice coordinates
+ Vector3d rot_ctr_slice = smodel->MapImageToSlice(to_double(rot_ctr_image));
+
+ // Check the distance to the widget
+ double radius = this->GetRotationWidgetRadius() / 2.0;
+
+ double dx = (xSlice[0] - rot_ctr_slice[0]) * smodel->GetSliceSpacing()[0];
+ double dy = (xSlice[1] - rot_ctr_slice[1]) * smodel->GetSliceSpacing()[1];
+ double rtest = sqrt(dx * dx + dy * dy);
+
+ bool newHover = fabs(rtest - radius) < 0.1 * radius;
+ if(newHover != m_HoveringOverRotationWidget)
+ {
+ m_HoveringOverRotationWidget = newHover;
+ InvokeEvent(ModelUpdateEvent());
+ }
+
+ return true;
+}
+
+bool InteractiveRegistrationModel::ProcessReleaseEvent(const Vector3d &xSlice, const Vector3d &xDragStart)
+{
+ bool status = this->ProcessDragEvent(xSlice, xDragStart);
+ m_LastTheta = 0;
+ return status;
+}
+
diff --git a/GUI/Model/InteractiveRegistrationModel.h b/GUI/Model/InteractiveRegistrationModel.h
new file mode 100644
index 0000000..e0c9c45
--- /dev/null
+++ b/GUI/Model/InteractiveRegistrationModel.h
@@ -0,0 +1,101 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: InteractiveRegistrationModel.h,v $
+ Language: C++
+ Date: $Date: 2010/10/18 11:25:44 $
+ Version: $Revision: 1.12 $
+ Copyright (c) 2011 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef INTERACTIVEREGISTRATIONMODEL_H
+#define INTERACTIVEREGISTRATIONMODEL_H
+
+#include <SNAPCommon.h>
+#include <AbstractModel.h>
+
+class GenericSliceModel;
+class RegistrationModel;
+
+class InteractiveRegistrationModel : public AbstractModel
+{
+public:
+ irisITKObjectMacro(InteractiveRegistrationModel, AbstractModel)
+
+ /**
+ * The parent of this model is the main generic slice model, which
+ * handles slice display and interaction
+ */
+ irisSetMacro(Parent, GenericSliceModel *)
+ irisGetMacro(Parent, GenericSliceModel *)
+
+ /**
+ * The other parent of this model is the main registration model, which
+ * is the back end of the registration dialog
+ */
+ irisSetMacro(RegistrationModel, RegistrationModel *)
+ irisGetMacro(RegistrationModel, RegistrationModel *)
+
+
+ bool ProcessPushEvent(const Vector3d &xSlice);
+ bool ProcessDragEvent(const Vector3d &xSlice, const Vector3d &xDragStart);
+ bool ProcessMouseMoveEvent(const Vector3d &xSlice, unsigned long hover_layer_id);
+ bool ProcessReleaseEvent(const Vector3d &xSlice, const Vector3d &xDragStart);
+
+ double GetRotationWidgetRadius();
+
+ void RotateByTheta(double theta);
+
+ irisIsMacro(HoveringOverMovingLayer)
+
+ irisIsMacro(HoveringOverRotationWidget)
+
+ irisGetMacro(LastTheta, double)
+
+ /**
+ * Check for a particular layer id whether the interactive registration widget
+ * should be shown over that layer and whether mouse events over this layer are
+ * processed as interactive registration events
+ */
+ bool GetDoProcessInteractionOverLayer(unsigned long layer_id);
+
+protected:
+
+ InteractiveRegistrationModel();
+ ~InteractiveRegistrationModel();
+
+ GenericSliceModel *m_Parent;
+ RegistrationModel *m_RegistrationModel;
+
+ // Radius of the rotation widget circle in slice coordinates
+ double m_RotationWidgetRadius;
+
+ // Whether we are hovering over the moving widget
+ bool m_HoveringOverMovingLayer;
+
+ // Whether we are hovering over the rotation widget radius (so it should be highlighted)
+ bool m_HoveringOverRotationWidget;
+
+ // During dragging, this stores the last theta rotation applied
+ double m_LastTheta;
+
+ // The last displacement vector
+ Vector3d m_LastDisplacement;
+};
+
+#endif // INTERACTIVEREGISTRATIONMODEL_H
diff --git a/GUI/Model/InterpolateLabelModel.cxx b/GUI/Model/InterpolateLabelModel.cxx
new file mode 100644
index 0000000..6c43050
--- /dev/null
+++ b/GUI/Model/InterpolateLabelModel.cxx
@@ -0,0 +1,119 @@
+#include "InterpolateLabelModel.h"
+#include "GlobalUIModel.h"
+#include "IRISApplication.h"
+#include "GenericImageData.h"
+#include "ScalarImageWrapper.h"
+#include "itkMorphologicalContourInterpolator.h"
+#include "SegmentationUpdateIterator.h"
+
+void InterpolateLabelModel::SetParentModel(GlobalUIModel *parent)
+{
+ this->m_Parent = parent;
+
+ m_DrawingLabelModel->Initialize(parent->GetDriver()->GetColorLabelTable());
+ m_InterpolateLabelModel->Initialize(parent->GetDriver()->GetColorLabelTable());
+ m_DrawOverFilterModel->Initialize(parent->GetDriver()->GetColorLabelTable());
+}
+
+void InterpolateLabelModel::UpdateOnShow()
+{
+ // What we want to do here is syncronize labels with existing selections for active and draw over
+ this->SetDrawingLabel(m_Parent->GetGlobalState()->GetDrawingColorLabel());
+ this->SetInterpolateLabel(m_Parent->GetGlobalState()->GetDrawingColorLabel());
+ this->SetDrawOverFilter(m_Parent->GetGlobalState()->GetDrawOverFilter());
+}
+
+void InterpolateLabelModel::Interpolate()
+{
+ // Get the segmentation wrapper
+ GenericImageData *id = this->m_Parent->GetDriver()->GetCurrentImageData();
+ LabelImageWrapper *liw = id->GetSegmentation();
+
+ // Create the morphological interpolation filter
+ typedef itk::MorphologicalContourInterpolator<GenericImageData::LabelImageType> MCIType;
+ SmartPtr<MCIType> mci = MCIType::New();
+
+ // Are we interpolating all labels?
+ bool interp_all = this->GetInterpolateAll();
+
+ mci->SetInput(liw->GetImage());
+
+ // Should we be interpolating a specific label?
+ if (!interp_all)
+ mci->SetLabel(this->GetInterpolateLabel());
+
+ // Should we interpolate only one axis?
+ if (this->GetMorphologyInterpolateOneAxis())
+ {
+ int axis = this->m_Parent->GetDriver()->GetImageDirectionForAnatomicalDirection(this->GetMorphologyInterpolationAxis());
+ mci->SetAxis(axis);
+ }
+
+ // Should we use the distance transform?
+ mci->SetUseDistanceTransform(this->GetMorphologyUseDistance());
+
+ // Should heuristic or optimal alignment be used?
+ mci->SetHeuristicAlignment(!this->GetMorphologyUseOptimalAlignment());
+
+ // Update the filter
+ mci->Update();
+
+ // Apply the labels back to the segmentation
+ SegmentationUpdateIterator it_trg(liw->GetImage(), liw->GetImage()->GetBufferedRegion(),
+ this->GetDrawingLabel(), this->GetDrawOverFilter());
+
+ itk::ImageRegionConstIterator<GenericImageData::LabelImageType>
+ it_src(mci->GetOutput(), mci->GetOutput()->GetBufferedRegion());
+
+ // The way we paint back into the segmentation depends on whether all labels
+ // or a specific label are being interpolated
+ if(interp_all)
+ {
+ // Just replace the segmentation by the interpolation, respecting draw-over
+ for(; !it_trg.IsAtEnd(); ++it_trg, ++it_src)
+ it_trg.PaintLabel(it_src.Get());
+ }
+ else
+ {
+ LabelType l_replace = this->GetDrawingLabel();
+ for(; !it_trg.IsAtEnd(); ++it_trg, ++it_src)
+ it_trg.PaintLabelWithExtraProtection(it_src.Get(), l_replace);
+ }
+
+ // Finish the segmentation editing and create an undo point
+ it_trg.Finalize();
+ id->StoreUndoPoint("Interpolate label", it_trg.RelinquishDelta());
+ this->m_Parent->GetDriver()->InvokeEvent(SegmentationChangeEvent());
+}
+
+
+InterpolateLabelModel::InterpolateLabelModel()
+{
+ m_InterpolateAllModel = NewSimpleConcreteProperty(false);
+
+ m_DrawingLabelModel = ConcreteColorLabelPropertyModel::New();
+ m_InterpolateLabelModel = ConcreteColorLabelPropertyModel::New();
+ m_DrawOverFilterModel = ConcreteDrawOverFilterPropertyModel::New();
+ m_RetainScaffoldModel = NewSimpleConcreteProperty(false);
+
+ RegistryEnumMap<InterpolationType> emap_interp;
+ emap_interp.AddPair(MORPHOLOGY,"Morphological");
+ emap_interp.AddPair(LEVEL_SET,"Level set");
+ emap_interp.AddPair(DISTANCE_MAP,"Distance map");
+ m_InterpolationMethodModel = NewSimpleEnumProperty("InterpolationType", MORPHOLOGY, emap_interp);
+
+ m_DefaultSmoothingModel = NewRangedConcreteProperty(3.0, 0.0, 20.0, 0.01);
+
+ m_LevelSetSmoothingModel = NewRangedConcreteProperty(3.0, 0.0, 20.0, 0.01);
+ m_LevelSetCurvatureModel = NewRangedConcreteProperty(0.2, 0.0, 1.0, 0.01);
+
+ m_MorphologyUseDistanceModel = NewSimpleConcreteProperty(false);
+ m_MorphologyUseOptimalAlignmentModel = NewSimpleConcreteProperty(false);
+ m_MorphologyInterpolateOneAxisModel = NewSimpleConcreteProperty(false);
+
+ RegistryEnumMap<AnatomicalDirection> emap_interp_axis;
+ emap_interp_axis.AddPair(ANATOMY_AXIAL,"Axial");
+ emap_interp_axis.AddPair(ANATOMY_SAGITTAL,"Sagittal");
+ emap_interp_axis.AddPair(ANATOMY_CORONAL,"Coronal");
+ m_MorphologyInterpolationAxisModel = NewSimpleEnumProperty("InterpolationAxis", ANATOMY_AXIAL, emap_interp_axis);
+}
diff --git a/GUI/Model/InterpolateLabelModel.h b/GUI/Model/InterpolateLabelModel.h
new file mode 100644
index 0000000..28bd76d
--- /dev/null
+++ b/GUI/Model/InterpolateLabelModel.h
@@ -0,0 +1,103 @@
+#ifndef INTERPOLATELABELMODEL_H
+#define INTERPOLATELABELMODEL_H
+
+#include <AbstractModel.h>
+#include <PropertyModel.h>
+
+#include "Registry.h"
+#include "AbstractPropertyContainerModel.h"
+#include "ColorLabelPropertyModel.h"
+
+
+class GlobalUIModel;
+
+class InterpolateLabelModel : public AbstractPropertyContainerModel
+{
+public:
+
+ enum InterpolationType
+ {
+ DISTANCE_MAP = 0, LEVEL_SET, MORPHOLOGY
+ };
+
+ // Standard ITK stuff
+ irisITKObjectMacro(InterpolateLabelModel, AbstractModel)
+
+ /** Initialize with the parent model */
+ void SetParentModel(GlobalUIModel *parent);
+
+ /** Whether all the labels should be interpolated */
+ irisSimplePropertyAccessMacro(InterpolateAll, bool)
+
+ /** Model for the label that will be interpolated */
+ irisGenericPropertyAccessMacro(InterpolateLabel, LabelType, ColorLabelItemSetDomain)
+
+ /** Model for the label that will be drawn with */
+ irisGenericPropertyAccessMacro(DrawingLabel, LabelType, ColorLabelItemSetDomain)
+
+ /** Model for the label that will be drawn over */
+ irisGenericPropertyAccessMacro(DrawOverFilter, DrawOverFilter, DrawOverLabelItemSetDomain)
+
+ /** Whether the scaffold should be kept */
+ irisSimplePropertyAccessMacro(RetainScaffold, bool)
+
+ /** Smoothing factor */
+ irisRangedPropertyAccessMacro(DefaultSmoothing, double)
+ irisRangedPropertyAccessMacro(LevelSetSmoothing, double)
+
+ /** Curvature parameter for level set method */
+ irisRangedPropertyAccessMacro(LevelSetCurvature, double)
+
+ /** Whether to use signed distance transform for morphological interpolation */
+ irisSimplePropertyAccessMacro(MorphologyUseDistance, bool)
+ /** Whether to use optimal slice alignment for morphological interpolation */
+ irisSimplePropertyAccessMacro(MorphologyUseOptimalAlignment, bool)
+
+ /** Which interpolation method to use */
+ irisSimplePropertyAccessMacro(InterpolationMethod, InterpolationType)
+
+ /** Which axis to interpolate along */
+ irisSimplePropertyAccessMacro(MorphologyInterpolateOneAxis, bool)
+ irisSimplePropertyAccessMacro(MorphologyInterpolationAxis, AnatomicalDirection)
+
+ /** Update the state of the widget whenever it is shown */
+ void UpdateOnShow();
+
+ /** Perform the actual interpolation */
+ void Interpolate();
+
+protected:
+
+ // Constructor
+ InterpolateLabelModel();
+ virtual ~InterpolateLabelModel() {}
+
+ // Models for the main things settable by the user
+ SmartPtr<ConcreteSimpleBooleanProperty> m_InterpolateAllModel;
+ SmartPtr<ConcreteColorLabelPropertyModel> m_InterpolateLabelModel;
+ SmartPtr<ConcreteColorLabelPropertyModel> m_DrawingLabelModel;
+ SmartPtr<ConcreteDrawOverFilterPropertyModel> m_DrawOverFilterModel;
+
+ SmartPtr<ConcreteSimpleBooleanProperty> m_RetainScaffoldModel;
+
+ SmartPtr<ConcreteRangedDoubleProperty> m_DefaultSmoothingModel;
+
+ SmartPtr<ConcreteRangedDoubleProperty> m_LevelSetSmoothingModel;
+ SmartPtr<ConcreteRangedDoubleProperty> m_LevelSetCurvatureModel;
+
+ SmartPtr<ConcreteSimpleBooleanProperty> m_MorphologyUseDistanceModel;
+ SmartPtr<ConcreteSimpleBooleanProperty> m_MorphologyUseOptimalAlignmentModel;
+ SmartPtr<ConcreteSimpleBooleanProperty> m_MorphologyInterpolateOneAxisModel;
+ typedef ConcretePropertyModel<AnatomicalDirection, TrivialDomain> ConcreteInterpolationAxisType;
+ SmartPtr<ConcreteInterpolationAxisType> m_MorphologyInterpolationAxisModel;
+
+ typedef ConcretePropertyModel<InterpolationType, TrivialDomain> ConcreteInterpolationType;
+ SmartPtr<ConcreteInterpolationType> m_InterpolationMethodModel;
+
+
+ // The parent model
+ GlobalUIModel *m_Parent;
+
+};
+
+#endif // INTERPOLATELABELMODEL_H
diff --git a/GUI/Model/LabelEditorModel.cxx b/GUI/Model/LabelEditorModel.cxx
index 8cae938..2416c43 100644
--- a/GUI/Model/LabelEditorModel.cxx
+++ b/GUI/Model/LabelEditorModel.cxx
@@ -328,6 +328,34 @@ void LabelEditorModel::ResetLabels()
m_LabelTable->InitializeToDefaults();
}
+void LabelEditorModel::SetAllLabelsVisibility(bool onoff)
+{
+ for(LabelType label = m_LabelTable->GetFirstValidLabel(); label != 0;
+ label = m_LabelTable->FindNextValidLabel(label, true) )
+ {
+ ColorLabel desc = m_LabelTable->GetColorLabel(label);
+ if(desc.IsVisible() != onoff)
+ {
+ desc.SetVisible(onoff);
+ m_LabelTable->SetColorLabel(label, desc);
+ }
+ }
+}
+
+void LabelEditorModel::SetAllLabelsVisibilityIn3D(bool onoff)
+{
+ for(LabelType label = m_LabelTable->GetFirstValidLabel(); label != 0;
+ label = m_LabelTable->FindNextValidLabel(label, true) )
+ {
+ ColorLabel desc = m_LabelTable->GetColorLabel(label);
+ if(desc.IsVisibleIn3D() != onoff)
+ {
+ desc.SetVisibleIn3D(onoff);
+ m_LabelTable->SetColorLabel(label, desc);
+ }
+ }
+}
+
bool LabelEditorModel::ReassignLabelId(LabelType newid)
{
// Check if the ID is taken
diff --git a/GUI/Model/LabelEditorModel.h b/GUI/Model/LabelEditorModel.h
index 6433518..18e5097 100644
--- a/GUI/Model/LabelEditorModel.h
+++ b/GUI/Model/LabelEditorModel.h
@@ -68,6 +68,12 @@ public:
/** Reset labels to defaults */
void ResetLabels();
+ /** Bulk visibility update on the labels */
+ void SetAllLabelsVisibility(bool onoff);
+
+ /** Bulk 3D visibility update on the labels */
+ void SetAllLabelsVisibilityIn3D(bool onoff);
+
protected:
// Hidden constructor/destructor
diff --git a/GUI/Model/LayerGeneralPropertiesModel.cxx b/GUI/Model/LayerGeneralPropertiesModel.cxx
index 8a8cc0a..af3f6a7 100644
--- a/GUI/Model/LayerGeneralPropertiesModel.cxx
+++ b/GUI/Model/LayerGeneralPropertiesModel.cxx
@@ -174,6 +174,10 @@ bool LayerGeneralPropertiesModel
{
value = MODE_RGB;
}
+ else if(mode.RenderAsGrid)
+ {
+ value = MODE_GRID;
+ }
else
{
switch(mode.SelectedScalarRep)
@@ -201,7 +205,10 @@ bool LayerGeneralPropertiesModel
// RGB is available only if there are three components
if(layer->GetNumberOfComponents() == 3)
+ {
(*domain)[MODE_RGB] = "RGB Display";
+ (*domain)[MODE_GRID] = "Deformation Grid Display";
+ }
}
return true;
@@ -221,24 +228,35 @@ void LayerGeneralPropertiesModel
case LayerGeneralPropertiesModel::MODE_COMPONENT:
mode.SelectedScalarRep = SCALAR_REP_COMPONENT;
mode.UseRGB = false;
+ mode.RenderAsGrid = false;
break;
case LayerGeneralPropertiesModel::MODE_MAGNITUDE:
mode.SelectedScalarRep = SCALAR_REP_MAGNITUDE;
mode.SelectedComponent = 0;
mode.UseRGB = false;
+ mode.RenderAsGrid = false;
break;
case LayerGeneralPropertiesModel::MODE_MAX:
mode.SelectedScalarRep = SCALAR_REP_MAX;
mode.SelectedComponent = 0;
mode.UseRGB = false;
+ mode.RenderAsGrid = false;
break;
case LayerGeneralPropertiesModel::MODE_AVERAGE:
mode.SelectedScalarRep = SCALAR_REP_AVERAGE;
mode.SelectedComponent = 0;
mode.UseRGB = false;
+ mode.RenderAsGrid = false;
break;
case LayerGeneralPropertiesModel::MODE_RGB:
mode.UseRGB = true;
+ mode.RenderAsGrid = false;
+ mode.SelectedScalarRep = SCALAR_REP_COMPONENT;
+ mode.SelectedComponent = 0;
+ break;
+ case LayerGeneralPropertiesModel::MODE_GRID:
+ mode.UseRGB = false;
+ mode.RenderAsGrid = true;
mode.SelectedScalarRep = SCALAR_REP_COMPONENT;
mode.SelectedComponent = 0;
break;
@@ -258,8 +276,7 @@ bool LayerGeneralPropertiesModel
MultiChannelDisplayMode mode = GetMultiChannelDisplayPolicy()->GetDisplayMode();
// Mode must be single component
- if(mode.UseRGB ||
- mode.SelectedScalarRep != SCALAR_REP_COMPONENT)
+ if(!mode.IsSingleComponent())
return false;
// Use 1-based indexing
@@ -294,8 +311,7 @@ bool LayerGeneralPropertiesModel
MultiChannelDisplayMode mode = GetMultiChannelDisplayPolicy()->GetDisplayMode();
// Animation is only possible when showing components
- if(mode.UseRGB ||
- mode.SelectedScalarRep != SCALAR_REP_COMPONENT)
+ if(!mode.IsSingleComponent())
return false;
value = GetMultiChannelDisplayPolicy()->GetAnimate();
diff --git a/GUI/Model/LayerGeneralPropertiesModel.h b/GUI/Model/LayerGeneralPropertiesModel.h
index 49935fe..b99d4d5 100644
--- a/GUI/Model/LayerGeneralPropertiesModel.h
+++ b/GUI/Model/LayerGeneralPropertiesModel.h
@@ -47,7 +47,7 @@ public:
* is not a scalar representation).
*/
enum DisplayMode {
- MODE_COMPONENT = 0, MODE_MAGNITUDE, MODE_MAX, MODE_AVERAGE, MODE_RGB
+ MODE_COMPONENT = 0, MODE_MAGNITUDE, MODE_MAX, MODE_AVERAGE, MODE_RGB, MODE_GRID
};
/** States for this model */
diff --git a/GUI/Model/LayerTableRowModel.cxx b/GUI/Model/LayerTableRowModel.cxx
index 3d2425b..f6319e2 100644
--- a/GUI/Model/LayerTableRowModel.cxx
+++ b/GUI/Model/LayerTableRowModel.cxx
@@ -117,17 +117,21 @@ void LayerTableRowModel::UpdateDisplayModeList()
{
for(int i = 0; i < m_Layer->GetNumberOfComponents(); i++)
m_AvailableDisplayModes.push_back(
- MultiChannelDisplayMode(false, SCALAR_REP_COMPONENT, i));
+ MultiChannelDisplayMode(false, false, SCALAR_REP_COMPONENT, i));
m_AvailableDisplayModes.push_back(
- MultiChannelDisplayMode(false, SCALAR_REP_MAGNITUDE, 0));
+ MultiChannelDisplayMode(false, false, SCALAR_REP_MAGNITUDE, 0));
m_AvailableDisplayModes.push_back(
- MultiChannelDisplayMode(false, SCALAR_REP_MAX, 0));
+ MultiChannelDisplayMode(false, false, SCALAR_REP_MAX, 0));
m_AvailableDisplayModes.push_back(
- MultiChannelDisplayMode(false, SCALAR_REP_AVERAGE, 0));
+ MultiChannelDisplayMode(false, false, SCALAR_REP_AVERAGE, 0));
if(m_Layer->GetNumberOfComponents() == 3)
+ {
m_AvailableDisplayModes.push_back(
- MultiChannelDisplayMode::DefaultForRGB());
+ MultiChannelDisplayMode(true, false, SCALAR_REP_COMPONENT));
+ m_AvailableDisplayModes.push_back(
+ MultiChannelDisplayMode(false, true, SCALAR_REP_COMPONENT));
+ }
}
}
@@ -275,7 +279,8 @@ void LayerTableRowModel::GenerateTextureFeatures()
SmartPtr<AnatomicImageWrapper> newWrapper = AnatomicImageWrapper::New();
newWrapper->InitializeToWrapper(m_Layer, filter->GetOutput(), NULL, NULL);
newWrapper->SetDefaultNickname("Textures");
- this->GetParentModel()->GetDriver()->AddDerivedOverlayImage(newWrapper);
+ this->GetParentModel()->GetDriver()->AddDerivedOverlayImage(
+ m_Layer, newWrapper, false);
}
}
@@ -287,6 +292,11 @@ LayerTableRowModel::GetDisplayModeString(const MultiChannelDisplayMode &mode)
return "RGB";
}
+ if(mode.RenderAsGrid)
+ {
+ return "Grid";
+ }
+
std::ostringstream oss;
switch(mode.SelectedScalarRep)
{
diff --git a/GUI/Model/OrthogonalSliceCursorNavigationModel.cxx b/GUI/Model/OrthogonalSliceCursorNavigationModel.cxx
index 64e1a39..f28b0e9 100644
--- a/GUI/Model/OrthogonalSliceCursorNavigationModel.cxx
+++ b/GUI/Model/OrthogonalSliceCursorNavigationModel.cxx
@@ -33,13 +33,13 @@
#include "SliceWindowCoordinator.h"
#include "ImageCoordinateTransform.h"
-void OrthogonalSliceCursorNavigationModel::UpdateCursor(Vector2f x)
+void OrthogonalSliceCursorNavigationModel::UpdateCursor(Vector2d x)
{
// Compute the position in slice coordinates
- Vector3f xClick = m_Parent->MapWindowToSlice(x);
+ Vector3d xClick = m_Parent->MapWindowToSlice(x);
// Compute the new cross-hairs position in image space
- Vector3f xCross = m_Parent->MapSliceToImage(xClick);
+ Vector3d xCross = m_Parent->MapSliceToImage(xClick);
// Round the cross-hairs position down to integer
Vector3i xCrossInteger = to_int(xCross);
@@ -58,8 +58,8 @@ void OrthogonalSliceCursorNavigationModel::UpdateCursor(Vector2f x)
void OrthogonalSliceCursorNavigationModel::ProcessKeyNavigation(Vector3i dx)
{
// Get the displacement in image space
- Vector3f dximg =
- m_Parent->GetDisplayToImageTransform().TransformVector(to_float(dx));
+ Vector3d dximg =
+ m_Parent->GetDisplayToImageTransform()->TransformVector(to_double(dx));
Vector3i dximgi = to_int(dximg);
// Update the cursor
@@ -85,7 +85,7 @@ void OrthogonalSliceCursorNavigationModel::EndZoom() { }
void OrthogonalSliceCursorNavigationModel::EndPan() { }
void OrthogonalSliceCursorNavigationModel
-::ProcessZoomGesture(float scaleFactor)
+::ProcessZoomGesture(double scaleFactor)
{
// Get the slice coordinator
SliceWindowCoordinator *coordinator =
@@ -102,7 +102,7 @@ void OrthogonalSliceCursorNavigationModel
else
{
// Compute the zoom factor (is this good?)
- float zoom = m_StartViewZoom * scaleFactor;
+ double zoom = m_StartViewZoom * scaleFactor;
// Clamp the zoom factor to reasonable limits
zoom = coordinator->ClampZoom(m_Parent->GetId(), zoom);
@@ -121,11 +121,11 @@ void OrthogonalSliceCursorNavigationModel
void
OrthogonalSliceCursorNavigationModel
-::ProcessPanGesture(Vector2f uvOffset)
+::ProcessPanGesture(Vector2d uvOffset)
{
// Compute the start and end point in slice coordinates
- Vector3f zOffset = m_Parent->MapWindowOffsetToSliceOffset(uvOffset);
- Vector2f xOffset(zOffset[0] * m_Parent->GetSliceSpacing()[0],
+ Vector3d zOffset = m_Parent->MapWindowOffsetToSliceOffset(uvOffset);
+ Vector2d xOffset(zOffset[0] * m_Parent->GetSliceSpacing()[0],
zOffset[1] * m_Parent->GetSliceSpacing()[1]);
// Under the left button, the tool changes the view_pos by the
@@ -134,14 +134,14 @@ OrthogonalSliceCursorNavigationModel
}
void OrthogonalSliceCursorNavigationModel
-::ProcessScrollGesture(float scrollAmount)
+::ProcessScrollGesture(double scrollAmount)
{
// Get the cross-hairs position in image space
Vector3ui xCrossImage = m_Parent->GetDriver()->GetCursorPosition();
// Map it into slice space
- Vector3f xCrossSlice =
- m_Parent->MapImageToSlice(to_float(xCrossImage) + Vector3f(0.5f));
+ Vector3d xCrossSlice =
+ m_Parent->MapImageToSlice(to_double(xCrossImage) + Vector3d(0.5));
// Advance by the scroll amount
xCrossSlice[2] += scrollAmount;
@@ -175,7 +175,7 @@ void OrthogonalSliceCursorNavigationModel
::ProcessThumbnailPanGesture(Vector2i uvOffset)
{
// Figure out how this movement translates to space units
- Vector2f xOffset(
+ Vector2d xOffset(
uvOffset[0] / m_Parent->GetThumbnailZoom(),
uvOffset[1] / m_Parent->GetThumbnailZoom());
diff --git a/GUI/Model/OrthogonalSliceCursorNavigationModel.h b/GUI/Model/OrthogonalSliceCursorNavigationModel.h
index 4c23cf5..239c216 100644
--- a/GUI/Model/OrthogonalSliceCursorNavigationModel.h
+++ b/GUI/Model/OrthogonalSliceCursorNavigationModel.h
@@ -42,7 +42,7 @@ public:
irisGetMacro(Parent, GenericSliceModel *)
// Move 3D cursor to (x,y) point on the screen supplied by user
- void UpdateCursor(Vector2f x);
+ void UpdateCursor(Vector2d x);
// Start zoom operation
void BeginZoom();
@@ -58,14 +58,14 @@ public:
// Process zoom or pan operation (parameter is the gesture length,
// which in theory should work both on desktop and on an iPhone).
- void ProcessZoomGesture(float scaleFactor);
+ void ProcessZoomGesture(double scaleFactor);
// Process pan operation (parameter is the gesture vector, i.e., mouse
// drag or three-finger gesture)
- void ProcessPanGesture(Vector2f uvOffset);
+ void ProcessPanGesture(Vector2d uvOffset);
// Process a scrolling-type gesture (mouse wheel, or two-finger scroll)
- void ProcessScrollGesture(float gLength);
+ void ProcessScrollGesture(double gLength);
// Check if the user press position is inside the thumbnail
bool CheckZoomThumbnail(Vector2i xCanvas);
@@ -82,8 +82,8 @@ protected:
~OrthogonalSliceCursorNavigationModel() {}
// Zoom and pan factors at the beginning of interaction
- Vector2f m_StartViewPosition;
- float m_StartViewZoom;
+ Vector2d m_StartViewPosition;
+ double m_StartViewZoom;
GenericSliceModel *m_Parent;
diff --git a/GUI/Model/PaintbrushModel.cxx b/GUI/Model/PaintbrushModel.cxx
index 7a3b74a..99964fb 100644
--- a/GUI/Model/PaintbrushModel.cxx
+++ b/GUI/Model/PaintbrushModel.cxx
@@ -4,8 +4,10 @@
#include "GlobalUIModel.h"
#include "IRISApplication.h"
#include "GenericImageData.h"
+#include "ImageWrapperTraits.h"
+#include "SegmentationUpdateIterator.h"
-#include "itkRegionOfInterestImageFilter.h"
+#include "RLERegionOfInterestImageFilter.h"
#include "itkGradientAnisotropicDiffusionImageFilter.h"
#include "itkGradientMagnitudeImageFilter.h"
#include "itkWatershedImageFilter.h"
@@ -16,9 +18,9 @@ class BrushWatershedPipeline
{
public:
typedef itk::Image<GreyType, 3> GreyImageType;
- typedef itk::Image<LabelType, 3> LabelImageType;
+ typedef LabelImageWrapperTraits::ImageType LabelImageType;
typedef itk::Image<float, 3> FloatImageType;
- typedef itk::Image<unsigned long, 3> WatershedImageType;
+ typedef itk::Image<itk::IdentifierType, 3> WatershedImageType;
typedef WatershedImageType::IndexType IndexType;
BrushWatershedPipeline()
@@ -83,49 +85,6 @@ public:
return wctr == widx;
}
- bool UpdateLabelImage(
- LabelImageType *ltrg,
- CoverageModeType mode,
- LabelType drawing_color,
- LabelType overwrt_color)
- {
- // Get the watershed ID at the center voxel
- unsigned long wid = wf->GetOutput()->GetPixel(vcenter);
-
- // Keep track of changed voxels
- bool flagChanged = false;
-
- // Do the update
- typedef itk::ImageRegionConstIterator<WatershedImageType> WIter;
- typedef itk::ImageRegionIterator<LabelImageType> LIter;
- WIter wit(wf->GetOutput(), wf->GetOutput()->GetBufferedRegion());
- LIter sit(lsrc, lsrc->GetBufferedRegion());
- LIter tit(ltrg, region);
- for(; !wit.IsAtEnd(); ++sit,++tit,++wit)
- {
- LabelType pxLabel = sit.Get();
- if(wit.Get() == wid)
- {
- // Standard paint mode
- if (mode == PAINT_OVER_ALL ||
- (mode == PAINT_OVER_ONE && pxLabel == overwrt_color) ||
- (mode == PAINT_OVER_VISIBLE && pxLabel != 0))
- {
- pxLabel = drawing_color;
- }
- }
- if(pxLabel != tit.Get())
- {
- tit.Set(pxLabel);
- flagChanged = true;
- }
- }
-
- if(flagChanged)
- ltrg->Modified();
- return flagChanged;
- }
-
private:
typedef itk::RegionOfInterestImageFilter<GreyImageType, FloatImageType> ROIType;
typedef itk::RegionOfInterestImageFilter<LabelImageType, LabelImageType> LROIType;
@@ -161,13 +120,13 @@ PaintbrushModel::~PaintbrushModel()
delete m_Watershed;
}
-Vector3f PaintbrushModel::ComputeOffset()
+Vector3d PaintbrushModel::ComputeOffset()
{
// Get the paintbrush properties
PaintbrushSettings pbs =
m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings();
- Vector3f offset(0.0);
+ Vector3d offset(0.0);
if(fmod(pbs.radius,1.0)==0)
{
offset.fill(0.5);
@@ -177,7 +136,7 @@ Vector3f PaintbrushModel::ComputeOffset()
return offset;
}
-void PaintbrushModel::ComputeMousePosition(const Vector3f &xSlice)
+void PaintbrushModel::ComputeMousePosition(const Vector3d &xSlice)
{
// Only when an image is loaded
if(!m_Parent->GetDriver()->IsMainImageLoaded())
@@ -188,7 +147,7 @@ void PaintbrushModel::ComputeMousePosition(const Vector3f &xSlice)
m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings();
// Compute the new cross-hairs position in image space
- Vector3f xCross = m_Parent->MapSliceToImage(xSlice);
+ Vector3d xCross = m_Parent->MapSliceToImage(xSlice);
// Round the cross-hairs position down to integer
Vector3i xCrossInteger = to_int(xCross + ComputeOffset());
@@ -223,7 +182,7 @@ bool PaintbrushModel::TestInside(const Vector3d &x, const PaintbrushSettings &ps
Vector3d xTest = x;
if(ps.isotropic)
{
- const Vector3f &spacing = m_Parent->GetSliceSpacing();
+ const Vector3d &spacing = m_Parent->GetSliceSpacing();
double xMinVoxelDim = spacing.min_value();
xTest(0) *= spacing(0) / xMinVoxelDim;
xTest(1) *= spacing(1) / xMinVoxelDim;
@@ -243,7 +202,7 @@ bool PaintbrushModel::TestInside(const Vector3d &x, const PaintbrushSettings &ps
bool
PaintbrushModel
-::ProcessPushEvent(const Vector3f &xSlice, const Vector2ui &gridCell, bool reverse_mode)
+::ProcessPushEvent(const Vector3d &xSlice, const Vector2ui &gridCell, bool reverse_mode)
{
// Get the paintbrush properties (TODO: should we own them?)
PaintbrushSettings pbs =
@@ -282,7 +241,7 @@ PaintbrushModel
bool
PaintbrushModel
-::ProcessDragEvent(const Vector3f &xSlice, const Vector3f &xSliceLast,
+::ProcessDragEvent(const Vector3d &xSlice, const Vector3d &xSliceLast,
double pixelsMoved, bool release)
{
IRISApplication *driver = m_Parent->GetDriver();
@@ -303,8 +262,8 @@ PaintbrushModel
size_t nSteps = (int) ceil(pixelsMoved / pbs.radius);
for(size_t i = 0; i < nSteps; i++)
{
- float t = (1.0 + i) / nSteps;
- Vector3f X = t * m_LastApplyX + (1.0f - t) * xSlice;
+ double t = (1.0 + i) / nSteps;
+ Vector3d X = t * m_LastApplyX + (1.0 - t) * xSlice;
ComputeMousePosition(X);
ApplyBrush(m_ReverseMode, true);
}
@@ -325,7 +284,8 @@ PaintbrushModel
// If the mouse is being released, we need to commit the drawing
if(release)
{
- driver->StoreUndoPoint("Drawing with paintbrush");
+ driver->GetCurrentImageData()->StoreUndoPoint("Drawing with paintbrush");
+ driver->RecordCurrentLabelUse();
// TODO: this is ugly. The code for applying a brush should really be
// placed in the IRISApplication.
@@ -343,7 +303,7 @@ PaintbrushModel
return 0;
}
-bool PaintbrushModel::ProcessMouseMoveEvent(const Vector3f &xSlice)
+bool PaintbrushModel::ProcessMouseMoveEvent(const Vector3d &xSlice)
{
ComputeMousePosition(xSlice);
return true;
@@ -366,7 +326,8 @@ void PaintbrushModel::AcceptAtCursor()
ApplyBrush(false, false);
// We need to commit the drawing
- driver->StoreUndoPoint("Drawing with paintbrush");
+ driver->GetCurrentImageData()->StoreUndoPoint("Drawing with paintbrush");
+ driver->RecordCurrentLabelUse();
// TODO: this is ugly. The code for applying a brush should really be
// placed in the IRISApplication.
@@ -379,6 +340,7 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging)
// Get the global objects
IRISApplication *driver = m_Parent->GetDriver();
GlobalState *gs = driver->GetGlobalState();
+ GenericImageData *gid = driver->GetCurrentImageData();
// Get the segmentation image
LabelImageWrapper *imgLabel = driver->GetCurrentImageData()->GetSegmentation();
@@ -417,13 +379,9 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging)
// Crop the region by the buffered region
xTestRegion.Crop(imgLabel->GetImage()->GetBufferedRegion());
- // Flag to see if anything was changed
- bool flagUpdate = false;
-
// Special code for Watershed brush
if(flagWatershed)
{
- GenericImageData *gid = driver->GetCurrentImageData();
// Get the currently engaged layer
ImageWrapperBase *context_layer = gid->FindLayer(m_ContextLayerId, false);
@@ -440,21 +398,23 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging)
}
// Shift vector (different depending on whether the brush has odd/even diameter
- Vector3f offset = ComputeOffset();
+ Vector3d offset = ComputeOffset();
- // Iterate over the region
- LabelImageWrapper::Iterator it(imgLabel->GetImage(), xTestRegion);
- for(; !it.IsAtEnd(); ++it)
+ // Iterate over the region using
+ SegmentationUpdateIterator it_update(
+ imgLabel->GetImage(), xTestRegion, drawing_color, drawover);
+
+ for(; !it_update.IsAtEnd(); ++it_update)
{
- // Check if we are inside the sphere
- LabelImageWrapper::ImageType::IndexType idx = it.GetIndex();
- Vector3f xDelta =
+ SegmentationUpdateIterator::IndexType idx = it_update.GetIndex();
+
+ Vector3d xDelta =
offset
- + to_float(Vector3l(idx.GetIndex()))
- - to_float(m_MousePosition);
+ + to_double(Vector3l(idx.GetIndex()))
+ - to_double(m_MousePosition);
Vector3d xDeltaSliceSpace = to_double(
- m_Parent->GetImageToDisplayTransform().TransformVector(xDelta));
+ m_Parent->GetImageToDisplayTransform()->TransformVector(xDelta));
// Check if the pixel is inside
if(!TestInside(xDeltaSliceSpace, pbs))
@@ -470,52 +430,34 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging)
}
// Paint the pixel
- LabelType pxLabel = it.Get();
-
- // Standard paint mode
- if(!reverse_mode)
- {
- if(drawover.CoverageMode == PAINT_OVER_ALL ||
- (drawover.CoverageMode == PAINT_OVER_ONE && pxLabel == drawover.DrawOverLabel) ||
- (drawover.CoverageMode == PAINT_OVER_VISIBLE && pxLabel != 0))
- {
- it.Set(drawing_color);
- if(pxLabel != drawing_color) flagUpdate = true;
- }
- }
- // Background paint mode (clear label over current label)
+ if(reverse_mode)
+ it_update.PaintAsBackground();
else
- {
- if(drawing_color != 0 && pxLabel == drawing_color)
- {
- it.Set(0);
- if(pxLabel != 0) flagUpdate = true;
- }
- else if(drawing_color == 0 && drawover.CoverageMode == PAINT_OVER_ONE)
- {
- it.Set(drawover.DrawOverLabel);
- if(pxLabel != drawover.DrawOverLabel) flagUpdate = true;
- }
- }
+ it_update.PaintAsForeground();
}
- // Image has been updated
- if(flagUpdate)
- {
- imgLabel->GetImage()->Modified();
- }
+ // Finalize the iteration
+ it_update.Finalize();
+
+ // If nothing actually changed, return
+ if(it_update.GetNumberOfChangedVoxels() == 0)
+ return false;
- return flagUpdate;
+ // Send the delta for undo
+ gid->StoreIntermediateUndoDelta(it_update.RelinquishDelta());
+
+ // Changes were made
+ return true;
}
-Vector3f PaintbrushModel::GetCenterOfPaintbrushInSliceSpace()
+Vector3d PaintbrushModel::GetCenterOfPaintbrushInSliceSpace()
{
PaintbrushSettings pbs =
m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings();
if(fmod(pbs.radius, 1.0) == 0)
- return m_Parent->MapImageToSlice(to_float(m_MousePosition));
+ return m_Parent->MapImageToSlice(to_double(m_MousePosition));
else
- return m_Parent->MapImageToSlice(to_float(m_MousePosition) + Vector3f(0.5f));
+ return m_Parent->MapImageToSlice(to_double(m_MousePosition) + Vector3d(0.5));
}
diff --git a/GUI/Model/PaintbrushModel.h b/GUI/Model/PaintbrushModel.h
index c663550..2e5f74f 100644
--- a/GUI/Model/PaintbrushModel.h
+++ b/GUI/Model/PaintbrushModel.h
@@ -22,18 +22,18 @@ public:
FIRES(PaintbrushMovedEvent)
- bool ProcessPushEvent(const Vector3f &xSlice, const Vector2ui &gridCell, bool reverse_mode);
- bool ProcessDragEvent(const Vector3f &xSlice, const Vector3f &xSliceLast,
+ bool ProcessPushEvent(const Vector3d &xSlice, const Vector2ui &gridCell, bool reverse_mode);
+ bool ProcessDragEvent(const Vector3d &xSlice, const Vector3d &xSliceLast,
double pixelsMoved, bool release);
- bool ProcessMouseMoveEvent(const Vector3f &xSlice);
+ bool ProcessMouseMoveEvent(const Vector3d &xSlice);
bool ProcessMouseLeaveEvent();
void AcceptAtCursor();
// Get the location in slice coordinates where the center of the paintbrush
// should be rendered
- Vector3f GetCenterOfPaintbrushInSliceSpace();
+ Vector3d GetCenterOfPaintbrushInSliceSpace();
protected:
@@ -47,7 +47,7 @@ protected:
// Mouse position in slice coordinates from which we need to draw the
// next segment
- Vector3f m_LastApplyX;
+ Vector3d m_LastApplyX;
// Layer over which the drawing operation started
unsigned long m_ContextLayerId;
@@ -58,8 +58,8 @@ protected:
PaintbrushModel();
virtual ~PaintbrushModel();
- Vector3f ComputeOffset();
- void ComputeMousePosition(const Vector3f &xSlice);
+ Vector3d ComputeOffset();
+ void ComputeMousePosition(const Vector3d &xSlice);
bool ApplyBrush(bool reverse_mode, bool dragging);
bool TestInside(const Vector2d &x, const PaintbrushSettings &ps);
diff --git a/GUI/Model/PolygonDrawingModel.cxx b/GUI/Model/PolygonDrawingModel.cxx
index 3e60b5f..8d452b7 100644
--- a/GUI/Model/PolygonDrawingModel.cxx
+++ b/GUI/Model/PolygonDrawingModel.cxx
@@ -43,20 +43,19 @@ PolygonDrawingModel
}
-Vector2f
-PolygonDrawingModel::GetPixelSize()
+Vector2d PolygonDrawingModel::GetPixelSize()
{
- float vppr = m_Parent->GetSizeReporter()->GetViewportPixelRatio();
- Vector3f x =
- m_Parent->MapWindowToSlice(Vector2f(vppr)) -
- m_Parent->MapWindowToSlice(Vector2f(0.0f));
+ double vppr = m_Parent->GetSizeReporter()->GetViewportPixelRatio();
+ Vector3d x =
+ m_Parent->MapWindowToSlice(Vector2d(vppr)) -
+ m_Parent->MapWindowToSlice(Vector2d(0.0));
- return Vector2f(x[0],x[1]);
+ return Vector2d(x[0],x[1]);
}
bool
PolygonDrawingModel
-::CheckNearFirstVertex(float x, float y, float pixel_x, float pixel_y)
+::CheckNearFirstVertex(double x, double y, double pixel_x, double pixel_y)
{
if(m_Vertices.size() > 2)
{
@@ -71,12 +70,12 @@ PolygonDrawingModel
bool
PolygonDrawingModel
-::ProcessPushEvent(float x, float y,
+::ProcessPushEvent(double x, double y,
bool shift_state)
{
bool handled = false;
- Vector2f pxsize = GetPixelSize();
- float pixel_x = pxsize(0), pixel_y = pxsize(1);
+ Vector2d pxsize = GetPixelSize();
+ double pixel_x = pxsize(0), pixel_y = pxsize(1);
if(m_State == INACTIVE_STATE)
{
@@ -163,13 +162,13 @@ PolygonDrawingModel
bool
PolygonDrawingModel
-::ProcessMouseMoveEvent(float x, float y)
+::ProcessMouseMoveEvent(double x, double y)
{
if(m_State == DRAWING_STATE)
{
// Check if we are hovering over the starting vertex
- Vector2f pxsize = GetPixelSize();
- float pixel_x = pxsize(0), pixel_y = pxsize(1);
+ Vector2d pxsize = GetPixelSize();
+ double pixel_x = pxsize(0), pixel_y = pxsize(1);
bool hover = CheckNearFirstVertex(x, y, pixel_x, pixel_y);
if(hover != m_HoverOverFirstVertex)
@@ -184,7 +183,7 @@ PolygonDrawingModel
bool
PolygonDrawingModel
-::ProcessDragEvent(float x, float y)
+::ProcessDragEvent(double x, double y)
{
bool handled = false;
if(m_State == DRAWING_STATE)
@@ -205,7 +204,7 @@ PolygonDrawingModel
}
else
{
- Vector2f pxsize = GetPixelSize();
+ Vector2d pxsize = GetPixelSize();
Vertex &v = m_Vertices.back();
double dx = (v.x-x) / pxsize[0];
double dy = (v.y-y) / pxsize[1];
@@ -256,11 +255,11 @@ PolygonDrawingModel
bool
PolygonDrawingModel
-::ProcessReleaseEvent(float x, float y)
+::ProcessReleaseEvent(double x, double y)
{
bool handled = false;
- Vector2f pxsize = GetPixelSize();
- float pixel_x = pxsize(0), pixel_y = pxsize(1);
+ Vector2d pxsize = GetPixelSize();
+ double pixel_x = pxsize(0), pixel_y = pxsize(1);
if(m_State == DRAWING_STATE)
{
@@ -283,7 +282,7 @@ PolygonDrawingModel
{
m_DraggingPickBox = false;
- float temp;
+ double temp;
if (m_SelectionBox[0] > m_SelectionBox[1])
{
temp = m_SelectionBox[0];
@@ -646,7 +645,7 @@ PolygonDrawingModel
// There may still be duplicates in the array, in which case we should
// add a tiny offset to them. Thanks to Jeff Tsao for this bug fix!
- std::set< std::pair<float, float> > xVertexSet;
+ std::set< std::pair<double, double> > xVertexSet;
vnl_random rnd;
for(VertexIterator it = m_Vertices.begin(); it != m_Vertices.end(); ++it)
{
@@ -729,8 +728,7 @@ PolygonDrawingModel
*/
bool
PolygonDrawingModel
-::CheckClickOnVertex(
- float x, float y, float pixel_x, float pixel_y, int k)
+::CheckClickOnVertex(double x, double y, double pixel_x, double pixel_y, int k)
{
// check if clicked within 4 pixels of a node (use closest node)
VertexIterator itmin = m_Vertices.end();
@@ -763,7 +761,7 @@ PolygonDrawingModel
bool
PolygonDrawingModel
::CheckClickOnLineSegment(
- float x, float y, float pixel_x, float pixel_y, int k)
+ double x, double y, double pixel_x, double pixel_y, int k)
{
// check if clicked near a line segment
VertexIterator itmin1 = m_Vertices.end(), itmin2 = m_Vertices.end();
diff --git a/GUI/Model/PolygonDrawingModel.h b/GUI/Model/PolygonDrawingModel.h
index 18498ca..b9efa9a 100644
--- a/GUI/Model/PolygonDrawingModel.h
+++ b/GUI/Model/PolygonDrawingModel.h
@@ -15,13 +15,13 @@ class GenericSliceModel;
struct PolygonVertex
{
- float x, y;
+ double x, y;
bool selected;
bool control;
- PolygonVertex(float x_, float y_, bool on_, bool ctl_)
+ PolygonVertex(double x_, double y_, bool on_, bool ctl_)
: x(x_), y(y_), selected(on_), control(ctl_) {}
- PolygonVertex() : x(0.0f), y(0.0f), selected(false), control(true) {}
- float &operator[](unsigned int i)
+ PolygonVertex() : x(0.0), y(0.0), selected(false), control(true) {}
+ double &operator[](unsigned int i)
{ return (i==0) ? x : y; }
};
@@ -52,7 +52,7 @@ public:
typedef VertexList::iterator VertexIterator;
typedef VertexList::reverse_iterator VertexRIterator;
- typedef vnl_vector_fixed<float, 4> BoxType;
+ typedef vnl_vector_fixed<double, 4> BoxType;
/** States that the polygon drawing is in */
enum PolygonState { INACTIVE_STATE = 0, DRAWING_STATE, EDITING_STATE };
@@ -119,19 +119,19 @@ public:
/** Is the cursor hovering over the starting voxel */
irisIsMacro(HoverOverFirstVertex)
- bool ProcessPushEvent(float x, float y, bool shift_state);
+ bool ProcessPushEvent(double x, double y, bool shift_state);
- bool ProcessDragEvent(float x, float y);
+ bool ProcessDragEvent(double x, double y);
- bool ProcessMouseMoveEvent(float x, float y);
+ bool ProcessMouseMoveEvent(double x, double y);
- bool ProcessReleaseEvent(float x, float y);
+ bool ProcessReleaseEvent(double x, double y);
/**
* Return the size of a screen logical pixel (as opposed to a physical
* pixel on the retina screen) in slice coordinate units
*/
- Vector2f GetPixelSize();
+ Vector2d GetPixelSize();
bool CheckState(PolygonDrawingUIState state);
@@ -159,25 +159,25 @@ protected:
// box the user drags to select new points
BoxType m_SelectionBox;
- float m_StartX, m_StartY;
+ double m_StartX, m_StartY;
// Whether we are hovering over the starting vertex
bool m_HoverOverFirstVertex;
void ComputeEditBox();
- void Add(float x, float y, int selected);
+ void Add(double x, double y, int selected);
void ProcessFreehandCurve();
- bool CheckClickOnVertex(float x, float y,
- float pixel_x, float pixel_y, int k);
+ bool CheckClickOnVertex(double x, double y,
+ double pixel_x, double pixel_y, int k);
- bool CheckClickOnLineSegment(float x, float y,
- float pixel_x, float pixel_y, int k);
+ bool CheckClickOnLineSegment(double x, double y,
+ double pixel_x, double pixel_y, int k);
// Check if the cursor (x,y) is near the first vertex (i.e., we should be
// closing the polygon)
- bool CheckNearFirstVertex(float x, float y, float pixel_x, float pixel_y);
+ bool CheckNearFirstVertex(double x, double y, double pixel_x, double pixel_y);
int GetNumberOfSelectedSegments();
diff --git a/GUI/Model/RegistrationModel.cxx b/GUI/Model/RegistrationModel.cxx
new file mode 100644
index 0000000..e92d4e9
--- /dev/null
+++ b/GUI/Model/RegistrationModel.cxx
@@ -0,0 +1,1090 @@
+#include "RegistrationModel.h"
+#include "IRISApplication.h"
+#include "GenericImageData.h"
+#include "IRISImageData.h"
+#include "GlobalUIModel.h"
+#include "SystemInterface.h"
+#include "HistoryManager.h"
+#include "itkQuaternionRigidTransform.h"
+#include "itkTransformFileWriter.h"
+#include "itkTransformFileReader.h"
+#include "itkTransformFactory.h"
+
+#include "ImageFunctions.h"
+#include "itkEuler3DTransform.h"
+#include "vnl/algo/vnl_svd.h"
+
+#include "OptimizationProgressRenderer.h"
+
+
+const unsigned long RegistrationModel::NOID = (unsigned long)(-1);
+
+RegistrationModel::RegistrationModel()
+{
+ m_MovingLayerModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetMovingLayerValueAndRange,
+ &Self::SetMovingLayerValue);
+
+ m_InteractiveToolModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetInteractiveToolValue,
+ &Self::SetInteractiveToolValue);
+
+ m_EulerAnglesModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetEulerAnglesValueAndRange,
+ &Self::SetEulerAnglesValue);
+
+ m_TranslationModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetTranslationValueAndRange,
+ &Self::SetTranslationValue);
+
+ m_ScalingModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetScalingValueAndRange,
+ &Self::SetScalingValue);
+
+ m_LogScalingModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetLogScalingValueAndRange,
+ &Self::SetLogScalingValue);
+
+ m_RotationCenter = Vector3ui(0, 0, 0);
+
+ // Set up the automatic registration parameters
+
+ // Registration mode
+ // TODO: add the other modes
+ TransformationDomain transform_domain;
+ transform_domain[RIGID] = "Rigid";
+ transform_domain[AFFINE] = "Affine";
+ m_TransformationModel = NewConcreteProperty(RIGID, transform_domain);
+
+ // Registration metric
+ SimilarityMetricDomain metric_domain;
+ metric_domain[NMI] = "Mutual information";
+ metric_domain[NCC] = "Cross-correlation";
+ metric_domain[SSD] = "Intensity difference";
+ m_SimilarityMetricModel = NewConcreteProperty(NMI, metric_domain);
+
+ // Mask model
+ m_UseSegmentationAsMaskModel = NewSimpleConcreteProperty(false);
+
+ m_LastMetricValueModel = NewSimpleConcreteProperty(0.0);
+
+ // Multi-resolution
+ m_CoarsestResolutionLevelModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetCoarsestResolutionLevelValueAndRange,
+ &Self::SetCoarsestResolutionLevelValue);
+
+ m_FinestResolutionLevelModel = wrapGetterSetterPairAsProperty(
+ this,
+ &Self::GetFinestResolutionLevelValueAndRange,
+ &Self::SetFinestResolutionLevelValue);
+
+ // Initialize the moving layer ID to be -1
+ m_MovingLayerId = NOID;
+
+ // Set up the metric renderer
+ m_RegistrationProgressRenderer = OptimizationProgressRenderer::New();
+ m_RegistrationProgressRenderer->SetModel(this);
+
+ m_Driver = NULL;
+ m_Parent = NULL;
+ m_GreedyAPI = NULL;
+}
+
+RegistrationModel::~RegistrationModel()
+{
+
+}
+
+void RegistrationModel::ResetOnMainImageChange()
+{
+ if(m_Driver->GetIRISImageData()->IsMainLoaded())
+ {
+ ImageWrapperBase* main_img = m_Driver->GetIRISImageData()->GetMain();
+ Vector3ui main_dim = main_img->GetSize();
+
+ // Reset the center of rotation
+ Vector3ui center;
+ for(int i = 0; i < 3; i++)
+ center[i] = main_dim[i] / 2;
+ this->SetRotationCenter(center);
+
+ // Reset the multi-resolution pyramid based on some heuristics
+ int dim_min = main_dim.min_value();
+ int dim_max = main_dim.max_value();
+
+ // The coarsest factor may not exceed smallest image dimension
+ int coarse_ub_1 = (int) (log2(dim_min));
+
+ // It does not make sense to do multi-resolution after the largest
+ // dimension has been reduced below 32. For example, for an image
+ // 300x140x120, 8x gives 37x17x15 but 16x would be pointless. But
+ // for an image that's 512x512x200 we want to offer 16x
+ int coarse_ub_2 = (int) (log2(dim_max / 32));
+
+ // We typically want to do registration at the at most two coarsest levels
+ m_CoarsestResolutionLevel = std::max(0, std::min(coarse_ub_1, coarse_ub_2));
+ m_FinestResolutionLevel = std::max(0, m_CoarsestResolutionLevel - 1);
+
+ // Update the domain with "1x", "2x", and so on
+ m_ResolutionLevelDomain.clear();
+ for(int i = 0; i <= m_CoarsestResolutionLevel; i++)
+ {
+ std::ostringstream oss;
+ oss << (1 << i) << "x";
+ m_ResolutionLevelDomain[i] = oss.str();
+ }
+ }
+}
+
+void RegistrationModel::UpdateManualParametersFromWrapper(bool force_update)
+{
+ ImageWrapperBase *layer = this->GetMovingLayerWrapper();
+
+ // If there is no layer, we just invalidate the parameters
+ if(!layer)
+ {
+ // Invalidate the parameters
+ m_ManualParam.LayerID = NOID;
+ return;
+ }
+
+ // Check if the data we have is already current
+ if(!force_update &&
+ m_MovingLayerId == m_ManualParam.LayerID &&
+ layer->GetITKTransform()->GetTimeStamp() <= m_ManualParam.UpdateTime)
+ return;
+
+ // Get the current transform
+ this->GetMovingTransform(m_ManualParam.AffineMatrix, m_ManualParam.AffineOffset);
+
+ // Decompose the transform into relevant parts
+ // first, there is the polar decomposition
+ // y = Ax + b = RSx + b
+ // here R is a rotation about the origin in the physical LPS coordinate system
+ // = RS(x - c) + RSc + b
+
+ // Perform polar decomposition
+ vnl_svd<double> svd(m_ManualParam.AffineMatrix.GetVnlMatrix());
+
+ ITKMatrixType rotation = svd.U() * svd.V().transpose();
+
+ // Get the rotation center in world coordinates
+ itk::Point<double, 3> ptCenter;
+ layer->GetReferenceSpace()->TransformIndexToPhysicalPoint(
+ to_itkIndex(m_RotationCenter), ptCenter);
+
+ // Compute Euler angles
+ typedef itk::Euler3DTransform<double> EulerTransform;
+ EulerTransform::Pointer euler = EulerTransform::New();
+ euler->SetCenter(ptCenter);
+ euler->SetMatrix(rotation);
+ euler->SetOffset(m_ManualParam.AffineOffset);
+
+ m_ManualParam.EulerAngles[0] = euler->GetAngleX();
+ m_ManualParam.EulerAngles[1] = euler->GetAngleY();
+ m_ManualParam.EulerAngles[2] = euler->GetAngleZ();
+
+ m_ManualParam.Translation[0] = euler->GetTranslation()[0];
+ m_ManualParam.Translation[1] = euler->GetTranslation()[1];
+ m_ManualParam.Translation[2] = euler->GetTranslation()[2];
+
+ // The scaling factors are the diagonal entries of the W matrix
+ m_ManualParam.Scaling[0] = svd.W()(0,0);
+ m_ManualParam.Scaling[1] = svd.W()(1,1);
+ m_ManualParam.Scaling[2] = svd.W()(2,2);
+
+ // The shearing rotation matrix can be represented as Euler angles or quaternion
+ // or whatever, but since we never present these parameters to the user, it is easier
+ // to just cache it in matrix format
+ m_ManualParam.ShearingMatrix = svd.V();
+
+ // Compute the range of the translation. This is based on the bounding box
+ // that contains both images in their native space
+ Vector3d ext_min_ref, ext_max_ref, ext_min_mov, ext_max_mov;
+ GetImagePhysicalExtents(layer->GetImageBase(), ext_min_mov, ext_max_mov);
+ GetImagePhysicalExtents(layer->GetReferenceSpace(), ext_min_ref, ext_max_ref);
+ Vector3d ext_min = vector_min(ext_min_ref, ext_min_mov);
+ Vector3d ext_max = vector_max(ext_max_ref, ext_max_mov);
+ Vector3d ext_range = ext_max - ext_min;
+
+ // Compute the step size of the translation - it should be smaller than 1/10 of the min-spacing
+ double min_spacing = 1e100;
+ for(int i = 0; i < 3; i++)
+ {
+ min_spacing = std::min(layer->GetImageBase()->GetSpacing()[i], min_spacing);
+ min_spacing = std::min(layer->GetReferenceSpace()->GetSpacing()[i], min_spacing);
+ }
+
+ double tran_step = pow(10, floor(log10(min_spacing / 10)));
+
+ m_ManualParam.TranslationRange.Set(-ext_range, ext_range, Vector3d(tran_step, tran_step, tran_step));
+ m_ManualParam.LayerID = m_MovingLayerId;
+ m_ManualParam.UpdateTime = layer->GetITKTransform()->GetTimeStamp();
+
+ this->InvokeEvent(ModelUpdateEvent());
+}
+
+void RegistrationModel::UpdateWrapperFromManualParameters()
+{
+ ImageWrapperBase *layer = this->GetMovingLayerWrapper();
+ assert(layer);
+
+ // Create a new euler transform
+ typedef itk::Euler3DTransform<double> EulerTransform;
+ EulerTransform::Pointer euler = EulerTransform::New();
+
+ // Get the rotation center in world coordinates
+ itk::Point<double, 3> ptCenter;
+ layer->GetReferenceSpace()->TransformIndexToPhysicalPoint(
+ to_itkIndex(m_RotationCenter), ptCenter);
+
+ euler->SetCenter(ptCenter);
+ euler->SetRotation(m_ManualParam.EulerAngles[0], m_ManualParam.EulerAngles[1], m_ManualParam.EulerAngles[2]);
+
+ ITKVectorType translation;
+ translation.SetVnlVector(m_ManualParam.Translation);
+ euler->SetTranslation(translation);
+
+
+ // Compute the scaling and shearing matrix
+ vnl_matrix_fixed<double, 3, 3> scaling;
+ scaling.fill(0.0);
+ scaling.set_diagonal(m_ManualParam.Scaling);
+
+ vnl_matrix_fixed<double, 3, 3> scale_shear =
+ m_ManualParam.ShearingMatrix * scaling * m_ManualParam.ShearingMatrix.transpose();
+
+ m_ManualParam.AffineMatrix = euler->GetMatrix().GetVnlMatrix() * scale_shear;
+ m_ManualParam.AffineOffset = euler->GetOffset();
+
+ // Create a new transform
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> AffineTransform;
+ AffineTransform::Pointer affine = AffineTransform::New();
+
+ // Set the matrix of the new transform y = R ( A x + b ) + z
+ affine->SetMatrix(m_ManualParam.AffineMatrix);
+ affine->SetOffset(m_ManualParam.AffineOffset);
+
+ // Update the layer's transform
+ layer->SetITKTransform(layer->GetReferenceSpace(), affine);
+
+ // Update the state of the cache
+ m_ManualParam.LayerID = m_MovingLayerId;
+ m_ManualParam.UpdateTime = layer->GetITKTransform()->GetTimeStamp();
+}
+
+void RegistrationModel::ApplyRotation(const Vector3d &axis, double theta)
+{
+ ImageWrapperBase *layer = this->GetMovingLayerWrapper();
+ assert(layer);
+
+ // Create a quaternion transform that will be applied on top of the existing
+ // transform
+ typedef itk::QuaternionRigidTransform<double> QuaterionTransform;
+ QuaterionTransform::Pointer rotation = QuaterionTransform::New();
+ QuaterionTransform::VnlQuaternionType quat(axis, theta);
+
+ // Get the rotation center in world coordinates
+ itk::Point<double, 3> ptCenter;
+ layer->GetReferenceSpace()->TransformIndexToPhysicalPoint(
+ to_itkIndex(m_RotationCenter), ptCenter);
+
+ rotation->SetRotation(quat);
+
+ // Figure out the center of rotation. The center of rotation should be preserved by
+ // the rotation, i.e. A * ptCenter + b = R(A * ptCenter + b) + \beta
+ vnl_matrix_fixed<double, 3, 3> A = m_ManualParam.AffineMatrix.GetVnlMatrix();
+ vnl_matrix_fixed<double, 3, 3> R = rotation->GetMatrix().GetVnlMatrix();
+ vnl_vector_fixed<double, 3> b = m_ManualParam.AffineOffset.GetVnlVector();
+ vnl_vector_fixed<double, 3> C = ptCenter.GetVnlVector();
+
+ m_ManualParam.AffineMatrix = A * R;
+ m_ManualParam.AffineOffset.SetVnlVector(A * C + b - A * (R * C));
+
+ // Update the transform
+ this->SetMovingTransform(
+ m_ManualParam.AffineMatrix,
+ m_ManualParam.AffineOffset);
+}
+
+void RegistrationModel::ApplyTranslation(const Vector3d &tran)
+{
+ ImageWrapperBase *layer = this->GetMovingLayerWrapper();
+ assert(layer);
+
+ // Get the current transform
+ ITKMatrixType matrix; ITKVectorType offset;
+ this->GetMovingTransform(matrix, offset);
+
+ // Add the translation to the offset
+ offset.SetVnlVector(offset.GetVnlVector() - matrix.GetVnlMatrix() * tran);
+
+ // Update the offset
+ this->SetMovingTransform(matrix, offset);
+}
+
+
+
+void RegistrationModel::SetRotationCenter(const Vector3ui &pos)
+{
+ m_RotationCenter = pos;
+ this->UpdateManualParametersFromWrapper(true);
+}
+
+void RegistrationModel::SetMovingTransform(const RegistrationModel::ITKMatrixType &matrix, const RegistrationModel::ITKVectorType &offset)
+{
+ // Create a new transform
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> AffineTransform;
+ AffineTransform::Pointer affine = AffineTransform::New();
+
+ // Set the matrix of the new transform y = R ( A x + b ) + z
+ affine->SetMatrix(matrix);
+ affine->SetOffset(offset);
+
+ // Create a new euler transform
+ ImageWrapperBase *layer = this->GetMovingLayerWrapper();
+ layer->SetITKTransform(layer->GetReferenceSpace(), affine);
+
+ // Update our parameters
+ this->UpdateManualParametersFromWrapper();
+}
+
+void RegistrationModel::GetMovingTransform(ITKMatrixType &matrix, ITKVectorType &offset)
+{
+ // Get the transform
+ ImageWrapperBase *layer = this->GetMovingLayerWrapper();
+ const ImageWrapperBase::ITKTransformType *transform = layer->GetITKTransform();
+
+ // TODO: in the future it might make more sense to stick to a single kind of
+ // transform in the ImageWrapper instead of allowing different transform
+ // classes. Using multiple classes seems pointless.
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformBase;
+ const TransformBase *tb = dynamic_cast<const TransformBase *>(transform);
+ matrix.SetIdentity();
+ offset.Fill(0.0);
+ if(tb)
+ {
+ matrix = tb->GetMatrix();
+ offset = tb->GetOffset();
+ }
+}
+
+ImageWrapperBase *RegistrationModel::GetMovingLayerWrapper()
+{
+ if(m_MovingLayerId == NOID)
+ return NULL;
+
+ return m_Driver->GetCurrentImageData()->FindLayer(m_MovingLayerId, false, OVERLAY_ROLE);
+}
+
+void RegistrationModel::SetIterationCommand(itk::Command *command)
+{
+ m_IterationCommand = command;
+}
+
+#include "GreedyAPI.h"
+void RegistrationModel::RunAutoRegistration()
+{
+ // Obtain the fixed and moving images.
+ ImageWrapperBase *fixed = this->GetParent()->GetDriver()->GetCurrentImageData()->GetMain();
+ ImageWrapperBase *moving = this->GetMovingLayerWrapper();
+
+ // TODO: for now, we are not supporting vector image registration, only registration between
+ // scalar components; and we use the default scalar component.
+ SmartPtr<ScalarImageWrapperBase::FloatVectorImageSource> castFixed =
+ fixed->GetDefaultScalarRepresentation()->CreateCastToFloatVectorPipeline();
+ castFixed->UpdateOutputInformation();
+
+ SmartPtr<ScalarImageWrapperBase::FloatVectorImageSource> castMoving =
+ moving->GetDefaultScalarRepresentation()->CreateCastToFloatVectorPipeline();
+ castMoving->UpdateOutputInformation();
+
+ // Caster for the mask image - declared here so that SmartPtr does not go out of scope
+ SmartPtr<ScalarImageWrapperBase::FloatImageSource> castMask;
+
+ // Set up the parameters for greedy registration
+ GreedyParameters param;
+ GreedyParameters::SetToDefaults(param);
+
+ // Create an API object
+ m_GreedyAPI = new GreedyAPI();
+
+ // Configure the fixed and moving images
+ ImagePairSpec ip;
+ ip.weight = 1.0;
+ ip.fixed = "FIXED_IMAGE";
+ ip.moving = "MOVING_IMAGE";
+ param.inputs.push_back(ip);
+
+ // Pass the actual images to the cache
+ m_GreedyAPI->AddCachedInputObject(ip.fixed, castFixed->GetOutput());
+ m_GreedyAPI->AddCachedInputObject(ip.moving, castMoving->GetOutput());
+
+ // Mask image
+ if(this->GetUseSegmentationAsMask())
+ {
+ param.gradient_mask = "GRADIENT_MASK";
+ ImageWrapperBase *seg = this->GetParent()->GetDriver()->GetCurrentImageData()->GetSegmentation();
+ castMask = seg->GetDefaultScalarRepresentation()->CreateCastToFloatPipeline();
+ castMask->UpdateLargestPossibleRegion();
+ m_GreedyAPI->AddCachedInputObject(param.gradient_mask, castMask->GetOutput());
+ }
+
+ // Set up the metric
+ switch(m_SimilarityMetricModel->GetValue())
+ {
+ case NCC:
+ param.metric = GreedyParameters::NCC;
+ param.metric_radius = std::vector<int>(3, 4);
+ break;
+ case NMI:
+ param.metric = GreedyParameters::NMI;
+ break;
+ default:
+ param.metric = GreedyParameters::SSD;
+ break;
+ };
+
+ // Set up the degrees of freedom
+ if(m_TransformationModel->GetValue() == RIGID)
+ param.affine_dof = GreedyParameters::DOF_RIGID;
+ else
+ param.affine_dof = GreedyParameters::DOF_AFFINE;
+
+ // Set up the pyramid
+ param.iter_per_level.clear();
+ for(int k = m_CoarsestResolutionLevel; k >= 0; k--)
+ {
+ if(k >= m_FinestResolutionLevel)
+ param.iter_per_level.push_back(100);
+ else
+ param.iter_per_level.push_back(0);
+ }
+
+ // Create a transform spec
+ param.affine_init_mode = RAS_FILENAME;
+ param.affine_init_transform.filename = "INPUT_TRANSFORM";
+ param.affine_init_transform.exponent = 1;
+
+ // Pass the input transformation object to the cache
+ ITKMatrixType matrix; ITKVectorType offset;
+ this->GetMovingTransform(matrix, offset);
+
+ // Unfortunately, we have to cast to float, argh!
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformType;
+ TransformType::Pointer tran = TransformType::New();
+ tran->SetMatrix(matrix);
+ tran->SetOffset(offset);
+
+ // Finally pass the float transform to the API
+ m_GreedyAPI->AddCachedInputObject(param.affine_init_transform.filename, tran);
+
+ // Pass the output string - same as the input transform
+ param.output = param.affine_init_transform.filename;
+
+ // Handle intermediate data
+ if(m_IterationCommand)
+ {
+ typedef itk::MemberCommand<Self> CommandType;
+ CommandType::Pointer cmd = CommandType::New();
+ cmd->SetCallbackFunction(this, &RegistrationModel::IterationCallback);
+ param.output_intermediate = param.affine_init_transform.filename;
+ tran->AddObserver(itk::ModifiedEvent(), cmd);
+ }
+
+ // Run the registration
+ m_GreedyAPI->RunAffine(param);
+
+ // Now, the transform tran should hold our matrix and offset
+ this->SetMovingTransform(tran->GetMatrix(), tran->GetOffset());
+
+ // Delete the API
+ delete(m_GreedyAPI); m_GreedyAPI = NULL;
+}
+
+void RegistrationModel::MatchByMoments(int order)
+{
+ // Obtain the fixed and moving images.
+ ImageWrapperBase *fixed = this->GetParent()->GetDriver()->GetCurrentImageData()->GetMain();
+ ImageWrapperBase *moving = this->GetMovingLayerWrapper();
+
+ // TODO: for now, we are not supporting vector image registration, only registration between
+ // scalar components; and we use the default scalar component.
+ SmartPtr<ScalarImageWrapperBase::FloatVectorImageSource> castFixed =
+ fixed->GetDefaultScalarRepresentation()->CreateCastToFloatVectorPipeline();
+ castFixed->UpdateOutputInformation();
+
+ SmartPtr<ScalarImageWrapperBase::FloatVectorImageSource> castMoving =
+ moving->GetDefaultScalarRepresentation()->CreateCastToFloatVectorPipeline();
+ castMoving->UpdateOutputInformation();
+
+ // Set up the parameters for greedy registration
+ GreedyParameters param;
+ GreedyParameters::SetToDefaults(param);
+
+ // Create an API object
+ m_GreedyAPI = new GreedyAPI();
+
+ // Configure the fixed and moving images
+ ImagePairSpec ip;
+ ip.weight = 1.0;
+ ip.fixed = "FIXED_IMAGE";
+ ip.moving = "MOVING_IMAGE";
+ param.inputs.push_back(ip);
+
+ // Pass the actual images to the cache
+ m_GreedyAPI->AddCachedInputObject(ip.fixed, castFixed->GetOutput());
+ m_GreedyAPI->AddCachedInputObject(ip.moving, castMoving->GetOutput());
+
+ // Set up the metric
+ switch(m_SimilarityMetricModel->GetValue())
+ {
+ case NCC:
+ param.metric = GreedyParameters::NCC;
+ param.metric_radius = std::vector<int>(3, 4);
+ break;
+ case NMI:
+ param.metric = GreedyParameters::NMI;
+ break;
+ default:
+ param.metric = GreedyParameters::SSD;
+ break;
+ };
+
+ // Create a transform spec
+ param.affine_init_mode = RAS_FILENAME;
+ param.affine_init_transform.filename = "INPUT_TRANSFORM";
+ param.affine_init_transform.exponent = 1;
+
+ // Pass the input transformation object to the cache
+ ITKMatrixType matrix; ITKVectorType offset;
+ this->GetMovingTransform(matrix, offset);
+
+ // Unfortunately, we have to cast to float, argh!
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformType;
+ TransformType::Pointer tran = TransformType::New();
+ tran->SetMatrix(matrix);
+ tran->SetOffset(offset);
+
+ // Finally pass the float transform to the API
+ m_GreedyAPI->AddCachedInputObject(param.affine_init_transform.filename, tran);
+
+ // Pass the output string - same as the input transform
+ param.output = param.affine_init_transform.filename;
+
+ // Set the order of the moments match
+ param.moments_order = order;
+
+ // Run the registration
+ m_GreedyAPI->RunAlignMoments(param);
+
+ // Now, the transform tran should hold our matrix and offset
+ this->SetMovingTransform(tran->GetMatrix(), tran->GetOffset());
+
+ // Delete the API
+ delete(m_GreedyAPI); m_GreedyAPI = NULL;
+}
+
+
+void RegistrationModel::LoadTransform(const char *filename, TransformFormat format)
+{
+ // Get the current transform to store
+ ITKMatrixType matrix;
+ ITKVectorType offset;
+
+ // Load based on the transform type
+ if(format == FORMAT_C3D)
+ {
+ // Read the matrix file
+ vnl_matrix<double> Q(4, 4);
+ std::ifstream fin(filename);
+ for(size_t i = 0; i < 4; i++)
+ for(size_t j = 0; j < 4; j++)
+ if(fin.good())
+ {
+ fin >> Q[i][j];
+ }
+ else
+ {
+ fin.close();
+ throw IRISException("Unable to read 4x4 matrix from file %s", filename);
+ }
+ fin.close();
+
+ // Flip between RAS and LPS
+ Q(2,0) *= -1; Q(2,1) *= -1;
+ Q(0,2) *= -1; Q(1,2) *= -1;
+ Q(0,3) *= -1; Q(1,3) *= -1;
+
+ // Populate the matrix and offset components
+ matrix.GetVnlMatrix() = Q.extract(3, 3, 0, 0);
+ offset.SetVnlVector(Q.get_column(3).extract(3, 0));
+ }
+ else // (format == FORMAT_ITK)
+ {
+ // The transform format we expect
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformBase;
+
+ // Create a factory for reading base transforms
+ itk::TransformFactory<TransformBase>::RegisterTransform();
+
+ // Read the trasnform
+ itk::TransformFileReader::Pointer reader = itk::TransformFileReader::New();
+ reader->SetFileName(filename);
+ reader->Update();
+
+ // Get the transform pointer
+ TransformBase *tran = NULL;
+ if(reader->GetTransformList()->size())
+ tran = dynamic_cast<TransformBase *>(reader->GetTransformList()->front().GetPointer());
+
+ if(tran == NULL)
+ throw IRISException("Failed to read linear transform from file %s", filename);
+
+ // Assign the matrix/offset
+ matrix = tran->GetMatrix();
+ offset = tran->GetOffset();
+ }
+
+ // Update the history
+ this->GetParent()->GetSystemInterface()
+ ->GetHistoryManager()->UpdateHistory("AffineTransform", filename, true);
+
+ // Now, the transform tran should hold our matrix and offset
+ this->SetMovingTransform(matrix, offset);
+}
+
+void RegistrationModel::SaveTransform(const char *filename, TransformFormat format)
+{
+ // Get the current transform to store
+ ITKMatrixType matrix;
+ ITKVectorType offset;
+ this->GetMovingTransform(matrix, offset);
+
+ // Save based on the transform type
+ if(format == FORMAT_C3D)
+ {
+ // Create a 4x4 matrix in RAS coordinate space
+ vnl_matrix<double> Q(4, 4);
+ vnl_matrix<double> v(offset.GetVnlVector().data_block(), 3, 1);
+ Q.set_identity();
+ Q.update(matrix.GetVnlMatrix(), 0, 0);
+ Q.update(v, 0, 3);
+
+ // Flip between RAS and LPS
+ Q(2,0) *= -1; Q(2,1) *= -1;
+ Q(0,2) *= -1; Q(1,2) *= -1;
+ Q(0,3) *= -1; Q(1,3) *= -1;
+
+ // Write the matrix
+ std::ofstream matrixFile;
+ matrixFile.open(filename);
+ matrixFile << Q;
+ matrixFile.close();
+ }
+ else // (format == FORMAT_ITK)
+ {
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformBase;
+ TransformBase::Pointer transform = TransformBase::New();
+
+ transform->SetMatrix(matrix);
+ transform->SetOffset(offset);
+
+ itk::TransformFileWriter::Pointer writer = itk::TransformFileWriter::New();
+ writer->SetFileName(filename);
+ writer->SetInput(transform);
+ writer->Update();
+ }
+
+ // Update the history
+ this->GetParent()->GetSystemInterface()
+ ->GetHistoryManager()->UpdateHistory("AffineTransform", filename, true);
+}
+
+const std::vector<std::vector<double> > &RegistrationModel::GetRegistrationMetricLog() const
+{
+ return m_GreedyAPI->GetMetricLog();
+}
+
+void RegistrationModel::OnDialogClosed()
+{
+ // Don't leave the interactive mode on
+ if(m_InteractiveToolModel->GetValue())
+ m_InteractiveToolModel->SetValue(false);
+}
+
+void RegistrationModel
+::ResliceMovingImage(InterpolationMethod method)
+{
+ ImageWrapperBase *moving = this->GetMovingLayerWrapper();
+ assert(moving);
+
+ // We can take advantage of existing facilities for interpolation and reslicing
+ // in the ImageWrapper class
+ SNAPSegmentationROISettings roi;
+ roi.SetInterpolationMethod(method);
+ roi.SetROI(m_Driver->GetCurrentImageData()->GetMain()->GetBufferedRegion());
+ SmartPtr<ImageWrapperBase> reslice =
+ moving->ExtractROI(roi, m_Parent->GetProgressCommand());
+
+ // Give it a nickname
+ reslice->SetCustomNickname(std::string("resliced ") + moving->GetNickname());
+
+ // This wrapper can now be added to the main list of wrappers
+ m_Driver->AddDerivedOverlayImage(moving, reslice, true);
+}
+
+bool RegistrationModel::CheckState(RegistrationModel::UIState state)
+{
+ switch(state)
+ {
+ case UIF_MOVING_SELECTION_AVAILABLE:
+ return m_Driver->GetIRISImageData()->GetNumberOfLayers(OVERLAY_ROLE) > 0;
+ case UIF_MOVING_SELECTED:
+ return m_MovingLayerId != NOID;
+ default:
+ return false;
+ }
+}
+
+void RegistrationModel::SetParentModel(GlobalUIModel *model)
+{
+ m_Parent = model;
+ m_Driver = model->GetDriver();
+
+ // Layer changes require a lot of things to be reset
+ Rebroadcast(m_Driver, LayerChangeEvent(), ModelUpdateEvent());
+ Rebroadcast(m_Driver, LayerChangeEvent(), StateMachineChangeEvent());
+
+ // Changes to layers also affect state
+ Rebroadcast(m_Driver, WrapperChangeEvent(), ModelUpdateEvent());
+
+ // Interactive mode tool model is just a wrapper around the toolbar mode
+ m_InteractiveToolModel->RebroadcastFromSourceProperty(
+ m_Driver->GetGlobalState()->GetToolbarModeModel());
+}
+
+void RegistrationModel::OnUpdate()
+{
+ bool main_changed = this->m_EventBucket->HasEvent(MainImageDimensionsChangeEvent());
+ bool layers_changed = this->m_EventBucket->HasEvent(LayerChangeEvent());
+ bool wrapper_updated = this->m_EventBucket->HasEvent(WrapperChangeEvent());
+
+ // Check for changes in the active layers
+ if(layers_changed)
+ {
+ // Check if the active layer is still available
+ if(!m_Driver->GetCurrentImageData()->FindLayer(m_MovingLayerId, false, OVERLAY_ROLE))
+ {
+ // Set the moving layer ID to the first available overlay
+ LayerIterator it = m_Driver->GetCurrentImageData()->GetLayers(OVERLAY_ROLE);
+ m_MovingLayerId = it.IsAtEnd() ? NOID : it.GetLayer()->GetUniqueId();
+ }
+ }
+
+ // Specifically for when the main image changes
+ if(main_changed)
+ {
+ // Reset the center of rotation to the image center
+ this->ResetOnMainImageChange();
+ }
+
+ // If there is a wrapper update, update the transform parameters
+ if(wrapper_updated || layers_changed)
+ {
+ // This will update the cached parameters
+ this->UpdateManualParametersFromWrapper();
+ }
+}
+
+void RegistrationModel::SetCenterOfRotationToCursor()
+{
+ this->SetRotationCenter(m_Driver->GetCursorPosition());
+}
+
+void RegistrationModel::ResetTransformToIdentity()
+{
+ ITKMatrixType matrix;
+ ITKVectorType offset;
+
+ matrix.SetIdentity();
+ offset.Fill(0.0);
+
+ this->SetMovingTransform(matrix, offset);
+}
+
+void RegistrationModel::MatchImageCenters()
+{
+ // Set the transforms so that the center voxels of the two images are matched
+ ImageWrapperBase *layer = this->GetMovingLayerWrapper();
+ assert(layer);
+
+ // Get the reference space - should match main image
+ ImageWrapperBase::ImageBaseType *refspc = layer->GetReferenceSpace();
+ ImageWrapperBase::ImageBaseType *movspc = layer->GetImageBase();
+
+
+ // Compute the world coordinates of the image centers
+ itk::Index<3> ciRef, ciMov;
+ for(int d = 0; d < 3; d++)
+ {
+ ciRef[d] = refspc->GetLargestPossibleRegion().GetSize()[d] / 2;
+ ciMov[d] = movspc->GetLargestPossibleRegion().GetSize()[d] / 2;
+ }
+
+ // Get the rotation center in world coordinates
+ itk::Point<double, 3> cpRef, cpMov;
+ refspc->TransformIndexToPhysicalPoint(ciRef, cpRef);
+ movspc->TransformIndexToPhysicalPoint(ciMov, cpMov);
+
+ // Translation should take cpRef to cpMov
+ ITKMatrixType matrix;
+ ITKVectorType offset;
+ this->GetMovingTransform(matrix, offset);
+
+ offset = cpMov - matrix * cpRef;
+
+ this->SetMovingTransform(matrix, offset);
+}
+
+
+
+bool RegistrationModel::GetMovingLayerValueAndRange(unsigned long &value, RegistrationModel::LayerSelectionDomain *range)
+{
+ // There must be at least one layer
+ LayerIterator it = m_Driver->GetCurrentImageData()->GetLayers(OVERLAY_ROLE);
+ if(it.IsAtEnd())
+ return false;
+
+ // Return the active ID
+ value = m_MovingLayerId;
+
+ // Compute the range of IDs
+ if(range)
+ {
+ range->clear();
+ for(; !it.IsAtEnd(); ++it)
+ {
+ (*range)[it.GetLayer()->GetUniqueId()] = it.GetLayer()->GetNickname();
+ }
+ }
+
+ return true;
+}
+
+void RegistrationModel::SetMovingLayerValue(unsigned long value)
+{
+ // Set the layer id
+ m_MovingLayerId = value;
+
+ // Update the cache
+ this->UpdateManualParametersFromWrapper();
+
+ // Fire a state change
+ this->InvokeEvent(StateMachineChangeEvent());
+}
+
+bool RegistrationModel::GetInteractiveToolValue(bool &value)
+{
+ value = (m_Parent->GetGlobalState()->GetToolbarMode() == REGISTRATION_MODE);
+ return true;
+}
+
+void RegistrationModel::SetInteractiveToolValue(bool value)
+{
+ if(value)
+ m_Parent->GetGlobalState()->SetToolbarMode(REGISTRATION_MODE);
+ else
+ m_Parent->GetGlobalState()->SetToolbarMode(CROSSHAIRS_MODE);
+}
+
+bool RegistrationModel::GetEulerAnglesValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range)
+{
+ // Make sure that the manual parameters are valid
+ if(m_ManualParam.LayerID == NOID)
+ return false;
+
+ // Assign the value trivially
+ value = m_ManualParam.EulerAngles * (180.0 / vnl_math::pi);
+
+ // Handle the range
+ if(range)
+ {
+ range->Set(Vector3d(-180.0, -90.0, -180.0),
+ Vector3d(+180.0, +90.0, +180.0),
+ Vector3d(0.1, 0.1, 0.1));
+ }
+ return true;
+}
+
+void RegistrationModel::SetEulerAnglesValue(Vector3d value)
+{
+ // Update the euler angles from the widget
+ m_ManualParam.EulerAngles = value * (vnl_math::pi / 180.0);
+
+ // Update the transform using the euler angles
+ this->UpdateWrapperFromManualParameters();
+}
+
+bool RegistrationModel::GetTranslationValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range)
+{
+ // Make sure that the manual parameters are valid
+ if(m_ManualParam.LayerID == NOID)
+ return false;
+
+ // Assign the value trivially
+ value = m_ManualParam.Translation;
+
+ // Handle the range
+ if(range)
+ {
+ *range = m_ManualParam.TranslationRange;
+ }
+ return true;
+
+}
+
+void RegistrationModel::SetTranslationValue(Vector3d value)
+{
+ // Update the translation vector
+ m_ManualParam.Translation = value;
+
+ // Update the transform
+ this->UpdateWrapperFromManualParameters();
+}
+
+bool RegistrationModel::GetScalingValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range)
+{
+ // Make sure that the manual parameters are valid
+ if(m_ManualParam.LayerID == NOID)
+ return false;
+
+ // Assign the value trivially
+ value = m_ManualParam.Scaling;
+
+ // Handle the range
+ if(range)
+ {
+ range->Set(Vector3d(0.01, 0.01, 0.01), Vector3d(100.0, 100.0, 100.0), Vector3d(0.01, 0.01, 0.01));
+ }
+ return true;
+}
+
+void RegistrationModel::SetScalingValue(Vector3d value)
+{
+ // Update the translation vector
+ m_ManualParam.Scaling = value;
+
+ // Update the transform
+ this->UpdateWrapperFromManualParameters();
+}
+
+bool RegistrationModel::GetLogScalingValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range)
+{
+ // Make sure that the manual parameters are valid
+ if(m_ManualParam.LayerID == NOID)
+ return false;
+
+ // Assign the value trivially
+ for(int i = 0; i < 3; i++)
+ value[i] = log10(m_ManualParam.Scaling[i]);
+
+ // Handle the range
+ if(range)
+ {
+ range->Set(Vector3d(-1., -1., -1.), Vector3d(1., 1., 1.), Vector3d(0.01, 0.01, 0.01));
+ }
+ return true;
+}
+
+void RegistrationModel::SetLogScalingValue(Vector3d value)
+{
+ // Update the translation vector
+ for(int i = 0; i < 3; i++)
+ m_ManualParam.Scaling[i] = pow(10.0, value[i]);
+
+ // Update the transform
+ this->UpdateWrapperFromManualParameters();
+}
+
+bool RegistrationModel::GetCoarsestResolutionLevelValueAndRange(
+ int &value, ResolutionLevelDomain *domain)
+{
+ // Is there a moving image?
+ if(!this->GetMovingLayerWrapper())
+ return false;
+
+ value = m_CoarsestResolutionLevel;
+ if(domain)
+ *domain = m_ResolutionLevelDomain;
+
+ return true;
+}
+
+void RegistrationModel::SetCoarsestResolutionLevelValue(int value)
+{
+ m_CoarsestResolutionLevel = value;
+ if(m_FinestResolutionLevel > value)
+ {
+ m_FinestResolutionLevelModel->SetValue(value);
+ this->InvokeEvent(ModelUpdateEvent());
+ }
+}
+
+bool RegistrationModel::GetFinestResolutionLevelValueAndRange(
+ int &value, ResolutionLevelDomain *domain)
+{
+ // Is there a moving image?
+ if(!this->GetMovingLayerWrapper())
+ return false;
+
+ value = m_FinestResolutionLevel;
+ if(domain)
+ *domain = m_ResolutionLevelDomain;
+
+ return true;
+}
+
+void RegistrationModel::SetFinestResolutionLevelValue(int value)
+{
+ m_FinestResolutionLevel = value;
+ if(m_CoarsestResolutionLevel < value)
+ {
+ m_CoarsestResolutionLevelModel->SetValue(value);
+ this->InvokeEvent(ModelUpdateEvent());
+ }
+}
+
+void RegistrationModel::IterationCallback(const itk::Object *object, const itk::EventObject &event)
+{
+ // Get the transform parameters
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformType;
+ const TransformType *tran = dynamic_cast<const TransformType *>(object);
+
+ // Apply the transform
+ this->SetMovingTransform(tran->GetMatrix(), tran->GetOffset());
+
+ // Update the last metric value
+ const GreedyAPI::MetricLogType &metric_log = m_GreedyAPI->GetMetricLog();
+ if(metric_log.size())
+ {
+ const std::vector<double> &last_log = metric_log.back();
+ if(last_log.size())
+ m_LastMetricValueModel->SetValue(last_log.back());
+ }
+
+ // Fire the iteration command - this is to force the GUI to process events, instead of
+ // just putting the above ModelUpdateEvent() into a bucket
+ if(m_IterationCommand)
+ m_IterationCommand->Execute(object, event);
+}
+
+
+
diff --git a/GUI/Model/RegistrationModel.h b/GUI/Model/RegistrationModel.h
new file mode 100644
index 0000000..3f645ea
--- /dev/null
+++ b/GUI/Model/RegistrationModel.h
@@ -0,0 +1,270 @@
+#ifndef REGISTRATIONMODEL_H
+#define REGISTRATIONMODEL_H
+
+#include "AbstractModel.h"
+#include "PropertyModel.h"
+#include "itkMatrix.h"
+#include "itkVector.h"
+
+class GlobalUIModel;
+class IRISApplication;
+class ImageWrapperBase;
+class OptimizationProgressRenderer;
+
+template <unsigned int VDim, class TReal> class GreedyApproach;
+
+namespace itk
+{
+ class Command;
+}
+
+class RegistrationModel : public AbstractModel
+{
+public:
+ irisITKObjectMacro(RegistrationModel, AbstractModel)
+
+ /**
+ States in which the model can be, which allow the activation and
+ deactivation of various widgets in the interface
+ */
+ enum UIState {
+ UIF_MOVING_SELECTION_AVAILABLE,
+ UIF_MOVING_SELECTED
+ };
+
+ /** Allowed transformation models - to be expanded in the future */
+ enum Transformation { RIGID = 0, AFFINE, INVALID_MODE };
+
+ /** Image similarity metrics */
+ enum SimilarityMetric { NMI = 0, NCC, SSD, INVALID_METRIC };
+
+ /** Types of transform file */
+ enum TransformFormat { FORMAT_ITK = 0, FORMAT_C3D };
+
+ /**
+ Check the state flags above
+ */
+ bool CheckState(UIState state);
+
+ void SetParentModel(GlobalUIModel *model);
+ irisGetMacro(Parent, GlobalUIModel *)
+
+ virtual void OnUpdate();
+
+ typedef SimpleItemSetDomain<unsigned long, std::string> LayerSelectionDomain;
+ typedef AbstractPropertyModel<unsigned long, LayerSelectionDomain> AbstractLayerSelectionModel;
+
+ /** Model for selecting the layer for registration */
+ irisGetMacro(MovingLayerModel, AbstractLayerSelectionModel *)
+
+ /** Euler angles */
+ irisGetMacro(EulerAnglesModel, AbstractRangedDoubleVec3Property *)
+
+ /** Translation */
+ irisGetMacro(TranslationModel, AbstractRangedDoubleVec3Property *)
+
+ /** Scaling factor */
+ irisGetMacro(ScalingModel, AbstractRangedDoubleVec3Property *)
+
+ /** Logarithm of the scaling factor - for the slider */
+ irisGetMacro(LogScalingModel, AbstractRangedDoubleVec3Property *)
+
+ /** Interactive registration tool button */
+ irisGetMacro(InteractiveToolModel, AbstractSimpleBooleanProperty *)
+
+ /** Set the center of rotation to current cross-hairs position */
+ void SetCenterOfRotationToCursor();
+
+ /** Reset the rotation to identity */
+ void ResetTransformToIdentity();
+
+ /** Match image centers */
+ void MatchImageCenters();
+
+ /** Match moments of inertia */
+ void MatchByMoments(int order);
+
+ /** Apply a rotation around a fixed angle */
+ void ApplyRotation(const Vector3d &axis, double theta);
+
+ /** Apply a translation (specified in physical ITK space) */
+ void ApplyTranslation(const Vector3d &tran);
+
+ /** Get a pointer to the selected moving wrapper, or NULL if none selected */
+ ImageWrapperBase *GetMovingLayerWrapper();
+
+ /** Get the center of rotation, in voxel units of the main image */
+ irisGetMacro(RotationCenter, Vector3ui)
+
+ // Automatic registration parameter domains
+ typedef SimpleItemSetDomain<Transformation, std::string> TransformationDomain;
+ typedef SimpleItemSetDomain<SimilarityMetric, std::string> SimilarityMetricDomain;
+ typedef SimpleItemSetDomain<int, std::string> ResolutionLevelDomain;
+
+ // Access to registration models
+ irisGenericPropertyAccessMacro(Transformation, Transformation, TransformationDomain)
+ irisGenericPropertyAccessMacro(SimilarityMetric, SimilarityMetric, SimilarityMetricDomain)
+ irisSimplePropertyAccessMacro(UseSegmentationAsMask, bool)
+ irisGenericPropertyAccessMacro(CoarsestResolutionLevel, int, ResolutionLevelDomain)
+ irisGenericPropertyAccessMacro(FinestResolutionLevel, int, ResolutionLevelDomain)
+
+ void SetIterationCommand(itk::Command *command);
+
+ void RunAutoRegistration();
+
+ void LoadTransform(const char *filename, TransformFormat format);
+
+ void SaveTransform(const char *filename, TransformFormat format);
+
+ const std::vector< std::vector<double> > &GetRegistrationMetricLog() const;
+
+ irisSimplePropertyAccessMacro(LastMetricValue, double)
+
+ /** Get the progress renderer object */
+ irisGetMacro(RegistrationProgressRenderer, OptimizationProgressRenderer *)
+
+ /** Cleanup on dialog closed (but not necessarily destroyed) */
+ void OnDialogClosed();
+
+ /** Reslice moving image */
+ void ResliceMovingImage(InterpolationMethod method);
+
+
+protected:
+ RegistrationModel();
+ ~RegistrationModel();
+
+ typedef itk::Matrix<double, 3, 3> ITKMatrixType;
+ typedef itk::Vector<double, 3> ITKVectorType;
+ typedef GreedyApproach<3, float> GreedyAPI;
+
+ GlobalUIModel *m_Parent;
+ IRISApplication *m_Driver;
+
+ // Pointer to the GreedyAPI. This is only non-null during RunAutoRegistration();
+ GreedyAPI *m_GreedyAPI;
+
+ void ResetOnMainImageChange();
+
+ // This method is used to updated the cached matrix/offset and the parameters such
+ // as Euler angles from the information in the moving image wrapper
+ void UpdateManualParametersFromWrapper(bool force_update = false);
+
+ // This method is called to recompute the transform in the moving image wrapper from
+ // parameters including scaling, euler angles, and translation
+ void UpdateWrapperFromManualParameters();
+
+ void SetRotationCenter(const Vector3ui &pos);
+
+ // Get the transform currently stored in the moving layer
+ void GetMovingTransform(ITKMatrixType &matrix, ITKVectorType &offset);
+
+ // Set the transform in the moving layer
+ void SetMovingTransform(const ITKMatrixType &matrix, const ITKVectorType &offset);
+
+ SmartPtr<AbstractLayerSelectionModel> m_MovingLayerModel;
+ bool GetMovingLayerValueAndRange(unsigned long &value, LayerSelectionDomain *range);
+ void SetMovingLayerValue(unsigned long value);
+
+ SmartPtr<AbstractSimpleBooleanProperty> m_InteractiveToolModel;
+ bool GetInteractiveToolValue(bool &value);
+ void SetInteractiveToolValue(bool value);
+
+ SmartPtr<AbstractRangedDoubleVec3Property> m_EulerAnglesModel;
+ bool GetEulerAnglesValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range);
+ void SetEulerAnglesValue(Vector3d value);
+
+ SmartPtr<AbstractRangedDoubleVec3Property> m_TranslationModel;
+ bool GetTranslationValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range);
+ void SetTranslationValue(Vector3d value);
+
+ SmartPtr<AbstractRangedDoubleVec3Property> m_ScalingModel;
+ bool GetScalingValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range);
+ void SetScalingValue(Vector3d value);
+
+ SmartPtr<AbstractRangedDoubleVec3Property> m_LogScalingModel;
+ bool GetLogScalingValueAndRange(Vector3d &value, NumericValueRange<Vector3d> *range);
+ void SetLogScalingValue(Vector3d value);
+
+ typedef ConcretePropertyModel<Transformation, TransformationDomain> TransformationModel;
+ SmartPtr<TransformationModel> m_TransformationModel;
+
+ typedef ConcretePropertyModel<SimilarityMetric, SimilarityMetricDomain> SimilarityMetricModel;
+ SmartPtr<SimilarityMetricModel> m_SimilarityMetricModel;
+
+ SmartPtr<ConcreteSimpleBooleanProperty> m_UseSegmentationAsMaskModel;
+
+ SmartPtr<ConcreteSimpleDoubleProperty> m_LastMetricValueModel;
+
+ // Multi-resolution schedule - coarsest and finest levels
+ int m_CoarsestResolutionLevel, m_FinestResolutionLevel;
+ ResolutionLevelDomain m_ResolutionLevelDomain;
+
+ typedef AbstractPropertyModel<int, ResolutionLevelDomain> ResolutionLevelModel;
+ SmartPtr<ResolutionLevelModel> m_CoarsestResolutionLevelModel;
+ SmartPtr<ResolutionLevelModel> m_FinestResolutionLevelModel;
+
+ bool GetCoarsestResolutionLevelValueAndRange(int &value, ResolutionLevelDomain *domain);
+ void SetCoarsestResolutionLevelValue(int value);
+
+ bool GetFinestResolutionLevelValueAndRange(int &value, ResolutionLevelDomain *domain);
+ void SetFinestResolutionLevelValue(int value);
+
+ // Value corresponding to no layer selected
+ static const unsigned long NOID;
+
+ // The active layer for the segmentation
+ unsigned long m_MovingLayerId;
+
+ // The components of the transform that are presented to the user by this widget
+ struct TransformManualParameters
+ {
+ // The affine matrix/offset from which the parameters were generated
+ ITKMatrixType AffineMatrix;
+ ITKVectorType AffineOffset;
+
+ // Euler angles
+ Vector3d EulerAngles;
+
+ // Translation
+ Vector3d Translation;
+
+ // Scaling
+ Vector3d Scaling;
+
+ // Shearing
+ vnl_matrix_fixed<double, 3, 3> ShearingMatrix;
+
+ // Range of translation
+ NumericValueRange<Vector3d> TranslationRange;
+
+ // The unique layer ID for which this data was computed
+ unsigned long LayerID;
+
+ // Time stamp of the last update
+ itk::TimeStamp UpdateTime;
+
+ TransformManualParameters() : LayerID(NOID) {}
+ };
+
+ // The current cached manual parameters
+ TransformManualParameters m_ManualParam;
+
+ // Current center of rotation - should be initialized to the center when new image is loaded
+ Vector3ui m_RotationCenter;
+
+ // Callback for when the transform being computed by auto-registration is modified
+ void IterationCallback(const itk::Object *object, const itk::EventObject &event);
+
+ // The number of iterations per registration level
+ // TODO: make this a model
+ std::vector<int> m_IterationPyramid;
+
+ // Command used for responding to intermediate data generated by registration
+ SmartPtr<itk::Command> m_IterationCommand;
+
+ // Renderer used to plot the metric
+ SmartPtr<OptimizationProgressRenderer> m_RegistrationProgressRenderer;
+};
+
+#endif // REGISTRATIONMODEL_H
diff --git a/GUI/Model/SaveModifiedLayersModel.cxx b/GUI/Model/SaveModifiedLayersModel.cxx
index 8ea31b9..f8e1e51 100644
--- a/GUI/Model/SaveModifiedLayersModel.cxx
+++ b/GUI/Model/SaveModifiedLayersModel.cxx
@@ -125,7 +125,8 @@ void SaveModifiedLayersModel::SetCurrentItemValue(int value)
InvokeEvent(StateMachineChangeEvent());
}
-void SaveModifiedLayersModel::BuildUnsavedItemsList(std::list<ImageWrapperBase *> layers)
+void SaveModifiedLayersModel::BuildUnsavedItemsList(std::list<ImageWrapperBase *> layers,
+ bool force_exclude_workspace)
{
// Clear the list
m_UnsavedItems.clear();
@@ -173,12 +174,13 @@ void SaveModifiedLayersModel::BuildUnsavedItemsList(std::list<ImageWrapperBase *
// unloaded. Otherwise, any changes (loading/unloading) should be considered as
// part of editing the workspace, and thus we should not be prompting to save the
// workspace. Also, the workspace must already exist.
- if(flag_main_included && m_ParentModel->GetGlobalState()->GetProjectFilename().size())
+ if(!force_exclude_workspace &&
+ flag_main_included && m_ParentModel->GetGlobalState()->GetProjectFilename().size())
{
// Additional conditions are that either the project has unsaved changes, or one of
// the items proposed for saving does not have a filename yet (and so saving it would
// cause a modification to the workspace)
- if(flag_unnamed_layers | m_ParentModel->GetDriver()->IsProjectUnsaved())
+ if(flag_unnamed_layers || m_ParentModel->GetDriver()->IsProjectUnsaved())
{
SmartPtr<WorkspaceSaveableItem> item = WorkspaceSaveableItem::New();
item->Initialize(this);
@@ -197,7 +199,9 @@ void SaveModifiedLayersModel::BuildUnsavedItemsList(std::list<ImageWrapperBase *
m_CurrentItem = 0;
}
-void SaveModifiedLayersModel::Initialize(GlobalUIModel *parent, std::list<ImageWrapperBase *> layers)
+void SaveModifiedLayersModel::Initialize(GlobalUIModel *parent,
+ std::list<ImageWrapperBase *> layers,
+ bool force_exclude_workspace)
{
m_ParentModel = parent;
@@ -205,7 +209,7 @@ void SaveModifiedLayersModel::Initialize(GlobalUIModel *parent, std::list<ImageW
// it supports is meant to be modal, i.e. the user can't make changes.
// Update the list of unsaved items
- BuildUnsavedItemsList(layers);
+ BuildUnsavedItemsList(layers, force_exclude_workspace);
}
bool SaveModifiedLayersModel::CheckState(SaveModifiedLayersModel::UIState state)
diff --git a/GUI/Model/SaveModifiedLayersModel.h b/GUI/Model/SaveModifiedLayersModel.h
index 4ca9f76..775cdee 100644
--- a/GUI/Model/SaveModifiedLayersModel.h
+++ b/GUI/Model/SaveModifiedLayersModel.h
@@ -144,7 +144,9 @@ public:
irisITKObjectMacro(SaveModifiedLayersModel, AbstractModel)
- void Initialize(GlobalUIModel *parent, std::list<ImageWrapperBase *> layers);
+ void Initialize(GlobalUIModel *parent,
+ std::list<ImageWrapperBase *> layers,
+ bool force_exclude_workspace);
irisGetMacro(ParentModel, GlobalUIModel *)
@@ -198,7 +200,7 @@ protected:
void SetCurrentItemValue(int value);
// Update the list of unsaved items
- void BuildUnsavedItemsList(std::list<ImageWrapperBase *> layers);
+ void BuildUnsavedItemsList(std::list<ImageWrapperBase *> layers, bool force_exclude_workspace);
// Update the current imate
void UpdateCurrentItem();
diff --git a/GUI/Model/SliceWindowCoordinator.cxx b/GUI/Model/SliceWindowCoordinator.cxx
index d7173bb..d666839 100644
--- a/GUI/Model/SliceWindowCoordinator.cxx
+++ b/GUI/Model/SliceWindowCoordinator.cxx
@@ -50,6 +50,10 @@ SliceWindowCoordinator
this, &Self::GetCommonZoomValueAndRange, &Self::SetCommonZoomValue,
ZoomLevelUpdateEvent(), ZoomLevelUpdateEvent());
+ m_CommonZoomFactorInLogicalPixelsModel = wrapGetterSetterPairAsProperty(
+ this, &Self::GetCommonZoomInLogicalPixelsValueAndRange, &Self::SetCommonZoomInLogicalPixelsValue,
+ ZoomLevelUpdateEvent(), ZoomLevelUpdateEvent());
+
m_LinkedZoomModel = wrapGetterSetterPairAsProperty(
this, &Self::GetLinkedZoomValue, &Self::SetLinkedZoomValue,
ZoomLevelUpdateEvent(), ZoomLevelUpdateEvent());
@@ -189,16 +193,23 @@ SliceWindowCoordinator
}
void SliceWindowCoordinator
-::SetZoomPercentageInAllWindows(float x)
+::SetZoomPercentageInAllWindows(double x)
{
// x screen pixels = smallest voxel dimension
// zf = x / (smallest voxel dimension)
SetZoomLevelAllWindows(x / m_SliceModel[0]->GetSliceSpacing().min_value());
}
+void SliceWindowCoordinator::SetZoomPercentageInLogicalPixelsInAllWindows(double x)
+{
+ // x screen pixels = smallest voxel dimension
+ // zf = x / (smallest voxel dimension)
+ SetZoomLevelInLogicalPixelsAllWindows(x / m_SliceModel[0]->GetSliceSpacing().min_value());
+}
+
void
SliceWindowCoordinator
-::SetZoomFactorAllWindows(float factor)
+::SetZoomFactorAllWindows(double factor)
{
// Only if initialized
assert(m_WindowsRegistered);
@@ -220,7 +231,7 @@ SliceWindowCoordinator
void
SliceWindowCoordinator
-::SetZoomLevelAllWindows(float level)
+::SetZoomLevelAllWindows(double level)
{
// Now scale the zoom in each window
for(unsigned int i=0;i<3;i++)
@@ -235,6 +246,23 @@ SliceWindowCoordinator
}
}
+void
+SliceWindowCoordinator
+::SetZoomLevelInLogicalPixelsAllWindows(double level)
+{
+ // Now scale the zoom in each window
+ for(unsigned int i=0;i<3;i++)
+ {
+ m_SliceModel[i]->SetViewZoomInLogicalPixels(level);
+ }
+
+ // Invoke event
+ if(m_LinkedZoom)
+ {
+ InvokeEvent(ZoomLevelUpdateEvent());
+ }
+}
+
void
@@ -255,7 +283,7 @@ SliceWindowCoordinator
void
SliceWindowCoordinator
-::ZoomInOrOutInOneWindow(unsigned int window, float factor)
+::ZoomInOrOutInOneWindow(unsigned int window, double factor)
{
// Only if initialized
assert(m_WindowsRegistered);
@@ -278,7 +306,7 @@ void SliceWindowCoordinator::CenterViewOnCursorInAllWindows()
void
SliceWindowCoordinator
-::OnZoomUpdateInWindow(unsigned int irisNotUsed(window), float zoom)
+::OnZoomUpdateInWindow(unsigned int irisNotUsed(window), double zoom)
{
// Only if initialized
assert(m_WindowsRegistered);
@@ -304,7 +332,7 @@ SliceWindowCoordinator
::SetCommonZoomToSmallestWindowZoom()
{
// Compute the minimum zoom
- float minZoom = 0;
+ double minZoom = 0;
for(unsigned int i=0; i<3; i++)
{
if(i == 0 || minZoom > m_SliceModel[i]->GetViewZoom())
@@ -316,18 +344,21 @@ SliceWindowCoordinator
}
-float
-SliceWindowCoordinator
-::GetCommonZoomLevel()
+double SliceWindowCoordinator::GetCommonZoomLevel()
{
if(m_LinkedZoom && m_WindowsRegistered)
return m_SliceModel[0]->GetViewZoom();
- else return std::numeric_limits<float>::quiet_NaN();
+ else return std::numeric_limits<double>::quiet_NaN();
}
-float
-SliceWindowCoordinator
-::GetCommonOptimalFitZoomLevel()
+double SliceWindowCoordinator::GetCommonZoomLevelInLogicalPixels()
+{
+ if(m_LinkedZoom && m_WindowsRegistered)
+ return m_SliceModel[0]->GetViewZoomInLogicalPixels();
+ else return std::numeric_limits<double>::quiet_NaN();
+}
+
+double SliceWindowCoordinator::GetCommonOptimalFitZoomLevel()
{
assert(m_LinkedZoom && m_WindowsRegistered);
return m_SliceModel[0]->GetOptimalZoom();
@@ -335,12 +366,12 @@ SliceWindowCoordinator
void
SliceWindowCoordinator
-::GetZoomRange(unsigned int window, float &minZoom, float &maxZoom)
+::GetZoomRange(unsigned int window, double &minZoom, double &maxZoom)
{
assert(m_WindowsRegistered);
- maxZoom = 0.0f;
- minZoom = 0.0f;
+ maxZoom = 0.0;
+ minZoom = 0.0;
for(unsigned int i=0;i<3;i++)
{
@@ -351,26 +382,54 @@ SliceWindowCoordinator
// Maximum zoom is constrained by the requirement that at least four
// pixels are visible in at least one dimensions
- float zMax1 = 0.25 * w / m_SliceModel[i]->GetSliceSpacing()[0];
- float zMax2 = 0.25 * h / m_SliceModel[i]->GetSliceSpacing()[1];
- float zMax = zMax1 > zMax2 ? zMax1 : zMax2;
- maxZoom = (maxZoom == 0.0f || maxZoom < zMax) ? zMax : maxZoom;
+ double zMax1 = 0.25 * w / m_SliceModel[i]->GetSliceSpacing()[0];
+ double zMax2 = 0.25 * h / m_SliceModel[i]->GetSliceSpacing()[1];
+ double zMax = zMax1 > zMax2 ? zMax1 : zMax2;
+ maxZoom = (maxZoom == 0.0 || maxZoom < zMax) ? zMax : maxZoom;
// Minimum zoom is just 0.25 of the optimal zoom
- float zMin = 0.25 * m_SliceModel[i]->GetOptimalZoom();
- minZoom = (minZoom == 0.0f || minZoom > zMin) ? zMin : minZoom;
+ double zMin = 0.25 * m_SliceModel[i]->GetOptimalZoom();
+ minZoom = (minZoom == 0.0 || minZoom > zMin) ? zMin : minZoom;
}
}
}
-
-float
+void
SliceWindowCoordinator
-::ClampZoom(unsigned int window,float zoom)
+::GetZoomRangeInLogicalPixels(unsigned int window, double &minZoom, double &maxZoom)
{
assert(m_WindowsRegistered);
- float minZoom, maxZoom;
+ maxZoom = 0.0;
+ minZoom = 0.0;
+
+ for(unsigned int i=0;i<3;i++)
+ {
+ if(m_LinkedZoom || window == i)
+ {
+ double w = (double) m_SliceModel[i]->GetSizeInLogicalPixels()[0];
+ double h = (double) m_SliceModel[i]->GetSizeInLogicalPixels()[1];
+
+ // Maximum zoom is constrained by the requirement that at least four
+ // pixels are visible in at least one dimensions
+ double zMax1 = 0.25 * w / m_SliceModel[i]->GetSliceSpacing()[0];
+ double zMax2 = 0.25 * h / m_SliceModel[i]->GetSliceSpacing()[1];
+ double zMax = zMax1 > zMax2 ? zMax1 : zMax2;
+ maxZoom = (maxZoom == 0.0 || maxZoom < zMax) ? zMax : maxZoom;
+
+ // Minimum zoom is just 0.25 of the optimal zoom
+ double zMin = 0.25 * m_SliceModel[i]->GetOptimalZoom();
+ minZoom = (minZoom == 0.0 || minZoom > zMin) ? zMin : minZoom;
+ }
+ }
+}
+
+
+double SliceWindowCoordinator::ClampZoom(unsigned int window, double zoom)
+{
+ assert(m_WindowsRegistered);
+
+ double minZoom, maxZoom;
GetZoomRange(window, minZoom, maxZoom);
// Apply the clamp
@@ -402,12 +461,12 @@ bool SliceWindowCoordinator::GetCommonZoomValueAndRange(
return false;
// Get the zoom
- zoom = (double) GetCommonZoomLevel();
+ zoom = GetCommonZoomLevel();
// Get the range
if(range)
{
- float fmin, fmax;
+ double fmin, fmax;
GetZoomRange(0, fmin, fmax);
range->Minimum = fmin;
@@ -425,7 +484,41 @@ bool SliceWindowCoordinator::GetCommonZoomValueAndRange(
void SliceWindowCoordinator::SetCommonZoomValue(double zoom)
{
- this->SetZoomLevelAllWindows((float) zoom);
+ this->SetZoomLevelAllWindows(zoom);
+}
+
+bool SliceWindowCoordinator
+::GetCommonZoomInLogicalPixelsValueAndRange(double &zoom, NumericValueRange<double> *range)
+{
+ // Linked zoom required
+ if(!GetLinkedZoom() || !m_ParentModel->GetDriver()->IsMainImageLoaded())
+ return false;
+
+ // Get the zoom
+ zoom = GetCommonZoomLevelInLogicalPixels();
+
+ // Get the range
+ if(range)
+ {
+ double fmin, fmax;
+ GetZoomRangeInLogicalPixels(0, fmin, fmax);
+
+ range->Minimum = fmin;
+ range->Maximum = fmax;
+
+ // Compute a reasonable step value. This is tricky, because zoom is not
+ // really a linear variable, at high zoom levels, you want steps to be
+ // larger than at small zoom levels. So how about a step that's just on
+ // the order of one hundredth of the current level?
+ range->StepSize = CalculatePowerOfTenStepSize(0, zoom, 10);
+ }
+
+ return true;
+}
+
+void SliceWindowCoordinator::SetCommonZoomInLogicalPixelsValue(double zoom)
+{
+ this->SetZoomLevelInLogicalPixelsAllWindows(zoom);
}
bool SliceWindowCoordinator::GetLinkedZoomValue(bool &out_value)
diff --git a/GUI/Model/SliceWindowCoordinator.h b/GUI/Model/SliceWindowCoordinator.h
index ebadf1e..eacd04b 100644
--- a/GUI/Model/SliceWindowCoordinator.h
+++ b/GUI/Model/SliceWindowCoordinator.h
@@ -79,10 +79,13 @@ public:
/** Set the zoom to a fraction of the optimal zoom. This makes
* the most sense when the zoom level is linked, but can be performed
* regardless */
- void SetZoomFactorAllWindows(float factor);
+ void SetZoomFactorAllWindows(double factor);
/** Set the zoom to an absolute value in all windows */
- void SetZoomLevelAllWindows(float level);
+ void SetZoomLevelAllWindows(double level);
+
+ /** Set the zoom to an absolute value in all windows */
+ void SetZoomLevelInLogicalPixelsAllWindows(double level);
/**
This sets the zoom 'percentage'. For example, if x=1, the zoom is set
@@ -91,7 +94,13 @@ public:
if your image is isotropic, by setting x=1,2,4, etc., you can avoid
aliasing the displayed image
*/
- void SetZoomPercentageInAllWindows(float x);
+ void SetZoomPercentageInAllWindows(double x);
+
+ /**
+ * This sets zoom percentage, but using logical (Retina) rather than physical
+ * pixel units. This keeps behavior the same on Retina and non-retina screens
+ */
+ void SetZoomPercentageInLogicalPixelsInAllWindows(double x);
/** Reset the zoom in all windows to an optimal value, ie, such a zoom
* that the image fits into each of the windows. Depending on whether
@@ -105,7 +114,7 @@ public:
void ResetViewToFitInOneWindow(unsigned int window);
/** Update zoom by a specified factor in a window */
- void ZoomInOrOutInOneWindow(unsigned int window, float factor);
+ void ZoomInOrOutInOneWindow(unsigned int window, double factor);
/** Center the view on the cursor in all slice windows */
void CenterViewOnCursorInAllWindows();
@@ -113,7 +122,7 @@ public:
/** When one of the windows wants to change the zoom w.r.t. a user
* action, this class will adjust, if necessary, the zoom in the other
* windows */
- void OnZoomUpdateInWindow(unsigned int window, float zoom);
+ void OnZoomUpdateInWindow(unsigned int window, double zoom);
/** React to a resizing of the windows. This will try to maintain the current view
* depending on the state. If the zooms are in 'reset' state, this will keep
@@ -121,19 +130,28 @@ public:
void OnWindowResize();
/** Get the common zoom factor */
- float GetCommonZoomLevel();
+ double GetCommonZoomLevel();
+
+ /** Get the common zoom factor - accounting for Retina scaling */
+ double GetCommonZoomLevelInLogicalPixels();
/** Get the zoom factor that will fit all three slices optimally */
- float GetCommonOptimalFitZoomLevel();
+ double GetCommonOptimalFitZoomLevel();
/** Get the model representing the optimal zoom */
irisGetMacro(CommonZoomFactorModel, AbstractRangedDoubleProperty*)
+ /** Get the model representing the optimal zoom */
+ irisGetMacro(CommonZoomFactorInLogicalPixelsModel, AbstractRangedDoubleProperty*)
+
/** Constrain a zoom factor to reasonable limits */
- float ClampZoom(unsigned int window,float zoom);
+ double ClampZoom(unsigned int window, double zoom);
/** Get the range of zoom allowed */
- void GetZoomRange(unsigned int window, float &minZoom, float &maxZoom);
+ void GetZoomRange(unsigned int window, double &minZoom, double &maxZoom);
+
+ /** Get the range of zoom allowed */
+ void GetZoomRangeInLogicalPixels(unsigned int window, double &minZoom, double &maxZoom);
/** Get the window number n */
GenericSliceModel *GetSliceModel(unsigned int window)
@@ -174,6 +192,14 @@ protected:
NumericValueRange<double> *range);
void SetCommonZoomValue(double zoom);
+ // Child model governing linked zoom properties
+ SmartPtr<AbstractRangedDoubleProperty> m_CommonZoomFactorInLogicalPixelsModel;
+
+ // Methods that the model above wraps around
+ bool GetCommonZoomInLogicalPixelsValueAndRange(double &zoom,
+ NumericValueRange<double> *range);
+ void SetCommonZoomInLogicalPixelsValue(double zoom);
+
// Child model for the linked zoom flag
SmartPtr<AbstractSimpleBooleanProperty> m_LinkedZoomModel;
diff --git a/GUI/Model/SnakeROIModel.cxx b/GUI/Model/SnakeROIModel.cxx
index 4782600..692ac97 100644
--- a/GUI/Model/SnakeROIModel.cxx
+++ b/GUI/Model/SnakeROIModel.cxx
@@ -26,7 +26,7 @@ void SnakeROIModel::SetParent(GenericSliceModel *parent)
// Return true if the selection state has changed
SnakeROISideSelectionState
SnakeROIModel
-::ComputeSelection(Vector2f &uvSlice, Vector3f corners[])
+::ComputeSelection(Vector2d &uvSlice, Vector3d corners[])
{
// This code computes the current selection based on the mouse coordinates
// Flag indicating whether we respond to this event or not
@@ -37,12 +37,12 @@ SnakeROIModel
{
// Variables used to find the closest edge that's within delta
int iClosest = -1;
- float dToClosest = m_PixelDelta;
+ double dToClosest = m_PixelDelta;
// Search for the closest edge
for(unsigned int i=0;i<2;i++)
{
- float d = GetEdgeDistance(dir,i,uvSlice,corners);
+ double d = GetEdgeDistance(dir,i,uvSlice,corners);
if(d < dToClosest)
{
dToClosest = d;
@@ -76,10 +76,10 @@ SnakeROIModel
return h;
}
-bool SnakeROIModel::ProcessPushEvent(float x, float y)
+bool SnakeROIModel::ProcessPushEvent(double x, double y)
{
// Convert the event location into slice u,v coordinates
- Vector2f uvSlice(x, y);
+ Vector2d uvSlice(x, y);
// Record the system's corners at the time of drag start
GetSystemROICorners(m_CornerDragStart);
@@ -98,10 +98,10 @@ bool SnakeROIModel::ProcessPushEvent(float x, float y)
}
bool SnakeROIModel
-::ProcessMoveEvent(float x, float y)
+::ProcessMoveEvent(double x, double y)
{
// Convert the event location into slice u,v coordinates
- Vector2f uvSlice(x, y);
+ Vector2d uvSlice(x, y);
// Record the system's corners at the time of drag start
GetSystemROICorners(m_CornerDragStart);
@@ -117,14 +117,13 @@ bool SnakeROIModel
return true;
}
-bool SnakeROIModel::ProcessDragEvent(
- float x, float y, float xStart, float yStart, bool release)
+bool SnakeROIModel::ProcessDragEvent(double x, double y, double xStart, double yStart, bool release)
{
// Only do something if there is a highlight
if(m_Highlight.AnyHighlighted())
{
// Update the corners in response to the dragging
- UpdateCorners(Vector2f(x, y), Vector2f(xStart,yStart));
+ UpdateCorners(Vector2d(x, y), Vector2d(xStart,yStart));
// Event has been handled
return true;
@@ -140,46 +139,44 @@ const unsigned int SnakeROIModel::m_PixelDelta = 8;
void
SnakeROIModel
-::GetEdgeVertices(unsigned int direction,unsigned int index,
- Vector2f &x0,Vector2f &x1,
- const Vector3f corner[2])
+::GetEdgeVertices(unsigned int direction, unsigned int index,
+ Vector2d &x0, Vector2d &x1,
+ const Vector3d corner[])
{
x0(direction) = corner[0](direction);
x1(direction) = corner[1](direction);
x0(1-direction) = x1(1-direction) = corner[index](1-direction);
}
-float
-SnakeROIModel
-::GetEdgeDistance(unsigned int direction,
+double SnakeROIModel::GetEdgeDistance(unsigned int direction,
unsigned int index,
- const Vector2f &x,
- const Vector3f corner[2])
+ const Vector2d &x,
+ const Vector3d corner[])
{
// Compute the vertices of the edge
- Vector2f x0,x1;
+ Vector2d x0,x1;
GetEdgeVertices(direction,index,x0,x1,corner);
// Compute the squared distance between the vertices
- float l2 = (x1-x0).squared_magnitude();
- float l = sqrt(l2);
+ double l2 = (x1-x0).squared_magnitude();
+ double l = sqrt(l2);
// Compute the projection of x onto x1-x0
- float p = dot_product(x-x0,x1-x0) / sqrt(l2);
- float p2 = p*p;
+ double p = dot_product(x-x0,x1-x0) / sqrt(l2);
+ double p2 = p*p;
// Compute the squared distance to the line of the edge
- float q2 = (x-x0).squared_magnitude() - p2;
+ double q2 = (x-x0).squared_magnitude() - p2;
// Compute the total distance
- float d = sqrt(q2 + (p < 0 ? p2 : 0) + (p > l ? (p-l)*(p-l) : 0));
+ double d = sqrt(q2 + (p < 0 ? p2 : 0) + (p > l ? (p-l)*(p-l) : 0));
// Return this distance
return d;
}
void SnakeROIModel
-::GetSystemROICorners(Vector3f corner[2])
+::GetSystemROICorners(Vector3d corner[])
{
// Get the region of interest in image coordinates
GlobalState *gs = m_Parent->GetDriver()->GetGlobalState();
@@ -192,32 +189,32 @@ void SnakeROIModel
Vector3ul sz(roi.GetSize().GetSize());
// Remap to slice coordinates
- corner[0] = m_Parent->MapImageToSlice(to_float(ul));
- corner[1] = m_Parent->MapImageToSlice(to_float(ul+to_long(sz)));
+ corner[0] = m_Parent->MapImageToSlice(to_double(ul));
+ corner[1] = m_Parent->MapImageToSlice(to_double(ul+to_long(sz)));
}
void SnakeROIModel
-::UpdateCorners(const Vector2f &uvSliceNow, const Vector2f &uvSlicePress)
+::UpdateCorners(const Vector2d &uvSliceNow, const Vector2d &uvSlicePress)
{
// Compute the corners in slice coordinates
- Vector3f corner[2];
+ Vector3d corner[2];
GetSystemROICorners(corner);
// Get the current bounds and extents of the region of interest
- Vector3f xCornerImage[2] = {
+ Vector3d xCornerImage[2] = {
m_Parent->MapSliceToImage(corner[0]),
m_Parent->MapSliceToImage(corner[1])
};
- Vector3f clamp[2][2] =
+ Vector3d clamp[2][2] =
{
{
- Vector3f(0.0f,0.0f,0.0f),
- xCornerImage[1] - Vector3f(1.0f,1.0f,1.0f)
+ Vector3d(0.0,0.0,0.0),
+ xCornerImage[1] - Vector3d(1.0,1.0,1.0)
},
{
- xCornerImage[0] + Vector3f(1.0f,1.0f,1.0f),
- to_float(m_Parent->GetDriver()->GetCurrentImageData()->GetVolumeExtents())
+ xCornerImage[0] + Vector3d(1.0,1.0,1.0),
+ to_double(m_Parent->GetDriver()->GetCurrentImageData()->GetVolumeExtents())
}
};
@@ -234,10 +231,10 @@ void SnakeROIModel
m_CornerDragStart[i](1-dir) + uvSliceNow(1-dir) - uvSlicePress(1-dir);
// Map the affected vertex to image space
- Vector3f vImage = m_Parent->MapSliceToImage(corner[i]);
+ Vector3d vImage = m_Parent->MapSliceToImage(corner[i]);
// Clamp the affected vertex in image space
- Vector3f vImageClamped = vImage.clamp(clamp[i][0],clamp[i][1]);
+ Vector3d vImageClamped = vImage.clamp(clamp[i][0],clamp[i][1]);
// Map the affected vertex back into slice space
corner[i] = m_Parent->MapImageToSlice(vImageClamped);
diff --git a/GUI/Model/SnakeROIModel.h b/GUI/Model/SnakeROIModel.h
index 3c32058..c007800 100644
--- a/GUI/Model/SnakeROIModel.h
+++ b/GUI/Model/SnakeROIModel.h
@@ -78,12 +78,12 @@ public:
void SetParent(GenericSliceModel *);
- bool ProcessPushEvent(float x, float y);
+ bool ProcessPushEvent(double x, double y);
bool ProcessDragEvent(
- float x, float y, float xStart, float yStart, bool release);
+ double x, double y, double xStart, double yStart, bool release);
- bool ProcessMoveEvent(float x, float y);
+ bool ProcessMoveEvent(double x, double y);
void ProcessLeaveEvent();
void ProcessEnterEvent();
@@ -100,7 +100,7 @@ protected:
// Four vertices in the region box (correspond to the two corners
// of the 3D region of interest
- Vector3f m_CornerDragStart[2];
+ Vector3d m_CornerDragStart[2];
/**
* The four edges in the rectangle, ordered first by orientation
@@ -111,23 +111,23 @@ protected:
SnakeROISideSelectionState m_Highlight;
/** Map from system's ROI in image coordinates to 2D slice coords */
- void GetSystemROICorners(Vector3f corner[2]);
+ void GetSystemROICorners(Vector3d corner[2]);
/** Compute the slice-space vertices corresponding to an edge */
void GetEdgeVertices(unsigned int direction,
- unsigned int index,Vector2f &x0,Vector2f &x1,const Vector3f corner[2]);
+ unsigned int index,Vector2d &x0,Vector2d &x1,const Vector3d corner[2]);
/** Compute a distance to an edge */
- float GetEdgeDistance(unsigned int direction,
- unsigned int index,const Vector2f &point,const Vector3f corner[2]);
+ double GetEdgeDistance(unsigned int direction,
+ unsigned int index,const Vector2d &point,const Vector3d corner[2]);
/**
* Update the region of interest in response to the dragging or release
* operations.
*/
- void UpdateCorners(const Vector2f &xPress, const Vector2f &xDrag);
+ void UpdateCorners(const Vector2d &xPress, const Vector2d &xDrag);
- SnakeROISideSelectionState ComputeSelection(Vector2f &uvSlice, Vector3f corners[]);
+ SnakeROISideSelectionState ComputeSelection(Vector2d &uvSlice, Vector3d corners[]);
// Parent model
GenericSliceModel *m_Parent;
diff --git a/GUI/Model/SnakeROIResampleModel.cxx b/GUI/Model/SnakeROIResampleModel.cxx
index d00d5d5..013172a 100644
--- a/GUI/Model/SnakeROIResampleModel.cxx
+++ b/GUI/Model/SnakeROIResampleModel.cxx
@@ -46,7 +46,7 @@ void SnakeROIResampleModel::Reset()
{
SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue();
m_ResampleDimensions = roi.GetROI().GetSize();
- m_InterpolationModeModel->SetValue(SNAPSegmentationROISettings::TRILINEAR);
+ m_InterpolationModeModel->SetValue(TRILINEAR);
InvokeEvent(ModelUpdateEvent());
}
@@ -114,9 +114,9 @@ void SnakeROIResampleModel::ComputeCachedDomains()
// vector images. This is a problem and should be fixed
// Set up the interpolation mode map
- m_InterpolationModeDomain[SNAPSegmentationROISettings::NEAREST_NEIGHBOR] =
+ m_InterpolationModeDomain[NEAREST_NEIGHBOR] =
"Nearest neighbor (fast)";
- m_InterpolationModeDomain[SNAPSegmentationROISettings::TRILINEAR] =
+ m_InterpolationModeDomain[TRILINEAR] =
"Linear interpolation (better quality)";
// m_InterpolationModeDomain[SNAPSegmentationROISettings::TRICUBIC] =
// "Cubic interpolation (high quality)";
diff --git a/GUI/Model/SnakeROIResampleModel.h b/GUI/Model/SnakeROIResampleModel.h
index 3f64b49..5bd5d7e 100644
--- a/GUI/Model/SnakeROIResampleModel.h
+++ b/GUI/Model/SnakeROIResampleModel.h
@@ -30,9 +30,8 @@ public:
irisSimplePropertyAccessMacro(FixedAspectRatio, bool)
- typedef SNAPSegmentationROISettings::InterpolationMethod InterpolationMode;
- typedef SimpleItemSetDomain<InterpolationMode, std::string> InterpolationModeDomain;
- typedef AbstractPropertyModel<InterpolationMode, InterpolationModeDomain> AbstractInterpolationModeModel;
+ typedef SimpleItemSetDomain<InterpolationMethod, std::string> InterpolationModeDomain;
+ typedef AbstractPropertyModel<InterpolationMethod, InterpolationModeDomain> AbstractInterpolationModeModel;
irisGetMacro(InterpolationModeModel, AbstractInterpolationModeModel *)
@@ -93,7 +92,7 @@ protected:
InterpolationModeDomain m_InterpolationModeDomain;
// Model for the interpolation modes
- typedef ConcretePropertyModel<InterpolationMode, InterpolationModeDomain> ConcreteInterpolationModeModel;
+ typedef ConcretePropertyModel<InterpolationMethod, InterpolationModeDomain> ConcreteInterpolationModeModel;
SmartPtr<ConcreteInterpolationModeModel> m_InterpolationModeModel;
};
diff --git a/GUI/Model/SnakeWizardModel.cxx b/GUI/Model/SnakeWizardModel.cxx
index cd9d60a..72558b6 100644
--- a/GUI/Model/SnakeWizardModel.cxx
+++ b/GUI/Model/SnakeWizardModel.cxx
@@ -424,7 +424,7 @@ void SnakeWizardModel::SetForegroundClassColorLabelValue(LabelType value)
bool SnakeWizardModel::GetForestSizeValueAndRange(int &value, NumericValueRange<int> *range)
{
// Must have a classification engine
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
if(!rfe)
return false;
@@ -437,7 +437,7 @@ bool SnakeWizardModel::GetForestSizeValueAndRange(int &value, NumericValueRange<
void SnakeWizardModel::SetForestSizeValue(int value)
{
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
assert(rfe);
rfe->SetForestSize(value);
@@ -448,7 +448,7 @@ void SnakeWizardModel::SetForestSizeValue(int value)
bool SnakeWizardModel::GetTreeDepthValueAndRange(int &value, NumericValueRange<int> *range)
{
// Must have a classification engine
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
if(!rfe)
return false;
@@ -461,7 +461,7 @@ bool SnakeWizardModel::GetTreeDepthValueAndRange(int &value, NumericValueRange<i
void SnakeWizardModel::SetTreeDepthValue(int value)
{
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
assert(rfe);
rfe->SetTreeDepth(value);
@@ -472,12 +472,12 @@ void SnakeWizardModel::SetTreeDepthValue(int value)
bool SnakeWizardModel::GetClassifierPatchRadiusValueAndRange(int &value, NumericValueRange<int> *range)
{
// Must have a classification engine
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
if(!rfe)
return false;
// Get the value
- RFClassificationEngine::RadiusType radius = rfe->GetPatchRadius();
+ IRISApplication::RFEngine::RadiusType radius = rfe->GetPatchRadius();
value = (int) radius[0];
if(range)
@@ -488,11 +488,11 @@ bool SnakeWizardModel::GetClassifierPatchRadiusValueAndRange(int &value, Numeric
void SnakeWizardModel::SetClassifierPatchRadiusValue(int value)
{
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
assert(rfe);
assert(value >= 0);
- RFClassificationEngine::RadiusType radius;
+ IRISApplication::RFEngine::RadiusType radius;
radius.Fill((unsigned int) value);
rfe->SetPatchRadius(radius);
@@ -502,12 +502,12 @@ void SnakeWizardModel::SetClassifierPatchRadiusValue(int value)
bool SnakeWizardModel::GetClassifierBiasValueAndRange(double &value, NumericValueRange<double> *range)
{
// Must have a classification engine
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
if(!rfe)
return false;
// Must have a classifier
- RandomForestClassifier *rfc = rfe->GetClassifier();
+ IRISApplication::RFClassifier *rfc = rfe->GetClassifier();
if(!rfc)
return false;
@@ -524,10 +524,10 @@ bool SnakeWizardModel::GetClassifierBiasValueAndRange(double &value, NumericValu
void SnakeWizardModel::SetClassifierBiasValue(double value)
{
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
assert(rfe);
- RandomForestClassifier *rfc = rfe->GetClassifier();
+ IRISApplication::RFClassifier *rfc = rfe->GetClassifier();
assert(rfc);
rfc->SetBiasParameter(value);
@@ -541,7 +541,7 @@ void SnakeWizardModel::SetClassifierBiasValue(double value)
bool SnakeWizardModel::GetClassifierUseCoordinatesValue(bool &value)
{
// Must have a classification engine
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
if(!rfe)
return false;
@@ -551,7 +551,7 @@ bool SnakeWizardModel::GetClassifierUseCoordinatesValue(bool &value)
void SnakeWizardModel::SetClassifierUseCoordinatesValue(bool value)
{
- RFClassificationEngine *rfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = m_Driver->GetClassificationEngine();
assert(rfe);
rfe->SetUseCoordinateFeatures(value);
InvokeEvent(RFClassifierModifiedEvent());
@@ -562,7 +562,7 @@ bool SnakeWizardModel
ClassifierLabelForegroundMap &value, ClassifierLabelForegroundMapDomain *range)
{
// Get the classification engine
- RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfengine = m_Driver->GetClassificationEngine();
if(!rfengine || !rfengine->GetClassifier()->IsValidClassifier())
return false;
@@ -572,11 +572,11 @@ bool SnakeWizardModel
range->clear();
// Add all of the labels
- RandomForestClassifier *rfc = rfengine->GetClassifier();
- const RandomForestClassifier::WeightArray &wa = rfc->GetClassWeights();
+ IRISApplication::RFClassifier *rfc = rfengine->GetClassifier();
+ const IRISApplication::RFClassifier::WeightArray &wa = rfc->GetClassWeights();
for(size_t i = 0; i < wa.size(); i++)
{
- RandomForestClassifier::MappingType::const_iterator itl = rfc->GetClassToLabelMapping().find(i);
+ IRISApplication::RFClassifier::MappingType::const_iterator itl = rfc->GetClassToLabelMapping().find(i);
if(itl != rfc->GetClassToLabelMapping().end())
{
LabelType label = itl->second;
@@ -594,15 +594,15 @@ void SnakeWizardModel
ClassifierLabelForegroundMap value)
{
// Get the classification engine
- RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfengine = m_Driver->GetClassificationEngine();
assert(rfengine && rfengine->GetClassifier()->IsValidClassifier());
// Check if anything was actually modified - to avoid loops
bool changed = false;
// Update the weight of each class
- RandomForestClassifier *rfc = rfengine->GetClassifier();
- for(RandomForestClassifier::MappingType::const_iterator it = rfc->GetClassToLabelMapping().begin();
+ IRISApplication::RFClassifier *rfc = rfengine->GetClassifier();
+ for(IRISApplication::RFClassifier::MappingType::const_iterator it = rfc->GetClassToLabelMapping().begin();
it != rfc->GetClassToLabelMapping().end(); ++it)
{
double old_weight = rfc->GetClassWeights()[it->first];
@@ -672,11 +672,13 @@ bool SnakeWizardModel::CanGenerateSpeedVolume()
return true;
case PREPROCESS_RF:
{
- RFClassificationEngine *cfe = m_Driver->GetClassificationEngine();
- RandomForestClassifier *rfc = cfe->GetClassifier();
+ IRISApplication::RFEngine *cfe = m_Driver->GetClassificationEngine();
+ IRISApplication::RFClassifier *rfc = cfe->GetClassifier();
return rfc->IsValidClassifier();
}
}
+
+ return false;
}
ScalarImageWrapperBase *SnakeWizardModel::GetSelectedScalarLayer()
@@ -1166,10 +1168,26 @@ void SnakeWizardModel::SetPreprocessingModeValue(PreprocessingMode value)
void SnakeWizardModel::CompletePreprocessing()
{
// If we are in classification pre-segmentation mode, set the active drawing label
- // to match the foreground class - otherwise it's confusing to the user
+ // to match the foreground class(es) - otherwise it's confusing to the user
if(m_Driver->GetPreprocessingMode() == PREPROCESS_RF)
- m_Parent->GetGlobalState()->SetDrawingColorLabel(
- this->GetClassiferFirstForegroundLabel());
+ {
+ // What is the current drawing label?
+ LabelType label = m_Parent->GetGlobalState()->GetDrawingColorLabel();
+
+ // Is the current label one of the foreground classes?
+ ClassifierLabelForegroundMap fbtable = this->GetClassifierLabelForeground();
+ ClassifierLabelForegroundMap::const_iterator itLabel = fbtable.find(label);
+ if(itLabel == fbtable.end() || itLabel->second == false)
+ {
+ // No, it's not one of the foreground classes. So find the first foreground class
+ for(ClassifierLabelForegroundMap::const_iterator it = fbtable.begin(); it != fbtable.end(); ++it)
+ if(it->second)
+ {
+ m_Parent->GetGlobalState()->SetDrawingColorLabel(it->first);
+ break;
+ }
+ }
+ }
// Disconnect preview pipeline
m_Driver->EnterPreprocessingMode(PREPROCESS_NONE);
@@ -1449,9 +1467,6 @@ void SnakeWizardModel::OnEvolutionPageFinish()
// Update IRIS with SNAP images
m_Driver->UpdateIRISWithSnapImageData(NULL);
- // Set an undo point in IRIS, not SNAP!
- m_Driver->GetIRISImageData()->StoreUndoPoint("Automatic Segmentation");
-
// Return to IRIS mode
m_Driver->SetCurrentImageDataToIRIS();
m_Driver->ReleaseSNAPImageData();
@@ -1608,7 +1623,7 @@ void SnakeWizardModel::TagRFPreprocessingFilterModified()
// TODO: this is not the right way to do this! Make RandomForestClassifier an itkObject
// and an inout to the filter, so we don't have to update the filter itself!!
// THIS IS HACKY!!!
- RFClassificationEngine *ce = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *ce = m_Driver->GetClassificationEngine();
typedef SlicePreviewFilterWrapper<RFPreprocessingFilterConfigTraits>
RFPreprocessingPreviewWrapperType;
RFPreprocessingPreviewWrapperType *junk =
@@ -1782,13 +1797,13 @@ LabelType SnakeWizardModel::GetClassiferFirstForegroundLabel()
void SnakeWizardModel::TrainClassifier()
{
// Get the classification engine
- RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfengine = m_Driver->GetClassificationEngine();
// Perform the classification
rfengine->TrainClassifier();
// Create a list of utilized labels
- RandomForestClassifier *rfc = rfengine->GetClassifier();
+ IRISApplication::RFClassifier *rfc = rfengine->GetClassifier();
// Fire the appropriate event
InvokeEvent(RFClassifierModifiedEvent());
@@ -1800,7 +1815,7 @@ void SnakeWizardModel::TrainClassifier()
bool SnakeWizardModel::IsClassifierTrained()
{
// Get the classification engine
- RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine();
+ IRISApplication::RFEngine *rfengine = m_Driver->GetClassificationEngine();
return rfengine && rfengine->GetClassifier()->IsValidClassifier();
}
diff --git a/GUI/Model/SynchronizationModel.cxx b/GUI/Model/SynchronizationModel.cxx
index fa80a1a..b4e3fe4 100644
--- a/GUI/Model/SynchronizationModel.cxx
+++ b/GUI/Model/SynchronizationModel.cxx
@@ -127,7 +127,7 @@ void SynchronizationModel::OnUpdate()
// Map the cursor to NIFTI coordinates
ImageWrapperBase *iw = app->GetCurrentImageData()->GetMain();
message.cursor =
- iw->TransformVoxelIndexToNIFTICoordinates(
+ iw->TransformVoxelCIndexToNIFTICoordinates(
to_double(app->GetCursorPosition()));
}
@@ -140,7 +140,7 @@ void SynchronizationModel::OnUpdate()
if(bc_zoom)
message.zoom_level[dir] = gsm->GetViewZoom();
if(bc_pan)
- message.viewPositionRelative[dir] = gsm->GetViewPositionRelativeToCursor();
+ message.viewPositionRelative[dir] = to_float(gsm->GetViewPositionRelativeToCursor());
}
// 3D viewpoint
@@ -170,7 +170,7 @@ void SynchronizationModel::ReadIPCState()
// Map the cursor position to the image coordinates
GenericImageData *id = app->GetCurrentImageData();
Vector3d vox =
- id->GetMain()->TransformNIFTICoordinatesToVoxelIndex(message.cursor);
+ id->GetMain()->TransformNIFTICoordinatesToVoxelCIndex(message.cursor);
// Round the cursor to integer value
itk::Index<3> pos; Vector3ui vpos;
@@ -201,9 +201,9 @@ void SynchronizationModel::ReadIPCState()
if(m_SyncPanModel->GetValue()
&& gsm->IsSliceInitialized()
- && gsm->GetViewPositionRelativeToCursor() != message.viewPositionRelative[dir])
+ && to_float(gsm->GetViewPositionRelativeToCursor()) != message.viewPositionRelative[dir])
{
- gsm->SetViewPositionRelativeToCursor(message.viewPositionRelative[dir]);
+ gsm->SetViewPositionRelativeToCursor(to_double(message.viewPositionRelative[dir]));
}
}
diff --git a/GUI/Qt/Components/DICOMListingTable.cxx b/GUI/Qt/Components/DICOMListingTable.cxx
new file mode 100644
index 0000000..3b3c181
--- /dev/null
+++ b/GUI/Qt/Components/DICOMListingTable.cxx
@@ -0,0 +1,68 @@
+#include "DICOMListingTable.h"
+#include "QHeaderView"
+#include "Registry.h"
+#include "SNAPQtCommon.h"
+
+DICOMListingTable::DICOMListingTable(QWidget *parent)
+ : QTableWidget(parent)
+{
+ this->setSelectionBehavior(QAbstractItemView::SelectRows);
+ this->setSelectionMode(QAbstractItemView::SingleSelection);
+ this->setAlternatingRowColors(true);
+ this->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ this->verticalHeader()->hide();
+
+}
+
+DICOMListingTable::~DICOMListingTable()
+{
+
+}
+
+void DICOMListingTable::setData(const std::vector<Registry> ®)
+{
+ this->clear();
+ this->setSortingEnabled(false);
+
+ this->setRowCount(reg.size());
+ this->setColumnCount(4);
+ this->setHorizontalHeaderItem(0, new QTableWidgetItem("Series Number"));
+ this->setHorizontalHeaderItem(1, new QTableWidgetItem("Description"));
+ this->setHorizontalHeaderItem(2, new QTableWidgetItem("Dimensions"));
+ this->setHorizontalHeaderItem(3, new QTableWidgetItem("Number of Images"));
+
+
+ for(size_t i = 0; i < reg.size(); i++)
+ {
+ Registry r = reg[i];
+
+ // Series number
+ QTableWidgetItem *iSN = new QTableWidgetItem();
+ iSN->setData(Qt::EditRole, r["SeriesNumber"][0]);
+ iSN->setData(Qt::UserRole, from_utf8(r["SeriesId"][""]));
+ this->setItem(i, 0, iSN);
+
+ // Strings
+ this->setItem(i, 1, new QTableWidgetItem(r["SeriesDescription"][""]));
+ this->setItem(i, 2, new QTableWidgetItem(r["Dimensions"][""]));
+
+ // Number of images
+ QTableWidgetItem *iNI = new QTableWidgetItem();
+ iNI->setData(Qt::EditRole, r["NumberOfImages"][0]);
+ this->setItem(i, 3, iNI);
+ }
+
+ this->resizeColumnsToContents();
+ this->resizeRowsToContents();
+
+ // If only one sequence selected, pick it
+ if(reg.size() == 1)
+ {
+ this->selectRow(0);
+ }
+
+ // Sort by number
+ this->setSortingEnabled(true);
+ this->sortByColumn(0, Qt::AscendingOrder);
+}
+
diff --git a/GUI/Qt/Components/DICOMListingTable.h b/GUI/Qt/Components/DICOMListingTable.h
new file mode 100644
index 0000000..bc9c594
--- /dev/null
+++ b/GUI/Qt/Components/DICOMListingTable.h
@@ -0,0 +1,21 @@
+#ifndef DICOMLISTINGTABLE_H
+#define DICOMLISTINGTABLE_H
+
+#include <QWidget>
+#include <QTableWidget>
+#include <vector>
+
+class Registry;
+
+class DICOMListingTable : public QTableWidget
+{
+ Q_OBJECT
+
+public:
+ DICOMListingTable(QWidget *parent = 0);
+ ~DICOMListingTable();
+
+ void setData(const std::vector<Registry> ®);
+};
+
+#endif // DICOMLISTINGTABLE_H
diff --git a/GUI/Qt/Components/FileChooserPanelWithHistory.cxx b/GUI/Qt/Components/FileChooserPanelWithHistory.cxx
index 1d1d4fc..e47bb21 100644
--- a/GUI/Qt/Components/FileChooserPanelWithHistory.cxx
+++ b/GUI/Qt/Components/FileChooserPanelWithHistory.cxx
@@ -31,6 +31,9 @@ FileChooserPanelWithHistory::FileChooserPanelWithHistory(QWidget *parent) :
// Connect up the format selector to the filename
connect(ui->inFormat, SIGNAL(activated(QString)), this, SLOT(setActiveFormat(QString)));
+
+ // This flag should be false almost always
+ m_keepActiveFormatOnFilenameUpdate = false;
}
FileChooserPanelWithHistory::~FileChooserPanelWithHistory()
@@ -187,11 +190,21 @@ void FileChooserPanelWithHistory::initializeForOpenFile(
// Set the initial file
if(initialFile.length())
{
+ // If activeFormat was specified, we want to prevent the command from guessing
+ // the format from the filename, we want it to trust the activeFormat provided
+ // by the user instead
+ if(activeFormat.length())
+ m_keepActiveFormatOnFilenameUpdate = true;
+
this->updateFilename(initialFile);
- }
- // Update the display
- on_inFilename_textChanged(ui->inFilename->text());
+ m_keepActiveFormatOnFilenameUpdate = false;
+ }
+ else
+ {
+ // Update the display
+ on_inFilename_textChanged(ui->inFilename->text());
+ }
}
void FileChooserPanelWithHistory::initializeForSaveFile(
@@ -318,6 +331,11 @@ QString FileChooserPanelWithHistory::absoluteFilename() const
return fi2.absoluteFilePath();
}
+void FileChooserPanelWithHistory::setFilename(QString filename)
+{
+ this->updateFilename(filename);
+}
+
QString FileChooserPanelWithHistory::absoluteFilenameKeepExtension() const
{
QFileInfo fi(ui->inFilename->text());
@@ -362,7 +380,19 @@ void FileChooserPanelWithHistory::setActiveFormat(QString format)
// In open mode, we don't tweak the extension
if(m_openMode)
+ {
+ // If there was previously an error about an unspecified format,
+ // clear it because the user manually overrode by selecting a
+ // new format
+ // TODO: this is hacky!
+ if(ui->outError->text() == QString("Unable to recognize file format"))
+ {
+ ui->outError->clear();
+ }
+
+ emit activeFormatChanged(activeFormat());
return;
+ }
// Get the default new suffix
QString newSuffix = m_Filter[format].front();
@@ -387,6 +417,7 @@ void FileChooserPanelWithHistory::setActiveFormat(QString format)
// Highlight the filename
highlightFilename();
+ emit activeFormatChanged(activeFormat());
}
void FileChooserPanelWithHistory::on_btnBrowse_clicked()
@@ -434,6 +465,7 @@ void FileChooserPanelWithHistory::on_btnBrowse_clicked()
QStringList flatExtensionList;
QStringList formatList;
QString formatEntry;
+ QString defaultExtension;
bool have_empty = false;
foreach(QString format, m_Filter.keys())
{
@@ -466,7 +498,10 @@ void FileChooserPanelWithHistory::on_btnBrowse_clicked()
formatList << line;
if(m_defaultFormat == format)
+ {
formatEntry = line;
+ defaultExtension = m_Filter[format].first();
+ }
}
if(m_openMode)
@@ -479,8 +514,14 @@ void FileChooserPanelWithHistory::on_btnBrowse_clicked()
}
else
{
+#ifdef __APPLE__
+ // On MacOS, compound extensions are a problem, see Qt bug QTBUG-44227
+ dialog.setDefaultSuffix(defaultExtension);
+
+#else
dialog.setNameFilters(formatList);
dialog.selectNameFilter(formatEntry);
+#endif
}
if(dialog.exec() && dialog.selectedFiles().size())
@@ -539,12 +580,33 @@ void FileChooserPanelWithHistory::setCurrentFormatText(const QString &format)
#endif
}
+bool FileChooserPanelWithHistory::isFilenameNonAscii(const QString &text)
+{
+#ifdef WIN32
+ for(int i = 0; i < text.length(); i++)
+ {
+ if(text[i].unicode() > 127)
+ return true;
+ }
+#endif
+
+ return false;
+}
+
void FileChooserPanelWithHistory::on_inFilename_textChanged(const QString &text)
{
// The filename has changed. The first thing we do is to see if the filename has
// an extension that matches one of our supported extensions. If it does, then
// we change the active format to be that format
- QString format = guessFormat(absoluteFilenameKeepExtension());
+ QString format;
+
+ // Do we want to trust the currently set format (i.e., provided by caller when
+ // calling initialize)
+ if(m_keepActiveFormatOnFilenameUpdate)
+ format = m_defaultFormat;
+ else
+ format = guessFormat(absoluteFilenameKeepExtension());
+
if(format.length())
{
m_defaultFormat = format;
@@ -582,6 +644,8 @@ void FileChooserPanelWithHistory::on_inFilename_textChanged(const QString &text)
ui->outError->setText("The file is not readable");
else if(ui->inFormat->currentIndex() == -1 && ui->inFilename->text().length())
ui->outError->setText("Unable to recognize file format");
+ else if(isFilenameNonAscii(fiwd.absoluteFilePath()))
+ ui->outError->setText("The filename contains unsupported characters");
else
ui->outError->setText("");
@@ -612,8 +676,10 @@ void FileChooserPanelWithHistory::on_inFilename_textChanged(const QString &text)
}
// Does the file exist?
- if(fiwd.exists())
- ui->outError->setText("Existing file will be overridden!");
+ if (isFilenameNonAscii(fiwd.absoluteFilePath()))
+ ui->outError->setText("The filename contains unsupported characters");
+ else if(fiwd.exists())
+ ui->outError->setText("Existing file will be overwritten!");
}
}
diff --git a/GUI/Qt/Components/FileChooserPanelWithHistory.h b/GUI/Qt/Components/FileChooserPanelWithHistory.h
index f55c5ef..94dc33b 100644
--- a/GUI/Qt/Components/FileChooserPanelWithHistory.h
+++ b/GUI/Qt/Components/FileChooserPanelWithHistory.h
@@ -55,6 +55,9 @@ public:
// Get the filename selected
QString absoluteFilename() const;
+ // Set the absolute filename
+ void setFilename(QString filename);
+
// Get teh active format
QString activeFormat() const;
@@ -103,6 +106,10 @@ private:
bool m_directoryMode;
bool m_forceExtension;
+ // Prevent auto-determination of format when calling updateFilename, meant
+ // to be used inside of initialize calls
+ bool m_keepActiveFormatOnFilenameUpdate;
+
QString m_historyCategory;
QString m_filePattern;
QString m_workingDir;
@@ -121,6 +128,7 @@ private:
const char *m_oracleSlot;
QString guessFormat(const QString &text);
void setCurrentFormatText(const QString &format);
+ bool isFilenameNonAscii(const QString &text);
};
#endif // FILECHOOSERPANELWITHHISTORY_H
diff --git a/GUI/Qt/Components/HistoryQListModel.cxx b/GUI/Qt/Components/HistoryQListModel.cxx
index 1a523ae..ef02701 100644
--- a/GUI/Qt/Components/HistoryQListModel.cxx
+++ b/GUI/Qt/Components/HistoryQListModel.cxx
@@ -95,7 +95,7 @@ void HistoryQListItem::onTimer()
this->setIcon(QIcon(load_pixmap));
QPixmapCache::insert(key, load_pixmap);
}
- catch(itk::ExceptionObject &exc)
+ catch(itk::ExceptionObject &)
{
QPixmap dummy(128, 128);
dummy.fill(Qt::black);
diff --git a/GUI/Qt/Components/LayerInspectorRowDelegate.ui b/GUI/Qt/Components/LayerInspectorRowDelegate.ui
index bf3c640..6d8be1e 100644
--- a/GUI/Qt/Components/LayerInspectorRowDelegate.ui
+++ b/GUI/Qt/Components/LayerInspectorRowDelegate.ui
@@ -451,7 +451,7 @@ color: rgb(120, 120, 120)</string>
<normalon>:/root/icons8_pin_16.png</normalon>:/root/icons8_pin_16.png</iconset>
</property>
<property name="text">
- <string>Display as ovelay</string>
+ <string>Display as overlay</string>
</property>
<property name="toolTip">
<string>Display the layer as a semi-transparent overlay on top of other image layers</string>
diff --git a/GUI/Qt/Components/ProcessEventsITKCommand.h b/GUI/Qt/Components/ProcessEventsITKCommand.h
new file mode 100644
index 0000000..fac10de
--- /dev/null
+++ b/GUI/Qt/Components/ProcessEventsITKCommand.h
@@ -0,0 +1,41 @@
+#ifndef __ProcessEventsITKCommand_h_
+#define __ProcessEventsITKCommand_h_
+
+#include <QCoreApplication>
+#include <itkCommand.h>
+
+/**
+ * This is an ITK command that can be passed to ITK filters as an iteration or
+ * progress observer. When the command is called, it executes
+ * QCoreApplication::processEvents(), i.e., responds to user actions
+ */
+class ProcessEventsITKCommand : public itk::Command
+{
+public:
+ /** Standard class typedefs. */
+ typedef ProcessEventsITKCommand Self;
+ typedef itk::SmartPointer< Self > Pointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(ProcessEventsITKCommand, itk::Command)
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Abstract method that defines the action to be taken by the command. */
+ virtual void Execute(itk::Object *caller, const itk::EventObject & event)
+ {
+ QCoreApplication::processEvents();
+ }
+
+ /** Abstract method that defines the action to be taken by the command.
+ * This variant is expected to be used when requests comes from a
+ * const Object */
+ virtual void Execute(const itk::Object *caller, const itk::EventObject & event)
+ {
+ QCoreApplication::processEvents();
+ }
+};
+
+
+#endif // __ProcessEventsITKCommand_h_
diff --git a/GUI/Qt/Components/QtReporterDelegates.cxx b/GUI/Qt/Components/QtReporterDelegates.cxx
index 11cf94a..8128e14 100644
--- a/GUI/Qt/Components/QtReporterDelegates.cxx
+++ b/GUI/Qt/Components/QtReporterDelegates.cxx
@@ -121,6 +121,7 @@ void QtProgressReporterDelegate::SetProgressDialog(QProgressDialog *dialog)
m_Dialog->setMaximum(1000);
m_Dialog->setWindowModality(Qt::WindowModal);
m_Dialog->setLabelText("ITK-SNAP progress");
+ m_Dialog->reset();
}
#include <QDebug>
@@ -156,6 +157,14 @@ void QtSystemInfoDelegate
image->SetRegions(region);
image->Allocate();
+ GrayscaleImage::SpacingType spacing;
+ spacing.Fill(1.0);
+ image->SetSpacing(spacing);
+
+ GrayscaleImage::DirectionType direction;
+ direction.SetIdentity();
+ image->SetDirection(direction);
+
// Fill the image buffer
for(itk::ImageRegionIteratorWithIndex<GrayscaleImage> it(image, region);
!it.IsAtEnd(); ++it)
diff --git a/GUI/Qt/Components/SNAPQApplication.cxx b/GUI/Qt/Components/SNAPQApplication.cxx
new file mode 100644
index 0000000..565f9ec
--- /dev/null
+++ b/GUI/Qt/Components/SNAPQApplication.cxx
@@ -0,0 +1,115 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: Filename.cxx,v $
+ Language: C++
+ Date: $Date: 2010/10/18 11:25:44 $
+ Version: $Revision: 1.12 $
+ Copyright (c) 2011 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "MainImageWindow.h"
+#include "SNAPQtCommon.h"
+#include "SNAPQApplication.h"
+#include <QFileOpenEvent>
+#include <QUrl>
+
+SNAPQApplication
+::SNAPQApplication(int &argc, char **argv)
+ : QApplication(argc, argv)
+{
+ this->setApplicationName("ITK-SNAP");
+ this->setOrganizationName("itksnap.org");
+
+#if QT_VERSION >= 0x050000
+ // Allow @x2 pixmaps for icons for retina displays
+ this->setAttribute(Qt::AA_UseHighDpiPixmaps, true);
+
+ // System-supplied DPI screws up widget and font scaling horribly
+ this->setAttribute(Qt::AA_Use96Dpi, true);
+#endif
+
+ m_MainWindow = NULL;
+
+ // Store the command-line arguments
+ for(int i = 1; i < argc; i++)
+ m_Args.push_back(QString::fromUtf8(argv[i]));
+}
+
+void SNAPQApplication::setMainWindow(MainImageWindow *mainwin)
+{
+ m_MainWindow = mainwin;
+ m_StartupTime = QTime::currentTime();
+}
+
+bool SNAPQApplication::notify(QObject *object, QEvent *event)
+{
+ try { return QApplication::notify(object, event); }
+ catch(std::exception &exc)
+ {
+ // Crash!
+ ReportNonLethalException(NULL, exc, "Unexpected Error",
+ "ITK-SNAP has crashed due to an unexpected error");
+
+ // Exit the application
+ QApplication::exit(-1);
+
+ return false;
+ }
+}
+
+bool SNAPQApplication::event(QEvent *event)
+{
+ // Handle file drops
+ if (event->type() == QEvent::FileOpen && m_MainWindow)
+ {
+ QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
+ QString file = openEvent->url().path();
+
+ // MacOS bug - we get these open document events automatically generated
+ // from command-line parameters, and I have no idea why. To avoid this,
+ // if the event occurs at startup (within a second), we will check if
+ // the passed in URL matches the command-line arguments, and ignore it
+ // if it does
+ if(m_StartupTime.secsTo(QTime::currentTime()) < 1)
+ {
+ foreach(const QString &arg, m_Args)
+ {
+ if(arg == file)
+ return true;
+ }
+ }
+
+ // Accept the event
+ event->accept();
+
+ // Ok, we passed the check, now it's safe to actually open the file
+ m_MainWindow->raise();
+ m_MainWindow->LoadDroppedFile(file);
+ return true;
+ }
+
+ else return QApplication::event(event);
+}
+
+void SNAPQApplication::quitWithReturnCode(int rc)
+{
+ this->exit(rc);
+}
+
+
diff --git a/GUI/Qt/Components/SNAPQApplication.h b/GUI/Qt/Components/SNAPQApplication.h
new file mode 100644
index 0000000..5e0e616
--- /dev/null
+++ b/GUI/Qt/Components/SNAPQApplication.h
@@ -0,0 +1,58 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: Filename.cxx,v $
+ Language: C++
+ Date: $Date: 2010/10/18 11:25:44 $
+ Version: $Revision: 1.12 $
+ Copyright (c) 2011 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __SNAPQApplication_h_
+#define __SNAPQApplication_h_
+
+#include <QApplication>
+#include <QStringList>
+#include <QTime>
+
+class MainImageWindow;
+
+/** Class to handle exceptions in Qt callbacks */
+class SNAPQApplication : public QApplication
+{
+ Q_OBJECT
+
+public:
+ SNAPQApplication(int &argc, char **argv);
+
+ void setMainWindow(MainImageWindow *mainwin);
+
+ bool notify(QObject *object, QEvent *event);
+ virtual bool event(QEvent *event);
+
+public slots:
+
+ void quitWithReturnCode(int rc);
+
+private:
+ MainImageWindow *m_MainWindow;
+ QStringList m_Args;
+ QTime m_StartupTime;
+};
+
+#endif
diff --git a/GUI/Qt/Components/SNAPQtCommon.cxx b/GUI/Qt/Components/SNAPQtCommon.cxx
index 43a5315..adbab5d 100644
--- a/GUI/Qt/Components/SNAPQtCommon.cxx
+++ b/GUI/Qt/Components/SNAPQtCommon.cxx
@@ -420,8 +420,11 @@ QString ShowSimpleSaveDialogWithHistory(
QString file_pattern, bool force_extension,
QString init_file)
{
- return SimpleFileDialogWithHistory::showSaveDialog(
+ SimpleFileDialogWithHistory::QueryResult result =
+ SimpleFileDialogWithHistory::showSaveDialog(
parent, model, window_title, file_title, hist_category, file_pattern, force_extension,init_file);
+
+ return result.filename;
}
/** Show a generic file open dialog with a history dropdown */
@@ -430,8 +433,11 @@ QString ShowSimpleOpenDialogWithHistory(
QString window_title, QString file_title, QString file_pattern,
QString init_file)
{
- return SimpleFileDialogWithHistory::showOpenDialog(
+ SimpleFileDialogWithHistory::QueryResult result =
+ SimpleFileDialogWithHistory::showOpenDialog(
parent, model, window_title, file_title, hist_category, file_pattern, init_file);
+
+ return result.filename;
}
bool SaveImageLayer(GlobalUIModel *model, ImageWrapperBase *wrapper,
diff --git a/GUI/Qt/Components/SliceViewPanel.cxx b/GUI/Qt/Components/SliceViewPanel.cxx
index 96631fa..bb5cb32 100644
--- a/GUI/Qt/Components/SliceViewPanel.cxx
+++ b/GUI/Qt/Components/SliceViewPanel.cxx
@@ -15,6 +15,7 @@
#include "SliceWindowCoordinator.h"
#include "PolygonDrawingModel.h"
#include "AnnotationModel.h"
+#include "InteractiveRegistrationModel.h"
#include "AnnotationRenderer.h"
#include "QtWidgetActivator.h"
#include "SnakeModeRenderer.h"
@@ -24,6 +25,7 @@
#include "SliceWindowDecorationRenderer.h"
#include "LayerInspectorDialog.h"
#include "MainImageWindow.h"
+#include "RegistrationRenderer.h"
#include "SNAPQtCommon.h"
#include "QtScrollbarCoupling.h"
#include "QtSliderCoupling.h"
@@ -188,6 +190,7 @@ void SliceViewPanel::Initialize(GlobalUIModel *model, unsigned int index)
ui->imSnakeROI->SetModel(m_GlobalUI->GetSnakeROIModel(index));
ui->imPaintbrush->SetModel(m_GlobalUI->GetPaintbrushModel(index));
ui->imAnnotation->SetModel(m_GlobalUI->GetAnnotationModel(index));
+ ui->imRegistration->SetModel(m_GlobalUI->GetInteractiveRegistrationModel(index));
// ui->labelQuickList->SetModel(m_GlobalUI);
@@ -224,6 +227,9 @@ void SliceViewPanel::Initialize(GlobalUIModel *model, unsigned int index)
// Listen to annotation changes
connectITK(m_GlobalUI->GetAnnotationModel(index), ModelUpdateEvent());
+ // Listen to registration changes
+ connectITK(m_GlobalUI->GetInteractiveRegistrationModel(index), ModelUpdateEvent());
+
// Listen to all (?) events from the snake wizard as well
connectITK(m_GlobalUI->GetSnakeWizardModel(), IRISEvent());
@@ -400,6 +406,10 @@ void SliceViewPanel::OnToolbarModeChange()
ConfigureEventChain(ui->imAnnotation);
ovTiled.push_back(ui->imAnnotation->GetRenderer());
break;
+ case REGISTRATION_MODE:
+ ConfigureEventChain(ui->imRegistration);
+ ovTiled.push_back(ui->imRegistration->GetRenderer());
+ break;
case ROI_MODE:
ConfigureEventChain(ui->imSnakeROI);
ovTiled.push_back(ui->imSnakeROI->GetRenderer());
diff --git a/GUI/Qt/Components/SliceViewPanel.ui b/GUI/Qt/Components/SliceViewPanel.ui
index 17c338b..c8acdfc 100644
--- a/GUI/Qt/Components/SliceViewPanel.ui
+++ b/GUI/Qt/Components/SliceViewPanel.ui
@@ -838,6 +838,9 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="AnnotationInteractionMode" name="imAnnotation" native="true"/>
</item>
+ <item>
+ <widget class="RegistrationInteractionMode" name="imRegistration" native="true"/>
+ </item>
</layout>
</widget>
</item>
@@ -1191,6 +1194,12 @@ p, li { white-space: pre-wrap; }
<header>AnnotationInteractionMode.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>RegistrationInteractionMode</class>
+ <extends>QWidget</extends>
+ <header>RegistrationInteractionMode.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources>
<include location="../Resources/SNAPResources.qrc"/>
diff --git a/GUI/Qt/Components/ViewPanel3D.ui b/GUI/Qt/Components/ViewPanel3D.ui
index 0b0ac53..234dbd3 100644
--- a/GUI/Qt/Components/ViewPanel3D.ui
+++ b/GUI/Qt/Components/ViewPanel3D.ui
@@ -13,6 +13,11 @@
<property name="windowTitle">
<string>Form</string>
</property>
+ <property name="styleSheet">
+ <string notr="true">* {
+font-size: 11px;
+}</string>
+ </property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
@@ -41,6 +46,11 @@
</item>
<item>
<widget class="QWidget" name="toolbar" native="true">
+ <property name="styleSheet">
+ <string notr="true">QToolButton {
+ font-size:11px;
+}</string>
+ </property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>4</number>
@@ -59,6 +69,9 @@
<height>20</height>
</size>
</property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
<property name="text">
<string>update</string>
</property>
@@ -139,6 +152,9 @@
<property name="toolTip">
<string>Cancel 3D editing operation</string>
</property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
<property name="text">
<string>cancel</string>
</property>
@@ -209,9 +225,15 @@
</property>
<property name="icon">
<iconset>
- <normalon>:/root/screencapture2.gif</normalon>
+ <normalon>:/root/icons8_slr_camera_12.png</normalon>
</iconset>
</property>
+ <property name="iconSize">
+ <size>
+ <width>12</width>
+ <height>12</height>
+ </size>
+ </property>
<property name="autoRaise">
<bool>true</bool>
</property>
diff --git a/GUI/Qt/Components/ZoomInspector.cxx b/GUI/Qt/Components/ZoomInspector.cxx
index b20557f..930e03e 100644
--- a/GUI/Qt/Components/ZoomInspector.cxx
+++ b/GUI/Qt/Components/ZoomInspector.cxx
@@ -72,7 +72,7 @@ void ZoomInspector::SetModel(GlobalUIModel *model)
// Couple zoom widget to the linked zoom level
makeCoupling(ui->inZoom,
- model->GetSliceCoordinator()->GetCommonZoomFactorModel());
+ model->GetSliceCoordinator()->GetCommonZoomFactorInLogicalPixelsModel());
}
void ZoomInspector::on_chkLinkedZoom_stateChanged(int state)
@@ -82,15 +82,15 @@ void ZoomInspector::on_chkLinkedZoom_stateChanged(int state)
void ZoomInspector::on_btnZoom1_pressed()
{
- m_Model->GetSliceCoordinator()->SetZoomPercentageInAllWindows(1);
+ m_Model->GetSliceCoordinator()->SetZoomPercentageInLogicalPixelsInAllWindows(1);
}
void ZoomInspector::on_btnZoom2_pressed()
{
- m_Model->GetSliceCoordinator()->SetZoomPercentageInAllWindows(2);
+ m_Model->GetSliceCoordinator()->SetZoomPercentageInLogicalPixelsInAllWindows(2);
}
void ZoomInspector::on_btnZoom4_pressed()
{
- m_Model->GetSliceCoordinator()->SetZoomPercentageInAllWindows(4);
+ m_Model->GetSliceCoordinator()->SetZoomPercentageInLogicalPixelsInAllWindows(4);
}
diff --git a/GUI/Qt/Components/ZoomInspector.ui b/GUI/Qt/Components/ZoomInspector.ui
index 204b228..431d323 100644
--- a/GUI/Qt/Components/ZoomInspector.ui
+++ b/GUI/Qt/Components/ZoomInspector.ui
@@ -144,7 +144,7 @@ font-size: 11px;
<bool>true</bool>
</property>
<property name="styleSheet">
- <string notr="true">font-size:9pt;</string>
+ <string notr="true">font-size:9px;</string>
</property>
<property name="text">
<string> px/mm</string>
diff --git a/GUI/Qt/Coupling/QtRadioButtonCoupling.h b/GUI/Qt/Coupling/QtRadioButtonCoupling.h
index 6062d58..5003b2e 100644
--- a/GUI/Qt/Coupling/QtRadioButtonCoupling.h
+++ b/GUI/Qt/Coupling/QtRadioButtonCoupling.h
@@ -69,5 +69,16 @@ void makeRadioGroupCoupling(
makeCheckableWidgetGroupCoupling(w, buttonMap, model);
}
+/**
+ Really simple mode - you have two radio buttons and a boolean model
+ */
+inline void makeRadioGroupCoupling(
+ QAbstractButton *w_True, QAbstractButton *w_False, AbstractPropertyModel<bool> *model)
+{
+ std::map<bool, QAbstractButton *> buttonMap;
+ buttonMap[true] = w_True;
+ buttonMap[false] = w_False;
+ makeCheckableWidgetGroupCoupling(w_True->parentWidget(), buttonMap, model);
+}
#endif // QTRADIOBUTTONCOUPLING_H
diff --git a/GUI/Qt/Resources/SNAPResources.qrc b/GUI/Qt/Resources/SNAPResources.qrc
index f52d78c..e98a7fb 100644
--- a/GUI/Qt/Resources/SNAPResources.qrc
+++ b/GUI/Qt/Resources/SNAPResources.qrc
@@ -159,6 +159,8 @@
<file>icons8_palette_16.png</file>
<file>icons8_layers_16 at 2x.png</file>
<file>icons8_layers_16.png</file>
+ <file>reslice_16 at 2x.png</file>
+ <file>reslice_16.png</file>
</qresource>
<qresource prefix="/snapres">
<file>snapres/EdgeForcesExample.png</file>
diff --git a/GUI/Qt/Resources/reslice_16.png b/GUI/Qt/Resources/reslice_16.png
new file mode 100644
index 0000000..e210c6e
Binary files /dev/null and b/GUI/Qt/Resources/reslice_16.png differ
diff --git a/GUI/Qt/Resources/reslice_16 at 2x.png b/GUI/Qt/Resources/reslice_16 at 2x.png
new file mode 100644
index 0000000..39ce241
Binary files /dev/null and b/GUI/Qt/Resources/reslice_16 at 2x.png differ
diff --git a/GUI/Qt/View/CrosshairsInteractionMode.cxx b/GUI/Qt/View/CrosshairsInteractionMode.cxx
index f1e30b2..8981740 100644
--- a/GUI/Qt/View/CrosshairsInteractionMode.cxx
+++ b/GUI/Qt/View/CrosshairsInteractionMode.cxx
@@ -108,7 +108,7 @@ void CrosshairsInteractionMode::mousePressEvent(QMouseEvent *ev)
// Use model to envoke event
if(btn == m_BtnCursor)
{
- m_Model->UpdateCursor(Vector2f(m_XSpace[0], m_XSpace[1]));
+ m_Model->UpdateCursor(Vector2d(m_XSpace[0], m_XSpace[1]));
}
else if(btn == m_BtnZoom)
{
@@ -134,7 +134,7 @@ void CrosshairsInteractionMode::mouseMoveEvent(QMouseEvent *ev)
if(m_LastPressEmulatedButton == m_BtnCursor)
{
- m_Model->UpdateCursor(Vector2f(m_XSpace[0], m_XSpace[1]));
+ m_Model->UpdateCursor(Vector2d(m_XSpace[0], m_XSpace[1]));
}
else if(m_LastPressEmulatedButton == m_BtnZoom)
{
@@ -143,7 +143,7 @@ void CrosshairsInteractionMode::mouseMoveEvent(QMouseEvent *ev)
}
else if(m_LastPressEmulatedButton == m_BtnPan)
{
- m_Model->ProcessPanGesture(Vector2f(dx(0), dx(1)));
+ m_Model->ProcessPanGesture(Vector2d(dx(0), dx(1)));
}
// Eat this event
@@ -165,7 +165,7 @@ void CrosshairsInteractionMode::mouseReleaseEvent(QMouseEvent *ev)
{
if(btn == m_BtnCursor)
{
- m_Model->UpdateCursor(Vector2f(m_XSpace[0], m_XSpace[1]));
+ m_Model->UpdateCursor(Vector2d(m_XSpace[0], m_XSpace[1]));
}
else if(btn == m_BtnZoom)
{
@@ -197,58 +197,17 @@ bool CrosshairsInteractionMode::gestureEvent(QGestureEvent *ev)
}
else if(pinch->state() == Qt::GestureUpdated)
{
- m_Model->ProcessZoomGesture(pinch->scaleFactor());
+ m_Model->ProcessZoomGesture(pinch->totalScaleFactor());
}
else if(pinch->state() == Qt::GestureFinished)
{
- m_Model->ProcessZoomGesture(pinch->scaleFactor());
+ m_Model->ProcessZoomGesture(pinch->totalScaleFactor());
m_Model->EndZoom();
}
ev->accept();
return true;
}
- /*
-
- // Get the pan event
- if(QPanGesture *pan =
- static_cast<QPanGesture *>(ev->gesture(Qt::PanGesture)))
- {
- if(pan->state() == Qt::GestureStarted)
- {
- m_Model->BeginPan();
- }
- else if(pan->state() == Qt::GestureUpdated)
- {
- m_Model->ProcessPanGesture(Vector2f(pan->delta().x(), pan->delta().y()));
- }
- else if(pan->state() == Qt::GestureFinished)
- {
- m_Model->ProcessPanGesture(Vector2f(pan->delta().x(), pan->delta().y()));
- m_Model->EndPan();
- }
- ev->accept();
- return true;
- }
-
- // Get the swipe event
- if(QSwipeGesture *swipe =
- static_cast<QSwipeGesture *>(ev->gesture(Qt::SwipeGesture)))
- {
- if(swipe->verticalDirection() == QSwipeGesture::Down)
- {
- m_Model->ProcessScrollGesture(1);
- }
- else if(swipe->verticalDirection() == QSwipeGesture::Up)
- {
- m_Model->ProcessScrollGesture(-1);
- }
- ev->accept();
- return true;
- }
- return false;
-
- */
else return false;
}
@@ -314,7 +273,7 @@ void CrosshairsInteractionMode::wheelEvent(QWheelEvent *event)
MultiChannelDisplayMode mode = dpolicy->GetDisplayMode();
// Mode must be single component
- if(!mode.UseRGB && mode.SelectedScalarRep == SCALAR_REP_COMPONENT)
+ if(mode.IsSingleComponent())
{
static double delta_accum = 0.0;
diff --git a/GUI/Qt/View/GenericView3D.cxx b/GUI/Qt/View/GenericView3D.cxx
index c7aff4d..8e490b3 100644
--- a/GUI/Qt/View/GenericView3D.cxx
+++ b/GUI/Qt/View/GenericView3D.cxx
@@ -23,7 +23,7 @@ class CursorPlacementInteractorStyle : public vtkInteractorStyleTrackballCamera
{
public:
static CursorPlacementInteractorStyle* New();
- vtkTypeRevisionMacro(CursorPlacementInteractorStyle, vtkInteractorStyleTrackballCamera)
+ vtkTypeMacro(CursorPlacementInteractorStyle, vtkInteractorStyleTrackballCamera)
irisGetSetMacro(Model, Generic3DModel *)
@@ -46,7 +46,7 @@ class SpraycanInteractorStyle : public vtkInteractorStyleTrackballCamera
{
public:
static SpraycanInteractorStyle* New();
- vtkTypeRevisionMacro(SpraycanInteractorStyle, vtkInteractorStyleTrackballCamera)
+ vtkTypeMacro(SpraycanInteractorStyle, vtkInteractorStyleTrackballCamera)
irisGetSetMacro(Model, Generic3DModel *)
@@ -99,7 +99,7 @@ class ScalpelInteractorStyle : public vtkInteractorStyleTrackballCamera
{
public:
static ScalpelInteractorStyle* New();
- vtkTypeRevisionMacro(ScalpelInteractorStyle, vtkInteractorStyleTrackballCamera)
+ vtkTypeMacro(ScalpelInteractorStyle, vtkInteractorStyleTrackballCamera)
irisGetSetMacro(Model, Generic3DModel *)
@@ -189,13 +189,10 @@ protected:
-vtkCxxRevisionMacro(CursorPlacementInteractorStyle, "$Revision: 1.1 $")
vtkStandardNewMacro(CursorPlacementInteractorStyle)
-vtkCxxRevisionMacro(SpraycanInteractorStyle, "$Revision: 1.1 $")
vtkStandardNewMacro(SpraycanInteractorStyle)
-vtkCxxRevisionMacro(ScalpelInteractorStyle, "$Revision: 1.1 $")
vtkStandardNewMacro(ScalpelInteractorStyle)
diff --git a/GUI/Qt/View/PaintbrushInteractionMode.cxx b/GUI/Qt/View/PaintbrushInteractionMode.cxx
index 425aaf4..2af5005 100644
--- a/GUI/Qt/View/PaintbrushInteractionMode.cxx
+++ b/GUI/Qt/View/PaintbrushInteractionMode.cxx
@@ -33,7 +33,7 @@ void PaintbrushInteractionMode::mousePressEvent(QMouseEvent *ev)
bool isright = (ev->button() == Qt::RightButton);
if(isleft || isright)
{
- if(m_Model->ProcessPushEvent(to_float(m_XSlice),this->m_LastPressLayoutCell, isright))
+ if(m_Model->ProcessPushEvent(m_XSlice,this->m_LastPressLayoutCell, isright))
ev->accept();
}
}
@@ -45,7 +45,7 @@ void PaintbrushInteractionMode::mouseMoveEvent(QMouseEvent *ev)
if(this->isDragging())
{
if(m_Model->ProcessDragEvent(
- to_float(m_XSlice), to_float(m_LastPressXSlice),
+ m_XSlice, m_LastPressXSlice,
GetNumberOfPixelsMoved(ev), false))
{
ev->accept();
@@ -53,7 +53,7 @@ void PaintbrushInteractionMode::mouseMoveEvent(QMouseEvent *ev)
}
else if(this->isHovering())
{
- if(m_Model->ProcessMouseMoveEvent(to_float(m_XSlice)))
+ if(m_Model->ProcessMouseMoveEvent(m_XSlice))
ev->accept();
}
}
@@ -61,7 +61,7 @@ void PaintbrushInteractionMode::mouseMoveEvent(QMouseEvent *ev)
void PaintbrushInteractionMode::mouseReleaseEvent(QMouseEvent *ev)
{
if(m_Model->ProcessDragEvent(
- to_float(m_XSlice), to_float(m_LastPressXSlice),
+ m_XSlice, m_LastPressXSlice,
GetNumberOfPixelsMoved(ev), true))
{
ev->accept();
diff --git a/GUI/Qt/View/QtAbstractOpenGLBox.cxx b/GUI/Qt/View/QtAbstractOpenGLBox.cxx
index 7bdc867..086e656 100644
--- a/GUI/Qt/View/QtAbstractOpenGLBox.cxx
+++ b/GUI/Qt/View/QtAbstractOpenGLBox.cxx
@@ -76,6 +76,9 @@ QtAbstractOpenGLBox
void QtAbstractOpenGLBox::paintGL()
{
+ if(!GetRenderer())
+ return;
+
// Update the renderer. This will cause the renderer to update itself
// based on any events that it has received upstream.
GetRenderer()->Update();
@@ -114,17 +117,23 @@ void QtAbstractOpenGLBox::paintGL()
void QtAbstractOpenGLBox::resizeGL(int w, int h)
{
- int wp = (int) this->size().width() * this->devicePixelRatio();
- int hp = (int) this->size().height() * this->devicePixelRatio();
+ if(GetRenderer())
+ {
+ int wp = (int) this->size().width() * this->devicePixelRatio();
+ int hp = (int) this->size().height() * this->devicePixelRatio();
- GetRenderer()->Update();
- GetRenderer()->resizeGL(wp, hp, this->devicePixelRatio());
+ GetRenderer()->Update();
+ GetRenderer()->resizeGL(wp, hp, this->devicePixelRatio());
+ }
}
void QtAbstractOpenGLBox::initializeGL()
{
- GetRenderer()->Update();
- GetRenderer()->initializeGL();
+ if(GetRenderer())
+ {
+ GetRenderer()->Update();
+ GetRenderer()->initializeGL();
+ }
}
/*
diff --git a/GUI/Qt/View/QtVTKRenderWindowBox.cxx b/GUI/Qt/View/QtVTKRenderWindowBox.cxx
index 2306616..0e6aa60 100644
--- a/GUI/Qt/View/QtVTKRenderWindowBox.cxx
+++ b/GUI/Qt/View/QtVTKRenderWindowBox.cxx
@@ -2,6 +2,7 @@
#include "AbstractVTKRenderer.h"
#include "QtVTKInteractionDelegateWidget.h"
#include "vtkRenderWindow.h"
+#include "vtkRenderWindowInteractor.h"
#include "vtkCommand.h"
#include "QtReporterDelegates.h"
@@ -28,17 +29,17 @@ void QtVTKRenderWindowBox::SetRenderer(AbstractRenderer *renderer)
// Hook up the interaction delegate
m_InteractionDelegate->SetVTKInteractor(renvtk->GetRenderWindowInteractor());
- // Create a size reporter
- }
+ // Disable rendering directly in the interactor
+ renvtk->GetRenderWindowInteractor()->EnableRenderOff();
- // Hook up context-related events (is this needed?)
- renvtk->GetRenderWindow()->AddObserver(
- vtkCommand::WindowMakeCurrentEvent,
- this, &QtVTKRenderWindowBox::RendererCallback);
+ // Disable buffer swapping in render window
+ renvtk->GetRenderWindow()->SwapBuffersOn();
- renvtk->GetRenderWindow()->AddObserver(
- vtkCommand::WindowIsCurrentEvent,
- this, &QtVTKRenderWindowBox::RendererCallback);
+ // Create a size reporter
+ renvtk->GetRenderWindowInteractor()->AddObserver(
+ vtkCommand::RenderEvent,
+ this, &QtVTKRenderWindowBox::RendererCallback);
+ }
// Call parent method
QtSimpleOpenGLBox::SetRenderer(renderer);
@@ -50,14 +51,9 @@ QtVTKRenderWindowBox
::RendererCallback(
vtkObject *src, unsigned long event, void *data)
{
- if(event == vtkCommand::WindowMakeCurrentEvent)
- {
- this->makeCurrent();
- }
- else if(event == vtkCommand::WindowIsCurrentEvent)
+ if(event == vtkCommand::RenderEvent)
{
- bool *result = static_cast<bool *>(data);
- *result = QOpenGLContext::currentContext() == this->context();
+ this->update();
}
}
diff --git a/GUI/Qt/View/RegistrationInteractionMode.cxx b/GUI/Qt/View/RegistrationInteractionMode.cxx
new file mode 100644
index 0000000..72319fa
--- /dev/null
+++ b/GUI/Qt/View/RegistrationInteractionMode.cxx
@@ -0,0 +1,98 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: Filename.cxx,v $
+ Language: C++
+ Date: $Date: 2010/10/18 11:25:44 $
+ Version: $Revision: 1.12 $
+ Copyright (c) 2011 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "RegistrationInteractionMode.h"
+#include "RegistrationRenderer.h"
+#include "InteractiveRegistrationModel.h"
+#include "GenericSliceView.h"
+
+RegistrationInteractionMode::RegistrationInteractionMode(GenericSliceView *parent)
+ : SliceWindowInteractionDelegateWidget(parent),
+ m_Model(NULL)
+{
+ // Create the renderer
+ m_Renderer = RegistrationRenderer::New();
+ m_Renderer->SetParentRenderer(
+ static_cast<GenericSliceRenderer *>(parent->GetRenderer()));
+}
+
+RegistrationInteractionMode::~RegistrationInteractionMode()
+{
+
+}
+
+void RegistrationInteractionMode::SetModel(InteractiveRegistrationModel *model)
+{
+ m_Model = model;
+ m_Renderer->SetModel(model);
+ this->SetParentModel(model->GetParent());
+
+ connectITK(m_Model, StateMachineChangeEvent());
+ connectITK(m_Model, ModelUpdateEvent());
+}
+
+
+void RegistrationInteractionMode::onModelUpdate(const EventBucket &bucket)
+{
+ this->update();
+}
+
+
+void RegistrationInteractionMode::mousePressEvent(QMouseEvent *ev)
+{
+ if(m_Model->ProcessPushEvent(m_XSlice))
+ ev->accept();
+}
+
+void RegistrationInteractionMode::mouseMoveEvent(QMouseEvent *ev)
+{
+ ev->ignore();
+ if(this->IsMouseOverFullLayer())
+ {
+ if(this->isDragging())
+ {
+ if(m_Model->ProcessDragEvent(m_XSlice, m_LastPressXSlice))
+ ev->accept();
+ }
+ else
+ {
+ if(m_Model->ProcessMouseMoveEvent(m_XSlice, this->m_HoverOverLayer->GetUniqueId()))
+ ev->accept();
+ }
+ }
+}
+
+void RegistrationInteractionMode::mouseReleaseEvent(QMouseEvent *ev)
+{
+ if(this->isDragging())
+ {
+ if(m_Model->ProcessReleaseEvent(m_XSlice, m_LastPressXSlice))
+ ev->accept();
+ }
+}
+
+
+
+
diff --git a/GUI/Qt/View/RegistrationInteractionMode.h b/GUI/Qt/View/RegistrationInteractionMode.h
new file mode 100644
index 0000000..6b36695
--- /dev/null
+++ b/GUI/Qt/View/RegistrationInteractionMode.h
@@ -0,0 +1,65 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: Filename.cxx,v $
+ Language: C++
+ Date: $Date: 2010/10/18 11:25:44 $
+ Version: $Revision: 1.12 $
+ Copyright (c) 2011 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef REGISTRATIONINTERACTIONMODE_H
+#define REGISTRATIONINTERACTIONMODE_H
+
+#include "SliceWindowInteractionDelegateWidget.h"
+#include "SNAPCommon.h"
+
+class GenericSliceModel;
+class InteractiveRegistrationModel;
+class GenericSliceView;
+class RegistrationRenderer;
+
+class RegistrationInteractionMode : public SliceWindowInteractionDelegateWidget
+{
+ Q_OBJECT
+
+public:
+ explicit RegistrationInteractionMode(GenericSliceView *parent = NULL);
+ ~RegistrationInteractionMode();
+
+ void SetModel(InteractiveRegistrationModel *model);
+
+ irisGetMacro(Renderer, RegistrationRenderer *)
+
+ void mousePressEvent(QMouseEvent *ev);
+ void mouseMoveEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+
+signals:
+
+public slots:
+
+ void onModelUpdate(const EventBucket &bucket);
+
+protected:
+
+ InteractiveRegistrationModel *m_Model;
+ SmartPtr<RegistrationRenderer> m_Renderer;
+};
+
+#endif // REGISTRATIONINTERACTIONMODE_H
diff --git a/GUI/Qt/View/SliceWindowInteractionDelegateWidget.cxx b/GUI/Qt/View/SliceWindowInteractionDelegateWidget.cxx
index d971e34..46a9655 100644
--- a/GUI/Qt/View/SliceWindowInteractionDelegateWidget.cxx
+++ b/GUI/Qt/View/SliceWindowInteractionDelegateWidget.cxx
@@ -30,8 +30,8 @@ void SliceWindowInteractionDelegateWidget::preprocessEvent(QEvent *ev)
ev->type() == QEvent::ContextMenu)
{
// Compute the spatial location of the event
- m_XSlice = to_double(m_ParentModel->MapWindowToSlice(
- to_float(Vector2d(m_XSpace.extract(2)))));
+ m_XSlice = m_ParentModel->MapWindowToSlice(
+ Vector2d(m_XSpace.extract(2)));
// Determine whether the mouse is over a layer, and if so what layer it is, and
// whether the layer is shown as a thumbnail or not
diff --git a/GUI/Qt/View/TestOpenGLDialog.h b/GUI/Qt/View/TestOpenGLDialog.h
new file mode 100644
index 0000000..34c68f1
--- /dev/null
+++ b/GUI/Qt/View/TestOpenGLDialog.h
@@ -0,0 +1,246 @@
+#ifndef _TestOpenGLDialog_
+#define _TestOpenGLDialog_
+
+#include <QtVTKRenderWindowBox.h>
+#include <AbstractVTKRenderer.h>
+#include <QApplication>
+#include <QMouseEvent>
+#include <QDebug>
+
+#include <vtkSphereSource.h>
+#include <vtkPolyData.h>
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkRenderWindow.h>
+#include <vtkRenderer.h>
+#include <vtkDiskSource.h>
+#include <vtkTextActor.h>
+#include <vtkTextProperty.h>
+#include <vtkGenericOpenGLRenderWindow.h>
+#include <vtkRenderWindowInteractor.h>
+#include <vtkInteractorStyleTrackballCamera.h>
+#include <vtkCommand.h>
+
+class SillyRenderer : public AbstractVTKRenderer
+{
+public:
+ irisITKObjectMacro(SillyRenderer, AbstractVTKRenderer)
+
+protected:
+ SillyRenderer()
+ {
+ // Create a sphere
+ vtkSmartPointer<vtkSphereSource> sphereSource =
+ vtkSmartPointer<vtkSphereSource>::New();
+ sphereSource->SetCenter(0.0, 0.0, 0.0);
+ sphereSource->SetRadius(5.0);
+
+ vtkSmartPointer<vtkPolyDataMapper> mapper =
+ vtkSmartPointer<vtkPolyDataMapper>::New();
+ mapper->SetInputConnection(sphereSource->GetOutputPort());
+
+ vtkSmartPointer<vtkActor> actor =
+ vtkSmartPointer<vtkActor>::New();
+ actor->SetMapper(mapper);
+
+ vtkSmartPointer<vtkDiskSource> diskSource =
+ vtkSmartPointer<vtkDiskSource>::New();
+
+ // Create a mapper and actor.
+ vtkSmartPointer<vtkPolyDataMapper> mapper2 =
+ vtkSmartPointer<vtkPolyDataMapper>::New();
+ mapper2->SetInputConnection(diskSource->GetOutputPort());
+
+ vtkSmartPointer<vtkActor> actor2 =
+ vtkSmartPointer<vtkActor>::New();
+ actor2->SetMapper(mapper2);
+
+ // Setup the text and add it to the renderer
+ vtkSmartPointer<vtkTextActor> textActor =
+ vtkSmartPointer<vtkTextActor>::New();
+ textActor->SetInput ( "Hello world" );
+ textActor->SetPosition2 ( 10, 40 );
+ textActor->GetTextProperty()->SetFontSize ( 24 );
+ textActor->GetTextProperty()->SetColor ( 1.0, 0.0, 0.0 );
+ m_Renderer->AddActor2D ( textActor );
+
+
+ // m_Renderer->AddActor(actor);
+ m_Renderer->AddActor(actor2);
+ m_Renderer->SetBackground(.3, .6, .3); // Background color green
+ }
+
+ virtual ~SillyRenderer() {}
+
+ friend class TestOpenGLDialog;
+};
+
+
+class TestOpenGLDialog : public QOpenGLWidget
+{
+ Q_OBJECT
+
+public:
+ explicit TestOpenGLDialog(QWidget *w = NULL)
+ : QOpenGLWidget(w)
+ {
+ m_Silly = SillyRenderer::New();
+ m_RenWin = vtkGenericOpenGLRenderWindow::New();
+ m_RenWin->AddRenderer(m_Silly->m_Renderer);
+ m_RenWin->SwapBuffersOff();
+
+ m_Interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
+ m_Interactor->SetRenderWindow(m_RenWin);
+
+ vtkSmartPointer<vtkInteractorObserver> is
+ = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
+
+ m_Interactor->SetInteractorStyle(is);
+ m_Interactor->Initialize();
+ m_Interactor->Enable();
+ m_Interactor->EnableRenderOff();
+
+
+ // Hook up context-related events (is this needed?)
+ m_RenWin->AddObserver(
+ vtkCommand::WindowMakeCurrentEvent,
+ this, &TestOpenGLDialog::RendererCallback);
+
+ m_RenWin->AddObserver(
+ vtkCommand::WindowIsCurrentEvent,
+ this, &TestOpenGLDialog::RendererCallback);
+
+ m_RenWin->AddObserver(
+ vtkCommand::WindowFrameEvent,
+ this, &TestOpenGLDialog::RendererCallback);
+
+ m_Interactor->AddObserver(
+ vtkCommand::RenderEvent,
+ this, &TestOpenGLDialog::RendererCallback);
+ }
+
+void RendererCallback(
+ vtkObject *src, unsigned long event, void *data)
+{
+ /*
+ if(event == vtkCommand::WindowMakeCurrentEvent)
+ {
+ std::cout << 1 << std::endl;
+ this->makeCurrent();
+ }
+ else if(event == vtkCommand::WindowIsCurrentEvent)
+ {
+ std::cout << 2 << std::endl;
+ bool *result = static_cast<bool *>(data);
+ *result = QOpenGLContext::currentContext() == this->context();
+ }
+ else if(event == vtkCommand::WindowFrameEvent)
+ {
+ std::cout << 3 << std::endl;
+ // if(m_RenWin->GetSwapBuffers())
+ this->context()->swapBuffers(this->context()->surface());
+ }
+ else */
+ if(event == vtkCommand::RenderEvent)
+ {
+ std::cout << "Received Render Event" << std::endl;
+ this->update();
+ }
+}
+
+ virtual void paintGL()
+ {
+ // Clear the screen
+ glClearColor(1.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ std::cout << "Paint Begin" << std::endl;
+ m_RenWin->Render();
+ std::cout << "Paint End" << std::endl;
+ }
+
+#if QT_VERSION < 0x050000
+ virtual int devicePixelRatio()
+ {
+ return 1;
+ }
+#endif
+
+ virtual void resizeGL(int w, int h)
+ {
+ int dpr = this->devicePixelRatio();
+ m_RenWin->SetSize(w * dpr, h * dpr);
+
+ qDebug() << this->format();
+ }
+
+ virtual void initializeGL()
+ {
+ // Is this what we should be calling?
+ m_RenWin->OpenGLInit();
+ }
+
+void SetVTKEventState(QMouseEvent *ev)
+{
+ Qt::KeyboardModifiers km = QApplication::keyboardModifiers();
+
+ // Account for Retina displays
+ int x = ev->pos().x() * this->devicePixelRatio();
+ int y = ev->pos().y() * this->devicePixelRatio();
+
+ m_Interactor->SetEventInformationFlipY(
+ x, y,
+ km.testFlag(Qt::ControlModifier),
+ km.testFlag(Qt::ShiftModifier));
+}
+void mousePressEvent(QMouseEvent *ev)
+{
+ // Set the position information
+ SetVTKEventState(ev);
+
+ // Fire appropriate event
+ if(ev->button() == Qt::LeftButton)
+ m_Interactor->LeftButtonPressEvent();
+ else if(ev->button() == Qt::RightButton)
+ m_Interactor->RightButtonPressEvent();
+ else if(ev->button() == Qt::MiddleButton)
+ m_Interactor->MiddleButtonPressEvent();
+
+ // this->update();
+}
+
+void mouseReleaseEvent(QMouseEvent *ev)
+{
+ // Set the position information
+ SetVTKEventState(ev);
+
+ // Fire appropriate event
+ if(ev->button() == Qt::LeftButton)
+ m_Interactor->LeftButtonReleaseEvent();
+ else if(ev->button() == Qt::RightButton)
+ m_Interactor->RightButtonReleaseEvent();
+ else if(ev->button() == Qt::MiddleButton)
+ m_Interactor->MiddleButtonReleaseEvent();
+
+ // this->update();
+}
+
+void mouseMoveEvent(QMouseEvent *ev)
+{
+ // Set the position information
+ SetVTKEventState(ev);
+ m_Interactor->MouseMoveEvent();
+
+ // this->update();
+}
+
+ virtual ~TestOpenGLDialog() {}
+
+private:
+ SillyRenderer::Pointer m_Silly;
+ vtkSmartPointer<vtkGenericOpenGLRenderWindow> m_RenWin;
+ vtkSmartPointer<vtkRenderWindowInteractor> m_Interactor;
+};
+
+#endif
diff --git a/GUI/Qt/Windows/DropActionDialog.cxx b/GUI/Qt/Windows/DropActionDialog.cxx
index b4710c3..3039cb3 100644
--- a/GUI/Qt/Windows/DropActionDialog.cxx
+++ b/GUI/Qt/Windows/DropActionDialog.cxx
@@ -11,6 +11,8 @@
#include "MainImageWindow.h"
#include "SaveModifiedLayersDialog.h"
#include "IRISImageData.h"
+#include "GuidedNativeImageIO.h"
+#include <QTimer>
DropActionDialog::DropActionDialog(QWidget *parent) :
QDialog(parent),
@@ -38,6 +40,12 @@ void DropActionDialog::SetModel(GlobalUIModel *model)
m_Model = model;
}
+void DropActionDialog::LoadMainImage(QString name)
+{
+ this->SetDroppedFilename(name);
+ this->on_btnLoadMain_clicked();
+}
+
void DropActionDialog::on_btnLoadMain_clicked()
{
// Prompt for unsaved changes before replacing the main image
@@ -86,22 +94,73 @@ void DropActionDialog::on_btnLoadNew_clicked()
}
}
+#include "ImageIOWizardModel.h"
+#include "ImageIOWizard.h"
+
void DropActionDialog::LoadCommon(AbstractLoadImageDelegate *delegate)
{
+ // File being loaded
std::string file = to_utf8(ui->outFilename->text());
- QtCursorOverride c(Qt::WaitCursor);
- try
+
+ // We need to handle the special case when the filename is a DICOM file or
+ // a folder containing DICOM files. In this case, instead of just directly
+ // opening the file, we use the wizard.
+
+ // Load the settings associated with this file
+ Registry regAssoc;
+ m_Model->GetDriver()->GetSystemInterface()
+ ->FindRegistryAssociatedWithFile(file.c_str(), regAssoc);
+
+ // Get the folder dealing with grey image properties
+ Registry &ioHints = regAssoc.Folder("Files.Grey");
+
+ // Is this a file with a known format?
+ GuidedNativeImageIO::FileFormat fmt = GuidedNativeImageIO::GetFileFormat(ioHints);
+
+ // If not, peek at the header to see if it's DICOM or unknown
+ if(fmt == GuidedNativeImageIO::FORMAT_COUNT)
+ fmt = GuidedNativeImageIO::GuessFormatForFileName(file, true);
+
+ // If this has been determined to be a DICOM directory, let's go to the DICOM page
+ if(fmt == GuidedNativeImageIO::FORMAT_DICOM_DIR || fmt == GuidedNativeImageIO::FORMAT_COUNT)
{
- IRISWarningList warnings;
- m_Model->GetDriver()->LoadImageViaDelegate(file.c_str(), delegate, warnings);
+ // Create the wizard model
+ SmartPtr<ImageIOWizardModel> model = ImageIOWizardModel::New();
+ model->InitializeForLoad(m_Model, delegate);
+ model->SetSuggestedFilename(file);
+ model->SetSuggestedFormat(fmt);
+
+ // Execute the IO wizard
+ ImageIOWizard wiz(this);
+ wiz.SetModel(model);
+
+ // For DICOM we can move ahead to the second ppage. We do this using a singleshot
+ // timer
+ if(fmt == GuidedNativeImageIO::FORMAT_DICOM_DIR)
+ {
+ QTimer::singleShot(0, &wiz, SLOT(next()));
+ }
+
this->accept();
+ wiz.exec();
}
- catch(exception &exc)
+ else
{
- QMessageBox b(this);
- b.setText(QString("Failed to load image %1").arg(ui->outFilename->text()));
- b.setDetailedText(exc.what());
- b.setIcon(QMessageBox::Critical);
- b.exec();
- }
+ // Load without the wizard
+ QtCursorOverride c(Qt::WaitCursor);
+ try
+ {
+ IRISWarningList warnings;
+ m_Model->GetDriver()->LoadImageViaDelegate(file.c_str(), delegate, warnings, &ioHints);
+ this->accept();
+ }
+ catch(exception &exc)
+ {
+ QMessageBox b(this);
+ b.setText(QString("Failed to load image %1").arg(ui->outFilename->text()));
+ b.setDetailedText(exc.what());
+ b.setIcon(QMessageBox::Critical);
+ b.exec();
+ }
+ }
}
diff --git a/GUI/Qt/Windows/DropActionDialog.h b/GUI/Qt/Windows/DropActionDialog.h
index 3bb0ffd..bd953e7 100644
--- a/GUI/Qt/Windows/DropActionDialog.h
+++ b/GUI/Qt/Windows/DropActionDialog.h
@@ -28,7 +28,14 @@ public:
void SetDroppedFilename(QString name);
void SetModel(GlobalUIModel *model);
-
+
+ /**
+ * Attempt to load the main image directly, without showing the dialog,
+ * but showing the ImageIOWizard if there is ambiguity about how to load
+ * the image
+ */
+ void LoadMainImage(QString name);
+
private slots:
void on_btnLoadMain_clicked();
@@ -38,12 +45,16 @@ private slots:
void on_btnLoadNew_clicked();
+ /**
+ * This method loads the dropped image.
+ */
+ void LoadCommon(AbstractLoadImageDelegate *delegate);
+
private:
Ui::DropActionDialog *ui;
GlobalUIModel *m_Model;
- void LoadCommon(AbstractLoadImageDelegate *delegate);
};
#endif // DROPACTIONDIALOG_H
diff --git a/GUI/Qt/Windows/DropActionDialog.ui b/GUI/Qt/Windows/DropActionDialog.ui
index 04cb8a6..0c44ebb 100644
--- a/GUI/Qt/Windows/DropActionDialog.ui
+++ b/GUI/Qt/Windows/DropActionDialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>622</width>
- <height>319</height>
+ <height>305</height>
</rect>
</property>
<property name="windowTitle">
@@ -251,8 +251,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
- <x>557</x>
- <y>282</y>
+ <x>603</x>
+ <y>359</y>
</hint>
<hint type="destinationlabel">
<x>614</x>
diff --git a/GUI/Qt/Windows/ImageIOWizard.cxx b/GUI/Qt/Windows/ImageIOWizard.cxx
index 58ec8b2..42f4c82 100644
--- a/GUI/Qt/Windows/ImageIOWizard.cxx
+++ b/GUI/Qt/Windows/ImageIOWizard.cxx
@@ -20,6 +20,7 @@
#include <QGridLayout>
#include <QSpinBox>
#include <QFrame>
+#include <QTimer>
#include <QtCursorOverride.h>
@@ -34,9 +35,10 @@
#include "SNAPQtCommon.h"
#include "FileChooserPanelWithHistory.h"
-#include "ImageIOWizard/RegistrationPage.h"
#include "ImageIOWizard/OverlayRolePage.h"
+#include "DICOMListingTable.h"
+
namespace imageiowiz {
@@ -172,6 +174,14 @@ SelectFilePage::SelectFilePage(QWidget *parent)
connect(wiz, SIGNAL(accepted()), m_FilePanel, SLOT(onFilenameAccept()));
}
+void SelectFilePage::SetFilename(
+ const std::string &filename,
+ GuidedNativeImageIO::FileFormat format)
+{
+ m_FilePanel->setFilename(from_utf8(filename));
+ m_FilePanel->setActiveFormat(from_utf8(m_Model->GetFileFormatName(format)));
+}
+
/*
class QtRegistryTableModel : public QAbstractTableModel
{
@@ -212,10 +222,18 @@ void SelectFilePage::initializePage()
// Determine the active format to use
QString activeFormat;
+
if(m_Model->IsSaveMode())
activeFormat = from_utf8(m_Model->GetDefaultFormatForSave());
+
if(m_Model->GetSelectedFormat() < GuidedNativeImageIO::FORMAT_COUNT)
+ {
activeFormat = from_utf8(m_Model->GetFileFormatName(m_Model->GetSelectedFormat()));
+ }
+ else if(m_Model->GetSuggestedFormat() < GuidedNativeImageIO::FORMAT_COUNT)
+ {
+ activeFormat = from_utf8(m_Model->GetFileFormatName(m_Model->GetSuggestedFormat()));
+ }
// Initialize the file panel
if(m_Model->IsLoadMode())
@@ -268,19 +286,7 @@ bool SelectFilePage::validatePage()
// If format is DICOM, process the DICOM directory
if(fmt == GuidedNativeImageIO::FORMAT_DICOM_DIR)
- {
- // Change cursor until this object moves out of scope
- QtCursorOverride curse(Qt::WaitCursor);
- try
- {
- m_Model->ProcessDicomDirectory(to_utf8(m_FilePanel->absoluteFilename()));
- return true;
- }
- catch(IRISException &exc)
- {
- return ErrorMessage(exc);
- }
- }
+ return true;
// Save or load the image
return this->PerformIO();
@@ -288,32 +294,9 @@ bool SelectFilePage::validatePage()
void SelectFilePage::onFilenameChanged(QString absoluteFilename)
{
- bool file_exists = false;
-
- // The file format for the checkbox
- /*
- GuidedNativeImageIO::FileFormat fmt =
- m_Model->GuessFileFormat(to_utf8(absoluteFilename), file_exists);
-
- if(fmt != GuidedNativeImageIO::FORMAT_COUNT)
- m_FilePanel->setActiveFormat(m_InFormat->currentText());
- */
-
// Is it a directory?
if(QFileInfo(absoluteFilename).isDir())
return;
-/*
- // Add some messages to help the user
- if(fmt == GuidedNativeImageIO::FORMAT_COUNT)
- {
- if(!m_FilePanel->errorText().length())
- m_FilePanel->setErrorText("The format can not be determined from the file name.");
- }
- else if(!m_Model->CanHandleFileFormat(fmt))
- {
- if(!m_FilePanel->errorText().length())
- m_FilePanel->setErrorText("The format is not supported for this operation.");
- }*/
emit completeChanged();
}
@@ -430,17 +413,11 @@ DICOMPage::DICOMPage(QWidget *parent)
: AbstractPage(parent)
{
// Set up a table widget
- m_Table = new QTableWidget();
+ m_Table = new DICOMListingTable();
QVBoxLayout *lo = new QVBoxLayout(this);
lo->addWidget(m_Table);
lo->addWidget(m_OutMessage);
- m_Table->setSelectionBehavior(QAbstractItemView::SelectRows);
- m_Table->setSelectionMode(QAbstractItemView::SingleSelection);
- m_Table->setAlternatingRowColors(true);
- m_Table->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_Table->verticalHeader()->hide();
-
connect(m_Table->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
SIGNAL(completeChanged()));
@@ -451,46 +428,67 @@ void DICOMPage::initializePage()
// Set the title, subtitle
setTitle("Select DICOM series to open");
- // Populate the DICOM page
- const std::vector<Registry> ® = m_Model->GetDicomContents();
+ // Process the DICOM directory on a timer - so that the GUI shows first
+ QTimer::singleShot(0, this, SLOT(processDicomDirectory()));
+}
+
+void DICOMPage::cleanupPage()
+{
+ AbstractPage::cleanupPage();
+}
+
+#include "ProcessEventsITKCommand.h"
+
+void DICOMPage::processDicomDirectory()
+{
+ // Change cursor until this object moves out of scope
+ QtCursorOverride curse(Qt::WaitCursor); (void) curse;
- m_Table->setRowCount(reg.size());
- m_Table->setColumnCount(4);
- m_Table->setHorizontalHeaderItem(0, new QTableWidgetItem("Series Number"));
- m_Table->setHorizontalHeaderItem(1, new QTableWidgetItem("Description"));
- m_Table->setHorizontalHeaderItem(2, new QTableWidgetItem("Dimensions"));
- m_Table->setHorizontalHeaderItem(3, new QTableWidgetItem("Number of Images"));
+ // Disable the buttons until we finish loading
+ this->setEnabled(false);
- for(size_t i = 0; i < reg.size(); i++)
+ try
{
- Registry r = reg[i];
- m_Table->setItem(i, 0, new QTableWidgetItem(r["SeriesNumber"][""]));
- m_Table->setItem(i, 1, new QTableWidgetItem(r["SeriesDescription"][""]));
- m_Table->setItem(i, 2, new QTableWidgetItem(r["Dimensions"][""]));
- m_Table->setItem(i, 3, new QTableWidgetItem(r["NumberOfImages"][""]));
- }
+ // Callback that will handle progress of DICOM loading
+ SmartPtr<ProcessEventsITKCommand> cmd = ProcessEventsITKCommand::New();
+
+ // Timer that will call a slot at regular intervals to update the table
+ QTimer timer;
+ connect(&timer, SIGNAL(timeout()), this, SLOT(updateTable()));
+ timer.start(100);
+
+ // Get the DICOM directory contents. The command and the timer make sure
+ // that the table of DICOM entries is updated every 100 ms
+ m_Model->ProcessDicomDirectory(to_utf8(field("Filename").toString()), cmd);
- m_Table->resizeColumnsToContents();
- m_Table->resizeRowsToContents();
+ // Stop the timer
+ timer.stop();
- // If only one sequence selected, pick it
- if(reg.size() == 1)
+ // Update the data
+ this->updateTable();
+ }
+ catch(IRISException &exc)
{
- m_Table->selectRow(0);
+ ErrorMessage(exc);
}
- // Choose the sequence previously loaded
- // TODO:
+ // Enable the buttons until we finish loading
+ this->setEnabled(true);
+}
- /*
- // See if one of the sequences in the registry matches
- StringType last = m_Registry["DICOM.SequenceId"]["NULL"];
- const Fl_Menu_Item *lastpos = m_InDICOMPageSequenceId->find_item(last.c_str());
- if(lastpos)
- m_InDICOMPageSequenceId->value(lastpos);
- else
- m_InDICOMPageSequenceId->value(0);
- */
+void DICOMPage::updateTable()
+{
+ // Get the list of existing series
+ // TODO: this is a cluge!
+ std::list<std::string> series_ids = m_Model->GetFoundDicomSeriesIds();
+ std::vector<Registry> reg;
+ for(std::list<std::string>::const_iterator it = series_ids.begin();
+ it != series_ids.end(); ++it)
+ {
+ reg.push_back(m_Model->GetFoundDicomSeriesMetaData(*it));
+ }
+
+ m_Table->setData(reg);
}
bool DICOMPage::validatePage()
@@ -498,13 +496,15 @@ bool DICOMPage::validatePage()
// Clear error state
m_OutMessage->clear();
- // Add registry entries for the selected DICOM series
+ // Get the data associated with the selected row
int row = m_Table->selectionModel()->selectedRows().front().row();
+ std::string series_id =
+ to_utf8(m_Table->item(row, 0)->data(Qt::UserRole).toString());
try
{
QtCursorOverride curse(Qt::WaitCursor);
- m_Model->LoadDicomSeries(to_utf8(this->field("Filename").toString()), row);
+ m_Model->LoadDicomSeries(to_utf8(this->field("Filename").toString()), series_id);
}
catch(IRISException &exc)
{
@@ -544,6 +544,9 @@ RawPage::RawPage(QWidget *parent)
{
m_Dims[i] = new QSpinBox();
connect(m_Dims[i], SIGNAL(valueChanged(int)), SLOT(onHeaderSizeChange()));
+
+ m_Spacing[i] = new QDoubleSpinBox();
+ m_Spacing[i]->setValue(1.0);
}
lo->addWidget(new QLabel("Image dimensions:"), 1, 0, 1, 1);
@@ -607,7 +610,19 @@ RawPage::RawPage(QWidget *parent)
lo->addWidget(lbrace, 5, 3, 2, 1);
lo->addWidget(new QLabel("should be equal"), 5, 4, 2, 2);
- lo->addWidget(m_OutMessage, 7, 0, 1, 7);
+ // Add some space
+ lo->setRowMinimumHeight(6,16);
+
+ lo->addWidget(new QLabel("Voxel Spacing:"), 7, 0, 1, 1);
+ lo->addWidget(new QLabel("x:"), 7, 1, 1, 1);
+ lo->addWidget(m_Spacing[0], 7, 2, 1, 1);
+ lo->addWidget(new QLabel("y:"), 7, 3, 1, 1);
+ lo->addWidget(m_Spacing[1], 7, 4, 1, 1);
+ lo->addWidget(new QLabel("z:"), 7, 5, 1, 1);
+ lo->addWidget(m_Spacing[2], 7, 6, 1, 1);
+
+
+ lo->addWidget(m_OutMessage, 9, 0, 1, 7);
// The output label
lo->setColumnMinimumWidth(0, 140);
@@ -663,6 +678,13 @@ void RawPage::initializePage()
m_Dims[i]->setRange(1, m_FileSize);
}
+ // Set the spacing
+ Vector3d spc = hint["Raw.Spacing"][Vector3d(1.0)];
+ for(size_t i = 0; i < 3; i++)
+ {
+ m_Spacing[i]->setValue(spc[i]);
+ }
+
// Set the data type (default to uchar)
int ipt = (int) (GuidedNativeImageIO::GetPixelType(
hint, GuidedNativeImageIO::PIXELTYPE_UCHAR) -
@@ -689,6 +711,10 @@ bool RawPage::validatePage()
hint["Raw.Dimensions"] << Vector3i(
m_Dims[0]->value(), m_Dims[1]->value(), m_Dims[2]->value());
+ // Set the spacing
+ hint["Raw.Spacing"] << Vector3d(
+ m_Spacing[0]->value(), m_Spacing[1]->value(), m_Spacing[2]->value());
+
// Set the endianness
hint["Raw.BigEndian"] << ( m_InEndian->currentIndex() == 0 );
@@ -745,7 +771,6 @@ ImageIOWizard::ImageIOWizard(QWidget *parent) :
setPage(Page_Summary, new SummaryPage(this));
setPage(Page_DICOM, new DICOMPage(this));
setPage(Page_Raw, new RawPage(this));
- setPage(Page_Coreg, new RegistrationPage(this));
setPage(Page_OverlayRole, new OverlayRolePage(this));
}
@@ -767,6 +792,13 @@ void ImageIOWizard::SetModel(ImageIOWizardModel *model)
this->setWindowTitle("Save Image - ITK-SNAP");
}
+void ImageIOWizard::SetFilename(const std::string &filename,
+ GuidedNativeImageIO::FileFormat format)
+{
+ SelectFilePage *pFile = static_cast<SelectFilePage *>(this->page(Page_File));
+ pFile->SetFilename(filename, format);
+}
+
int ImageIOWizard::nextId() const
{
// Determine the sequence of pages based on current state
@@ -791,10 +823,6 @@ int ImageIOWizard::nextId() const
if(m_Model->IsOverlay())
pages.push_back(Page_OverlayRole);
- // Registration page
- if(m_Model->GetUseRegistration())
- pages.push_back(Page_Coreg);
-
// Summary page
pages.push_back(Page_Summary);
pages.push_back(-1);
@@ -819,9 +847,7 @@ int ImageIOWizard::nextId() const
int ImageIOWizard::nextPageAfterLoad()
{
- if(m_Model->GetUseRegistration())
- return ImageIOWizard::Page_Coreg;
- else if(m_Model->IsOverlay() && m_Model->IsLoadMode())
+ if(m_Model->IsOverlay() && m_Model->IsLoadMode())
return ImageIOWizard::Page_OverlayRole;
else
return ImageIOWizard::Page_Summary;
diff --git a/GUI/Qt/Windows/ImageIOWizard.h b/GUI/Qt/Windows/ImageIOWizard.h
index 4e4fda1..7044ac6 100644
--- a/GUI/Qt/Windows/ImageIOWizard.h
+++ b/GUI/Qt/Windows/ImageIOWizard.h
@@ -19,9 +19,11 @@ class QTreeWidget;
class QTreeWidgetItem;
class QTableWidget;
class QSpinBox;
+class QDoubleSpinBox;
class FileChooserPanelWithHistory;
class OptimizationProgressRenderer;
class QtVTKRenderWindowBox;
+class DICOMListingTable;
// Helper classes in their own namespace, so I can use simple class names
namespace imageiowiz
@@ -73,6 +75,10 @@ class SelectFilePage : public AbstractPage
public:
explicit SelectFilePage(QWidget *parent = 0);
+ // Externally set the filename
+ void SetFilename(const std::string &filename,
+ GuidedNativeImageIO::FileFormat format);
+
void initializePage();
bool validatePage();
// bool isComplete() const;
@@ -120,9 +126,16 @@ public:
bool validatePage();
bool isComplete() const;
+ void cleanupPage();
+
+public slots:
+
+ void processDicomDirectory();
+ void updateTable();
+
private:
- QTableWidget *m_Table;
+ DICOMListingTable *m_Table;
};
class RawPage : public AbstractPage
@@ -141,6 +154,7 @@ public slots:
private:
QSpinBox *m_Dims[3], *m_HeaderSize;
+ QDoubleSpinBox *m_Spacing[3];
QComboBox *m_InFormat, *m_InEndian;
QSpinBox *m_OutImpliedSize, *m_OutActualSize;
unsigned long m_FileSize;
@@ -168,6 +182,9 @@ public:
void SetModel(ImageIOWizardModel *model);
+ void SetFilename(const std::string &filename,
+ GuidedNativeImageIO::FileFormat format = GuidedNativeImageIO::FORMAT_COUNT);
+
virtual int nextId() const;
/** Helper function to get the next page that should be opened after the image has
diff --git a/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.cxx b/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.cxx
deleted file mode 100644
index 5d4acc9..0000000
--- a/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.cxx
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "ImageIOWizard/RegistrationPage.h"
-#include "ui_RegistrationPage.h"
-#include "SNAPQtCommon.h"
-#include <QtWidgetCoupling.h>
-#include <QtComboBoxCoupling.h>
-
-Q_DECLARE_METATYPE(ImageIOWizardModel::RegistrationMode)
-Q_DECLARE_METATYPE(ImageIOWizardModel::RegistrationMetric)
-Q_DECLARE_METATYPE(ImageIOWizardModel::RegistrationInit)
-
-
-namespace imageiowiz {
-
-void RegistrationWorkerThread::Initialize(ImageIOWizardModel *model)
-{
- // Connect the model
- m_Model = model;
-
- // Respond to events from the model
- AddListener<RegistrationWorkerThread>(
- m_Model, ImageIOWizardModel::RegistrationProgressEvent(),
- this, &RegistrationWorkerThread::OnProgressEvent);
-}
-
-void RegistrationWorkerThread::run()
-{
- // This method executes the run registration code
- m_Model->PerformRegistration();
-}
-
-void RegistrationWorkerThread::OnProgressEvent()
-{
- emit registrationProgress();
-}
-
-
-RegistrationPage::RegistrationPage(QWidget *parent) :
- AbstractPage(parent),
- ui(new Ui::RegistrationPage)
-{
- ui->setupUi(this);
-}
-
-RegistrationPage::~RegistrationPage()
-{
- delete ui;
-}
-
-void RegistrationPage::on_btnRun_clicked()
-{
- RegistrationWorkerThread *workerThread = new RegistrationWorkerThread();
- workerThread->Initialize(m_Model);
- connect(workerThread, SIGNAL(registrationProgress()), this, SLOT(onRegistrationProgress()));
- connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
- workerThread->start();
-}
-
-int RegistrationPage::nextId() const
-{
- return ImageIOWizard::Page_Summary;
-}
-
-void RegistrationPage::initializePage()
-{
- this->setTitle("Image Registration");
-
- // Connect widgets to the model
- makeCoupling(ui->inRegistrationMode, this->m_Model->GetRegistrationModeModel());
- makeCoupling(ui->inSimilarityMetric, this->m_Model->GetRegistrationMetricModel());
- makeCoupling(ui->inInitialAlignment, this->m_Model->GetRegistrationInitModel());
-
- // Get the progress renderer and attach it to the widget
- ui->progressBox->SetRenderer(this->m_Model->GetRegistrationProgressRenderer());
-}
-
-bool RegistrationPage::validatePage()
-{
- return true;
-}
-
-bool RegistrationPage::isComplete() const
-{
- return true;
-}
-
-void RegistrationPage::onRegistrationProgress()
-{
- this->m_Model->UpdateImageTransformFromRegistration();
- this->setTitle(QString("Metric %1").arg(m_Model->GetRegistrationObjective()));
-}
-
-}
diff --git a/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.h b/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.h
deleted file mode 100644
index dec237e..0000000
--- a/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef REGISTRATIONPAGE_H
-#define REGISTRATIONPAGE_H
-
-#include <QWidget>
-#include "ImageIOWizard.h"
-
-namespace Ui {
- class RegistrationPage;
-}
-
-namespace imageiowiz {
-
-class RegistrationWorkerThread : public QThread
-{
- Q_OBJECT
-
-public:
-
- void Initialize(ImageIOWizardModel *model);
- void run();
- void OnProgressEvent();
-
-signals:
-
- void registrationProgress();
-
-private:
-
- ImageIOWizardModel *m_Model;
-};
-
-class RegistrationPage : public AbstractPage
-{
- Q_OBJECT
-
-public:
- explicit RegistrationPage(QWidget *parent = 0);
- ~RegistrationPage();
-
- int nextId() const;
- void initializePage();
- bool validatePage();
- virtual bool isComplete() const;
-
-public slots:
-
- void onRegistrationProgress();
-
-private slots:
- void on_btnRun_clicked();
-
-private:
- Ui::RegistrationPage *ui;
-};
-
-} // end namespace
-
-#endif // REGISTRATIONPAGE_H
diff --git a/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.ui b/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.ui
deleted file mode 100644
index 745c605..0000000
--- a/GUI/Qt/Windows/ImageIOWizard/RegistrationPage.ui
+++ /dev/null
@@ -1,154 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>RegistrationPage</class>
- <widget class="QWidget" name="RegistrationPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>404</width>
- <height>339</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="4" column="0" colspan="2">
- <widget class="QWidget" name="widget" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>282</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QToolButton" name="btnStop">
- <property name="text">
- <string>Stop</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="btnRun">
- <property name="text">
- <string>Run</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item row="5" column="0" colspan="2">
- <widget class="QFrame" name="frame">
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QtVTKRenderWindowBox" name="progressBox" native="true">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>120</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">background-color:white;</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QComboBox" name="inSimilarityMetric"/>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>Similarity metric:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="inRegistrationMode"/>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="inInitialAlignment"/>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Initial alignment:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Registration mode:</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>QtVTKRenderWindowBox</class>
- <extends>QWidget</extends>
- <header location="global">QtVTKRenderWindowBox.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/GUI/Qt/Windows/InterpolateLabelsDialog.cxx b/GUI/Qt/Windows/InterpolateLabelsDialog.cxx
new file mode 100644
index 0000000..e3b1d97
--- /dev/null
+++ b/GUI/Qt/Windows/InterpolateLabelsDialog.cxx
@@ -0,0 +1,63 @@
+#include "InterpolateLabelsDialog.h"
+#include "ui_InterpolateLabelsDialog.h"
+#include "InterpolateLabelModel.h"
+
+#include "QtComboBoxCoupling.h"
+#include "QtRadioButtonCoupling.h"
+#include "QtDoubleSpinBoxCoupling.h"
+#include "QtCheckBoxCoupling.h"
+
+Q_DECLARE_METATYPE(InterpolateLabelModel::InterpolationType)
+Q_DECLARE_METATYPE(AnatomicalDirection)
+
+InterpolateLabelsDialog::InterpolateLabelsDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::InterpolateLabelsDialog)
+{
+ ui->setupUi(this);
+}
+
+InterpolateLabelsDialog::~InterpolateLabelsDialog()
+{
+ delete ui;
+}
+
+void InterpolateLabelsDialog::SetModel(InterpolateLabelModel *model)
+{
+ m_Model = model;
+
+ makeCoupling(ui->inActiveLabel, m_Model->GetDrawingLabelModel());
+ makeCoupling(ui->inLabelToInterpolate, m_Model->GetInterpolateLabelModel());
+
+ makeRadioGroupCoupling(ui->btnInterpolateAll, ui->btnInterpolateOne, m_Model->GetInterpolateAllModel());
+
+ // Settings for morphology method
+ makeCoupling(ui->chkMorphologyUseDistance, m_Model->GetMorphologyUseDistanceModel());
+ makeCoupling(ui->chkMorphologyUseOptimalAlignment, m_Model->GetMorphologyUseOptimalAlignmentModel());
+ makeCoupling(ui->chkMorphologyInterpolateOneAxis, m_Model->GetMorphologyInterpolateOneAxisModel());
+
+ ui->morphologyInterpolationAxis->clear();
+ ui->morphologyInterpolationAxis->addItem("Axial",QVariant::fromValue(ANATOMY_AXIAL));
+ ui->morphologyInterpolationAxis->addItem("Sagittal",QVariant::fromValue(ANATOMY_SAGITTAL));
+ ui->morphologyInterpolationAxis->addItem("Coronal",QVariant::fromValue(ANATOMY_CORONAL));
+ makeCoupling(ui->morphologyInterpolationAxis, m_Model->GetMorphologyInterpolationAxisModel());
+}
+
+void InterpolateLabelsDialog::on_btnInterpolate_clicked()
+{
+ m_Model->Interpolate();
+}
+
+void InterpolateLabelsDialog::on_btnClose_clicked()
+{
+ this->close();
+}
+
+void InterpolateLabelsDialog::showEvent(QShowEvent *e)
+{
+ // Call parent method
+ QDialog::showEvent(e);
+
+ // If the widget is not currently showing, update it's state
+ m_Model->UpdateOnShow();
+}
diff --git a/GUI/Qt/Windows/InterpolateLabelsDialog.h b/GUI/Qt/Windows/InterpolateLabelsDialog.h
new file mode 100644
index 0000000..7582736
--- /dev/null
+++ b/GUI/Qt/Windows/InterpolateLabelsDialog.h
@@ -0,0 +1,36 @@
+#ifndef INTERPOLATELABELSDIALOG_H
+#define INTERPOLATELABELSDIALOG_H
+
+#include <QDialog>
+#include <SNAPCommon.h>
+
+namespace Ui {
+class InterpolateLabelsDialog;
+}
+
+class InterpolateLabelModel;
+
+class InterpolateLabelsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit InterpolateLabelsDialog(QWidget *parent = 0);
+ ~InterpolateLabelsDialog();
+
+ void SetModel(InterpolateLabelModel *model);
+
+private slots:
+ void on_btnInterpolate_clicked();
+
+ void on_btnClose_clicked();
+
+private:
+ Ui::InterpolateLabelsDialog *ui;
+
+ SmartPtr<InterpolateLabelModel> m_Model;
+
+ virtual void showEvent(QShowEvent *e);
+};
+
+#endif // INTERPOLATELABELSDIALOG_H
diff --git a/GUI/Qt/Windows/InterpolateLabelsDialog.ui b/GUI/Qt/Windows/InterpolateLabelsDialog.ui
new file mode 100644
index 0000000..086d48c
--- /dev/null
+++ b/GUI/Qt/Windows/InterpolateLabelsDialog.ui
@@ -0,0 +1,290 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>InterpolateLabelsDialog</class>
+ <widget class="QDialog" name="InterpolateLabelsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>368</width>
+ <height>615</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Interpolate Labels - ITK-SNAP</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <pointsize>-1</pointsize>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">font-size:16px;</string>
+ </property>
+ <property name="text">
+ <string>Morphological Interpolation</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="styleSheet">
+ <string notr="true">font-size:12px;
+color: gray;</string>
+ </property>
+ <property name="text">
+ <string>Use this tool to fill in sparsely drawn segmentations. For example, you can label a structure on every fifth slice and fill in the gaps using this tool. You can also create three-dimensional scaffolds and fill in the space in between.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget_2" native="true">
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="1">
+ <widget class="QRadioButton" name="btnInterpolateAll">
+ <property name="toolTip">
+ <string>All labels in the segmentation will be interpolated at the same time</string>
+ </property>
+ <property name="text">
+ <string>Interpolate all labels</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QRadioButton" name="btnInterpolateOne">
+ <property name="toolTip">
+ <string>A single label in the segmentation will be interpolated.</string>
+ </property>
+ <property name="text">
+ <string>Interpolate a single label</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Label to interpolate:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="inLabelToInterpolate">
+ <property name="toolTip">
+ <string>Select the label which will be interpolated. Your segmentation should have two or more slices painted with that label.</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Interpolate with:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="inActiveLabel">
+ <property name="toolTip">
+ <string>Select the label which will be used to fill in the interpolated voxels. Normally this is the same label as the "label to interpolate" but you have the option of using a different label.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Advanced Options:</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="chkMorphologyUseDistance">
+ <property name="toolTip">
+ <string>Use an alternative signed distance function-based method. Slightly faster, but can fail for twisted shapes.</string>
+ </property>
+ <property name="text">
+ <string>Use signed distance function</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="chkMorphologyUseOptimalAlignment">
+ <property name="toolTip">
+ <string>Use optimal slice alignment instead of the default heuristic. This can take a long time.</string>
+ </property>
+ <property name="text">
+ <string>Use optimal slice alignment</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="chkMorphologyInterpolateOneAxis">
+ <property name="toolTip">
+ <string>Interpolate slices along a single axis. By default, interpolation is applied along all three axes in the image.</string>
+ </property>
+ <property name="text">
+ <string>Interpolate along a single axis</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget_4" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="leftMargin">
+ <number>80</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QComboBox" name="morphologyInterpolationAxis">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <item>
+ <property name="text">
+ <string>Axial</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Sagittal</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Coronal</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget_3" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnInterpolate">
+ <property name="text">
+ <string>Interpolate</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnClose">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>btnInterpolateAll</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>inLabelToInterpolate</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>267</x>
+ <y>136</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>346</x>
+ <y>190</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>btnInterpolateAll</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>inActiveLabel</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>247</x>
+ <y>136</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>346</x>
+ <y>220</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>chkMorphologyInterpolateOneAxis</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>morphologyInterpolationAxis</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>115</x>
+ <y>332</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>153</x>
+ <y>357</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/GUI/Qt/Windows/LabelEditorDialog.cxx b/GUI/Qt/Windows/LabelEditorDialog.cxx
index 0377745..0b292ed 100644
--- a/GUI/Qt/Windows/LabelEditorDialog.cxx
+++ b/GUI/Qt/Windows/LabelEditorDialog.cxx
@@ -38,6 +38,16 @@ LabelEditorDialog::LabelEditorDialog(QWidget *parent) :
menu->addAction(FindUpstreamAction(this, "actionSaveLabels"));
menu->addSeparator();
menu->addAction(ui->actionResetLabels);
+ menu->addSeparator();
+
+
+ QMenu *menu_vis = new QMenu("Visibility", this);
+ menu->addMenu(menu_vis);
+ menu_vis->addAction(ui->actionHide_all_labels);
+ menu_vis->addAction(ui->actionHide_all_labels_in_3D_window);
+ menu_vis->addSeparator();
+ menu_vis->addAction(ui->actionShow_all_labels);
+ menu_vis->addAction(ui->actionShow_all_labels_in_3D_window);
QStandardItemModel *simodel = new QStandardItemModel(this);
simodel->setColumnCount(2);
@@ -208,3 +218,23 @@ void LabelEditorDialog::on_inLabelFilter_textChanged(const QString &arg1)
{
m_LabelListFilterModel->setFilterFixedString(arg1);
}
+
+void LabelEditorDialog::on_actionHide_all_labels_triggered()
+{
+ m_Model->SetAllLabelsVisibility(false);
+}
+
+void LabelEditorDialog::on_actionHide_all_labels_in_3D_window_triggered()
+{
+ m_Model->SetAllLabelsVisibilityIn3D(false);
+}
+
+void LabelEditorDialog::on_actionShow_all_labels_triggered()
+{
+ m_Model->SetAllLabelsVisibility(true);
+}
+
+void LabelEditorDialog::on_actionShow_all_labels_in_3D_window_triggered()
+{
+ m_Model->SetAllLabelsVisibilityIn3D(true);
+}
diff --git a/GUI/Qt/Windows/LabelEditorDialog.h b/GUI/Qt/Windows/LabelEditorDialog.h
index 4685422..d18ac31 100644
--- a/GUI/Qt/Windows/LabelEditorDialog.h
+++ b/GUI/Qt/Windows/LabelEditorDialog.h
@@ -36,6 +36,14 @@ private slots:
void on_inLabelFilter_textChanged(const QString &arg1);
+ void on_actionHide_all_labels_triggered();
+
+ void on_actionHide_all_labels_in_3D_window_triggered();
+
+ void on_actionShow_all_labels_triggered();
+
+ void on_actionShow_all_labels_in_3D_window_triggered();
+
private:
Ui::LabelEditorDialog *ui;
diff --git a/GUI/Qt/Windows/LabelEditorDialog.ui b/GUI/Qt/Windows/LabelEditorDialog.ui
index 3d4ece2..a31adf7 100644
--- a/GUI/Qt/Windows/LabelEditorDialog.ui
+++ b/GUI/Qt/Windows/LabelEditorDialog.ui
@@ -743,6 +743,26 @@ QGroupBox::title {
<string>Restores label descriptions to the default values.</string>
</property>
</action>
+ <action name="actionHide_all_labels">
+ <property name="text">
+ <string>Hide all labels</string>
+ </property>
+ </action>
+ <action name="actionHide_all_labels_in_3D_window">
+ <property name="text">
+ <string>Hide all labels in 3D window</string>
+ </property>
+ </action>
+ <action name="actionShow_all_labels">
+ <property name="text">
+ <string>Show all labels</string>
+ </property>
+ </action>
+ <action name="actionShow_all_labels_in_3D_window">
+ <property name="text">
+ <string>Show all labels in 3D window</string>
+ </property>
+ </action>
</widget>
<customwidgets>
<customwidget>
diff --git a/GUI/Qt/Windows/LayerInspectorDialog.cxx b/GUI/Qt/Windows/LayerInspectorDialog.cxx
index 5338cf0..97f6868 100644
--- a/GUI/Qt/Windows/LayerInspectorDialog.cxx
+++ b/GUI/Qt/Windows/LayerInspectorDialog.cxx
@@ -28,6 +28,7 @@
#include "QtActionCoupling.h"
#include "QtActionGroupCoupling.h"
#include "DisplayLayoutModel.h"
+#include "QShortcut"
#include <QMenu>
@@ -204,18 +205,10 @@ void LayerInspectorDialog::BuildLayerWidgetHierarchy()
// Before deleting all of the existing widgets in the pane,
// find the one that is currently selected, and remember its
// layer, so we can re-select it if needed
- bool have_selected_layer = false, found_selected_layer = false;
- unsigned long selected_layer = 0;
- for(QList<LayerInspectorRowDelegate *>::iterator itdel = m_Delegates.begin();
- itdel != m_Delegates.end(); ++itdel)
- {
- if((*itdel)->selected() && (*itdel)->GetLayer())
- {
- selected_layer = (*itdel)->GetLayer()->GetUniqueId();
- have_selected_layer = true;
- break;
- }
- }
+ bool found_selected_layer = false;
+
+ // Get the ID of the currently selected layer
+ unsigned long selected_layer = m_Model->GetGlobalState()->GetSelectedLayerId();
// Get rid of all existing widgets in the pane
m_Delegates.clear();
@@ -278,12 +271,11 @@ void LayerInspectorDialog::BuildLayerWidgetHierarchy()
// Select the layer if it was previously selected or nothing was previously
// selected and the layer is the main layer
- if((have_selected_layer && it.GetLayer()->GetUniqueId() == selected_layer))
+ if(it.GetLayer()->GetUniqueId() == selected_layer)
{
w->setSelected(true);
found_selected_layer = true;
}
-
else
{
w->setSelected(false);
@@ -293,7 +285,7 @@ void LayerInspectorDialog::BuildLayerWidgetHierarchy()
m_Delegates.push_back(w);
}
- // If we haven't selected anything, select the main layer's widget
+ // If we haven't selected anything, select the main layer's widget - this should not happen
if(!found_selected_layer && w_main)
w_main->setSelected(true);
diff --git a/GUI/Qt/Windows/MainControlPanel.cxx b/GUI/Qt/Windows/MainControlPanel.cxx
index 0139301..5cea36d 100644
--- a/GUI/Qt/Windows/MainControlPanel.cxx
+++ b/GUI/Qt/Windows/MainControlPanel.cxx
@@ -215,7 +215,8 @@ void MainControlPanel::onModelUpdate(const EventBucket &bucket)
ui->btnPolygonInspector,
ui->btnPaintbrushInspector,
ui->btnSnakeInspector,
- ui->btnAnnotateInspector
+ ui->btnAnnotateInspector,
+ NULL
};
@@ -231,7 +232,8 @@ void MainControlPanel::onModelUpdate(const EventBucket &bucket)
ui->btnAnnotateInspector->setVisible(mode == ANNOTATION_MODE);
// Click the button corresponding to the mode
- mode_inspector_btn[mode]->click();
+ if(mode_inspector_btn[mode])
+ mode_inspector_btn[mode]->click();
}
}
diff --git a/GUI/Qt/Windows/MainControlPanel.ui b/GUI/Qt/Windows/MainControlPanel.ui
index f4d455e..0d53737 100644
--- a/GUI/Qt/Windows/MainControlPanel.ui
+++ b/GUI/Qt/Windows/MainControlPanel.ui
@@ -230,7 +230,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/crosshair.gif</normaloff>:/root/crosshair.gif</iconset>
</property>
<property name="checkable">
@@ -247,7 +247,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/zoom.gif</normaloff>:/root/zoom.gif</iconset>
</property>
<property name="checkable">
@@ -261,7 +261,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/poly.gif</normaloff>:/root/poly.gif</iconset>
</property>
<property name="checkable">
@@ -275,7 +275,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/paintbrush.gif</normaloff>:/root/paintbrush.gif</iconset>
</property>
<property name="checkable">
@@ -289,7 +289,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/snake.gif</normaloff>:/root/snake.gif</iconset>
</property>
<property name="checkable">
@@ -303,7 +303,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/annotation_28.png</normaloff>:/root/annotation_28.png</iconset>
</property>
<property name="checkable">
@@ -391,7 +391,7 @@ QToolButton::menu-arrow {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/icons8_undo-48.png</normaloff>:/root/icons8_undo-48.png</iconset>
</property>
</widget>
@@ -402,7 +402,7 @@ QToolButton::menu-arrow {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/icons8_redo-48.png</normaloff>:/root/icons8_redo-48.png</iconset>
</property>
</widget>
@@ -429,7 +429,7 @@ QToolButton::menu-arrow {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/layer_Inspector_16.png</normaloff>:/root/layer_Inspector_16.png</iconset>
</property>
</widget>
@@ -462,7 +462,7 @@ QToolButton::menu-arrow {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/layer_Inspector_16.png</normaloff>:/root/layer_Inspector_16.png</iconset>
</property>
</widget>
@@ -473,7 +473,7 @@ QToolButton::menu-arrow {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/layer_Inspector_16.png</normaloff>:/root/layer_Inspector_16.png</iconset>
</property>
</widget>
@@ -586,7 +586,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/crosshair.gif</normaloff>:/root/crosshair.gif</iconset>
</property>
<property name="checkable">
@@ -618,7 +618,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/zoom.gif</normaloff>:/root/zoom.gif</iconset>
</property>
<property name="checkable">
@@ -647,7 +647,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/dl_toolbox.png</normaloff>:/root/dl_toolbox.png</iconset>
</property>
<property name="checkable">
@@ -676,7 +676,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/network-wireless.png</normaloff>:/root/network-wireless.png</iconset>
</property>
<property name="checkable">
@@ -705,7 +705,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/poly.gif</normaloff>:/root/poly.gif</iconset>
</property>
<property name="checkable">
@@ -734,7 +734,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/paintbrush.gif</normaloff>:/root/paintbrush.gif</iconset>
</property>
<property name="checkable">
@@ -763,7 +763,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/snake.gif</normaloff>:/root/snake.gif</iconset>
</property>
<property name="checkable">
@@ -792,7 +792,7 @@ QWidget#widget {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/annotation_28.png</normaloff>:/root/annotation_28.png</iconset>
</property>
<property name="checkable">
@@ -965,7 +965,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/rotate3d.gif</normaloff>:/root/rotate3d.gif</iconset>
</property>
</widget>
@@ -976,7 +976,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/crosshair3D.gif</normaloff>:/root/crosshair3D.gif</iconset>
</property>
</widget>
@@ -987,7 +987,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/spray.gif</normaloff>:/root/spray.gif</iconset>
</property>
</widget>
@@ -998,7 +998,7 @@ QToolButton:checked {
<string>...</string>
</property>
<property name="icon">
- <iconset>
+ <iconset resource="../Resources/SNAPResources.qrc">
<normaloff>:/root/scalpel.gif</normaloff>:/root/scalpel.gif</iconset>
</property>
</widget>
@@ -1088,6 +1088,8 @@ QToolButton:checked {
<header>LabelSelectionButton.h</header>
</customwidget>
</customwidgets>
- <resources/>
+ <resources>
+ <include location="../Resources/SNAPResources.qrc"/>
+ </resources>
<connections/>
</ui>
diff --git a/GUI/Qt/Windows/MainImageWindow.cxx b/GUI/Qt/Windows/MainImageWindow.cxx
index 267efdf..93310b9 100644
--- a/GUI/Qt/Windows/MainImageWindow.cxx
+++ b/GUI/Qt/Windows/MainImageWindow.cxx
@@ -58,8 +58,6 @@
#include "DefaultBehaviorSettings.h"
#include "SynchronizationModel.h"
-
-
#include "QtCursorOverride.h"
#include "QtWarningDialog.h"
#include <QtWidgetCoupling.h>
@@ -71,7 +69,8 @@
#include <DropActionDialog.h>
#include <PreferencesDialog.h>
#include "SaveModifiedLayersDialog.h"
-
+#include <InterpolateLabelsDialog.h>
+#include "RegistrationDialog.h"
#include <QAbstractListModel>
#include <QItemDelegate>
@@ -211,6 +210,10 @@ MainImageWindow::MainImageWindow(QWidget *parent) :
m_StatisticsDialog = new StatisticsDialog(this);
m_StatisticsDialog->setModal(false);
+ m_InterpolateLabelsDialog = new InterpolateLabelsDialog(this);
+ m_InterpolateLabelsDialog->setModal(false);
+
+
// Initialize the docked panels
m_DockLeft = new QDockWidget(this);
m_DockLeft->setAllowedAreas(Qt::LeftDockWidgetArea);
@@ -224,13 +227,30 @@ MainImageWindow::MainImageWindow(QWidget *parent) :
m_ControlPanel = new MainControlPanel(this);
m_DockLeft->setWidget(m_ControlPanel);
+ // Connect left dock to its menu item
+ connect(m_DockLeft, SIGNAL(visibilityChanged(bool)),
+ ui->actionMainControlPanel, SLOT(setChecked(bool)));
+
+ // Set up the right hand side dock widget
m_DockRight = new QDockWidget("Segment 3D", this);
- m_SnakeWizard = new SnakeWizardPanel(this);
- m_DockRight->setWidget(m_SnakeWizard);
+
m_DockRight->setAllowedAreas(Qt::RightDockWidgetArea);
m_DockRight->setFeatures(
QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetMovable);
+
+ m_RightDockStack = new QStackedWidget(m_DockRight);
+ connect(m_RightDockStack, SIGNAL(currentChanged(int)),
+ this, SLOT(onRightDockCurrentChanged(int)));
+
+ m_DockRight->setWidget(m_RightDockStack);
+
+ m_SnakeWizard = new SnakeWizardPanel(this);
+ m_RegistrationDialog = new RegistrationDialog(this);
+
+ m_RightDockStack->addWidget(m_SnakeWizard);
+ m_RightDockStack->addWidget(m_RegistrationDialog);
+
this->addDockWidget(Qt::RightDockWidgetArea, m_DockRight);
// Set up the recent items panels
@@ -255,7 +275,10 @@ MainImageWindow::MainImageWindow(QWidget *parent) :
// Hide the dock when the wizard finishes
connect(m_SnakeWizard, SIGNAL(wizardFinished()),
- this, SLOT(onSnakeWizardFinished()));
+ this, SLOT(onRightDockDialogFinished()));
+
+ connect(m_RegistrationDialog, SIGNAL(wizardFinished()),
+ this, SLOT(onRightDockDialogFinished()));
// Make the margins adjust when the docks are attached
connect(m_DockLeft, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
@@ -294,23 +317,6 @@ MainImageWindow::MainImageWindow(QWidget *parent) :
// We accept drop events
setAcceptDrops(true);
- // Connect recent file/project menu items
- connect(ui->actionRecent_1, SIGNAL(triggered()), SLOT(LoadRecentActionTriggered()));
- connect(ui->actionRecent_2, SIGNAL(triggered()), SLOT(LoadRecentActionTriggered()));
- connect(ui->actionRecent_3, SIGNAL(triggered()), SLOT(LoadRecentActionTriggered()));
- connect(ui->actionRecent_4, SIGNAL(triggered()), SLOT(LoadRecentActionTriggered()));
- connect(ui->actionRecent_5, SIGNAL(triggered()), SLOT(LoadRecentActionTriggered()));
- connect(ui->actionRecentOverlay_1, SIGNAL(triggered()), SLOT(LoadRecentOverlayActionTriggered()));
- connect(ui->actionRecentOverlay_2, SIGNAL(triggered()), SLOT(LoadRecentOverlayActionTriggered()));
- connect(ui->actionRecentOverlay_3, SIGNAL(triggered()), SLOT(LoadRecentOverlayActionTriggered()));
- connect(ui->actionRecentOverlay_4, SIGNAL(triggered()), SLOT(LoadRecentOverlayActionTriggered()));
- connect(ui->actionRecentOverlay_5, SIGNAL(triggered()), SLOT(LoadRecentOverlayActionTriggered()));
- connect(ui->actionRecentWorkspace1, SIGNAL(triggered()), SLOT(LoadRecentProjectActionTriggered()));
- connect(ui->actionRecentWorkspace2, SIGNAL(triggered()), SLOT(LoadRecentProjectActionTriggered()));
- connect(ui->actionRecentWorkspace3, SIGNAL(triggered()), SLOT(LoadRecentProjectActionTriggered()));
- connect(ui->actionRecentWorkspace4, SIGNAL(triggered()), SLOT(LoadRecentProjectActionTriggered()));
- connect(ui->actionRecentWorkspace5, SIGNAL(triggered()), SLOT(LoadRecentProjectActionTriggered()));
-
// Set up the animation timer
m_AnimateTimer = new QTimer(this);
m_AnimateTimer->setInterval(1000);
@@ -406,6 +412,12 @@ MainImageWindow::MainImageWindow(QWidget *parent) :
#ifndef __APPLE__
TranslateChildTooltipKeyModifiers(this);
#endif
+
+ // Listen to changes in the active window. This affects the behavior of the "close" shortcut
+ connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(onActiveChanged()));
+
+ // Start with the "close window" menu item hidden
+ ui->actionClose_Window->setVisible(false);
}
@@ -418,7 +430,7 @@ MainImageWindow::~MainImageWindow()
void MainImageWindow::HookupShortcutToAction(const QKeySequence &ks, QAction *action)
{
// The bug/feature of single-key shortcuts not working is only in MacOS
-#if QT_VERSION >= 0x050000 && defined __APPLE__
+#if QT_VERSION >= 0x050000 && QT_VERSION < 0x050600 && defined __APPLE__
QShortcut *short_S = new QShortcut(ks, this);
connect(short_S, SIGNAL(activated()), action, SLOT(trigger()));
#endif
@@ -449,6 +461,8 @@ void MainImageWindow::Initialize(GlobalUIModel *model)
m_DropDialog->SetModel(model);
m_StatisticsDialog->SetModel(model);
m_PreferencesDialog->SetModel(model->GetGlobalPreferencesModel());
+ m_InterpolateLabelsDialog->SetModel(model->GetInterpolateLabelModel());
+ m_RegistrationDialog->SetModel(model->GetRegistrationModel());
// Initialize the docked panels
m_ControlPanel->SetModel(model);
@@ -584,7 +598,7 @@ void MainImageWindow::Initialize(GlobalUIModel *model)
activateOnFlag(ui->actionColor_Map_Editor, m_Model, UIF_BASEIMG_LOADED);
activateOnFlag(ui->actionLabel_Editor, m_Model, UIF_BASEIMG_LOADED);
activateOnFlag(ui->actionImage_Information, m_Model, UIF_BASEIMG_LOADED);
- activateOnFlag(ui->actionLabel_Editor, m_Model, UIF_BASEIMG_LOADED);
+ activateOnFlag(ui->actionRegistration, m_Model, UIF_IRIS_WITH_OVERLAY_LOADED);
activateOnFlag(ui->actionReorient_Image, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED);
@@ -612,6 +626,7 @@ void MainImageWindow::ShowFirstTime()
this->UpdateWindowTitle();
this->UpdateLayerLayoutActions();
this->UpdateSelectedLayerActions();
+ this->UpdateDICOMContentsMenu();
// Show the window
this->show();
@@ -641,6 +656,7 @@ void MainImageWindow::onModelUpdate(const EventBucket &b)
b.HasEvent(ValueChangedEvent(), m_Model->GetHistoryModel("MainImage")))
{
this->UpdateRecentMenu();
+ this->UpdateDICOMContentsMenu();
}
if(b.HasEvent(ValueChangedEvent(), m_Model->GetHistoryModel("Project")))
@@ -650,6 +666,7 @@ void MainImageWindow::onModelUpdate(const EventBucket &b)
if(b.HasEvent(ValueChangedEvent(), m_Model->GetGlobalState()->GetProjectFilenameModel()))
{
+ this->UpdateWindowTitle();
this->UpdateProjectMenuItems();
}
@@ -670,6 +687,27 @@ void MainImageWindow::onModelUpdate(const EventBucket &b)
}
}
+void MainImageWindow::externalStyleSheetFileChanged(const QString &file)
+{
+ QFile File(file);
+ File.open(QFile::ReadOnly);
+ this->setStyleSheet(QLatin1String(File.readAll()));
+}
+
+void MainImageWindow::onActiveChanged()
+{
+ if(this->isActiveWindow())
+ {
+ ui->actionUnload_Last_Overlay->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_W));
+ ui->actionClose_Window->setVisible(false);
+ }
+ else
+ {
+ ui->actionUnload_Last_Overlay->setShortcut(QKeySequence());
+ ui->actionClose_Window->setVisible(true);
+ }
+}
+
void MainImageWindow::UpdateMainLayout()
{
// Update the image dimensions
@@ -792,89 +830,103 @@ void MainImageWindow::UpdateSelectedLayerActions()
}
}
-void MainImageWindow::UpdateRecentMenu()
+Q_DECLARE_METATYPE(IRISApplication::DicomSeriesDescriptor)
+
+void MainImageWindow::UpdateDICOMContentsMenu()
{
- // Menus to populate
- QAction *menus[] = {
- ui->actionRecent_1,
- ui->actionRecent_2,
- ui->actionRecent_3,
- ui->actionRecent_4,
- ui->actionRecent_5};
+ // Clear the menu
+ ui->menuAddAnotherDicomImage->clear();
- // List of filenames
- std::vector<std::string> recent = m_Model->GetRecentHistoryItems("MainImage", 5);
+ // Any actions added?
+ bool have_actions = false;
- // Toggle the state of each menu item
- for(int i = 0; i < 5; i++)
+ // Get the list of dicom series grouped by filename
+ IRISApplication::DicomSeriesTree dicoms =
+ m_Model->GetDriver()->ListAvailableSiblingDicomSeries();
+
+ // Iterate over all of these
+ for(IRISApplication::DicomSeriesTree::const_iterator it_map = dicoms.begin();
+ it_map != dicoms.end(); ++it_map)
{
- if(i < recent.size())
+ // Create a submenu or point to the menu itself
+ QMenu *target_menu = ui->menuAddAnotherDicomImage;
+ if(dicoms.size() > 1)
{
- menus[i]->setText(from_utf8(recent[i]));
- menus[i]->setEnabled(true);
+ target_menu = new QMenu(from_utf8(it_map->first), ui->menuAddAnotherDicomImage);
+ ui->menuAddAnotherDicomImage->addMenu(target_menu);
}
- else
+
+ // Add all the series_ids as actions
+ for(IRISApplication::DicomSeriesListing::const_iterator it_list =
+ it_map->second.begin(); it_list != it_map->second.end(); it_list++)
{
- menus[i]->setText("Not available");
- menus[i]->setEnabled(false);
+ // Create a new action
+ QAction *action = new QAction(this);
+ QVariant user_data; user_data.setValue(*it_list);
+ action->setData(user_data);
+ action->setText(QString("%1 [%2]")
+ .arg(from_utf8(it_list->series_desc))
+ .arg(from_utf8(it_list->dimensions)));
+
+ // Connect this action to its slot
+ connect(action, SIGNAL(triggered()),
+ this, SLOT(LoadAnotherDicomActionTriggered()));
+
+ // Add the action to the menu
+ target_menu->addAction(action);
+
+ // We have some actions!
+ have_actions = true;
}
}
- // Do the same for the overlay menus
- QAction *omenus[] = {
- ui->actionRecentOverlay_1,
- ui->actionRecentOverlay_2,
- ui->actionRecentOverlay_3,
- ui->actionRecentOverlay_4,
- ui->actionRecentOverlay_5};
+ // Hide or show the menu based on availability of actions
+ ui->menuAddAnotherDicomImage->menuAction()->setVisible(have_actions);
+}
- // List of filenames - from local history
- recent = m_Model->GetRecentHistoryItems("AnatomicImage", 5, false);
+void MainImageWindow::CreateRecentMenu(
+ QMenu *submenu,
+ const char *history_category,
+ bool use_global_history,
+ int max_items,
+ const char *slot)
+{
+ // Delete all the menu items in the parent menu
+ submenu->clear();
- // Toggle the state of each menu item
- for(int i = 0; i < 5; i++)
+ // Get the recent history for this category
+ std::vector<std::string> recent =
+ m_Model->GetRecentHistoryItems(history_category, max_items, use_global_history);
+
+ // Create an action for each recent item
+ for(int i = 0; i < recent.size(); i++)
{
- if(i < recent.size())
- {
- omenus[i]->setText(from_utf8(recent[i]));
- omenus[i]->setEnabled(true);
- }
- else
- {
- omenus[i]->setText("Not available");
- omenus[i]->setEnabled(false);
- }
+ // Create an action for this file
+ QAction *action = submenu->addAction(from_utf8(recent[i]));
+ connect(action, SIGNAL(triggered(bool)), this, slot);
}
+ // Toggle the visibility of the submenu
+ submenu->menuAction()->setVisible(recent.size() > 0);
}
-void MainImageWindow::UpdateRecentProjectsMenu()
+void MainImageWindow::UpdateRecentMenu()
{
- // Menus to populate
- QAction *menus[] = {
- ui->actionRecentWorkspace1,
- ui->actionRecentWorkspace2,
- ui->actionRecentWorkspace3,
- ui->actionRecentWorkspace4,
- ui->actionRecentWorkspace5};
+ // Create recent menus for various history categories
+ this->CreateRecentMenu(ui->menuRecent_Images, "MainImage", true, 5,
+ SLOT(LoadRecentActionTriggered()));
- // List of filenames
- std::vector<std::string> recent = m_Model->GetRecentHistoryItems("Project", 5);
+ this->CreateRecentMenu(ui->menuRecent_Overlays, "AnatomicImage", false, 5,
+ SLOT(LoadRecentOverlayActionTriggered()));
- // Toggle the state of each menu item
- for(int i = 0; i < 5; i++)
- {
- if(i < recent.size())
- {
- menus[i]->setText(from_utf8(recent[i]));
- menus[i]->setEnabled(true);
- }
- else
- {
- menus[i]->setText("Not available");
- menus[i]->setEnabled(false);
- }
- }
+ this->CreateRecentMenu(ui->menuRecent_Segmentations, "LabelImage", false, 5,
+ SLOT(LoadRecentSegmentationActionTriggered()));
+}
+
+void MainImageWindow::UpdateRecentProjectsMenu()
+{
+ this->CreateRecentMenu(ui->menuRecentWorkspaces, "Project", true, 5,
+ SLOT(LoadRecentProjectActionTriggered()));
}
@@ -994,7 +1046,7 @@ void MainImageWindow::on_actionLoad_from_Image_triggered()
delegate->Initialize(m_Model->GetDriver());
SmartPtr<ImageIOWizardModel> model = ImageIOWizardModel::New();
- model->InitializeForLoad(m_Model, delegate, "LabelImage", "Segmentation Image");
+ model->InitializeForLoad(m_Model, delegate);
// Execute the IO wizard
ImageIOWizard wiz(this);
@@ -1045,6 +1097,8 @@ void MainImageWindow::OpenSnakeWizard()
m_SizeWithoutRightDock = this->size();
// Make the dock containing the wizard visible
+ m_DockRight->setWindowTitle("Segment 3D");
+ m_RightDockStack->setCurrentWidget(m_SnakeWizard);
m_DockRight->setVisible(true);
}
@@ -1118,8 +1172,8 @@ void MainImageWindow::LoadDroppedFile(QString file)
}
else
{
- // Otherwise, attempt to load the image
- this->LoadMainImage(file);
+ // Otherwise, load the main image directly
+ m_DropDialog->LoadMainImage(file);
}
}
}
@@ -1197,8 +1251,8 @@ void MainImageWindow::dropEvent(QDropEvent *event)
#endif
QString file = url.toLocalFile();
- LoadDroppedFile(file);
event->acceptProposedAction();
+ LoadDroppedFile(file);
}
QActionGroup *MainImageWindow::GetMainToolActionGroup()
@@ -1271,6 +1325,63 @@ void MainImageWindow::LoadRecentOverlayActionTriggered()
}
}
+void MainImageWindow::LoadRecentSegmentationActionTriggered()
+{
+ // Get the filename that wants to be loaded
+ QAction *action = qobject_cast<QAction *>(sender());
+ QString file = action->text();
+
+ // Prompt for unsaved changes
+ if(!SaveModifiedLayersDialog::PromptForUnsavedSegmentationChanges(m_Model))
+ return;
+
+ // Try loading the image
+ try
+ {
+ // Change cursor for this operation
+ QtCursorOverride c(Qt::WaitCursor);
+ IRISWarningList warnings;
+ SmartPtr<LoadSegmentationImageDelegate> del = LoadSegmentationImageDelegate::New();
+ del->Initialize(m_Model->GetDriver());
+ m_Model->GetDriver()->LoadImageViaDelegate(file.toUtf8().constData(), del, warnings);
+ }
+ catch(exception &exc)
+ {
+ ReportNonLethalException(this, exc, "Image IO Error",
+ QString("Failed to load segmentation image %1").arg(file));
+ }
+}
+
+void MainImageWindow::LoadAnotherDicomActionTriggered()
+{
+ // Request to load another DICOM from the main image's folder
+ QAction *action = qobject_cast<QAction *>(sender());
+
+ // Get the dicom descriptor
+ IRISApplication::DicomSeriesDescriptor desc =
+ action->data().value<IRISApplication::DicomSeriesDescriptor>();
+
+ // Try to load a DICOM with this series ID
+ try
+ {
+ // Change cursor for this operation
+ QtCursorOverride c(Qt::WaitCursor);
+ IRISWarningList warnings;
+ SmartPtr<LoadOverlayImageDelegate> del = LoadOverlayImageDelegate::New();
+ del->Initialize(m_Model->GetDriver());
+ m_Model->GetDriver()->LoadAnotherDicomSeriesViaDelegate(
+ desc.layer_uid, desc.series_id.c_str(), del, warnings);
+ }
+ catch(exception &exc)
+ {
+ ReportNonLethalException(this, exc, "Image IO Error",
+ QString("Failed to load overlay image %1").arg(action->text()));
+ }
+
+}
+
+
+
void MainImageWindow::LoadProject(const QString &file)
{
// Try loading the image
@@ -1309,13 +1420,14 @@ void MainImageWindow::LoadRecentProjectActionTriggered()
}
-void MainImageWindow::onSnakeWizardFinished()
+void MainImageWindow::onRightDockDialogFinished()
{
// Make the dock containing the wizard visible
m_DockRight->setVisible(false);
// Auto-adjust the canvas size
- this->UpdateCanvasDimensions();
+ QTimer::singleShot(0, this, SLOT(UpdateCanvasDimensions()));
+ // this->UpdateCanvasDimensions();
}
void MainImageWindow::on_actionUnload_All_triggered()
@@ -1396,8 +1508,7 @@ void MainImageWindow::on_actionOpenMain_triggered()
SmartPtr<LoadMainImageDelegate> delegate = LoadMainImageDelegate::New();
delegate->Initialize(m_Model->GetDriver());
SmartPtr<ImageIOWizardModel> model = ImageIOWizardModel::New();
- model->InitializeForLoad(m_Model, delegate,
- "AnatomicImage", "Main Image");
+ model->InitializeForLoad(m_Model, delegate);
// Execute the IO wizard
ImageIOWizard wiz(this);
@@ -1410,23 +1521,7 @@ void MainImageWindow::on_actionAdd_Overlay_triggered()
SmartPtr<LoadOverlayImageDelegate> delegate = LoadOverlayImageDelegate::New();
delegate->Initialize(m_Model->GetDriver());
SmartPtr<ImageIOWizardModel> model = ImageIOWizardModel::New();
- model->InitializeForLoad(m_Model, delegate,
- "AnatomicImage", "Additional Image");
-
- // Execute the IO wizard
- ImageIOWizard wiz(this);
- wiz.SetModel(model);
- wiz.exec();
-}
-
-
-void MainImageWindow::on_actionCoregister_Overlay_triggered()
-{
- SmartPtr<LoadCoregisteredOverlayImageDelegate> delegate = LoadCoregisteredOverlayImageDelegate::New();
- delegate->Initialize(m_Model->GetDriver());
- SmartPtr<ImageIOWizardModel> model = ImageIOWizardModel::New();
- model->InitializeForLoad(m_Model, delegate,
- "AnatomicImage", "Coregistered Overlay Image");
+ model->InitializeForLoad(m_Model, delegate);
// Execute the IO wizard
ImageIOWizard wiz(this);
@@ -2062,6 +2157,25 @@ void MainImageWindow::changeEvent(QEvent *)
m_Model->GetSynchronizationModel()->SetCanBroadcast(this->isActiveWindow());
}
+void MainImageWindow::on_actionClose_Window_triggered()
+{
+ // If main window is not the active window, close the active window instead of closing
+ // any images. This is because Ctrl-W shortcut should be reserved for closign windows
+ if(QApplication::activeWindow() != this)
+ {
+ QApplication::activeWindow()->close();
+ return;
+ }
+}
+
+void MainImageWindow::onRightDockCurrentChanged(int)
+{
+ // Adjust the width of the stack to match the current widget
+ m_RightDockStack->setMaximumWidth(
+ m_RightDockStack->currentWidget()->maximumWidth());
+}
+
+
void MainImageWindow::on_actionUnload_Last_Overlay_triggered()
{
// Get the selected ID
@@ -2104,3 +2218,27 @@ void MainImageWindow::on_actionActivatePreviousLayer_triggered()
{
m_Model->GetDisplayLayoutModel()->ActivatePrevLayerInTiledMode();
}
+
+void MainImageWindow::on_actionInterpolate_Labels_triggered()
+{
+ RaiseDialog(m_InterpolateLabelsDialog);
+}
+
+void MainImageWindow::on_actionRegistration_triggered()
+{
+ // Remember the size of the window before the right dock was shown
+ m_SizeWithoutRightDock = this->size();
+
+ m_DockRight->setWindowTitle("Registration");
+ m_RightDockStack->setCurrentWidget(m_RegistrationDialog);
+ m_DockRight->setVisible(true);
+}
+
+
+void MainImageWindow::on_actionMainControlPanel_triggered()
+{
+ if(ui->actionMainControlPanel->isChecked())
+ m_DockLeft->show();
+ else
+ m_DockLeft->hide();
+}
diff --git a/GUI/Qt/Windows/MainImageWindow.h b/GUI/Qt/Windows/MainImageWindow.h
index df7fa76..f24d48d 100644
--- a/GUI/Qt/Windows/MainImageWindow.h
+++ b/GUI/Qt/Windows/MainImageWindow.h
@@ -40,16 +40,19 @@ class EventBucket;
class QModelIndex;
class QProgressDialog;
class AboutDialog;
+class QStackedWidget;
class LabelEditorDialog;
class LayerInspectorDialog;
class QtProgressReporterDelegate;
class ReorientImageDialog;
+class RegistrationDialog;
class DropActionDialog;
class MainControlPanel;
class StatisticsDialog;
class QActionGroup;
class PreferencesDialog;
+class InterpolateLabelsDialog;
class ImageIOWizard;
class ImageIOWizardModel;
@@ -122,12 +125,19 @@ public slots:
void LoadRecentActionTriggered();
void LoadRecentOverlayActionTriggered();
+ void LoadRecentSegmentationActionTriggered();
void LoadRecentProjectActionTriggered();
+ void LoadAnotherDicomActionTriggered();
void AdjustMarginsForDocks();
void onModelUpdate(const EventBucket &b);
+ void externalStyleSheetFileChanged(const QString &file);
+
private slots:
+
+ void onActiveChanged();
+
void on_actionQuit_triggered();
void on_actionLoad_from_Image_triggered();
@@ -136,7 +146,7 @@ private slots:
void on_actionLabel_Editor_triggered();
- void onSnakeWizardFinished();
+ void onRightDockDialogFinished();
void on_actionUnload_All_triggered();
@@ -271,14 +281,22 @@ private slots:
void on_actionUnload_All_Overlays_triggered();
- void on_actionCoregister_Overlay_triggered();
-
void on_actionToggleLayerLayout_triggered();
void on_actionActivateNextLayer_triggered();
void on_actionActivatePreviousLayer_triggered();
+ void on_actionInterpolate_Labels_triggered();
+
+ void on_actionRegistration_triggered();
+
+ void on_actionClose_Window_triggered();
+
+ void onRightDockCurrentChanged(int);
+
+ void on_actionMainControlPanel_triggered();
+
protected:
// bool eventFilter(QObject *obj, QEvent *event);
@@ -288,12 +306,18 @@ protected:
private:
+ void CreateRecentMenu(QMenu *submenu,
+ const char *history_category,
+ bool use_global_history,
+ int max_items, const char *slot);
+
void UpdateRecentMenu();
void UpdateWindowTitle();
void UpdateProjectMenuItems();
void UpdateRecentProjectsMenu();
void UpdateLayerLayoutActions();
void UpdateSelectedLayerActions();
+ void UpdateDICOMContentsMenu();
// Save the segmentation (interactively or not). Return true if save was
// successful
@@ -318,6 +342,9 @@ private:
// Left and right docks
QDockWidget *m_DockLeft, *m_DockRight;
+ // A stack widget for the right dock
+ QStackedWidget *m_RightDockStack;
+
// Size before the right dock is shown
QSize m_SizeWithoutRightDock;
@@ -354,6 +381,10 @@ private:
PreferencesDialog *m_PreferencesDialog;
+ InterpolateLabelsDialog *m_InterpolateLabelsDialog;
+
+ RegistrationDialog *m_RegistrationDialog;
+
// A timer used to animate components
QTimer *m_AnimateTimer;
};
diff --git a/GUI/Qt/Windows/MainImageWindow.ui b/GUI/Qt/Windows/MainImageWindow.ui
index aaa3c5e..ae68cab 100644
--- a/GUI/Qt/Windows/MainImageWindow.ui
+++ b/GUI/Qt/Windows/MainImageWindow.ui
@@ -14,7 +14,9 @@
<string>MainWindow</string>
</property>
<property name="styleSheet">
- <string notr="true"/>
+ <string notr="true">* {
+font-size:13px;
+}</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
@@ -189,12 +191,12 @@ QTextBrowser {
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
-</style></head><body style=" font-family:'.Helvetica Neue DeskInterface'; font-size:13pt; font-weight:400; font-style:normal;">
-<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Lucida Grande';"><br /></p>
-<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Lucida Grande';" style=" margin-top:12px; margin-bottom:4px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Learn about the <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.NewFeaturesVersion30"><span style=" text-decoration: underline; color:#0000ff;& [...]
-<li style=" font-family:'Lucida Grande';" style=" margin-top:0px; margin-bottom:4px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Read a <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.UpgradingTo30"><span style=" text-decoration: underline; color:#0000ff;">quick transition guide</span></a> for Version 2 users</li>
-<li style=" font-family:'Lucida Grande';" style=" margin-top:0px; margin-bottom:4px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Download <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Downloads.Data"><span style=" text-decoration: underline; color:#0000ff;">sample datasets</span></a></li>
-<li style=" font-family:'Lucida Grande';" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Connect to the <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=MailingLists.MailingLists"><span style=" text-decoration: underline; color:#0000ff;">ITK-SNAP community</span></a></li></ul></body></html></string>
+</style></head><body style=" font-family:'.Helvetica Neue DeskInterface'; font-size:13px; font-weight:400; font-style:normal;">
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Lucida Grande'; font-size:13pt;"><br /></p>
+<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Lucida Grande'; font-size:13pt;" style=" margin-top:12px; margin-bottom:4px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Learn about the </span><a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.NewFeaturesVersion3 [...]
+<li style=" font-family:'Lucida Grande'; font-size:13pt;" style=" margin-top:0px; margin-bottom:4px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Read a </span><a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.UpgradingTo30"><span style=" font-size:11px; text-decoration: underline; color:#0000ff;">quick transition guide</span>< [...]
+<li style=" font-family:'Lucida Grande'; font-size:13pt;" style=" margin-top:0px; margin-bottom:4px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Download </span><a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Downloads.Data"><span style=" font-size:11px; text-decoration: underline; color:#0000ff;">sample datasets</span></a></li>
+<li style=" font-family:'Lucida Grande'; font-size:13pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Connect to the </span><a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=MailingLists.MailingLists"><span style=" font-size:11px; text-decoration: underline; color:#0000ff;">ITK-SNAP community</span> [...]
</property>
<property name="openExternalLinks">
<bool>true</bool>
@@ -411,9 +413,17 @@ color:rgb(103, 103, 103);
<addaction name="menuScreenshot"/>
<addaction name="menuScreenshot_Series"/>
</widget>
+ <widget class="QMenu" name="menuAddAnotherDicomImage">
+ <property name="title">
+ <string>Add Another DICOM Series</string>
+ </property>
+ <addaction name="actionDicomDummy"/>
+ </widget>
<addaction name="actionOpenMain"/>
<addaction name="menuRecent_Images"/>
<addaction name="separator"/>
+ <addaction name="menuAddAnotherDicomImage"/>
+ <addaction name="separator"/>
<addaction name="actionAdd_Overlay"/>
<addaction name="menuRecent_Overlays"/>
<addaction name="separator"/>
@@ -421,6 +431,7 @@ color:rgb(103, 103, 103);
<addaction name="separator"/>
<addaction name="menuExport"/>
<addaction name="separator"/>
+ <addaction name="actionClose_Window"/>
<addaction name="actionUnload_Last_Overlay"/>
<addaction name="actionUnload_All"/>
<addaction name="separator"/>
@@ -441,7 +452,18 @@ color:rgb(103, 103, 103);
<addaction name="actionSegmentationIncreaseOpacity"/>
<addaction name="actionSegmentationDecreaseOpacity"/>
</widget>
+ <widget class="QMenu" name="menuRecent_Segmentations">
+ <property name="title">
+ <string>Recent Segmentations</string>
+ </property>
+ <addaction name="actionRecentSeg_1"/>
+ <addaction name="actionRecentSeg_2"/>
+ <addaction name="actionRecentSeg_3"/>
+ <addaction name="actionRecentSeg_4"/>
+ <addaction name="actionRecentSeg_5"/>
+ </widget>
<addaction name="actionLoad_from_Image"/>
+ <addaction name="menuRecent_Segmentations"/>
<addaction name="separator"/>
<addaction name="actionSaveSegmentation"/>
<addaction name="actionSaveSegmentationAs"/>
@@ -503,6 +525,9 @@ color:rgb(103, 103, 103);
<addaction name="menuActive_3D_Tool"/>
<addaction name="separator"/>
<addaction name="actionReorient_Image"/>
+ <addaction name="actionRegistration"/>
+ <addaction name="separator"/>
+ <addaction name="actionInterpolate_Labels"/>
<addaction name="separator"/>
<addaction name="actionPreferences"/>
</widget>
@@ -535,7 +560,7 @@ color:rgb(103, 103, 103);
</widget>
<widget class="QMenu" name="menuZoom">
<property name="title">
- <string>View Zoom</string>
+ <string>Zoom</string>
</property>
<addaction name="actionZoomToFitInAllViews"/>
<addaction name="separator"/>
@@ -558,17 +583,23 @@ color:rgb(103, 103, 103);
<addaction name="actionOverlayVisibilityIncreaseAll"/>
<addaction name="actionOverlayVisibilityDecreaseAll"/>
</widget>
+ <widget class="QMenu" name="menuViews">
+ <property name="title">
+ <string>Views</string>
+ </property>
+ <addaction name="actionMainControlPanel"/>
+ </widget>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
<addaction name="separator"/>
<addaction name="menuForeground_Label"/>
<addaction name="menuBackground_Label"/>
<addaction name="separator"/>
- <addaction name="menuSlice_Annotations"/>
- <addaction name="separator"/>
<addaction name="menuZoom"/>
- <addaction name="separator"/>
<addaction name="menuLayers"/>
+ <addaction name="menuSlice_Annotations"/>
+ <addaction name="separator"/>
+ <addaction name="menuViews"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
@@ -1465,6 +1496,65 @@ color:rgb(103, 103, 103);
<string>[</string>
</property>
</action>
+ <action name="actionInterpolate_Labels">
+ <property name="text">
+ <string>Interpolate Labels...</string>
+ </property>
+ </action>
+ <action name="actionRegistration">
+ <property name="text">
+ <string>Registration ...</string>
+ </property>
+ <property name="toolTip">
+ <string>Perform image registration</string>
+ </property>
+ </action>
+ <action name="actionClose_Window">
+ <property name="text">
+ <string>Close Window</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+W</string>
+ </property>
+ </action>
+ <action name="actionDicomDummy">
+ <property name="text">
+ <string>Dummy</string>
+ </property>
+ </action>
+ <action name="actionMainControlPanel">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Main Control Panel</string>
+ </property>
+ </action>
+ <action name="actionRecentSeg_1">
+ <property name="text">
+ <string>Recent1</string>
+ </property>
+ </action>
+ <action name="actionRecentSeg_2">
+ <property name="text">
+ <string>Recent2</string>
+ </property>
+ </action>
+ <action name="actionRecentSeg_3">
+ <property name="text">
+ <string>Recent3</string>
+ </property>
+ </action>
+ <action name="actionRecentSeg_4">
+ <property name="text">
+ <string>Recent4</string>
+ </property>
+ </action>
+ <action name="actionRecentSeg_5">
+ <property name="text">
+ <string>Recent5</string>
+ </property>
+ </action>
</widget>
<customwidgets>
<customwidget>
diff --git a/GUI/Qt/Windows/PreferencesDialog.cxx b/GUI/Qt/Windows/PreferencesDialog.cxx
index 4f94ce3..48e8252 100644
--- a/GUI/Qt/Windows/PreferencesDialog.cxx
+++ b/GUI/Qt/Windows/PreferencesDialog.cxx
@@ -52,6 +52,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) :
append_appearance_item(itemSliceViews, SNAPAppearanceSettings::MARKERS, "Anatomic Markers");
append_appearance_item(itemSliceViews, SNAPAppearanceSettings::ROI_BOX, "ROI Edges");
append_appearance_item(itemSliceViews, SNAPAppearanceSettings::PAINTBRUSH_OUTLINE, "Paintbrush");
+ append_appearance_item(itemSliceViews, SNAPAppearanceSettings::GRID_LINES, "Deformation Grid");
QStandardItem *item3DView = append_category_item(model->invisibleRootItem(), "3D View");
append_appearance_item(item3DView, SNAPAppearanceSettings::BACKGROUND_3D, "Background");
@@ -66,6 +67,10 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) :
append_appearance_item(itemPoly, SNAPAppearanceSettings::POLY_EDIT, "Outline (editing)");
append_appearance_item(itemPoly, SNAPAppearanceSettings::POLY_DRAW_CLOSE, "Completion line");
+ QStandardItem *itemReg = append_category_item(model->invisibleRootItem(), "Registration Tool");
+ append_appearance_item(itemReg, SNAPAppearanceSettings::REGISTRATION_WIDGETS, "Registration Widgets");
+ append_appearance_item(itemReg, SNAPAppearanceSettings::REGISTRATION_GRID, "Registration Grid Lines");
+
ui->treeVisualElements->setModel(model);
ui->treeVisualElements->expandAll();
diff --git a/GUI/Qt/Windows/PreferencesDialog.ui b/GUI/Qt/Windows/PreferencesDialog.ui
index bdcc537..feb9bc5 100644
--- a/GUI/Qt/Windows/PreferencesDialog.ui
+++ b/GUI/Qt/Windows/PreferencesDialog.ui
@@ -221,6 +221,12 @@
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
+ <property name="formAlignment">
+ <set>Qt::AlignHCenter|Qt::AlignTop</set>
+ </property>
+ <property name="rightMargin">
+ <number>24</number>
+ </property>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
@@ -257,7 +263,7 @@
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
- <string>Zoom Thumbnail</string>
+ <string>Zoom Thumbnail:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
diff --git a/GUI/Qt/Windows/Registration/RegistrationDialog.cxx b/GUI/Qt/Windows/Registration/RegistrationDialog.cxx
new file mode 100644
index 0000000..3b81752
--- /dev/null
+++ b/GUI/Qt/Windows/Registration/RegistrationDialog.cxx
@@ -0,0 +1,309 @@
+#include "RegistrationDialog.h"
+#include "ui_RegistrationDialog.h"
+
+#include <QMenu>
+#include <QVBoxLayout>
+#include "QtComboBoxCoupling.h"
+#include "QtCheckBoxCoupling.h"
+#include "QtLineEditCoupling.h"
+#include "QtDoubleSpinBoxCoupling.h"
+#include "QtSliderCoupling.h"
+#include "QtAbstractButtonCoupling.h"
+#include "QtWidgetArrayCoupling.h"
+#include "RegistrationModel.h"
+#include "QtWidgetActivator.h"
+#include "QtCursorOverride.h"
+#include "SimpleFileDialogWithHistory.h"
+#include "ProcessEventsITKCommand.h"
+#include "OptimizationProgressRenderer.h"
+
+#include "QtVTKRenderWindowBox.h"
+
+Q_DECLARE_METATYPE(RegistrationModel::Transformation)
+Q_DECLARE_METATYPE(RegistrationModel::SimilarityMetric)
+
+RegistrationDialog::RegistrationDialog(QWidget *parent) :
+ SNAPComponent(parent),
+ ui(new Ui::RegistrationDialog)
+{
+ ui->setupUi(this);
+
+ // Set up a menu
+ QMenu *menuMatch = new QMenu(this);
+ menuMatch->addAction(ui->actionImage_Centers);
+ menuMatch->addAction(ui->actionCenters_of_Mass);
+ menuMatch->addAction(ui->actionMoments_of_Inertia);
+ ui->btnMatchCenters->setMenu(menuMatch);
+}
+
+RegistrationDialog::~RegistrationDialog()
+{
+ delete ui;
+}
+
+
+
+void RegistrationDialog::SetModel(RegistrationModel *model)
+{
+ m_Model = model;
+
+ makeCoupling(ui->inMovingLayer, m_Model->GetMovingLayerModel());
+
+ // Manual page couplings
+ makeCoupling((QAbstractButton *) ui->btnInteractiveTool, m_Model->GetInteractiveToolModel());
+
+ makeArrayCoupling(ui->inRotX, ui->inRotY, ui->inRotZ,
+ m_Model->GetEulerAnglesModel());
+
+ makeArrayCoupling(ui->inRotXSlider, ui->inRotYSlider, ui->inRotZSlider,
+ m_Model->GetEulerAnglesModel());
+
+ makeArrayCoupling(ui->inTranX, ui->inTranY, ui->inTranZ,
+ m_Model->GetTranslationModel());
+
+ makeArrayCoupling(ui->inTranXSlider, ui->inTranYSlider, ui->inTranZSlider,
+ m_Model->GetTranslationModel());
+
+ makeArrayCoupling(ui->inScaleX, ui->inScaleY, ui->inScaleZ,
+ m_Model->GetScalingModel());
+
+ makeArrayCoupling(ui->inScaleXSlider, ui->inScaleYSlider, ui->inScaleZSlider,
+ m_Model->GetLogScalingModel());
+
+ // Automatic page couplings
+ makeCoupling(ui->inTransformation, m_Model->GetTransformationModel());
+ makeCoupling(ui->inSimilarityMetric, m_Model->GetSimilarityMetricModel());
+ makeCoupling(ui->inUseMask, m_Model->GetUseSegmentationAsMaskModel());
+
+ makeCoupling(ui->inCoarseLevel, m_Model->GetCoarsestResolutionLevelModel());
+ makeCoupling(ui->inFineLevel, m_Model->GetFinestResolutionLevelModel());
+
+ activateOnFlag(ui->inMovingLayer, m_Model,
+ RegistrationModel::UIF_MOVING_SELECTION_AVAILABLE);
+ activateOnFlag(ui->pgManual, m_Model,
+ RegistrationModel::UIF_MOVING_SELECTED);
+
+ // This command just updates the GUI after each iteration - causing the images to
+ // jitter over different iterations
+ ProcessEventsITKCommand::Pointer cmdProcEvents = ProcessEventsITKCommand::New();
+ m_Model->SetIterationCommand(cmdProcEvents);
+
+}
+
+void RegistrationDialog::on_pushButton_clicked()
+{
+ m_Model->SetCenterOfRotationToCursor();
+}
+
+void RegistrationDialog::on_pushButton_2_clicked()
+{
+ m_Model->ResetTransformToIdentity();
+}
+
+void RegistrationDialog::on_btnRunRegistration_clicked()
+{
+ // Create the render panels based on the number of iterations
+ int coarsest = m_Model->GetCoarsestResolutionLevel();
+ int finest = m_Model->GetFinestResolutionLevel();
+ int n_levels = 1 + (coarsest - finest);
+ assert(n_levels > 0);
+
+ // Delete all existing render boxes
+ QList<QtVTKRenderWindowBox *> bx = ui->grpPlots->findChildren<QtVTKRenderWindowBox*>();
+ foreach (QtVTKRenderWindowBox * w, bx)
+ delete w;
+
+ // Turn on the wait cursor
+ QtCursorOverride cursor(Qt::WaitCursor);
+
+ // Update the user interface
+ QCoreApplication::processEvents();
+
+ // Vector of renderers - so they don't disappear
+ m_PlotRenderers.clear();
+ m_PlotRenderers.resize(n_levels);
+
+ // Background color
+ QPalette pMain = ui->pgAuto->palette(), pScroll = ui->scrollPlots->palette();
+ pScroll.setBrush(QPalette::Window, pMain.background());
+ ui->scrollPlots->setPalette(pScroll);
+ ui->scrollAreaWidgetContents->setPalette(pScroll);
+
+ // Vector of box widgets
+ for(int k = 0; k < n_levels; k++)
+ {
+ // Create a new VTK box
+ QtVTKRenderWindowBox *plot = new QtVTKRenderWindowBox(NULL);
+ m_PlotRenderers[k] = OptimizationProgressRenderer::New();
+ m_PlotRenderers[k]->SetModel(m_Model);
+ m_PlotRenderers[k]->SetPyramidLevel(k);
+ m_PlotRenderers[k]->SetPyramidZoom(1 << (coarsest - k));
+ plot->SetRenderer(m_PlotRenderers[k]);
+ plot->setMinimumHeight(76);
+ plot->setMaximumHeight(76);
+
+ ui->grpPlots->layout()->addWidget(plot);
+ }
+
+ m_Model->RunAutoRegistration();
+}
+
+int RegistrationDialog::GetTransformFormat(QString &format)
+{
+ if(format == "ITK Transform Files")
+ return RegistrationModel::FORMAT_ITK;
+ else if(format == "Convert3D Transform Files")
+ return RegistrationModel::FORMAT_C3D;
+ else
+ return RegistrationModel::FORMAT_ITK;
+}
+
+void RegistrationDialog::on_btnLoad_clicked()
+{
+ // Ask for a filename
+ SimpleFileDialogWithHistory::QueryResult result =
+ SimpleFileDialogWithHistory::showOpenDialog(
+ this, m_Model->GetParent(),
+ "Open Transform - ITK-SNAP", "Transform File",
+ "AffineTransform",
+ "ITK Transform Files (*.txt);; Convert3D Transform Files (*.mat)");
+
+ RegistrationModel::TransformFormat format =
+ (RegistrationModel::TransformFormat) this->GetTransformFormat(result.activeFormat);
+
+
+ // Open
+ if(result.filename.length())
+ {
+ try
+ {
+ std::string utf = to_utf8(result.filename);
+ m_Model->LoadTransform(utf.c_str(), format);
+ }
+ catch(std::exception &exc)
+ {
+ ReportNonLethalException(this, exc, "Transform IO Error",
+ QString("Failed to load transform file"));
+ }
+ }
+}
+
+void RegistrationDialog::on_btnSave_clicked()
+{
+ // Ask for a filename
+ SimpleFileDialogWithHistory::QueryResult result =
+ SimpleFileDialogWithHistory::showSaveDialog(
+ this, m_Model->GetParent(),
+ "Save Transform - ITK-SNAP", "Transform File",
+ "AffineTransform",
+ "ITK Transform Files (*.txt);; Convert3D Transform Files (*.mat)", true);
+
+ RegistrationModel::TransformFormat format =
+ (RegistrationModel::TransformFormat) this->GetTransformFormat(result.activeFormat);
+
+ // Save
+ if(result.filename.length())
+ {
+ try
+ {
+ std::string utf = to_utf8(result.filename);
+ m_Model->SaveTransform(utf.c_str(), format);
+ }
+ catch(std::exception &exc)
+ {
+ ReportNonLethalException(this, exc, "Transform IO Error",
+ QString("Failed to save transform file"));
+ }
+ }
+}
+
+void RegistrationDialog::on_buttonBox_clicked(QAbstractButton *button)
+{
+ // Tell the model the dialog is closing
+ m_Model->OnDialogClosed();
+
+ // The only button is close
+ emit wizardFinished();
+}
+
+void RegistrationDialog::on_tabWidget_currentChanged(int index)
+{
+ // Activate the interactive tool when the user switches to the manual page
+ if(ui->tabWidget->currentWidget() == ui->pgManual)
+ m_Model->GetInteractiveToolModel()->SetValue(true);
+}
+
+#include <QDialog>
+#include <QFormLayout>
+#include <QComboBox>
+#include <QDoubleSpinBox>
+#include <QDialogButtonBox>
+
+void RegistrationDialog::on_btnReslice_clicked()
+{
+ // Prompt the user for the kind of reslicing to do
+ QDialog *dialog = new QDialog(this);
+ QFormLayout *lo = new QFormLayout();
+
+ dialog->setWindowTitle("Reslicing Options - ITK-SNAP");
+
+ // Set up interpolation options
+ QComboBox *cbInterp = new QComboBox(dialog);
+ cbInterp->addItem("Nearest Neighbor", QVariant(NEAREST_NEIGHBOR));
+ cbInterp->addItem("Linear", QVariant(TRILINEAR));
+ cbInterp->setCurrentIndex(1);
+ lo->addRow("&Interpolation:", cbInterp);
+
+ // Set up background value (currently unimplemented)
+ /*
+ QDoubleSpinBox *inBkValue = new QDoubleSpinBox(dialog);
+ inBkValue->setValue(0.0);
+ inBkValue->setToolTip(
+ "Intensity value that will be assigned to voxels that are "
+ "outside of the image domain.");
+ lo->addRow("&Background intensity:", inBkValue);
+ */
+
+ QLabel *label = new QLabel;
+ label->setText("The resliced image will be created as an addtional\n"
+ "image layer. You can save the resliced image using\n"
+ "the context menu.");
+ lo->addRow(label);
+
+ QDialogButtonBox *bbox =
+ new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(bbox, SIGNAL(accepted()), dialog, SLOT(accept()));
+ connect(bbox, SIGNAL(rejected()), dialog, SLOT(reject()));
+ lo->addRow(bbox);
+
+ dialog->setLayout(lo);
+
+
+ // Get the selected values
+ if(dialog->exec() == QDialog::Accepted)
+ {
+ bool is_linear = (cbInterp->currentText() == "Linear");
+ m_Model->ResliceMovingImage(is_linear ? TRILINEAR : NEAREST_NEIGHBOR);
+ }
+
+ delete dialog;
+}
+
+void RegistrationDialog::on_actionImage_Centers_triggered()
+{
+ m_Model->MatchImageCenters();
+}
+
+void RegistrationDialog::on_actionCenters_of_Mass_triggered()
+{
+ // Turn on the wait cursor
+ QtCursorOverride cursor(Qt::WaitCursor);
+ m_Model->MatchByMoments(1);
+}
+
+void RegistrationDialog::on_actionMoments_of_Inertia_triggered()
+{
+ // Turn on the wait cursor
+ QtCursorOverride cursor(Qt::WaitCursor);
+ m_Model->MatchByMoments(2);
+}
diff --git a/GUI/Qt/Windows/Registration/RegistrationDialog.h b/GUI/Qt/Windows/Registration/RegistrationDialog.h
new file mode 100644
index 0000000..0859724
--- /dev/null
+++ b/GUI/Qt/Windows/Registration/RegistrationDialog.h
@@ -0,0 +1,68 @@
+#ifndef REGISTRATIONDIALOG_H
+#define REGISTRATIONDIALOG_H
+
+#include <SNAPComponent.h>
+#include <SNAPCommon.h>
+
+class RegistrationModel;
+class QAbstractButton;
+class OptimizationProgressRenderer;
+
+namespace Ui {
+class RegistrationDialog;
+}
+
+class RegistrationDialog : public SNAPComponent
+{
+ Q_OBJECT
+
+public:
+ explicit RegistrationDialog(QWidget *parent = 0);
+ ~RegistrationDialog();
+
+ void SetModel(RegistrationModel *model);
+
+signals:
+
+ void wizardFinished();
+
+private slots:
+ void on_pushButton_clicked();
+
+ void on_pushButton_2_clicked();
+
+ void on_btnRunRegistration_clicked();
+
+ void on_btnLoad_clicked();
+
+ void on_btnSave_clicked();
+
+ void on_buttonBox_clicked(QAbstractButton *button);
+
+ void on_tabWidget_currentChanged(int index);
+
+ void on_btnReslice_clicked();
+
+ void on_actionImage_Centers_triggered();
+
+ void on_actionCenters_of_Mass_triggered();
+
+ void on_actionMoments_of_Inertia_triggered();
+
+private:
+ Ui::RegistrationDialog *ui;
+
+ RegistrationModel *m_Model;
+
+ int GetTransformFormat(QString &format);
+
+ // This is a bit unfortunate but we need to keep a list of renderers for the
+ // plot widgets currently shown
+ typedef SmartPtr<OptimizationProgressRenderer> RendererPtr;
+
+ std::vector<RendererPtr> m_PlotRenderers;
+
+
+};
+
+#endif // REGISTRATIONDIALOG_H
diff --git a/GUI/Qt/Windows/Registration/RegistrationDialog.ui b/GUI/Qt/Windows/Registration/RegistrationDialog.ui
new file mode 100644
index 0000000..6c1e478
--- /dev/null
+++ b/GUI/Qt/Windows/Registration/RegistrationDialog.ui
@@ -0,0 +1,931 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RegistrationDialog</class>
+ <widget class="QWidget" name="RegistrationDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>192</width>
+ <height>762</height>
+ </rect>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>192</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">* {
+font-size: 12px;
+}
+QSpinBox {
+font-size: 11px;
+}
+QDoubleSpinBox {
+font-size: 11px;
+}
+
+
+QToolButton::checked {
+ background-color:rgb(210, 210, 210);
+}
+
+QToolButton::pressed {
+ background-color:rgb(210, 210, 210);
+}
+</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Moving image layer:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="inMovingLayer"/>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="styleSheet">
+ <string notr="true">QDoubleSpinBox {
+ font-size: 10px;
+}
+</string>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="pgAuto">
+ <attribute name="title">
+ <string>Automatic</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0,0,0,0,1">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Transformation model:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="inTransformation"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Image similarity metric:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="inSimilarityMetric"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="inUseMask">
+ <property name="toolTip">
+ <string><html><head/><body><p>When this is checked, the current segmentation in ITK-SNAP will be used as a mask for the registration. This means that the similarity between the main image and the moving image layer will only be computed at the voxels assigned a segmentation label.</p></body></html></string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">font-size:11px;</string>
+ </property>
+ <property name="text">
+ <string>Use segmentation as mask</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>12</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="styleSheet">
+ <string notr="true">QLabel {
+ font-size:11px;
+}
+
+QComboBox {
+ font-size:11px;
+}</string>
+ </property>
+ <property name="title">
+ <string>Multi-resolution schedule:</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::FieldsStayAtSizeHint</enum>
+ </property>
+ <property name="horizontalSpacing">
+ <number>4</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>20</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Coarsest level:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Finest level:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="inCoarseLevel">
+ <property name="maximumSize">
+ <size>
+ <width>48</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>Set the zoom factor used for the <span style=" font-weight:600;">first</span> stage of automatic segmentation. We recommend <span style=" font-weight:600;">8x</span> for images on the scale of 256x256x256. </p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="inFineLevel">
+ <property name="maximumSize">
+ <size>
+ <width>48</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>Set the zoom factor used for the <span style=" font-weight:600;">last</span> stage of automatic segmentation. We recommend <span style=" font-weight:600;">4x</span> for images on the scale of 256x256x256. </p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnRunRegistration">
+ <property name="toolTip">
+ <string><html><head/><body><p>Run automatic registration</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Run Registration</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QScrollArea" name="scrollPlots">
+ <property name="styleSheet">
+ <string notr="true">background-color: transparent;</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>161</width>
+ <height>214</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="grpPlots" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="pgManual">
+ <attribute name="title">
+ <string>Manual</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="grpRotation">
+ <property name="styleSheet">
+ <string notr="true">QLabel {
+ font-size: 11px;
+}</string>
+ </property>
+ <property name="title">
+ <string>Rotation (Euler angles):</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>2</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="widget_2" native="true">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>2</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>4</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>x:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="inRotX">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QDoubleSlider" name="inRotXSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="inRotY">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QDoubleSlider" name="inRotYSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="inRotZ">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QDoubleSlider" name="inRotZSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="grpTranslation">
+ <property name="styleSheet">
+ <string notr="true">QLabel {
+ font-size: 11px;
+}</string>
+ </property>
+ <property name="title">
+ <string>Translation (mm):</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>2</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="widget_3" native="true">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>2</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>4</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>x:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="inTranX">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QDoubleSlider" name="inTranXSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="inTranY">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QDoubleSlider" name="inTranYSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="inTranZ">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QDoubleSlider" name="inTranZSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="grpScaling">
+ <property name="styleSheet">
+ <string notr="true">QLabel {
+ font-size: 11px;
+}</string>
+ </property>
+ <property name="title">
+ <string>Scaling:</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>2</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="widget_4" native="true">
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>2</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>4</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>x:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QDoubleSpinBox" name="inScaleX">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QDoubleSlider" name="inScaleXSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>y:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="inScaleY">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QDoubleSlider" name="inScaleYSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>z:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="inScaleZ">
+ <property name="maximumSize">
+ <size>
+ <width>72</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QDoubleSlider" name="inScaleZSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnInteractiveTool">
+ <property name="text">
+ <string>Interactive Tool</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton_2">
+ <property name="text">
+ <string>Reset to Identity</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButton">
+ <property name="text">
+ <string>Set Center of Rotation</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnMatchCenters">
+ <property name="text">
+ <string>Match by ...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>2</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="btnLoad">
+ <property name="toolTip">
+ <string><html><head/><body><p>Load transformation from file</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../Resources/SNAPResources.qrc">
+ <normaloff>:/root/open_22.png</normaloff>:/root/open_22.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="btnSave">
+ <property name="toolTip">
+ <string><html><head/><body><p>Save transformation to file</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../Resources/SNAPResources.qrc">
+ <normaloff>:/root/save_22.png</normaloff>:/root/save_22.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>4</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="btnReslice">
+ <property name="toolTip">
+ <string><html><head/><body><p>Reslice the moving image into the space of the main image</p></body></html></string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../Resources/SNAPResources.qrc">
+ <normaloff>:/root/reslice_16.png</normaloff>:/root/reslice_16.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <action name="actionImage_Centers">
+ <property name="text">
+ <string>Image Centers</string>
+ </property>
+ <property name="toolTip">
+ <string>Aligns the center voxel of the moving image with the center voxel of the fixed image</string>
+ </property>
+ </action>
+ <action name="actionCenters_of_Mass">
+ <property name="text">
+ <string>Centers of Mass</string>
+ </property>
+ <property name="toolTip">
+ <string>Aligns the center of mass of the moving image with the center of mass of the fixed image</string>
+ </property>
+ </action>
+ <action name="actionMoments_of_Inertia">
+ <property name="text">
+ <string>Moments of Inertia</string>
+ </property>
+ <property name="toolTip">
+ <string>Aligns the centers of mass and the second moments of inertia tensors between fixed and moving images. This involves rotation, translation and flipping.</string>
+ </property>
+ </action>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QDoubleSlider</class>
+ <extends>QSlider</extends>
+ <header location="global">QDoubleSlider.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../../Resources/SNAPResources.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/GUI/Qt/Windows/ReorientImageDialog.ui b/GUI/Qt/Windows/ReorientImageDialog.ui
index ebcd1fc..7737170 100644
--- a/GUI/Qt/Windows/ReorientImageDialog.ui
+++ b/GUI/Qt/Windows/ReorientImageDialog.ui
@@ -30,7 +30,16 @@ QTableWidget {
<property name="spacing">
<number>3</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -73,7 +82,16 @@ font: 11px;</string>
<property name="spacing">
<number>4</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -122,7 +140,16 @@ font: 11px;</string>
<property name="spacing">
<number>4</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -171,7 +198,16 @@ font: 11px;</string>
<property name="spacing">
<number>4</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -246,7 +282,16 @@ font: 11px;</string>
<item row="6" column="2">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -385,7 +430,16 @@ font: 11px;</string>
<item row="6" column="1">
<widget class="QWidget" name="widget_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -480,7 +534,16 @@ font: 11px;</string>
<property name="spacing">
<number>0</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -512,7 +575,16 @@ font: 11px;</string>
<property name="spacing">
<number>0</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
<item>
diff --git a/GUI/Qt/Windows/ResampleDialog.cxx b/GUI/Qt/Windows/ResampleDialog.cxx
index c25d8e1..dac33be 100644
--- a/GUI/Qt/Windows/ResampleDialog.cxx
+++ b/GUI/Qt/Windows/ResampleDialog.cxx
@@ -8,7 +8,7 @@
#include "QtComboBoxCoupling.h"
#include <QMenu>
-Q_DECLARE_METATYPE(SNAPSegmentationROISettings::InterpolationMethod)
+Q_DECLARE_METATYPE(InterpolationMethod)
ResampleDialog::ResampleDialog(QWidget *parent) :
QDialog(parent),
diff --git a/GUI/Qt/Windows/SaveModifiedLayersDialog.cxx b/GUI/Qt/Windows/SaveModifiedLayersDialog.cxx
index 53000f0..bc14407 100644
--- a/GUI/Qt/Windows/SaveModifiedLayersDialog.cxx
+++ b/GUI/Qt/Windows/SaveModifiedLayersDialog.cxx
@@ -121,7 +121,7 @@ bool SaveModifiedLayersDialog
// Create and configure the model
SmartPtr<SaveModifiedLayersModel> saveModel = SaveModifiedLayersModel::New();
- saveModel->Initialize(model, layers);
+ saveModel->Initialize(model, layers, opts.testFlag(ProjectsDisabled));
saveModel->SetUIDelegate(&cb_delegate);
// Check if there is anything to save
diff --git a/GUI/Qt/Windows/SimpleFileDialogWithHistory.cxx b/GUI/Qt/Windows/SimpleFileDialogWithHistory.cxx
index b6bee0f..738d71d 100644
--- a/GUI/Qt/Windows/SimpleFileDialogWithHistory.cxx
+++ b/GUI/Qt/Windows/SimpleFileDialogWithHistory.cxx
@@ -25,7 +25,7 @@ SimpleFileDialogWithHistory::~SimpleFileDialogWithHistory()
delete ui;
}
-QString
+SimpleFileDialogWithHistory::QueryResult
SimpleFileDialogWithHistory
::showOpenDialog(QWidget *parent,
GlobalUIModel *model,
@@ -34,6 +34,8 @@ SimpleFileDialogWithHistory
QString history_name,
QString file_pattern, QString init_file)
{
+ QueryResult result;
+
// Configure the dialog
SimpleFileDialogWithHistory *dialog = new SimpleFileDialogWithHistory(parent);
dialog->ui->filePanel->initializeForOpenFile(model, file_title, history_name, file_pattern, init_file);
@@ -42,22 +44,25 @@ SimpleFileDialogWithHistory
// Launch the dialog
if(dialog->exec() == QDialog::Accepted)
{
- return dialog->ui->filePanel->absoluteFilename();
+ result.filename = dialog->ui->filePanel->absoluteFilename();
+ result.activeFormat = dialog->ui->filePanel->activeFormat();
}
- else return QString();
+
+ return result;
}
#include <QMessageBox>
-QString
-SimpleFileDialogWithHistory
-::showSaveDialog(QWidget *parent, GlobalUIModel *model,
+SimpleFileDialogWithHistory::QueryResult
+SimpleFileDialogWithHistory::showSaveDialog(QWidget *parent, GlobalUIModel *model,
QString window_title,
QString file_title,
QString history_name,
QString file_pattern, bool force_extension,
QString init_file)
{
+ QueryResult result;
+
// Configure the dialog
SimpleFileDialogWithHistory *dialog = new SimpleFileDialogWithHistory(parent);
dialog->setWindowTitle(window_title);
@@ -67,9 +72,11 @@ SimpleFileDialogWithHistory
// Launch the dialog
if(dialog->exec() == QDialog::Accepted)
{
- return dialog->ui->filePanel->absoluteFilename();
+ result.filename = dialog->ui->filePanel->absoluteFilename();
+ result.activeFormat = dialog->ui->filePanel->activeFormat();
}
- else return QString();
+
+ return result;
}
diff --git a/GUI/Qt/Windows/SimpleFileDialogWithHistory.h b/GUI/Qt/Windows/SimpleFileDialogWithHistory.h
index f242ed7..938b0b9 100644
--- a/GUI/Qt/Windows/SimpleFileDialogWithHistory.h
+++ b/GUI/Qt/Windows/SimpleFileDialogWithHistory.h
@@ -15,7 +15,13 @@ class SimpleFileDialogWithHistory : public QDialog
public:
- static QString showOpenDialog(
+ struct QueryResult
+ {
+ QString filename;
+ QString activeFormat;
+ };
+
+ static QueryResult showOpenDialog(
QWidget *parent,
GlobalUIModel *model,
QString window_title,
@@ -24,7 +30,7 @@ public:
QString file_pattern,
QString init_file = QString());
- static QString showSaveDialog(
+ static QueryResult showSaveDialog(
QWidget *parent,
GlobalUIModel *model,
QString window_title, QString file_title,
diff --git a/GUI/Qt/Windows/SpeedImageDialog.cxx b/GUI/Qt/Windows/SpeedImageDialog.cxx
index 8e392ec..d40594e 100644
--- a/GUI/Qt/Windows/SpeedImageDialog.cxx
+++ b/GUI/Qt/Windows/SpeedImageDialog.cxx
@@ -92,8 +92,11 @@ void SpeedImageDialog::SetModel(SnakeWizardModel *model)
// Couple the edge preprocessing widgets
makeCoupling(ui->inEdgeSmoothing, model->GetEdgePreprocessingSigmaModel());
+ makeCoupling(ui->inEdgeSmoothingSlider, model->GetEdgePreprocessingSigmaModel());
makeCoupling(ui->inEdgeKappa, model->GetEdgePreprocessingKappaModel());
+ makeCoupling(ui->inEdgeKappaSlider, model->GetEdgePreprocessingKappaModel());
makeCoupling(ui->inEdgeExponent, model->GetEdgePreprocessingExponentModel());
+ makeCoupling(ui->inEdgeExponentSlider, model->GetEdgePreprocessingExponentModel());
// Couple the clustering widgets
makeCoupling(ui->inNumClusters, model->GetNumberOfClustersModel());
@@ -174,9 +177,11 @@ void SpeedImageDialog::on_btnClassifyLoad_clicked()
// Create a model for IO
SmartPtr<LoadSegmentationImageDelegate> delegate = LoadSegmentationImageDelegate::New();
delegate->Initialize(m_Model->GetParent()->GetDriver());
+ delegate->SetHistoryName("ClassifierSamples");
+ delegate->SetDisplayName("Classifier Samples Image");
SmartPtr<ImageIOWizardModel> model = ImageIOWizardModel::New();
- model->InitializeForLoad(m_Model->GetParent(), delegate, "ClassifierSamples", "Classifier Samples Image");
+ model->InitializeForLoad(m_Model->GetParent(), delegate);
// Execute the IO wizard
ImageIOWizard wiz(this);
diff --git a/GUI/Qt/Windows/SpeedImageDialog.ui b/GUI/Qt/Windows/SpeedImageDialog.ui
index 4afc907..a325185 100644
--- a/GUI/Qt/Windows/SpeedImageDialog.ui
+++ b/GUI/Qt/Windows/SpeedImageDialog.ui
@@ -42,7 +42,7 @@ QGroupBox::title {
<item>
<widget class="QTabWidget" name="tabMode">
<property name="currentIndex">
- <number>1</number>
+ <number>0</number>
</property>
<widget class="QWidget" name="tabThreshold">
<attribute name="title">
@@ -1304,6 +1304,25 @@ color: rgb(64, 64, 64);</string>
<property name="verticalSpacing">
<number>2</number>
</property>
+ <item row="1" column="0">
+ <widget class="QtSimpleOpenGLBox" name="viewEdgeMapping" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>160</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color:white;</string>
+ </property>
+ </widget>
+ </item>
<item row="2" column="0">
<spacer name="verticalSpacer_8">
<property name="orientation">
@@ -1320,10 +1339,10 @@ color: rgb(64, 64, 64);</string>
</property>
</spacer>
</item>
- <item row="7" column="0">
- <widget class="QDoubleSliderWithEditor" name="inEdgeKappa">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Edge transformation exponent:</string>
</property>
</widget>
</item>
@@ -1350,32 +1369,6 @@ color: rgb(64, 64, 64);</string>
</property>
</widget>
</item>
- <item row="9" column="0">
- <widget class="QLabel" name="label_9">
- <property name="text">
- <string>Edge transformation exponent:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QtSimpleOpenGLBox" name="viewEdgeMapping" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>160</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">background-color:white;</string>
- </property>
- </widget>
- </item>
<item row="11" column="0">
<spacer name="verticalSpacer_6">
<property name="orientation">
@@ -1389,13 +1382,6 @@ color: rgb(64, 64, 64);</string>
</property>
</spacer>
</item>
- <item row="10" column="0">
- <widget class="QDoubleSliderWithEditor" name="inEdgeExponent">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
<item row="5" column="0">
<spacer name="verticalSpacer_9">
<property name="orientation">
@@ -1419,13 +1405,6 @@ color: rgb(64, 64, 64);</string>
</property>
</widget>
</item>
- <item row="4" column="0">
- <widget class="QDoubleSliderWithEditor" name="inEdgeSmoothing">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
@@ -1433,6 +1412,102 @@ color: rgb(64, 64, 64);</string>
</property>
</widget>
</item>
+ <item row="4" column="0">
+ <widget class="QWidget" name="widget_12" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QDoubleSpinBox" name="inEdgeSmoothing">
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSlider" name="inEdgeSmoothingSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="10" column="0">
+ <widget class="QWidget" name="widget_14" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QDoubleSpinBox" name="inEdgeExponent">
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSlider" name="inEdgeExponentSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QWidget" name="widget_13" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QDoubleSpinBox" name="inEdgeKappa">
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSlider" name="inEdgeKappaSlider">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
</widget>
</widget>
@@ -1594,12 +1669,6 @@ color: rgb(64, 64, 64);</string>
<header location="global">QDoubleSlider.h</header>
</customwidget>
<customwidget>
- <class>QtSimpleOpenGLBox</class>
- <extends>QWidget</extends>
- <header location="global">QtSimpleOpenGLBox.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
<class>QDoubleSliderWithEditor</class>
<extends>QSlider</extends>
<header location="global">QDoubleSliderWithEditor.h</header>
@@ -1610,6 +1679,12 @@ color: rgb(64, 64, 64);</string>
<header location="global">QtVTKRenderWindowBox.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>QtSimpleOpenGLBox</class>
+ <extends>QWidget</extends>
+ <header location="global">QtSimpleOpenGLBox.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources>
<include location="../Resources/SNAPResources.qrc"/>
diff --git a/GUI/Qt/Windows/SplashPanel.ui b/GUI/Qt/Windows/SplashPanel.ui
index 3cc6672..ad8e46c 100644
--- a/GUI/Qt/Windows/SplashPanel.ui
+++ b/GUI/Qt/Windows/SplashPanel.ui
@@ -107,7 +107,7 @@ Jan 12, 2013</string>
color:rgb(109, 109, 109);</string>
</property>
<property name="text">
- <string>Copyright (C) 1998-2015
+ <string>Copyright (C) 1998-2017
Paul A. Yushkevich
Guido Gerig</string>
</property>
diff --git a/GUI/Qt/main.cxx b/GUI/Qt/main.cxx
index 6e09756..4acfa26 100644
--- a/GUI/Qt/main.cxx
+++ b/GUI/Qt/main.cxx
@@ -1,7 +1,20 @@
#include <QApplication>
#include <QSettings>
+#include <QAction>
+#include <QShortcutEvent>
+#include <QStyleFactory>
+#include <QUrl>
+#include <QDir>
+#include <QFileSystemWatcher>
+
+#if QT_VERSION > 0x050000
+#include <QSurfaceFormat>
+#endif
+
+#include "SNAPQApplication.h"
#include "MainImageWindow.h"
#include "SliceViewPanel.h"
+#include "ImageIODelegates.h"
#include "IRISException.h"
#include "IRISApplication.h"
#include "SNAPAppearanceSettings.h"
@@ -12,6 +25,8 @@
#include "QtIPCManager.h"
#include "QtCursorOverride.h"
#include "SNAPQtCommon.h"
+#include "SNAPTestQt.h"
+#include "TestOpenGLDialog.h"
#include "GenericSliceView.h"
#include "GenericSliceModel.h"
@@ -23,14 +38,9 @@
#include "itkCommand.h"
#include "vtkObject.h"
-#include <QStyleFactory>
-#include <QAction>
-#include <QUrl>
-
-#include "ImageIODelegates.h"
-
#include <iostream>
-#include "SNAPTestQt.h"
+#include <clocale>
+#include <cstdlib>
using namespace std;
@@ -60,6 +70,7 @@ void SetupSignalHandlers()
signal(SIGSEGV, SegmentationFaultHandler);
}
+
#else
void SetupSignalHandlers()
@@ -70,96 +81,46 @@ void SetupSignalHandlers()
#endif
-/*
- * Code to handle forking the application on startup. Forking is desirable
- * because many users execute SNAP from command line, and it is annoying to
- * have SNAP blocking the command line. Also, this reduced the frequency
- * of users interrupting SNAP or killing it by closing the terminal.
- */
-#include <QFileOpenEvent>
-#include <QTime>
-#include <QMessageBox>
+// Setting environment variables
-/** Class to handle exceptions in Qt callbacks */
-class SNAPQApplication : public QApplication
-{
-public:
- SNAPQApplication(int &argc, char **argv) :
- QApplication(argc, argv)
- {
- this->setApplicationName("ITK-SNAP");
- this->setOrganizationName("itksnap.org");
+#ifdef WIN32
-#if QT_VERSION >= 0x050000
- // Allow @x2 pixmaps for icons for retina displays
- this->setAttribute(Qt::AA_UseHighDpiPixmaps, true);
-#endif
+template<typename TVal>
+void itksnap_putenv(const std::string &var, TVal value)
+{
+ std::ostringstream s;
+ s << var << "=" << value;
+ _putenv(s.str().c_str());
+}
- m_MainWindow = NULL;
+#else
- // Store the command-line arguments
- for(int i = 1; i < argc; i++)
- m_Args.push_back(QString::fromUtf8(argv[i]));
- }
+template<typename TVal>
+void itksnap_putenv(const std::string &var, TVal value)
+{
+ std::ostringstream s;
+ s << value;
+ setenv(var.c_str(), s.str().c_str(), 1);
+}
- void setMainWindow(MainImageWindow *mainwin)
- {
- m_MainWindow = mainwin;
- m_StartupTime = QTime::currentTime();
- }
+#endif
- bool notify(QObject *object, QEvent *event)
- {
- try { return QApplication::notify(object, event); }
- catch(std::exception &exc)
- {
- // Crash!
- ReportNonLethalException(NULL, exc, "Unexpected Error",
- "ITK-SNAP has crashed due to an unexpected error");
- // Exit the application
- QApplication::exit(-1);
- return false;
- }
- }
+/*
+ * Code to handle forking the application on startup. Forking is desirable
+ * because many users execute SNAP from command line, and it is annoying to
+ * have SNAP blocking the command line. Also, this reduced the frequency
+ * of users interrupting SNAP or killing it by closing the terminal.
+ */
- virtual bool event(QEvent *event)
- {
- if (event->type() == QEvent::FileOpen && m_MainWindow)
- {
- QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
- QString file = openEvent->url().path();
-
- // MacOS bug - we get these open document events automatically generated
- // from command-line parameters, and I have no idea why. To avoid this,
- // if the event occurs at startup (within a second), we will check if
- // the passed in URL matches the command-line arguments, and ignore it
- // if it does
- if(m_StartupTime.secsTo(QTime::currentTime()) < 1)
- {
- foreach(const QString &arg, m_Args)
- {
- if(arg == file)
- return true;
- }
- }
+#include <QFileOpenEvent>
+#include <QTime>
+#include <QMessageBox>
- // Ok, we passed the check, now it's safe to actually open the file
- m_MainWindow->raise();
- m_MainWindow->LoadDroppedFile(file);
- return true;
- }
- else return QApplication::event(event);
- }
-private:
- MainImageWindow *m_MainWindow;
- QStringList m_Args;
- QTime m_StartupTime;
-};
#ifdef SNAP_DEBUG_EVENTS
@@ -182,6 +143,8 @@ void usage(const char *progname)
cout << "Additional Options:" << endl;
cout << " -z FACTOR : Specify initial zoom in screen pixels/mm" << endl;
cout << " --cwd PATH : Start with PATH as the initial directory" << endl;
+ cout << " --threads N : Limit maximum number of CPU cores used to N." << endl;
+ cout << " --scale N : Scale all GUI elements by factor of N (e.g., 2)." << endl;
cout << "Debugging/Testing Options:" << endl;
#ifdef SNAP_DEBUG_EVENTS
cout << " --debug-events : Dump information regarding UI events" << endl;
@@ -190,6 +153,9 @@ void usage(const char *progname)
cout << " --test TESTID : Execute a test. " << endl;
cout << " --testdir DIR : Set the root directory for tests. " << endl;
cout << " --testacc factor : Adjust the interval between test commands by factor (e.g., 0.5). " << endl;
+ cout << " --css file : Read stylesheet from file." << endl;
+ cout << " --opengl MAJOR MINOR : Set the OpenGL major and minor version. Experimental." << endl;
+ cout << " --testgl : Diagnose OpenGL/VTK issues." << endl;
cout << "Platform-Specific Options:" << endl;
#if QT_VERSION < 0x050000
#ifdef Q_WS_X11
@@ -234,17 +200,29 @@ public:
std::string cwd;
// GUI related
- std::string style;
+ std::string style, cssfile;
+
+ // OpenGL version preferred
+ int opengl_major, opengl_minor;
+ bool flagTestOpenGL;
+
+ // Number of threads
+ int nThreads;
+
+ // GUI scaling
+ int nDevicePixelRatio;
CommandLineRequest()
: flagDebugEvents(false), flagNoFork(false), flagConsole(false), xZoomFactor(0.0),
- flagX11DoubleBuffer(false)
+ flagX11DoubleBuffer(false), nThreads(0), nDevicePixelRatio(0), flagTestOpenGL(false)
{
#if QT_VERSION >= 0x050000
style = "fusion";
#else
style = "plastique";
#endif
+ opengl_major = 1;
+ opengl_minor = 3;
}
};
@@ -346,6 +324,10 @@ int parse(int argc, char *argv[], CommandLineRequest &argdata)
parser.AddOption("--testdir", 1);
parser.AddOption("--testacc", 1);
+ // Restrict number of threads
+ // TODO: use and document this
+ parser.AddOption("--threads", 1);
+
// Current working directory
parser.AddOption("--cwd", 1);
@@ -359,6 +341,14 @@ int parse(int argc, char *argv[], CommandLineRequest &argdata)
parser.AddOption("--x11-db",0);
+ parser.AddOption("--css", 1);
+
+ parser.AddOption("--scale", 1);
+
+ parser.AddOption("--opengl", 2);
+
+ parser.AddOption("--testgl", 0);
+
// Obtain the result
CommandLineArgumentParseResult parseResult;
@@ -516,16 +506,41 @@ int parse(int argc, char *argv[], CommandLineRequest &argdata)
if(parseResult.IsOptionPresent("--style"))
argdata.style = parseResult.GetOptionParameter("--style");
+ if(parseResult.IsOptionPresent("--css"))
+ argdata.cssfile = parseResult.GetOptionParameter("--css");
+
+ if(parseResult.IsOptionPresent("--opengl"))
+ {
+ argdata.opengl_major = atoi(parseResult.GetOptionParameter("--opengl", 0));
+ argdata.opengl_minor = atoi(parseResult.GetOptionParameter("--opengl", 1));
+ }
+
+ if(parseResult.IsOptionPresent("--testgl"))
+ argdata.flagTestOpenGL = true;
+
+
// Enable double buffering on X11
- if(parseResult.IsOptionPresent("x11-db"))
+ if(parseResult.IsOptionPresent("--x11-db"))
argdata.flagX11DoubleBuffer = true;
+ // Number of threads
+ if(parseResult.IsOptionPresent("--threads"))
+ argdata.nThreads = atoi(parseResult.GetOptionParameter("--threads"));
+
+ // Number of threads
+ if(parseResult.IsOptionPresent("--scale"))
+ argdata.nDevicePixelRatio = atoi(parseResult.GetOptionParameter("--scale"));
+
return 0;
}
-
-#include <QDir>
+int test_opengl()
+{
+ TestOpenGLDialog *dialog = new TestOpenGLDialog();
+ dialog->show();
+ return QApplication::exec();
+}
int main(int argc, char *argv[])
{
@@ -550,6 +565,66 @@ int main(int argc, char *argv[])
// if(argdata.flagNoFork)
// sleep(60);
+
+
+#if QT_VERSION > 0x050000
+
+ // Starting with Qt 5.6, the OpenGL implementation uses OpenGL 2.0
+ // In this version of OpenGL, transparency is handled differently and
+ // looks wrong.
+ QSurfaceFormat gl_fmt;
+ gl_fmt.setMajorVersion(argdata.opengl_major);
+ gl_fmt.setMinorVersion(argdata.opengl_minor);
+ /*
+ gl_fmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
+ gl_fmt.setRedBufferSize(1);
+ gl_fmt.setGreenBufferSize(1);
+ gl_fmt.setBlueBufferSize(1);
+ gl_fmt.setDepthBufferSize(1);
+ gl_fmt.setStencilBufferSize(0);
+ gl_fmt.setAlphaBufferSize(0);
+ */
+
+ QSurfaceFormat::setDefaultFormat(gl_fmt);
+
+#endif
+
+#if QT_VERSION > 0x050400
+
+ // Environment variable for the scale factor
+ const char *QT_SCALE_FACTOR = "QT_SCALE_FACTOR";
+ const char *QT_SCALE_AUTO_VAR = "QT_AUTO_SCREEN_SCALE_FACTOR";
+ const char *QT_SCALE_AUTO_VALUE = "1";
+
+#else
+
+ const char *QT_SCALE_FACTOR = "QT_DEVICE_PIXEL_RATIO";
+ const char *QT_SCALE_AUTO_VAR = "QT_DEVICE_PIXEL_RATIO";
+ const char *QT_SCALE_AUTO_VALUE = "auto";
+
+#endif
+
+ /* -----------------------------
+ * DEAL WITH PIXEL RATIO SCALING
+ * ----------------------------- */
+
+ // Read the pixel ratio from environment or command line
+ int devicePixelRatio = 0;
+ if(argdata.nDevicePixelRatio > 0)
+ devicePixelRatio = argdata.nDevicePixelRatio;
+ else if(getenv("ITKSNAP_SCALE_FACTOR"))
+ devicePixelRatio = atoi(getenv("ITKSNAP_SCALE_FACTOR"));
+
+ // Set the environment variable
+ if(devicePixelRatio > 0)
+ {
+ itksnap_putenv(QT_SCALE_FACTOR,devicePixelRatio);
+ }
+ else
+ {
+ itksnap_putenv(QT_SCALE_AUTO_VAR, QT_SCALE_AUTO_VALUE);
+ }
+
// Turn off event debugging if needed
#ifdef SNAP_DEBUG_EVENTS
flag_snap_debug_events = argdata.flagDebugEvents;
@@ -558,6 +633,10 @@ int main(int argc, char *argv[])
// Setup crash signal handlers
SetupSignalHandlers();
+ // Deal with threads
+ if(argdata.nThreads > 0)
+ itk::MultiThreader::SetGlobalMaximumNumberOfThreads(argdata.nThreads);
+
// Turn off ITK and VTK warning windows
itk::Object::GlobalWarningDisplayOff();
vtkObject::GlobalWarningDisplayOff();
@@ -570,11 +649,16 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(SNAPResources);
Q_INIT_RESOURCE(TestingScripts);
+
+ // Reset the locale to posix to avoid weird issues with NRRD files
+ std::setlocale(LC_NUMERIC, "POSIX");
+
// Force use of native OpenGL, since all of our functions and VTK use native
// and cannot use ANGLE
// TODO: we haven't proven that this actually helps with anything so hold off..
// app.setAttribute(Qt::AA_UseDesktopOpenGL);
+
// Set the application style
app.setStyle(QStyleFactory::create(argdata.style.c_str()));
if(argdata.style != "fusion")
@@ -584,6 +668,12 @@ int main(int argc, char *argv[])
app.setPalette(fpal);
}
+ // Test OpenGL?
+ if(argdata.flagTestOpenGL)
+ {
+ return test_opengl();
+ }
+
// Before we can create any of the framework classes, we need to get some
// platform-specific functionality to the SystemInterface
QtSystemInfoDelegate siDelegate;
@@ -631,6 +721,15 @@ int main(int argc, char *argv[])
MainImageWindow *mainwin = new MainImageWindow();
mainwin->Initialize(gui);
+ // Load stylesheet
+ if(argdata.cssfile.size())
+ {
+ QFileSystemWatcher *watcher = new QFileSystemWatcher(mainwin);
+ watcher->addPath(from_utf8(argdata.cssfile));
+ QObject::connect(watcher, SIGNAL(fileChanged(QString)),
+ mainwin, SLOT(externalStyleSheetFileChanged(QString)));
+ }
+
// Disable double buffering in X11 to avoid flickering issues. The documentation
// says this only happens on X11. For the time being, we are only implementing this
// for Qt4 and X11
diff --git a/GUI/Renderer/AnnotationRenderer.cxx b/GUI/Renderer/AnnotationRenderer.cxx
index c7179e0..6217fd7 100644
--- a/GUI/Renderer/AnnotationRenderer.cxx
+++ b/GUI/Renderer/AnnotationRenderer.cxx
@@ -8,8 +8,8 @@
#include "ImageAnnotationData.h"
#include <iomanip>
-void AnnotationRenderer::DrawLineLength(const Vector3f &xSlice1,
- const Vector3f &xSlice2,
+void AnnotationRenderer::DrawLineLength(const Vector3d &xSlice1,
+ const Vector3d &xSlice2,
const Vector3d &color,
double alpha)
{
@@ -20,13 +20,13 @@ void AnnotationRenderer::DrawLineLength(const Vector3f &xSlice1,
int vppr = m_ParentRenderer->GetModel()->GetSizeReporter()->GetViewportPixelRatio();
// Shared settings for text drawing
- Vector3f text_offset_slice =
+ Vector3d text_offset_slice =
m_Model->GetParent()->MapWindowOffsetToSliceOffset(
- Vector2f(5.f, 5.f) * (float) vppr);
+ Vector2d(5.0, 5.0) * (double) vppr);
- Vector3f text_width_slice =
+ Vector3d text_width_slice =
m_Model->GetParent()->MapWindowOffsetToSliceOffset(
- Vector2f(96.f, 12.f) * (float) vppr);
+ Vector2d(96., 12.) * (double) vppr);
// TODO: support other units
std::ostringstream oss_length;
@@ -38,7 +38,7 @@ void AnnotationRenderer::DrawLineLength(const Vector3f &xSlice1,
12 * vppr,
false };
- Vector3f curr_center = (xSlice1 + xSlice2) * 0.5f;
+ Vector3d curr_center = (xSlice1 + xSlice2) * 0.5;
// Draw the length text
m_PlatformSupport->RenderTextInOpenGL(
@@ -76,13 +76,13 @@ void AnnotationRenderer::paintGL()
int vppr = m_ParentRenderer->GetModel()->GetSizeReporter()->GetViewportPixelRatio();
// Shared settings for text drawing
- Vector3f text_offset_slice =
+ Vector3d text_offset_slice =
m_Model->GetParent()->MapWindowOffsetToSliceOffset(
- Vector2f(5.f, 5.f) * (float) vppr);
+ Vector2d(5 * vppr, 5 * vppr));
- Vector3f text_width_slice =
+ Vector3d text_width_slice =
m_Model->GetParent()->MapWindowOffsetToSliceOffset(
- Vector2f(96.f, 12.f) * (float) vppr);
+ Vector2d(96 * vppr , 12 * vppr));
// Get the list of annotations
const ImageAnnotationData *adata = m_Model->GetAnnotations();
@@ -154,8 +154,8 @@ void AnnotationRenderer::paintGL()
if(lsa)
{
// Draw the line
- Vector3f p1 = m_Model->GetParent()->MapImageToSlice(lsa->GetSegment().first);
- Vector3f p2 = m_Model->GetParent()->MapImageToSlice(lsa->GetSegment().second);
+ Vector3d p1 = m_Model->GetParent()->MapImageToSlice(lsa->GetSegment().first);
+ Vector3d p2 = m_Model->GetParent()->MapImageToSlice(lsa->GetSegment().second);
glColor4d(lsa->GetColor()[0], lsa->GetColor()[1], lsa->GetColor()[2], alpha);
@@ -183,7 +183,7 @@ void AnnotationRenderer::paintGL()
std::ostringstream oss_angle;
oss_angle << std::setprecision(3) << angle << "°";
- Vector3f line_center = m_Model->GetAnnotationCenter(lsa);
+ Vector3d line_center = m_Model->GetAnnotationCenter(lsa);
// Set up the rendering properties
AbstractRendererPlatformSupport::FontInfo font_info =
@@ -211,7 +211,7 @@ void AnnotationRenderer::paintGL()
if(lma)
{
// Get the head and tail coordinate in slice units
- Vector3f xHeadSlice, xTailSlice;
+ Vector3d xHeadSlice, xTailSlice;
m_Model->GetLandmarkArrowPoints(lma->GetLandmark(), xHeadSlice, xTailSlice);
std::string text = lma->GetLandmark().Text;
@@ -236,12 +236,12 @@ void AnnotationRenderer::paintGL()
fi.bold = false;
// Text box size in screen pixels
- Vector2f xTextSizeWin;
+ Vector2d xTextSizeWin;
xTextSizeWin[0] = this->m_PlatformSupport->MeasureTextWidth(text.c_str(), fi);
xTextSizeWin[1] = fi.pixel_size * vppr;
// Text box size in slice coordinate units
- Vector3f xTextSizeSlice = m_Model->GetParent()->MapWindowOffsetToSliceOffset(xTextSizeWin);
+ Vector3d xTextSizeSlice = m_Model->GetParent()->MapWindowOffsetToSliceOffset(xTextSizeWin);
// How to position the text
double xbox, ybox;
@@ -291,11 +291,11 @@ void AnnotationRenderer::paintGL()
glPopAttrib();
}
-void AnnotationRenderer::DrawSelectionHandle(const Vector3f &xSlice)
+void AnnotationRenderer::DrawSelectionHandle(const Vector3d &xSlice)
{
// Determine the width of the line
- float radius = 4 * m_Model->GetParent()->GetSizeReporter()->GetViewportPixelRatio();
- Vector3f offset = m_Model->GetParent()->MapWindowOffsetToSliceOffset(Vector2f(radius, radius));
+ double radius = 4 * m_Model->GetParent()->GetSizeReporter()->GetViewportPixelRatio();
+ Vector3d offset = m_Model->GetParent()->MapWindowOffsetToSliceOffset(Vector2d(radius, radius));
glColor4d(1, 1, 1, 0.5);
glBegin(GL_QUADS);
diff --git a/GUI/Renderer/AnnotationRenderer.h b/GUI/Renderer/AnnotationRenderer.h
index f306cab..b62db04 100644
--- a/GUI/Renderer/AnnotationRenderer.h
+++ b/GUI/Renderer/AnnotationRenderer.h
@@ -24,10 +24,10 @@ protected:
virtual ~AnnotationRenderer() {}
AnnotationModel *m_Model;
- void DrawLineLength(const Vector3f &xSlice1, const Vector3f &xSlice2,
+ void DrawLineLength(const Vector3d &xSlice1, const Vector3d &xSlice2,
const Vector3d &color, double alpha);
- void DrawSelectionHandle(const Vector3f &xSlice);
+ void DrawSelectionHandle(const Vector3d &xSlice);
};
#endif // ANNOTATIONRENDERER_H
diff --git a/GUI/Renderer/CrosshairsRenderer.cxx b/GUI/Renderer/CrosshairsRenderer.cxx
index f6d18c9..d9c4d14 100644
--- a/GUI/Renderer/CrosshairsRenderer.cxx
+++ b/GUI/Renderer/CrosshairsRenderer.cxx
@@ -60,10 +60,10 @@ void CrosshairsRenderer::paintGL()
// Shift the cursor position by by 0.5 in order to have it appear
// between voxels
- Vector3f xCursorImage = to_float(xCursorInteger) + Vector3f(0.5f);
+ Vector3d xCursorImage = to_double(xCursorInteger) + Vector3d(0.5);
// Get the cursor position on the slice
- Vector3f xCursorSlice = parentModel->MapImageToSlice(xCursorImage);
+ Vector3d xCursorSlice = parentModel->MapImageToSlice(xCursorImage);
// Upper and lober bounds to which the crosshairs are drawn
Vector2i lower(0);
@@ -84,10 +84,10 @@ void CrosshairsRenderer::paintGL()
// Paint the cross-hairs
glBegin(GL_LINES);
- glVertex2f(0, 0); glVertex2f(lower(0) - xCursorSlice(0), 0);
- glVertex2f(0, 0); glVertex2f(upper(0) - xCursorSlice(0), 0);
- glVertex2f(0, 0); glVertex2f(0, lower(1) - xCursorSlice(1));
- glVertex2f(0, 0); glVertex2f(0, upper(1) - xCursorSlice(1));
+ glVertex2d(0, 0); glVertex2d(lower(0) - xCursorSlice(0), 0);
+ glVertex2d(0, 0); glVertex2d(upper(0) - xCursorSlice(0), 0);
+ glVertex2d(0, 0); glVertex2d(0, lower(1) - xCursorSlice(1));
+ glVertex2d(0, 0); glVertex2d(0, upper(1) - xCursorSlice(1));
glEnd();
glPopMatrix();
diff --git a/GUI/Renderer/Generic3DRenderer.cxx b/GUI/Renderer/Generic3DRenderer.cxx
index 487fea0..bb6f433 100644
--- a/GUI/Renderer/Generic3DRenderer.cxx
+++ b/GUI/Renderer/Generic3DRenderer.cxx
@@ -532,7 +532,7 @@ void Generic3DRenderer::UpdateCamera(bool reset)
Vector3d spacing = app->GetCurrentImageData()->GetImageSpacing();
ImageWrapperBase *main = app->GetCurrentImageData()->GetMain();
Vector3d dim = element_product(to_double(main->GetSize()), spacing);
- Vector3d ctr = main->TransformVoxelIndexToNIFTICoordinates(to_double(cursor));
+ Vector3d ctr = main->TransformVoxelCIndexToNIFTICoordinates(to_double(cursor));
if(reset)
diff --git a/GUI/Renderer/GenericSliceRenderer.cxx b/GUI/Renderer/GenericSliceRenderer.cxx
index 609b015..c5289a7 100644
--- a/GUI/Renderer/GenericSliceRenderer.cxx
+++ b/GUI/Renderer/GenericSliceRenderer.cxx
@@ -44,6 +44,7 @@ GenericSliceRenderer
{
this->m_DrawingZoomThumbnail = false;
this->m_DrawingLayerThumbnail = false;
+ this->m_DrawingViewportIndex = -1;
}
void
@@ -51,12 +52,6 @@ GenericSliceRenderer::SetModel(GenericSliceModel *model)
{
this->m_Model = model;
- // Build the texture map
- OpenGLTextureAssociationFactory texFactoryDelegate = { this };
- m_Texture.SetDelegate(texFactoryDelegate);
- m_Texture.SetSource(model->GetDriver());
- this->UpdateTextureMap();
-
// Record and rebroadcast changes in the model
Rebroadcast(m_Model, ModelUpdateEvent(), ModelUpdateEvent());
@@ -106,12 +101,6 @@ void GenericSliceRenderer::OnUpdate()
// Also make sure to update the display layout model
m_Model->GetParentUI()->GetDisplayLayoutModel()->Update();
-
- // Only update the texture map in response to "big" events
- if(m_EventBucket->HasEvent(ModelUpdateEvent(), m_Model))
- {
- this->UpdateTextureMap();
- }
}
void
@@ -166,6 +155,7 @@ GenericSliceRenderer
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
+
glPushMatrix();
// First set of transforms
@@ -192,11 +182,14 @@ GenericSliceRenderer
// Draw the main layers for this row/column combination
ImageWrapperBase *layer = id->FindLayer(vp.layer_id, false);
- if(layer && this->DrawImageLayers(layer, !vp.isThumbnail))
+ if(layer && this->DrawImageLayers(layer, vp))
{
// Set the thumbnail flag
m_DrawingLayerThumbnail = vp.isThumbnail;
+ // Set the current vp index
+ m_DrawingViewportIndex = k;
+
// We don't want to draw segmentation over the speed image and other
// SNAP-mode layers.
this->DrawSegmentationTexture();
@@ -302,8 +295,9 @@ GenericSliceRenderer
}
}
- // No longer drawing thumbnails
+ // No longer drawing thumbnails or viewports
m_DrawingLayerThumbnail = false;
+ m_DrawingViewportIndex = -1;
// Set the viewport and projection to original dimensions
glViewport(0, 0, vp_full[0], vp_full[1]);
@@ -343,6 +337,17 @@ GenericSliceRenderer
glFlush();
}
+const GenericSliceRenderer::ViewportType *
+GenericSliceRenderer
+::GetDrawingViewport() const
+{
+ if(m_DrawingViewportIndex < 0)
+ return NULL;
+ else
+ return &m_Model->GetViewportLayout().vpList[m_DrawingViewportIndex];
+
+}
+
void
GenericSliceRenderer
::resizeGL(int w, int h, int device_pixel_ratio)
@@ -358,7 +363,7 @@ GenericSliceRenderer
glLoadIdentity();
}
-bool GenericSliceRenderer::DrawImageLayers(ImageWrapperBase *base_layer, bool drawStickies)
+bool GenericSliceRenderer::DrawImageLayers(ImageWrapperBase *base_layer, const ViewportType &vp)
{
// Get the image data
GenericImageData *id = m_Model->GetImageData();
@@ -366,7 +371,7 @@ bool GenericSliceRenderer::DrawImageLayers(ImageWrapperBase *base_layer, bool dr
// If drawing the thumbnail, only draw the main layer
if(m_DrawingZoomThumbnail)
{
- DrawTextureForLayer(base_layer, false);
+ DrawTextureForLayer(base_layer, vp, false);
return true;
}
@@ -374,10 +379,10 @@ bool GenericSliceRenderer::DrawImageLayers(ImageWrapperBase *base_layer, bool dr
if(!this->IsTiledMode())
{
// Draw the base layer without transparency
- DrawTextureForLayer(base_layer, false);
+ DrawTextureForLayer(base_layer, vp, false);
// Now draw all the sticky layers on top
- if(drawStickies)
+ if(!vp.isThumbnail)
{
for(LayerIterator it(id); !it.IsAtEnd(); ++it)
{
@@ -387,7 +392,7 @@ bool GenericSliceRenderer::DrawImageLayers(ImageWrapperBase *base_layer, bool dr
&& layer->IsSticky()
&& layer->GetAlpha() > 0)
{
- DrawTextureForLayer(layer, true);
+ DrawTextureForLayer(layer, vp, true);
}
}
}
@@ -397,10 +402,10 @@ bool GenericSliceRenderer::DrawImageLayers(ImageWrapperBase *base_layer, bool dr
else
{
// Draw the particular layer
- DrawTextureForLayer(base_layer, false);
+ DrawTextureForLayer(base_layer, vp, false);
// Now draw all the non-sticky layers
- if(drawStickies)
+ if(!vp.isThumbnail)
{
for(LayerIterator itov(id); !itov.IsAtEnd(); ++itov)
{
@@ -409,7 +414,7 @@ bool GenericSliceRenderer::DrawImageLayers(ImageWrapperBase *base_layer, bool dr
&& itov.GetLayer()->IsDrawable()
&& itov.GetLayer()->GetAlpha() > 0)
{
- DrawTextureForLayer(itov.GetLayer(), true);
+ DrawTextureForLayer(itov.GetLayer(), vp, true);
}
}
}
@@ -427,28 +432,124 @@ bool GenericSliceRenderer::IsTiledMode() const
+GenericSliceRenderer::Texture *
+GenericSliceRenderer
+::GetTextureForLayer(ImageWrapperBase *layer)
+{
+ const char *user_data_ids[] = {
+ "OpenGLTexture[0]",
+ "OpenGLTexture[1]",
+ "OpenGLTexture[2]"
+ };
+ const char *user_data_id = user_data_ids[m_Model->GetId()];
+
+ // If layer uninitialized, return NULL
+ if(!layer->IsInitialized())
+ return NULL;
+
+ // Retrieve the texture
+ SmartPtr<Texture> tex = static_cast<Texture *>(layer->GetUserData(user_data_id));
+
+ // Get the image that should be associated with the texture
+ Texture::ImageType *slice = layer->GetDisplaySlice(m_Model->GetId()).GetPointer();
+
+ // If the texture does not exist - or if the image has changed for some reason, update it
+ if(!tex || tex->GetImage() != slice)
+ {
+ tex = Texture::New();
+ tex->SetDepth(4, GL_RGBA);
+ tex->SetImage(slice);
+
+ layer->SetUserData(user_data_id, tex.GetPointer());
+ }
+
+ // Configure the texture parameters
+ const GlobalDisplaySettings *gds = m_Model->GetParentUI()->GetGlobalDisplaySettings();
+ GLint imode =
+ (gds->GetGreyInterpolationMode() == GlobalDisplaySettings::LINEAR)
+ ? GL_LINEAR : GL_NEAREST;
+ tex->SetInterpolation(imode);
+
+ // Set the mip-mapping behaviour depending on whether the image wrapper is rendering
+ // in image space or in display space
+ tex->SetMipMapping(layer->IsSlicingOrthogonal());
+ return tex;
+}
+#include <itkImageLinearConstIteratorWithIndex.h>
-void GenericSliceRenderer::DrawMainTexture()
+Vector3d GenericSliceRenderer::ComputeGridPosition(
+ const Vector3d &disp_pix,
+ const itk::Index<2> &slice_index,
+ ImageWrapperBase *vecimg)
{
- // Get the image data
- GenericImageData *id = m_Model->GetImageData();
+ // The pixel must be mapped to native
+ Vector3d disp;
+ disp[0] = vecimg->GetNativeIntensityMapping()->MapInternalToNative(disp_pix[0]);
+ disp[1] = vecimg->GetNativeIntensityMapping()->MapInternalToNative(disp_pix[1]);
+ disp[2] = vecimg->GetNativeIntensityMapping()->MapInternalToNative(disp_pix[2]);
+
+ // This is the physical coordinate of the current pixel - in LPS
+ Vector3d xPhys;
+ if(vecimg->IsSlicingOrthogonal())
+ {
+ // The pixel gives the displacement in LPS coordinates (by ANTS/Greedy convention)
+ // We need to map it back into the slice domain. First, we need to know the 3D index
+ // of the current pixel in the image space
+ Vector3d xSlice;
+ xSlice[0] = slice_index[0] + 0.5;
+ xSlice[1] = slice_index[1] + 0.5;
+ xSlice[2] = m_Model->GetSliceIndex();
+
+ // For orthogonal slicing, the input coordinates are in units of image voxels
+ xPhys = m_Model->MapSliceToImagePhysical(xSlice);
+ }
+ else
+ {
+ // Otherwise, the slice coordinates are relative to the rendered slice
+ GenericImageData *gid = m_Model->GetImageData();
+ GenericImageData::ImageBaseType *dispimg =
+ gid->GetDisplayViewportGeometry(m_Model->GetId());
+
+ // Use that image to transform coordinates
+ itk::Point<double, 3> pPhys;
+ itk::Index<3> index;
+ index[0] = slice_index[0]; index[1] = slice_index[1]; index[2] = 0;
+ dispimg->TransformIndexToPhysicalPoint(index, pPhys);
+ xPhys = pPhys;
+ }
+
+ // Add displacement and map back to slice space
+ itk::ContinuousIndex<double, 3> cix;
+ itk::Point<double, 3> pt = to_itkPoint(xPhys + disp);
- // Draw the main texture
- if (id->IsMainLoaded())
- DrawTextureForLayer(id->GetMain(), false);
+ m_Model->GetDriver()->GetCurrentImageData()->GetMain()->GetImageBase()
+ ->TransformPhysicalPointToContinuousIndex(pt, cix);
- // Draw each of the overlays
- if (!m_DrawingZoomThumbnail)
+ // The displaced location in slice coordinates
+ Vector3d disp_slice = m_Model->MapImageToSlice(Vector3d(cix));
+
+ // What we return also depends on whether slicing is ortho or not. For ortho
+ // slicing, the renderer is configured in the "Slice" coordinate system (1 unit =
+ // 1 image voxel) while for oblique slicing, the renderer uses the window coordinate
+ // system (1 unit = 1 screen pixel). Whatever we return needs to be in those units.
+ if(vecimg->IsSlicingOrthogonal())
+ {
+ return disp_slice;
+ }
+ else
{
- for(LayerIterator it(id, OVERLAY_ROLE); !it.IsAtEnd(); ++it)
- DrawTextureForLayer(it.GetLayer(), true);
+ Vector3d win3d;
+ Vector2d win2d = m_Model->MapSliceToWindow(disp_slice);
+ win3d[0] = win2d[0]; win3d[1] = win2d[1]; win3d[2] = disp_slice[2];
+ return win3d;
}
}
+
void GenericSliceRenderer::DrawTextureForLayer(
- ImageWrapperBase *layer, bool use_transparency)
+ ImageWrapperBase *layer, const ViewportType &vp, bool use_transparency)
{
// Get the appearance settings pointer since we use it a lot
SNAPAppearanceSettings *as =
@@ -464,7 +565,26 @@ void GenericSliceRenderer::DrawTextureForLayer(
? GL_LINEAR : GL_NEAREST;
// Get the texture
- Texture *tex = m_Texture[layer];
+ Texture *tex = this->GetTextureForLayer(layer);
+
+ // Set up the drawing mode
+ glPushMatrix();
+
+ // If a layer is sliced orthogonally, it's sliced in its native voxel space
+ // and we rely on OpenGL for scaling into display space
+ // Otherwise there is a 1:1 mapping from slice pixels to display pixels
+ if(!layer->IsSlicingOrthogonal())
+ {
+ glLoadIdentity();
+ if(vp.isThumbnail)
+ {
+ double scale_x = vp.size[0] * 1.0 / m_Model->GetCanvasSize()[0];
+ double scale_y = vp.size[1] * 1.0 / m_Model->GetCanvasSize()[1];
+ double zoom = std::max(scale_x, scale_y);
+ glScalef(zoom, zoom, 0);
+ }
+
+ }
// Paint the texture with alpha
if(tex)
@@ -482,6 +602,131 @@ void GenericSliceRenderer::DrawTextureForLayer(
tex->Draw(clrBackground);
}
}
+
+ // TODO: move this somewhere
+ AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast<
+ AbstractMultiChannelDisplayMappingPolicy *>(layer->GetDisplayMapping());
+ if(dp && dp->GetDisplayMode().RenderAsGrid
+ && !this->IsDrawingZoomThumbnail() && !this->IsDrawingLayerThumbnail())
+ {
+ // Draw the texture for the layer
+ AnatomicImageWrapper *vecimg = dynamic_cast<AnatomicImageWrapper *>(layer);
+ if(vecimg && vecimg->GetNumberOfComponents() == 3)
+ {
+ // Get the slice
+ AnatomicImageWrapper::SliceType::Pointer slice = vecimg->GetSlice(m_Model->GetId());
+ slice->GetSource()->UpdateLargestPossibleRegion();
+
+ // Appearance settings for grid lines
+ SNAPAppearanceSettings *as = m_Model->GetParentUI()->GetAppearanceSettings();
+ const OpenGLAppearanceElement *elt =
+ as->GetUIElement(SNAPAppearanceSettings::GRID_LINES);
+
+ // Line properties
+ glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT);
+
+ elt->ApplyLineSettings();
+
+
+ // The mapping between (index, phi[index]) and on-screen coordinate for a grid
+ // point is linear (combines a bunch of transforms). To save time, we can
+ // compute this mapping once at the beginning of the loop. We also know that the
+ // index will only be going up by one at each iteration
+ itk::Index<2> ind;
+ Vector3d phi, G0, d_grid_d_phi[3], d_grid_d_ind[2];
+
+ // Compute the initial displacement G0
+ ind.Fill(0); phi.fill(0.0f);
+ G0 = ComputeGridPosition(phi, ind, vecimg);
+
+ // Compute derivative of grid displacement wrt warp components
+ for(int a = 0; a < 3; a++)
+ {
+ ind.Fill(0); phi.fill(0.0f);
+ phi[a] = 1.0f;
+ d_grid_d_phi[a] = ComputeGridPosition(phi, ind, vecimg) - G0;
+ }
+
+ // Compute derivative of grid displacement wrt index components
+ for(int b = 0; b < 2; b++)
+ {
+ ind.Fill(0); phi.fill(0.0f);
+ ind[b] = 1;
+ d_grid_d_ind[b] = ComputeGridPosition(phi, ind, vecimg) - G0;
+ }
+
+ // Iterate line direction
+ for(int d = 0; d < 2; d++)
+ {
+
+ // The current matrix is such that we should be drawing in pixel coordinates.
+ typedef itk::ImageLinearConstIteratorWithIndex<AnatomicImageWrapper::SliceType> IterType;
+ IterType it1(slice, slice->GetBufferedRegion());
+ it1.SetDirection(d);
+ it1.GoToBegin();
+
+ int vox_increment;
+ if(vecimg->IsSlicingOrthogonal())
+ {
+ // Figure out how frequently to sample lines. The spacing on the screen should be at
+ // most every 4 pixels. Zoom is in units of px/mm. Spacing is in units of mm/vox, so
+ // zoom * spacing is (display pixels) / (image voxels).
+ double disp_pix_per_vox = m_Model->GetSliceSpacing()[d] * m_Model->GetViewZoom();
+ vox_increment = (int) ceil(8.0 / disp_pix_per_vox);
+ }
+ else
+ {
+ // The slice is in screen pixel units already - so just 8!
+ vox_increment = 8;
+ }
+
+ while( !it1.IsAtEnd() )
+ {
+ // Do we draw this line?
+ if(it1.GetIndex()[1-d] % vox_increment == 0)
+ {
+ glColor3d(elt->GetNormalColor()[0], elt->GetNormalColor()[1], elt->GetNormalColor()[2]);
+ glBegin(GL_LINE_STRIP);
+
+ // Set up the current position and increment
+ Vector3d G1 = G0 +
+ (d_grid_d_ind[0] * (double) (it1.GetIndex()[0])) +
+ (d_grid_d_ind[1] * (double) (it1.GetIndex()[1]));
+
+ while( !it1.IsAtEndOfLine() )
+ {
+ // Read the pixel
+ AnatomicImageWrapper::SliceType::PixelType pix = it1.Get();
+
+ // Alternative version
+ Vector3d xDispSlice = G1 +
+ (d_grid_d_phi[0] * (double) (pix[0])) +
+ (d_grid_d_phi[1] * (double) (pix[1])) +
+ (d_grid_d_phi[2] * (double) (pix[2]));
+
+ glVertex2d(xDispSlice[0], xDispSlice[1]);
+
+ // Add the displacement
+ ++it1;
+
+ // Update the current position
+ G1 += d_grid_d_ind[d];
+ }
+
+ glEnd();
+ }
+
+ it1.NextLine();
+ }
+
+ }
+
+ glPopAttrib();
+ }
+ }
+
+ // Pop the matrix
+ glPopMatrix();
}
@@ -492,7 +737,7 @@ void GenericSliceRenderer::DrawSegmentationTexture()
if (id->IsSegmentationLoaded() && alpha > 0)
{
- Texture *texture = m_Texture[id->GetSegmentation()];
+ Texture *texture = this->GetTextureForLayer(id->GetSegmentation());
texture->DrawTransparent(alpha);
}
}
@@ -525,7 +770,11 @@ void GenericSliceRenderer::DrawThumbnail()
glScalef(m_Model->GetSliceSpacing()[0],m_Model->GetSliceSpacing()[1],1.0);
// Draw the Main image (the background will be picked automatically)
- DrawMainTexture();
+ if (m_Model->GetImageData()->IsMainLoaded())
+ {
+ ViewportType vp = m_Model->GetViewportLayout().vpList.front();
+ DrawTextureForLayer(m_Model->GetImageData()->GetMain(), vp, false);
+ }
// Draw the overlays that are shown on the thumbnail
DrawTiledOverlays();
@@ -568,69 +817,6 @@ void GenericSliceRenderer::DrawThumbnail()
m_DrawingZoomThumbnail = false;
}
-GenericSliceRenderer::Texture *
-GenericSliceRenderer::CreateTexture(ImageWrapperBase *iw)
-{
- if(iw->IsInitialized())
- {
- Texture *texture = new Texture(4, GL_RGBA);
- texture->SetImage(iw->GetDisplaySlice(m_Model->GetId()).GetPointer());
-
- const GlobalDisplaySettings *gds = m_Model->GetParentUI()->GetGlobalDisplaySettings();
-
- GLint imode =
- (gds->GetGreyInterpolationMode() == GlobalDisplaySettings::LINEAR)
- ? GL_LINEAR : GL_NEAREST;
-
- texture->SetInterpolation(imode);
- return texture;
- }
- else return NULL;
-}
-
-/*
-void GenericSliceRenderer::AssociateTexture(
- ImageWrapperBase *iw, TextureMap &src, TextureMap &trg)
-{
- if(iw->IsInitialized())
- {
- TextureMap::iterator it = src.find(iw);
- Texture *texture;
-
- if (it != src.end())
- {
- texture = it->second;
- itk::ImageBase<2> *b1 = iw->GetDisplaySlice(m_Model->GetId()).GetPointer();
- const itk::ImageBase<2> *b2 = texture->GetImage();
- std::cout << "TEX1 " << b1 << " TEX2" << b2 << std::endl;
- src.erase(it);
- }
- else
- {
- texture = new Texture(4, GL_RGBA);
- texture->SetImage(iw->GetDisplaySlice(m_Model->GetId()).GetPointer());
- }
-
- // Set the interpolation approach
- SNAPAppearanceSettings *as = m_Model->GetParentUI()->GetAppearanceSettings();
- GLint imode = as->GetGreyInterpolationMode() == SNAPAppearanceSettings::LINEAR
- ? GL_LINEAR : GL_NEAREST;
- texture->SetInterpolation(imode);
-
- // Store the texture association
- trg[iw] = texture;
- }
-}
-
-*/
-
-void GenericSliceRenderer::UpdateTextureMap()
-{
- if(m_Model->IsSliceInitialized())
- {
- m_Texture.Update();
- }
-}
void GenericSliceRenderer::initializeGL()
{
@@ -658,16 +844,3 @@ void GenericSliceRenderer::DrawGlobalOverlays()
}
}
-OpenGLTextureAssociationFactory::Texture *
-OpenGLTextureAssociationFactory
-::New(ImageWrapperBase *layer)
-{
- return m_Renderer->CreateTexture(layer);
-}
-
-
-template class LayerAssociation<GenericSliceRenderer::Texture,
- ImageWrapperBase,
- OpenGLTextureAssociationFactory>;
-
-
diff --git a/GUI/Renderer/GenericSliceRenderer.h b/GUI/Renderer/GenericSliceRenderer.h
index f1db6db..3153262 100644
--- a/GUI/Renderer/GenericSliceRenderer.h
+++ b/GUI/Renderer/GenericSliceRenderer.h
@@ -48,17 +48,14 @@ protected:
GenericSliceRenderer *m_ParentRenderer;
};
-struct OpenGLTextureAssociationFactory
-{
- typedef OpenGLSliceTexture<ImageWrapperBase::DisplayPixelType> Texture;
- Texture *New(ImageWrapperBase *layer);
- GenericSliceRenderer *m_Renderer;
-};
-
class GenericSliceRenderer : public AbstractRenderer
{
public:
+ // texture type
+ typedef OpenGLSliceTexture<ImageWrapperBase::DisplayPixelType> Texture;
+
+
irisITKObjectMacro(GenericSliceRenderer, AbstractModel)
FIRES(ModelUpdateEvent)
@@ -77,6 +74,16 @@ public:
/** This flag is on while the layer thumbnail is being painted */
irisIsMacro(DrawingLayerThumbnail)
+ // Viewport object
+ typedef SliceViewportLayout::SubViewport ViewportType;
+
+ /**
+ * Get a pointer to the viewport that is currently being drawn, or
+ * NULL if a viewport is not being drawn
+ */
+ const ViewportType *GetDrawingViewport() const;
+
+
typedef std::list<SliceRendererDelegate *> RendererDelegateList;
// Get a reference to the list of overlays stored in here
@@ -93,12 +100,14 @@ public:
RendererDelegateList &GetTiledOverlays()
{ return m_TiledOverlays; }
- // This method can be used by the renderer delegates to draw a texture
- void DrawTextureForLayer(ImageWrapperBase *layer, bool use_transparency);
-
// A callback for when the model is reinitialized
// void OnModelReinitialize();
+ // Get (creating if necessary) and configure the texture for a given layer
+ Texture *GetTextureForLayer(ImageWrapperBase *iw);
+
+ // Set list of child renderers
+ void SetChildRenderers(std::list<AbstractRenderer *> renderers);
protected:
@@ -107,17 +116,22 @@ protected:
void OnUpdate();
- void DrawMainTexture();
void DrawSegmentationTexture();
void DrawOverlayTexture();
void DrawThumbnail();
void DrawTiledOverlays();
void DrawGlobalOverlays();
+
// Draw the image and overlays either on top of each other or separately
// in individual cells. Returns true if a layer was drawn, false if not,
// i.e., the cell is outside of the range of available layers
- bool DrawImageLayers(ImageWrapperBase *base_layer, bool drawStickes);
+ bool DrawImageLayers(
+ ImageWrapperBase *base_layer,
+ const ViewportType &vp);
+
+ // This method can be used by the renderer delegates to draw a texture
+ void DrawTextureForLayer(ImageWrapperBase *layer, const ViewportType &vp, bool use_transparency);
bool IsTiledMode() const;
@@ -126,27 +140,16 @@ protected:
// Whether rendering to thumbnail or not
bool m_DrawingZoomThumbnail, m_DrawingLayerThumbnail;
- // A dynamic association between various image layers and texture objects
- typedef OpenGLSliceTexture<ImageWrapperBase::DisplayPixelType> Texture;
- typedef LayerAssociation<Texture, ImageWrapperBase,
- OpenGLTextureAssociationFactory> TextureMap;
-
- TextureMap m_Texture;
+ // The index of the viewport that is currently being drawn - for use in child renderers
+ int m_DrawingViewportIndex;
// A list of overlays that the user can configure
RendererDelegateList m_TiledOverlays, m_GlobalOverlays;
- // Internal method used by UpdateTextureMap()
- // void AssociateTexture(ImageWrapperBase *iw, TextureMap &src, TextureMap &trg);
-
- // Texture factory method
- Texture *CreateTexture(ImageWrapperBase *iw);
-
-
- // Update the texture map to mirror the current images in the model
- void UpdateTextureMap();
+ // List of child renderers
+ std::list<AbstractRenderer *>m_ChildRenderers;
- friend struct OpenGLTextureAssociationFactory;
+ Vector3d ComputeGridPosition(const Vector3d &disp_pix, const itk::Index<2> &slice_index, ImageWrapperBase *vecimg);
};
diff --git a/GUI/Renderer/IntensityCurveVTKRenderer.cxx b/GUI/Renderer/IntensityCurveVTKRenderer.cxx
index 6df2d63..679adb1 100644
--- a/GUI/Renderer/IntensityCurveVTKRenderer.cxx
+++ b/GUI/Renderer/IntensityCurveVTKRenderer.cxx
@@ -326,11 +326,11 @@ public:
{
this->MouseMoved = false;
- if (mouse.Button == vtkContextMouseEvent::LEFT_BUTTON)
+ if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
{
double pos[2];
- pos[0] = mouse.Pos[0];
- pos[1] = mouse.Pos[1];
+ pos[0] = mouse.GetPos()[0];
+ pos[1] = mouse.GetPos()[1];
vtkIdType pointUnderMouse = this->FindPoint(pos);
this->SetCurrentPoint(pointUnderMouse);
return true;
diff --git a/GUI/Renderer/OpenGLSliceTexture.cxx b/GUI/Renderer/OpenGLSliceTexture.cxx
index 424fdbe..fa55cec 100755
--- a/GUI/Renderer/OpenGLSliceTexture.cxx
+++ b/GUI/Renderer/OpenGLSliceTexture.cxx
@@ -54,20 +54,13 @@ OpenGLSliceTexture<TPixel>
}
template<class TPixel>
+void
OpenGLSliceTexture<TPixel>
-::OpenGLSliceTexture(GLuint components, GLenum format)
+::SetDepth(GLuint components, GLenum format)
{
- // Set to -1 to force a call to 'generate'
- m_IsTextureInitalized = false;
-
- // Set the update time to -1
- m_UpdateTime = 0;
-
// Init the GL settings to uchar, luminance defautls, which are harmless
m_GlComponents = components;
m_GlFormat = format;
- m_GlType = GL_UNSIGNED_BYTE;
- m_InterpolationMode = GL_NEAREST;
}
template<class TPixel>
@@ -91,6 +84,7 @@ OpenGLSliceTexture<TPixel>
}
}
+#include "itkImageFileWriter.h"
template<class TPixel>
void
@@ -135,6 +129,10 @@ OpenGLSliceTexture<TPixel>
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_InterpolationMode );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_InterpolationMode );
+ // TODO: figure out how this can be applied in a fully compatible way
+ // glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+ // glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+
// Turn off modulo-4 rounding in GL
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
@@ -179,7 +177,9 @@ OpenGLSliceTexture<TPixel>
double tx = w * 1.0 / m_TextureSize(0);
double ty = h * 1.0 / m_TextureSize(1);
- // Draw quad
+ glPushMatrix();
+
+ // Draw quad
glBegin(GL_QUADS);
glTexCoord2d(0,0);
glVertex2d(0,0);
@@ -191,9 +191,72 @@ OpenGLSliceTexture<TPixel>
glVertex2d(w,0);
glEnd();
+ glPopMatrix();
+
+ // Even though we use glPushAttrib, on some graphics cards, the texture bit remains set
+ glDisable(GL_TEXTURE_2D);
glPopAttrib();
}
+template<class TPixel>
+void
+OpenGLSliceTexture<TPixel>
+::DrawCheckerboard(int rows, int cols)
+{
+ // Update the texture
+ Update();
+
+ // Should have a texture number
+ assert(m_IsTextureInitalized);
+
+ // GL settings
+ glPushAttrib(GL_TEXTURE_BIT);
+ glEnable(GL_TEXTURE_2D);
+
+ // Select our texture
+ glBindTexture(GL_TEXTURE_2D,m_TextureIndex);
+
+ int w = m_Image->GetBufferedRegion().GetSize()[0];
+ int h = m_Image->GetBufferedRegion().GetSize()[1];
+ double tx = w * 1.0 / m_TextureSize(0);
+ double ty = h * 1.0 / m_TextureSize(1);
+
+ glPushMatrix();
+ glBegin(GL_QUADS);
+
+ // Repeat for each checkerboard
+ for(int iy = 0; iy < rows; iy++)
+ {
+ for(int ix = 0; ix < cols; ix++)
+ {
+ if((ix + iy) % 2 == 0)
+ {
+ double x0 = ix * 1.0 / cols;
+ double x1 = (ix + 1) * 1.0 / cols;
+ double y0 = iy * 1.0 / rows;
+ double y1 = (iy + 1) * 1.0 / rows;
+
+ // Draw quad
+ glTexCoord2d(x0 * tx, y0 * ty);
+ glVertex2d(x0 * w, y0 * h);
+ glTexCoord2d(x0 * tx, y1 * ty);
+ glVertex2d(x0 * w, y1 * h);
+ glTexCoord2d(x1 * tx, y1 * ty);
+ glVertex2d(x1 * w, y1 * h);
+ glTexCoord2d(x1 * tx, y0 * ty);
+ glVertex2d(x1 * w, y0 * h);
+ }
+ }
+ }
+
+ glEnd();
+ glPopMatrix();
+
+ // Even though we use glPushAttrib, on some graphics cards, the texture bit remains set
+ glDisable(GL_TEXTURE_2D);
+
+ glPopAttrib();
+}
template<class TPixel>
void
@@ -237,16 +300,32 @@ OpenGLSliceTexture<TPixel>
glVertex2d(w,0);
glEnd();
+ // Even though we use glPushAttrib, on some graphics cards, the texture bit remains set
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2D);
+
glPopAttrib();
}
template<class TPixel>
void OpenGLSliceTexture<TPixel>::SetImage(ImageType *inImage)
{
+ // Assign the image
m_Image = inImage;
m_Image->GetSource()->UpdateLargestPossibleRegion();
m_UpdateTime = 0;
}
+template<class TPixel>
+void OpenGLSliceTexture<TPixel>::SetMipMapping(bool state)
+{
+ // Set the mipmapping state
+ if(m_MipMapping != state)
+ {
+ m_MipMapping = state;
+ m_UpdateTime = 0;
+ }
+}
+
// Explicit instantiation of templates
template class OpenGLSliceTexture<ImageWrapperBase::DisplayPixelType>;
diff --git a/GUI/Renderer/OpenGLSliceTexture.h b/GUI/Renderer/OpenGLSliceTexture.h
index 561f30c..493150a 100644
--- a/GUI/Renderer/OpenGLSliceTexture.h
+++ b/GUI/Renderer/OpenGLSliceTexture.h
@@ -54,39 +54,43 @@
* The calls to Update will make sure that the texture is up to date.
*/
template <class TPixel>
-class OpenGLSliceTexture
+class OpenGLSliceTexture : public itk::Object
{
public:
+
+ irisITKObjectMacro(OpenGLSliceTexture, itk::Object)
+
// Image typedefs
typedef itk::Image<TPixel, 2> ImageType;
- typedef typename itk::SmartPointer<ImageType> ImagePointer;
+ typedef SmartPtr<ImageType> ImagePointer;
- /** Constructor, initializes the texture object */
- OpenGLSliceTexture();
- OpenGLSliceTexture(GLuint, GLenum);
+ /** Initialize the texture object */
+ void SetDepth(GLuint, GLenum);
- /** Destructor, deallocates texture memory */
- virtual ~OpenGLSliceTexture();
-
- irisGetMacro(Image, const ImageType *);
+ /** Set the image from which the texture is initialized */
+ irisGetMacro(Image, const ImageType *)
/** Pass in a pointer to a 2D image */
void SetImage(ImageType *inImage);
+ /** Set the mip mapping behavior */
+ irisGetMacro(MipMapping, bool)
+ void SetMipMapping(bool state);
+
/** Get the dimensions of the texture image, which are powers of 2 */
- irisGetMacro(TextureSize,Vector2ui);
+ irisGetMacro(TextureSize,Vector2ui)
/** Get the GL texture number automatically allocated by this object */
- irisGetMacro(TextureIndex,int);
+ irisGetMacro(TextureIndex,int)
/** Set the number of components used in call to glTextureImage */
- irisSetMacro(GlComponents,GLuint);
+ irisSetMacro(GlComponents,GLuint)
/** Get the format (e.g. GL_LUMINANCE) in call to glTextureImage */
- irisSetMacro(GlFormat,GLenum);
+ irisSetMacro(GlFormat,GLenum)
/** Get the type (e.g. GL_UNSIGNED_INT) in call to glTextureImage */
- irisSetMacro(GlType,GLenum);
+ irisSetMacro(GlType,GLenum)
/**
* Make sure that the texture is up to date (reflects the image)
@@ -111,6 +115,16 @@ public:
*/
void DrawTransparent(double alpha);
+ /**
+ * Draw the texture as a checkerboard
+ */
+ void DrawCheckerboard(int rows, int cols);
+
+protected:
+
+ OpenGLSliceTexture();
+ ~OpenGLSliceTexture();
+
private:
// The dimensions of the texture as stored in memory
@@ -125,6 +139,9 @@ private:
// Has the texture been initialized?
bool m_IsTextureInitalized;
+ // Are mip-maps required
+ bool m_MipMapping;
+
// The pipeline time of the source image (vs. our pipeline time)
unsigned long m_UpdateTime;
diff --git a/GUI/Renderer/OptimizationProgressRenderer.cxx b/GUI/Renderer/OptimizationProgressRenderer.cxx
index d0100b3..03ec18b 100644
--- a/GUI/Renderer/OptimizationProgressRenderer.cxx
+++ b/GUI/Renderer/OptimizationProgressRenderer.cxx
@@ -1,6 +1,6 @@
#include "OptimizationProgressRenderer.h"
-#include "ImageIOWizardModel.h"
+#include "RegistrationModel.h"
#include <vtkChartXY.h>
#include <vtkPlot.h>
@@ -13,6 +13,8 @@
#include <vtkContextMouseEvent.h>
#include "vtkGenericOpenGLRenderWindow.h"
+#include <cstdlib>
+#include <algorithm>
OptimizationProgressRenderer::OptimizationProgressRenderer()
{
@@ -21,7 +23,7 @@ OptimizationProgressRenderer::OptimizationProgressRenderer()
// Set up the scene for rendering
m_Chart = vtkSmartPointer<vtkChartXY>::New();
m_Chart->SetActionToButton(vtkChartXY::PAN, vtkContextMouseEvent::LEFT_BUTTON);
- m_Chart->SetActionToButton(vtkChartXY::ZOOM, vtkContextMouseEvent::RIGHT_BUTTON);
+ m_Chart->SetActionToButton(vtkChartXY::ZOOM_AXIS, vtkContextMouseEvent::RIGHT_BUTTON);
// Add the chart to the renderer
m_ContextView->GetScene()->AddItem(m_Chart);
@@ -46,9 +48,9 @@ OptimizationProgressRenderer::OptimizationProgressRenderer()
m_Plot->GetYAxis()->SetBehavior(vtkAxis::FIXED);
m_Plot->GetYAxis()->SetMinimum(-0.05);
m_Plot->GetYAxis()->SetMaximum(1.05);
- m_Plot->GetXAxis()->SetTitle("Optimization iteration");
+ m_Plot->GetXAxis()->SetTitle("Iteration");
m_Plot->GetXAxis()->SetBehavior(vtkAxis::FIXED);
- m_Plot->GetYAxis()->SetTitle("Metric value");
+ m_Plot->GetYAxis()->SetTitle("Metric");
// Set the background to white
m_BackgroundColor.fill(1.0);
@@ -57,40 +59,54 @@ OptimizationProgressRenderer::OptimizationProgressRenderer()
this->m_RenderWindow->SetMultiSamples(0);
this->m_RenderWindow->SetLineSmoothing(1);
this->m_RenderWindow->SetPolygonSmoothing(1);
+
+ m_PyramidLevel = 0;
}
-void OptimizationProgressRenderer::SetModel(ImageIOWizardModel *model)
+void OptimizationProgressRenderer::SetModel(RegistrationModel *model)
{
m_Model = model;
// Rebroadcast the relevant events from the model in order for the
// widget that uses this renderer to cause an update
- Rebroadcast(model, ImageIOWizardModel::RegistrationProgressEvent(), ModelUpdateEvent());
-
- // Reset value range
+ Rebroadcast(model->GetLastMetricValueModel(), ValueChangedEvent(), ModelUpdateEvent());
}
void OptimizationProgressRenderer::OnUpdate()
{
- int x = m_DataX->GetNumberOfTuples()+1;
- double y = m_Model->GetRegistrationObjective();
+ // Get the metric log
+ typedef std::vector< std::vector<double> > MetricLog;
+ const MetricLog &mlog = m_Model->GetRegistrationMetricLog();
+
+ // Set the data points
+ m_DataX->Reset();
+ m_DataY->Reset();
+
+ int x = 0;
+ if(mlog.size() > m_PyramidLevel)
+ {
+ for(int i = 0; i < mlog[m_PyramidLevel].size(); i++, x++)
+ {
+ double y = mlog[m_PyramidLevel][i];
+ m_DataX->InsertNextValue(x);
+ m_DataY->InsertNextValue(y);
+
+ m_MinValue = (x == 0) ? y : std::min(m_MinValue, y);
+ m_MaxValue = (x == 0) ? y : std::max(m_MaxValue, y);
+ }
+ }
- m_DataX->InsertNextValue(x);
- m_DataY->InsertNextValue(y);
m_PlotTable->Modified();
- m_Plot->GetXAxis()->SetRange(0.0, ((x + 5) / 40 + 1) * 40.0);
+ m_Plot->GetXAxis()->SetRange(0.0, ((m_DataX->GetNumberOfTuples()+ 5) / 20 + 1) * 20.0);
+ double min_rnd = (((int) floor(m_MinValue * 10)) - 1) * 0.1;
+ double max_rnd = (((int) ceil(m_MaxValue * 10)) + 1) * 0.1;
- if (x == 1 || y <= m_MinValue)
- {
- m_MinValue = (((int) floor(y * 10)) - 1) * 0.1;
- }
- if (x == 1 || y >= m_MaxValue)
- {
- m_MaxValue = (((int) ceil(y * 10)) + 1) * 0.1;
- }
+ m_Plot->GetYAxis()->SetRange(min_rnd, max_rnd);
- m_Plot->GetYAxis()->SetRange(m_MinValue, m_MaxValue);
+ char plotLabel[64];
+ sprintf(plotLabel, "%dx Level", m_PyramidZoom);
+ m_Chart->SetTitle(plotLabel);
}
diff --git a/GUI/Renderer/OptimizationProgressRenderer.h b/GUI/Renderer/OptimizationProgressRenderer.h
index e33769d..b75818b 100644
--- a/GUI/Renderer/OptimizationProgressRenderer.h
+++ b/GUI/Renderer/OptimizationProgressRenderer.h
@@ -11,7 +11,7 @@ class vtkFloatArray;
class vtkPlot;
class vtkTable;
-class ImageIOWizardModel;
+class RegistrationModel;
class OptimizationProgressRenderer : public AbstractVTKSceneRenderer
{
@@ -19,7 +19,13 @@ public:
irisITKObjectMacro(OptimizationProgressRenderer, AbstractVTKSceneRenderer)
- void SetModel(ImageIOWizardModel *model);
+ void SetModel(RegistrationModel *model);
+
+ itkGetMacro(PyramidLevel, int)
+ itkSetMacro(PyramidLevel, int)
+
+ itkGetMacro(PyramidZoom, int)
+ itkSetMacro(PyramidZoom, int)
void OnUpdate();
@@ -31,7 +37,7 @@ protected:
virtual ~OptimizationProgressRenderer() {}
- ImageIOWizardModel *m_Model;
+ RegistrationModel *m_Model;
// Rendering stuff
vtkSmartPointer<vtkChartXY> m_Chart;
@@ -41,6 +47,9 @@ protected:
// Range of values
double m_MinValue, m_MaxValue;
+
+ // Pyramid level that this plots
+ int m_PyramidLevel, m_PyramidZoom;
};
#endif // OPTIMIZATIONPROGRESSRENDERER_H
diff --git a/GUI/Renderer/OrientationGraphicRenderer.cxx b/GUI/Renderer/OrientationGraphicRenderer.cxx
index 7f4c7c8..3c39780 100644
--- a/GUI/Renderer/OrientationGraphicRenderer.cxx
+++ b/GUI/Renderer/OrientationGraphicRenderer.cxx
@@ -73,7 +73,7 @@ void OrientationGraphicRenderer::OnUpdate()
{
for(nJ = 0; nJ < 3; nJ++)
{
- (*pMatrix4x4)[nI][nJ] = dm[nI][nJ];
+ pMatrix4x4->SetElement(nI, nJ, dm[nI][nJ]);
}
}
m_ReorientProps.Update(pMatrix4x4);
diff --git a/GUI/Renderer/OrientationWidget/Reorient/ScanningROI.cxx b/GUI/Renderer/OrientationWidget/Reorient/ScanningROI.cxx
index a3fdc42..4e8729b 100755
--- a/GUI/Renderer/OrientationWidget/Reorient/ScanningROI.cxx
+++ b/GUI/Renderer/OrientationWidget/Reorient/ScanningROI.cxx
@@ -90,7 +90,7 @@ void ScanningROI::setPlanesNr(int anPlanesNr)
int nI, nJ;
- int nPlanesNr = m_arrpPairsPP_Axial.size();
+ int nPlanesNr = (int) m_arrpPairsPP_Axial.size();
if(nPlanesNr != anPlanesNr)
{
@@ -142,7 +142,7 @@ void ScanningROI::setPlanesNr(int anPlanesNr)
int ScanningROI::getPlanesNr() const
{
- return(m_arrpPairsPP_Axial.size());
+ return (int) m_arrpPairsPP_Axial.size();
}
void ScanningROI::setSpacing(double adbX, double adbY, double adbZ)
@@ -174,11 +174,11 @@ void ScanningROI::Update()
{
//m_pAxesWidget->GetAxesActor()->SetPosition(m_dbGraphicScale / 2.0, m_dbGraphicScale / 2.0, m_dbGraphicScale / 2.0);
- (*pMatrix4x4DirectionsAccompanying)[0][0] = -1.0;
- (*pMatrix4x4DirectionsAccompanying)[1][1] = 0.0;
- (*pMatrix4x4DirectionsAccompanying)[1][2] = -1.0;
- (*pMatrix4x4DirectionsAccompanying)[2][1] = -1.0;
- (*pMatrix4x4DirectionsAccompanying)[2][2] = 0.0;
+ pMatrix4x4DirectionsAccompanying->SetElement(0, 0, -1.0);
+ pMatrix4x4DirectionsAccompanying->SetElement(1, 1, 0.0);
+ pMatrix4x4DirectionsAccompanying->SetElement(1, 2, -1.0);
+ pMatrix4x4DirectionsAccompanying->SetElement(2, 1, -1.0);
+ pMatrix4x4DirectionsAccompanying->SetElement(2, 2, 0.0);
changeOrientation3x3(m_pMatrix4x4Directions);
m_pAxesWidget->SetLabels("i", "k", "j");
@@ -305,7 +305,7 @@ void ScanningROI::changeOrientation3x3(vtkSmartPointer < vtkMatrix4x4 > apvtkMat
{
for(nJ = 0; nJ < 3; nJ++)
{
- (*apvtkMatrix4x4)[nI][nJ] = - (*apvtkMatrix4x4)[nI][nJ];
+ apvtkMatrix4x4->SetElement(nI, nJ, apvtkMatrix4x4->GetElement(nI, nJ));
}
}
}
diff --git a/GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.cxx b/GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.cxx
index 9a9131f..90b13cd 100755
--- a/GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.cxx
+++ b/GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.cxx
@@ -35,7 +35,7 @@ OrientationWidgetGUI::OrientationWidgetGUI()
{
for(nJ = 0; nJ < 4; nJ++)
{
- double dbEquals = (*pMatrix)[nI][nJ];
+ double dbEquals = pMatrix->GetElement(nI, nJ);
QTableWidgetItem* pItem= new QTableWidgetItem();
pItem->setText(QString::number(dbEquals));
@@ -73,7 +73,7 @@ vtkSmartPointer < vtkMatrix4x4 > OrientationWidgetGUI::getMtrx4x4GUI()
for(nJ = 0; nJ < 4; nJ++)
{
QTableWidgetItem* pItem = m_pTableWidget->item(nI, nJ);
- (*pMatrix4x4)[nI][nJ] = pItem->text().toDouble();
+ pMatrix4x4->SetElement(nI, nJ, pItem->text().toDouble());
}
}
return(pMatrix4x4);
@@ -102,17 +102,17 @@ void OrientationWidgetGUI::slotPhiThetaPsi(double adbValue)
//Formulas taken from http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions
vtkSmartPointer < vtkMatrix4x4 > pMatrix4x4 = vtkSmartPointer < vtkMatrix4x4 >::New();
- (*pMatrix4x4)[0][0] = cosTheta * cosPsi;
- (*pMatrix4x4)[0][1] = cosPhi * sinPsi + sinPhi * sinTheta * cosPsi;
- (*pMatrix4x4)[0][2] = sinPhi * sinPsi - cosPhi * sinTheta * cosPsi;
+ pMatrix4x4->SetElement(0,0, cosTheta * cosPsi);
+ pMatrix4x4->SetElement(0,1, cosPhi * sinPsi + sinPhi * sinTheta * cosPsi);
+ pMatrix4x4->SetElement(0,2, sinPhi * sinPsi - cosPhi * sinTheta * cosPsi);
- (*pMatrix4x4)[1][0] = -cosTheta * sinPsi;
- (*pMatrix4x4)[1][1] = cosPhi * cosPsi - sinPhi * sinTheta * sinPsi;
- (*pMatrix4x4)[1][2] = sinPhi * cosPsi + cosPhi * sinTheta * sinPsi;
+ pMatrix4x4->SetElement(1, 0, -cosTheta * sinPsi);
+ pMatrix4x4->SetElement(1, 1, cosPhi * cosPsi - sinPhi * sinTheta * sinPsi);
+ pMatrix4x4->SetElement(1, 2, sinPhi * cosPsi + cosPhi * sinTheta * sinPsi);
- (*pMatrix4x4)[2][0] = sinTheta;
- (*pMatrix4x4)[2][1] = -sinPhi * cosTheta;
- (*pMatrix4x4)[2][2] = cosPhi * cosTheta;
+ pMatrix4x4->SetElement(2, 0, sinTheta);
+ pMatrix4x4->SetElement(2, 1, -sinPhi * cosTheta);
+ pMatrix4x4->SetElement(2, 2, cosPhi * cosTheta);
int nI, nJ;
for(nI = 0; nI < 4; nI++)
@@ -120,7 +120,7 @@ void OrientationWidgetGUI::slotPhiThetaPsi(double adbValue)
for(nJ = 0; nJ < 4; nJ++)
{
QTableWidgetItem* pItem = m_pTableWidget->item(nI, nJ);
- pItem->setText(QString::number((*pMatrix4x4)[nI][nJ]));
+ pItem->setText(QString::number(pMatrix4x4->GetElement(nI, nJ)));
}
}
pMatrix4x4->Transpose();
diff --git a/GUI/Renderer/PaintbrushRenderer.cxx b/GUI/Renderer/PaintbrushRenderer.cxx
index d4dde26..9b9a035 100644
--- a/GUI/Renderer/PaintbrushRenderer.cxx
+++ b/GUI/Renderer/PaintbrushRenderer.cxx
@@ -102,7 +102,7 @@ void PaintbrushRenderer::paintGL()
elt->ApplyLineSettings();
// Get the brush position
- Vector3f xPos = m_Model->GetCenterOfPaintbrushInSliceSpace();
+ Vector3d xPos = m_Model->GetCenterOfPaintbrushInSliceSpace();
// Refit matrix so that the lines are centered on the current pixel
glPushMatrix();
diff --git a/GUI/Renderer/PolygonDrawingRenderer.cxx b/GUI/Renderer/PolygonDrawingRenderer.cxx
index ec79734..e4542a9 100644
--- a/GUI/Renderer/PolygonDrawingRenderer.cxx
+++ b/GUI/Renderer/PolygonDrawingRenderer.cxx
@@ -5,9 +5,9 @@
// Default colors
-const float PolygonDrawingRenderer::m_DrawingModeColor[] = { 1.0f, 0.0f, 0.5f };
-const float PolygonDrawingRenderer::m_EditModeNormalColor[] = { 1.0f, 0.0f, 0.0f };
-const float PolygonDrawingRenderer::m_EditModeSelectedColor[] = { 0.0f, 1.0f, 0.0f };
+const double PolygonDrawingRenderer::m_DrawingModeColor[] = { 1.0f, 0.0f, 0.5f };
+const double PolygonDrawingRenderer::m_EditModeNormalColor[] = { 1.0f, 0.0f, 0.0f };
+const double PolygonDrawingRenderer::m_EditModeSelectedColor[] = { 0.0f, 1.0f, 0.0f };
PolygonDrawingRenderer::PolygonDrawingRenderer()
{
@@ -16,14 +16,14 @@ PolygonDrawingRenderer::PolygonDrawingRenderer()
void
PolygonDrawingRenderer
-::DrawBox(const vnl_vector_fixed<float, 4> &box,
- float border_x, float border_y)
+::DrawBox(const vnl_vector_fixed<double, 4> &box,
+ double border_x, double border_y)
{
glBegin(GL_LINE_LOOP);
- glVertex3f(box[0] - border_x, box[2] - border_y, 0.0);
- glVertex3f(box[1] + border_x, box[2] - border_y, 0.0);
- glVertex3f(box[1] + border_x, box[3] + border_y, 0.0);
- glVertex3f(box[0] - border_x, box[3] + border_y, 0.0);
+ glVertex3d(box[0] - border_x, box[2] - border_y, 0.0);
+ glVertex3d(box[1] + border_x, box[2] - border_y, 0.0);
+ glVertex3d(box[1] + border_x, box[3] + border_y, 0.0);
+ glVertex3d(box[0] - border_x, box[3] + border_y, 0.0);
glEnd();
}
@@ -68,7 +68,7 @@ PolygonDrawingRenderer
glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT);
// Set line and point drawing parameters
- float vppr = m_ParentRenderer->GetModel()->GetSizeReporter()->GetViewportPixelRatio();
+ double vppr = m_ParentRenderer->GetModel()->GetSizeReporter()->GetViewportPixelRatio();
glPointSize(3 * vppr);
// Draw the line segments
@@ -99,8 +99,8 @@ PolygonDrawingRenderer
glColor3dv(aeEdit->GetNormalColor().data_block());
// Draw the line
- glVertex3f(it->x, it->y, 0);
- glVertex3f(itNext->x, itNext->y, 0);
+ glVertex3d(it->x, it->y, 0);
+ glVertex3d(itNext->x, itNext->y, 0);
}
glEnd();
@@ -116,7 +116,7 @@ PolygonDrawingRenderer
glBegin(GL_LINE_STRIP);
glColor3dv(aeDrawColor.data_block());
for(it = vx.begin(); it!=vx.end(); ++it)
- glVertex3f(it->x, it->y, 0);
+ glVertex3d(it->x, it->y, 0);
glEnd();
// Draw the drag vertices
@@ -124,7 +124,7 @@ PolygonDrawingRenderer
{
glBegin(GL_LINE_STRIP);
for(it = dvx.begin(); it != dvx.end(); ++it)
- glVertex3f(it->x, it->y, 0);
+ glVertex3d(it->x, it->y, 0);
glEnd();
}
@@ -135,10 +135,10 @@ PolygonDrawingRenderer
{
glBegin(GL_LINES);
if(dvx.size())
- glVertex3f(dvx.back().x, dvx.back().y, 0);
+ glVertex3d(dvx.back().x, dvx.back().y, 0);
else
- glVertex3f(vx.back().x, vx.back().y, 0);
- glVertex3f(vx.front().x, vx.front().y, 0);
+ glVertex3d(vx.back().x, vx.back().y, 0);
+ glVertex3d(vx.front().x, vx.front().y, 0);
glEnd();
}
@@ -151,10 +151,10 @@ PolygonDrawingRenderer
glBegin(GL_LINES);
glColor3dv(aeCloseColor.data_block());
if(dvx.size())
- glVertex3f(dvx.back().x, dvx.back().y, 0);
+ glVertex3d(dvx.back().x, dvx.back().y, 0);
else
- glVertex3f(vx.back().x, vx.back().y, 0);
- glVertex3f(vx.front().x, vx.front().y, 0);
+ glVertex3d(vx.back().x, vx.back().y, 0);
+ glVertex3d(vx.front().x, vx.front().y, 0);
glEnd();
glPopAttrib();
}
@@ -177,7 +177,7 @@ PolygonDrawingRenderer
else
glColor3dv(aeEdit->GetNormalColor().data_block());
- glVertex3f(it->x,it->y,0.0f);
+ glVertex3d(it->x,it->y,0.0f);
}
}
@@ -186,7 +186,7 @@ PolygonDrawingRenderer
{
PolygonVertex last = dvx.back();
glColor3dv(aeEdit->GetActiveColor().data_block());
- glVertex3f(last.x, last.y, 0.0f);
+ glVertex3d(last.x, last.y, 0.0f);
}
glEnd();
@@ -209,9 +209,9 @@ PolygonDrawingRenderer
glLineWidth(1);
glColor3dv(aeEdit->GetActiveColor().data_block());
- Vector2f border = m_Model->GetPixelSize() * 4.0f;
+ Vector2d border = m_Model->GetPixelSize() * 4.0;
glLineWidth(1);
- glColor3fv(m_EditModeSelectedColor);
+ glColor3dv(m_EditModeSelectedColor);
DrawBox(m_Model->GetEditBox(), border[0], border[1]);
glPopAttrib();
}
diff --git a/GUI/Renderer/PolygonDrawingRenderer.h b/GUI/Renderer/PolygonDrawingRenderer.h
index 9339479..d5f093e 100644
--- a/GUI/Renderer/PolygonDrawingRenderer.h
+++ b/GUI/Renderer/PolygonDrawingRenderer.h
@@ -23,15 +23,15 @@ protected:
PolygonDrawingRenderer();
virtual ~PolygonDrawingRenderer() {}
- void DrawBox(const vnl_vector_fixed<float, 4> &box,
- float border_x = 0.0f, float border_y = 0.0f);
+ void DrawBox(const vnl_vector_fixed<double, 4> &box,
+ double border_x = 0.0, double border_y = 0.0);
PolygonDrawingModel *m_Model;
// Colors used to draw polygon
- const static float m_DrawingModeColor[];
- const static float m_EditModeSelectedColor[];
- const static float m_EditModeNormalColor[];
+ const static double m_DrawingModeColor[];
+ const static double m_EditModeSelectedColor[];
+ const static double m_EditModeNormalColor[];
};
#endif // POLYGONDRAWINGRENDERER_H
diff --git a/GUI/Renderer/RegistrationRenderer.cxx b/GUI/Renderer/RegistrationRenderer.cxx
new file mode 100644
index 0000000..1600968
--- /dev/null
+++ b/GUI/Renderer/RegistrationRenderer.cxx
@@ -0,0 +1,161 @@
+#include "RegistrationRenderer.h"
+#include "InteractiveRegistrationModel.h"
+#include "GenericSliceModel.h"
+#include "RegistrationModel.h"
+#include "SNAPAppearanceSettings.h"
+#include "SNAPOpenGL.h"
+#include "GlobalUIModel.h"
+#include "IRISApplication.h"
+#include "GenericImageData.h"
+#include "ImageWrapperBase.h"
+#include "OpenGLSliceTexture.h"
+
+RegistrationRenderer::RegistrationRenderer()
+{
+ m_Model = NULL;
+}
+
+RegistrationRenderer::~RegistrationRenderer()
+{
+
+}
+
+void RegistrationRenderer::DrawRotationWidget()
+{
+ // Draw the main line
+ glBegin(GL_LINE_LOOP);
+ for(int i = 0; i < 360; i++)
+ {
+ double alpha = i * vnl_math::pi / 180;
+ glVertex2d(cos(alpha), sin(alpha));
+ }
+ glEnd();
+
+ // Draw the hash marks at 5 degree intervals
+ glBegin(GL_LINES);
+ for(int i = 0; i < 360; i+=5)
+ {
+ double alpha = i * vnl_math::pi / 180;
+ double x = cos(alpha), y = sin(alpha);
+ glVertex2d(0.95 * x, 0.95 * y);
+ glVertex2d(1.05 * x, 1.05 * y);
+ }
+ glEnd();
+}
+
+void RegistrationRenderer::paintGL()
+{
+ assert(m_Model);
+
+ // Not in thumbnail mode
+ if(m_ParentRenderer->IsDrawingZoomThumbnail() || m_ParentRenderer->IsDrawingLayerThumbnail())
+ return;
+
+ // Find out what layer is being used for registration
+ RegistrationModel *rmodel = m_Model->GetRegistrationModel();
+ GenericSliceModel *smodel = m_Model->GetParent();
+ ImageWrapperBase *moving = rmodel->GetMovingLayerWrapper();
+ if(!moving)
+ return;
+
+ SNAPAppearanceSettings *as =
+ smodel->GetParentUI()->GetAppearanceSettings();
+
+ // Get the registration grid lines
+ OpenGLAppearanceElement *eltGrid =
+ as->GetUIElement(SNAPAppearanceSettings::REGISTRATION_GRID);
+
+ // How to draw the registration grid? Should it just be every 5 voxels?
+ glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // Apply the grid settings
+ eltGrid->ApplyLineSettings();
+ glColor3dv(eltGrid->GetNormalColor().data_block());
+
+ // Draw gridlines at regular intervals (this is dumb)
+ Vector2ui canvas = smodel->GetCanvasSize();
+
+ int spacing = 16 * smodel->GetSizeReporter()->GetViewportPixelRatio();
+
+ glBegin(GL_LINES);
+ for(int i = 0; i <= canvas[0]; i+=spacing)
+ {
+ glVertex2i(i, 0);
+ glVertex2i(i, canvas[1]);
+ }
+
+ for(int i = 0; i <= canvas[1]; i+=spacing)
+ {
+ glVertex2i(0, i);
+ glVertex2i(canvas[0], i);
+ }
+
+ glEnd();
+ glPopMatrix();
+ glPopAttrib();
+
+ // Should we draw the widget? Yes, if we are in tiled mode and are viewing the moving layer,
+ // and yes if we are in non-tiled mode.
+ unsigned long drawing_id = this->m_ParentRenderer->GetDrawingViewport()->layer_id;
+ if(m_Model->GetDoProcessInteractionOverLayer(drawing_id))
+ {
+ // Get the line color, thickness and dash spacing for the rotation widget
+ OpenGLAppearanceElement *eltWidgets =
+ as->GetUIElement(SNAPAppearanceSettings::REGISTRATION_WIDGETS);
+
+ // The rotation widget is a circular arc that is drawn around the center of rotation
+ // The radius of the arc is chosen so that there is maximum overlap between the arc
+ // and the screen area, minus a margin. For now though, we compute the radius in a
+ // very heuristic way
+ double radius = m_Model->GetRotationWidgetRadius();
+
+ // Get the center of rotation
+ Vector3ui rot_ctr_image = rmodel->GetRotationCenter();
+
+ // Map the center of rotation into the slice coordinates
+ Vector3d rot_ctr_slice = smodel->MapImageToSlice(to_double(rot_ctr_image));
+
+ // Set line properties
+ glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT);
+ glPushMatrix();
+
+ // The matrix is configured in the slice coordinate system. However, to draw the
+ // circle, we should be in a coordinate system where the origin is the center of
+ // rotation,
+
+ glTranslated(rot_ctr_slice[0], rot_ctr_slice[1], 0.0);
+ glScaled(0.5 * radius / smodel->GetSliceSpacing()[0], 0.5 * radius / smodel->GetSliceSpacing()[1], 1.0);
+
+ // Draw a white circle
+ eltWidgets->ApplyLineSettings();
+
+ if(m_Model->IsHoveringOverRotationWidget())
+ {
+ if(m_Model->GetLastTheta() != 0.0)
+ {
+ glColor3dv(eltWidgets->GetNormalColor().data_block());
+ this->DrawRotationWidget();
+
+ glColor3dv(eltWidgets->GetActiveColor().data_block());
+ glRotated(m_Model->GetLastTheta() * 180 / vnl_math::pi, 0.0, 0.0, 1.0);
+ this->DrawRotationWidget();
+ }
+ else
+ {
+ glColor3dv(eltWidgets->GetActiveColor().data_block());
+ this->DrawRotationWidget();
+ }
+ }
+ else
+ {
+ glColor3dv(eltWidgets->GetNormalColor().data_block());
+ this->DrawRotationWidget();
+ }
+
+ glPopMatrix();
+ glPopAttrib();
+ }
+}
+
diff --git a/GUI/Renderer/RegistrationRenderer.h b/GUI/Renderer/RegistrationRenderer.h
new file mode 100644
index 0000000..16ab1d5
--- /dev/null
+++ b/GUI/Renderer/RegistrationRenderer.h
@@ -0,0 +1,29 @@
+#ifndef REGISTRATIONRENDERER_H
+#define REGISTRATIONRENDERER_H
+
+#include "SNAPCommon.h"
+#include "GenericSliceRenderer.h"
+
+class InteractiveRegistrationModel;
+
+class RegistrationRenderer : public SliceRendererDelegate
+{
+public:
+ irisITKObjectMacro(RegistrationRenderer, SliceRendererDelegate)
+
+ irisSetMacro(Model, InteractiveRegistrationModel *)
+ irisGetMacro(Model, InteractiveRegistrationModel *)
+
+ void paintGL();
+
+protected:
+
+ RegistrationRenderer();
+ ~RegistrationRenderer();
+
+ InteractiveRegistrationModel *m_Model;
+private:
+ void DrawRotationWidget();
+};
+
+#endif // REGISTRATIONRENDERER_H
diff --git a/GUI/Renderer/SliceWindowDecorationRenderer.cxx b/GUI/Renderer/SliceWindowDecorationRenderer.cxx
index f972251..585081a 100644
--- a/GUI/Renderer/SliceWindowDecorationRenderer.cxx
+++ b/GUI/Renderer/SliceWindowDecorationRenderer.cxx
@@ -46,11 +46,11 @@ void SliceWindowDecorationRenderer::DrawOrientationLabels()
{
// Which axis are we on in anatomy space?
unsigned int anatomyAxis =
- parentModel->GetDisplayToAnatomyTransform().GetCoordinateIndexZeroBased(i);
+ parentModel->GetDisplayToAnatomyTransform()->GetCoordinateIndexZeroBased(i);
// Which direction is the axis facing (returns -1 or 1)
unsigned int anatomyAxisDirection =
- parentModel->GetDisplayToAnatomyTransform().GetCoordinateOrientation(i);
+ parentModel->GetDisplayToAnatomyTransform()->GetCoordinateOrientation(i);
// Map the direction onto 0 or 1
unsigned int letterIndex = (1 + anatomyAxisDirection) >> 1;
@@ -109,6 +109,8 @@ std::string SliceWindowDecorationRenderer::GetDisplayText(ImageWrapperBase *laye
MultiChannelDisplayMode mode = policy->GetDisplayMode();
if(mode.UseRGB)
nickname += " [RGB]";
+ else if(mode.RenderAsGrid)
+ nickname += " [Grid]";
else if(mode.SelectedScalarRep == SCALAR_REP_MAGNITUDE)
nickname += " [Mag]";
else if(mode.SelectedScalarRep == SCALAR_REP_MAX)
diff --git a/GUI/Renderer/SnakeModeRenderer.cxx b/GUI/Renderer/SnakeModeRenderer.cxx
index 5264f55..11d1e16 100644
--- a/GUI/Renderer/SnakeModeRenderer.cxx
+++ b/GUI/Renderer/SnakeModeRenderer.cxx
@@ -75,13 +75,13 @@ void SnakeModeRenderer::DrawBubbles()
cl.GetRGBVector(rgb);
// Get the current crosshairs position
- Vector3f cursorImage = to_float(app->GetCursorPosition()) + Vector3f(0.5f);
+ Vector3d cursorImage = to_double(app->GetCursorPosition()) + Vector3d(0.5);
// Get the image space dimension that corresponds to this window
int iid = sliceModel->GetSliceDirectionInImageSpace();
// Get the other essentials from the parent
- Vector3f scaling = sliceModel->GetSliceSpacing();
+ Vector3d scaling = sliceModel->GetSliceSpacing();
// Turn on alpha blending
glPushAttrib(GL_POLYGON_BIT | GL_COLOR_BUFFER_BIT);
@@ -97,11 +97,11 @@ void SnakeModeRenderer::DrawBubbles()
{
// Get the center and radius of the i-th bubble
- Vector3f ctrImage = to_float(bubbles[i].center) + Vector3f(0.5f);
+ Vector3d ctrImage = to_double(bubbles[i].center) + Vector3d(0.5);
double radius = bubbles[i].radius;
// Remap the center into slice coordinates
- Vector3f ctrSlice = sliceModel->MapImageToSlice(to_float(ctrImage));
+ Vector3d ctrSlice = sliceModel->MapImageToSlice(ctrImage);
// Compute the offset from the center along the slice z-direction
// in physical coordinates
diff --git a/GUI/Renderer/SnakeParameterPreviewRenderer.cxx b/GUI/Renderer/SnakeParameterPreviewRenderer.cxx
index 0a7b597..2a04c7b 100644
--- a/GUI/Renderer/SnakeParameterPreviewRenderer.cxx
+++ b/GUI/Renderer/SnakeParameterPreviewRenderer.cxx
@@ -6,14 +6,13 @@
SnakeParameterPreviewRenderer::SnakeParameterPreviewRenderer()
{
// Initialize the texture object
- m_Texture = new OpenGLSliceTexture<RGBAType>;
+ m_Texture = Texture::New();
m_Texture->SetGlComponents(4);
m_Texture->SetGlFormat(GL_RGBA);
}
SnakeParameterPreviewRenderer::~SnakeParameterPreviewRenderer()
{
- delete m_Texture;
}
void SnakeParameterPreviewRenderer::initializeGL()
diff --git a/GUI/Renderer/SnakeParameterPreviewRenderer.h b/GUI/Renderer/SnakeParameterPreviewRenderer.h
index 037d4be..60b5794 100644
--- a/GUI/Renderer/SnakeParameterPreviewRenderer.h
+++ b/GUI/Renderer/SnakeParameterPreviewRenderer.h
@@ -44,7 +44,8 @@ protected:
SnakeParametersPreviewPipeline *m_Pipeline;
/** A texture object used to store the image */
- OpenGLSliceTexture<RGBAType> *m_Texture;
+ typedef OpenGLSliceTexture<RGBAType> Texture;
+ SmartPtr<Texture> m_Texture;
/** Which force is being displayed? */
DisplayMode m_ForceToDisplay;
diff --git a/GUI/Renderer/SnakeROIRenderer.cxx b/GUI/Renderer/SnakeROIRenderer.cxx
index dae8a29..4adc24f 100644
--- a/GUI/Renderer/SnakeROIRenderer.cxx
+++ b/GUI/Renderer/SnakeROIRenderer.cxx
@@ -28,7 +28,7 @@ void SnakeROIRenderer::paintGL()
assert(gs->isSegmentationROIValid());
// Compute the corners in slice coordinates
- Vector3f corner[2];
+ Vector3d corner[2];
m_Model->GetSystemROICorners(corner);
// Check that the current slice is actually within the bounding box
@@ -65,12 +65,12 @@ void SnakeROIRenderer::paintGL()
elt->GetActiveColor().data_block() : elt->GetNormalColor().data_block() );
// Compute the vertices of the edge
- Vector2f x0,x1;
+ Vector2d x0,x1;
m_Model->GetEdgeVertices(dir,i,x0,x1,corner);
// Draw the line
- glVertex2f(x0[0],x0[1]);
- glVertex2f(x1[0],x1[1]);
+ glVertex2d(x0[0],x0[1]);
+ glVertex2d(x1[0],x1[1]);
}
}
diff --git a/GUI/Renderer/Window3DPicker.cxx b/GUI/Renderer/Window3DPicker.cxx
index 4ff4800..a2b374d 100644
--- a/GUI/Renderer/Window3DPicker.cxx
+++ b/GUI/Renderer/Window3DPicker.cxx
@@ -7,6 +7,7 @@
#include "ImageRayIntersectionFinder.h"
#include "Generic3DModel.h"
#include "vtkObjectFactory.h"
+#include "ImageWrapperTraits.h"
/** These classes are used internally for m_Ray intersection testing */
class LabelImageHitTester
@@ -60,8 +61,8 @@ double Window3DPicker::IntersectWithLine(
ImageWrapperBase *main = app->GetCurrentImageData()->GetMain();
// Transform the points into voxel space
- Vector3d x0 = main->TransformNIFTICoordinatesToVoxelIndex(Vector3d(p1));
- Vector3d x1 = main->TransformNIFTICoordinatesToVoxelIndex(Vector3d(p2));
+ Vector3d x0 = main->TransformNIFTICoordinatesToVoxelCIndex(Vector3d(p1));
+ Vector3d x1 = main->TransformNIFTICoordinatesToVoxelCIndex(Vector3d(p2));
Vector3i pos;
int result = -1;
@@ -70,7 +71,7 @@ double Window3DPicker::IntersectWithLine(
// Intersect with the correct wrapper
if(app->IsSnakeModeActive() && app->GetSNAPImageData()->IsSnakeLoaded())
{
- typedef ImageRayIntersectionFinder<float, SnakeImageHitTester> RayCasterType;
+ typedef ImageRayIntersectionFinder<itk::Image<float, 3>, SnakeImageHitTester> RayCasterType;
RayCasterType caster;
result = caster.FindIntersection(
@@ -79,7 +80,7 @@ double Window3DPicker::IntersectWithLine(
else
{
LabelImageWrapper *layer = app->GetCurrentImageData()->GetSegmentation();
- typedef ImageRayIntersectionFinder<LabelType, LabelImageHitTester> Finder;
+ typedef ImageRayIntersectionFinder<LabelImageWrapperTraits::ImageType, LabelImageHitTester> Finder;
Finder finder;
LabelImageHitTester tester(app->GetColorLabelTable());
finder.SetHitTester(tester);
@@ -114,6 +115,5 @@ Window3DPicker::Window3DPicker()
m_Model = NULL;
}
-vtkCxxRevisionMacro(Window3DPicker, "$Revision: 1.1 $")
vtkStandardNewMacro(Window3DPicker)
diff --git a/GUI/Renderer/Window3DPicker.h b/GUI/Renderer/Window3DPicker.h
index eb86663..c7113b9 100644
--- a/GUI/Renderer/Window3DPicker.h
+++ b/GUI/Renderer/Window3DPicker.h
@@ -11,7 +11,7 @@ class Window3DPicker : public vtkPicker
public:
static Window3DPicker *New();
- vtkTypeRevisionMacro(Window3DPicker, vtkPicker)
+ vtkTypeMacro(Window3DPicker, vtkPicker)
irisGetSetMacro(Model, Generic3DModel *)
diff --git a/Logic/Common/ImageCoordinateGeometry.cxx b/Logic/Common/ImageCoordinateGeometry.cxx
index 06e0a10..882f601 100644
--- a/Logic/Common/ImageCoordinateGeometry.cxx
+++ b/Logic/Common/ImageCoordinateGeometry.cxx
@@ -47,6 +47,14 @@ ImageCoordinateGeometry
::ImageCoordinateGeometry()
{
// By default all transforms are identity. Nothing to do here
+ m_ImageToAnatomyTransform = ImageCoordinateTransform::New();
+
+ for(int i = 0; i < 3; i++)
+ {
+ m_AnatomyToDisplayTransform[i] = ImageCoordinateTransform::New();
+ m_ImageToDisplayTransform[i] = ImageCoordinateTransform::New();
+ m_DisplayToImageTransform[i] = ImageCoordinateTransform::New();
+ }
}
ImageCoordinateGeometry
@@ -80,11 +88,11 @@ ImageCoordinateGeometry
imageAnatomyRAICode.c_str());
// We can easily compute the image to anatomy transform
- m_ImageToAnatomyTransform.SetTransform(
+ m_ImageToAnatomyTransform->SetTransform(
ConvertRAIToCoordinateMapping(imageAnatomyRAICode.c_str()),imageSize);
// Compute the size of the anatomy image in its coordinates
- Vector3ui szAnatomy = m_ImageToAnatomyTransform.TransformSize(imageSize);
+ Vector3ui szAnatomy = m_ImageToAnatomyTransform->TransformSize(imageSize);
// Compute the anatomy to display transform
for(unsigned int slice=0;slice < 3;slice++)
@@ -95,17 +103,17 @@ ImageCoordinateGeometry
// Make sure the code is valid
assert(IsRAICodeValid(displayAnatomyRAICode));
- m_AnatomyToDisplayTransform[slice].SetTransform(
+ m_AnatomyToDisplayTransform[slice]->SetTransform(
InvertMappingVector(ConvertRAIToCoordinateMapping(displayAnatomyRAICode)),
szAnatomy);
// Compute the combined transform
- m_ImageToDisplayTransform[slice] =
- m_AnatomyToDisplayTransform[slice].Product(m_ImageToAnatomyTransform);
-
+ m_AnatomyToDisplayTransform[slice]->ComputeProduct(
+ m_ImageToAnatomyTransform, m_ImageToDisplayTransform[slice]);
+
// Compute the opposite direction transform
- m_DisplayToImageTransform[slice] =
- m_ImageToDisplayTransform[slice].Inverse();
+ m_ImageToDisplayTransform[slice]->ComputeInverse(
+ m_DisplayToImageTransform[slice]);
}
}
diff --git a/Logic/Common/ImageCoordinateGeometry.h b/Logic/Common/ImageCoordinateGeometry.h
index b10350f..e9149db 100644
--- a/Logic/Common/ImageCoordinateGeometry.h
+++ b/Logic/Common/ImageCoordinateGeometry.h
@@ -83,25 +83,25 @@ public:
const Vector3ui &imageSize);
/** Get the transform from image to patient coordinate system */
- irisGetMacro(ImageToAnatomyTransform,const ImageCoordinateTransform &)
+ irisGetMacro(ImageToAnatomyTransform,const ImageCoordinateTransform *)
/** Get the image to anatomy direction matrix */
irisGetMacro(ImageDirectionCosineMatrix, DirectionMatrix)
/** Get the transform from patient to display coordinate system */
- const ImageCoordinateTransform & GetAnatomyToDisplayTransform(unsigned int i) const
+ const ImageCoordinateTransform *GetAnatomyToDisplayTransform(unsigned int i) const
{
return m_AnatomyToDisplayTransform[i];
}
/** Get the transform from image to display coordinate system */
- const ImageCoordinateTransform & GetImageToDisplayTransform(unsigned int i) const
+ const ImageCoordinateTransform *GetImageToDisplayTransform(unsigned int i) const
{
return m_ImageToDisplayTransform[i];
}
/** Get the transform from display to image coordinate system */
- const ImageCoordinateTransform & GetDisplayToImageTransform(unsigned int i) const
+ const ImageCoordinateTransform *GetDisplayToImageTransform(unsigned int i) const
{
return m_DisplayToImageTransform[i];
}
@@ -142,12 +142,12 @@ public:
private:
// Slice transform information
- ImageCoordinateTransform m_ImageToAnatomyTransform;
- ImageCoordinateTransform m_AnatomyToDisplayTransform[3];
+ SmartPtr<ImageCoordinateTransform> m_ImageToAnatomyTransform;
+ SmartPtr<ImageCoordinateTransform> m_AnatomyToDisplayTransform[3];
// Three anatomy to display transforms (for each of the 3D slices)
- ImageCoordinateTransform m_ImageToDisplayTransform[3];
- ImageCoordinateTransform m_DisplayToImageTransform[3];
+ SmartPtr<ImageCoordinateTransform> m_ImageToDisplayTransform[3];
+ SmartPtr<ImageCoordinateTransform> m_DisplayToImageTransform[3];
// The string representation of the display to anatomy RAI codes
IRISDisplayGeometry m_DisplayGeometry;
diff --git a/Logic/Common/ImageCoordinateTransform.cxx b/Logic/Common/ImageCoordinateTransform.cxx
index 1a38638..8a83135 100644
--- a/Logic/Common/ImageCoordinateTransform.cxx
+++ b/Logic/Common/ImageCoordinateTransform.cxx
@@ -40,7 +40,8 @@
#include <IRISVectorTypes.h>
Vector3ui IRISSNAPdummyVector;
-iris_vector_fixed<float,3> irissnapdummyvector = to_float<unsigned int,3>(IRISSNAPdummyVector);
+iris_vector_fixed<double,3> irissnapdummyvector
+ = to_double<unsigned int,3>(IRISSNAPdummyVector);
ImageCoordinateTransform
@@ -80,7 +81,7 @@ ImageCoordinateTransform
}
// Initialize the offset vector
- m_Offset = m_Transform * to_float(size);
+ m_Offset = m_Transform * to_double(size);
for(i=0;i<3;i++)
{
@@ -88,7 +89,24 @@ ImageCoordinateTransform
m_Offset[i] = m_Offset[i] < 0 ? - m_Offset[i] : 0;
}
- ComputeSecondaryVectors();
+ ComputeSecondaryVectors();
+}
+
+void
+ImageCoordinateTransform
+::SetTransform(const ImageCoordinateTransform::Self *other)
+{
+ // A transform matrix
+ m_Transform = other->m_Transform;
+
+ // An offset vector
+ m_Offset = other->m_Offset;
+
+ // The operation abs(i) - 1 applied to m_Mapping
+ m_AxesIndex = other->m_AxesIndex;
+
+ // The operation sign(i) applied to m_Mapping
+ m_AxesDirection = other->m_AxesDirection;
}
void
@@ -98,98 +116,46 @@ ImageCoordinateTransform
// For this calculation we need the transpose of the matrix
MatrixType T = m_Transform.transpose();
- Vector3f map = T * Vector3f(0.0f,1.0f,2.0f);
+ Vector3d map = T * Vector3d(0.0, 1.0, 2.0);
m_AxesIndex[0] = (unsigned int) fabs(map[0]);
m_AxesIndex[1] = (unsigned int) fabs(map[1]);
m_AxesIndex[2] = (unsigned int) fabs(map[2]);
- m_AxesDirection = to_int(T * Vector3f(1.0f));
+ m_AxesDirection = to_int(T * Vector3d(1.0));
}
+void
ImageCoordinateTransform
-ImageCoordinateTransform
-::Inverse() const
+::ComputeInverse(Self *result) const
{
// Compute the new transform's details
- ImageCoordinateTransform inv;
- inv.m_Transform = vnl_inverse(m_Transform);
- inv.m_Offset = - inv.m_Transform * m_Offset;
+ result->m_Transform = vnl_inverse(m_Transform);
+ result->m_Offset = - result->m_Transform * m_Offset;
// Compute additional quantities
- inv.ComputeSecondaryVectors();
-
- /*
- // Compute the transpose of the transform
- int newMapping[3];
- for(int i=0;i<3;i++)
- {
- if(m_Mapping[i] > 0)
- newMapping[m_Mapping[i]-1] = i+1;
- else
- newMapping[-1-m_Mapping[i]] = -i-1;
- }
-
- return ImageCoordinateTransform(newMapping[0],newMapping[1],newMapping[2]);
- */
-
- return inv;
+ result->ComputeSecondaryVectors();
}
-ImageCoordinateTransform
-ImageCoordinateTransform
-::Product(const ImageCoordinateTransform &t1) const
+void ImageCoordinateTransform::ComputeProduct(const Self *t1, Self *result) const
{
// Compute the new transform's details
- ImageCoordinateTransform prod;
- prod.m_Transform = m_Transform * t1.m_Transform;
- prod.m_Offset = m_Transform * t1.m_Offset + m_Offset;
+ result->m_Transform = m_Transform * t1->m_Transform;
+ result->m_Offset = m_Transform * t1->m_Offset + m_Offset;
// Compute additional quantities
- prod.ComputeSecondaryVectors();
-/*
- // Multiply two transforms
- int newMapping[3];
- for(int i=0;i<3;i++)
- {
- if(m_Mapping[i] > 0)
- newMapping[i] = t1.m_Mapping[m_Mapping[i]-1];
- else
- newMapping[i] = -t1.m_Mapping[-1-m_Mapping[i]];
- }
-
- return ImageCoordinateTransform(newMapping[0],newMapping[1],newMapping[2]);
-*/
-
- return prod;
+ result->ComputeSecondaryVectors();
}
-Vector3f
-ImageCoordinateTransform
-::TransformPoint(const Vector3f &x) const
+Vector3d ImageCoordinateTransform::TransformPoint(const Vector3d &x) const
{
return m_Transform * x + m_Offset;
-/*
- // Transform the vector
- Vector3f xVec = TransformVector(x);
-
- // Transform the size
- Vector3f xSpace = TransformVector(xSourceVolumeSize);
-
- // Compute an offset to add to the vector
- if(xVec[0] < 0) xVec[0] -= xSpace[0];
- if(xVec[1] < 0) xVec[1] -= xSpace[1];
- if(xVec[2] < 0) xVec[2] -= xSpace[2];
-
- // Return the result
- return xVec;
-*/
}
-Vector3f
+Vector3d
ImageCoordinateTransform
-::TransformVector(const Vector3f &x) const
+::TransformVector(const Vector3d &x) const
{
return m_Transform * x;
}
@@ -198,7 +164,7 @@ Vector3ui
ImageCoordinateTransform
::TransformSize(const Vector3ui &sz) const
{
- Vector3f szSigned = m_Transform * to_float(sz);
+ Vector3d szSigned = m_Transform * to_double(sz);
return Vector3ui(
(unsigned int)fabs(szSigned(0)),
(unsigned int)fabs(szSigned(1)),
@@ -210,13 +176,13 @@ ImageCoordinateTransform
::TransformVoxelIndex(const Vector3ui &x) const
{
// Convert to voxel center coordinates
- Vector3f xVoxelCenter = to_float(x) + Vector3f(0.5);
+ Vector3d xVoxelCenter = to_double(x) + Vector3d(0.5);
// Make sure the mapping is legit
- Vector3f xMappedCenter = TransformPoint(xVoxelCenter);
- assert(xMappedCenter(0) >= 0.0f &&
- xMappedCenter(1) >= 0.0f &&
- xMappedCenter(2) >= 0.0f);
+ Vector3d xMappedCenter = TransformPoint(xVoxelCenter);
+ assert(xMappedCenter(0) >= 0.0 &&
+ xMappedCenter(1) >= 0.0 &&
+ xMappedCenter(2) >= 0.0);
// Compute the result and scale down to integers
return to_unsigned_int(xMappedCenter);
diff --git a/Logic/Common/ImageCoordinateTransform.h b/Logic/Common/ImageCoordinateTransform.h
index 2dae670..e7704a3 100644
--- a/Logic/Common/ImageCoordinateTransform.h
+++ b/Logic/Common/ImageCoordinateTransform.h
@@ -37,6 +37,8 @@
#include "SNAPCommon.h"
#include <vnl/vnl_matrix_fixed.h>
+#include "itkObjectFactory.h"
+#include "itkSmartPointer.h"
/**
* \class ImageCoordinateTransform
@@ -54,30 +56,44 @@
* mapping vector (example: (-1,3,-2) means x maps to -x, y to z and z to -y),
* as well as the size of the image volume.
*/
-class ImageCoordinateTransform
+class ImageCoordinateTransform : public itk::Object
{
public:
- /** Default constructor creates an identity transform */
- ImageCoordinateTransform();
-
+ /** Standard class typedefs. */
+ typedef ImageCoordinateTransform Self;
+ typedef itk::Object Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(ImageCoordinateTransform, itk::Object)
+
+ /** define the New method */
+ itkNewMacro(Self)
+
/**
* Initialize the transform with new singed coordinate mappings
* (1-based signed indices)
*/
void SetTransform(const Vector3i &map, const Vector3ui &size);
+ /**
+ * Set to an existing transform
+ */
+ void SetTransform(const Self *other);
+
/** Compute the inverse of this transform */
- ImageCoordinateTransform Inverse() const;
+ void ComputeInverse(Self *result) const;
/** Multiply by another transform */
- ImageCoordinateTransform Product(const ImageCoordinateTransform &t1) const;
+ void ComputeProduct(const Self *t1, Self *result) const;
/** Apply transform to a vector */
- Vector3f TransformVector(const Vector3f &x) const;
+ Vector3d TransformVector(const Vector3d &x) const;
/** Apply transform to a point. */
- Vector3f TransformPoint(const Vector3f &x) const;
+ Vector3d TransformPoint(const Vector3d &x) const;
/** Apply to an integer voxel index */
Vector3ui TransformVoxelIndex(const Vector3ui &xVoxel) const;
@@ -97,14 +113,19 @@ public:
return m_AxesDirection[c];
}
-private:
- typedef vnl_matrix_fixed<float,3,3> MatrixType;
+protected:
+
+ /** Default constructor creates an identity transform */
+ ImageCoordinateTransform();
+ ~ImageCoordinateTransform() {}
+
+ typedef vnl_matrix_fixed<double,3,3> MatrixType;
// A transform matrix
MatrixType m_Transform;
// An offset vector
- Vector3f m_Offset;
+ Vector3d m_Offset;
// The operation abs(i) - 1 applied to m_Mapping
Vector3i m_AxesIndex;
diff --git a/Logic/Common/ImageRayIntersectionFinder.h b/Logic/Common/ImageRayIntersectionFinder.h
index 41a3ccc..aad8e16 100644
--- a/Logic/Common/ImageRayIntersectionFinder.h
+++ b/Logic/Common/ImageRayIntersectionFinder.h
@@ -6,8 +6,8 @@
Date: $Date: 2009/01/23 20:09:38 $
Version: $Revision: 1.3 $
Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
+
+ This file is part of ITK-SNAP
ITK-SNAP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -29,7 +29,7 @@
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE. See the above copyright notices for more information.
+ PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef __ImageRayIntersectionFinder_h_
@@ -42,26 +42,26 @@
* \class ImageRayIntersectionFinder
* \brief An algorithm for testing ray hits against arbitrary images.
* This algorithm traverses a ray until it finds a pixel that satisfies the
- * hit tester (a functor with operator () which returns 0 for no-hit and
+ * hit tester (a functor with operator () which returns 0 for no-hit and
* 1 for hit).
*/
-template <class TPixel, class THitTester>
+template <class TImage, class THitTester>
class ImageRayIntersectionFinder
{
public:
virtual ~ImageRayIntersectionFinder() {}
/** Image type */
- typedef itk::Image<TPixel,3> ImageType;
+ typedef TImage ImageType;
/** Set the hit-test functor to evaluate for hits */
irisSetMacro(HitTester,THitTester);
- /**
+ /**
* Compute the intersection (index of the first pixel in the
- * image that the ray crosses and which satisfies the THitTester's
+ * image that the ray crosses and which satisfies the THitTester's
* condition.
*
- * Returns: 1 on success, 0 on no hit and -1 if the ray misses the
+ * Returns: 1 on success, 0 on no hit and -1 if the ray misses the
* image completely.
*/
int FindIntersection(ImageType *image,Vector3d xRayStart,
diff --git a/Logic/Common/ImageRayIntersectionFinder.txx b/Logic/Common/ImageRayIntersectionFinder.txx
index b3a2220..d3b03e3 100644
--- a/Logic/Common/ImageRayIntersectionFinder.txx
+++ b/Logic/Common/ImageRayIntersectionFinder.txx
@@ -6,8 +6,8 @@
Date: $Date: 2009/01/23 20:09:38 $
Version: $Revision: 1.4 $
Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
+
+ This file is part of ITK-SNAP
ITK-SNAP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -29,20 +29,20 @@
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE. See the above copyright notices for more information.
+ PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "itkImage.h"
-template <class TPixel, class THitTester>
+template <class TImage, class THitTester>
int
-ImageRayIntersectionFinder<TPixel,THitTester>
-::FindIntersection(ImageType *image,Vector3d point,
+ImageRayIntersectionFinder<TImage, THitTester>
+::FindIntersection(TImage *image, Vector3d point,
Vector3d ray,Vector3i &hit) const
{
typename ImageType::IndexType lIndex;
- typename ImageType::SizeType size =
+ typename ImageType::SizeType size =
image->GetLargestPossibleRegion().GetSize();
double delta[3][3] = {{0.,0.,0.},{0.,0.,0.},{0.,0.,0.}}, dratio[3]={0.,0.,0.};
@@ -99,8 +99,8 @@ ImageRayIntersectionFinder<TPixel,THitTester>
lIndex[2] = (int)pz;
// Get the pixel
- TPixel hitPixel = image->GetPixel(lIndex);
-
+ typename ImageType::PixelType hitPixel = image->GetPixel(lIndex);
+
// Test if the pixel is a hit
if(m_HitTester(hitPixel))
{
@@ -146,7 +146,7 @@ ImageRayIntersectionFinder<TPixel,THitTester>
delta[2][0] = dratio[2] * rx;
}
- // choose the shortest path
+ // choose the shortest path
if ( fabs(delta[0][0]) <= fabs(delta[1][0]) && fabs(delta[0][0]) <= fabs(delta[2][0]) )
{
dratio[0] = delta[0][0]/rx;
@@ -164,7 +164,7 @@ ImageRayIntersectionFinder<TPixel,THitTester>
pz += delta[1][2];
}
else
- { //if (fabs(delta[2][0] <= fabs(delta[0][0] && fabs(delta[2][0] <= fabs(delta[0][0])
+ { //if (fabs(delta[2][0] <= fabs(delta[0][0] && fabs(delta[2][0] <= fabs(delta[0][0])
delta[2][1] = dratio[2] * ry;
px += delta[2][0];
py += delta[2][1];
diff --git a/Logic/Common/MetaDataAccess.cxx b/Logic/Common/MetaDataAccess.cxx
index 1cb7ba5..2a20d9f 100644
--- a/Logic/Common/MetaDataAccess.cxx
+++ b/Logic/Common/MetaDataAccess.cxx
@@ -89,6 +89,11 @@ std::string MetaDataAccess::GetValueAsString(const std::string &key)
return value;
}
+bool MetaDataAccess::HasKey(const string &key) const
+{
+ return m_Image->GetMetaDataDictionary().HasKey(key);
+}
+
std::string MetaDataAccess::MapKeyToDICOM(std::string key)
{
// Try to remap the key to DICOM
diff --git a/Logic/Common/MetaDataAccess.h b/Logic/Common/MetaDataAccess.h
index 408f39a..ad34807 100644
--- a/Logic/Common/MetaDataAccess.h
+++ b/Logic/Common/MetaDataAccess.h
@@ -19,6 +19,8 @@ public:
std::vector<std::string> GetKeysAsArray();
std::string GetValueAsString(const std::string &key);
+ bool HasKey(const std::string &key) const;
+
// Useful routine for mapping orientation strings to text
static std::string GetRAICode(
itk::SpatialOrientation::ValidCoordinateOrientationFlags code);
diff --git a/Logic/Common/SNAPAppearanceSettings.cxx b/Logic/Common/SNAPAppearanceSettings.cxx
index 531253e..c0590ff 100644
--- a/Logic/Common/SNAPAppearanceSettings.cxx
+++ b/Logic/Common/SNAPAppearanceSettings.cxx
@@ -44,7 +44,10 @@ SNAPAppearanceSettings
{ 1, 0, 1, 0, 1, 1, 1 }, // Rulers
{ 1, 0, 1, 1, 0, 0, 1 }, // POLY_DRAW_MAIN
{ 1, 0, 1, 1, 0, 1, 1 }, // POLY_DRAW_CLOSE
- { 1, 1, 1, 1, 0, 0, 1 } // POLY_EDIT
+ { 1, 1, 1, 1, 0, 0, 1 }, // POLY_EDIT
+ { 1, 1, 1, 1, 0, 0, 1 }, // REGISTRATION_WIDGETS
+ { 1, 0, 1, 1, 0, 0, 1 }, // REGISTRATION_GRID
+ { 1, 0, 1, 0, 0, 0, 1 } // GRID_LINES
};
void
@@ -205,6 +208,36 @@ SNAPAppearanceSettings
elt->SetLineThickness(2.0);
elt->SetVisible(true);
elt->SetAlphaBlending(true);
+
+ // Registration widget
+ elt = m_DefaultElementSettings[REGISTRATION_WIDGETS];
+ elt->SetNormalColor(Vector3d(1.0, 1.0, 1.0));
+ elt->SetActiveColor(Vector3d(1.0, 1.0, 0.4));
+ elt->SetLineThickness(1.0);
+ elt->SetDashSpacing(0.0);
+ elt->SetFontSize(0);
+ elt->SetVisible(true);
+ elt->SetAlphaBlending(true);
+
+ // Registration widget
+ elt = m_DefaultElementSettings[REGISTRATION_GRID];
+ elt->SetNormalColor(Vector3d(0.8, 0.8, 0.8));
+ elt->SetActiveColor(Vector3d(1.0, 1.0, 1.0));
+ elt->SetLineThickness(0.5);
+ elt->SetDashSpacing(0.0);
+ elt->SetFontSize(0);
+ elt->SetVisible(true);
+ elt->SetAlphaBlending(true);
+
+ // Warp grid lines
+ elt = m_DefaultElementSettings[GRID_LINES];
+ elt->SetNormalColor(Vector3d(1.0, 1.0, 0.0));
+ elt->SetActiveColor(Vector3d(1.0, 1.0, 1.0));
+ elt->SetLineThickness(1.0);
+ elt->SetDashSpacing(0.0);
+ elt->SetFontSize(0);
+ elt->SetVisible(true);
+ elt->SetAlphaBlending(true);
}
const char *
@@ -213,7 +246,8 @@ SNAPAppearanceSettings
{ "CROSSHAIRS", "MARKERS", "ROI_BOX", "BACKGROUND_2D", "BACKGROUND_3D",
"ZOOM_THUMBNAIL", "CROSSHAIRS_3D", "CROSSHAIRS_THUMB", "IMAGE_BOX_3D",
"ROI_BOX_3D", "RULER", "PAINTBRUSH_OUTLINE",
- "POLY_DRAW_MAIN", "POLY_DRAW_CLOSE", "POLY_EDIT"};
+ "POLY_DRAW_MAIN", "POLY_DRAW_CLOSE", "POLY_EDIT",
+ "REGISTRATION_WIDGETS", "REGISTRATION_GRID", "GRID_LINES"};
SNAPAppearanceSettings
::SNAPAppearanceSettings()
@@ -315,7 +349,6 @@ GlobalDisplaySettings::GlobalDisplaySettings()
m_LayerLayoutModel =
NewSimpleEnumProperty("LayerLayout", LAYOUT_STACKED, emap_layer_layout);
-
}
void GlobalDisplaySettings
diff --git a/Logic/Common/SNAPAppearanceSettings.h b/Logic/Common/SNAPAppearanceSettings.h
index 0299766..1d314bc 100644
--- a/Logic/Common/SNAPAppearanceSettings.h
+++ b/Logic/Common/SNAPAppearanceSettings.h
@@ -177,6 +177,7 @@ public:
ZOOM_THUMBNAIL, CROSSHAIRS_3D, CROSSHAIRS_THUMB,
IMAGE_BOX_3D, ROI_BOX_3D, PAINTBRUSH_OUTLINE, RULER,
POLY_DRAW_MAIN, POLY_DRAW_CLOSE, POLY_EDIT,
+ REGISTRATION_WIDGETS, REGISTRATION_GRID, GRID_LINES,
ELEMENT_COUNT
};
diff --git a/Logic/Common/SNAPRegistryIO.cxx b/Logic/Common/SNAPRegistryIO.cxx
index 36eb4d0..630e21b 100644
--- a/Logic/Common/SNAPRegistryIO.cxx
+++ b/Logic/Common/SNAPRegistryIO.cxx
@@ -50,7 +50,7 @@
RegistryEnumMap<CoverageModeType> SNAPRegistryIO::m_EnumMapCoverage;
RegistryEnumMap<SnakeParameters::SolverType> SNAPRegistryIO::m_EnumMapSolver;
RegistryEnumMap<SnakeParameters::SnakeType> SNAPRegistryIO::m_EnumMapSnakeType;
-RegistryEnumMap<SNAPSegmentationROISettings::InterpolationMethod> SNAPRegistryIO::m_EnumMapROI;
+RegistryEnumMap<InterpolationMethod> SNAPRegistryIO::m_EnumMapROI;
RegistryEnumMap<LayerRole> SNAPRegistryIO::m_EnumMapLayerRole;
RegistryEnumMap<LayerLayout> SNAPRegistryIO::m_EnumMapLayerLayout;
@@ -132,7 +132,7 @@ SNAPRegistryIO
registry["Ground"] << in.GetGround();
registry["Clamp"] << in.GetClamp();
registry["PropagationWeight"] << in.GetPropagationWeight();
- registry["PropagationSpeedExponent"][in.GetPropagationSpeedExponent()];
+ registry["PropagationSpeedExponent"] << in.GetPropagationSpeedExponent();
registry["CurvatureWeight"] << in.GetCurvatureWeight();
registry["CurvatureSpeedExponent"] << in.GetCurvatureSpeedExponent();
registry["LaplacianWeight"] << in.GetLaplacianWeight();
@@ -195,7 +195,7 @@ RegistryEnumMap<SnakeParameters::SnakeType> &SNAPRegistryIO::GetEnumMapSnakeType
return m_EnumMapSnakeType;
}
-RegistryEnumMap<SNAPSegmentationROISettings::InterpolationMethod> &SNAPRegistryIO::GetEnumMapROI()
+RegistryEnumMap<InterpolationMethod> &SNAPRegistryIO::GetEnumMapROI()
{
BuildEnums();
return m_EnumMapROI;
@@ -444,10 +444,10 @@ void SNAPRegistryIO::BuildEnums()
m_EnumMapSnakeType.AddPair(SnakeParameters::EDGE_SNAKE,"EdgeStopping");
m_EnumMapSnakeType.AddPair(SnakeParameters::REGION_SNAKE,"RegionCompetition");
- m_EnumMapROI.AddPair(SNAPSegmentationROISettings::NEAREST_NEIGHBOR,"Nearest");
- m_EnumMapROI.AddPair(SNAPSegmentationROISettings::TRILINEAR,"TriLinear");
- m_EnumMapROI.AddPair(SNAPSegmentationROISettings::TRICUBIC,"Cubic");
- m_EnumMapROI.AddPair(SNAPSegmentationROISettings::SINC_WINDOW_05,"Sinc05");
+ m_EnumMapROI.AddPair(NEAREST_NEIGHBOR,"Nearest");
+ m_EnumMapROI.AddPair(TRILINEAR,"TriLinear");
+ m_EnumMapROI.AddPair(TRICUBIC,"Cubic");
+ m_EnumMapROI.AddPair(SINC_WINDOW_05,"Sinc05");
m_EnumMapLayerRole.AddPair(MAIN_ROLE, "MainRole");
m_EnumMapLayerRole.AddPair(OVERLAY_ROLE, "OverlayRole");
diff --git a/Logic/Common/SNAPRegistryIO.h b/Logic/Common/SNAPRegistryIO.h
index 2989fa4..9d426d2 100644
--- a/Logic/Common/SNAPRegistryIO.h
+++ b/Logic/Common/SNAPRegistryIO.h
@@ -90,7 +90,7 @@ public:
static RegistryEnumMap<CoverageModeType> &GetEnumMapCoverage();
static RegistryEnumMap<SnakeParameters::SolverType> &GetEnumMapSolver();
static RegistryEnumMap<SnakeParameters::SnakeType> &GetEnumMapSnakeType();
- static RegistryEnumMap<SNAPSegmentationROISettings::InterpolationMethod> &GetEnumMapROI();
+ static RegistryEnumMap<InterpolationMethod> &GetEnumMapROI();
static RegistryEnumMap<LayerRole> &GetEnumMapLayerRole();
static RegistryEnumMap<LayerLayout> &GetEnumMapLayerLayout();
@@ -103,7 +103,7 @@ protected:
static RegistryEnumMap<CoverageModeType> m_EnumMapCoverage;
static RegistryEnumMap<SnakeParameters::SolverType> m_EnumMapSolver;
static RegistryEnumMap<SnakeParameters::SnakeType> m_EnumMapSnakeType;
- static RegistryEnumMap<SNAPSegmentationROISettings::InterpolationMethod> m_EnumMapROI;
+ static RegistryEnumMap<InterpolationMethod> m_EnumMapROI;
static RegistryEnumMap<LayerRole> m_EnumMapLayerRole;
static RegistryEnumMap<LayerLayout> m_EnumMapLayerLayout;
diff --git a/Logic/Common/SNAPSegmentationROISettings.h b/Logic/Common/SNAPSegmentationROISettings.h
index 70ef913..d2176b2 100644
--- a/Logic/Common/SNAPSegmentationROISettings.h
+++ b/Logic/Common/SNAPSegmentationROISettings.h
@@ -46,10 +46,6 @@
class SNAPSegmentationROISettings
{
public:
- // List of available interpolation methods
- enum InterpolationMethod {
- NEAREST_NEIGHBOR, TRILINEAR, TRICUBIC, SINC_WINDOW_05
- };
SNAPSegmentationROISettings()
{
diff --git a/Logic/Framework/GenericImageData.cxx b/Logic/Framework/GenericImageData.cxx
index c6ccdc7..267c248 100644
--- a/Logic/Framework/GenericImageData.cxx
+++ b/Logic/Framework/GenericImageData.cxx
@@ -35,7 +35,7 @@
// ITK Includes
#include "itkImage.h"
#include "itkImageIterator.h"
-#include "itkImageRegionIterator.h"
+#include "RLEImageRegionIterator.h"
#include "itkImageRegionConstIterator.h"
#include "itkImageRegionIteratorWithIndex.h"
#include "itkMinimumMaximumImageCalculator.h"
@@ -91,6 +91,11 @@ GenericImageData
// Create empty annotations
m_Annotations = ImageAnnotationData::New();
+
+ // Initialize the display viewport geometry objects
+ m_DisplayViewportGeometry[0] = ImageBaseType::New();
+ m_DisplayViewportGeometry[1] = ImageBaseType::New();
+ m_DisplayViewportGeometry[2] = ImageBaseType::New();
}
GenericImageData
@@ -137,21 +142,13 @@ GenericImageData
#include "itkIdentityTransform.h"
SmartPtr<ImageWrapperBase>
-GenericImageData::CreateAnatomicWrapper(GuidedNativeImageIO *io, bool sameSpaceAsMainWrapper)
+GenericImageData::CreateAnatomicWrapper(GuidedNativeImageIO *io, ITKTransformType *transform)
{
// The output wrapper
SmartPtr<ImageWrapperBase> out_wrapper;
- // Determine the reference image and transform
- ImageBaseType *refSpace = NULL;
- typedef itk::IdentityTransform<double, 3> TransformType;
- SmartPtr<TransformType> transform = NULL;
-
- if(!sameSpaceAsMainWrapper)
- {
- refSpace = this->GetMain()->GetImageBase();
- transform = TransformType::New();
- }
+ // If the transform is not NULL, the reference space must be specified
+ ImageBaseType *refSpace = (transform) ? this->GetMain()->GetImageBase() : NULL;
// Split depending on whether the image is scalar or vector
if(io->GetNumberOfComponentsInNativeImage() > 1)
@@ -174,6 +171,9 @@ GenericImageData::CreateAnatomicWrapper(GuidedNativeImageIO *io, bool sameSpaceA
wrapper->SetDisplayGeometry(m_DisplayGeometry);
wrapper->SetImage(image, refSpace, transform);
wrapper->SetNativeMapping(mapper);
+ for(int i = 0; i < 3; i++)
+ wrapper->SetDisplayViewportGeometry(i, m_DisplayViewportGeometry[i]);
+
out_wrapper = wrapper.GetPointer();
}
@@ -197,6 +197,10 @@ GenericImageData::CreateAnatomicWrapper(GuidedNativeImageIO *io, bool sameSpaceA
wrapper->SetDisplayGeometry(m_DisplayGeometry);
wrapper->SetImage(image, refSpace, transform);
wrapper->SetNativeMapping(mapper);
+
+ for(int i = 0; i < 3; i++)
+ wrapper->SetDisplayViewportGeometry(i, m_DisplayViewportGeometry[i]);
+
out_wrapper = wrapper.GetPointer();
}
@@ -208,7 +212,7 @@ void GenericImageData::SetMainImage(GuidedNativeImageIO *io)
{
// Create the wrapper from the Native IO (the wrapper will either be a scalar
// or a vector-valued image, depending on the number of components)
- SmartPtr<ImageWrapperBase> wrapper = this->CreateAnatomicWrapper(io, true);
+ SmartPtr<ImageWrapperBase> wrapper = this->CreateAnatomicWrapper(io, NULL);
// Assign this wrapper to the main image
this->SetMainImageInternal(wrapper);
@@ -228,7 +232,6 @@ GenericImageData
// Reset the undo manager
m_UndoManager.Clear();
- m_UndoManager.SetCumulativeDelta(NULL);
}
void
@@ -256,7 +259,7 @@ GenericImageData
{
// Create the wrapper from the Native IO (the wrapper will either be a scalar
// or a vector-valued image, depending on the number of components)
- SmartPtr<ImageWrapperBase> wrapper = this->CreateAnatomicWrapper(io, true);
+ SmartPtr<ImageWrapperBase> wrapper = this->CreateAnatomicWrapper(io, NULL);
// Assign this wrapper to the main image
this->AddOverlayInternal(wrapper);
@@ -264,11 +267,11 @@ GenericImageData
void
GenericImageData
-::AddCoregOverlay(GuidedNativeImageIO *io)
+::AddCoregOverlay(GuidedNativeImageIO *io, ITKTransformType *transform)
{
// Create the wrapper from the Native IO (the wrapper will either be a scalar
// or a vector-valued image, depending on the number of components)
- SmartPtr<ImageWrapperBase> wrapper = this->CreateAnatomicWrapper(io, false);
+ SmartPtr<ImageWrapperBase> wrapper = this->CreateAnatomicWrapper(io, transform);
// Assign this wrapper to the main image
this->AddOverlayInternal(wrapper, false);
@@ -346,7 +349,6 @@ GenericImageData
// Reset the undo manager
m_UndoManager.Clear();
- m_UndoManager.SetCumulativeDelta(this->CompressLabelImage());
}
GenericImageData::UndoManagerType::Delta *
@@ -355,10 +357,12 @@ GenericImageData
{
UndoManagerType::Delta *new_cumulative = new UndoManagerType::Delta();
LabelImageType *seg = this->GetSegmentation()->GetImage();
- LabelType *buffer = seg->GetBufferPointer();
- LabelType *buffer_end = buffer + seg->GetPixelContainer()->Size();
- while (buffer < buffer_end)
- new_cumulative->Encode(*buffer++);
+
+ itk::ImageRegionConstIterator<LabelImageType> it(seg, seg->GetLargestPossibleRegion());
+ for (; !it.IsAtEnd(); ++it)
+ {
+ new_cumulative->Encode(it.Get());
+ }
new_cumulative->FinishEncoding();
return new_cumulative;
@@ -390,15 +394,19 @@ GenericImageData
void GenericImageData::SetDisplayGeometry(const IRISDisplayGeometry &dispGeom)
{
- m_DisplayGeometry = dispGeom;
for(LayerIterator lit(this); !lit.IsAtEnd(); ++lit)
if(lit.GetLayer())
{
// Set the direction matrix in the image
- lit.GetLayer()->SetDisplayGeometry(m_DisplayGeometry);
+ lit.GetLayer()->SetDisplayGeometry(m_Parent->GetDisplayGeometry());
}
}
+GenericImageData::ImageBaseType *GenericImageData::GetDisplayViewportGeometry(int index)
+{
+ return m_DisplayViewportGeometry[index];
+}
+
void GenericImageData::SetDirectionMatrix(const vnl_matrix<double> &direction)
{
for(LayerIterator lit(this); !lit.IsAtEnd(); ++lit)
@@ -415,65 +423,19 @@ const ImageCoordinateGeometry &GenericImageData::GetImageGeometry() const
return m_MainImageWrapper->GetImageGeometry();
}
-GenericImageData::UndoManagerType::Delta *
-GenericImageData::GetCumulativeUndoDelta()
+void GenericImageData::StoreIntermediateUndoDelta(UndoManagerDelta *delta)
{
- return m_UndoManager.GetCumulativeDelta();
+ m_UndoManager.AddDeltaToStaging(delta);
}
-void GenericImageData::StoreUndoPoint(const char *text)
+void GenericImageData::StoreUndoPoint(const char *text, UndoManagerDelta *delta)
{
- // Set the current state as the undo point. We store the difference between
- // the last 'undo' image and the current segmentation image, and then copy
- // the current segmentation image into the undo image
- LabelImageWrapper *seg = this->GetSegmentation();
- UndoManagerType::Delta *new_cumulative = new UndoManagerType::Delta();
-
- LabelType *dseg = seg->GetVoxelPointer();
- size_t n = seg->GetNumberOfVoxels();
-
- // Create the Undo delta object
- UndoManagerType::Delta *delta = new UndoManagerType::Delta();
-
- // Get the old cumulative delta
- UndoManagerType::Delta *old_cumulative = m_UndoManager.GetCumulativeDelta();
-
- // Run over the old cumulative data
- if(old_cumulative)
- {
- for(size_t i = 0; i < old_cumulative->GetNumberOfRLEs(); i++)
- {
- size_t rle_len = old_cumulative->GetRLELength(i);
- LabelType rle_val = old_cumulative->GetRLEValue(i);
-
- for(size_t j = 0; j < rle_len; j++)
- {
- delta->Encode(*dseg - rle_val);
- new_cumulative->Encode(*dseg);
- dseg++;
- }
- }
-
- // Important last step!
- delta->FinishEncoding();
- new_cumulative->FinishEncoding();
- }
- else
- {
- LabelType *dseg_end = dseg + n;
- for(; dseg < dseg_end; ++dseg)
- {
- // TODO: add code to duplicate
- delta->Encode(*dseg);
- }
-
- delta->FinishEncoding();
- *new_cumulative = *delta;
- }
+ // If there is a delta, add it to staging
+ if(delta)
+ m_UndoManager.AddDeltaToStaging(delta);
- // Add the delta object
- m_UndoManager.AppendDelta(delta);
- m_UndoManager.SetCumulativeDelta(new_cumulative);
+ // Commit the deltas
+ m_UndoManager.CommitStaging(text);
}
void GenericImageData::ClearUndoPoints()
@@ -492,43 +454,39 @@ void
GenericImageData
::Undo()
{
- // In order to undo, we must take the 'current' delta and apply
- // it to the image
- UndoManagerType::Delta *delta = m_UndoManager.GetDeltaForUndo();
- UndoManagerType::Delta *cumulative = new UndoManagerType::Delta();
+ // Get the commit for the undo
+ const UndoManagerType::Commit &commit = m_UndoManager.GetCommitForUndo();
- LabelImageWrapper *imSeg = this->GetSegmentation();
- LabelType *dseg = imSeg->GetVoxelPointer();
+ // The label image that will undergo undo
+ typedef itk::ImageRegionIterator<LabelImageType> IteratorType;
+ LabelImageType *imSeg = this->GetSegmentation()->GetImage();
- // Applying the delta means adding
- for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++)
+ // Iterate over all the deltas in reverse order
+ UndoManagerType::DList::const_reverse_iterator dit = commit.GetDeltas().rbegin();
+ for(; dit != commit.GetDeltas().rend(); ++dit)
{
- size_t n = delta->GetRLELength(i);
- LabelType d = delta->GetRLEValue(i);
- if(d == 0)
- {
- for(size_t j = 0; j < n; j++)
- {
- cumulative->Encode(*dseg);
- ++dseg;
- }
- }
- else
+ // Apply the changes in the current delta
+ UndoManagerType::Delta *delta = *dit;
+
+ // Iterator for the relevant region in the label image
+ IteratorType lit(imSeg, delta->GetRegion());
+
+ // Iterate over the rles in the delta
+ for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++)
{
+ size_t n = delta->GetRLELength(i);
+ LabelType d = delta->GetRLEValue(i);
for(size_t j = 0; j < n; j++)
{
- *dseg -= d;
- cumulative->Encode(*dseg);
- ++dseg;
+ if(d != 0)
+ lit.Set(lit.Get() - d);
+ ++lit;
}
}
}
- cumulative->FinishEncoding();
- m_UndoManager.SetCumulativeDelta(cumulative);
-
// Set modified flags
- imSeg->GetImage()->Modified();
+ imSeg->Modified();
InvokeEvent(SegmentationChangeEvent());
}
@@ -543,48 +501,42 @@ void
GenericImageData
::Redo()
{
- // In order to undo, we must take the 'current' delta and apply
- // it to the image
- UndoManagerType::Delta *delta = m_UndoManager.GetDeltaForRedo();
- LabelImageWrapper *imSeg = this->GetSegmentation();
- LabelType *dseg = imSeg->GetVoxelPointer();
+ // Get the commit for the redo
+ const UndoManagerType::Commit &commit = m_UndoManager.GetCommitForRedo();
- UndoManagerType::Delta *cumulative = new UndoManagerType::Delta();
+ // The label image that will undergo redo
+ typedef itk::ImageRegionIterator<LabelImageType> IteratorType;
+ LabelImageType *imSeg = this->GetSegmentation()->GetImage();
- // Applying the delta means adding
- for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++)
+ // Iterate over all the deltas in reverse order
+ UndoManagerType::DList::const_iterator dit = commit.GetDeltas().begin();
+ for(; dit != commit.GetDeltas().end(); ++dit)
{
- size_t n = delta->GetRLELength(i);
- LabelType d = delta->GetRLEValue(i);
- if(d == 0)
- {
- for(size_t j = 0; j < n; j++)
- {
- cumulative->Encode(*dseg);
- ++dseg;
- }
- }
- else
+ // Apply the changes in the current delta
+ UndoManagerType::Delta *delta = *dit;
+
+ // Iterator for the relevant region in the label image
+ IteratorType lit(imSeg, delta->GetRegion());
+
+ // Iterate over the rles in the delta
+ for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++)
{
+ size_t n = delta->GetRLELength(i);
+ LabelType d = delta->GetRLEValue(i);
for(size_t j = 0; j < n; j++)
{
- *dseg += d;
- cumulative->Encode(*dseg);
- ++dseg;
+ if(d != 0)
+ lit.Set(lit.Get() + d);
+ ++lit;
}
}
}
- cumulative->FinishEncoding();
- m_UndoManager.SetCumulativeDelta(cumulative);
-
// Set modified flags
- imSeg->GetImage()->Modified();
+ imSeg->Modified();
InvokeEvent(SegmentationChangeEvent());
}
-
-
GenericImageData::RegionType
GenericImageData
::GetImageRegion() const
@@ -646,11 +598,10 @@ int GenericImageData::GetNumberOfOverlays()
ImageWrapperBase *GenericImageData::GetLastOverlay()
{
- return m_Wrappers[OVERLAY_ROLE].back();
+ return m_Wrappers[OVERLAY_ROLE].back();
}
-
void GenericImageData::PushBackImageWrapper(LayerRole role,
ImageWrapperBase *wrapper)
{
diff --git a/Logic/Framework/GenericImageData.h b/Logic/Framework/GenericImageData.h
index 6a4b3d1..b12c8f2 100644
--- a/Logic/Framework/GenericImageData.h
+++ b/Logic/Framework/GenericImageData.h
@@ -44,6 +44,7 @@
#define __GenericImageData_h_
#include "SNAPCommon.h"
+#include "RLEImageRegionIterator.h"
#include "IRISException.h"
#include "ImageWrapperTraits.h"
#include <itkObject.h>
@@ -78,6 +79,7 @@ public:
// Image type definitions
typedef itk::ImageRegion<3> RegionType;
typedef itk::ImageBase<3> ImageBaseType;
+ typedef SmartPtr<ImageBaseType> ImageBasePointer;
/**
* The type of anatomical images. For the time being, all anatomic images
@@ -97,6 +99,10 @@ public:
// Segmentation undo support
typedef UndoDataManager<LabelType> UndoManagerType;
+ typedef UndoManagerType::Delta UndoManagerDelta;
+
+ // Transforms
+ typedef ImageWrapperBase::ITKTransformType ITKTransformType;
/**
@@ -233,7 +239,7 @@ public:
* Add an overlay that is obtained from the image referenced by *io by applying
* a spatial transformation.
*/
- void AddCoregOverlay(GuidedNativeImageIO *io);
+ void AddCoregOverlay(GuidedNativeImageIO *io, ITKTransformType *transform);
/**
* Change the ordering of the layers within a particular role (for now just
@@ -275,6 +281,14 @@ public:
virtual void SetDisplayGeometry(const IRISDisplayGeometry &dispGeom);
/**
+ * Get a pointer to the display viewport geometry object corresponding
+ * to viewports 0, 1 or 2. Viewport geometry is represented by an ImageBase
+ * object. When the display viewport changes, this should be updated so
+ * that the slices can be correctly generated in the ImageWrappers
+ */
+ virtual ImageBaseType *GetDisplayViewportGeometry(int index);
+
+ /**
* Set the direction matrix of all the images
*/
virtual void SetDirectionMatrix(const vnl_matrix<double> &direction);
@@ -285,11 +299,19 @@ public:
/** Get the list of annotations created by the user */
irisGetMacro(Annotations, ImageAnnotationData *)
- /** TODO: in 3.6 this will be unnecessary */
- UndoManagerType::Delta *GetCumulativeUndoDelta();
+ /**
+ * Store an intermediate delta without committing it as an undo point
+ * Multiple deltas can be stored and then committed with StoreUndoPoint()
+ */
+ void StoreIntermediateUndoDelta(UndoManagerDelta *delta);
- /** Store an undo point */
- void StoreUndoPoint(const char *text);
+ /**
+ * Store an undo point. The first parameter is the description of the
+ * update, and the second parameter is the delta to be applied. The delta
+ * can be NULL. All deltas previously submitted with StoreIntermediateUndoDelta
+ * and the delta passed in to this method will be commited to this undo point.
+ */
+ void StoreUndoPoint(const char *text, UndoManagerDelta *delta = NULL);
/** Clear all undo points */
void ClearUndoPoints();
@@ -344,14 +366,22 @@ protected:
// The display to anatomy transformation, which is stored by this object
IRISDisplayGeometry m_DisplayGeometry;
+ // The complete specification of each display viewport as a 3D image in the same anatomical
+ // space as the 3D images. This specification is used to sample images onto the viewport.
+ ImageBasePointer m_DisplayViewportGeometry[3];
+
// Image annotations - these are distinct from segmentations
SmartPtr<ImageAnnotationData> m_Annotations;
friend class SNAPImageData;
friend class LayerIterator;
- // Create a wrapper (vector or scalar) from native format stored in the IO
- SmartPtr<ImageWrapperBase> CreateAnatomicWrapper(GuidedNativeImageIO *io, bool sameSpaceAsMainWrapper);
+ // Create a wrapper (vector or scalar) from native format stored in the IO, with the
+ // specified transform. Null transform indicates that the wrapper is in the space space
+ // as the main wrapper
+ SmartPtr<ImageWrapperBase> CreateAnatomicWrapper(
+ GuidedNativeImageIO *io,
+ ITKTransformType *transform = NULL);
// Update the main image
virtual void SetMainImageInternal(ImageWrapperBase *wrapper);
diff --git a/Logic/Framework/GlobalState.h b/Logic/Framework/GlobalState.h
index 2bdb292..8561c79 100644
--- a/Logic/Framework/GlobalState.h
+++ b/Logic/Framework/GlobalState.h
@@ -111,7 +111,8 @@ enum ToolbarModeType
POLYGON_DRAWING_MODE,
PAINTBRUSH_MODE,
ROI_MODE,
- ANNOTATION_MODE
+ ANNOTATION_MODE,
+ REGISTRATION_MODE
};
enum ToolbarMode3DType
diff --git a/Logic/Framework/IRISApplication.cxx b/Logic/Framework/IRISApplication.cxx
index 64a3450..85034fb 100644
--- a/Logic/Framework/IRISApplication.cxx
+++ b/Logic/Framework/IRISApplication.cxx
@@ -48,11 +48,9 @@
#include "MeshManager.h"
#include "MeshExportSettings.h"
#include "SegmentationStatistics.h"
-#include "itkImageRegionIterator.h"
-#include "itkImageRegionConstIterator.h"
-#include "itkImageRegionIteratorWithIndex.h"
+#include "RLEImageRegionIterator.h"
+#include "RLERegionOfInterestImageFilter.h"
#include "itkPasteImageFilter.h"
-#include "itkImageRegionIterator.h"
#include "itkIdentityTransform.h"
#include "itkResampleImageFilter.h"
#include "itkNearestNeighborInterpolateImageFunction.h"
@@ -86,7 +84,7 @@
#include "RandomForestClassifyImageFilter.h"
#include "LabelUseHistory.h"
#include "ImageAnnotationData.h"
-
+#include "SegmentationUpdateIterator.h"
#include <stdio.h>
@@ -195,7 +193,7 @@ IRISApplication
// Override the interpolator in ROI for label interpolation, or we will get
// nonsense
SNAPSegmentationROISettings roiLabel = roi;
- roiLabel.SetInterpolationMethod(SNAPSegmentationROISettings::NEAREST_NEIGHBOR);
+ roiLabel.SetInterpolationMethod(NEAREST_NEIGHBOR);
// Get chunk of the label image
LabelImageType::Pointer imgNewLabel =
@@ -210,8 +208,8 @@ IRISApplication
unsigned int nCopied = 0;
while(!itLabel.IsAtEnd())
{
- if(itLabel.Value() != passThroughLabel || !roi.IsSeedWithCurrentSegmentation())
- itLabel.Value() = (LabelType) 0;
+ if(itLabel.Get() != passThroughLabel || !roi.IsSeedWithCurrentSegmentation())
+ itLabel.Set((LabelType) 0);
else
nCopied++;
++itLabel;
@@ -385,9 +383,20 @@ IRISApplication
// This has to happen in 'pure' SNAP mode
assert(IsSnakeModeActive());
- // Cast the image to label type
- CastNativeImage<LabelImageType> caster;
- LabelImageType::Pointer imgLabel = caster(io);
+ typedef itk::Image<LabelType, 3> UncompressedImageType;
+
+ // Cast the native to label type
+ CastNativeImage<UncompressedImageType> caster;
+ UncompressedImageType::Pointer imgUncompressed = caster(io);
+
+ //use specialized RoI filter to convert to RLEImage
+ typedef itk::RegionOfInterestImageFilter<UncompressedImageType, LabelImageType> inConverterType;
+ inConverterType::Pointer inConv = inConverterType::New();
+ inConv->SetInput(imgUncompressed);
+ inConv->SetRegionOfInterest(imgUncompressed->GetLargestPossibleRegion());
+ inConv->Update();
+ LabelImageType::Pointer imgLabel = inConv->GetOutput();
+ imgUncompressed = NULL; //deallocate intermediate image to save memory
// The header of the label image is made to match that of the grey image
imgLabel->SetOrigin(m_CurrentImageData->GetMain()->GetImageBase()->GetOrigin());
@@ -400,12 +409,13 @@ IRISApplication
// Update filenames
m_CurrentImageData->GetSegmentation()->SetFileName(io->GetFileNameOfNativeImage());
+ // TODO: this is inefficient with the new representation
// Set the loaded labels as valid
LabelImageType *seg = m_CurrentImageData->GetSegmentation()->GetImage();
- LabelType *buffer = seg->GetBufferPointer();
- LabelType *buffer_end = buffer + seg->GetPixelContainer()->Size();
- while (buffer < buffer_end)
- m_ColorLabelTable->SetColorLabelValid(*buffer++, true);
+ typedef itk::ImageRegionConstIterator<LabelImageType> IteratorType;
+ IteratorType it(seg, seg->GetBufferedRegion());
+ for(; !it.IsAtEnd(); ++it)
+ m_ColorLabelTable->SetColorLabelValid(it.Get(), true);
// Let the GUI know that segmentation changed
InvokeEvent(SegmentationChangeEvent());
@@ -418,9 +428,23 @@ IRISApplication
// This has to happen in 'pure' IRIS mode
assert(!IsSnakeModeActive());
- // Cast the image to label type
- CastNativeImage<LabelImageType> caster;
- LabelImageType::Pointer imgLabel = caster(io);
+ typedef itk::Image<LabelType, 3> UncompressedImageType;
+
+ // Cast the native to label type
+ CastNativeImage<UncompressedImageType> caster;
+ UncompressedImageType::Pointer imgUncompressed = caster(io);
+
+ //use specialized RoI filter to convert to RLEImage
+ typedef itk::RegionOfInterestImageFilter<UncompressedImageType, LabelImageType> inConverterType;
+ inConverterType::Pointer inConv = inConverterType::New();
+ inConv->SetInput(imgUncompressed);
+ inConv->SetRegionOfInterest(imgUncompressed->GetLargestPossibleRegion());
+ inConv->Update();
+ LabelImageType::Pointer imgLabel = inConv->GetOutput();
+ imgUncompressed = NULL; //deallocate intermediate image to save memory
+
+ // Disconnect from the pipeline right away
+ imgLabel->DisconnectPipeline();
// The header of the label image is made to match that of the grey image
imgLabel->SetOrigin(m_CurrentImageData->GetMain()->GetImageBase()->GetOrigin());
@@ -437,15 +461,26 @@ IRISApplication
m_SystemInterface->GetHistoryManager()->UpdateHistory(
"LabelImage", io->GetFileNameOfNativeImage(), true);
- // Now we can use the RLE encoding of the segmentation to quickly determine
- // which labels are valid
- // TODO: this will become unnecessary when we move to compressed segmentations!
- GenericImageData::UndoManagerType::Delta *new_cumulative =
- m_IRISImageData->GetCumulativeUndoDelta();
- for(size_t j = 0; j < new_cumulative->GetNumberOfRLEs(); j++)
+ // Iterate over the RLEs in the label image
+ LabelType last_label = 0;
+ typedef itk::ImageRegionConstIterator<LabelImageType::BufferType> RLLineIter;
+ RLLineIter rlit(m_IRISImageData->GetSegmentation()->GetImage()->GetBuffer(),
+ m_IRISImageData->GetSegmentation()->GetImage()->GetBuffer()->GetBufferedRegion());
+ for(; !rlit.IsAtEnd(); ++rlit)
{
- LabelType label = new_cumulative->GetRLEValue(j);
- m_ColorLabelTable->SetColorLabelValid(label, true);
+ // Get the line
+ const LabelImageType::RLLine &line = rlit.Value();
+
+ // Iterate over the entries
+ for(int i = 0; i < line.size(); i++)
+ {
+ LabelType label = line[i].second;
+ if(label != last_label)
+ {
+ m_ColorLabelTable->SetColorLabelValid(label, true);
+ last_label = label;
+ }
+ }
}
// Let the GUI know that segmentation changed
@@ -475,101 +510,104 @@ IRISApplication
return iTarget;
}
-void IRISApplication::BeginSegmentationUpdate(std::string undo_name)
-{
- m_SegmentationUpdateName = undo_name;
- m_SegmentationChangeCount = 0;
-}
-
-void IRISApplication::UpdateSegmentationVoxel(const Vector3ui &pos)
-{
- // Get the segmentation image
- LabelImageType *seg = m_CurrentImageData->GetSegmentation()->GetImage();
- LabelType &label = seg->GetPixel(to_itkIndex(pos));
- LabelType newlabel = DrawOverLabel(label);
- if(label != newlabel)
- {
- label = newlabel;
- m_SegmentationChangeCount++;
- }
-}
-
-int IRISApplication::EndSegmentationUpdate()
-{
- if(m_SegmentationChangeCount > 0)
- {
- m_CurrentImageData->GetSegmentation()->GetImage()->Modified();
- this->StoreUndoPoint(m_SegmentationUpdateName.c_str());
- this->InvokeEvent(SegmentationChangeEvent());
- }
-
- m_SegmentationUpdateName = std::string();
- return m_SegmentationChangeCount;
-}
-
unsigned int
IRISApplication
::UpdateSegmentationWithSliceDrawing(
IRISApplication::SliceBinaryImageType *drawing,
- const ImageCoordinateTransform &xfmSliceToImage,
+ const ImageCoordinateTransform *xfmSliceToImage,
double zSlice,
const std::string &undoTitle)
{
// Get the segmentation image
LabelImageType *seg = m_CurrentImageData->GetSegmentation()->GetImage();
- // Drawing parameters
- CoverageModeType iMode = m_GlobalState->GetDrawOverFilter().CoverageMode;
- LabelType iDrawing = m_GlobalState->GetDrawingColorLabel();
- LabelType iDrawOver = m_GlobalState->GetDrawOverFilter().DrawOverLabel;
- bool invert = m_GlobalState->GetPolygonInvert();
-
- // Keep track of the number of pixels changed
- unsigned int nUpdates = 0;
-
- // Iterate through the drawing
- for (itk::ImageRegionIteratorWithIndex<SliceBinaryImageType>
- it(drawing, drawing->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ // Turn the 2D region of the drawing into a 3D region in the segmentation
+ IRISApplication::SliceBinaryImageType::RegionType r_draw = drawing->GetBufferedRegion();
+
+ // Array of corners of the drawing region
+ Vector2ui corners[4];
+ corners[0][0] = r_draw.GetIndex()[0];
+ corners[0][1] = r_draw.GetIndex()[1];
+ corners[1][0] = r_draw.GetUpperIndex()[0];
+ corners[1][1] = r_draw.GetIndex()[1];
+ corners[2][0] = r_draw.GetIndex()[0];
+ corners[2][1] = r_draw.GetUpperIndex()[1];
+ corners[3][0] = r_draw.GetUpperIndex()[0];
+ corners[3][1] = r_draw.GetUpperIndex()[1];
+
+ // Compute 3D extents of the region
+ Vector3ui pos_min, pos_max;
+ for(int i = 0; i < 4; i++)
{
- // Get the current polygon pixel
- SliceBinaryImageType::PixelType px = it.Get();
+ // Get the 3D coordinate of the corner
+ Vector3ui idxVol = to_unsigned_int(
+ xfmSliceToImage->TransformPoint(
+ Vector3d(corners[i][0] + 0.5, corners[i][1] + 0.5, zSlice)));
- // Check for non-zero alpha of the pixel
- if((px != 0) ^ invert)
+ if(i == 0)
+ {
+ pos_min = idxVol;
+ pos_max = idxVol;
+ }
+ else
{
- // Figure out the coordinate of the target image
- itk::Index<2> idx = it.GetIndex();
- Vector3f idxImageFloat = xfmSliceToImage.TransformPoint(
- Vector3f(idx[0] + 0.5, idx[1] + 0.5, zSlice));
- itk::Index<3> iseg = to_itkIndex(to_unsigned_int(idxImageFloat));
-
- // Access the voxel in the segmentation
- LabelType &voxel = seg->GetPixel(iseg);
-
- // Apply the label to the voxel
- if(iMode == PAINT_OVER_ALL ||
- (iMode == PAINT_OVER_ONE && voxel == iDrawOver) ||
- (iMode == PAINT_OVER_VISIBLE &&
- m_ColorLabelTable->GetColorLabel(voxel).IsVisible()))
+ for(int j = 0; j < 3; j++)
{
- if(voxel != iDrawing)
- {
- voxel = iDrawing;
- nUpdates++;
- }
+ if(pos_min[j] > idxVol[j]) pos_min[j] = idxVol[j];
+ if(pos_max[j] < idxVol[j]) pos_max[j] = idxVol[j];
}
}
}
- // Has anything been changed?
- if(nUpdates > 0)
+ // Define the volumetric region
+ LabelImageType::RegionType r_vol;
+ r_vol.SetIndex(to_itkIndex(pos_min));
+ r_vol.SetUpperIndex(to_itkIndex(pos_max));
+ r_vol.Crop(seg->GetBufferedRegion());
+
+ // Create an iterator for painting
+ SegmentationUpdateIterator itVol(seg, r_vol,
+ m_GlobalState->GetDrawingColorLabel(),
+ m_GlobalState->GetDrawOverFilter());
+
+ // Drawing parameters
+ bool invert = m_GlobalState->GetPolygonInvert();
+
+ // Inverse transform
+ ImageCoordinateTransform::Pointer xfmImageToSlice = ImageCoordinateTransform::New();
+ xfmSliceToImage->ComputeInverse(xfmImageToSlice);
+
+ // Iterate over the volume region
+ for(; !itVol.IsAtEnd(); ++itVol)
+ {
+ // Find the coordinate of the voxel in the slice
+ itk::Index<3> idx_vol = itVol.GetIndex();
+ Vector3d x_slice = xfmImageToSlice->TransformPoint(
+ Vector3d(idx_vol[0] + 0.5, idx_vol[1] + 0.5, idx_vol[2] + 0.5));
+ itk::Index<2> idx_slice;
+ idx_slice[0] = (int) x_slice[0];
+ idx_slice[1] = (int) x_slice[1];
+
+ // Check value
+ SliceBinaryImageType::PixelType px = drawing->GetPixel(idx_slice);
+ if((px != 0) ^ invert)
+ itVol.PaintAsForeground();
+
+
+ }
+
+ // Finalize
+ itVol.Finalize();
+
+ // Store update
+ if(itVol.GetNumberOfChangedVoxels() > 0)
{
- seg->Modified();
- StoreUndoPoint(undoTitle.c_str());
+ m_CurrentImageData->StoreUndoPoint(undoTitle.c_str(), itVol.RelinquishDelta());
+ this->RecordCurrentLabelUse();
InvokeEvent(SegmentationChangeEvent());
}
- return nUpdates;
+ return itVol.GetNumberOfChangedVoxels();
}
void
@@ -620,19 +658,19 @@ IRISApplication
// Choose the interpolator
switch(roi.GetInterpolationMethod())
{
- case SNAPSegmentationROISettings::NEAREST_NEIGHBOR :
+ case NEAREST_NEIGHBOR :
fltSample->SetInterpolator(NNInterpolatorType::New());
break;
- case SNAPSegmentationROISettings::TRILINEAR :
+ case TRILINEAR :
fltSample->SetInterpolator(LinearInterpolatorType::New());
break;
- case SNAPSegmentationROISettings::TRICUBIC :
+ case TRICUBIC :
fltSample->SetInterpolator(CubicInterpolatorType::New());
break;
- case SNAPSegmentationROISettings::SINC_WINDOW_05 :
+ case SINC_WINDOW_05 :
fltSample->SetInterpolator(SincInterpolatorType::New());
break;
};
@@ -657,54 +695,42 @@ IRISApplication
// Change the source to the output
source = fltSample->GetOutput();
}
-
- // Create iterators for copying from one to the other
+
+ // Creat the source iterator
typedef itk::ImageRegionConstIterator<SourceImageType> SourceIteratorType;
- typedef itk::ImageRegionIterator<TargetImageType> TargetIteratorType;
SourceIteratorType itSource(source,source->GetLargestPossibleRegion());
- TargetIteratorType itTarget(target,roi.GetROI());
-
- // Figure out which color draws and which color is clear
- unsigned int iClear = m_GlobalState->GetPolygonInvert() ? 1 : 0;
- // Construct a merge table that contains an output intensity for every
- // possible combination of two input intensities (note that snap image only
- // has two possible intensities
- LabelType mergeTable[2][MAX_COLOR_LABELS];
+ // Create the smart target iterator
+ SegmentationUpdateIterator itTarget(
+ target, roi.GetROI(),
+ m_GlobalState->GetDrawingColorLabel(), m_GlobalState->GetDrawOverFilter());
- // Perform the merge
- for(unsigned int i=0;i<MAX_COLOR_LABELS;i++)
- {
- // Whe the SNAP image is clear, IRIS passes through to the output
- // except for the IRIS voxels of the drawing color, which get cleared out
- mergeTable[iClear][i] = (i!=m_GlobalState->GetDrawingColorLabel()) ? i : 0;
-
- // If mode is paint over all, the victim is overridden
- mergeTable[1-iClear][i] = DrawOverLabel((LabelType) i);
- }
+ // Inversion state
+ bool invert = m_GlobalState->GetPolygonInvert();
// Go through both iterators, copy the new over the old
- itSource.GoToBegin();
- itTarget.GoToBegin();
while(!itSource.IsAtEnd())
{
- // Get the two voxels
- LabelType &voxIRIS = itTarget.Value();
+ // Get the level set value
float voxSNAP = itSource.Value();
-
- // Check that we're ok (debug mode only)
- assert(!itTarget.IsAtEnd());
-
- // Perform the merge
- voxIRIS = mergeTable[voxSNAP <= 0 ? 1 : 0][voxIRIS];
+ if((!invert && voxSNAP <= 0) || (invert && voxSNAP >= 0))
+ itTarget.PaintAsForeground();
// Iterate
++itSource;
++itTarget;
}
- // The target has been modified
- target->Modified();
+ // Finalize the segmentation
+ itTarget.Finalize();
+
+ // Store the undo delta
+ if(itTarget.GetNumberOfChangedVoxels() > 0)
+ {
+ m_IRISImageData->StoreUndoPoint("Automatic Segmentation", itTarget.RelinquishDelta());
+ RecordCurrentLabelUse();
+ InvokeEvent(SegmentationChangeEvent());
+ }
}
void
@@ -729,26 +755,16 @@ IRISApplication
}
+
void
IRISApplication
-::StoreUndoPoint(const char *text)
+::RecordCurrentLabelUse()
{
- // Delegate to the image data
- m_CurrentImageData->StoreUndoPoint(text);
-
- // TODO: I am not sure this is the best place for this code. I think it's a
- // good idea to migrate all of the code that deals with updating the
- // segmentation image into one place, such as the LabelImageWrapper class.
-
- // Along with the undo point, we would like to store the combination of
- // the foreground and background label used for this update. This will
- // help us keep track of the most recently used combinations.
m_LabelUseHistory->RecordLabelUse(
m_GlobalState->GetDrawingColorLabel(),
m_GlobalState->GetDrawOverFilter());
}
-
void
IRISApplication
::ClearUndoPoints()
@@ -764,53 +780,55 @@ IRISApplication
return m_CurrentImageData->IsUndoPossible();
}
-/*
void
IRISApplication
::Undo()
{
+ m_CurrentImageData->Undo();
+ /*
+=======
// In order to undo, we must take the 'current' delta and apply
// it to the image
UndoManagerType::Delta *delta = m_UndoManager.GetDeltaForUndo();
+ UndoManagerType::Delta *cumulative = new UndoManagerType::Delta();
- LabelImageWrapper *imUndo = m_IRISImageData->GetUndoImage();
- LabelImageWrapper *imSeg = m_IRISImageData->GetSegmentation();
- LabelType *dundo = imUndo->GetVoxelPointer();
- LabelType *dseg = imSeg->GetVoxelPointer();
+ LabelImageType *imSeg = m_IRISImageData->GetSegmentation()->GetImage();
+ typedef itk::ImageRegionIterator<LabelImageType> IteratorType;
+ IteratorType it(imSeg, imSeg->GetLargestPossibleRegion());
- // Applying the delta means adding
+ // Applying the delta means adding
for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++)
{
size_t n = delta->GetRLELength(i);
LabelType d = delta->GetRLEValue(i);
if(d == 0)
{
- dundo += n;
- dseg += n;
+ for(size_t j = 0; j < n; j++)
+ {
+ cumulative->Encode(it.Get());
+ ++it;
+ }
}
else
{
for(size_t j = 0; j < n; j++)
{
- *dundo -= d;
- *dseg = *dundo;
- ++dundo; ++dseg;
+ LabelType v = it.Get();
+ v -= d;
+ it.Set(v);
+ cumulative->Encode(v);
+ ++it;
}
}
}
+ cumulative->FinishEncoding();
+ m_UndoManager.SetCumulativeDelta(cumulative);
+
// Set modified flags
- imSeg->GetImage()->Modified();
- imUndo->GetImage()->Modified();
- InvokeEvent(SegmentationChangeEvent());
-}
+ imSeg->Modified();
+>>>>>>> dev_3.6
*/
-
-void
-IRISApplication
-::Undo()
-{
- m_CurrentImageData->Undo();
InvokeEvent(SegmentationChangeEvent());
}
@@ -826,6 +844,50 @@ IRISApplication
::Redo()
{
m_CurrentImageData->Redo();
+ /*
+=======
+ // In order to undo, we must take the 'current' delta and apply
+ // it to the image
+ UndoManagerType::Delta *delta = m_UndoManager.GetDeltaForRedo();
+ LabelImageType *imSeg = m_IRISImageData->GetSegmentation()->GetImage();
+ typedef itk::ImageRegionIterator<LabelImageType> IteratorType;
+ IteratorType it(imSeg, imSeg->GetLargestPossibleRegion());
+
+ UndoManagerType::Delta *cumulative = new UndoManagerType::Delta();
+
+ // Applying the delta means adding
+ for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++)
+ {
+ size_t n = delta->GetRLELength(i);
+ LabelType d = delta->GetRLEValue(i);
+ if(d == 0)
+ {
+ for(size_t j = 0; j < n; j++)
+ {
+ cumulative->Encode(it.Get());
+ ++it;
+ }
+ }
+ else
+ {
+ for(size_t j = 0; j < n; j++)
+ {
+ LabelType v = it.Get();
+ v += d;
+ it.Set(v);
+ cumulative->Encode(v);
+ ++it;
+ }
+ }
+ }
+
+ cumulative->FinishEncoding();
+ m_UndoManager.SetCumulativeDelta(cumulative);
+
+ // Set modified flags
+ imSeg->Modified();
+>>>>>>> dev_3.6
+*/
InvokeEvent(SegmentationChangeEvent());
}
@@ -849,10 +911,10 @@ IRISApplication
Vector3d cursorSource = to_double(this->GetCursorPosition());
Vector3d xyzSource =
- source->GetMain()->TransformVoxelIndexToNIFTICoordinates(cursorSource);
+ source->GetMain()->TransformVoxelCIndexToNIFTICoordinates(cursorSource);
itk::Index<3> indexTarget =
- to_itkIndex(target->GetMain()->TransformNIFTICoordinatesToVoxelIndex(xyzSource));
+ to_itkIndex(target->GetMain()->TransformNIFTICoordinatesToVoxelCIndex(xyzSource));
Vector3ui newCursor =
target->GetMain()->GetBufferedRegion().IsInside(indexTarget)
@@ -1174,7 +1236,7 @@ IRISApplication
}
-void
+int
IRISApplication
::RelabelSegmentationWithCutPlane(const Vector3d &normal, double intercept)
{
@@ -1182,20 +1244,10 @@ IRISApplication
LabelImageWrapper::ImagePointer imgLabel =
m_CurrentImageData->GetSegmentation()->GetImage();
- // Get an iterator for the image
- typedef itk::ImageRegionIteratorWithIndex<
- LabelImageWrapper::ImageType> IteratorType;
- IteratorType it(imgLabel, imgLabel->GetBufferedRegion());
-
- // Compute a label mapping table based on the color labels
- LabelType table[MAX_COLOR_LABELS];
-
- // The clear label does not get painted over, no matter what
- table[0] = 0;
-
- // The other labels get painted over, depending on current settings
- for(unsigned int i = 1; i < MAX_COLOR_LABELS; i++)
- table[i] = DrawOverLabel(i);
+ // Create the smart target iterator
+ SegmentationUpdateIterator it(
+ imgLabel, imgLabel->GetBufferedRegion(),
+ m_GlobalState->GetDrawingColorLabel(), m_GlobalState->GetDrawOverFilter());
// Adjust the intercept by 0.5 for voxel offset
intercept -= 0.5 * (normal[0] + normal[1] + normal[2]);
@@ -1204,7 +1256,7 @@ IRISApplication
while(!it.IsAtEnd())
{
// Compute the distance to the plane
- const long *index = it.GetIndex().GetIndex();
+ itk::Index<3> index = it.GetIndex();
double distance =
index[0]*normal[0] +
index[1]*normal[1] +
@@ -1212,22 +1264,24 @@ IRISApplication
// Check the side of the plane
if(distance > 0)
- {
- LabelType voxel = it.Value();
- LabelType newvox = table[voxel];
- if(voxel != newvox)
- {
- it.Set(newvox);
- m_SegmentationChangeCount++;
- }
- }
+ it.PaintAsForegroundPreserveClear();
// Next voxel
++it;
}
-
- // Register that the image has been updated
- imgLabel->Modified();
+
+ // Finalize
+ it.Finalize();
+
+ // Store the undo point if needed
+ if(it.GetNumberOfChangedVoxels() > 0)
+ {
+ m_CurrentImageData->StoreUndoPoint("3D scalpel", it.RelinquishDelta());
+ RecordCurrentLabelUse();
+ InvokeEvent(SegmentationChangeEvent());
+ }
+
+ return it.GetNumberOfChangedVoxels();
}
int
@@ -1375,6 +1429,91 @@ IRISApplication
return 0;
}
+#include "itkMatrixOffsetTransformBase.h"
+
+SmartPtr<IRISApplication::ITKTransformType>
+IRISApplication
+::ReadTransform(Registry *reg, bool &is_identity)
+{
+ typedef itk::IdentityTransform<double, 3> IdTransform;
+ SmartPtr<IdTransform> id_transform = IdTransform::New();
+ SmartPtr<ITKTransformType> transform = id_transform.GetPointer();
+ is_identity = true;
+
+ // No registry? Return identity transform
+ if(!reg)
+ return transform;
+
+ // Is the transform identity
+ Registry &folder = reg->Folder("ImageTransform");
+ is_identity = folder["IsIdentity"][true];
+ if(is_identity)
+ {
+ return transform;
+ }
+
+ // Not an identity transform - so read the parameters
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> MOTBTransformType;
+ typedef MOTBTransformType::MatrixType MatrixType;
+ typedef MOTBTransformType::OffsetType OffsetType;
+
+ // Read the matrix, defaulting to identity
+ MatrixType matrix; matrix.SetIdentity();
+ OffsetType offset; offset.Fill(0.0);
+
+ for(int i = 0; i < 3; i++)
+ {
+ offset[i] = folder[folder.Key("Offset.Element[%d]",i)][offset[i]];
+ for(int j = 0; j < 3; j++)
+ matrix(i, j) = folder[folder.Key("Matrix.Element[%d][%d]",i,j)][matrix(i,j)];
+ }
+
+ // Check the matrix and offset for being identity
+ if(matrix.GetVnlMatrix().is_identity() && offset.GetVnlVector().is_zero())
+ {
+ is_identity = true;
+ return transform;
+ }
+
+ // Use the matrix/offset transform
+ SmartPtr<MOTBTransformType> motb = MOTBTransformType::New();
+ motb->SetMatrix(matrix);
+ motb->SetOffset(offset);
+ transform = motb.GetPointer();
+ return transform;
+}
+
+void
+IRISApplication
+::WriteTransform(Registry *reg, const ITKTransformType *transform)
+{
+ // Get the target folder
+ Registry &folder = reg->Folder("ImageTransform");
+
+ // Cast the transform to the matrix/offset type
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> MOTBTransformType;
+ const MOTBTransformType *motb = dynamic_cast<const MOTBTransformType *>(transform);
+
+ // Check for identity
+ if(!motb || (motb->GetMatrix().GetVnlMatrix().is_identity() &&
+ motb->GetOffset().GetVnlVector().is_zero()))
+ {
+ folder["IsIdentity"] << true;
+ folder.RemoveKeys("Matrix");
+ folder.RemoveKeys("Offset");
+ }
+ else
+ {
+ folder["IsIdentity"] << false;
+
+ for(int i = 0; i < 3; i++)
+ {
+ folder[folder.Key("Offset.Element[%d]",i)] << motb->GetOffset()[i];
+ for(int j = 0; j < 3; j++)
+ folder[folder.Key("Matrix.Element[%d][%d]",i,j)] << motb->GetMatrix()(i,j);
+ }
+ }
+}
void
IRISApplication
@@ -1384,8 +1523,44 @@ IRISApplication
assert(m_IRISImageData->IsMainLoaded());
assert(io->IsNativeImageLoaded());
- // Add the image as the current grayscale overlay
- m_IRISImageData->AddOverlay(io);
+ // Test if the image is in the same size as the main image
+ ImageWrapperBase *main = this->m_IRISImageData->GetMain();
+ bool same_size = (main->GetSize() == io->GetDimensionsOfNativeImage());
+
+ // Now test the 3D geometry of the image to see if it occupies the same space
+ bool same_space = true;
+
+ // Read the transform from the registry. This method will return an identity transform
+ // even if no registry was provided
+ bool id_transform = true;
+ SmartPtr<ITKTransformType> transform = this->ReadTransform(metadata, id_transform);
+
+ // We use a tolerance for header comparisons here
+ double tol = 1e-5;
+
+ for(int i = 0; i < 3; i++)
+ {
+ if(fabs(io->GetNativeImage()->GetOrigin()[i] - main->GetImageBase()->GetOrigin()[i]) > tol)
+ same_space = false;
+ if(fabs(io->GetNativeImage()->GetSpacing()[i] - main->GetImageBase()->GetSpacing()[i]) > tol)
+ same_space = false;
+ for(int j = 0; j < 3; j++)
+ {
+ if(fabs(io->GetNativeImage()->GetDirection()[i][j] - main->GetImageBase()->GetDirection()[i][j]) > tol)
+ same_space = false;
+ }
+ }
+
+ // TODO: in situations where the size is the same and space is different, we may want
+ // to ask the user how to handle it, or at least display a warning? For now, we just use
+ // the header information, which may be different from how old ITK-SNAP handled this
+
+ // Old code - prevented registration of same-size images
+ // if(same_size && same_space && id_transform)
+ // m_IRISImageData->AddOverlay(io);
+ // else
+ m_IRISImageData->AddCoregOverlay(io, transform);
+
ImageWrapperBase *layer = m_IRISImageData->GetLastOverlay();
// Set the filename of the overlay
@@ -1417,8 +1592,10 @@ IRISApplication
if(m_GlobalState->GetDefaultBehaviorSettings()->GetAutoContrast())
AutoContrastLayerOnLoad(layer);
- // Set the selected layer ID to be the new overlay
- m_GlobalState->SetSelectedLayerId(layer->GetUniqueId());
+ // Set the selected layer ID to be the new selected overlay - but only if it is
+ // not sticky!
+ if(!layer->IsSticky())
+ m_GlobalState->SetSelectedLayerId(layer->GetUniqueId());
// Fire event
InvokeEvent(LayerChangeEvent());
@@ -1426,10 +1603,12 @@ IRISApplication
void
IRISApplication
-::AddDerivedOverlayImage(ImageWrapperBase *overlay)
+::AddDerivedOverlayImage(
+ const ImageWrapperBase *sourceLayer,
+ ImageWrapperBase *overlay,
+ bool inherit_colormap)
{
assert(this->IsMainImageLoaded());
- ImageWrapperBase *layer = m_CurrentImageData->GetLastOverlay();
// Add the image as the current grayscale overlay
m_CurrentImageData->AddOverlay(overlay);
@@ -1439,70 +1618,32 @@ IRISApplication
m_CurrentImageData->SetCrosshairs(m_GlobalState->GetCrosshairsPosition());
// Apply the default color map for overlays
- std::string deflt_preset =
- m_GlobalState->GetDefaultBehaviorSettings()->GetOverlayColorMapPreset();
- m_ColorMapPresetManager->SetToPreset(layer->GetDisplayMapping()->GetColorMap(),
- deflt_preset);
+ if(inherit_colormap)
+ {
+ const ColorMap *cmSource = sourceLayer->GetDisplayMapping()->GetColorMap();
+ ColorMap *cmOverlay = overlay->GetDisplayMapping()->GetColorMap();
+ if(cmSource && cmOverlay)
+ cmOverlay->CopyInformation(cmSource);
+ }
+ else
+ {
+ std::string deflt_preset =
+ m_GlobalState->GetDefaultBehaviorSettings()->GetOverlayColorMapPreset();
+ m_ColorMapPresetManager->SetToPreset(overlay->GetDisplayMapping()->GetColorMap(),
+ deflt_preset);
+ }
// Initialize the layer-specific segmentation parameters
- CreateSegmentationSettings(layer, OVERLAY_ROLE);
+ CreateSegmentationSettings(overlay, OVERLAY_ROLE);
// If the default is to auto-contrast, perform the contrast adjustment
// operation on the image
if(m_GlobalState->GetDefaultBehaviorSettings()->GetAutoContrast())
- AutoContrastLayerOnLoad(layer);
+ AutoContrastLayerOnLoad(overlay);
// Set the selected layer ID to be the new overlay
- m_GlobalState->SetSelectedLayerId(layer->GetUniqueId());
-
- // Fire event
- InvokeEvent(LayerChangeEvent());
-}
-
-// TODO: this code is 99% same as above - fix it!
-void
-IRISApplication
-::AddIRISCoregOverlayImage(GuidedNativeImageIO *io, Registry *metadata)
-{
- assert(!IsSnakeModeActive());
- assert(m_IRISImageData->IsMainLoaded());
- assert(io->IsNativeImageLoaded());
-
- // Add the image as the current grayscale overlay
- m_IRISImageData->AddCoregOverlay(io);
-
- // Set the filename of the overlay
- // TODO: this is cumbersome, could we just initialize the wrapper from the
- // GuidedNativeImageIO without passing all this junk around?
- m_IRISImageData->GetLastOverlay()->SetFileName(io->GetFileNameOfNativeImage());
-
- // Add the overlay to the history
- m_HistoryManager->UpdateHistory("AnatomicImage", io->GetFileNameOfNativeImage(), true);
-
- // for overlay, we don't want to change the cursor location
- // just force the IRISSlicer to update
- m_IRISImageData->SetCrosshairs(m_GlobalState->GetCrosshairsPosition());
-
- // Apply the default color map for overlays
- std::string deflt_preset =
- m_GlobalState->GetDefaultBehaviorSettings()->GetOverlayColorMapPreset();
- m_ColorMapPresetManager->SetToPreset(
- m_IRISImageData->GetLastOverlay()->GetDisplayMapping()->GetColorMap(),
- deflt_preset);
-
- // Initialize the layer-specific segmentation parameters
- CreateSegmentationSettings(m_IRISImageData->GetLastOverlay(), OVERLAY_ROLE);
-
- // Read and apply the project-level settings associated with the main image
- LoadMetaDataAssociatedWithLayer(
- m_IRISImageData->GetLastOverlay(), OVERLAY_ROLE, metadata);
-
- // If the default is to auto-contrast, perform the contrast adjustment
- // operation on the image
- if(m_GlobalState->GetDefaultBehaviorSettings()->GetAutoContrast())
- {
- AutoContrastLayerOnLoad(m_IRISImageData->GetLastOverlay());
- }
+ if(!overlay->IsSticky())
+ m_GlobalState->SetSelectedLayerId(overlay->GetUniqueId());
// Fire event
InvokeEvent(LayerChangeEvent());
@@ -1681,6 +1822,13 @@ void IRISApplication
// Write the metadata for the specific layer
layer->WriteMetaData(folder->Folder("LayerMetaData"));
+ // Write the layer IO hints - overriding the association file data
+ if(!layer->GetIOHints().IsEmpty())
+ {
+ folder->Folder("IOHints").Clear();
+ folder->Folder("IOHints").Update(layer->GetIOHints());
+ }
+
// For the main image layer, write the project-level settings
if(role == MAIN_ROLE)
{
@@ -1746,23 +1894,30 @@ IRISApplication
InvokeEvent(MainImageDimensionsChangeEvent());
}
-void IRISApplication
+ImageWrapperBase *
+IRISApplication
::LoadImageViaDelegate(const char *fname,
AbstractLoadImageDelegate *del,
- IRISWarningList &wl)
+ IRISWarningList &wl,
+ Registry *ioHints)
{
- // Load the settings associated with this file
- Registry reg;
- m_SystemInterface->FindRegistryAssociatedWithFile(fname, reg);
+ Registry regAssoc;
- // Get the folder dealing with grey image properties
- Registry &folder = reg.Folder("Files.Grey");
+ // When hints are not provided, we load them using the association system
+ if(!ioHints)
+ {
+ // Load the settings associated with this file
+ m_SystemInterface->FindRegistryAssociatedWithFile(fname, regAssoc);
+
+ // Get the folder dealing with grey image properties
+ ioHints = ®Assoc.Folder("Files.Grey");
+ }
// Create a native image IO object
SmartPtr<GuidedNativeImageIO> io = GuidedNativeImageIO::New();
// Load the header of the image
- io->ReadNativeImageHeader(fname, folder);
+ io->ReadNativeImageHeader(fname, *ioHints);
// Validate the header
del->ValidateHeader(io, wl);
@@ -1777,12 +1932,133 @@ void IRISApplication
del->ValidateImage(io, wl);
// Put the image in the right place
- del->UpdateApplicationWithImage(io);
+ ImageWrapperBase *layer = del->UpdateApplicationWithImage(io);
+
+ // Store the IO hints inside of the image - in case it ever gets added
+ // to a project
+ layer->SetIOHints(*ioHints);
+
+ return layer;
}
+IRISApplication::DicomSeriesTree
+IRISApplication::ListAvailableSiblingDicomSeries()
+{
+ // Create an empty listing
+ DicomSeriesTree available_dicoms;
+
+ // Create a structure to keep track of already loaded DICOM series so they
+ // are not included
+ std::map< std::string, std::set<std::string> > loaded_dicoms;
+
+ // Iterate through the loaded image layers
+ LayerIterator it = this->GetIRISImageData()->GetLayers(MAIN_ROLE | OVERLAY_ROLE);
+ for(; !it.IsAtEnd(); ++it)
+ {
+ // Get the IO hints registry
+ Registry io_hints = it.GetLayer()->GetIOHints();
+
+ // Is this image a DICOM?
+ if(io_hints.HasFolder("DICOM"))
+ {
+ // Get the directory of the DICOM files
+ std::string layer_fn = it.GetLayer()->GetFileName();
+ if(!itksys::SystemTools::FileIsDirectory(layer_fn))
+ layer_fn = itksys::SystemTools::GetParentDirectory(layer_fn);
+
+ // Get the series ID of the DICOM files
+ std::string layer_series_id = io_hints["DICOM.SeriesId"][""];
+ loaded_dicoms[layer_fn].insert(layer_series_id);
+
+ // Has this directory already been included? Then we can skip the rest
+ if(available_dicoms.find(layer_fn) == available_dicoms.end())
+ {
+ // Get the number of DICOM siblings
+ int n_entries = io_hints["DICOM.DirectoryInfo.ArraySize"][0];
+ for(int i = 0; i < n_entries; i++)
+ {
+ // Read the entry for this ID
+ DicomSeriesDescriptor desc;
+ Registry &r = io_hints.Folder(io_hints.Key("DICOM.DirectoryInfo.Entry[%d]", i));
+ desc.series_id = r["SeriesId"][""];
+ if(desc.series_id.length())
+ {
+ desc.series_desc = r["SeriesDescription"][""];
+ desc.dimensions = r["Dimensions"][""];
+ desc.layer_uid = it.GetLayer()->GetUniqueId();
+ available_dicoms[layer_fn].push_back(desc);
+ }
+ }
+ }
+ }
+ }
+
+ // Loop again and remove series that are already loaded
+ DicomSeriesTree::iterator it_map = available_dicoms.begin();
+ while(it_map != available_dicoms.end())
+ {
+ DicomSeriesListing::iterator it_list = it_map->second.begin();
+ while(it_list != it_map->second.end())
+ {
+ if(loaded_dicoms[it_map->first].count(it_list->series_id))
+ it_map->second.erase(it_list++);
+ else
+ it_list++;
+ }
+
+ if(it_map->second.size() == 0)
+ available_dicoms.erase(it_map++);
+ else
+ it_map++;
+ }
+
+ // Return the map
+ return available_dicoms;
+}
+
+#include "MetaDataAccess.h"
+
void IRISApplication
-::LoadImage(const char *fname, LayerRole role,
- IRISWarningList &wl, Registry *meta_data_reg)
+::AssignNicknameFromDicomMetadata(ImageWrapperBase *layer)
+{
+ const std::string tag = "0008|103e";
+ MetaDataAccess mda(layer->GetImageBase());
+ if(mda.HasKey(tag))
+ layer->SetCustomNickname(mda.GetValueAsString(tag));
+}
+
+void IRISApplication
+::LoadAnotherDicomSeriesViaDelegate(unsigned long reference_layer_id,
+ const char *series_id,
+ AbstractLoadImageDelegate *del,
+ IRISWarningList &wl)
+{
+ // We will use the main image's IO hints to create the IO hints for the
+ // image that is being loaded.
+ ImageWrapperBase *ref =
+ this->GetIRISImageData()->FindLayer(reference_layer_id, false);
+
+ if(ref)
+ {
+ // Create a copy of these hints for the new image we are loading
+ Registry io_hints = ref->GetIOHints();
+
+ // Replace the SeriesID with the one we are intending to load
+ io_hints["DICOM.SeriesId"] << series_id;
+
+ // Use the current filename of the main image
+ ImageWrapperBase *layer =
+ this->LoadImageViaDelegate(ref->GetFileName(), del, wl, &io_hints);
+
+ // Assign the series ID of the loaded image as the nickname
+ if(layer->GetCustomNickname().length() == 0)
+ this->AssignNicknameFromDicomMetadata(layer);
+ }
+}
+
+void IRISApplication
+::LoadImage(const char *fname, LayerRole role, IRISWarningList &wl,
+ Registry *meta_data_reg, Registry *io_hints_reg)
{
// Pointer to the delegate
SmartPtr<AbstractLoadImageDelegate> delegate;
@@ -1805,7 +2081,9 @@ void IRISApplication
delegate->Initialize(this);
if(meta_data_reg)
delegate->SetMetaDataRegistry(meta_data_reg);
- this->LoadImageViaDelegate(fname, delegate, wl);
+
+ // Load via delegate, providing the IO hints
+ this->LoadImageViaDelegate(fname, delegate, wl, io_hints_reg);
}
SmartPtr<AbstractSaveImageDelegate>
@@ -1911,11 +2189,21 @@ void IRISApplication::SaveProjectToRegistry(Registry &preg, const std::string pr
// Save the metadata associated with the layer
SaveMetaDataAssociatedWithLayer(layer, it.GetRole(), &folder);
+
+ // Save the layer transform - relevant only for overlays
+ if(it.GetRole() == OVERLAY_ROLE)
+ {
+ this->WriteTransform(&folder, layer->GetITKTransform());
+ }
}
// Save the annotations in the workspace
Registry &ann_folder = preg.Folder("Annotations");
this->m_IRISImageData->GetAnnotations()->SaveAnnotations(ann_folder);
+
+ // Recursively search and delete empty folders
+ preg.CleanZeroSizeArrays();
+ preg.CleanEmptyFolders();
}
void IRISApplication::SaveProject(const std::string &proj_file)
@@ -2011,8 +2299,15 @@ void IRISApplication::OpenProject(
layer_file_full = moved_file_full;
}
+ // Load the IO hints for the image from the project - but only if this
+ // folder is actually present (otherwise some projects from before 2016
+ // will not load hints)
+ Registry *io_hints = NULL;
+ if(folder.HasFolder("IOHints"))
+ io_hints = &folder.Folder("IOHints");
+
// Load the image and its metadata
- LoadImage(layer_file_full.c_str(), role, warn, &folder);
+ LoadImage(layer_file_full.c_str(), role, warn, &folder, io_hints);
// Check if the main has been loaded
if(role == MAIN_ROLE)
@@ -2337,7 +2632,7 @@ void IRISApplication::EnterGMMPreprocessingMode()
void IRISApplication::EnterRandomForestPreprocessingMode()
{
// Create a random forest classification engine
- m_ClassificationEngine = RFClassificationEngine::New();
+ m_ClassificationEngine = RFEngine::New();
m_ClassificationEngine->SetDataSource(m_SNAPImageData);
// Check if we can reuse the classifier from the last run
diff --git a/Logic/Framework/IRISApplication.h b/Logic/Framework/IRISApplication.h
index a7b7a54..2b32cbf 100644
--- a/Logic/Framework/IRISApplication.h
+++ b/Logic/Framework/IRISApplication.h
@@ -36,6 +36,7 @@
#ifndef __IRISApplication_h_
#define __IRISApplication_h_
+#include "ImageWrapperTraits.h"
#include "ImageCoordinateTransform.h"
#include "IRISDisplayGeometry.h"
#include "itkImageRegion.h"
@@ -60,18 +61,19 @@ class ThresholdSettings;
class EdgePreprocessingSettings;
class AbstractSlicePreviewFilterWrapper;
class UnsupervisedClustering;
-class RFClassificationEngine;
class ImageWrapperBase;
class MeshManager;
class AbstractLoadImageDelegate;
class AbstractSaveImageDelegate;
class IRISWarningList;
class GaussianMixtureModel;
-class RandomForestClassifier;
struct IRISDisplayGeometry;
class LabelUseHistory;
class ImageAnnotationData;
+template <class TPixel, class TLabel, int VDim> class RandomForestClassifier;
+template <class TPixel, class TLabel, int VDim> class RFClassificationEngine;
+
template <class TTraits> class PresetManager;
class ColorMapPresetTraits;
typedef PresetManager<ColorMapPresetTraits> ColorMapPresetManager;
@@ -89,6 +91,7 @@ template <typename TIn, typename TVIn, typename TOut> class GMMClassifyImageFilt
namespace itk {
template <class TPixel, unsigned int VDimension> class Image;
template <class TPixel, unsigned int VDimension> class VectorImage;
+ template <class TParametersValueType> class TransformBaseTemplate;
}
@@ -122,7 +125,10 @@ public:
// The internal representation of anatomical images
typedef itk::VectorImage<GreyType, 3> AnatomyImageType;
- typedef itk::Image<LabelType,3> LabelImageType;
+ //typedef RLEImage<LabelType> LabelImageType;
+ //avoid duplicating definition of LabelImageType, like this:
+ typedef LabelImageWrapperTraits::ImageType LabelImageType;
+
typedef itk::Image<short ,3> SpeedImageType;
typedef itk::Command CommandType;
@@ -132,6 +138,22 @@ public:
// Bubble array
typedef std::vector<Bubble> BubbleArray;
+ // Structure for listing DICOM series ids (SeriesId/LayerId pair)
+ struct DicomSeriesDescriptor
+ {
+ std::string series_id;
+ std::string series_desc;
+ std::string dimensions;
+ unsigned long layer_uid; // links back to the loaded layer
+ };
+
+ typedef std::list<DicomSeriesDescriptor> DicomSeriesListing;
+ typedef std::map<std::string, DicomSeriesListing> DicomSeriesTree;
+
+ // Classifier stuff
+ typedef RFClassificationEngine<GreyType, LabelType, 3> RFEngine;
+ typedef RandomForestClassifier<GreyType, LabelType, 3> RFClassifier;
+
// Declare events fired by this object
FIRES(CursorUpdateEvent)
FIRES(MainImageDimensionsChangeEvent)
@@ -182,10 +204,39 @@ public:
* of this class to different layer roles (main image, overlay). The warnings
* generated in the course of the IO operation are stored in the passed in
* warning list object;
+ *
+ * By default the IO hints are obtained from the association files, i.e. by
+ * looking up the hints associated with fname in the user's application data
+ * directory. But it is also possible to provide a pointer to the ioHints, i.e.,
+ * if the image is being as part of loading a workspace.
*/
- void LoadImageViaDelegate(const char *fname,
- AbstractLoadImageDelegate *del,
- IRISWarningList &wl);
+ ImageWrapperBase* LoadImageViaDelegate(const char *fname,
+ AbstractLoadImageDelegate *del,
+ IRISWarningList &wl,
+ Registry *ioHints = NULL);
+
+ /**
+ * List available additional DICOM series that can be loaded given the currently
+ * loaded DICOM images. This creates a listing of 'sibling' DICOM series Ids,
+ * grouped by the directory of the DICOM data
+ */
+ DicomSeriesTree ListAvailableSiblingDicomSeries();
+
+ /**
+ * Load another dicom series via delegate. This is similar to LoadImageViaDelegate
+ * but the input is a SeriesId assumed to be in the same DICOM directory as the
+ * main image
+ */
+ void LoadAnotherDicomSeriesViaDelegate(unsigned long reference_layer_id,
+ const char *series_id,
+ AbstractLoadImageDelegate *del,
+ IRISWarningList &wl);
+
+ /**
+ * Assign a nickname to an image layer based on its DICOM metadata. For now this
+ * implementation uses just the "Series Description" field.
+ */
+ void AssignNicknameFromDicomMetadata(ImageWrapperBase *layer);
/**
* Load an image for a particular role using the default delegate for this role.
@@ -195,7 +246,9 @@ public:
* the provided Registry object.
*/
void LoadImage(const char *fname, LayerRole role,
- IRISWarningList &wl, Registry *meta_data_reg = NULL);
+ IRISWarningList &wl,
+ Registry *meta_data_reg = NULL,
+ Registry *io_hints_reg = NULL);
/**
* Create a delegate for saving an image interactively or non-interactively
@@ -219,17 +272,12 @@ public:
void AddIRISOverlayImage(GuidedNativeImageIO *nativeIO, Registry *metadata = NULL);
/**
- * Add an overlay image that resides in its native space, but is transformed into
- * the space of the main image using a set of transformation parameters (e.g., rigid
- * transform)
- */
- void AddIRISCoregOverlayImage(GuidedNativeImageIO *io, Registry *metadata);
-
- /**
* Add a 'derived' overlay, i.e., an overlay generated using image processing from one
* of the existing image layers
*/
- void AddDerivedOverlayImage(ImageWrapperBase *overlay);
+ void AddDerivedOverlayImage(const ImageWrapperBase *sourceLayer,
+ ImageWrapperBase *overlay,
+ bool inherit_colormap);
/**
* Remove a specific overlay
@@ -428,35 +476,6 @@ public:
LabelType DrawOverLabel(LabelType iTarget);
/**
- * Method that signals the beginning of segmentation update operation. This
- * method should be used in conjuction with UpdateSegmentationVoxel method
- * to apply current drawing properties to a set of voxels
- *
- * These methods don't perform error checking - it's the user's responsibility
- * to call them in the correct order. The methods are not currently reentrant
- * (i.e., should not be used by multiple threads).
- *
- * TODO: this is currently only being used for spray paint. But this set of
- * methods should be expanded with a method that works with itk iterators and
- * should be made the main entrypoint for changing segmentations. This will
- * especially be necessary for RLE segmentation management.
- *
- */
- void BeginSegmentationUpdate(std::string undo_name);
-
- /**
- * Apply the current drawing label to a voxel. Depending on coverage mode
- * and the voxel's current label, the label of the voxel may be changed
- */
- void UpdateSegmentationVoxel(const Vector3ui &pos);
-
- /**
- * Complete the segmentation update. Returns the actual number of voxels
- * relabeled since BeginSegmentationUpdate();
- */
- int EndSegmentationUpdate();
-
- /**
* Really simple replacement of one label with another. Returns the
* number of voxels changed.
*/
@@ -471,7 +490,7 @@ public:
* Cut the segmentation using a plane and relabed the segmentation
* on the side of that plane
*/
- void RelabelSegmentationWithCutPlane(
+ int RelabelSegmentationWithCutPlane(
const Vector3d &normal, double intercept);
/**
@@ -498,13 +517,6 @@ public:
*/
void SaveLabelDescriptions(const char *filename);
- /**
- * Store the current state as an undo point, allowing the user to revert
- * to this state at a later point. The state in this context is just the
- * segmentation image in IRIS.
- */
- void StoreUndoPoint(const char *text);
-
/**
* Clear all the undo points, e.g., after an operation that can not be
* undone
@@ -534,7 +546,7 @@ public:
*/
unsigned int UpdateSegmentationWithSliceDrawing(
SliceBinaryImageType *drawing,
- const ImageCoordinateTransform &xfmSliceToImage,
+ const ImageCoordinateTransform *xfmSliceToImage,
double zSlice,
const std::string &undoTitle);
@@ -548,7 +560,7 @@ public:
irisGetMacro(ClusteringEngine, UnsupervisedClustering *)
/** Get the object used to drive the supervised classification */
- irisGetMacro(ClassificationEngine, RFClassificationEngine *)
+ irisGetMacro(ClassificationEngine, RFEngine *)
/** Set the current snake mode. This method should be called instead of the
method in GlobalState because when the snake mode is set, some changes
@@ -609,6 +621,12 @@ public:
*/
void SaveAnnotations(const char *filename);
+ /**
+ * Record the fact that the current active label and draw over label were used.
+ * This is to maintain a history of commonly used labels.
+ */
+ void RecordCurrentLabelUse();
+
protected:
IRISApplication();
@@ -682,12 +700,12 @@ protected:
SmartPtr<UnsupervisedClustering> m_ClusteringEngine;
// The Random Foreset classification object
- SmartPtr<RFClassificationEngine> m_ClassificationEngine;
+ SmartPtr<RFEngine> m_ClassificationEngine;
// The last classifier used for random forest segmentation. This is reused during
// repeated calls to the active contour segmentation, as long as the layers haven't
// been updated.
- SmartPtr<RandomForestClassifier> m_LastUsedRFClassifier;
+ SmartPtr<RFClassifier> m_LastUsedRFClassifier;
// The number of components for the last used RF classifier
int m_LastUsedRFClassifierComponents;
@@ -704,10 +722,6 @@ protected:
// Array of bubbles
BubbleArray m_BubbleArray;
- // State used in conjunction with BeginSegmentationUpdate/EndSegmentationUpdate
- std::string m_SegmentationUpdateName;
- unsigned int m_SegmentationChangeCount;
-
// Save metadata for a layer to the associations file
void SaveMetaDataAssociatedWithLayer(ImageWrapperBase *layer, int role,
Registry *override = NULL);
@@ -737,7 +751,13 @@ protected:
// Auto-adjust contrast of a layer on load
void AutoContrastLayerOnLoad(ImageWrapperBase *layer);
+ typedef ImageWrapperBase::ITKTransformType ITKTransformType;
+
+ // Read transform from project registry
+ SmartPtr<ITKTransformType> ReadTransform(Registry *reg, bool &is_identity);
+ // Write transform to project registry
+ void WriteTransform(Registry *reg, const ITKTransformType *transform);
};
#endif // __IRISApplication_h_
diff --git a/Logic/Framework/ImageAnnotationData.cxx b/Logic/Framework/ImageAnnotationData.cxx
index f4456f0..ab1aa66 100644
--- a/Logic/Framework/ImageAnnotationData.cxx
+++ b/Logic/Framework/ImageAnnotationData.cxx
@@ -55,10 +55,10 @@ int LineSegmentAnnotation::GetSliceIndex(int plane) const
return (int) (m_Segment.first[plane]);
}
-Vector3f LineSegmentAnnotation::GetAnchorPoint(int plane) const
+Vector3d LineSegmentAnnotation::GetAnchorPoint(int plane) const
{
// Use the midpoint
- return (m_Segment.first + m_Segment.second) * 0.5f;
+ return (m_Segment.first + m_Segment.second) * 0.5;
}
void LineSegmentAnnotation::Save(Registry &folder)
@@ -72,13 +72,13 @@ void LineSegmentAnnotation::Save(Registry &folder)
void LineSegmentAnnotation::Load(Registry &folder)
{
Superclass::Load(folder);
- m_Segment.first = to_float(folder["Point1"][Vector3d(0.0)]);
- m_Segment.second = to_float(folder["Point2"][Vector3d(0.0)]);
+ m_Segment.first = folder["Point1"][Vector3d(0.0)];
+ m_Segment.second = folder["Point2"][Vector3d(0.0)];
if(m_Segment.first[this->m_Plane] != m_Segment.second[this->m_Plane])
throw IRISException("Invalid line segment annotation detected in file.");
}
-void LineSegmentAnnotation::MoveBy(const Vector3f &offset)
+void LineSegmentAnnotation::MoveBy(const Vector3d &offset)
{
m_Segment.first += offset;
m_Segment.second += offset;
@@ -91,12 +91,12 @@ int LandmarkAnnotation::GetSliceIndex(int plane) const
return (int) (m_Landmark.Pos[plane]);
}
-Vector3f LandmarkAnnotation::GetAnchorPoint(int plane) const
+Vector3d LandmarkAnnotation::GetAnchorPoint(int plane) const
{
return m_Landmark.Pos;
}
-void LandmarkAnnotation::MoveBy(const Vector3f &offset)
+void LandmarkAnnotation::MoveBy(const Vector3d &offset)
{
m_Landmark.Pos += offset;
}
@@ -113,8 +113,8 @@ void LandmarkAnnotation::Save(Registry &folder)
void LandmarkAnnotation::Load(Registry &folder)
{
Superclass::Load(folder);
- m_Landmark.Pos = to_float(folder["Pos"][Vector3d(0.0)]);
- m_Landmark.Offset = to_float(folder["Offset"][Vector2d(0.0)]);
+ m_Landmark.Pos = folder["Pos"][Vector3d(0.0)];
+ m_Landmark.Offset = folder["Offset"][Vector2d(0.0)];
m_Landmark.Text = folder["Text"]["??? Landmark"];
}
diff --git a/Logic/Framework/ImageAnnotationData.h b/Logic/Framework/ImageAnnotationData.h
index d06aa07..ad708ef 100644
--- a/Logic/Framework/ImageAnnotationData.h
+++ b/Logic/Framework/ImageAnnotationData.h
@@ -13,7 +13,7 @@ class Registry;
namespace annot
{
-typedef Vector3f Point;
+typedef Vector3d Point;
/**
* @brief Slices where the annotation is displayed
@@ -55,10 +55,10 @@ public:
virtual int GetSliceIndex(int plane) const = 0;
/** Get the anchor point for the annotation in a given plane - used for sorting */
- virtual Vector3f GetAnchorPoint(int plane) const = 0;
+ virtual Vector3d GetAnchorPoint(int plane) const = 0;
/** Move the annotation by given amount in physical space */
- virtual void MoveBy(const Vector3f &offset) = 0;
+ virtual void MoveBy(const Vector3d &offset) = 0;
/** Save the annotation data to registry */
virtual void Save(Registry &folder);
@@ -81,7 +81,7 @@ protected:
};
/** A simple line segment */
-typedef std::pair<Vector3f,Vector3f> LineSegment;
+typedef std::pair<Vector3d,Vector3d> LineSegment;
class LineSegmentAnnotation : public AbstractAnnotation
{
@@ -95,13 +95,13 @@ public:
virtual void Save(Registry &folder);
virtual void Load(Registry &folder);
- virtual void MoveBy(const Vector3f &offset);
+ virtual void MoveBy(const Vector3d &offset);
protected:
virtual int GetSliceIndex(int plane) const;
- virtual Vector3f GetAnchorPoint(int plane) const;
+ virtual Vector3d GetAnchorPoint(int plane) const;
LineSegment m_Segment;
};
@@ -112,8 +112,8 @@ protected:
struct Landmark
{
std::string Text;
- Vector3f Pos;
- Vector2f Offset;
+ Vector3d Pos;
+ Vector2d Offset;
};
class LandmarkAnnotation : public AbstractAnnotation
@@ -129,9 +129,9 @@ public:
protected:
virtual int GetSliceIndex(int plane) const;
- virtual Vector3f GetAnchorPoint(int plane) const;
+ virtual Vector3d GetAnchorPoint(int plane) const;
- virtual void MoveBy(const Vector3f &offset);
+ virtual void MoveBy(const Vector3d &offset);
virtual void Save(Registry &folder);
virtual void Load(Registry &folder);
diff --git a/Logic/Framework/ImageIODelegates.cxx b/Logic/Framework/ImageIODelegates.cxx
index 1d7d34a..5d970a0 100644
--- a/Logic/Framework/ImageIODelegates.cxx
+++ b/Logic/Framework/ImageIODelegates.cxx
@@ -49,6 +49,12 @@ ImageWrapperBase *LoadMainImageDelegate::UpdateApplicationWithImage(GuidedNative
return m_Driver->GetIRISImageData()->GetMain();
}
+LoadMainImageDelegate::LoadMainImageDelegate()
+{
+ this->m_HistoryName = "AnatomicImage";
+ this->m_DisplayName = "Main Image";
+}
+
/* =============================
OVERLAY Image
@@ -79,6 +85,9 @@ LoadOverlayImageDelegate
// Do the parent's check
LoadAnatomicImageDelegate::ValidateHeader(io, wl);
+ // The check below is commented out because we no longer require the overlays
+ // to be in the same space as the main image
+ /*
// Now check for dimensions mismatch
GenericImageData *id = m_Driver->GetCurrentImageData();
@@ -94,33 +103,13 @@ LoadOverlayImageDelegate
szSeg[0], szSeg[1], szSeg[2],
szMain[0], szMain[1], szMain[2]);
}
+ */
}
-
-
-/* =============================
- Co-Registered Overlay Image
- ============================= */
-
-void
-LoadCoregisteredOverlayImageDelegate
-::UnloadCurrentImage()
+LoadOverlayImageDelegate::LoadOverlayImageDelegate()
{
-}
-
-ImageWrapperBase *LoadCoregisteredOverlayImageDelegate::UpdateApplicationWithImage(GuidedNativeImageIO *io)
-{
- m_Driver->AddIRISCoregOverlayImage(io, this->GetMetaDataRegistry());
- return m_Driver->GetIRISImageData()->GetLastOverlay();
-}
-
-
-void
-LoadCoregisteredOverlayImageDelegate
-::ValidateHeader(GuidedNativeImageIO *io, IRISWarningList &wl)
-{
- // Do the parent's check
- LoadAnatomicImageDelegate::ValidateHeader(io, wl);
+ this->m_HistoryName = "AnatomicImage";
+ this->m_DisplayName = "Additional Image";
}
/* =============================
@@ -227,6 +216,12 @@ ImageWrapperBase *LoadSegmentationImageDelegate::UpdateApplicationWithImage(Guid
return m_Driver->GetCurrentImageData()->GetSegmentation();
}
+LoadSegmentationImageDelegate::LoadSegmentationImageDelegate()
+{
+ this->m_HistoryName = "LabelImage";
+ this->m_DisplayName = "Segmentation Image";
+}
+
void
LoadSegmentationImageDelegate
::UnloadCurrentImage()
diff --git a/Logic/Framework/ImageIODelegates.h b/Logic/Framework/ImageIODelegates.h
index bdbc53a..bd4c5ff 100644
--- a/Logic/Framework/ImageIODelegates.h
+++ b/Logic/Framework/ImageIODelegates.h
@@ -39,6 +39,10 @@ public:
*/
irisGetSetMacro(MetaDataRegistry, Registry *)
+ irisGetSetMacro(HistoryName, const std::string &)
+
+ irisGetSetMacro(DisplayName, const std::string &)
+
virtual void ValidateHeader(GuidedNativeImageIO *io, IRISWarningList &wl) {}
virtual void ValidateImage(GuidedNativeImageIO *io, IRISWarningList &wl) {}
virtual void UnloadCurrentImage() = 0;
@@ -58,6 +62,12 @@ protected:
IRISApplication *m_Driver;
+ // Name of the history associated with this delegate
+ std::string m_HistoryName;
+
+ // Display name associated with this delegate
+ std::string m_DisplayName;
+
private:
Registry *m_MetaDataRegistry;
};
@@ -85,7 +95,7 @@ public:
ImageWrapperBase * UpdateApplicationWithImage(GuidedNativeImageIO *io);
protected:
- LoadMainImageDelegate() {}
+ LoadMainImageDelegate();
virtual ~LoadMainImageDelegate() {}
};
@@ -102,32 +112,11 @@ public:
virtual bool IsOverlay() const { return true; }
protected:
- LoadOverlayImageDelegate() { }
+ LoadOverlayImageDelegate();
virtual ~LoadOverlayImageDelegate() {}
};
-class LoadCoregisteredOverlayImageDelegate : public LoadAnatomicImageDelegate
-{
-public:
-
- irisITKObjectMacro(LoadCoregisteredOverlayImageDelegate, LoadAnatomicImageDelegate)
-
- void UnloadCurrentImage();
- ImageWrapperBase * UpdateApplicationWithImage(GuidedNativeImageIO *io);
- void ValidateHeader(GuidedNativeImageIO *io, IRISWarningList &wl);
-
- virtual bool GetUseRegistration() const { return true; }
- virtual bool IsOverlay() const { return true; }
-
-
-protected:
- LoadCoregisteredOverlayImageDelegate() { }
- virtual ~LoadCoregisteredOverlayImageDelegate() {}
-};
-
-
-
class LoadSegmentationImageDelegate : public AbstractLoadImageDelegate
{
public:
@@ -140,7 +129,7 @@ public:
ImageWrapperBase * UpdateApplicationWithImage(GuidedNativeImageIO *io);
protected:
- LoadSegmentationImageDelegate() {}
+ LoadSegmentationImageDelegate();
virtual ~LoadSegmentationImageDelegate() {}
};
diff --git a/Logic/Framework/SNAPImageData.cxx b/Logic/Framework/SNAPImageData.cxx
index e8f6453..c5fd0db 100644
--- a/Logic/Framework/SNAPImageData.cxx
+++ b/Logic/Framework/SNAPImageData.cxx
@@ -43,7 +43,6 @@
#include "itkEllipseSpatialObject.h"
#include "itkSpatialObjectToImageFilter.h"
#include "itkMaximumImageFilter.h"
-#include "itkDanielssonDistanceMapImageFilter.h"
#include "itkSubtractImageFilter.h"
#include "itkUnaryFunctorImageFilter.h"
#include "itkFastMutexLock.h"
@@ -52,7 +51,6 @@
#include "GlobalState.h"
#include "EdgePreprocessingImageFilter.h"
#include "IRISVectorTypesToITKConversion.h"
-#include "SignedDistanceFilter.h"
#include "IRISException.h"
#include "IRISApplication.h"
#include "ThresholdSettings.h"
@@ -528,7 +526,6 @@ void SNAPImageData::SwapLabelImageWithCompressedAlternative()
// Clear the undo manager
this->m_UndoManager.Clear();
- this->m_UndoManager.SetCumulativeDelta(m_CompressedAlternateLabelImage);
// Decompress the currently saved alternative
if(m_CompressedAlternateLabelImage)
@@ -538,7 +535,7 @@ void SNAPImageData::SwapLabelImageWithCompressedAlternative()
{
LabelType value = m_CompressedAlternateLabelImage->GetRLEValue(i);
for(size_t j = 0; j < m_CompressedAlternateLabelImage->GetRLELength(i); ++j, ++it_write)
- it_write.Value() = value;
+ it_write.Set(value);
}
}
else
diff --git a/Logic/Framework/SegmentationUpdateIterator.h b/Logic/Framework/SegmentationUpdateIterator.h
new file mode 100644
index 0000000..8e2cb62
--- /dev/null
+++ b/Logic/Framework/SegmentationUpdateIterator.h
@@ -0,0 +1,287 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: GenericImageData.h,v $
+ Language: C++
+ Date: $Date: 2009/08/29 23:02:43 $
+ Version: $Revision: 1.11 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __SegmentationUpdateIterator_h_
+#define __SegmentationUpdateIterator_h_
+
+#include "SNAPCommon.h"
+#include "ImageWrapperTraits.h"
+#include "UndoDataManager.h"
+
+
+/**
+ * \class SegmentationUpdate
+ * \brief This class handles updates to the segmentation image at a high level.
+ */
+class SegmentationUpdateIterator
+{
+public:
+ typedef itk::Index<3> IndexType;
+ typedef itk::ImageRegion<3> RegionType;
+ typedef LabelImageWrapper::ImageType LabelImageType;
+ typedef itk::ImageRegionIterator<LabelImageType> LabelIteratorType;
+
+ typedef UndoDataManager<LabelType>::Delta UndoDelta;
+
+ enum UpdateType {
+ FOREGROUND, BACKGROUND, SKIP
+ };
+
+ SegmentationUpdateIterator(LabelImageType *labelImage,
+ const RegionType ®ion,
+ LabelType active_label,
+ DrawOverFilter draw_over)
+ : m_Region(region),
+ m_ActiveLabel(active_label),
+ m_DrawOver(draw_over),
+ m_Iterator(labelImage, region),
+ m_ChangedVoxels(0)
+ {
+ // Create the delta
+ m_Delta = new UndoDelta();
+ m_Delta->SetRegion(region);
+
+ // Set the voxel delta to zero
+ m_VoxelDelta = 0;
+ }
+
+ ~SegmentationUpdateIterator()
+ {
+ if(m_Delta)
+ delete m_Delta;
+ }
+
+ void operator ++()
+ {
+ // Encode the current voxel delta
+ m_Delta->Encode(m_VoxelDelta);
+ m_VoxelDelta = 0;
+
+ // Keep track of changed voxels
+ if(m_VoxelDelta != 0)
+ m_ChangedVoxels++;
+
+ // Update the internal iterator
+ ++m_Iterator;
+ }
+
+ const IndexType GetIndex()
+ {
+ return m_Iterator.GetIndex();
+ }
+
+ /**
+ * Paint with a specified label - respecting the draw-over mask
+ */
+ virtual void PaintLabel(LabelType new_label)
+ {
+ LabelType lOld = m_Iterator.Get();
+
+ if(m_DrawOver.CoverageMode == PAINT_OVER_ALL ||
+ (m_DrawOver.CoverageMode == PAINT_OVER_ONE && lOld == m_DrawOver.DrawOverLabel) ||
+ (m_DrawOver.CoverageMode == PAINT_OVER_VISIBLE && lOld != 0))
+ {
+ if(lOld != new_label)
+ {
+ m_VoxelDelta += new_label - lOld;
+ m_Iterator.Set(new_label);
+ m_ChangedVoxels++;
+ }
+ }
+ }
+
+
+ /**
+ * Default painting mode - applies active label using the current draw over mask
+ */
+ void PaintAsForeground()
+ {
+ this->PaintLabel(m_ActiveLabel);
+ }
+
+ /**
+ * Similar but clear label is not affected (used in cutplane mode)
+ */
+ void PaintAsForegroundPreserveClear()
+ {
+ LabelType lOld = m_Iterator.Get();
+ if(lOld == 0)
+ return;
+
+ if(m_DrawOver.CoverageMode == PAINT_OVER_ALL ||
+ (m_DrawOver.CoverageMode == PAINT_OVER_ONE && lOld == m_DrawOver.DrawOverLabel) ||
+ (m_DrawOver.CoverageMode == PAINT_OVER_VISIBLE && lOld != 0))
+ {
+ if(lOld != m_ActiveLabel)
+ {
+ m_VoxelDelta += m_ActiveLabel - lOld;
+ m_Iterator.Set(m_ActiveLabel);
+ m_ChangedVoxels++;
+ }
+ }
+ }
+
+
+
+ /**
+ * Reverse painting mode - applies clear label over active label (paintbrush RMB click)
+ */
+ void PaintAsBackground()
+ {
+ LabelType lOld = m_Iterator.Get();
+
+ if(m_ActiveLabel != 0 && lOld == m_ActiveLabel)
+ {
+ m_VoxelDelta += 0 - lOld;
+ m_Iterator.Set(0);
+ m_ChangedVoxels++;
+ }
+ }
+
+ /**
+ * More general method, replaces label with new_label if current label matches target_label,
+ * does not take into account Active/DrawOver state
+ */
+ void ReplaceLabel(LabelType target_label, LabelType new_label)
+ {
+ LabelType lOld = m_Iterator.Get();
+
+ if(lOld == target_label)
+ {
+ m_VoxelDelta += new_label - lOld;
+ m_Iterator.Set(new_label);
+ m_ChangedVoxels++;
+ }
+ }
+
+ /**
+ * A more funky paint method, applies the following test, where X is the
+ * label at the voxel
+ *
+ * if(X != A and X \in DrawOver)
+ * X = B
+ */
+ void PaintLabelWithExtraProtection(LabelType protect_label, LabelType new_label)
+ {
+ LabelType lOld = m_Iterator.Get();
+
+ // Test for protection or empty operation
+ if(lOld == protect_label || lOld == new_label)
+ return;
+
+ // Apply the draw over test
+ if(m_DrawOver.CoverageMode == PAINT_OVER_ALL ||
+ (m_DrawOver.CoverageMode == PAINT_OVER_ONE && lOld == m_DrawOver.DrawOverLabel) ||
+ (m_DrawOver.CoverageMode == PAINT_OVER_VISIBLE && lOld != 0))
+ {
+ m_VoxelDelta += new_label - lOld;
+ m_Iterator.Set(new_label);
+ m_ChangedVoxels++;
+ }
+ }
+
+
+ bool IsAtEnd()
+ {
+ return m_Iterator.IsAtEnd();
+ }
+
+ /**
+ * Call this method at the end of the iteration to finish encoding. This will also set the
+ * modified flag of the label image if there were any actual updates.
+ */
+ void Finalize()
+ {
+ m_Delta->FinishEncoding();
+ if(m_ChangedVoxels > 0)
+ m_Iterator.GetImage()->Modified();
+ }
+
+ // Keep delta from being deleted
+ UndoDelta *RelinquishDelta()
+ {
+ UndoDelta *delta = m_Delta;
+ m_Delta = NULL;
+ return delta;
+ }
+
+ // Get the number of changed voxels
+ unsigned long GetNumberOfChangedVoxels() const
+ {
+ return m_ChangedVoxels;
+ }
+
+ // Get the pointer to the detla
+ UndoDelta *GetDelta() const
+ {
+ return m_Delta;
+ }
+
+protected:
+
+ // Name of the segmentation update (for undo tracking)
+ std::string m_Title;
+
+ // Region over which update is performed
+ RegionType m_Region;
+
+ // Active label for the registration update
+ LabelType m_ActiveLabel;
+
+ // Coverage mode
+ DrawOverFilter m_DrawOver;
+
+ // RLE encoding of the segmentation update - for storing undo/redo points
+ UndoDelta *m_Delta;
+
+ // Iterator used internally
+ LabelIteratorType m_Iterator;
+
+ // Delta at the current location
+ LabelType m_VoxelDelta;
+
+ // Number of voxels actually modified
+ unsigned long m_ChangedVoxels;
+};
+
+
+#endif // SegmentationUpdateIterator
diff --git a/Logic/Framework/UndoDataManager.h b/Logic/Framework/UndoDataManager.h
index 5b3a21d..94e5fb9 100644
--- a/Logic/Framework/UndoDataManager.h
+++ b/Logic/Framework/UndoDataManager.h
@@ -38,6 +38,7 @@
#include <vector>
#include <list>
+#include <RLEImage.h>
/**
* \class UndoDataManager
@@ -47,6 +48,8 @@ template<typename TPixel> class UndoDataManager
{
public:
+ typedef itk::ImageRegion<3> RegionType;
+
/**
* The Delta class represents a difference between two images used in
* the Undo system. It only supports linear traversal of images and
@@ -60,6 +63,16 @@ public:
m_CurrentLength = 0;
m_UniqueID = m_UniqueIDCounter++;
}
+
+ void SetRegion(const RegionType ®ion)
+ {
+ this->m_Region = region;
+ }
+
+ const RegionType &GetRegion()
+ {
+ return m_Region;
+ }
void Encode(const TPixel &value)
{
@@ -103,6 +116,7 @@ public:
m_Array = other.m_Array;
m_CurrentLength = other.m_CurrentLength;
m_LastValue = other.m_LastValue;
+ m_Region = other.m_Region;
return *this;
}
@@ -113,49 +127,71 @@ public:
size_t m_CurrentLength;
TPixel m_LastValue;
+ // The delta is associated with an image region
+ RegionType m_Region;
+
// Each delta is assigned a unique ID at creation
unsigned long m_UniqueID;
static unsigned long m_UniqueIDCounter;
};
- UndoDataManager(size_t nMinDeltas, size_t nMaxTotalSize);
+ /** List of deltas and related iterators */
+ typedef std::list<Delta *> DList;
+ typedef typename DList::iterator DIterator;
+ typedef typename DList::const_iterator DConstIterator;
- void AppendDelta(Delta *delta);
+ /**
+ * A "commit" to the undo system consists of one or more deltas. For example
+ * in paintbrush drawing, as you drag the paintbrush, deltas are generated, but
+ * these deltas are associated with a single commit. The commit owns the deltas
+ * and deletes them when it is itself deleted.
+ */
+ class Commit
+ {
+ public:
+ Commit(const DList &list, const char *name);
+ void DeleteDeltas();
+ size_t GetNumberOfRLEs() const;
+ const DList &GetDeltas() const { return m_Deltas; }
+ protected:
+ DList m_Deltas;
+ std::string m_Name;
+ };
+
+ UndoDataManager(size_t nMinCommits, size_t nMaxTotalSize);
+
+ /** Add a delta to the staging list. The staging list must be committed */
+ void AddDeltaToStaging(Delta *delta);
+
+ /** Commit the deltas in the staging list - returns total number of RLEs updated */
+ int CommitStaging(const char *text);
+
+ /** Clear the undo stack (removes all commits) */
void Clear();
bool IsUndoPossible();
- Delta *GetDeltaForUndo();
+ const Commit &GetCommitForUndo();
bool IsRedoPossible();
- Delta *GetDeltaForRedo();
+ const Commit &GetCommitForRedo();
- Delta *GetCumulativeDelta()
- { return m_CumulativeDelta; }
+ size_t GetNumberOfCommits()
+ { return m_CommitList.size(); }
- void SetCumulativeDelta(Delta *);
-
- size_t GetNumberOfDeltas()
- { return m_DeltaList.size(); }
+private:
- /**
- * A state descriptor. This descriptor is used to compare the
- * state of the undo queue between two time points. The idea is
- * to know whether an image has changed from the time it was
- * saved or not. The state is basically the recording of all
- * deltas from the starting point to the current position
- */
- typedef std::list<unsigned long> StateDescriptor;
- StateDescriptor GetState() const;
+ // Current staging list - where deltas are added
+ DList m_StagingList;
-private:
- typedef std::list<Delta *> DList;
- typedef typename DList::iterator DIterator;
- typedef typename DList::const_iterator DConstIterator;
- DList m_DeltaList;
- DIterator m_Position;
- size_t m_TotalSize, m_MinDeltas, m_MaxTotalSize;
+ // List of commits typedefs
+ typedef std::list<Commit> CList;
+ typedef typename CList::iterator CIterator;
+ typedef typename CList::const_iterator CConstIterator;
- Delta *m_CumulativeDelta;
+ // A list of commits
+ CList m_CommitList;
+ CIterator m_Position;
+ size_t m_TotalSize, m_MinCommits, m_MaxTotalSize;
};
#endif // __UndoDataManager_h_
diff --git a/Logic/Framework/UndoDataManager.txx b/Logic/Framework/UndoDataManager.txx
index 24bf20a..e7189f2 100644
--- a/Logic/Framework/UndoDataManager.txx
+++ b/Logic/Framework/UndoDataManager.txx
@@ -39,13 +39,12 @@ UndoDataManager<TPixel>::Delta::m_UniqueIDCounter = 0;
template<typename TPixel>
UndoDataManager<TPixel>
-::UndoDataManager(size_t nMinDeltas, size_t nMaxTotalSize)
+::UndoDataManager(size_t nMinCommits, size_t nMaxTotalSize)
{
- this->m_MinDeltas = nMinDeltas;
+ this->m_MinCommits = nMinCommits;
this->m_MaxTotalSize = nMaxTotalSize;
this->m_TotalSize = 0;
- m_Position = m_DeltaList.begin();
- m_CumulativeDelta = NULL;
+ m_Position = m_CommitList.begin();
}
template<typename TPixel>
@@ -53,46 +52,77 @@ void
UndoDataManager<TPixel>
::Clear()
{
- // Delete all the deltas
- m_Position = m_DeltaList.begin();
- while(m_Position != m_DeltaList.end())
+ // Delete all the commits
+ m_Position = m_CommitList.begin();
+ while(m_Position != m_CommitList.end())
{
- delete *m_Position;
- m_Position = m_DeltaList.erase(m_Position);
+ // Deallocate all the deltas in this commit
+ m_Position->DeleteDeltas();
+ m_Position = m_CommitList.erase(m_Position);
}
m_TotalSize = 0;
+
+ // Clear the staging list
+ m_StagingList.clear();
}
template<typename TPixel>
void
UndoDataManager<TPixel>
-::AppendDelta(Delta *delta)
+::AddDeltaToStaging(Delta *delta)
+{
+ // Just add the delta to the staging list
+ m_StagingList.push_back(delta);
+}
+
+template<typename TPixel>
+int
+UndoDataManager<TPixel>
+::CommitStaging(const char *text)
{
// If we are not currently pointing past the end of the delta
// list, we should prune all the deltas from the current point
// to the end. So that's the loop that we do
- while(m_Position != m_DeltaList.end())
+ while(m_Position != m_CommitList.end())
{
- m_TotalSize -= (*m_Position)->GetNumberOfRLEs();
- delete *m_Position;
- m_Position = m_DeltaList.erase(m_Position);
+ m_TotalSize -= m_Position->GetNumberOfRLEs();
+ m_Position->DeleteDeltas();
+ m_Position = m_CommitList.erase(m_Position);
}
- // Check whether we need to prune from the back
- DIterator itHead = m_DeltaList.begin();
- while(m_DeltaList.size() > m_MinDeltas
- && m_TotalSize + delta->GetNumberOfRLEs() > m_MaxTotalSize)
+ // Create a commit that we will be adding
+ Commit new_commit(m_StagingList, text);
+
+ // Empty the staging list
+ m_StagingList.clear();
+
+ // Get the number of RLEs being added
+ size_t n_new_rles = new_commit.GetNumberOfRLEs();
+
+ // If there are no new rles, the just bail out
+ if(n_new_rles == 0)
{
- m_TotalSize -= (*itHead)->GetNumberOfRLEs();
- delete *itHead;
- itHead = m_DeltaList.erase(itHead);
+ new_commit.DeleteDeltas();
+ return 0;
+ }
+
+ // Check whether we need to prune from the back to keep total size under control
+ CIterator itHead = m_CommitList.begin();
+ while(m_CommitList.size() > m_MinCommits && m_TotalSize + n_new_rles > m_MaxTotalSize)
+ {
+ m_TotalSize -= itHead->GetNumberOfRLEs();
+ itHead->DeleteDeltas();
+ itHead = m_CommitList.erase(itHead);
}
// Now we have a well pruned list of deltas, and we can append
// the current delta to it;
- m_DeltaList.push_back(delta);
- m_Position = m_DeltaList.end();
- m_TotalSize += delta->GetNumberOfRLEs();
+ m_CommitList.push_back(new_commit);
+ m_Position = m_CommitList.end();
+ m_TotalSize += n_new_rles;
+
+ // Return the number of RLEs
+ return n_new_rles;
}
template<typename TPixel>
@@ -100,13 +130,13 @@ bool
UndoDataManager<TPixel>
::IsUndoPossible()
{
- return (m_DeltaList.size() > 0 && m_Position != m_DeltaList.begin());
+ return (m_CommitList.size() > 0 && m_Position != m_CommitList.begin());
}
template<typename TPixel>
-typename UndoDataManager<TPixel>::Delta *
+const typename UndoDataManager<TPixel>::Commit &
UndoDataManager<TPixel>
-::GetDeltaForUndo()
+::GetCommitForUndo()
{
// Can't be at the beginning
assert(IsUndoPossible());
@@ -123,48 +153,63 @@ bool
UndoDataManager<TPixel>
::IsRedoPossible()
{
- return (m_DeltaList.size() > 0 && m_Position != m_DeltaList.end());
+ return (m_CommitList.size() > 0 && m_Position != m_CommitList.end());
}
template<typename TPixel>
-typename UndoDataManager<TPixel>::Delta *
+const typename UndoDataManager<TPixel>::Commit &
UndoDataManager<TPixel>
-::GetDeltaForRedo()
+::GetCommitForRedo()
{
// Can't be at the beginning
assert(IsRedoPossible());
// Return the delta at the current position
- Delta *del = *m_Position;
+ const Commit &commit = *m_Position;
// Move the position one delta to the end
m_Position++;
// Return the current delta
- return del;
+ return commit;
}
+
+
template<typename TPixel>
-typename UndoDataManager<TPixel>::StateDescriptor
-UndoDataManager<TPixel>
-::GetState() const
+UndoDataManager<TPixel>::Commit::Commit(const DList &list, const char *name)
+{
+ m_Deltas = list;
+ m_Name = name;
+}
+
+template<typename TPixel>
+void
+UndoDataManager<TPixel>::Commit::DeleteDeltas()
{
- StateDescriptor sd;
- for(DConstIterator it = m_DeltaList.begin(); it != m_Position; ++it)
+ // Delete all the deltas
+ for(DIterator dit = m_Deltas.begin(); dit != m_Deltas.end(); ++dit)
{
- const Delta *delta = *it;
- sd.push_back(delta->GetUniqueID());
+ if(*dit)
+ {
+ delete *dit;
+ *dit = NULL;
+ }
}
- return sd;
}
+
+
template<typename TPixel>
-void
-UndoDataManager<TPixel>
-::SetCumulativeDelta(Delta *delta)
+size_t
+UndoDataManager<TPixel>::Commit::GetNumberOfRLEs() const
{
- if(m_CumulativeDelta)
- delete m_CumulativeDelta;
- m_CumulativeDelta = delta;
+ size_t n = 0;
+ for(DConstIterator dit = m_Deltas.begin(); dit != m_Deltas.end(); ++dit)
+ {
+ if(*dit)
+ n += (*dit)->GetNumberOfRLEs();
+ }
+ return n;
}
diff --git a/Logic/ImageWrapper/DisplayMappingPolicy.cxx b/Logic/ImageWrapper/DisplayMappingPolicy.cxx
index 4761a9c..2e6b3fb 100644
--- a/Logic/ImageWrapper/DisplayMappingPolicy.cxx
+++ b/Logic/ImageWrapper/DisplayMappingPolicy.cxx
@@ -71,6 +71,17 @@ ColorLabelTableDisplayMappingPolicy<TWrapperTraits>
}
template<class TWrapperTraits>
+typename ColorLabelTableDisplayMappingPolicy<TWrapperTraits>::DisplayPixelType
+ColorLabelTableDisplayMappingPolicy<TWrapperTraits>
+::MapPixel(const InputPixelType &val)
+{
+ DisplayPixelType pix;
+ ColorLabelTable *table = this->m_RGBAFilter[0]->GetColorTable();
+ table->GetColorLabel(val).GetRGBAVector(pix.GetDataPointer());
+ return pix;
+}
+
+template<class TWrapperTraits>
void
ColorLabelTableDisplayMappingPolicy<TWrapperTraits>
::SetLabelColorTable(ColorLabelTable *labels)
@@ -311,7 +322,14 @@ CachingCurveAndColorMapDisplayMappingPolicy<TWrapperTraits>
}
-
+template<class TWrapperTraits>
+typename CachingCurveAndColorMapDisplayMappingPolicy<TWrapperTraits>::DisplayPixelType
+CachingCurveAndColorMapDisplayMappingPolicy<TWrapperTraits>
+::MapPixel(const PixelType &val)
+{
+ DisplayPixelType pix = m_IntensityFilter[0]->MapPixel(val);
+ return pix;
+}
@@ -492,6 +510,14 @@ LinearColorMapDisplayMappingPolicy<TWrapperTraits>
+template<class TWrapperTraits>
+typename LinearColorMapDisplayMappingPolicy<TWrapperTraits>::DisplayPixelType
+LinearColorMapDisplayMappingPolicy<TWrapperTraits>
+::MapPixel(const PixelType &val)
+{
+ DisplayPixelType pix = m_Functor(val);
+ return pix;
+}
@@ -511,22 +537,24 @@ LinearColorMapDisplayMappingPolicy<TWrapperTraits>
MultiChannelDisplayMode::MultiChannelDisplayMode()
{
UseRGB = false;
+ RenderAsGrid = false;
SelectedScalarRep = SCALAR_REP_COMPONENT;
SelectedComponent = 0;
}
MultiChannelDisplayMode::MultiChannelDisplayMode(
- bool use_rgb, ScalarRepresentation rep,
+ bool use_rgb, bool render_as_grid,
+ ScalarRepresentation rep,
int comp)
- : UseRGB(use_rgb), SelectedScalarRep(rep),
- SelectedComponent(comp)
+ : UseRGB(use_rgb), RenderAsGrid(render_as_grid),
+ SelectedScalarRep(rep), SelectedComponent(comp)
{
-
}
MultiChannelDisplayMode::MultiChannelDisplayMode(int value)
{
UseRGB = false;
+ RenderAsGrid = false;
SelectedScalarRep = SCALAR_REP_COMPONENT;
SelectedComponent = 0;
}
@@ -542,6 +570,7 @@ MultiChannelDisplayMode::DefaultForRGB()
void MultiChannelDisplayMode::Save(Registry ®)
{
reg["UseRGB"] << UseRGB;
+ reg["RenderAsGrid"] << RenderAsGrid;
reg["SelectedScalarRep"].PutEnum(GetScalarRepNames(), SelectedScalarRep);
reg["SelectedComponent"] << SelectedComponent;
}
@@ -551,6 +580,7 @@ MultiChannelDisplayMode::Load(Registry ®)
{
MultiChannelDisplayMode mode;
mode.UseRGB = reg["UseRGB"][mode.UseRGB];
+ mode.RenderAsGrid = reg["RenderAsGrid"][mode.RenderAsGrid];
mode.SelectedScalarRep = reg["SelectedScalarRep"].GetEnum(
GetScalarRepNames(), mode.SelectedScalarRep);
mode.SelectedComponent = reg["SelectedComponent"][mode.SelectedComponent];
@@ -573,18 +603,21 @@ MultiChannelDisplayMode::GetScalarRepNames()
int MultiChannelDisplayMode::GetHashValue() const
{
+ if(RenderAsGrid)
+ return 0x1000000;
+
if(UseRGB)
- return 10000;
+ return 0x8000000;
if(SelectedScalarRep != SCALAR_REP_COMPONENT)
- return SelectedScalarRep * 100;
+ return 0x4000000 + SelectedScalarRep;
return SelectedComponent;
}
bool MultiChannelDisplayMode::IsSingleComponent()
{
- return !UseRGB && (SelectedScalarRep == SCALAR_REP_COMPONENT);
+ return !UseRGB && !RenderAsGrid && (SelectedScalarRep == SCALAR_REP_COMPONENT);
}
bool operator < (const MultiChannelDisplayMode &a, const MultiChannelDisplayMode &b)
@@ -666,7 +699,11 @@ MultiChannelDisplayMappingPolicy<TWrapperTraits>
// Add this filter as the input to the selector
m_DisplaySliceSelector[i]->AddSelectableInput(
- MultiChannelDisplayMode(true, SCALAR_REP_COMPONENT),
+ MultiChannelDisplayMode(true, false, SCALAR_REP_COMPONENT),
+ m_RGBMapper[i]->GetOutput());
+
+ m_DisplaySliceSelector[i]->AddSelectableInput(
+ MultiChannelDisplayMode(false, true, SCALAR_REP_COMPONENT),
m_RGBMapper[i]->GetOutput());
}
}
@@ -723,7 +760,7 @@ MultiChannelDisplayMappingPolicy<TWrapperTraits>
for(int i = 0; i < 3; i++)
{
m_DisplaySliceSelector[i]->AddSelectableInput(
- MultiChannelDisplayMode(false, rep, k),
+ MultiChannelDisplayMode(false, false, rep, k),
sw->GetDisplaySlice(i));
}
}
@@ -753,6 +790,12 @@ MultiChannelDisplayMappingPolicy<TWrapperTraits>
throw IRISException("RGB mode requested for %d component image", nc);
m_ScalarRepresentation = NULL;
}
+ else if(mode.RenderAsGrid)
+ {
+ if(nc != 3)
+ throw IRISException("Grid rendering mode requested for %d component image", nc);
+ m_ScalarRepresentation = NULL;
+ }
else
{
if(mode.SelectedComponent >= nc || mode.SelectedComponent < 0)
@@ -806,6 +849,23 @@ MultiChannelDisplayMappingPolicy<TWrapperTraits>
else return NULL;
}
+template<class TWrapperTraits>
+typename MultiChannelDisplayMappingPolicy<TWrapperTraits>::DisplayPixelType
+MultiChannelDisplayMappingPolicy<TWrapperTraits>
+::MapPixel(const PixelType &val)
+{
+ // This method should never be called directly for scalar modes (component, max, etc)
+ // because VectorImageWrapper should delegate calling this function to the
+ // appropriate scalar image wrapper.
+ assert(!m_ScalarRepresentation);
+
+ // Use the LUT
+ DisplayPixelType pix = m_RGBMapper[0]->MapPixel(val[0], val[1], val[2]);
+ return pix;
+}
+
+
+
template <class TWrapperTraits>
void
MultiChannelDisplayMappingPolicy<TWrapperTraits>
@@ -820,7 +880,7 @@ bool
MultiChannelDisplayMappingPolicy<TWrapperTraits>
::IsContrastMultiComponent() const
{
- if(m_DisplayMode.UseRGB || m_Animate)
+ if(m_DisplayMode.UseRGB || m_DisplayMode.RenderAsGrid || m_Animate)
return true;
return false;
@@ -837,7 +897,7 @@ MultiChannelDisplayMappingPolicy<TWrapperTraits>
// The native range is global componentwise max/min when we are in RGB mode
// or when we are in single component mode (because the curves are shared
// between these display modes).
- if(m_DisplayMode.UseRGB ||
+ if(m_DisplayMode.UseRGB || m_DisplayMode.RenderAsGrid ||
m_DisplayMode.SelectedScalarRep == SCALAR_REP_COMPONENT)
{
cmin = m_Wrapper->GetImageMinNative();
@@ -860,7 +920,7 @@ const ScalarImageHistogram *
MultiChannelDisplayMappingPolicy<TWrapperTraits>
::GetHistogram(int nBins)
{
- if(m_DisplayMode.UseRGB)
+ if(m_DisplayMode.UseRGB || m_DisplayMode.RenderAsGrid)
{
// In RGB mode, we should return a pooled histogram of the data.
return m_Wrapper->GetHistogram(nBins);
@@ -949,6 +1009,9 @@ MultiChannelDisplayMappingPolicy<TWrapperTraits>
if(m_Wrapper && mode.UseRGB && m_Wrapper->GetNumberOfComponents() != 3)
mode = MultiChannelDisplayMode();
+ if(m_Wrapper && mode.RenderAsGrid && m_Wrapper->GetNumberOfComponents() != 3)
+ mode = MultiChannelDisplayMode();
+
if(m_Wrapper && mode.SelectedComponent >= m_Wrapper->GetNumberOfComponents())
mode = MultiChannelDisplayMode();
diff --git a/Logic/ImageWrapper/DisplayMappingPolicy.h b/Logic/ImageWrapper/DisplayMappingPolicy.h
index 0a96a58..7cf54f1 100644
--- a/Logic/ImageWrapper/DisplayMappingPolicy.h
+++ b/Logic/ImageWrapper/DisplayMappingPolicy.h
@@ -72,6 +72,9 @@ public:
typedef ImageWrapperBase::DisplaySliceType DisplaySliceType;
typedef ImageWrapperBase::DisplaySlicePointer DisplaySlicePointer;
+ typedef InputSliceType::PixelType InputPixelType;
+ typedef DisplaySliceType::PixelType DisplayPixelType;
+
/**
* Set the table of color labels used to produce color slice images
*/
@@ -93,6 +96,8 @@ public:
virtual void Save(Registry &folder) {}
virtual void Restore(Registry &folder) {}
+ virtual DisplayPixelType MapPixel(const InputPixelType &val);
+
protected:
ColorLabelTableDisplayMappingPolicy();
@@ -249,6 +254,8 @@ public:
virtual void Save(Registry &folder);
virtual void Restore(Registry &folder);
+ virtual DisplayPixelType MapPixel(const PixelType &val);
+
protected:
@@ -326,6 +333,11 @@ struct MultiChannelDisplayMode
bool UseRGB;
/**
+ * Special mode for rendering 3-component images as displacement grids
+ */
+ bool RenderAsGrid;
+
+ /**
* When not in RGB mode, which scalar representation is selected for
* display. Only used if UseRGB is false.
*/
@@ -341,7 +353,7 @@ struct MultiChannelDisplayMode
MultiChannelDisplayMode();
/** Default constructor - select first component */
- MultiChannelDisplayMode(bool use_rgb,
+ MultiChannelDisplayMode(bool use_rgb, bool render_as_grid,
ScalarRepresentation rep,
int comp = 0);
@@ -428,7 +440,9 @@ public:
irisGetMacro(ColorMap, ColorMap *)
- protected:
+ DisplayPixelType MapPixel(const PixelType &xin);
+
+protected:
LinearColorMapDisplayMappingPolicy();
virtual ~LinearColorMapDisplayMappingPolicy();
@@ -526,6 +540,8 @@ public:
irisGetMacro(ScalarRepresentation, ScalarImageWrapperBase *)
+ DisplayPixelType MapPixel(const PixelType &val);
+
protected:
MultiChannelDisplayMappingPolicy();
diff --git a/Logic/ImageWrapper/GuidedNativeImageIO.cxx b/Logic/ImageWrapper/GuidedNativeImageIO.cxx
index 4da55a9..92ab7d1 100755
--- a/Logic/ImageWrapper/GuidedNativeImageIO.cxx
+++ b/Logic/ImageWrapper/GuidedNativeImageIO.cxx
@@ -56,6 +56,7 @@
#include "itkImageSeriesReader.h"
#include "itkImageIOFactory.h"
#include "itkGDCMSeriesFileNames.h"
+#include "itkCommand.h"
#include "gdcmFile.h"
#include "gdcmReader.h"
#include "gdcmStringFilter.h"
@@ -426,6 +427,40 @@ GuidedNativeImageIO
}
}
+/**
+ * This is basically to just expose AddFileName
+ */
+class ExtendedGDCMSerieHelper : public gdcm::SerieHelper
+{
+public:
+ ExtendedGDCMSerieHelper() : gdcm::SerieHelper() {}
+
+ void SetFilesAndOrder(std::vector<std::string> &files)
+ {
+ this->Clear();
+ this->SetUseSeriesDetails(true);
+
+ for(int i = 0; i < files.size(); i++)
+ this->AddFileName(files[i]);
+
+ gdcm::FileList *flist = this->GetFirstSingleSerieUIDFileSet();
+
+ if(flist->size() != files.size())
+ throw(IRISException(
+ "Mismatch in number of DICOM files parsed (%d vs. %d). "),
+ flist->size(), files.size());
+
+ this->OrderFileList(flist);
+
+ files.clear();
+ for(int i = 0; i < flist->size(); i++)
+ {
+ gdcm::FileWithName *header = (*flist)[i];
+ files.push_back(header->filename);
+ }
+ }
+};
+
void
GuidedNativeImageIO
@@ -450,35 +485,29 @@ GuidedNativeImageIO
if(!itksys::SystemTools::FileIsDirectory(FileName))
SeriesDir = itksys::SystemTools::GetParentDirectory(FileName);
- // NOTE: for the time being, we are relying on GDCMSeriesFileNames for
- // proper sorting of the DICOM data. This is marked as deprecated in GDCM 2
- if(m_GDCMSeries.IsNull() || m_GDCMSeriesDirectory != SeriesDir)
+ // Have we already parsed this directory?
+ if(m_LastDicomParseResult.Directory != SeriesDir)
{
- m_GDCMSeries = itk::GDCMSeriesFileNames::New();
- m_GDCMSeries->SetUseSeriesDetails(true);
- m_GDCMSeries->SetDirectory(SeriesDir);
- m_GDCMSeriesDirectory = SeriesDir;
+ // Parse the specified directory
+ this->ParseDicomDirectory(SeriesDir);
}
// Select which series
std::string SeriesID = m_Hints["DICOM.SeriesId"][""];
if(SeriesID.length() == 0)
{
- // Get the list of series in the directory
- const itk::SerieUIDContainer &sids = m_GDCMSeries->GetSeriesUIDs();
-
- // There must be at least of series
- if(sids.size() == 0)
+ // There must be at least one series
+ if(m_LastDicomParseResult.SeriesMap.size() == 0)
throw IRISException("Error: DICOM series not found. "
"Directory '%s' does not appear to contain a "
- "series of DICOM images.",FileName);
+ "series of DICOM images.", FileName);
- // Read the first DICOM series in the directory
- SeriesID = sids.front();
+ // Take the first series ID we have
+ SeriesID = m_LastDicomParseResult.SeriesMap.begin()->first;
}
- // Use the series provided by the user
- m_DICOMFiles = m_GDCMSeries->GetFileNames(SeriesID.c_str());
+ // Obtain the filename for this series
+ m_DICOMFiles = m_LastDicomParseResult.SeriesMap[SeriesID].FileList;
// Read the information from the first filename
if(m_DICOMFiles.size() == 0)
@@ -486,6 +515,13 @@ GuidedNativeImageIO
"Directory '%s' does not appear to contain a "
"series of DICOM images.",FileName);
+ // Following this quick parsing of the directory, we need to actually
+ // load the image data and sort it in a meaningful order. This is too
+ // complicated to replicate here so we revert to gdcm::SerieHelper, but
+ // we only have it parse the filenames for the current SeriesId
+ ExtendedGDCMSerieHelper helper;
+ helper.SetFilesAndOrder(m_DICOMFiles);
+
m_IOBase->SetFileName(m_DICOMFiles[0]);
m_IOBase->ReadImageInformation();
}
@@ -1262,18 +1298,161 @@ GuidedNativeImageIO::GuessFormatForFileName(
const gdcm::Tag GuidedNativeImageIO::m_tagRows(0x0028, 0x0010);
const gdcm::Tag GuidedNativeImageIO::m_tagCols(0x0028, 0x0011);
const gdcm::Tag GuidedNativeImageIO::m_tagDesc(0x0008, 0x103e);
-const gdcm::Tag GuidedNativeImageIO::m_tagTextDesc(0x0028, 0x0010);
const gdcm::Tag GuidedNativeImageIO::m_tagSeriesInstanceUID(0x0020,0x000E);
const gdcm::Tag GuidedNativeImageIO::m_tagSeriesNumber(0x0020,0x0011);
const gdcm::Tag GuidedNativeImageIO::m_tagAcquisitionNumber(0x0020,0x0012);
const gdcm::Tag GuidedNativeImageIO::m_tagInstanceNumber(0x0020,0x0013);
+const gdcm::Tag GuidedNativeImageIO::m_tagSequenceName(0x0018, 0x0024);
+const gdcm::Tag GuidedNativeImageIO::m_tagSliceThickness(0x0018, 0x0050);
+#include "gdcmDirectory.h"
+#include "gdcmImageReader.h"
-void GuidedNativeImageIO::ParseDicomDirectory(
- const std::string &dir,
- GuidedNativeImageIO::RegistryArray ®,
- const GuidedNativeImageIO::DicomRequest &req)
+void
+GuidedNativeImageIO
+::ParseDicomDirectory(const std::string &dir, itk::Command *progressCommand)
+{
+ // We will parse the DICOM directory manually to avoid extra time opening
+ // files and also to allow progress reporting
+
+ // Must have a directory
+ if(!itksys::SystemTools::FileIsDirectory(dir.c_str()))
+ throw IRISException(
+ "Error: Not a directory. "
+ "Trying to look for DICOM series in '%s', which is not a directory",
+ dir.c_str());
+
+ // List of tags used for refined grouping of files - order matters!
+ std::vector<gdcm::Tag> tags_refine;
+ tags_refine.push_back(m_tagSeriesNumber);
+ tags_refine.push_back(m_tagSequenceName);
+ tags_refine.push_back(m_tagSliceThickness);
+ tags_refine.push_back(m_tagRows);
+ tags_refine.push_back(m_tagCols);
+
+ // List of tags that we want to parse - everything else may be ignored
+ std::set<gdcm::Tag> tags_all;
+ tags_all.insert(tags_refine.begin(), tags_refine.end());
+ tags_all.insert(m_tagDesc);
+ tags_all.insert(m_tagSeriesInstanceUID);
+
+ // Clear the information about the last parse
+ m_LastDicomParseResult.Reset();
+ m_LastDicomParseResult.Directory = dir;
+
+ // GDCM directory listing
+ gdcm::Directory dirList;
+
+ // Load the directory - this should be quick
+ dirList.Load(dir, false);
+ gdcm::Directory::FilenamesType const &filenames = dirList.GetFilenames();
+ for(gdcm::Directory::FilenamesType::const_iterator it = filenames.begin();
+ it != filenames.end(); ++it)
+ {
+ // Process each filename in the directory
+ gdcm::Reader reader;
+ reader.SetFileName(it->c_str());
+
+ // Try reading this file. Fail quietly.
+ bool read = false;
+ try { read = reader.ReadSelectedTags(tags_all, true); }
+ catch(...) {}
+
+ // If nothing read, keep going
+ if(!read)
+ continue;
+
+ // Create a string filter to get tags
+ gdcm::StringFilter sf;
+ sf.SetFile(reader.GetFile());
+
+ // Start with the ID being the UID
+ std::string uid = sf.ToString(m_tagSeriesInstanceUID);
+ std::string full_id = uid;
+
+ // Iterate over the tags in the refine list
+ for(int iTag = 0; iTag < tags_refine.size(); iTag++)
+ {
+ // Read the tag value
+ std::string s = sf.ToString(tags_refine[iTag]);
+
+ // This code is from gdcmSerieHelper
+ if( full_id == uid && !s.empty() )
+ {
+ full_id += "."; // add separator
+ }
+ full_id += s;
+ }
+
+ // Eliminate non-alnum characters, including whitespace...
+ // that may have been introduced by concats.
+ for(size_t i=0; i<full_id.size(); i++)
+ {
+ while(i<full_id.size()
+ && !( full_id[i] == '.'
+ || (full_id[i] >= 'a' && full_id[i] <= 'z')
+ || (full_id[i] >= '0' && full_id[i] <= '9')
+ || (full_id[i] >= 'A' && full_id[i] <= 'Z')))
+ {
+ full_id.erase(i, 1);
+ }
+ }
+
+ // The info for the current series
+ DicomDirectoryParseResult::DicomSeriesInfo &series_info
+ = m_LastDicomParseResult.SeriesMap[full_id];
+
+ // The registry for the current series
+ Registry &r = series_info.MetaData;
+
+ // Have we found this ID before?
+ if(r.IsEmpty())
+ {
+ r["SeriesId"] << full_id;
+
+ // Read series description
+ r["SeriesDescription"] << sf.ToString(m_tagDesc);
+ r["SeriesNumber"] << sf.ToString(m_tagSeriesNumber);
+
+ // Read the dimensions
+ r["Rows"] << std::atoi(sf.ToString(m_tagRows).c_str());
+ r["Columns"] << std::atoi(sf.ToString(m_tagCols).c_str());
+ r["NumberOfImages"] << 1;
+ }
+ else
+ {
+ // Increement the number of images
+ r["NumberOfImages"] << r["NumberOfImages"][0] + 1;
+ }
+
+ // Update the dimensions string
+ ostringstream oss;
+ oss << r["Rows"][0] << " x " << r["Columns"][0] << " x " << r["NumberOfImages"][0];
+ r["Dimensions"] << oss.str();
+
+ // Update the filelist
+ series_info.FileList.push_back(*it);
+
+ // Indicate some progress
+ if(progressCommand)
+ progressCommand->Execute(this, itk::ProgressEvent());
+ }
+
+ // Complain if no series have been found
+ if(m_LastDicomParseResult.SeriesMap.size() == 0)
+ throw IRISException(
+ "Error: DICOM series not found. "
+ "Directory '%s' does not appear to contain a DICOM series.", dir.c_str());
+}
+
+void GuidedNativeImageIO::DicomDirectoryParseResult::Reset()
+{
+ Directory.clear();
+ SeriesMap.clear();
+}
+
+/*
{
// Must have a directory
if(!itksys::SystemTools::FileIsDirectory(dir.c_str()))
@@ -1339,6 +1518,8 @@ void GuidedNativeImageIO::ParseDicomDirectory(
// Add the registry to the list
reg.push_back(r);
+
+ progressCommand->Execute(this, itk::ProgressEvent());
}
}
@@ -1349,6 +1530,8 @@ void GuidedNativeImageIO::ParseDicomDirectory(
"Directory '%s' does not appear to contain a DICOM series.", dir.c_str());
}
+*/
+
/*
template<typename TPixel>
diff --git a/Logic/ImageWrapper/GuidedNativeImageIO.h b/Logic/ImageWrapper/GuidedNativeImageIO.h
index b077761..2085100 100755
--- a/Logic/ImageWrapper/GuidedNativeImageIO.h
+++ b/Logic/ImageWrapper/GuidedNativeImageIO.h
@@ -48,6 +48,7 @@ namespace itk
{
template<class TPixel, unsigned int VDim> class Image;
class ImageIOBase;
+ class Command;
}
@@ -92,6 +93,32 @@ public:
bool TestFilename(std::string fname);
};
+ /**
+ * Structure describing last parsed DICOM directory - basically
+ * a map from SeriesId to MetaData registry and a list of files
+ * that is not ordered until the image is actually loaded.
+ */
+ struct DicomDirectoryParseResult
+ {
+ typedef std::vector<std::string> FileListType;
+
+ // Structure describing one DICOM series
+ struct DicomSeriesInfo {
+ Registry MetaData;
+ FileListType FileList;
+ };
+
+ typedef std::map<std::string, DicomSeriesInfo> SeriesMapType;
+
+ // Directory that was parsed
+ std::string Directory;
+
+ // Filenames of the images for each series ID
+ SeriesMapType SeriesMap;
+
+ void Reset();
+ };
+
// Image type. This is only for 3D images.
typedef itk::ImageIOBase IOBase;
@@ -211,41 +238,30 @@ public:
/** Set the file format in a registry */
static void SetPixelType(Registry &folder, RawPixelType type);
- /** A field used to request DICOM fields */
- struct DicomRequestField
- {
- short group, elem;
- std::string code;
- DicomRequestField(short g, short e, std::string c)
- : group(g), elem(e), code(c) {}
- DicomRequestField()
- : group(0), elem(0) {}
- };
-
- /** A parameter for ParseDicomDirectory */
- typedef std::vector<DicomRequestField> DicomRequest;
-
/** Output for ParseDicomDirectory */
typedef std::vector<Registry> RegistryArray;
/**
* Get series information from a DICOM directory. This will list all the
* files in the DICOM directory and generate a registry for each series in
- * the directory. By default, the following registry entries are generated
- * for each series:
+ * the directory. The following registry entries are generated for each series:
* - SeriesDescription
* - Dimensions
* - NumberOfImages
* - SeriesId
* - SeriesFiles (an array with filenames)
- * You can also ask for additional DICOM entries to be extracted by giving
- * a list of DICOM keys in the third optional parameter.
+ *
+ * To obtain the result of the parsing call GetLastDicomParseRegistry()
*/
void ParseDicomDirectory(
- const std::string &dir,
- RegistryArray ®,
- const DicomRequest &req = DicomRequest());
+ const std::string &dir, itk::Command *progressCommand = NULL);
+ /**
+ * Get the result of the last parse operation. This should be safe to
+ * call from the callback of progressCommand in ParseDicomDirectory(),
+ * and can be done to update a GUI on the fly, as images are being parsed
+ */
+ itkGetConstReferenceMacro(LastDicomParseResult, DicomDirectoryParseResult)
/**
* Create an ImageIO object using a registry folder. Second parameter is
@@ -280,9 +296,8 @@ protected:
// The IO base used to read the files
IOBasePointer m_IOBase;
- // GDCM series reader/parser (for DICOM)
- SmartPtr<itk::GDCMSeriesFileNames> m_GDCMSeries;
- std::string m_GDCMSeriesDirectory;
+ // DICOM directory last processed by ParseDicomSeries
+ DicomDirectoryParseResult m_LastDicomParseResult;
// This information is copied from IOBase in order to delete IOBase at the
// earliest possible point, so as to conserve memory
@@ -314,12 +329,13 @@ protected:
static const gdcm::Tag m_tagRows;
static const gdcm::Tag m_tagCols;
static const gdcm::Tag m_tagDesc;
- static const gdcm::Tag m_tagTextDesc;
static const gdcm::Tag m_tagSeriesInstanceUID;
static const gdcm::Tag m_tagSeriesNumber;
static const gdcm::Tag m_tagAcquisitionNumber;
static const gdcm::Tag m_tagInstanceNumber;
-
+ static const gdcm::Tag m_tagSequenceName;
+ static const gdcm::Tag m_tagSliceThickness;
+
};
diff --git a/Logic/ImageWrapper/ImageWrapper.cxx b/Logic/ImageWrapper/ImageWrapper.cxx
index cf88d0b..d156d6c 100644
--- a/Logic/ImageWrapper/ImageWrapper.cxx
+++ b/Logic/ImageWrapper/ImageWrapper.cxx
@@ -6,8 +6,8 @@
Date: $Date: 2010/10/14 16:21:04 $
Version: $Revision: 1.11 $
Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
+
+ This file is part of ITK-SNAP
ITK-SNAP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -37,17 +37,18 @@
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE. See the above copyright notices for more information.
+ PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "ImageWrapper.h"
-#include "itkImageRegionIterator.h"
+#include "RLEImageRegionIterator.h"
+#include "RLERegionOfInterestImageFilter.h"
#include "itkImageSliceConstIteratorWithIndex.h"
#include "itkNumericTraits.h"
#include "itkRegionOfInterestImageFilter.h"
#include "itkIdentityTransform.h"
-#include "IRISSlicer.h"
+#include "AdaptiveSlicingPipeline.h"
#include "SNAPSegmentationROISettings.h"
#include "itkCommand.h"
#include "ImageCoordinateGeometry.h"
@@ -69,6 +70,8 @@
#include "ScalarImageHistogram.h"
#include "GuidedNativeImageIO.h"
#include "itkTransform.h"
+#include "itkExtractImageFilter.h"
+#include "itkMatrixOffsetTransformBase.h"
#include <vnl/vnl_inverse.h>
@@ -89,6 +92,8 @@ public:
};
+
+
/**
* Some functions in the image wrapper are only defined for 'concrete' image
* wrappers, i.e., those that store an image or a vectorimage. These functions
@@ -103,6 +108,9 @@ public:
typedef TImage ImageType;
typedef typename TImage::PixelType PixelType;
+ typedef itk::ImageBase<TImage::ImageDimension> ImageBaseType;
+ typedef itk::Transform<double, TImage::ImageDimension, TImage::ImageDimension> TransformType;
+
static void FillBuffer(ImageType *image, PixelType)
{
throw IRISException("FillBuffer unsupported for class %s",
@@ -116,7 +124,10 @@ public:
}
static SmartPtr<ImageType> CopyRegion(ImageType *image,
+ ImageBaseType *ref_space,
+ const TransformType *transform,
const SNAPSegmentationROISettings &roi,
+ bool force_resampling,
itk::Command *progressCommand)
{
throw IRISException("CopyRegion unsupported for class %s",
@@ -131,6 +142,8 @@ class ImageWrapperPartialSpecializationTraitsCommon
public:
typedef TImage ImageType;
typedef typename TImage::PixelType PixelType;
+ typedef itk::ImageBase<TImage::ImageDimension> ImageBaseType;
+ typedef itk::Transform<double, TImage::ImageDimension, TImage::ImageDimension> TransformType;
static void FillBuffer(ImageType *image, PixelType p)
{
@@ -155,17 +168,20 @@ public:
template <class TInterpolateFunction>
static SmartPtr<ImageType> DeepCopyImageRegion(
ImageType *image,
+ ImageBaseType *refspace,
+ const TransformType *transform,
TInterpolateFunction *interp,
const SNAPSegmentationROISettings &roi,
+ bool force_resampling,
itk::Command *progressCommand)
{
// Check if there is a difference in voxel size, i.e., user wants resampling
- Vector3d vOldSpacing = image->GetSpacing();
- Vector3d vOldOrigin = image->GetOrigin();
+ Vector3d vOldSpacing = refspace->GetSpacing();
+ Vector3d vOldOrigin = refspace->GetOrigin();
Vector3i vROIIndex(roi.GetROI().GetIndex());
Vector3ui vROISize(roi.GetROI().GetSize());
- if(roi.IsResampling())
+ if(force_resampling || roi.IsResampling())
{
// Compute the number of voxels in the output
typedef typename itk::ImageRegion<3> RegionType;
@@ -174,7 +190,7 @@ public:
// We need to compute the new spacing and origin of the resampled
// ROI piece. To do this, we need the direction matrix
typedef typename ImageType::DirectionType DirectionType;
- const DirectionType &dm = image->GetDirection();
+ const DirectionType &dm = refspace->GetDirection();
// The spacing of the new ROI
Vector3d vNewSpacing =
@@ -193,14 +209,14 @@ public:
// Initialize the resampling filter
fltSample->SetInput(image);
- fltSample->SetTransform(itk::IdentityTransform<double,3>::New());
+ fltSample->SetTransform(transform);
fltSample->SetInterpolator(interp);
// Set the image sizes and spacing
fltSample->SetSize(to_itkSize(roi.GetResampleDimensions()));
fltSample->SetOutputSpacing(vNewSpacing.data_block());
fltSample->SetOutputOrigin(vNewOrigin.data_block());
- fltSample->SetOutputDirection(image->GetDirection());
+ fltSample->SetOutputDirection(refspace->GetDirection());
// Set the progress bar
if(progressCommand)
@@ -243,7 +259,10 @@ public:
typedef ImageWrapperPartialSpecializationTraitsCommon<ImageType> Superclass;
static SmartPtr<ImageType> CopyRegion(ImageType *image,
+ typename Superclass::ImageBaseType *refspace,
+ const typename Superclass::TransformType *transform,
const SNAPSegmentationROISettings &roi,
+ bool force_resampling,
itk::Command *progressCommand)
{
typedef itk::InterpolateImageFunction<ImageType> Interpolator;
@@ -252,22 +271,22 @@ public:
// Choose the interpolator
switch(roi.GetInterpolationMethod())
{
- case SNAPSegmentationROISettings::NEAREST_NEIGHBOR :
+ case NEAREST_NEIGHBOR :
typedef itk::NearestNeighborInterpolateImageFunction<ImageType,double> NNInterpolatorType;
interp = NNInterpolatorType::New().GetPointer();
break;
- case SNAPSegmentationROISettings::TRILINEAR :
+ case TRILINEAR :
typedef itk::LinearInterpolateImageFunction<ImageType,double> LinearInterpolatorType;
interp = LinearInterpolatorType::New().GetPointer();
break;
- case SNAPSegmentationROISettings::TRICUBIC :
+ case TRICUBIC :
typedef itk::BSplineInterpolateImageFunction<ImageType,double> CubicInterpolatorType;
interp = CubicInterpolatorType::New().GetPointer();
break;
- case SNAPSegmentationROISettings::SINC_WINDOW_05 :
+ case SINC_WINDOW_05 :
// More typedefs are needed for the sinc interpolator
static const unsigned int VRadius = 5;
typedef itk::Function::HammingWindowFunction<VRadius> WindowFunction;
@@ -278,7 +297,7 @@ public:
break;
};
- return Superclass::template DeepCopyImageRegion<Interpolator>(image,interp,roi,progressCommand);
+ return Superclass::template DeepCopyImageRegion<Interpolator>(image,refspace,transform,interp,roi,force_resampling,progressCommand);
}
};
@@ -298,7 +317,10 @@ public:
}
static SmartPtr<ImageType> CopyRegion(ImageType *image,
+ typename Superclass::ImageBaseType *refspace,
+ const typename Superclass::TransformType *transform,
const SNAPSegmentationROISettings &roi,
+ bool force_resampling,
itk::Command *progressCommand)
{
typedef itk::InterpolateImageFunction<ImageType> Interpolator;
@@ -307,12 +329,12 @@ public:
// Choose the interpolator
switch(roi.GetInterpolationMethod())
{
- case SNAPSegmentationROISettings::NEAREST_NEIGHBOR :
+ case NEAREST_NEIGHBOR :
typedef itk::NearestNeighborInterpolateImageFunction<ImageType> NNInterpolatorType;
interp = NNInterpolatorType::New().GetPointer();
break;
- case SNAPSegmentationROISettings::TRILINEAR :
+ case TRILINEAR :
typedef itk::LinearInterpolateImageFunction<ImageType> LinearInterpolatorType;
interp = LinearInterpolatorType::New().GetPointer();
break;
@@ -321,28 +343,203 @@ public:
throw IRISException("Higher-order interpolation for vector images is unsupported.");
};
- return Superclass::template DeepCopyImageRegion<Interpolator>(image,interp,roi,progressCommand);
+ return Superclass::template DeepCopyImageRegion<Interpolator>(image,refspace,transform,interp,roi,force_resampling,progressCommand);
+ }
+
+};
+
+
+template<class TPixel, unsigned int VDim, class CounterType>
+class ImageWrapperPartialSpecializationTraits< RLEImage<TPixel, VDim, CounterType> >
+{
+public:
+ typedef ImageWrapperPartialSpecializationTraits Self;
+ typedef RLEImage<TPixel, VDim, CounterType> ImageType;
+ typedef itk::Image<TPixel, VDim> UncompressedType;
+ typedef typename ImageType::PixelType PixelType;
+ typedef itk::ImageBase<VDim> ImageBaseType;
+ typedef itk::Transform<double, VDim, VDim> TransformType;
+
+ static void FillBuffer(ImageType *image, PixelType p)
+ {
+ image->FillBuffer(p);
+ }
+
+ static void Write(ImageType *image, const char *fname, Registry &hints)
+ {
+ //use specialized RoI filter to convert to itk::Image
+ typedef itk::RegionOfInterestImageFilter<ImageType, UncompressedType> outConverterType;
+ typename outConverterType::Pointer outConv = outConverterType::New();
+ outConv->SetInput(image);
+ outConv->SetRegionOfInterest(image->GetLargestPossibleRegion());
+ outConv->Update();
+ typename UncompressedType::Pointer imgUncompressed = outConv->GetOutput();
+
+ SmartPtr<GuidedNativeImageIO> io = GuidedNativeImageIO::New();
+ io->CreateImageIO(fname, hints, false);
+ itk::ImageIOBase *base = io->GetIOBase();
+
+ typedef itk::ImageFileWriter<UncompressedType> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetFileName(fname);
+ if (base)
+ writer->SetImageIO(base);
+ writer->SetInput(imgUncompressed);
+ writer->Update();
+ }
+
+ template <class TInterpolateFunction>
+ static SmartPtr<ImageType> DeepCopyImageRegion(
+ ImageType *image,
+ ImageBaseType *ref_space,
+ const TransformType *transform,
+ TInterpolateFunction *interp,
+ const SNAPSegmentationROISettings &roi,
+ bool force_resampling,
+ itk::Command *progressCommand)
+ {
+ // Check if there is a difference in voxel size, i.e., user wants resampling
+ Vector3d vOldSpacing = image->GetSpacing();
+ Vector3d vOldOrigin = image->GetOrigin();
+ Vector3i vROIIndex(roi.GetROI().GetIndex());
+ Vector3ui vROISize(roi.GetROI().GetSize());
+
+ if (force_resampling || roi.IsResampling())
+ {
+ // Compute the number of voxels in the output
+ typedef typename itk::ImageRegion<3> RegionType;
+ typedef typename itk::Size<3> SizeType;
+
+ // We need to compute the new spacing and origin of the resampled
+ // ROI piece. To do this, we need the direction matrix
+ typedef typename ImageType::DirectionType DirectionType;
+ const DirectionType &dm = ref_space->GetDirection();
+
+ // The spacing of the new ROI
+ Vector3d vNewSpacing =
+ element_quotient(element_product(vOldSpacing, to_double(vROISize)),
+ to_double(roi.GetResampleDimensions()));
+
+ // The origin of the new ROI
+ Vector3d vNewOrigin =
+ vOldOrigin + dm.GetVnlMatrix() * (
+ element_product((to_double(vROIIndex) - 0.5), vOldSpacing) +
+ vNewSpacing * 0.5);
+
+ //use specialized RoI filter to convert the region to be resampled to itk::Image
+ typedef itk::RegionOfInterestImageFilter<ImageType, UncompressedType> outConverterType;
+ typename outConverterType::Pointer outConv = outConverterType::New();
+ outConv->SetInput(image);
+ outConv->SetRegionOfInterest(roi.GetROI());
+ outConv->Update();
+ typename UncompressedType::Pointer imgUncompressed = outConv->GetOutput();
+
+ // Create a filter for resampling the image
+ typedef itk::ResampleImageFilter<UncompressedType, ImageType> ResampleFilterType;
+ typename ResampleFilterType::Pointer fltSample = ResampleFilterType::New();
+
+ // Initialize the resampling filter
+ fltSample->SetInput(imgUncompressed);
+ fltSample->SetTransform(transform);
+ fltSample->SetInterpolator(interp);
+
+ // Set the image sizes and spacing
+ fltSample->SetSize(to_itkSize(roi.GetResampleDimensions()));
+ fltSample->SetOutputSpacing(vNewSpacing.data_block());
+ fltSample->SetOutputOrigin(vNewOrigin.data_block());
+ fltSample->SetOutputDirection(ref_space->GetDirection());
+
+ // Set the progress bar
+ if (progressCommand)
+ fltSample->AddObserver(itk::AnyEvent(), progressCommand);
+
+ fltSample->Update();
+
+ return fltSample->GetOutput();
+ }
+ else
+ {
+ // The filter used to chop off the region of interest
+ typedef itk::RegionOfInterestImageFilter <ImageType, ImageType> ChopFilterType;
+ typename ChopFilterType::Pointer fltChop = ChopFilterType::New();
+
+ // Pipe image into the chopper
+ fltChop->SetInput(image);
+
+ // Set the region of interest
+ fltChop->SetRegionOfInterest(roi.GetROI());
+
+ // Update the pipeline
+ fltChop->Update();
+
+ // Return the resulting image
+ return fltChop->GetOutput();
+ }
}
+ static SmartPtr<ImageType> CopyRegion(ImageType *image,
+ ImageBaseType *refspace,
+ const TransformType *transform,
+ const SNAPSegmentationROISettings &roi,
+ bool force_resampling,
+ itk::Command *progressCommand)
+ {
+ //the interpolator will operate on uncompressed region
+ typedef itk::InterpolateImageFunction<UncompressedType> Interpolator;
+ SmartPtr<Interpolator> interp = NULL;
+
+ // Choose the interpolator
+ switch(roi.GetInterpolationMethod())
+ {
+ case NEAREST_NEIGHBOR :
+ typedef itk::NearestNeighborInterpolateImageFunction<UncompressedType, double> NNInterpolatorType;
+ interp = NNInterpolatorType::New().GetPointer();
+ break;
+
+ case TRILINEAR:
+ typedef itk::LinearInterpolateImageFunction<UncompressedType, double> LinearInterpolatorType;
+ interp = LinearInterpolatorType::New().GetPointer();
+ break;
+
+ case TRICUBIC:
+ typedef itk::BSplineInterpolateImageFunction<UncompressedType, double> CubicInterpolatorType;
+ interp = CubicInterpolatorType::New().GetPointer();
+ break;
+
+ case SINC_WINDOW_05:
+ // More typedefs are needed for the sinc interpolator
+ static const unsigned int VRadius = 5;
+ typedef itk::Function::HammingWindowFunction<VRadius> WindowFunction;
+ typedef itk::ConstantBoundaryCondition<UncompressedType> Condition;
+ typedef itk::WindowedSincInterpolateImageFunction<
+ UncompressedType, VRadius, WindowFunction, Condition, double> SincInterpolatorType;
+ interp = SincInterpolatorType::New().GetPointer();
+ break;
+ };
+
+ return Self::template DeepCopyImageRegion<Interpolator>(image, refspace, transform, interp, roi, force_resampling, progressCommand);
+ }
};
+
template<class TTraits, class TBase>
ImageWrapper<TTraits,TBase>
-::ImageWrapper()
+::ImageWrapper()
{
CommonInitialization();
}
template<class TTraits, class TBase>
ImageWrapper<TTraits,TBase>
-::~ImageWrapper()
+::~ImageWrapper()
{
Reset();
+ delete m_IOHints;
}
template<class TTraits, class TBase>
-void
+void
ImageWrapper<TTraits,TBase>
::CommonInitialization()
{
@@ -351,10 +548,13 @@ ImageWrapper<TTraits,TBase>
// Set the unique wrapper id
m_UniqueId = ++GlobalImageWrapperIndex;
- // Set initial state
+ // Set initial state
m_Initialized = false;
m_PipelineReady = false;
+ // Create empty IO hints
+ m_IOHints = new Registry();
+
// Create slicer objects
m_Slicer[0] = SlicerType::New();
m_Slicer[1] = SlicerType::New();
@@ -362,8 +562,7 @@ ImageWrapper<TTraits,TBase>
// Initialize the display mapping
m_DisplayMapping = DisplayMapping::New();
- m_DisplayMapping->Initialize(
- static_cast<typename TTraits::WrapperType *>(this));
+ m_DisplayMapping->Initialize(static_cast<typename DisplayMapping::WrapperType *>(this));
// Set sticky flag
m_Sticky = TTraits::StickyByDefault;
@@ -385,23 +584,20 @@ ImageWrapper<TTraits,TBase>
// If the source contains an image, make a copy of that image
if (copy.IsInitialized() && copy.GetImage())
{
- // Create and allocate the image
- ImagePointer newImage = ImageType::New();
- newImage->SetRegions(copy.GetImage()->GetBufferedRegion());
- newImage->Allocate();
-
- // Copy the image contents
- InternalPixelType *ptrTarget = newImage->GetBufferPointer();
- InternalPixelType *ptrSource = copy.GetImage()->GetBufferPointer();
- memcpy(ptrTarget,ptrSource,
- sizeof(PixelType) * newImage->GetPixelContainer()->Size());
-
+ typedef itk::RegionOfInterestImageFilter<ImageType, ImageType> roiType;
+ typename roiType::Pointer roi = roiType::New();
+ roi->SetInput(copy.GetImage());
+ roi->Update();
+ ImagePointer newImage = roi->GetOutput();
UpdateImagePointer(newImage);
}
+
+ // Copy IO hints
+ *m_IOHints = copy.GetIOHints();
}
template<class TTraits, class TBase>
-const ImageCoordinateTransform &
+const ImageCoordinateTransform *
ImageWrapper<TTraits,TBase>
::GetImageToDisplayTransform(unsigned int iSlice) const
{
@@ -411,7 +607,7 @@ ImageWrapper<TTraits,TBase>
template<class TTraits, class TBase>
void
ImageWrapper<TTraits,TBase>
-::SetDisplayGeometry(IRISDisplayGeometry &dispGeom)
+::SetDisplayGeometry(const IRISDisplayGeometry &dispGeom)
{
// Set the display geometry
m_DisplayGeometry = dispGeom;
@@ -511,25 +707,68 @@ ImageWrapper<TTraits,TBase>
template<class TTraits, class TBase>
Vector3d
ImageWrapper<TTraits,TBase>
-::TransformVoxelIndexToPosition(const Vector3ui &iVoxel) const
+::TransformVoxelCIndexToPosition(const Vector3d &iVoxel) const
{
// Use the ITK method to do this
- typename ImageBaseType::IndexType xIndex;
+ itk::ContinuousIndex<double, 3> xIndex;
for(size_t d = 0; d < 3; d++) xIndex[d] = iVoxel[d];
itk::Point<double, 3> xPoint;
+ m_ReferenceSpace->TransformContinuousIndexToPhysicalPoint(xIndex, xPoint);
+
+ return Vector3d(xPoint);
+}
+
+template<class TTraits, class TBase>
+Vector3d
+ImageWrapper<TTraits,TBase>
+::TransformVoxelIndexToPosition(const Vector3i &iVoxel) const
+{
+ // Use the ITK method to do this
+ typename ImageBaseType::IndexType xIndex = to_itkIndex(iVoxel);
+
+ itk::Point<double, 3> xPoint;
m_ReferenceSpace->TransformIndexToPhysicalPoint(xIndex, xPoint);
- Vector3d xOut;
- for(unsigned int q = 0; q < 3; q++) xOut[q] = xPoint[q];
+ return Vector3d(xPoint);
+}
+
+template<class TTraits, class TBase>
+Vector3d
+ImageWrapper<TTraits,TBase>
+::TransformPositionToVoxelCIndex(const Vector3d &vLPS) const
+{
+ itk::Point<double, 3> xPoint;
+ for(size_t d = 0; d < 3; d++) xPoint[d] = vLPS[d];
+
+ // Use the ITK method to do this
+ itk::ContinuousIndex<double, 3> xIndex;
+
+ m_ReferenceSpace->TransformPhysicalPointToContinuousIndex(xPoint, xIndex);
- return xOut;
+ return Vector3d(xIndex);
+}
+
+template<class TTraits, class TBase>
+Vector3i
+ImageWrapper<TTraits,TBase>
+::TransformPositionToVoxelIndex(const Vector3d &vLPS) const
+{
+ itk::Point<double, 3> xPoint;
+ for(size_t d = 0; d < 3; d++) xPoint[d] = vLPS[d];
+
+ // Use the ITK method to do this
+ typename ImageBaseType::IndexType xIndex;
+
+ m_ReferenceSpace->TransformPhysicalPointToIndex(xPoint, xIndex);
+
+ return Vector3i(xIndex);
}
template<class TTraits, class TBase>
Vector3d
ImageWrapper<TTraits,TBase>
-::TransformVoxelIndexToNIFTICoordinates(const Vector3d &iVoxel) const
+::TransformVoxelCIndexToNIFTICoordinates(const Vector3d &iVoxel) const
{
// Create homogeneous vector
vnl_vector_fixed<double, 4> x;
@@ -547,7 +786,7 @@ ImageWrapper<TTraits,TBase>
template<class TTraits, class TBase>
Vector3d
ImageWrapper<TTraits,TBase>
-::TransformNIFTICoordinatesToVoxelIndex(const Vector3d &vNifti) const
+::TransformNIFTICoordinatesToVoxelCIndex(const Vector3d &vNifti) const
{
// Create homogeneous vector
vnl_vector_fixed<double, 4> x;
@@ -564,9 +803,9 @@ ImageWrapper<TTraits,TBase>
template<class TTraits, class TBase>
-void
+void
ImageWrapper<TTraits,TBase>
-::PrintDebugInformation()
+::PrintDebugInformation()
{
std::cout << "=== Image Properties ===" << std::endl;
std::cout << " Dimensions : " << m_Image->GetLargestPossibleRegion().GetSize() << std::endl;
@@ -575,34 +814,118 @@ ImageWrapper<TTraits,TBase>
std::cout << "------------------------" << std::endl;
}
+
template<class TTraits, class TBase>
-void
+bool
ImageWrapper<TTraits,TBase>
-::UpdateImagePointer(ImageType *newImage, ImageBaseType *referenceSpace, ITKTransformType *transform)
+::CompareGeometry(
+ ImageBaseType *image1,
+ ImageBaseType *image2,
+ double tol)
{
- // If there is no reference space, we assume that the reference space is the same as the image
- referenceSpace = referenceSpace ? referenceSpace : newImage;
+ // If one of the images is NULL return false
+ if(!image1 || !image2)
+ return false;
- // Check if the image size or image direction matrix has changed
- bool hasSizeChanged = true, hasDirectionChanged = true;
- if(m_ReferenceSpace && m_ReferenceSpace != referenceSpace)
+ // Check if the images have same dimensions
+ bool same_size = (image1->GetBufferedRegion() == image2->GetBufferedRegion());
+
+ // Now test the 3D geometry of the image to see if it occupies the same space
+ bool same_space = true;
+
+ for(int i = 0; i < 3; i++)
+ {
+ if(fabs(image1->GetOrigin()[i] - image2->GetOrigin()[i]) > tol)
+ same_space = false;
+ if(fabs(image1->GetSpacing()[i] - image2->GetSpacing()[i]) > tol)
+ same_space = false;
+ for(int j = 0; j < 3; j++)
+ {
+ if(fabs(image1->GetDirection()[i][j] - image2->GetDirection()[i][j]) > tol)
+ same_space = false;
+ }
+ }
+
+ return same_size && same_space;
+}
+
+
+template<class TTraits, class TBase>
+bool
+ImageWrapper<TTraits,TBase>
+::CanOrthogonalSlicingBeUsed(
+ ImageType *image, ImageBaseType *referenceSpace, ITKTransformType *transform)
+{
+ // For orthogonal slicing to be usable, two conditions must be met
+ // 1. The reference space and the new image must have the same geometry
+ // 2. The transform must be identity
+
+ // Check if the images have same dimensions
+ double tol = 1e-5;
+ bool same_geom = CompareGeometry(image, referenceSpace, tol);
+
+ // Get the transform matrix and offset
+ // TODO: this is silly and unnecessary. We should always use one Transform class
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> TransformBase;
+ TransformBase *tb = dynamic_cast<TransformBase *>(transform);
+ typename TransformBase::MatrixType matrix;
+ typename TransformBase::OffsetType offset;
+ matrix.SetIdentity();
+ offset.Fill(0.0);
+ if(tb)
{
- hasSizeChanged = referenceSpace->GetLargestPossibleRegion().GetSize()
- != m_ReferenceSpace->GetLargestPossibleRegion().GetSize();
+ matrix = tb->GetMatrix();
+ offset = tb->GetOffset();
+ }
- hasDirectionChanged = referenceSpace->GetDirection()
- != m_ReferenceSpace->GetDirection();
+ // Check if transform is identity.
+ bool is_identity = true;
+
+ for(int i = 0; i < 3; i++)
+ {
+ if(fabs(offset[i]) > tol)
+ is_identity = false;
+ for(int j = 0; j < 3; j++)
+ {
+ if(fabs(matrix(i,j) - (i==j ? 1.0 : 0.0)) > tol)
+ is_identity = false;
+ }
+ }
+
+ return same_geom && is_identity;
+}
+
+template<class TTraits, class TBase>
+void
+ImageWrapper<TTraits,TBase>
+::UpdateSlicingPipelines(ImageType *image, ImageBaseType *referenceSpace, ITKTransformType *transform)
+{
+ /*
+ // Can we use orthogonal spacing
+ bool ortho = CanOrthogonalSlicingBeUsed(image, referenceSpace, transform);
+
+ // Update each of the slicers
+ for(int i = 0; i < 3; i++)
+ {
+ m_Slicer[i]->SetInput(image);
+ m_Slicer[i]->SetPreviewImage(NULL);
+ m_Slicer[i]->SetTransform(transform);
}
// Set the input of the slicers, depending on whether the image is subject to transformation
- if(transform == NULL)
+ if(ortho)
{
// Slicers take their input directly from the new image
for(int i = 0; i < 3; i++)
{
- m_Slicer[i]->SetInput(newImage);
+ // Set up the basic slicing pipeline
+ m_Slicer[i]->SetInput(image);
m_Slicer[i]->SetPreviewInput(NULL);
m_Slicer[i]->SetBypassMainInput(false);
+
+ // Drop the advanced slicing pipeline
+ m_AdvancedSlicer[i] = NULL;
+ m_ResampleFilter[i+3] = NULL;
}
}
else
@@ -619,21 +942,62 @@ ImageWrapper<TTraits,TBase>
// Set the input to the dummy image
m_Slicer[i]->SetInput(dummy);
- // Create an itk reslicing filter
- m_ResampleFilter[i] = ResampleFilter::New();
- m_ResampleFilter[i]->SetInput(newImage);
- m_ResampleFilter[i]->SetTransform(transform);
- m_ResampleFilter[i]->SetOutputParametersFromImage(referenceSpace);
- m_Slicer[i]->SetPreviewInput(m_ResampleFilter[i]->GetOutput());
+ // Create an advanced slicer
+ m_AdvancedSlicer[i] = NonOrthogonalSlicerType::New();
+ m_AdvancedSlicer[i]->SetInput(image);
+ m_AdvancedSlicer[i]->SetTransform(transform);
+ m_AdvancedSlicer[i]->SetReferenceImage(m_DisplayViewportGeometryReference[i]);
+
+ // Create another set that work with the older slicers - this is temporary
+ // TODO: get rid of this
+ m_ResampleFilter[i+3] = ResampleFilter::New();
+ m_ResampleFilter[i+3]->SetInput(image);
+ m_ResampleFilter[i+3]->SetTransform(transform);
+ m_ResampleFilter[i+3]->SetOutputParametersFromImage(referenceSpace);
+ m_Slicer[i]->SetPreviewInput(m_ResampleFilter[i+3]->GetOutput());
m_Slicer[i]->SetBypassMainInput(true);
}
}
+ */
+}
+
+template<class TTraits, class TBase>
+void
+ImageWrapper<TTraits,TBase>
+::UpdateImagePointer(ImageType *newImage, ImageBaseType *referenceSpace, ITKTransformType *transform)
+{
+ // If there is no reference space, we assume that the reference space is the same as the image
+ referenceSpace = referenceSpace ? referenceSpace : newImage;
+
+ // Check if the image size or image direction matrix has changed
+ bool isReferenceGeometrySame = CompareGeometry(m_ReferenceSpace, referenceSpace);
// Update the image
this->m_ReferenceSpace = referenceSpace;
this->m_ImageBase = newImage;
this->m_Image = newImage;
+ // Create the transform if it does not exist
+ typename ITKTransformType::Pointer tran = transform;
+ if(tran.IsNull())
+ {
+ typedef itk::IdentityTransform<double, 3> IdTransformType;
+ typename IdTransformType::Pointer idTran = IdTransformType::New();
+ tran = idTran.GetPointer();
+ }
+
+ // Which slicer should be used?
+ bool ortho = CanOrthogonalSlicingBeUsed(newImage, referenceSpace, tran);
+
+ // Update the slicers
+ for(int i = 0; i < 3; i++)
+ {
+ m_Slicer[i]->SetInput(newImage);
+ m_Slicer[i]->SetObliqueTransform(tran);
+ m_Slicer[i]->SetPreviewImage(NULL);
+ m_Slicer[i]->SetUseOrthogonalSlicing(ortho);
+ }
+
// Mark the image as Modified to enforce correct sequence of
// operations with MinMaxCalc
m_Image->Modified();
@@ -642,7 +1006,7 @@ ImageWrapper<TTraits,TBase>
m_DisplayMapping->UpdateImagePointer(m_Image);
// Update the image coordinate geometry
- if(hasSizeChanged || hasDirectionChanged)
+ if(!isReferenceGeometrySame)
{
// Reset the transform to identity
this->UpdateImageGeometry();
@@ -662,7 +1026,7 @@ ImageWrapper<TTraits,TBase>
}
template<class TTraits, class TBase>
-void
+void
ImageWrapper<TTraits,TBase>
::InitializeToWrapper(const ImageWrapperBase *source,
ImageType *image, ImageBaseType *refSpace, ITKTransformType *tran)
@@ -677,10 +1041,16 @@ ImageWrapper<TTraits,TBase>
SetSliceIndex(source->GetSliceIndex());
}
-
+template<class TTraits, class TBase>
+bool
+ImageWrapper<TTraits,TBase>
+::IsSlicingOrthogonal() const
+{
+ return m_Slicer[0]->GetUseOrthogonalSlicing();
+}
template<class TTraits, class TBase>
-void
+void
ImageWrapper<TTraits,TBase>
::InitializeToWrapper(const ImageWrapperBase *source, const PixelType &value)
{
@@ -706,9 +1076,9 @@ ImageWrapper<TTraits,TBase>
}
template<class TTraits, class TBase>
-void
+void
ImageWrapper<TTraits,TBase>
-::SetImage(ImagePointer newImage)
+::SetImage(ImagePointer newImage)
{
UpdateImagePointer(newImage);
}
@@ -727,20 +1097,48 @@ void
ImageWrapper<TTraits,TBase>
::SetITKTransform(ImageBaseType *refSpace, ITKTransformType *transform)
{
- // TODO: this is a hack, to get around the display slices not updating...
- Vector3ui index = this->GetSliceIndex();
- UpdateImagePointer(m_Image, refSpace, transform);
- this->SetSliceIndex(Vector3ui(0u));
- this->SetSliceIndex(index);
- this->InvokeEvent(WrapperDisplayMappingChangeEvent());
+
+ // Check if the reference space has changed
+ if(m_ReferenceSpace != refSpace)
+ {
+ // Force a reinitialization of this layer
+ this->UpdateImagePointer(m_Image, refSpace, transform);
+ }
+ else
+ {
+ bool ortho = CanOrthogonalSlicingBeUsed(m_Image, refSpace, transform);
+ for(int i = 0; i < 3; i++)
+ {
+ m_Slicer[i]->SetObliqueTransform(transform);
+ m_Slicer[i]->SetUseOrthogonalSlicing(ortho);
+ this->InvokeEvent(WrapperDisplayMappingChangeEvent());
+
+ // m_ResampleFilter[i+3]->SetTransform(transform);
+ }
+ }
}
+template<class TTraits, class TBase>
+const typename ImageWrapper<TTraits,TBase>::ITKTransformType *
+ImageWrapper<TTraits,TBase>
+::GetITKTransform() const
+{
+ return m_Slicer[0]->GetObliqueTransform();
+}
template<class TTraits, class TBase>
+typename ImageWrapper<TTraits,TBase>::ImageBaseType *
+ImageWrapper<TTraits,TBase>
+::GetReferenceSpace() const
+{
+ return m_ReferenceSpace;
+}
+
+template<class TTraits, class TBase>
void
ImageWrapper<TTraits,TBase>
-::Reset()
+::Reset()
{
if (m_Initialized)
{
@@ -756,7 +1154,7 @@ ImageWrapper<TTraits,TBase>
template<class TTraits, class TBase>
inline typename ImageWrapper<TTraits,TBase>::PixelType
ImageWrapper<TTraits,TBase>
-::GetVoxel(const Vector3ui &index) const
+::GetVoxel(const Vector3ui &index) const
{
return GetVoxel(index[0],index[1],index[2]);
}
@@ -809,7 +1207,7 @@ ImageWrapper<TTraits,TBase>
template<class TTraits, class TBase>
typename ImageWrapper<TTraits,TBase>::ConstIterator
ImageWrapper<TTraits,TBase>
-::GetImageConstIterator() const
+::GetImageConstIterator() const
{
ConstIterator it(m_Image,m_Image->GetLargestPossibleRegion());
it.GoToBegin();
@@ -819,7 +1217,7 @@ ImageWrapper<TTraits,TBase>
template<class TTraits, class TBase>
typename ImageWrapper<TTraits,TBase>::Iterator
ImageWrapper<TTraits,TBase>
-::GetImageIterator()
+::GetImageIterator()
{
Iterator it(m_Image,m_Image->GetLargestPossibleRegion());
it.GoToBegin();
@@ -827,7 +1225,7 @@ ImageWrapper<TTraits,TBase>
}
template<class TTraits, class TBase>
-void
+void
ImageWrapper<TTraits,TBase>
::SetSliceIndex(const Vector3ui &cursor)
{
@@ -837,14 +1235,28 @@ ImageWrapper<TTraits,TBase>
// Select the appropriate slice for each slicer
for(unsigned int i=0;i<3;i++)
{
- // Which axis does this slicer slice?
- unsigned int axis = m_Slicer[i]->GetSliceDirectionImageAxis();
-
// Set the slice using that axis
- m_Slicer[i]->SetSliceIndex(cursor[axis]);
+ m_Slicer[i]->SetSliceIndex(to_itkIndex(cursor));
}
}
+template<class TTraits, class TBase>
+void
+ImageWrapper<TTraits,TBase>
+::SetDisplayViewportGeometry(unsigned int index,
+ const ImageBaseType *viewport_image)
+{
+ m_Slicer[index]->SetObliqueReferenceImage(viewport_image);
+}
+
+template<class TTraits, class TBase>
+const typename ImageWrapper<TTraits,TBase>::ImageBaseType*
+ImageWrapper<TTraits,TBase>
+::GetDisplayViewportGeometry(unsigned int index) const
+{
+ return m_Slicer[index]->GetObliqueReferenceImage();
+}
+
template<class TTraits, class TBase>
void
@@ -871,26 +1283,11 @@ ImageWrapper<TTraits,TBase>
// Update the geometry for each slice
for(unsigned int iSlice = 0;iSlice < 3;iSlice ++)
{
- // Get the transform and its inverse
- ImageCoordinateTransform tran = m_ImageGeometry.GetImageToDisplayTransform(iSlice);
- ImageCoordinateTransform tinv = tran.Inverse();
-
- // Tell slicer in which directions to slice
- m_Slicer[iSlice]->SetSliceDirectionImageAxis(
- tinv.GetCoordinateIndexZeroBased(2));
-
- m_Slicer[iSlice]->SetLineDirectionImageAxis(
- tinv.GetCoordinateIndexZeroBased(1));
-
- m_Slicer[iSlice]->SetPixelDirectionImageAxis(
- tinv.GetCoordinateIndexZeroBased(0));
-
- m_Slicer[iSlice]->SetPixelTraverseForward(
- tinv.GetCoordinateOrientation(0) > 0);
-
- m_Slicer[iSlice]->SetLineTraverseForward(
- tinv.GetCoordinateOrientation(1) > 0);
+ // Assign the new geometry to the slicer
+ m_Slicer[iSlice]->SetOrthogonalTransform(
+ m_ImageGeometry.GetImageToDisplayTransform(iSlice));
+ // TODO: is this necessary and the right place to do ut?
// Invalidate the requested region in the display slice. This will
// cause the RR to reset to largest possible region on next Update
typename DisplaySliceType::RegionType invalidRegion;
@@ -911,6 +1308,9 @@ ImageWrapper<TTraits,TBase>
// Set the geometry to default values
m_ImageGeometry.SetGeometry(dirmat.GetVnlMatrix(), m_DisplayGeometry, size);
+
+ // TODO: why are we not updating the slicers?
+ // TODO: does this code even get run?
}
}
@@ -972,11 +1372,15 @@ ImageWrapper<TTraits,TBase>
/** For each slicer, find out which image dimension does is slice along */
template<class TTraits, class TBase>
-unsigned int
+unsigned int
ImageWrapper<TTraits,TBase>
::GetDisplaySliceImageAxis(unsigned int iSlice)
{
- return m_Slicer[iSlice]->GetSliceDirectionImageAxis();
+ // TODO: this is wasteful computing inverse for something that should be cached
+ const ImageCoordinateTransform *tran = m_Slicer[iSlice]->GetOrthogonalTransform();
+ ImageCoordinateTransform::Pointer traninv = ImageCoordinateTransform::New();
+ tran->ComputeInverse(traninv);
+ return traninv->GetCoordinateIndexZeroBased(2);
}
template<class TTraits, class TBase>
@@ -987,18 +1391,18 @@ ImageWrapper<TTraits,TBase>
return m_Slicer[dimension]->GetOutput();
}
-template<class TTraits, class TBase>
-typename ImageWrapper<TTraits,TBase>::InternalPixelType *
-ImageWrapper<TTraits,TBase>
-::GetVoxelPointer() const
-{
- return m_Image->GetBufferPointer();
-}
+// template<class TTraits, class TBase>
+// typename ImageWrapper<TTraits,TBase>::InternalPixelType *
+// ImageWrapper<TTraits,TBase>
+// ::GetVoxelPointer() const
+// {
+ // return m_Image->GetBufferPointer();
+// }
// TODO: this should take advantage of an in-place filter!
template<class TTraits, class TBase>
-unsigned int
+unsigned int
ImageWrapper<TTraits,TBase>
::ReplaceIntensity(PixelType iOld, PixelType iNew)
{
@@ -1022,7 +1426,7 @@ ImageWrapper<TTraits,TBase>
}
template<class TTraits, class TBase>
-unsigned int
+unsigned int
ImageWrapper<TTraits,TBase>
::SwapIntensities(PixelType iFirst, PixelType iSecond)
{
@@ -1061,14 +1465,6 @@ ImageWrapper<TTraits,TBase>::GetDisplaySlice(unsigned int dim)
}
template<class TTraits, class TBase>
-void *
-ImageWrapper<TTraits,TBase>
-::GetVoxelVoidPointer() const
-{
- return (void *) m_Image->GetBufferPointer();
-}
-
-template<class TTraits, class TBase>
void
ImageWrapper<TTraits, TBase>
::SetFileName(const std::string &name)
@@ -1107,6 +1503,21 @@ ImageWrapper<TTraits, TBase>
this->InvokeEvent(WrapperMetadataChangeEvent());
}
+template<class TTraits, class TBase>
+const Registry &
+ImageWrapper<TTraits, TBase>
+::GetIOHints() const
+{
+ return *this->m_IOHints;
+}
+
+template<class TTraits, class TBase>
+void
+ImageWrapper<TTraits, TBase>
+::SetIOHints(const Registry &io_hints)
+{
+ *this->m_IOHints = io_hints;
+}
// The method that can be called for some wrappers, not others
template <class TImage>
@@ -1187,7 +1598,7 @@ ImageWrapper<TTraits,TBase>
for(int i = 0; i < 3; i++)
{
// Update the preview inputs to the slicers
- m_Slicer[i]->SetPreviewInput(filter[i]->GetOutput());
+ m_Slicer[i]->SetPreviewImage(filter[i]->GetOutput());
// Mark the preview filters as modified to ensure that the slicer
// is going to use it. TODO: is this really needed?
@@ -1205,7 +1616,7 @@ ImageWrapper<TTraits,TBase>
{
for(int i = 0; i < 3; i++)
{
- m_Slicer[i]->SetPreviewInput(NULL);
+ m_Slicer[i]->SetPreviewImage(NULL);
}
}
@@ -1214,7 +1625,7 @@ bool
ImageWrapper<TTraits,TBase>
::IsPreviewPipelineAttached() const
{
- return m_Slicer[0]->GetPreviewInput() != NULL;
+ return m_Slicer[0]->GetPreviewImage() != NULL;
}
@@ -1237,7 +1648,8 @@ ImageWrapper<TTraits,TBase>
// Get the display slice
// For now, just use the z-axis for exporting the thumbnails
DisplaySliceType *slice = this->GetDisplaySlice(2);
- slice->Update();
+ slice->GetSource()->UpdateLargestPossibleRegion();
+ // slice->Update();
// The size of the slice
Vector2ui slice_dim = slice->GetBufferedRegion().GetSize();
@@ -1370,7 +1782,8 @@ ImageWrapper<TTraits,TBase>
SmartPtr<WrapperType> newWrapper = WrapperType::New();
// Copy the display to anatomy geometry to the new wrapper
- newWrapper->m_DisplayGeometry = m_DisplayGeometry;
+ IRISDisplayGeometry temp = m_DisplayGeometry;
+ newWrapper->SetDisplayGeometry(temp);
// Assign the new image to the new wrapper
newWrapper->SetImage(newImage);
@@ -1396,11 +1809,17 @@ ImageWrapper<TTraits,TBase>
::DeepCopyRegion(const SNAPSegmentationROISettings &roi,
itk::Command *progressCommand) const
{
+ // If the image in this wrapper is not the same as the reference space,
+ // we must force resampling to occur
+ bool force_resampling = !this->IsSlicingOrthogonal();
// We use partial template specialization here because region copy is
// only supported for images that are concrete (Image, VectorImage)
typedef ImageWrapperPartialSpecializationTraits<ImageType> Specialization;
- return Specialization::CopyRegion(m_Image, roi, progressCommand);
+ return Specialization::CopyRegion(
+ m_Image, m_ReferenceSpace,
+ this->GetITKTransform(), roi,
+ force_resampling, progressCommand);
}
diff --git a/Logic/ImageWrapper/ImageWrapper.h b/Logic/ImageWrapper/ImageWrapper.h
index 94e6286..d766fec 100644
--- a/Logic/ImageWrapper/ImageWrapper.h
+++ b/Logic/ImageWrapper/ImageWrapper.h
@@ -36,19 +36,23 @@
#define __ImageWrapper_h_
// Smart pointers have to be included from ITK, can't forward reference them
+#include "RLEImageScanlineIterator.h"
#include "ImageWrapperBase.h"
#include "ImageCoordinateGeometry.h"
-#include <itkImageRegionIterator.h>
#include <itkVectorImage.h>
#include <itkRGBAPixel.h>
#include <DisplayMappingPolicy.h>
#include <itkSimpleDataObjectDecorator.h>
// Forward declarations to IRIS classes
-template <class TInputImage, class TOutputImage> class IRISSlicer;
template <class TFunctor> class UnaryValueToValueFilter;
+template <class TInputImage, class TOutputImage, class TTraits>
+class AdaptiveSlicingPipeline;
+
+
class SNAPSegmentationROISettings;
+
namespace itk {
template <unsigned int VDimension> class ImageBase;
template <class TImage> class ImageSource;
@@ -57,6 +61,8 @@ namespace itk {
typename TOutputImage,
typename TInterpolatorPrecisionType,
typename TTransformPrecisionType> class ResampleImageFilter;
+ template<typename TInputImage,
+ typename TOutputImage> class ExtractImageFilter;
}
#include <itkImageSource.h>
@@ -100,8 +106,13 @@ public:
// adaptor, the component type is the output type of the adaptor
typedef typename TTraits::ComponentType ComponentType;
+ // This is the type of the intermediate 'preview' images used in the slicing
+ // pipeline. For regular image and vector image wrappers, this is the same as
+ // ImageType, but for adapter-based image wrappers, this is a concrete image
+ typedef itk::Image<PixelType, 3> PreviewImageType;
+
// Slice image type
- typedef itk::Image<PixelType,2> SliceType;
+ typedef typename TTraits::SliceType SliceType;
typedef SmartPtr<SliceType> SlicePointer;
// Display slice type - inherited
@@ -110,11 +121,11 @@ public:
typedef typename Superclass::DisplaySlicePointer DisplaySlicePointer;
// Slicer type
- typedef IRISSlicer<ImageType, SliceType> SlicerType;
+ typedef AdaptiveSlicingPipeline<ImageType, SliceType, PreviewImageType> SlicerType;
typedef SmartPtr<SlicerType> SlicerPointer;
// Preview source for preview pipelines
- typedef itk::ImageSource<ImageType> PreviewFilterType;
+ typedef itk::ImageSource<PreviewImageType> PreviewFilterType;
// Iterator types
typedef typename itk::ImageRegionIterator<ImageType> Iterator;
@@ -170,19 +181,24 @@ public:
irisGetMacro(UniqueId, unsigned long)
/**
+ Does this wrapper use the non-orthogonal slicing pipeline?
+ */
+ virtual bool IsSlicingOrthogonal() const;
+
+ /**
* Clear the data associated with storing an image
*/
virtual void Reset();
/** Get the coordinate transform for each display slice */
- virtual const ImageCoordinateTransform &GetImageToDisplayTransform(
+ virtual const ImageCoordinateTransform *GetImageToDisplayTransform(
unsigned int) const;
/**
* Set the coordinate transformation between the display coordinates and
* the anatomical coordinates. This affects the behavior of the slicers
*/
- virtual void SetDisplayGeometry(IRISDisplayGeometry &dispGeom);
+ virtual void SetDisplayGeometry(const IRISDisplayGeometry &dispGeom);
/** Get the display to anatomy coordinate mapping */
irisGetMacro(DisplayGeometry, const IRISDisplayGeometry &)
@@ -202,7 +218,7 @@ public:
irisGetMacro(ImageGeometry, const ImageCoordinateGeometry &)
- /** Get the current slice index */
+ /** Get the current slice index - which really means cursor position */
irisGetMacro(SliceIndex, Vector3ui)
/** Return some image info independently of pixel type */
@@ -244,13 +260,22 @@ public:
virtual itk::ImageRegion<3> GetBufferedRegion() const;
/** Transform a voxel index into a spatial position */
- virtual Vector3d TransformVoxelIndexToPosition(const Vector3ui &iVoxel) const;
+ virtual Vector3d TransformVoxelIndexToPosition(const Vector3i &iVoxel) const ;
+
+ /** Transform a voxel index into a spatial position */
+ virtual Vector3d TransformVoxelCIndexToPosition(const Vector3d &iVoxel) const;
+
+ /** Transform spatial position to voxel continuous index (LPS) */
+ virtual Vector3d TransformPositionToVoxelCIndex(const Vector3d &vLPS) const;
+
+ /** Transform spatial position to voxel index (LPS) */
+ virtual Vector3i TransformPositionToVoxelIndex(const Vector3d &vLPS) const;
/** Transform a voxel index into NIFTI coordinates (RAS) */
- virtual Vector3d TransformVoxelIndexToNIFTICoordinates(const Vector3d &iVoxel) const;
+ virtual Vector3d TransformVoxelCIndexToNIFTICoordinates(const Vector3d &iVoxel) const;
/** Transform NIFTI coordinates to a continuous voxel index */
- virtual Vector3d TransformNIFTICoordinatesToVoxelIndex(const Vector3d &vNifti) const;
+ virtual Vector3d TransformNIFTICoordinatesToVoxelCIndex(const Vector3d &vNifti) const;
/** Get the NIFTI s-form matrix for this image */
irisGetMacro(NiftiSform, TransformType)
@@ -289,7 +314,11 @@ public:
DisplayMapping *GetDisplayMapping()
{ return m_DisplayMapping; }
- /**
+ /** Get the intensity to display mapping */
+ const DisplayMapping *GetDisplayMapping() const
+ { return m_DisplayMapping; }
+
+ /**
* Return the pointed to the ITK image encapsulated by this wrapper.
*/
virtual ImageType *GetImage() const
@@ -308,6 +337,12 @@ public:
*/
virtual void SetSliceIndex(const Vector3ui &cursor);
+ const ImageBaseType* GetDisplayViewportGeometry(unsigned int index) const;
+
+ virtual void SetDisplayViewportGeometry(
+ unsigned int index,
+ const ImageBaseType *viewport_image);
+
/**
* Get an ITK pipeline object holding the minimum value in the image. For
* multi-component images, this is the minimum value over all components.
@@ -335,7 +370,7 @@ public:
/**
* This method exposes the scalar pointer in the image
*/
- virtual InternalPixelType *GetVoxelPointer() const;
+ //virtual InternalPixelType *GetVoxelPointer() const;
/** Number of voxels */
virtual size_t GetNumberOfVoxels() const;
@@ -364,6 +399,16 @@ public:
virtual void SetITKTransform(ImageBaseType *referenceSpace, ITKTransformType *transform);
/**
+ * Get the ITK transform between this layer and its reference space
+ */
+ virtual const ITKTransformType *GetITKTransform() const;
+
+ /**
+ * Get the reference space space in which this image is defined
+ */
+ virtual ImageBaseType* GetReferenceSpace() const;
+
+ /**
* Extract a region of interest from the image wrapper, as a new wrapper of
* the same type
*/
@@ -438,9 +483,6 @@ public:
irisIsMacro(PipelineReady)
irisSetMacro(PipelineReady, bool)
-
- virtual void* GetVoxelVoidPointer() const;
-
/**
* Set the filename of the image wrapper. If the wrapper does not have a
* nickname, the nickname will be changed to the file part of the filename.
@@ -470,6 +512,18 @@ public:
virtual void SetCustomNickname(const std::string &nickname);
irisGetMacro(CustomNickname, const std::string &);
+ /**
+ * Access the "IO hints" registry associated with this wrapper. The IO hints
+ * are used to help read the image when the filename alone is not sufficient.
+ * For example, it may contain the DICOM series ID of the image, or for a raw
+ * image the dimensions.
+ */
+ virtual const Registry &GetIOHints() const;
+
+ /**
+ * Set the IO hints
+ */
+ virtual void SetIOHints(const Registry &io_hints);
/**
* Write the image to disk with the help of the GuidedNativeImageIO object
@@ -609,6 +663,8 @@ protected:
typedef std::map<std::string, SmartPtr<itk::Object> > UserDataMapType;
UserDataMapType m_UserDataMap;
+ // IO Hints registry
+ Registry *m_IOHints;
/**
* Handle a change in the image pointer (i.e., a load operation on the image or
@@ -640,9 +696,25 @@ protected:
/** Parent wrapper */
ImageWrapperBase *m_ParentWrapper;
+ /**
+ * Resampling filter data type. These filters are used when slicing is required in
+ * non-orthogonal directions. There are four of these filters, and they are used to
+ * produce three display slices and also a complete image that matches the dimensions
+ * of the main image (this is for feature extraction, etc.)
+ */
+ typedef itk::ResampleImageFilter<ImageType, PreviewImageType, double, double> ResampleFilter;
+ SmartPtr<ResampleFilter> m_ResampleFilter[6];
+
+ // Compare the geometry (size and header) of two images. Returns true if the headers are
+ // within tolerance of each other.
+ static bool CompareGeometry(ImageBaseType *image1, ImageBaseType *image2, double tol = 0.0);
+
+ // Check if the orthogonal slicer can be used for the given image, ref space and transform
+ static bool CanOrthogonalSlicingBeUsed(
+ ImageType *image, ImageBaseType *referenceSpace, ITKTransformType *transform);
- typedef itk::ResampleImageFilter<ImageType, ImageType, double, double> ResampleFilter;
- SmartPtr<ResampleFilter> m_ResampleFilter[3];
+ // Update the orthogonal and/or non-orthogonal slicing pipelines
+ void UpdateSlicingPipelines(ImageType *image, ImageBaseType *referenceSpace, ITKTransformType *transform);
};
#endif // __ImageWrapper_h_
diff --git a/Logic/ImageWrapper/ImageWrapperBase.h b/Logic/ImageWrapper/ImageWrapperBase.h
index b045ded..b88757c 100644
--- a/Logic/ImageWrapper/ImageWrapperBase.h
+++ b/Logic/ImageWrapper/ImageWrapperBase.h
@@ -10,6 +10,7 @@
namespace itk {
template <unsigned int VDim> class ImageBase;
template <class TPixel, unsigned int VDim> class Image;
+ template <class TPixel, unsigned int VDim> class VectorImage;
template <class TPixel> class RGBAPixel;
template <class TOutputImage> class ImageSource;
template <class TScalar, unsigned int V1, unsigned int V2> class Transform;
@@ -118,14 +119,14 @@ public:
virtual void SetParentWrapper(ImageWrapperBase *parent) = 0;
/** Get the coordinate transform for each display slice */
- virtual const ImageCoordinateTransform &GetImageToDisplayTransform(
+ virtual const ImageCoordinateTransform *GetImageToDisplayTransform(
unsigned int) const = 0;
/**
* Set the coordinate transformation between the display coordinates and
* the anatomical coordinates. This affects the behavior of the slicers
*/
- virtual void SetDisplayGeometry(IRISDisplayGeometry &dispGeom) = 0;
+ virtual void SetDisplayGeometry(const IRISDisplayGeometry &dispGeom) = 0;
/** Get the display to anatomy coordinate mapping */
virtual const IRISDisplayGeometry &GetDisplayGeometry() const = 0;
@@ -155,6 +156,15 @@ public:
*/
virtual void SetSliceIndex(const Vector3ui &) = 0;
+ /**
+ * Set the viewport rectangle onto which the three display slices
+ * will be rendered
+ */
+ virtual void SetDisplayViewportGeometry(
+ unsigned int index,
+ const ImageBaseType *viewport_image) = 0;
+
+
/** Return some image info independently of pixel type */
irisVirtualGetMacro(ImageBase, ImageBaseType *)
@@ -200,6 +210,13 @@ public:
irisVirtualIsMacro(Drawable)
/**
+ * Whether the layer is initialized to use orthogonal slicing or non-orthogonal
+ * slicing. There are two slicing pipelines, one for the images whose slicing
+ * directions are parallel to the display planes, and one for the opposite case.
+ */
+ irisVirtualIsMacro(SlicingOrthogonal)
+
+ /**
* Get the buffered region of the image
*/
virtual itk::ImageRegion<3> GetBufferedRegion() const = 0;
@@ -212,13 +229,22 @@ public:
const SNAPSegmentationROISettings &roi, itk::Command *progressCommand) const = 0;
/** Transform a voxel index into a spatial position */
- virtual Vector3d TransformVoxelIndexToPosition(const Vector3ui &iVoxel) const = 0;
+ virtual Vector3d TransformVoxelIndexToPosition(const Vector3i &iVoxel) const = 0;
+
+ /** Transform a voxel index into a spatial position */
+ virtual Vector3d TransformVoxelCIndexToPosition(const Vector3d &iVoxel) const = 0;
+
+ /** Transform spatial position to voxel continuous index (LPS) */
+ virtual Vector3d TransformPositionToVoxelCIndex(const Vector3d &vLPS) const = 0;
+
+ /** Transform spatial position to voxel index (LPS) */
+ virtual Vector3i TransformPositionToVoxelIndex(const Vector3d &vLPS) const = 0;
/** Transform a voxel index into NIFTI coordinates (RAS) */
- virtual Vector3d TransformVoxelIndexToNIFTICoordinates(const Vector3d &iVoxel) const = 0;
+ virtual Vector3d TransformVoxelCIndexToNIFTICoordinates(const Vector3d &iVoxel) const = 0;
/** Transform NIFTI coordinates to a continuous voxel index */
- virtual Vector3d TransformNIFTICoordinatesToVoxelIndex(const Vector3d &vNifti) const = 0;
+ virtual Vector3d TransformNIFTICoordinatesToVoxelCIndex(const Vector3d &vNifti) const = 0;
/** Get the NIFTI s-form matrix for this image */
irisVirtualGetMacro(NiftiSform, TransformType)
@@ -296,9 +322,6 @@ public:
virtual void GetVoxelUnderCursorDisplayedValueAndAppearance(
vnl_vector<double> &out_value, DisplayPixelType &out_appearance) = 0;
- /** Get the voxel array, as void pointer */
- virtual void *GetVoxelVoidPointer() const = 0;
-
/** Clear the data associated with storing an image */
virtual void Reset() = 0;
@@ -315,6 +338,12 @@ public:
*/
virtual AbstractDisplayMappingPolicy *GetDisplayMapping() = 0;
+ /**
+ * Get the display mapping policy. This policy differs from wrapper to wrapper
+ * and may involve using color labels or color maps.
+ */
+ virtual const AbstractDisplayMappingPolicy *GetDisplayMapping() const = 0;
+
// Access the filename
irisVirtualGetStringMacro(FileName)
irisVirtualSetStringMacro(FileName)
@@ -337,6 +366,19 @@ public:
virtual void WriteThumbnail(const char *filename, unsigned int maxdim) = 0;
/**
+ * Access the "IO hints" registry associated with this wrapper. The IO hints
+ * are used to help read the image when the filename alone is not sufficient.
+ * For example, it may contain the DICOM series ID of the image, or for a raw
+ * image the dimensions.
+ */
+ virtual const Registry &GetIOHints() const = 0;
+
+ /**
+ * Set the IO hints
+ */
+ virtual void SetIOHints(const Registry &io_hints) = 0;
+
+ /**
* Write the image to disk with the help of the GuidedNativeImageIO object
*/
virtual void WriteToFile(const char *filename, Registry &hints) = 0;
@@ -401,6 +443,16 @@ public:
*/
virtual void SetITKTransform(ImageBaseType *referenceSpace, ITKTransformType *transform) = 0;
+ /**
+ * Get the ITK transform between this image and the reference space
+ */
+ virtual const ITKTransformType *GetITKTransform() const = 0;
+
+ /**
+ * Get the reference space space in which this image is defined
+ */
+ virtual ImageBaseType* GetReferenceSpace() const = 0;
+
protected:
};
@@ -412,8 +464,16 @@ public:
// A common image format to which the contents of the scalar image wrapper
// may be cast for downstream processing
typedef itk::Image<GreyType, 3> CommonFormatImageType;
+
typedef itk::Image<float, 3> FloatImageType;
typedef itk::ImageSource<FloatImageType> FloatImageSource;
+ typedef itk::Image<double, 3> DoubleImageType;
+ typedef itk::ImageSource<DoubleImageType> DoubleImageSource;
+
+ typedef itk::VectorImage<float, 3> FloatVectorImageType;
+ typedef itk::ImageSource<FloatVectorImageType> FloatVectorImageSource;
+ typedef itk::VectorImage<double, 3> DoubleVectorImageType;
+ typedef itk::ImageSource<DoubleVectorImageType> DoubleVectorImageSource;
/**
* An enum of export channel types. Export channels are used to present the
@@ -498,6 +558,15 @@ public:
*/
virtual SmartPtr<FloatImageSource> CreateCastToFloatPipeline() const = 0;
+ /** Same as CreateCastToFloatPipeline, but for double precision */
+ virtual SmartPtr<DoubleImageSource> CreateCastToDoublePipeline() const = 0;
+
+ /** Same as CreateCastToFloatPipeline, but for vector images of single dimension */
+ virtual SmartPtr<FloatVectorImageSource> CreateCastToFloatVectorPipeline() const = 0;
+
+ /** Same as CreateCastToFloatPipeline, but for vector images of single dimension */
+ virtual SmartPtr<DoubleVectorImageSource> CreateCastToDoubleVectorPipeline() const = 0;
+
/**
* Get the intensity curve used to map raw intensities to color map inputs.
* The intensity curve is only used by some wrappers (anatomic, speed) and
diff --git a/Logic/ImageWrapper/ImageWrapperTraits.h b/Logic/ImageWrapper/ImageWrapperTraits.h
index 55302ec..f69ddf7 100644
--- a/Logic/ImageWrapper/ImageWrapperTraits.h
+++ b/Logic/ImageWrapper/ImageWrapperTraits.h
@@ -3,6 +3,7 @@
#include "SNAPCommon.h"
#include "ImageWrapperBase.h"
+#include "RLEImageRegionIterator.h"
#include "CommonRepresentationPolicy.h"
#include "DisplayMappingPolicy.h"
@@ -38,7 +39,8 @@ public:
typedef ScalarImageWrapper<LabelImageWrapperTraits> WrapperType;
typedef LabelType ComponentType;
- typedef itk::Image<ComponentType, 3> ImageType;
+ typedef RLEImage<ComponentType> ImageType;
+ typedef itk::Image<ComponentType, 2> SliceType;
typedef IdentityInternalToNativeIntensityMapping NativeIntensityMapping;
typedef ColorLabelTableDisplayMappingPolicy<Self> DisplayMapping;
@@ -60,6 +62,7 @@ public:
typedef GreyType ComponentType;
typedef itk::Image<ComponentType, 3> ImageType;
+ typedef itk::Image<ComponentType, 2> SliceType;
typedef SpeedImageInternalToNativeIntensityMapping NativeIntensityMapping;
typedef LinearColorMapDisplayMappingPolicy<Self> DisplayMapping;
@@ -86,6 +89,7 @@ public:
typedef float ComponentType;
typedef itk::Image<ComponentType, 3> ImageType;
+ typedef itk::Image<ComponentType, 2> SliceType;
typedef IdentityInternalToNativeIntensityMapping NativeIntensityMapping;
typedef LinearColorMapDisplayMappingPolicy<Self> DisplayMapping;
@@ -131,6 +135,8 @@ public:
typedef TPixel ComponentType;
typedef itk::VectorImageToImageAdaptor<ComponentType, 3> ImageType;
+ typedef itk::Image<ComponentType, 2> SliceType;
+
typedef LinearInternalToNativeIntensityMapping NativeIntensityMapping;
typedef CachingCurveAndColorMapDisplayMappingPolicy<Self> DisplayMapping;
@@ -158,6 +164,8 @@ public:
typedef itk::VectorImage<InternalComponentType, 3> InternalImageType;
typedef VectorToScalarImageAccessor<TFunctor> AccessorType;
typedef itk::ImageAdaptor<InternalImageType, AccessorType> ImageType;
+ typedef itk::Image<ComponentType, 2> SliceType;
+
typedef IdentityInternalToNativeIntensityMapping NativeIntensityMapping;
typedef CachingCurveAndColorMapDisplayMappingPolicy<Self> DisplayMapping;
@@ -188,6 +196,7 @@ public:
typedef ScalarImageWrapper<ComponentWrapperTraits> ComponentWrapperType;
typedef itk::VectorImage<ComponentType, 3> ImageType;
+ typedef itk::VectorImage<ComponentType, 2> SliceType;
typedef LinearInternalToNativeIntensityMapping NativeIntensityMapping;
typedef MultiChannelDisplayMappingPolicy<Self> DisplayMapping;
@@ -210,6 +219,7 @@ public:
typedef TPixel ComponentType;
typedef itk::Image<ComponentType, 3> ImageType;
+ typedef itk::Image<ComponentType, 2> SliceType;
typedef LinearInternalToNativeIntensityMapping NativeIntensityMapping;
typedef CachingCurveAndColorMapDisplayMappingPolicy<Self> DisplayMapping;
diff --git a/Logic/ImageWrapper/ScalarImageWrapper.cxx b/Logic/ImageWrapper/ScalarImageWrapper.cxx
index 6ac44ff..47a143f 100644
--- a/Logic/ImageWrapper/ScalarImageWrapper.cxx
+++ b/Logic/ImageWrapper/ScalarImageWrapper.cxx
@@ -15,10 +15,10 @@
#include "ScalarImageWrapper.h"
#include "ImageWrapperTraits.h"
-#include "itkImageRegionIterator.h"
+#include "RLEImageRegionIterator.h"
#include "itkImageSliceConstIteratorWithIndex.h"
#include "itkNumericTraits.h"
-#include "itkRegionOfInterestImageFilter.h"
+#include "RLERegionOfInterestImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkIdentityTransform.h"
#include "itkResampleImageFilter.h"
@@ -29,7 +29,7 @@
#include "itkVTKImageExport.h"
#include "itkStreamingImageFilter.h"
-#include "IRISSlicer.h"
+#include "AdaptiveSlicingPipeline.h"
#include "SNAPSegmentationROISettings.h"
#include "itkCommand.h"
#include "itkMinimumMaximumImageFilter.h"
@@ -52,7 +52,7 @@ ScalarImageWrapper<TTraits,TBase>
m_HistogramFilter = HistogramFilterType::New();
// Set up VTK export pipeline
- this->SetupVTKImportExport();
+ //this->SetupVTKImportExport();
}
template<class TTraits, class TBase>
@@ -80,6 +80,24 @@ ScalarImageWrapper<TTraits,TBase>
}
}
+template<>
+ScalarImageWrapper<LabelImageWrapperTraits, ScalarImageWrapperBase>
+::ScalarImageWrapper(const Self ©)
+{
+ CommonInitialization();
+
+ // If the source contains an image, make a copy of that image
+ if (copy.IsInitialized() && copy.GetImage())
+ {
+ typedef itk::RegionOfInterestImageFilter<ImageType, ImageType> roiType;
+ roiType::Pointer roi = roiType::New();
+ roi->SetInput(copy.GetImage());
+ roi->Update();
+ ImagePointer newImage = roi->GetOutput();
+ UpdateImagePointer(newImage);
+ }
+}
+
template<class TTraits, class TBase>
ScalarImageWrapper<TTraits,TBase>
::~ScalarImageWrapper()
@@ -111,7 +129,7 @@ ScalarImageWrapper<TTraits,TBase>
m_CommonRepresentationPolicy.UpdateInputImage(newImage);
// Update the VTK export pipeline
- m_VTKExporter->SetInput(newImage);
+ // m_VTKExporter->SetInput(newImage);
}
template <class TTraits, class TBase>
@@ -197,6 +215,136 @@ ScalarImageWrapper<TTraits, TBase>::CreateCastToFloatPipeline() const
return output;
}
+template<class TTraits, class TBase>
+SmartPtr<typename ScalarImageWrapper<TTraits, TBase>::DoubleImageSource>
+ScalarImageWrapper<TTraits, TBase>::CreateCastToDoublePipeline() const
+{
+ // The kind of mini-pipeline that is created here depends on whether the
+ // internal image is a floating point image or not, and whether the native
+ // to intensity mapping is identity or not. We use template specialization to
+ // select the right behavior
+ typedef itk::UnaryFunctorImageFilter<ImageType, DoubleImageType, NativeIntensityMapping> FilterType;
+ SmartPtr<FilterType> filter = FilterType::New();
+ filter->SetInput(this->m_Image);
+ filter->SetFunctor(this->m_NativeMapping);
+
+ SmartPtr<DoubleImageSource> output = filter.GetPointer();
+ return output;
+}
+
+template <class TInputImage, class TOutputImage, class TFunctor>
+class UnaryFunctorImageToSingleComponentVectorImageFilter
+ : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ typedef UnaryFunctorImageToSingleComponentVectorImageFilter<TInputImage, TOutputImage, TFunctor> Self;
+ typedef itk::ImageToImageFilter<TInputImage, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer< const Self > ConstPointer;
+
+ typedef TInputImage InputImageType;
+ typedef TOutputImage OutputImageType;
+ typedef TFunctor FunctorType;
+
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(UnaryFunctorImageToSingleComponentVectorImageFilter, ImageToImageFilter)
+ itkNewMacro(Self)
+
+ /** ImageDimension constants */
+ itkStaticConstMacro(InputImageDimension, unsigned int,
+ TInputImage::ImageDimension);
+ itkStaticConstMacro(OutputImageDimension, unsigned int,
+ TOutputImage::ImageDimension);
+
+ void SetFunctor(const FunctorType &functor)
+ {
+ if(m_Functor != functor)
+ {
+ m_Functor = functor;
+ this->Modified();
+ }
+ }
+
+ itkGetConstReferenceMacro(Functor, FunctorType)
+
+ void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId);
+
+
+protected:
+
+ UnaryFunctorImageToSingleComponentVectorImageFilter() {}
+ virtual ~UnaryFunctorImageToSingleComponentVectorImageFilter() {}
+
+ FunctorType m_Functor;
+
+};
+
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+
+template <class TInputImage, class TOutputImage, class TFunctor>
+void
+UnaryFunctorImageToSingleComponentVectorImageFilter<TInputImage, TOutputImage, TFunctor>
+::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId)
+{
+ // Use our fast iterators for vector images
+ typedef itk::ImageLinearIteratorWithIndex<OutputImageType> IterBase;
+ typedef IteratorExtender<IterBase> IterType;
+
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+
+ // Define the iterators
+ IterType outputIt(this->GetOutput(), outputRegionForThread);
+ int line_len = outputRegionForThread.GetSize(0);
+
+ // Using a generic ITK iterator for the input because it supports RLE images and adaptors
+ itk::ImageScanlineConstIterator< InputImageType > inputIt(this->GetInput(), outputRegionForThread);
+
+ while ( !inputIt.IsAtEnd() )
+ {
+ // Get the pointer to the input and output pixel lines
+ OutputComponentType *out = outputIt.GetPixelPointer(this->GetOutput());
+
+ for(int i = 0; i < line_len; i++, ++inputIt)
+ {
+ out[i] = m_Functor(inputIt.Get());
+ }
+
+ outputIt.NextLine();
+ inputIt.NextLine();
+ }
+}
+
+template<class TTraits, class TBase>
+SmartPtr<typename ScalarImageWrapper<TTraits, TBase>::FloatVectorImageSource>
+ScalarImageWrapper<TTraits, TBase>::CreateCastToFloatVectorPipeline() const
+{
+ typedef UnaryFunctorImageToSingleComponentVectorImageFilter<
+ ImageType, FloatVectorImageType, NativeIntensityMapping> FilterType;
+ SmartPtr<FilterType> filter = FilterType::New();
+ filter->SetInput(this->m_Image);
+ filter->SetFunctor(this->m_NativeMapping);
+
+ SmartPtr<FloatVectorImageSource> output = filter.GetPointer();
+ return output;
+}
+
+template<class TTraits, class TBase>
+SmartPtr<typename ScalarImageWrapper<TTraits, TBase>::DoubleVectorImageSource>
+ScalarImageWrapper<TTraits, TBase>::CreateCastToDoubleVectorPipeline() const
+{
+ typedef UnaryFunctorImageToSingleComponentVectorImageFilter<
+ ImageType, DoubleVectorImageType, NativeIntensityMapping> FilterType;
+ SmartPtr<FilterType> filter = FilterType::New();
+ filter->SetInput(this->m_Image);
+ filter->SetFunctor(this->m_NativeMapping);
+
+ SmartPtr<DoubleVectorImageSource> output = filter.GetPointer();
+ return output;
+}
template<class TTraits, class TBase>
double
@@ -243,72 +391,55 @@ ScalarImageWrapper<TTraits,TBase>
::GetVoxelUnderCursorDisplayedValueAndAppearance(
vnl_vector<double> &out_value, DisplayPixelType &out_appearance)
{
- // Make sure the display slice is updated
- this->GetDisplaySlice(0)->GetSource()->UpdateLargestPossibleRegion();
-
- // Find the correct voxel in the space of the first display slice
- Vector3ui idxDisp =
- this->GetImageToDisplayTransform(0).TransformVoxelIndex(this->GetSliceIndex());
-
- DisplaySliceType *slice = this->GetDisplaySlice(0);
- ImageType *source = this->m_Slicer[0]->GetPreviewInput();
-
- itk::ImageRegion<3> rgnSource;
- if(source)
- {
- rgnSource = source->GetBufferedRegion();
- }
-
- // Get the RGB value
- typename DisplaySliceType::IndexType idx2D = {{idxDisp[0], idxDisp[1]}};
- out_appearance = slice->GetPixel(idx2D);
-
- // Get the numerical value
- PixelType val_raw = this->GetSlicer(0)->GetOutput()->GetPixel(idx2D);
+ // Look up the actual intensity of the voxel from the slicer
out_value.set_size(1);
- out_value[0] = this->m_NativeMapping(val_raw);
-}
+ out_value[0] = this->m_NativeMapping(
+ this->m_Slicer[0]->LookupIntensityAtSliceIndex(this->m_ReferenceSpace));
-template<class TTraits, class TBase>
-void
-ScalarImageWrapper<TTraits,TBase>
-::SetupVTKImportExport()
-{
- // Initialize the VTK Exporter
- m_VTKExporter = VTKExporter::New();
- m_VTKExporter->ReleaseDataFlagOn();
-
- // Initialize the VTK Importer
- m_VTKImporter = vtkImageImport::New();
- m_VTKImporter->ReleaseDataFlagOn();
-
- // Pipe the importer into the exporter (that's a lot of code)
- m_VTKImporter->SetUpdateInformationCallback(
- m_VTKExporter->GetUpdateInformationCallback());
- m_VTKImporter->SetPipelineModifiedCallback(
- m_VTKExporter->GetPipelineModifiedCallback());
- m_VTKImporter->SetWholeExtentCallback(
- m_VTKExporter->GetWholeExtentCallback());
- m_VTKImporter->SetSpacingCallback(
- m_VTKExporter->GetSpacingCallback());
- m_VTKImporter->SetOriginCallback(
- m_VTKExporter->GetOriginCallback());
- m_VTKImporter->SetScalarTypeCallback(
- m_VTKExporter->GetScalarTypeCallback());
- m_VTKImporter->SetNumberOfComponentsCallback(
- m_VTKExporter->GetNumberOfComponentsCallback());
- m_VTKImporter->SetPropagateUpdateExtentCallback(
- m_VTKExporter->GetPropagateUpdateExtentCallback());
- m_VTKImporter->SetUpdateDataCallback(
- m_VTKExporter->GetUpdateDataCallback());
- m_VTKImporter->SetDataExtentCallback(
- m_VTKExporter->GetDataExtentCallback());
- m_VTKImporter->SetBufferPointerCallback(
- m_VTKExporter->GetBufferPointerCallback());
- m_VTKImporter->SetCallbackUserData(
- m_VTKExporter->GetCallbackUserData());
+ // Use the display mapping to map to display pixel
+ out_appearance = this->m_DisplayMapping->MapPixel(out_value[0]);
}
+//template<class TTraits, class TBase>
+//void
+//ScalarImageWrapper<TTraits,TBase>
+//::SetupVTKImportExport()
+//{
+// // Initialize the VTK Exporter
+// m_VTKExporter = VTKExporter::New();
+// m_VTKExporter->ReleaseDataFlagOn();
+//
+// // Initialize the VTK Importer
+// m_VTKImporter = vtkImageImport::New();
+// m_VTKImporter->ReleaseDataFlagOn();
+//
+// // Pipe the importer into the exporter (that's a lot of code)
+// m_VTKImporter->SetUpdateInformationCallback(
+// m_VTKExporter->GetUpdateInformationCallback());
+// m_VTKImporter->SetPipelineModifiedCallback(
+// m_VTKExporter->GetPipelineModifiedCallback());
+// m_VTKImporter->SetWholeExtentCallback(
+// m_VTKExporter->GetWholeExtentCallback());
+// m_VTKImporter->SetSpacingCallback(
+// m_VTKExporter->GetSpacingCallback());
+// m_VTKImporter->SetOriginCallback(
+// m_VTKExporter->GetOriginCallback());
+// m_VTKImporter->SetScalarTypeCallback(
+// m_VTKExporter->GetScalarTypeCallback());
+// m_VTKImporter->SetNumberOfComponentsCallback(
+// m_VTKExporter->GetNumberOfComponentsCallback());
+// m_VTKImporter->SetPropagateUpdateExtentCallback(
+// m_VTKExporter->GetPropagateUpdateExtentCallback());
+// m_VTKImporter->SetUpdateDataCallback(
+// m_VTKExporter->GetUpdateDataCallback());
+// m_VTKImporter->SetDataExtentCallback(
+// m_VTKExporter->GetDataExtentCallback());
+// m_VTKImporter->SetBufferPointerCallback(
+// m_VTKExporter->GetBufferPointerCallback());
+// m_VTKImporter->SetCallbackUserData(
+// m_VTKExporter->GetCallbackUserData());
+//}
+
template<class TTraits, class TBase>
vtkImageImport *
ScalarImageWrapper<TTraits,TBase>
diff --git a/Logic/ImageWrapper/ScalarImageWrapper.h b/Logic/ImageWrapper/ScalarImageWrapper.h
index cebe3b5..a2e103c 100644
--- a/Logic/ImageWrapper/ScalarImageWrapper.h
+++ b/Logic/ImageWrapper/ScalarImageWrapper.h
@@ -77,11 +77,22 @@ public:
typedef typename Superclass::ImagePointer ImagePointer;
typedef typename Superclass::PixelType PixelType;
typedef typename Superclass::CommonFormatImageType CommonFormatImageType;
+ typedef typename Superclass::PreviewImageType PreviewImageType;
// Floating point image type
typedef itk::Image<float, 3> FloatImageType;
typedef itk::ImageSource<FloatImageType> FloatImageSource;
+ // Double precision floating point image type
+ typedef itk::Image<double, 3> DoubleImageType;
+ typedef itk::ImageSource<DoubleImageType> DoubleImageSource;
+
+ // Vector image types
+ typedef itk::VectorImage<float, 3> FloatVectorImageType;
+ typedef itk::ImageSource<FloatVectorImageType> FloatVectorImageSource;
+ typedef itk::VectorImage<double, 3> DoubleVectorImageType;
+ typedef itk::ImageSource<DoubleVectorImageType> DoubleVectorImageSource;
+
// Slice image type
typedef typename Superclass::SliceType SliceType;
typedef typename Superclass::SlicePointer SlicePointer;
@@ -231,6 +242,16 @@ public:
*/
SmartPtr<FloatImageSource> CreateCastToFloatPipeline() const;
+ /** Same as above, but casts to double. For compatibility with C3D, until we
+ * safely switch C3D to use float instead of double */
+ SmartPtr<DoubleImageSource> CreateCastToDoublePipeline() const;
+
+ /** Same as CreateCastToFloatPipeline, but for vector images of single dimension */
+ SmartPtr<FloatVectorImageSource> CreateCastToFloatVectorPipeline() const;
+
+ /** Same as CreateCastToFloatPipeline, but for vector images of single dimension */
+ SmartPtr<DoubleVectorImageSource> CreateCastToDoubleVectorPipeline() const;
+
/**
* Get an image cast to a common representation.
* @see ScalarImageWrapperBase::GetCommonFormatImage()
@@ -299,12 +320,12 @@ protected:
ITKTransformType *tran = NULL);
- typedef itk::VTKImageExport<ImageType> VTKExporter;
- SmartPtr<VTKExporter> m_VTKExporter;
+ //typedef itk::VTKImageExport<ImageType> VTKExporter;
+ //SmartPtr<VTKExporter> m_VTKExporter;
vtkSmartPointer<vtkImageImport> m_VTKImporter;
- void SetupVTKImportExport();
+ //void SetupVTKImportExport();
};
#endif // __ScalarImageWrapper_h_
diff --git a/Logic/ImageWrapper/VectorImageWrapper.cxx b/Logic/ImageWrapper/VectorImageWrapper.cxx
index 90a4c61..8bae8ac 100644
--- a/Logic/ImageWrapper/VectorImageWrapper.cxx
+++ b/Logic/ImageWrapper/VectorImageWrapper.cxx
@@ -14,13 +14,13 @@
=========================================================================*/
#include "VectorImageWrapper.h"
-#include "itkImageRegionIterator.h"
+#include "RLEImageRegionIterator.h"
#include "itkImageSliceConstIteratorWithIndex.h"
#include "itkNumericTraits.h"
#include "itkRegionOfInterestImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkIdentityTransform.h"
-#include "IRISSlicer.h"
+#include "AdaptiveSlicingPipeline.h"
#include "SNAPSegmentationROISettings.h"
#include "itkCommand.h"
#include "ImageWrapperTraits.h"
@@ -97,27 +97,18 @@ VectorImageWrapper<TTraits,TBase>
{
// Get the numerical value
MultiChannelDisplayMode mode = this->m_DisplayMapping->GetDisplayMode();
- if(mode.UseRGB)
+ if(mode.UseRGB || mode.RenderAsGrid)
{
- // Make sure the display slice is updated
- this->GetDisplaySlice(0)->Update();
+ // Look up the actual intensity of the voxel from the slicer
+ PixelType pixel_value = this->m_Slicer[0]->LookupIntensityAtSliceIndex(this->m_ReferenceSpace);
- // Find the correct voxel in the space of the first display slice
- Vector3ui idxDisp =
- this->GetImageToDisplayTransform(0).TransformVoxelIndex(this->GetSliceIndex());
-
- // Get the RGB value
- typename DisplaySliceType::IndexType idx2D = {{idxDisp[0], idxDisp[1]}};
- out_appearance = this->GetDisplaySlice(0)->GetPixel(idx2D);
-
- // Get the value vector in native range
+ // Set the output value
out_value.set_size(this->GetNumberOfComponents());
for(int i = 0; i < this->GetNumberOfComponents(); i++)
- {
- InternalPixelType p =
- this->GetComponentWrapper(i)->GetSlicer(0)->GetOutput()->GetPixel(idx2D);
- out_value[i] = this->m_NativeMapping(p);
- }
+ out_value[i] = this->m_NativeMapping(pixel_value[i]);
+
+ // Use the display mapping to map to display pixel
+ out_appearance = this->m_DisplayMapping->MapPixel(pixel_value);
}
else
{
@@ -210,6 +201,10 @@ VectorImageWrapper<TTraits,TBase>
// Assign a parent wrapper to the derived wrapper
wrapper->SetParentWrapper(this);
+ // Pass the display geometry to the component wrapper
+ for(int k = 0; k < 3; k++)
+ wrapper->SetDisplayViewportGeometry(k, this->GetDisplayViewportGeometry(k));
+
SmartPtr<ScalarImageWrapperBase> ptrout = wrapper.GetPointer();
// When creating derived wrappers, we need to rebroadcast the events from
@@ -241,6 +236,12 @@ VectorImageWrapper<TTraits,TBase>
// Create a wrapper for this image and assign the component image
SmartPtr<ComponentWrapperType> cw = ComponentWrapperType::New();
+
+ // Pass the display geometry to the component wrapper
+ for(int k = 0; k < 3; k++)
+ cw->SetDisplayViewportGeometry(k, this->GetDisplayViewportGeometry(k));
+
+ // Initialize referencing the current wrapper
cw->InitializeToWrapper(this, comp, referenceSpace, transform);
// Assign a parent wrapper to the derived wrapper
@@ -313,6 +314,19 @@ VectorImageWrapper<TTraits,TBase>
}
+template<class TTraits, class TBase>
+void
+VectorImageWrapper<TTraits,TBase>
+::SetITKTransform(ImageBaseType *referenceSpace, ITKTransformType *transform)
+{
+ Superclass::SetITKTransform(referenceSpace, transform);
+ for(ScalarRepIterator it = m_ScalarReps.begin(); it != m_ScalarReps.end(); ++it)
+ {
+ it->second->SetITKTransform(referenceSpace, transform);
+ }
+}
+
+
template <class TTraits, class TBase>
inline ScalarImageWrapperBase *
VectorImageWrapper<TTraits,TBase>
@@ -407,7 +421,23 @@ VectorImageWrapper<TTraits,TBase>
template <class TTraits, class TBase>
void
VectorImageWrapper<TTraits,TBase>
-::SetDisplayGeometry(IRISDisplayGeometry &dispGeom)
+::SetDisplayViewportGeometry(
+ unsigned int index,
+ ImageBaseType *viewport_image)
+{
+ Superclass::SetDisplayViewportGeometry(index, viewport_image);
+
+ // Propagate to owned scalar wrappers
+ for(ScalarRepIterator it = m_ScalarReps.begin(); it != m_ScalarReps.end(); ++it)
+ {
+ it->second->SetDisplayViewportGeometry(index, viewport_image);
+ }
+}
+
+template <class TTraits, class TBase>
+void
+VectorImageWrapper<TTraits,TBase>
+::SetDisplayGeometry(const IRISDisplayGeometry &dispGeom)
{
Superclass::SetDisplayGeometry(dispGeom);
for(ScalarRepIterator it = m_ScalarReps.begin(); it != m_ScalarReps.end(); ++it)
diff --git a/Logic/ImageWrapper/VectorImageWrapper.h b/Logic/ImageWrapper/VectorImageWrapper.h
index f940da6..b407265 100644
--- a/Logic/ImageWrapper/VectorImageWrapper.h
+++ b/Logic/ImageWrapper/VectorImageWrapper.h
@@ -55,6 +55,7 @@ public:
typedef typename Superclass::ImageBaseType ImageBaseType;
typedef typename Superclass::ImageType ImageType;
typedef typename Superclass::ImagePointer ImagePointer;
+ typedef typename Superclass::PreviewImageType PreviewImageType;
// Pixel type
typedef typename Superclass::PixelType PixelType;
@@ -203,7 +204,9 @@ public:
virtual void SetSliceIndex(const Vector3ui &cursor);
- virtual void SetDisplayGeometry(IRISDisplayGeometry &dispGeom);
+ virtual void SetDisplayGeometry(const IRISDisplayGeometry &dispGeom);
+
+ virtual void SetDisplayViewportGeometry(unsigned int index, ImageBaseType *viewport_image);
virtual void SetDirectionMatrix(const vnl_matrix<double> &direction);
@@ -240,6 +243,8 @@ protected:
ImageBaseType *refSpace = NULL,
ITKTransformType *tran = NULL);
+ virtual void SetITKTransform(ImageBaseType *referenceSpace, ITKTransformType *transform);
+
/** Destructor */
virtual ~VectorImageWrapper();
diff --git a/Logic/ImageWrapper/VectorToScalarImageAccessor.h b/Logic/ImageWrapper/VectorToScalarImageAccessor.h
index 4bbc33e..3fc3185 100644
--- a/Logic/ImageWrapper/VectorToScalarImageAccessor.h
+++ b/Logic/ImageWrapper/VectorToScalarImageAccessor.h
@@ -35,6 +35,9 @@ public:
inline ExternalType Get(const ActualPixelType &input) const
{ return m_Functor.Get(input); }
+ inline ExternalType Get(const InternalType *incomp) const
+ { return m_Functor.Get(incomp, Superclass::GetVectorLength()); }
+
inline ExternalType Get(const InternalType &input,
const SizeValueType offset) const
{ return Get(Superclass::Get(input, offset)); }
@@ -45,6 +48,11 @@ public:
Superclass::SetVectorLength(l);
}
+ VectorLengthType GetVectorLength() const
+ {
+ return Superclass::GetVectorLength();
+ }
+
/**
* Set the linear mapping from the vector image internal values to the
* native data type. The mapping is native = internal * scale + shift
@@ -115,10 +123,16 @@ class VectorToScalarMagnitudeFunctor : public AbstractVectorToDerivedQuantityFun
public:
typedef TInputPixel InputPixelType;
typedef TOutputPixel OutputPixelType;
+
OutputPixelType Get(const itk::VariableLengthVector<InputPixelType> &input) const
{
+ return this->Get(input.GetDataPointer(), input.Size());
+ }
+
+ OutputPixelType Get(const InputPixelType *input, int n_comp) const
+ {
double sumT2 = 0.0, sumT = 0.0;
- for(int i = 0; i < input.Size(); i++)
+ for(int i = 0; i < n_comp; i++)
{
double t = input[i];
sumT2 += t * t;
@@ -152,8 +166,13 @@ public:
typedef TOutputPixel OutputPixelType;
OutputPixelType Get(const itk::VariableLengthVector<InputPixelType> &input) const
{
+ return this->Get(input.GetDataPointer(), input.Size());
+ }
+
+ OutputPixelType Get(const InputPixelType *input, int n_comp) const
+ {
InputPixelType mymax = input[0];
- for(int i = 1; i < input.Size(); i++)
+ for(int i = 1; i < n_comp; i++)
mymax = std::max(input[i], mymax);
return static_cast<OutputPixelType>(mymax * this->m_Scale + this->m_Shift);
}
@@ -168,10 +187,15 @@ public:
OutputPixelType Get(const itk::VariableLengthVector<InputPixelType> &input) const
{
+ return this->Get(input.GetDataPointer(), input.Size());
+ }
+
+ OutputPixelType Get(const InputPixelType *input, int n_comp) const
+ {
double mean = 0.0;
- for(int i = 0; i < input.Size(); i++)
+ for(int i = 0; i < n_comp; i++)
mean += input[i];
- mean /= input.Size();
+ mean /= n_comp;
return static_cast<OutputPixelType>(mean * this->m_Scale + this->m_Shift);
}
};
diff --git a/Logic/LevelSet/LevelSetExtensionFilter.h b/Logic/LevelSet/LevelSetExtensionFilter.h
index 9efaff1..ffba2f5 100644
--- a/Logic/LevelSet/LevelSetExtensionFilter.h
+++ b/Logic/LevelSet/LevelSetExtensionFilter.h
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
diff --git a/Logic/LevelSet/SNAPAdvectionFieldImageFilter.h b/Logic/LevelSet/SNAPAdvectionFieldImageFilter.h
index a3315f6..3ec3ba4 100644
--- a/Logic/LevelSet/SNAPAdvectionFieldImageFilter.h
+++ b/Logic/LevelSet/SNAPAdvectionFieldImageFilter.h
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
diff --git a/Logic/LevelSet/SNAPAdvectionFieldImageFilter.txx b/Logic/LevelSet/SNAPAdvectionFieldImageFilter.txx
index bd8eef8..7f9d668 100644
--- a/Logic/LevelSet/SNAPAdvectionFieldImageFilter.txx
+++ b/Logic/LevelSet/SNAPAdvectionFieldImageFilter.txx
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
diff --git a/Logic/LevelSet/SNAPLevelSetDriver.h b/Logic/LevelSet/SNAPLevelSetDriver.h
index 1a5c83a..2eb660e 100644
--- a/Logic/LevelSet/SNAPLevelSetDriver.h
+++ b/Logic/LevelSet/SNAPLevelSetDriver.h
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
@@ -35,7 +37,6 @@
#ifndef __SNAPLevelSetDriver_h_
#define __SNAPLevelSetDriver_h_
-#include "SNAPCommon.h"
#include "SnakeParameters.h"
#include "SNAPLevelSetFunction.h"
// #include "SNAPLevelSetStopAndGoFilter.h"
@@ -156,7 +157,7 @@ public:
void Restart();
/** Get the level set function */
- irisGetMacro(LevelSetFunction,LevelSetFunctionType *);
+ itkGetConstMacro(LevelSetFunction,LevelSetFunctionType *);
/** Get the current state of the snake (level set and narrow band) */
FloatImageType *GetCurrentState();
diff --git a/Logic/LevelSet/SNAPLevelSetDriver.txx b/Logic/LevelSet/SNAPLevelSetDriver.txx
index e84d591..a1d339b 100644
--- a/Logic/LevelSet/SNAPLevelSetDriver.txx
+++ b/Logic/LevelSet/SNAPLevelSetDriver.txx
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
@@ -42,7 +44,6 @@
//#endif
#include "SNAPLevelSetDriver.h"
-#include "IRISVectorTypesToITKConversion.h"
#include "itkCommand.h"
#include "itkNarrowBandLevelSetImageFilter.h"
@@ -137,7 +138,7 @@ SNAPLevelSetDriver<VDimension>
template<unsigned int VDimension>
void
SNAPLevelSetDriver<VDimension>
-::AssignParametersToPhi(const SnakeParameters &p, bool irisNotUsed(firstTime))
+::AssignParametersToPhi(const SnakeParameters &p, bool itkNotUsed(firstTime))
{
// Set up the level set function
diff --git a/Logic/LevelSet/SNAPLevelSetFunction.h b/Logic/LevelSet/SNAPLevelSetFunction.h
index cbdaebb..e15baf2 100644
--- a/Logic/LevelSet/SNAPLevelSetFunction.h
+++ b/Logic/LevelSet/SNAPLevelSetFunction.h
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
@@ -44,10 +46,12 @@
#include "itkLinearInterpolateImageFunction.h"
#include "itkVectorLinearInterpolateImageFunction.h"
#include "itkVectorCastImageFilter.h"
-#include "ThreadSpecificData.h"
-
#include "SNAPAdvectionFieldImageFilter.h"
+#ifndef THREAD_SPECIFIC_DATA
+ #include "ThreadSpecificData.h"
+#endif
+
/**
\class SNAPLevelSetFunction
\brief A level set function that implements the generic level set equation
@@ -345,7 +349,11 @@ private:
VectorType > m_VectorCast;
/** The current value of the speed function */
+#ifndef THREAD_SPECIFIC_DATA
ThreadSpecificData<ScalarValueType> m_CachedSpeed;
+#else
+ ScalarValueType m_CachedSpeed;
+#endif
};
#ifndef ITK_MANUAL_INSTANTIATION
diff --git a/Logic/LevelSet/SNAPLevelSetFunction.txx b/Logic/LevelSet/SNAPLevelSetFunction.txx
index 3adfcfc..2a5f6fe 100644
--- a/Logic/LevelSet/SNAPLevelSetFunction.txx
+++ b/Logic/LevelSet/SNAPLevelSetFunction.txx
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
@@ -35,7 +37,6 @@
#include "itkGradientImageFilter.h"
#include "itkUnaryFunctorImageFilter.h"
#include "itkMultiplyImageFilter.h"
-#include "itkImageRegionIterator.h"
#include "itkNumericTraits.h"
#include "itkSimpleFastMutexLock.h"
#include "itkMutexLockHolder.h"
diff --git a/Logic/LevelSet/SignedDistanceFilter.h b/Logic/LevelSet/SignedDistanceFilter.h
deleted file mode 100644
index 5199194..0000000
--- a/Logic/LevelSet/SignedDistanceFilter.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*=========================================================================
-
- Program: ITK-SNAP
- Module: $RCSfile: SignedDistanceFilter.h,v $
- Language: C++
- Date: $Date: 2007/12/30 04:05:14 $
- Version: $Revision: 1.2 $
- Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
-
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- -----
-
- Copyright (c) 2003 Insight Software Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-
-=========================================================================*/
-#ifndef __SignedDistanceFilter_h_
-#define __SignedDistanceFilter_h_
-
-#include "itkCommand.h"
-#include "itkProgressAccumulator.h"
-#include "itkUnaryFunctorImageFilter.h"
-#include "itkDanielssonDistanceMapImageFilter.h"
-#include "itkSubtractImageFilter.h"
-
-/**
- * \class SignedDistanceFilter
- * \brief This filter computes an inside/outside signed distance image
- * given a binary image of the 'inside'
- */
-template <typename TInputImage,typename TOutputImage>
-class SignedDistanceFilter:
- public itk::ImageToImageFilter<TInputImage,TOutputImage>
-{
-public:
-
- /** Standard class typedefs. */
- typedef SignedDistanceFilter Self;
- typedef itk::ImageToImageFilter<TInputImage,TOutputImage> Superclass;
- typedef itk::SmartPointer<Self> Pointer;
- typedef itk::SmartPointer<const Self> ConstPointer;
-
- /** Pixel Type of the input image */
- typedef TInputImage InputImageType;
- typedef typename InputImageType::PixelType InputPixelType;
- typedef itk::SmartPointer<InputImageType> InputImagePointer;
-
- /** Pixel Type of the output image */
- typedef TOutputImage OutputImageType;
- typedef typename OutputImageType::PixelType OutputPixelType;
- typedef itk::SmartPointer<OutputImageType> OutputImagePointer;
-
- /** Type used for internal calculations */
- typedef float RealType;
- typedef itk::Image<RealType,3> InternalImageType;
- typedef itk::SmartPointer<InternalImageType> InternalImagePointer;
-
- /** Method for creation through the object factory. */
- itkNewMacro(Self)
-
- /** Image dimension. */
- itkStaticConstMacro(ImageDimension, unsigned int,
- TInputImage::ImageDimension);
-protected:
-
- SignedDistanceFilter();
- virtual ~SignedDistanceFilter() {};
- void PrintSelf(std::ostream& os, itk::Indent indent) const;
-
- /** Generate Data */
- void GenerateData( void );
-
-private:
-
- /** A functor used to invert the input image */
- class InvertFunctor {
- public:
- InputPixelType operator()(InputPixelType input)
- {
- return input == itk::NumericTraits<InputPixelType>::Zero ?
- itk::NumericTraits<InputPixelType>::One :
- itk::NumericTraits<InputPixelType>::Zero;
- }
- };
-
- // Types used in the internal pipeline
- typedef itk::UnaryFunctorImageFilter<InputImageType,
- InputImageType,InvertFunctor> InvertFilterType;
-
- typedef itk::DanielssonDistanceMapImageFilter<
- InputImageType,OutputImageType> DistanceFilterType;
-
- typedef itk::SubtractImageFilter<OutputImageType,
- OutputImageType,OutputImageType> SubtractFilterType;
-
- // Progress accumulator object
- typedef itk::ProgressAccumulator::Pointer AccumulatorPointer;
-
- // Define the actual filters
- typename InvertFilterType::Pointer m_InvertFilter;
- typename DistanceFilterType::Pointer m_InsideDistanceFilter;
- typename DistanceFilterType::Pointer m_OutsideDistanceFilter;
- typename SubtractFilterType::Pointer m_SubtractFilter;
-
- /** Progress tracking object */
- AccumulatorPointer m_ProgressAccumulator;
-};
-
-#ifndef ITK_MANUAL_INSTANTIATION
-#include "SignedDistanceFilter.txx"
-#endif
-
-#endif // __SignedDistanceFilter_h_
diff --git a/Logic/LevelSet/SignedDistanceFilter.txx b/Logic/LevelSet/SignedDistanceFilter.txx
deleted file mode 100644
index 2bc391d..0000000
--- a/Logic/LevelSet/SignedDistanceFilter.txx
+++ /dev/null
@@ -1,108 +0,0 @@
-/*=========================================================================
-
- Program: ITK-SNAP
- Module: $RCSfile: SignedDistanceFilter.txx,v $
- Language: C++
- Date: $Date: 2010/06/28 18:45:08 $
- Version: $Revision: 1.3 $
- Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
-
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- -----
-
- Copyright (c) 2003 Insight Software Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-
-=========================================================================*/
-
-template<typename TInputImage,typename TOutputImage>
-SignedDistanceFilter<TInputImage,TOutputImage>
-::SignedDistanceFilter()
-{
- // Create the inverter
- m_InvertFilter = InvertFilterType::New();
- m_InvertFilter->SetInput(this->GetInput());
- m_InvertFilter->ReleaseDataFlagOn();
-
- // Create the exterior distance filter
- m_OutsideDistanceFilter = DistanceFilterType::New();
- m_OutsideDistanceFilter->SetInputIsBinary(true);
- m_OutsideDistanceFilter->SetInput(this->GetInput());
- m_OutsideDistanceFilter->ReleaseDataFlagOn();
-
- // And the interior filter
- m_InsideDistanceFilter = DistanceFilterType::New();
- m_InsideDistanceFilter->SetInput(m_InvertFilter->GetOutput());
- m_InsideDistanceFilter->SetInputIsBinary(true);
- m_InsideDistanceFilter->ReleaseDataFlagOn();
-
- // Filter to subtract the inside from the outside
- m_SubtractFilter = SubtractFilterType::New();
- m_SubtractFilter->SetInput1(m_OutsideDistanceFilter->GetDistanceMap());
- m_SubtractFilter->SetInput2(m_InsideDistanceFilter->GetDistanceMap());
-
- // Create the progress accumulator
- m_ProgressAccumulator = itk::ProgressAccumulator::New();
- m_ProgressAccumulator->SetMiniPipelineFilter(this);
-
- // Register the filters with the progress accumulator
- m_ProgressAccumulator->RegisterInternalFilter(m_InvertFilter,0.05f);
- m_ProgressAccumulator->RegisterInternalFilter(m_InsideDistanceFilter,0.45f);
- m_ProgressAccumulator->RegisterInternalFilter(m_OutsideDistanceFilter,0.45f);
- m_ProgressAccumulator->RegisterInternalFilter(m_SubtractFilter,0.05f);
-}
-
-template<typename TInputImage,typename TOutputImage>
-void
-SignedDistanceFilter<TInputImage,TOutputImage>
-::GenerateData()
-{
- // Get the input and output pointers
- const typename InputImageType::ConstPointer inputImage = this->GetInput();
- typename OutputImageType::Pointer outputImage = this->GetOutput();
-
- // Initialize the progress counter
- m_ProgressAccumulator->ResetProgress();
-
- // Allocate the output image
- outputImage->SetBufferedRegion(outputImage->GetRequestedRegion());
- outputImage->Allocate();
-
- // Set the inputs
- m_InvertFilter->SetInput(inputImage);
- m_OutsideDistanceFilter->SetInput(inputImage);
-
- // Call the filter's GenerateData()
- m_SubtractFilter->GraftOutput(outputImage);
- m_SubtractFilter->Update();
-
- // graft the mini-pipeline output back onto this filter's output.
- // this is needed to get the appropriate regions passed back.
- GraftOutput( m_SubtractFilter->GetOutput() );
-}
-
-template<typename TInputImage,typename TOutputImage>
-void
-SignedDistanceFilter<TInputImage,TOutputImage>
-::PrintSelf(std::ostream& os, itk::Indent indent) const
-{
- Superclass::PrintSelf(os,indent);
-}
diff --git a/Logic/LevelSet/SnakeParameters.cxx b/Logic/LevelSet/SnakeParameters.cxx
index 816917a..39e909d 100644
--- a/Logic/LevelSet/SnakeParameters.cxx
+++ b/Logic/LevelSet/SnakeParameters.cxx
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
diff --git a/Logic/LevelSet/SnakeParameters.h b/Logic/LevelSet/SnakeParameters.h
index 1a4c30f..79d9115 100644
--- a/Logic/LevelSet/SnakeParameters.h
+++ b/Logic/LevelSet/SnakeParameters.h
@@ -9,19 +9,21 @@
This file is part of ITK-SNAP
- ITK-SNAP is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+ 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 program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-----
Copyright (c) 2003 Insight Software Consortium. All rights reserved.
@@ -35,7 +37,7 @@
#ifndef __SnakeParameters_h_
#define __SnakeParameters_h_
-#include "SNAPCommon.h"
+#include "itkMacro.h"
// TODO: implement this using AbstractPropertyContainerModel
@@ -89,56 +91,99 @@ public:
/** Whether we wish to automatically compute optimal time step
* in level snake propagation */
- irisGetMacro(AutomaticTimeStep,bool);
- irisSetMacro(AutomaticTimeStep,bool);
+ itkGetConstMacro(AutomaticTimeStep,bool);
+ void SetAutomaticTimeStep( bool value )
+ {
+ this->m_AutomaticTimeStep = value;
+ }
/** Time step factor in level snake propagation. This is is only used if the
* automatic computation is off, and represents the factor by which the auto
* time step is multiplied */
- irisGetMacro(TimeStepFactor,float);
- irisSetMacro(TimeStepFactor,float);
+ itkGetConstMacro(TimeStepFactor,float);
+ void SetTimeStepFactor( float value )
+ {
+ this->m_TimeStepFactor = value;
+ }
/** Clamp-to-ground parameter. Obsolete in ITK implementation, kept for
backward compatibility and regression testing */
- irisGetMacro(Ground,float);
- irisSetMacro(Ground,float);
+ itkGetConstMacro(Ground,float);
+ void SetGround( float value )
+ {
+ this->m_Ground = value;
+ }
/** Whether to clamp or not. Obsolete in ITK implementation, kept for
backward compatibility and regression testing */
- irisGetMacro(Clamp,bool);
- irisSetMacro(Clamp,bool);
+ itkGetConstMacro(Clamp,bool);
+ void SetClamp( bool value )
+ {
+ this->m_Clamp = value;
+ }
/** Which solver to use to run the equation */
- irisGetMacro(Solver,SolverType);
- irisSetMacro(Solver,SolverType);
+ itkGetConstMacro(Solver,SolverType);
+ void SetSolver( SolverType value )
+ {
+ this->m_Solver = value;
+ }
/** Type of equation (well known parameter sets) */
- irisGetMacro(SnakeType,SnakeType);
- irisSetMacro(SnakeType,SnakeType);
-
- irisGetMacro(PropagationWeight,float);
- irisSetMacro(PropagationWeight,float);
+ itkGetConstMacro(SnakeType,SnakeType);
+ void SetSnakeType( SnakeType value )
+ {
+ this->m_SnakeType = value;
+ }
+
+ itkGetConstMacro(PropagationWeight,float);
+ void SetPropagationWeight( float value )
+ {
+ this->m_PropagationWeight = value;
+ }
- irisGetMacro(PropagationSpeedExponent,int);
- irisSetMacro(PropagationSpeedExponent,int);
-
- irisGetMacro(CurvatureWeight,float);
- irisSetMacro(CurvatureWeight,float);
-
- irisGetMacro(CurvatureSpeedExponent,int);
- irisSetMacro(CurvatureSpeedExponent,int);
-
- irisGetMacro(LaplacianWeight,float);
- irisSetMacro(LaplacianWeight,float);
-
- irisGetMacro(LaplacianSpeedExponent,int);
- irisSetMacro(LaplacianSpeedExponent,int);
-
- irisGetMacro(AdvectionWeight,float);
- irisSetMacro(AdvectionWeight,float);
+ itkGetConstMacro(PropagationSpeedExponent,int);
+ void SetPropagationSpeedExponent( int value )
+ {
+ this->m_PropagationSpeedExponent = value;
+ }
+
+ itkGetConstMacro(CurvatureWeight,float);
+ void SetCurvatureWeight( float value )
+ {
+ this->m_CurvatureWeight = value;
+ }
+
+ itkGetConstMacro(CurvatureSpeedExponent,int);
+ void SetCurvatureSpeedExponent( int value )
+ {
+ this->m_CurvatureSpeedExponent = value;
+ }
+
+ itkGetConstMacro(LaplacianWeight,float);
+ void SetLaplacianWeight( float value )
+ {
+ this->m_LaplacianWeight = value;
+ }
+
+ itkGetConstMacro(LaplacianSpeedExponent,int);
+ void SetLaplacianSpeedExponent( int value )
+ {
+ this->m_LaplacianSpeedExponent = value;
+ }
+
+ itkGetConstMacro(AdvectionWeight,float);
+ void SetAdvectionWeight( float value )
+ {
+ this->m_AdvectionWeight = value;
+ }
+
+ itkGetConstMacro(AdvectionSpeedExponent,int);
+ void SetAdvectionSpeedExponent( int value )
+ {
+ this->m_AdvectionSpeedExponent = value;
+ }
- irisGetMacro(AdvectionSpeedExponent,int);
- irisSetMacro(AdvectionSpeedExponent,int);
private:
float m_TimeStepFactor;
float m_Ground;
diff --git a/Logic/LevelSet/SnakeParametersPreviewPipeline.cxx b/Logic/LevelSet/SnakeParametersPreviewPipeline.cxx
index b16eb24..fba3e88 100644
--- a/Logic/LevelSet/SnakeParametersPreviewPipeline.cxx
+++ b/Logic/LevelSet/SnakeParametersPreviewPipeline.cxx
@@ -6,8 +6,8 @@
Date: $Date: 2010/10/19 19:15:14 $
Version: $Revision: 1.6 $
Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
+
+ This file is part of ITK-SNAP
ITK-SNAP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -29,7 +29,7 @@
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE. See the above copyright notices for more information.
+ PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "SnakeParametersPreviewPipeline.h"
@@ -38,7 +38,6 @@
#include "GlobalState.h"
#include "LevelSetExtensionFilter.h"
-#include "SignedDistanceFilter.h"
#include "SNAPLevelSetFunction.h"
#include "itkBSplineInterpolationWeightFunction.h"
#include "itkBSplineKernelFunction.h"
@@ -55,7 +54,7 @@
#include "PolygonScanConvert.h"
#ifndef vtkFloatingPointType
-#define vtkFloatingPointType float
+#define vtkFloatingPointType double
#endif
using namespace std;
@@ -90,8 +89,8 @@ public:
// Destructor
~LevelSetPreview2d()
- {
- if(m_Driver) delete m_Driver;
+ {
+ if(m_Driver) delete m_Driver;
}
// Timer callback, used to regenerate the current contour
@@ -102,7 +101,7 @@ public:
// If the driver is dirty, we need to create a new one
if(m_DriverDirty && m_Driver != NULL)
- {
+ {
delete m_Driver;
m_Driver = NULL;
}
@@ -111,7 +110,7 @@ public:
if(m_Driver == NULL && m_SpeedImage.IsNotNull() && m_Curve.size() > 0)
{
// Check if we need to allocate the level set image
- if(m_LevelSetImage.IsNull() || m_LevelSetImage->GetBufferedRegion() !=
+ if(m_LevelSetImage.IsNull() || m_LevelSetImage->GetBufferedRegion() !=
m_SpeedImage->GetBufferedRegion())
{
m_LevelSetImage = FloatImageType::New();
@@ -128,7 +127,7 @@ public:
for(CurveType::iterator it = m_Curve.begin(); it != m_Curve.end(); ++it)
{
points.push_back(Vector2d(
- it->x[0] * m_LevelSetImage->GetBufferedRegion().GetSize()[0],
+ it->x[0] * m_LevelSetImage->GetBufferedRegion().GetSize()[0],
it->x[1] * m_LevelSetImage->GetBufferedRegion().GetSize()[1]));
}
@@ -198,9 +197,9 @@ public:
m_VTKImporter->SetDataExtentCallback(
m_VTKExporter->GetDataExtentCallback());
m_VTKImporter->SetBufferPointerCallback(
- m_VTKExporter->GetBufferPointerCallback());
+ m_VTKExporter->GetBufferPointerCallback());
m_VTKImporter->SetCallbackUserData(
- m_VTKExporter->GetCallbackUserData());
+ m_VTKExporter->GetCallbackUserData());
// Create and configure the contour filter
m_VTKContour = vtkContourFilter::New();
@@ -316,7 +315,7 @@ private:
-void
+void
SnakeParametersPreviewPipeline
::AnimationCallback()
{
@@ -366,7 +365,7 @@ SnakeParametersPreviewPipeline
::SetControlPoints(const std::vector<Vector2d> &points)
{
if(m_ControlPoints != points)
- {
+ {
// Save the points
m_ControlPoints = points;
@@ -376,7 +375,7 @@ SnakeParametersPreviewPipeline
}
}
-void
+void
SnakeParametersPreviewPipeline
::ChangeControlPoint(
unsigned int index, const Vector2d &point, bool quickUpdate)
@@ -409,10 +408,9 @@ SnakeParametersPreviewPipeline
typedef itk::GradientImageFilter<ShortImageType> GradientFilter;
GradientFilter::Pointer filter = GradientFilter::New();
- // Set up and run the filter
filter->SetInput(m_SpeedImage);
- m_GradientImage = filter->GetOutput();
filter->Update();
+ m_GradientImage = filter->GetOutput();
// Pass the image to the display functor
m_DisplayMapper->SetInput(m_SpeedImage);
@@ -441,7 +439,7 @@ SnakeParametersPreviewPipeline
// Don't waste time on nonsense
if(m_Parameters == clean) return;
-
+
// Save the parameters
m_Parameters = clean;
m_ParametersModified = true;
@@ -450,7 +448,7 @@ SnakeParametersPreviewPipeline
m_DemoLoop->SetSnakeParameters(m_Parameters);
}
-void
+void
SnakeParametersPreviewPipeline
::SetNumberOfSampledPoints(unsigned int number)
{
@@ -491,7 +489,7 @@ SnakeParametersPreviewPipeline
// TODO: the colormap from the current speed image should be used!
}
-void
+void
SnakeParametersPreviewPipeline
::UpdateContour()
{
@@ -507,12 +505,12 @@ SnakeParametersPreviewPipeline
// Initialize the sampled point array
m_SampledPoints.clear();
m_SampledPoints.reserve(m_NumberOfSampledPoints);
-
- int uMax = m_ControlPoints.size() - 3;
+
+ int uMax = m_ControlPoints.size() - 3;
for(double t = 0; t < 1.0; t += 0.005)
{
double s = t * uMax;
-
+
// The starting index
// int si = ((int)(t * uMax)) - 1;
int sidx = (int) floor(s - 1);
@@ -523,7 +521,7 @@ SnakeParametersPreviewPipeline
Vector2d xu(0.0f,0.0f);
Vector2d xuu(0.0f,0.0f);
for(int k=0; k < 4; k++)
- {
+ {
double w = kf3->Evaluate(u);
double wu = kf2->Evaluate(u+0.5) - kf2->Evaluate(u-0.5);
double wuu = kf1->Evaluate(u+1) + kf1->Evaluate(u-1) - 2 * kf1->Evaluate(u);
@@ -540,9 +538,9 @@ SnakeParametersPreviewPipeline
pt.x = x;
pt.t = t;
xu.normalize();
- pt.n = Vector2d(-xu[1],xu[0]);
+ pt.n = Vector2d(-xu[1],xu[0]);
pt.PropagationForce = pt.CurvatureForce = pt.AdvectionForce = 0.0;
- pt.kappa
+ pt.kappa
= (xu[0] * xuu[1] - xu[1] * xuu[0]) / pow(xu[0]*xu[0] + xu[1]*xu[1],1.5);
m_SampledPoints.push_back(pt);
@@ -570,7 +568,7 @@ SnakeParametersPreviewPipeline
ShortImageType,double> LerpType;
typedef itk::VectorLinearInterpolateImageFunction<
VectorImageType,double> VectorLerpType;
-
+
// Create the speed image interpolator
LerpType::Pointer sLerp = LerpType::New();
sLerp->SetInputImage(m_SpeedImage);
@@ -581,13 +579,13 @@ SnakeParametersPreviewPipeline
// Get the image dimensions
itk::Size<2> idim = m_SpeedImage->GetBufferedRegion().GetSize();
-
+
// Compute the geometry of each point
for(unsigned int i = 0; i < m_SampledPoints.size(); i++)
{
// A reference so we can access the point in shorthand
SampledPoint &p = m_SampledPoints[i];
-
+
// We're done computing the geometric properties of the curve. Now, let's
// compute the image-related quantities. First, convert the point to image
// coordinates
@@ -604,9 +602,9 @@ SnakeParametersPreviewPipeline
// Get the value of the gradient
VectorLerpType::OutputType gradG = gLerp->EvaluateAtContinuousIndex(idx);
gradG /= 0x7fff;
-
+
// Compute the propagation force component of the curve evolution
- p.PropagationForce = m_Parameters.GetPropagationWeight()
+ p.PropagationForce = m_Parameters.GetPropagationWeight()
* pow(g,m_Parameters.GetPropagationSpeedExponent());
// Compute the curvature force component of the curve evolution
@@ -616,7 +614,7 @@ SnakeParametersPreviewPipeline
// Compute the advection force component of the curve evolution
p.AdvectionForce = - m_Parameters.GetAdvectionWeight()
* (p.n[0] * gradG[0] + p.n[1] * gradG[1])
- * pow(g,m_Parameters.GetAdvectionSpeedExponent());
+ * pow(g,m_Parameters.GetAdvectionSpeedExponent());
}
}
diff --git a/Logic/LevelSet/SnakeParametersPreviewPipeline.h b/Logic/LevelSet/SnakeParametersPreviewPipeline.h
index ce6359b..7328953 100644
--- a/Logic/LevelSet/SnakeParametersPreviewPipeline.h
+++ b/Logic/LevelSet/SnakeParametersPreviewPipeline.h
@@ -43,7 +43,6 @@
#include "ColorMap.h"
#include "ImageWrapperTraits.h"
-template<class TInputImage, class TOutputImage> class SignedDistanceFilter;
template <class TSpeedImageType, class TImageType> class SNAPLevelSetFunction;
template<class TFilter> class LevelSetExtensionFilter;
diff --git a/Logic/Mesh/MultiLabelMeshPipeline.cxx b/Logic/Mesh/MultiLabelMeshPipeline.cxx
index f8689c7..18a8b70 100644
--- a/Logic/Mesh/MultiLabelMeshPipeline.cxx
+++ b/Logic/Mesh/MultiLabelMeshPipeline.cxx
@@ -46,9 +46,7 @@
#include "MeshOptions.h"
// ITK includes
-#include "itkRegionOfInterestImageFilter.h"
#include "itkBinaryThresholdImageFilter.h"
-#include "itkImageRegionConstIteratorWithIndex.h"
using namespace std;
@@ -159,7 +157,7 @@ inline unsigned long rotl(unsigned long value, int shift)
void MultiLabelMeshPipeline::UpdateMeshInfoHelper(
MultiLabelMeshPipeline::MeshInfo *current_meshinfo,
const itk::Index<3> &run_start,
- itk::ImageLinearConstIteratorWithIndex<MultiLabelMeshPipeline::InputImageType> &it,
+ itk::ImageRegionConstIteratorWithIndex<MultiLabelMeshPipeline::InputImageType> &it,
unsigned long pos)
{
// The end of the run, i.e., the last voxel that matched the label of run_start
@@ -220,60 +218,31 @@ void MultiLabelMeshPipeline::UpdateMeshes(itk::Command *progressCommand)
// each pixel read, the code collects runs of pixels of the same label and
// updates once the run ends (a pixel of another label is found or the end
// of a line of pixels is reached). This makes for much more efficient code.
- typedef itk::ImageLinearConstIteratorWithIndex<InputImageType> InputIterator;
+ typedef itk::ImageRegionConstIteratorWithIndex<InputImageType> InputIterator;
InputIterator it(m_InputImage, m_InputImage->GetLargestPossibleRegion());
while( !it.IsAtEnd() )
{
- // When starting a new line, we must record the pass through the
- // last line and reset the current objects
- if(current_label)
- UpdateMeshInfoHelper(current_meshinfo, run_start, it, line_length);
-
- // Reset the current objects
- current_label = it.Get();
- if(current_label)
- {
- current_meshinfo = &meshmap[current_label];
- run_start = it.GetIndex();
- }
-
- // Take one step forward
- ++it;
-
+ run_start = it.GetIndex();
+ const InputIterator::RLLine &line=*(it.rlLine);
+ int t = 0;
// Iterate through the line
- while(!it.IsAtEndOfLine())
+ for (int x = 0; x < line.size(); x++)
{
- // The the current label
- LabelType label = it.Get();
-
- // If the label does not match the current label, do the same deal
- if(label != current_label)
+ run_start[0] = t;
+ current_label = line[x].second;
+ t += line[x].first;
+ if (current_label != 0)
{
+ current_meshinfo = &meshmap[current_label];
// Update the current mesh info
- if(current_label)
- UpdateMeshInfoHelper(current_meshinfo, run_start, it, it.GetIndex()[0]);
-
- // Reset the current objects
- current_label = it.Get();
- if(current_label)
- {
- current_meshinfo = &meshmap[current_label];
- run_start = it.GetIndex();
- }
+ UpdateMeshInfoHelper(current_meshinfo, run_start, it, t);
+ current_meshinfo = &meshmap[current_label];
}
-
- // Go to the next voxel
- ++it;
}
-
- // Go to the next line
- it.NextLine();
+ ++(it.bi);
+ it.rlLine = &it.bi.Value();
}
- // At the end of the iteration, one more update!
- if(current_label)
- UpdateMeshInfoHelper(current_meshinfo, run_start, it, line_length);
-
// At this point, meshmap has the number of voxels for every label, as well
// as the checksum for every label and the extent for every label. Now we
// can determine which meshes actually need to be updated
diff --git a/Logic/Mesh/MultiLabelMeshPipeline.h b/Logic/Mesh/MultiLabelMeshPipeline.h
index fde734a..9fcc8a9 100644
--- a/Logic/Mesh/MultiLabelMeshPipeline.h
+++ b/Logic/Mesh/MultiLabelMeshPipeline.h
@@ -41,11 +41,14 @@
#include "vtkSmartPointer.h"
#include "itksys/MD5.h"
#include "itkObjectFactory.h"
+#include "ImageWrapperTraits.h"
+#include "RLERegionOfInterestImageFilter.h"
+#include "RLEImageScanlineIterator.h"
+
// Forward reference to itk classes
namespace itk {
template <class TPixel,unsigned int VDimension> class Image;
- template <class TInputImage, class TOutputImage> class RegionOfInterestImageFilter;
template <class TInputImage, class TOutputImage> class BinaryThresholdImageFilter;
template <class TImage> class ImageLinearConstIteratorWithIndex;
}
@@ -72,10 +75,33 @@ class MultiLabelMeshPipeline : public itk::Object
{
public:
+ // Cached information about a VTK mesh
+ struct MeshInfo
+ {
+ // The pointer to the mesh
+ vtkSmartPointer<vtkPolyData> Mesh;
+
+ // The checksum for the mesh
+ unsigned long CheckSum;
+
+ // The extents of the bounding box
+ Vector3i BoundingBox[2];
+
+ // The number of voxels
+ unsigned long Count;
+
+ MeshInfo();
+ ~MeshInfo();
+ };
+
+ // Collection of mesh data for labels present in the image
+ typedef std::map<LabelType, MeshInfo> MeshInfoMap;
+
+
irisITKObjectMacro(MultiLabelMeshPipeline, itk::Object)
/** Input image type */
- typedef itk::Image<LabelType,3> InputImageType;
+ typedef LabelImageWrapperTraits::ImageType InputImageType;
typedef itk::SmartPointer<InputImageType> InputImagePointer;
/** Set the input segmentation image */
@@ -87,6 +113,8 @@ public:
unsigned long GetVoxelsInBoundingBox(LabelType label) const;
+ const MeshInfoMap& GetMeshInfo() { return m_MeshInfo; }
+
/** Set the mesh options for this filter */
void SetMeshOptions(const MeshOptions *options);
@@ -144,27 +172,6 @@ private:
// standardized range
ThresholdFilterPointer m_ThrehsoldFilter;
- // Cached information about a VTK mesh
- struct MeshInfo
- {
- // The pointer to the mesh
- vtkSmartPointer<vtkPolyData> Mesh;
-
- // The checksum for the mesh
- unsigned long CheckSum;
-
- // The extents of the bounding box
- Vector3i BoundingBox[2];
-
- // The number of voxels
- unsigned long Count;
-
- MeshInfo();
- ~MeshInfo();
- };
-
- // Collection of mesh data for labels present in the image
- typedef std::map<LabelType, MeshInfo> MeshInfoMap;
MeshInfoMap m_MeshInfo;
// Set of bounding boxes
@@ -180,7 +187,7 @@ private:
void UpdateMeshInfoHelper(
MeshInfo *current_meshinfo,
const itk::Index<3> &run_start,
- itk::ImageLinearConstIteratorWithIndex<InputImageType> &it,
+ itk::ImageRegionConstIteratorWithIndex<InputImageType> &it,
unsigned long pos);
};
diff --git a/Logic/Preprocessing/ImageCollectionToImageFilter.h b/Logic/Preprocessing/ImageCollectionToImageFilter.h
index 5c5c618..f9dfdc0 100644
--- a/Logic/Preprocessing/ImageCollectionToImageFilter.h
+++ b/Logic/Preprocessing/ImageCollectionToImageFilter.h
@@ -38,7 +38,7 @@
#include <itkImageSource.h>
#include <itkImageToImageFilter.h>
-#include <itkImageRegionIteratorWithIndex.h>
+#include <itkImageRegionIterator.h>
#include <itkConstNeighborhoodIterator.h>
diff --git a/Logic/Preprocessing/PreprocessingFilterConfigTraits.cxx b/Logic/Preprocessing/PreprocessingFilterConfigTraits.cxx
index a01239f..9de8c62 100644
--- a/Logic/Preprocessing/PreprocessingFilterConfigTraits.cxx
+++ b/Logic/Preprocessing/PreprocessingFilterConfigTraits.cxx
@@ -208,7 +208,7 @@ RFPreprocessingFilterConfigTraits
}
// Set the classifier input
- RFClassificationEngine *rfe = sid->GetParent()->GetClassificationEngine();
+ IRISApplication::RFEngine *rfe = sid->GetParent()->GetClassificationEngine();
assert(rfe);
filter->SetClassifier(rfe->GetClassifier());
}
diff --git a/Logic/Preprocessing/PreprocessingFilterConfigTraits.h b/Logic/Preprocessing/PreprocessingFilterConfigTraits.h
index c1600f6..0e536ff 100644
--- a/Logic/Preprocessing/PreprocessingFilterConfigTraits.h
+++ b/Logic/Preprocessing/PreprocessingFilterConfigTraits.h
@@ -40,12 +40,12 @@
template <class TInput, class TOutput> class SmoothBinaryThresholdImageFilter;
template <class TInput, class TOutput> class EdgePreprocessingImageFilter;
template <class TInput, class TVectorInput, class TOutput> class GMMClassifyImageFilter;
-template <class TInput, class TVectorInput, class TOutput> class RandomForestClassifyImageFilter;
+template <class TInput, class TInputVector, class TOutput, class TLabel> class RandomForestClassifyImageFilter;
class ThresholdSettings;
class EdgePreprocessingSettings;
class GaussianMixtureModel;
-class RandomForestClassifier;
+template <class TPixel, class TLabel, int VDim> class RandomForestClassifier;
class SmoothBinaryThresholdFilterConfigTraits {
public:
@@ -143,9 +143,9 @@ public:
typedef SpeedImageWrapper OutputWrapperType;
typedef RandomForestClassifyImageFilter<
- GreyScalarType, GreyVectorType, SpeedType> FilterType;
+ GreyScalarType, GreyVectorType, SpeedType, LabelType> FilterType;
- typedef RandomForestClassifier ParameterType;
+ typedef RandomForestClassifier<GreyType,LabelType,3> ParameterType;
static void AttachInputs(SNAPImageData *sid, FilterType *filter, int channel);
static void DetachInputs(FilterType *filter);
diff --git a/Logic/Preprocessing/RandomForest/RFClassificationEngine.cxx b/Logic/Preprocessing/RFClassificationEngine.cxx
similarity index 72%
rename from Logic/Preprocessing/RandomForest/RFClassificationEngine.cxx
rename to Logic/Preprocessing/RFClassificationEngine.cxx
index 8754fa5..412a331 100644
--- a/Logic/Preprocessing/RandomForest/RFClassificationEngine.cxx
+++ b/Logic/Preprocessing/RFClassificationEngine.cxx
@@ -4,33 +4,33 @@
#include "SNAPImageData.h"
#include "ImageWrapper.h"
#include "ImageCollectionToImageFilter.h"
-#include "itkImageRegionIterator.h"
+#include "RLEImageRegionIterator.h"
// Includes from the random forest library
-typedef GreyType data_t;
-typedef LabelType label_t;
-
#include "Library/classification.h"
#include "Library/data.h"
-RFClassificationEngine::RFClassificationEngine()
+template <class TPixel, class TLabel, int VDim>
+RFClassificationEngine<TPixel,TLabel,VDim>::RFClassificationEngine()
{
m_DataSource = NULL;
m_Sample = NULL;
- m_Classifier = RandomForestClassifier::New();
+ m_Classifier = ClassifierType::New();
m_ForestSize = 50;
m_TreeDepth = 30;
m_PatchRadius.Fill(0);
m_UseCoordinateFeatures = false;
}
-RFClassificationEngine::~RFClassificationEngine()
+template <class TPixel, class TLabel, int VDim>
+RFClassificationEngine<TPixel,TLabel,VDim>::~RFClassificationEngine()
{
if(m_Sample)
delete m_Sample;
}
-void RFClassificationEngine::SetDataSource(SNAPImageData *imageData)
+template <class TPixel, class TLabel, int VDim>
+void RFClassificationEngine<TPixel,TLabel,VDim>::SetDataSource(SNAPImageData *imageData)
{
if(m_DataSource != imageData)
{
@@ -42,12 +42,14 @@ void RFClassificationEngine::SetDataSource(SNAPImageData *imageData)
}
}
-void RFClassificationEngine::ResetClassifier()
+template <class TPixel, class TLabel, int VDim>
+void RFClassificationEngine<TPixel,TLabel,VDim>::ResetClassifier()
{
m_Classifier->Reset();
}
-void RFClassificationEngine:: TrainClassifier()
+template <class TPixel, class TLabel, int VDim>
+void RFClassificationEngine<TPixel,TLabel,VDim>:: TrainClassifier()
{
assert(m_DataSource && m_DataSource->IsMainLoaded());
@@ -159,7 +161,7 @@ void RFClassificationEngine:: TrainClassifier()
params.subSamplePercent = 0;
// Create the classification engine
- typedef RandomForestClassifier::RFAxisClassifierType RFAxisClassifierType;
+ typedef typename ClassifierType::RFAxisClassifierType RFAxisClassifierType;
typedef Classification<GreyType, LabelType, RFAxisClassifierType> ClassificationType;
ClassificationType classification;
@@ -169,12 +171,12 @@ void RFClassificationEngine:: TrainClassifier()
if(m_Classifier->IsValidClassifier())
{
// Get the class weights
- const RandomForestClassifier::WeightArray &class_weights = m_Classifier->GetClassWeights();
+ const typename ClassifierType::WeightArray &class_weights = m_Classifier->GetClassWeights();
// Convert them to label weights (since class to label mapping may change)
- for(RandomForestClassifier::MappingType::const_iterator it =
- m_Classifier->m_ClassToLabelMapping.begin();
- it != m_Classifier->m_ClassToLabelMapping.end(); ++it)
+ for(typename ClassifierType::MappingType::const_iterator it =
+ m_Classifier->GetClassToLabelMapping().begin();
+ it != m_Classifier->GetClassToLabelMapping().end(); ++it)
{
old_label_weights[it->second] = class_weights[it->first];
}
@@ -186,26 +188,26 @@ void RFClassificationEngine:: TrainClassifier()
// Perform classifier training
classification.Learning(
params, *m_Sample,
- *m_Classifier->m_Forest,
- m_Classifier->m_ValidLabel,
- m_Classifier->m_ClassToLabelMapping);
+ *m_Classifier->GetForest(),
+ m_Classifier->GetValidLabel(),
+ m_Classifier->GetClassToLabelMapping());
// Reset the class weights to the number of classes and assign default
- int n_classes = m_Classifier->m_ClassToLabelMapping.size(), n_fore = 0, n_back = 0;
- m_Classifier->m_ClassWeights.resize(n_classes, -1.0);
+ int n_classes = m_Classifier->GetClassToLabelMapping().size(), n_fore = 0, n_back = 0;
+ m_Classifier->GetClassWeights().resize(n_classes, -1.0);
// Apply the old weight assignments if possible. Keep track of the number of fore and back classes
- for(RandomForestClassifier::MappingType::iterator it =
- m_Classifier->m_ClassToLabelMapping.begin();
- it != m_Classifier->m_ClassToLabelMapping.end(); ++it)
+ for(typename ClassifierType::MappingType::iterator it =
+ m_Classifier->GetClassToLabelMapping().begin();
+ it != m_Classifier->GetClassToLabelMapping().end(); ++it)
{
if(old_label_weights.find(it->second) != old_label_weights.end())
{
- m_Classifier->m_ClassWeights[it->first] = old_label_weights[it->second];
+ m_Classifier->GetClassWeights()[it->first] = old_label_weights[it->second];
}
- if(m_Classifier->m_ClassWeights[it->first] < 0.0)
+ if(m_Classifier->GetClassWeights()[it->first] < 0.0)
n_back++;
- else if(m_Classifier->m_ClassWeights[it->first] > 0.0)
+ else if(m_Classifier->GetClassWeights()[it->first] > 0.0)
n_fore++;
}
@@ -213,18 +215,19 @@ void RFClassificationEngine:: TrainClassifier()
if(n_classes >= 2)
{
if(n_fore == 0)
- m_Classifier->m_ClassWeights.front() = 1.0;
+ m_Classifier->GetClassWeights().front() = 1.0;
if(n_back == 0)
- m_Classifier->m_ClassWeights.back() = -1.0;
+ m_Classifier->GetClassWeights().back() = -1.0;
}
// Store the patch radius in the classifier - this remains fixed until
// training is repeated
- m_Classifier->m_PatchRadius = m_PatchRadius;
- m_Classifier->m_UseCoordinateFeatures = m_UseCoordinateFeatures;
+ m_Classifier->SetPatchRadius(m_PatchRadius);
+ m_Classifier->SetUseCoordinateFeatures(m_UseCoordinateFeatures);
}
-void RFClassificationEngine::SetClassifier(RandomForestClassifier *rf)
+template <class TPixel, class TLabel, int VDim>
+void RFClassificationEngine<TPixel,TLabel,VDim>::SetClassifier(ClassifierType *rf)
{
// Set the classifier
m_Classifier = rf;
@@ -233,7 +236,8 @@ void RFClassificationEngine::SetClassifier(RandomForestClassifier *rf)
m_ForestSize = m_Classifier->GetForest()->GetForestSize();
}
-int RFClassificationEngine::GetNumberOfComponents() const
+template <class TPixel, class TLabel, int VDim>
+int RFClassificationEngine<TPixel,TLabel,VDim>::GetNumberOfComponents() const
{
assert(m_DataSource);
@@ -246,4 +250,5 @@ int RFClassificationEngine::GetNumberOfComponents() const
return ncomp;
}
-
+// Template instantiation
+template class RFClassificationEngine<GreyType, LabelType, 3>;
diff --git a/Logic/Preprocessing/RandomForest/RFClassificationEngine.h b/Logic/Preprocessing/RFClassificationEngine.h
similarity index 83%
rename from Logic/Preprocessing/RandomForest/RFClassificationEngine.h
rename to Logic/Preprocessing/RFClassificationEngine.h
index 7e02439..8aecb9f 100644
--- a/Logic/Preprocessing/RandomForest/RFClassificationEngine.h
+++ b/Logic/Preprocessing/RFClassificationEngine.h
@@ -3,19 +3,18 @@
#include <itkObject.h>
#include <itkObjectFactory.h>
-#include <itkSize.h>
#include "SNAPCommon.h"
-#include "PropertyModel.h"
-
-class SNAPImageData;
-class RandomForestClassifier;
+#include <itkSize.h>
+template <class TPixel, class TLabel, int VDim> class RandomForestClassifier;
template <class TData, class TLabel> class MLData;
+class SNAPImageData;
/**
* This class serves as the high-level interface between ITK-SNAP and the
* random forest code.
*/
+template <class TPixel, class TLabel, int VDim>
class RFClassificationEngine : public itk::Object
{
public:
@@ -24,7 +23,10 @@ public:
irisITKObjectMacro(RFClassificationEngine, itk::Object)
// Patch radius type
- typedef itk::Size<3> RadiusType;
+ typedef itk::Size<VDim> RadiusType;
+
+ // Classifier type
+ typedef RandomForestClassifier<TPixel, TLabel, VDim> ClassifierType;
/** Set the data source for the classification */
void SetDataSource(SNAPImageData *imageData);
@@ -36,10 +38,10 @@ public:
void TrainClassifier();
/** Set the classifier */
- void SetClassifier(RandomForestClassifier *rf);
+ void SetClassifier(ClassifierType *rf);
/** Access the trained classifier */
- itkGetMacro(Classifier, RandomForestClassifier *)
+ itkGetMacro(Classifier, ClassifierType *)
/** Size of the random forest (main parameter) */
itkGetMacro(ForestSize, int)
@@ -67,7 +69,7 @@ protected:
virtual ~RFClassificationEngine();
// The trained classifier
- SmartPtr<RandomForestClassifier> m_Classifier;
+ SmartPtr<ClassifierType> m_Classifier;
// The data source
SNAPImageData *m_DataSource;
diff --git a/Logic/Preprocessing/RandomForest/RandomForestClassifier.cxx b/Logic/Preprocessing/RandomForest/RandomForestClassifier.cxx
deleted file mode 100644
index 89a3b83..0000000
--- a/Logic/Preprocessing/RandomForest/RandomForestClassifier.cxx
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "RandomForestClassifier.h"
-#include "Library/classification.h"
-
-void RandomForestClassifier::Reset()
-{
- if(m_Forest)
- delete m_Forest;
-
- m_Forest = new RandomForestType(true);
- m_ClassToLabelMapping.clear();
- m_BiasParameter = 0.5;
- m_PatchRadius.Fill(0);
- m_UseCoordinateFeatures = false;
- m_ClassWeights.clear();
-}
-
-void RandomForestClassifier::SetClassWeight(size_t class_id, double weight)
-{
- m_ClassWeights[class_id] = weight;
-}
-
-bool RandomForestClassifier::IsValidClassifier() const
-{
- return m_ClassToLabelMapping.size() >= 2 && m_Forest->GetForestSize() > 0;
-}
-
-RandomForestClassifier::RandomForestClassifier()
-{
- m_Forest = NULL;
- this->Reset();
-}
-
-RandomForestClassifier::~RandomForestClassifier()
-{
- if(m_Forest)
- delete m_Forest;
-}
diff --git a/Logic/Preprocessing/RandomForest/RandomForestClassifier.h b/Logic/Preprocessing/RandomForest/RandomForestClassifier.h
deleted file mode 100644
index a5c0b7f..0000000
--- a/Logic/Preprocessing/RandomForest/RandomForestClassifier.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef RANDOMFORESTCLASSIFIER_H
-#define RANDOMFORESTCLASSIFIER_H
-
-#include <itkDataObject.h>
-#include <itkObjectFactory.h>
-#include <itkSize.h>
-#include <SNAPCommon.h>
-#include <map>
-
-template <class dataT, class labelT> class Histogram;
-template <class dataT, class labelT> class AxisAlignedClassifier;
-template <class HistT, class ClassT, class dataT> class DecisionForest;
-
-/**
- * This class encapsulates a Random Forest classifier
- */
-class RandomForestClassifier : public itk::DataObject
-{
-public:
-
- // Standard ITK stuff
- irisITKObjectMacro(RandomForestClassifier, itk::DataObject)
-
- // typedefs
- typedef Histogram<GreyType, LabelType> RFHistogramType;
- typedef AxisAlignedClassifier<GreyType, LabelType> RFAxisClassifierType;
- typedef DecisionForest<RFHistogramType, RFAxisClassifierType, GreyType> RandomForestType;
- typedef std::map<size_t, LabelType> MappingType;
- typedef itk::Size<3> SizeType;
-
- // A list of weights for each class - used to construct speed image
- typedef std::vector<double> WeightArray;
-
- // Reset the classifier
- void Reset();
-
- // Get the mapping from the class indices to labels
- irisGetMacro(ClassToLabelMapping, const MappingType &)
-
- // Get the random forest
- irisGetMacro(Forest, RandomForestType *)
-
- // Get the patch radius
- irisGetMacro(PatchRadius, const SizeType &)
-
- /** Whether coordinates of the voxels are used as features */
- itkGetMacro(UseCoordinateFeatures, bool)
- itkSetMacro(UseCoordinateFeatures, bool)
-
- // Set the bias parameter (adjusts the mapping of FG probability to speed)
- itkGetMacro(BiasParameter, double)
- itkSetMacro(BiasParameter, double)
-
- // Get a reference to the weight array
- irisGetMacro(ClassWeights, const WeightArray &)
-
- // Set the weight for a class
- void SetClassWeight(size_t class_id, double weight);
-
- // Test if the classifier is valid (has 2+ classes)
- bool IsValidClassifier() const;
-
-protected:
-
- RandomForestClassifier();
- ~RandomForestClassifier();
-
- // The actual decision forest
- RandomForestType *m_Forest;
-
- // Whether the labels are valid (?)
- bool m_ValidLabel;
-
- // Mapping of index to label (?)
- MappingType m_ClassToLabelMapping;
-
- // Weight of each class
- WeightArray m_ClassWeights;
-
- // The patch radius
- SizeType m_PatchRadius;
-
- // Whether coordinate features are used
- bool m_UseCoordinateFeatures;
-
- // Bias parameter
- double m_BiasParameter;
-
- // Let the engine handle our data
- friend class RFClassificationEngine;
-};
-
-#endif // RANDOMFORESTCLASSIFIER_H
diff --git a/Logic/Preprocessing/SlicePreviewFilterWrapper.h b/Logic/Preprocessing/SlicePreviewFilterWrapper.h
index 8538723..e2f7140 100644
--- a/Logic/Preprocessing/SlicePreviewFilterWrapper.h
+++ b/Logic/Preprocessing/SlicePreviewFilterWrapper.h
@@ -7,7 +7,9 @@
class ImageWrapperBase;
class ScalarImageWrapperBase;
-template <class TInputImage, class TOutputImage> class IRISSlicer;
+
+template <typename TInputImage, typename TOutputImage, typename TPreviewImage>
+class AdaptiveSlicingPipeline;
namespace itk {
template<class TIn, class TOut> class StreamingImageFilter;
@@ -127,8 +129,8 @@ public:
typedef typename TFilterConfigTraits::InputDataType InputDataType;
typedef typename TFilterConfigTraits::OutputWrapperType OutputWrapperType;
- typedef IRISSlicer<OutputImageType, itk::Image<OutputPixelType, 2> >
- SlicerType;
+ typedef itk::Image<OutputPixelType, 2> SliceType;
+ typedef AdaptiveSlicingPipeline<OutputImageType,SliceType,OutputImageType> SlicerType;
typedef typename TFilterConfigTraits::ParameterType ParameterType;
diff --git a/Logic/Preprocessing/SlicePreviewFilterWrapper.txx b/Logic/Preprocessing/SlicePreviewFilterWrapper.txx
index c7fe826..bd264ca 100644
--- a/Logic/Preprocessing/SlicePreviewFilterWrapper.txx
+++ b/Logic/Preprocessing/SlicePreviewFilterWrapper.txx
@@ -3,7 +3,7 @@
#include "SmoothBinaryThresholdImageFilter.h"
#include "EdgePreprocessingImageFilter.h"
#include "itkStreamingImageFilter.h"
-#include <IRISSlicer.h>
+#include <AdaptiveSlicingPipeline.h>
#include <ColorMap.h>
#include <itkTimeProbe.h>
@@ -109,7 +109,7 @@ SlicePreviewFilterWrapper<TFilterConfigTraits>
for(unsigned int i = 0; i < 3; i++)
{
// Disconnect wrapper from this pipeline
- m_OutputWrapper->GetSlicer(i)->SetPreviewInput(NULL);
+ m_OutputWrapper->GetSlicer(i)->SetPreviewImage(NULL);
}
// Undo the graft
diff --git a/Logic/RLEImage/RLEImage.h b/Logic/RLEImage/RLEImage.h
new file mode 100644
index 0000000..9f1c9f1
--- /dev/null
+++ b/Logic/RLEImage/RLEImage.h
@@ -0,0 +1,253 @@
+#ifndef RLEImage_h
+#define RLEImage_h
+
+#include <utility> //std::pair
+#include <vector>
+#include <itkImageBase.h>
+#include <itkImage.h>
+
+/** Run-Length Encoded image.
+* It saves memory for label images at the expense of processing times.
+* Unsuitable for ordinary images (in which case it is counterproductive).
+*
+* BufferedRegion must include complete run-length lines (along X index axis).
+* BufferedRegion can be smaller than LargestPossibleRegion along other axes.
+*
+* It is best if pixel type and counter type have the same byte size
+* (for memory alignment purposes).
+*
+* Copied and adapted from itk::Image.
+*/
+template< typename TPixel, unsigned int VImageDimension = 3, typename CounterType = unsigned short >
+class RLEImage : public itk::ImageBase < VImageDimension >
+{
+
+public:
+ /** Standard class typedefs */
+ typedef RLEImage Self;
+ typedef itk::ImageBase < VImageDimension > Superclass;
+ typedef itk::SmartPointer< Self > Pointer;
+ typedef itk::SmartPointer< const Self > ConstPointer;
+ typedef itk::WeakPointer< const Self > ConstWeakPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(RLEImage, ImageBase);
+
+ /** Pixel typedef support. Used to declare pixel type in filters
+ * or other operations. */
+ typedef TPixel PixelType;
+
+ /** Typedef alias for PixelType */
+ typedef TPixel ValueType;
+
+ /** First element is count of repetitions,
+ * second element is the pixel value. */
+ typedef std::pair<CounterType, PixelType> RLSegment;
+
+ /** A Run-Length encoded line of pixels. */
+ typedef std::vector<RLSegment> RLLine;
+
+ /** Internal Pixel representation. Used to maintain a uniform API
+ * with Image Adaptors and allow to keep a particular internal
+ * representation of data while showing a different external
+ * representation. */
+ typedef RLLine InternalPixelType;
+
+ //typedef PixelType IOPixelType;
+
+ /** Dimension of the image. This constant is used by functions that are
+ * templated over image type (as opposed to being templated over pixel type
+ * and dimension) when they need compile time access to the dimension of
+ * the image. */
+ itkStaticConstMacro(ImageDimension, unsigned int, VImageDimension);
+
+ /** Index typedef support. An index is used to access pixel values. */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+
+ /** Offset typedef support. An offset is used to access pixel values. */
+ typedef typename Superclass::OffsetType OffsetType;
+
+ /** Size typedef support. A size is used to define region bounds. */
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::SizeValueType SizeValueType;
+
+ /** Direction typedef support. A matrix of direction cosines. */
+ typedef typename Superclass::DirectionType DirectionType;
+
+ /** Region typedef support. A region is used to specify a subset of an image.
+ */
+ typedef typename Superclass::RegionType RegionType;
+
+ /** Spacing typedef support. Spacing holds the size of a pixel. The
+ * spacing is the geometric distance between image samples. */
+ typedef typename Superclass::SpacingType SpacingType;
+ typedef typename Superclass::SpacingValueType SpacingValueType;
+
+ /** Origin typedef support. The origin is the geometric coordinates
+ * of the index (0,0). */
+ typedef typename Superclass::PointType PointType;
+
+ /** Offset typedef (relative position between indices) */
+ typedef typename Superclass::OffsetValueType OffsetValueType;
+
+ /** Allocate the image memory. The size of the image must
+ * already be set, e.g. by calling SetRegions().
+ * Pixel values are initialized using default constructor. */
+ virtual void Allocate(bool initialize = false);
+
+ /** Restore the data object to its initial state. This means releasing
+ * memory. */
+ virtual void Initialize()
+ {
+ // Call the superclass which should initialize the BufferedRegion ivar.
+ Superclass::Initialize();
+ m_OnTheFlyCleanup = true;
+ myBuffer = BufferType::New();
+ }
+
+ /** Fill the image buffer with a value. Be sure to call Allocate()
+ * first. */
+ void FillBuffer(const TPixel & value);
+
+ virtual void SetLargestPossibleRegion(const RegionType & region)
+ {
+ Superclass::SetLargestPossibleRegion(region);
+ myBuffer->SetLargestPossibleRegion(truncateRegion(region));
+ }
+
+ virtual void SetBufferedRegion(const RegionType & region)
+ {
+ Superclass::SetBufferedRegion(region);
+ myBuffer->SetBufferedRegion(truncateRegion(region));
+ }
+
+ virtual void SetRequestedRegion(const RegionType & region)
+ {
+ Superclass::SetRequestedRegion(region);
+ myBuffer->SetRequestedRegion(truncateRegion(region));
+ }
+
+ /** \brief Set a pixel value.
+ *
+ * Allocate() needs to have been called first -- for efficiency,
+ * this function does not check that the image has actually been
+ * allocated yet. SLOW -> Use iterators instead. */
+ void SetPixel(const IndexType & index, const TPixel & value);
+
+ /** Set a pixel value in the given line and updates segmentRemainder
+ * and realIndex to refer to the same pixel.
+ * Returns difference in line length which happens due to merging or splitting segments.
+ * This method is used by iterators directly. */
+ int SetPixel(RLLine & line, IndexValueType & segmentRemainder, IndexValueType & realIndex, const TPixel & value);
+
+ /** \brief Get a pixel. SLOW! Better use iterators for pixel access. */
+ const TPixel & GetPixel(const IndexType & index) const;
+
+ ///** Get a reference to a pixel. Chaning it changes the whole RLE segment! */
+ //TPixel & GetPixel(const IndexType & index);
+
+ ///** \brief Access a pixel. Chaning it changes the whole RLE segment! */
+ //TPixel & operator[](const IndexType & index)
+ //{
+ // return this->GetPixel(index);
+ //}
+
+ /** \brief Access a pixel. This version can only be an rvalue.
+ * SLOW -> Use iterators instead. */
+ const TPixel & operator[](const IndexType & index) const
+ {
+ return this->GetPixel(index);
+ }
+
+ virtual unsigned int GetNumberOfComponentsPerPixel() const
+ {
+ // use the GetLength() method which works with variable length arrays,
+ // to make it work with as much pixel types as possible
+ PixelType p;
+ return itk::NumericTraits< PixelType >::GetLength(p);
+ }
+
+ /** Typedef for the internally used buffer. */
+ typedef typename itk::Image<RLLine, VImageDimension - 1> BufferType;
+
+ /** We need to allow itk-style iterators to be constructed. */
+ typename BufferType::Pointer GetBuffer() { return myBuffer; }
+
+ /** We need to allow itk-style const iterators to be constructed. */
+ typename BufferType::Pointer GetBuffer() const { return myBuffer; }
+
+ /** Returns N-1-dimensional index, the remainder after 0-index is removed. */
+ static inline typename BufferType::IndexType
+ truncateIndex(const IndexType & index);
+
+ /** Returns N-1-dimensional size, the remainder after 0-size is removed. */
+ static inline typename BufferType::SizeType
+ truncateSize(const SizeType & size);
+
+ /** Returns N-1-dimensional region, the remainder after 0-index and size are removed. */
+ static typename BufferType::RegionType
+ truncateRegion(const RegionType & region);
+
+ /** Merges adjacent segments with duplicate values.
+ * Automatically called when turning on OnTheFlyCleanup. */
+ void CleanUp() const;
+
+ /** Should same-valued segments be merged on the fly?
+ * On the fly merging usually provides better performance. */
+ bool GetOnTheFlyCleanup() const { return m_OnTheFlyCleanup; }
+
+ /** Should same-valued segments be merged on the fly?
+ * On the fly merging usually provides better performance. */
+ void SetOnTheFlyCleanup(bool value)
+ {
+ if (value == m_OnTheFlyCleanup)
+ return;
+ m_OnTheFlyCleanup = value;
+ if (m_OnTheFlyCleanup)
+ CleanUp(); //put the image into a clean state
+ }
+
+
+protected:
+ RLEImage() : itk::ImageBase < VImageDimension >()
+ {
+ m_OnTheFlyCleanup = true;
+ myBuffer = BufferType::New();
+ }
+ void PrintSelf(std::ostream & os, itk::Indent indent) const;
+
+ virtual ~RLEImage() {}
+
+ /** Compute helper matrices used to transform Index coordinates to
+ * PhysicalPoint coordinates and back. This method is virtual and will be
+ * overloaded in derived classes in order to provide backward compatibility
+ * behavior in classes that did not used to take image orientation into
+ * account. */
+ virtual void ComputeIndexToPhysicalPointMatrices()
+ {
+ this->Superclass::ComputeIndexToPhysicalPointMatrices();
+ }
+
+ /** Merges adjacent segments with duplicate values in a single line. */
+ void CleanUpLine(RLLine & line) const;
+
+private:
+ bool m_OnTheFlyCleanup; //should same-valued segments be merged on the fly
+
+ RLEImage(const Self &); //purposely not implemented
+ void operator=(const Self &); //purposely not implemented
+
+ /** Memory for the current buffer. */
+ mutable typename BufferType::Pointer myBuffer;
+};
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "RLEImage.txx"
+#endif
+
+#endif //RLEImage_h
diff --git a/Logic/RLEImage/RLEImage.txx b/Logic/RLEImage/RLEImage.txx
new file mode 100644
index 0000000..159cab2
--- /dev/null
+++ b/Logic/RLEImage/RLEImage.txx
@@ -0,0 +1,260 @@
+#ifndef RLEImage_txx
+#define RLEImage_txx
+
+#include "RLEImage.h"
+#include "itkImageRegionConstIterator.h"
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+inline typename RLEImage<TPixel, VImageDimension, CounterType>::BufferType::IndexType
+RLEImage<TPixel, VImageDimension, CounterType>
+::truncateIndex(const IndexType & index)
+{
+ typename BufferType::IndexType result;
+ for (IndexValueType i = 0; i < VImageDimension - 1; i++)
+ result[i] = index[i + 1];
+ return result;
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+inline typename RLEImage<TPixel, VImageDimension, CounterType>::BufferType::SizeType
+RLEImage<TPixel, VImageDimension, CounterType>
+::truncateSize(const SizeType & size)
+{
+ typename BufferType::SizeType result;
+ for (IndexValueType i = 0; i < VImageDimension - 1; i++)
+ result[i] = size[i + 1];
+ return result;
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+typename RLEImage<TPixel, VImageDimension, CounterType>::BufferType::RegionType
+RLEImage<TPixel, VImageDimension, CounterType>
+::truncateRegion(const RegionType & region)
+{
+ typename BufferType::RegionType result;
+ result.SetIndex(truncateIndex(region.GetIndex()));
+ result.SetSize(truncateSize(region.GetSize()));
+ return result;
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RLEImage<TPixel, VImageDimension, CounterType>::Allocate(bool initialize)
+{
+ itkAssertOrThrowMacro(this->GetBufferedRegion().GetSize(0)
+ == this->GetLargestPossibleRegion().GetSize(0),
+ "BufferedRegion must contain complete run-length lines!");
+ itkAssertOrThrowMacro(this->GetLargestPossibleRegion().GetSize(0)
+ <= std::numeric_limits<CounterType>::max(),
+ "CounterType is not large enough to support image's X dimension!");
+ this->ComputeOffsetTable();
+ //SizeValueType num = static_cast<SizeValueType>(this->GetOffsetTable()[VImageDimension]);
+ myBuffer->Allocate(false);
+ //if (initialize) //there is assumption that the image is fully formed after a call to allocate
+ {
+ RLSegment segment(CounterType(this->GetBufferedRegion().GetSize(0)), TPixel());
+ RLLine line(1);
+ line[0] = segment;
+ myBuffer->FillBuffer(line);
+ }
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RLEImage<TPixel, VImageDimension, CounterType>
+::FillBuffer(const TPixel & value)
+{
+ RLSegment segment(CounterType(this->GetBufferedRegion().GetSize(0)), value);
+ RLLine line(1);
+ line[0] = segment;
+ myBuffer->FillBuffer(line);
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RLEImage<TPixel, VImageDimension, CounterType>::CleanUpLine(RLLine & line) const
+{
+ CounterType x = 0;
+ RLLine out;
+ out.reserve(this->GetLargestPossibleRegion().GetSize(0));
+ do
+ {
+ out.push_back(line[x]);
+ while (++x < line.size() && line[x].second == line[x - 1].second)
+ out.back().first += line[x].first;
+ } while (x < line.size());
+ out.swap(line);
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RLEImage<TPixel, VImageDimension, CounterType>::CleanUp() const
+{
+ assert(!myBuffer.empty());
+ if (this->GetLargestPossibleRegion().GetSize(0) == 0)
+ return;
+#pragma omp parallel for
+ for (CounterType z = 0; z < myBuffer.size(); z++)
+ for (CounterType y = 0; y < myBuffer[0].size(); y++)
+ CleanUpLine(myBuffer[z][y]);
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+int RLEImage<TPixel, VImageDimension, CounterType>::
+SetPixel(RLLine & line, IndexValueType & segmentRemainder, IndexValueType & realIndex, const TPixel & value)
+{
+ //complete Run-Length Lines have to be buffered
+ itkAssertOrThrowMacro(this->GetBufferedRegion().GetSize(0)
+ == this->GetLargestPossibleRegion().GetSize(0),
+ "BufferedRegion must contain complete run-length lines!");
+ if (line[realIndex].second == value) //already correct value
+ return 0;
+ else if (line[realIndex].first == 1) //single pixel segment
+ {
+ line[realIndex].second = value;
+ if (m_OnTheFlyCleanup)//now see if we can merge it into adjacent segments
+ {
+ if (realIndex>0 && realIndex < line.size() - 1 &&
+ line[realIndex + 1].second == value && line[realIndex - 1].second == value)
+ {
+ //merge these 3 segments
+ line[realIndex - 1].first += 1 + line[realIndex + 1].first;
+ segmentRemainder += line[realIndex + 1].first;
+ line.erase(line.begin() + realIndex, line.begin() + realIndex + 2);
+ realIndex--;
+ return -2;
+ }
+ if (realIndex>0 && line[realIndex - 1].second == value)
+ {
+ //merge into previous
+ line[realIndex - 1].first++;
+ line.erase(line.begin() + realIndex);
+ realIndex--; assert(segmentRemainder == 1);
+ return -1;
+ }
+ else if (realIndex < line.size() - 1 && line[realIndex + 1].second == value)
+ {
+ //merge into next
+ segmentRemainder = ++(line[realIndex + 1].first);
+ line.erase(line.begin() + realIndex);
+ return -1;
+ }
+ }
+ return 0;
+ }
+ else if (segmentRemainder==1 && realIndex < line.size() - 1 && line[realIndex + 1].second == value)
+ {
+ //shift this pixel to next segment
+ line[realIndex].first--;
+ segmentRemainder = ++(line[realIndex + 1].first);
+ realIndex++;
+ return 0;
+ }
+ else if (realIndex>0 && segmentRemainder == line[realIndex].first && line[realIndex - 1].second == value)
+ {
+ //shift this pixel to previous segment
+ line[realIndex].first--;
+ line[realIndex - 1].first++;
+ realIndex--;
+ segmentRemainder = 1;
+ return 0;
+ }
+ else if (segmentRemainder == 1) //insert after
+ {
+ line[realIndex].first--;
+ line.insert(line.begin() + realIndex + 1, RLSegment(1, value));
+ realIndex++;
+ return +1;
+ }
+ else if (segmentRemainder == line[realIndex].first) //insert before
+ {
+ line[realIndex].first--;
+ line.insert(line.begin() + realIndex, RLSegment(1, value));
+ segmentRemainder = 1;
+ return +1;
+ }
+ else //general case: split a segment into 3 segments
+ {
+ //first take care of values
+ line.insert(line.begin() + realIndex + 1, 2, RLSegment(1, value));
+ line[realIndex + 2].second = line[realIndex].second;
+
+ //now take care of counts
+ line[realIndex].first -= segmentRemainder;
+ line[realIndex + 2].first = segmentRemainder - 1;
+ realIndex++;
+ segmentRemainder = 1;
+ return +2;
+ }
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RLEImage<TPixel, VImageDimension, CounterType>::
+SetPixel(const IndexType & index, const TPixel & value)
+{
+ //complete Run-Length Lines have to be buffered
+ itkAssertOrThrowMacro(this->GetBufferedRegion().GetSize(0)
+ == this->GetLargestPossibleRegion().GetSize(0),
+ "BufferedRegion must contain complete run-length lines!");
+ IndexValueType bri0 = this->GetBufferedRegion().GetIndex(0);
+ typename BufferType::IndexType bi = truncateIndex(index);
+ RLLine & line = myBuffer->GetPixel(bi);
+ IndexValueType t = 0;
+ for (IndexValueType x = 0; x < line.size(); x++)
+ {
+ t += line[x].first;
+ if (t > index[0] - bri0)
+ {
+ t -= index[0] - bri0; //we need to supply a reference
+ SetPixel(line, t, x, value);
+ return;
+ }
+ }
+ throw itk::ExceptionObject(__FILE__, __LINE__, "Reached past the end of Run-Length line!", __FUNCTION__);
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+const TPixel & RLEImage<TPixel, VImageDimension, CounterType>::
+GetPixel(const IndexType & index) const
+{
+ //complete Run-Length Lines have to be buffered
+ itkAssertOrThrowMacro(this->GetBufferedRegion().GetSize(0)
+ == this->GetLargestPossibleRegion().GetSize(0),
+ "BufferedRegion must contain complete run-length lines!");
+ IndexValueType bri0 = this->GetBufferedRegion().GetIndex(0);
+ typename BufferType::IndexType bi = truncateIndex(index);
+ RLLine & line = myBuffer->GetPixel(bi);
+ IndexValueType t = 0;
+ for (IndexValueType x = 0; x < line.size(); x++)
+ {
+ t += line[x].first;
+ if (t > index[0] - bri0)
+ return line[x].second;
+ }
+ throw itk::ExceptionObject(__FILE__, __LINE__, "Reached past the end of Run-Length line!", __FUNCTION__);
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RLEImage<TPixel, VImageDimension, CounterType>
+::PrintSelf(std::ostream & os, itk::Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+ os << indent << "Internal image (for storage of RLLine-s): " << std::endl;
+ myBuffer->Print(os, indent.GetNextIndent());
+
+ itk::SizeValueType c = 0;
+ itk::ImageRegionConstIterator<BufferType> it(myBuffer, myBuffer->GetBufferedRegion());
+ while (!it.IsAtEnd())
+ {
+ c += it.Get().capacity();
+ ++it;
+ }
+
+ double cr = double(c*(sizeof(PixelType) + sizeof(CounterType))
+ + sizeof(std::vector<RLLine>) * this->GetOffsetTable()[VImageDimension] / this->GetOffsetTable()[1])
+ / (this->GetOffsetTable()[VImageDimension] * sizeof(PixelType));
+
+ os << indent << "OnTheFlyCleanup: " << (m_OnTheFlyCleanup ? "On" : "Off") << std::endl;
+ os << indent << "RLEImage compressed pixel count: " << c << std::endl;
+ int prec = os.precision(3);
+ os << indent << "Compressed size in relation to original size: "<< cr*100 <<"%" << std::endl;
+ os.precision(prec);
+}
+
+#endif //RLEImage_txx
diff --git a/Logic/RLEImage/RLEImageConstIterator.h b/Logic/RLEImage/RLEImageConstIterator.h
new file mode 100644
index 0000000..e1332f1
--- /dev/null
+++ b/Logic/RLEImage/RLEImageConstIterator.h
@@ -0,0 +1,407 @@
+#ifndef RLEImageConstIterator_h
+#define RLEImageConstIterator_h
+
+#include "itkImage.h"
+#include "itkIndex.h"
+#include "itkNumericTraits.h"
+#include "RLEImage.h"
+#include "itkImageConstIterator.h"
+#include "itkImageConstIteratorWithIndex.h"
+#include "itkImageConstIteratorWithOnlyIndex.h"
+#include "itkImageRegionIterator.h"
+
+class MultiLabelMeshPipeline;
+
+namespace itk
+{
+/** \class ImageConstIterator
+ * \brief A multi-dimensional image iterator templated over image type.
+ */
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+ friend class ::MultiLabelMeshPipeline;
+public:
+ /** Standard class typedefs. */
+ typedef ImageConstIterator Self;
+
+ /** Dimension of the image the iterator walks. This constant is needed so
+ * functions that are templated over image iterator type (as opposed to
+ * being templated over pixel type and dimension) can have compile time
+ * access to the dimension of the image that the iterator walks. */
+ itkStaticConstMacro(ImageIteratorDimension, unsigned int, VImageDimension);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacroNoParent(ImageConstIterator);
+
+ /** Image typedef support. */
+ typedef RLEImage<TPixel, VImageDimension, CounterType> ImageType;
+
+ /** Run-Length Line (we iterate along it). */
+ typedef typename ImageType::RLLine RLLine;
+
+ /** Buffer Type used. */
+ typedef typename ImageType::BufferType BufferType;
+
+ /** Type for the internal buffer iterator. */
+ typedef ImageRegionIterator<BufferType> BufferIterator;
+
+ /** Index typedef support. */
+ typedef typename ImageType::IndexType IndexType;
+
+ /** Index typedef support. */
+ typedef typename ImageType::IndexValueType IndexValueType;
+
+ /** Size typedef support. */
+ typedef typename ImageType::SizeType SizeType;
+
+ /** Offset typedef support. */
+ typedef typename ImageType::OffsetType OffsetType;
+
+ /** Region typedef support. */
+ typedef typename ImageType::RegionType RegionType;
+
+ /** Internal Pixel Type */
+ typedef typename ImageType::InternalPixelType InternalPixelType;
+
+ /** External Pixel Type */
+ typedef typename ImageType::PixelType PixelType;
+
+ /** Default Constructor. Need to provide a default constructor since we
+ * provide a copy constructor. */
+ ImageConstIterator():
+ myBuffer(0), rlLine(0)
+ {
+ m_Image = ITK_NULLPTR;
+ m_Index0 = 0;
+ m_BeginIndex0 = 0;
+ m_EndIndex0 = 0;
+ realIndex = 0;
+ segmentRemainder = 0;
+ }
+
+ /** Default Destructor. */
+ virtual ~ImageConstIterator() {}
+
+ /** Copy Constructor. The copy constructor is provided to make sure the
+ * handle to the image is properly reference counted. */
+ ImageConstIterator(const Self & it)
+ :myBuffer(const_cast<ImageType *>(it.GetImage())->GetBuffer())
+ {
+ rlLine = it.rlLine;
+ m_Image = it.m_Image; // copy the smart pointer
+ m_Index0 = it.m_Index0;
+ this->bi = it.bi;
+
+ realIndex = it.realIndex;
+ segmentRemainder = it.segmentRemainder;
+ m_BeginIndex0 = it.m_BeginIndex0;
+ m_EndIndex0 = it.m_EndIndex0;
+ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageConstIterator(const ImageType *ptr, const RegionType & region)
+ :myBuffer(const_cast<ImageType *>(ptr)->GetBuffer())
+ {
+ m_Image = ptr;
+ SetRegion(region);
+ }
+
+ /** operator= is provided to make sure the handle to the image is properly
+ * reference counted. */
+ Self & operator=(const Self & it)
+ {
+ if(this != &it)
+ {
+ myBuffer = it.myBuffer;
+ rlLine = it.rlLine;
+ m_Image = it.m_Image; // copy the smart pointer
+ m_Index0 = it.m_Index0;
+ bi = it.bi;
+
+ realIndex = it.realIndex;
+ segmentRemainder = it.segmentRemainder;
+ m_BeginIndex0 = it.m_BeginIndex0;
+ m_EndIndex0 = it.m_EndIndex0;
+ }
+ return *this;
+ }
+
+ /** Set the region of the image to iterate over. */
+ virtual void SetRegion(const RegionType & region)
+ {
+ //m_Region = region;
+
+ if ( region.GetNumberOfPixels() > 0 ) // If region is non-empty
+ {
+ const RegionType & bufferedRegion = m_Image->GetBufferedRegion();
+ itkAssertOrThrowMacro( ( bufferedRegion.IsInside(region) ),
+ "Region " << region << " is outside of buffered region " << bufferedRegion);
+ }
+
+ bi = BufferIterator(myBuffer, ImageType::truncateRegion(region));
+ m_Index0 = region.GetIndex(0);
+ m_BeginIndex0 = m_Index0 - m_Image->GetBufferedRegion().GetIndex(0);
+ m_EndIndex0 = m_BeginIndex0 + region.GetSize(0);
+ SetIndexInternal(m_BeginIndex0); //sets realIndex and segmentRemainder
+ }
+
+ /** Get the dimension (size) of the index. */
+ static unsigned int GetImageIteratorDimension()
+ { return VImageDimension; }
+
+ /** Comparison operator. Two iterators are the same if they "point to" the
+ * same memory location */
+ bool operator!=(const Self & it) const
+ {
+ return bi != it.bi ||
+ m_Index0 + m_BeginIndex0 != it.m_Index0 + it.m_BeginIndex0;
+ }
+
+ /** Comparison operator. Two iterators are the same if they "point to" the
+ * same memory location */
+ bool operator==(const Self & it) const
+ {
+ return bi == it.bi &&
+ m_Index0 + m_BeginIndex0 == it.m_Index0 + it.m_BeginIndex0;
+ }
+
+ /** Comparison operator. An iterator is "less than" another if it "points to"
+ * a lower memory location. */
+ bool operator<=(const Self & it) const
+ {
+ if (bi < it.bi)
+ return true;
+ else if (bi > it.bi)
+ return false;
+ return m_Index0 + m_BeginIndex0 <= it.m_Index0 + it.m_BeginIndex0;
+ }
+
+ /** Comparison operator. An iterator is "less than" another if it "points to"
+ * a lower memory location. */
+ bool operator<(const Self & it) const
+ {
+ if (bi < it.bi)
+ return true;
+ else if (bi > it.bi)
+ return false;
+ return m_Index0 + m_BeginIndex0 < it.m_Index0 + it.m_BeginIndex0;
+ }
+
+ /** Comparison operator. An iterator is "greater than" another if it
+ * "points to" a higher location. */
+ bool operator>=(const Self & it) const
+ {
+ if (bi > it.bi)
+ return true;
+ else if (bi < it.bi)
+ return false;
+ return m_Index0 + m_BeginIndex0 >= it.m_Index0 + it.m_BeginIndex0;
+ }
+
+ /** Comparison operator. An iterator is "greater than" another if it
+ * "points to" a higher location. */
+ bool operator>(const Self & it) const
+ {
+ if (bi > it.bi)
+ return true;
+ else if (bi < it.bi)
+ return false;
+ return m_Index0 + m_BeginIndex0 > it.m_Index0 + it.m_BeginIndex0;
+ }
+
+ /** Get the index. This provides a read only copy of the index. */
+ const IndexType GetIndex() const
+ {
+ IndexType indR(m_Image->GetBufferedRegion().GetIndex());
+ indR[0] += m_Index0;
+ typename BufferType::IndexType bufInd = bi.GetIndex();
+ for (IndexValueType i = 1; i < VImageDimension; i++)
+ indR[i] = bufInd[i - 1];
+ return indR;
+ }
+
+ /** Sets the image index. No bounds checking is performed. */
+ virtual void SetIndex(const IndexType & ind)
+ {
+ typename BufferType::IndexType bufInd;
+ for (IndexValueType i = 1; i < VImageDimension; i++)
+ bufInd[i - 1] = ind[i];
+ bi.SetIndex(bufInd);
+ SetIndexInternal(ind[0] - m_Image->GetBufferedRegion().GetIndex(0));
+ }
+
+ /** Get the region that this iterator walks. ImageConstIterators know the
+ * beginning and the end of the region of the image to iterate over. */
+ const RegionType GetRegion() const
+ {
+ RegionType r;
+ r.SetIndex(0, m_BeginIndex0 + m_Image->GetBufferedRegion().GetIndex(0));
+ r.SetSize(0, m_EndIndex0 - m_BeginIndex0);
+ typename BufferType::RegionType ir = bi.GetRegion();
+ for (IndexValueType i = 1; i < VImageDimension; i++)
+ {
+ r.SetIndex(i, ir.GetIndex(i - 1));
+ r.SetSize(i, ir.GetSize(i - 1));
+ }
+ return r;
+ }
+
+ /** Get the image that this iterator walks. */
+ const ImageType * GetImage() const
+ { return m_Image.GetPointer(); }
+
+ /** Get the pixel value */
+ PixelType Get(void) const
+ { return Value(); }
+
+ /** Return a const reference to the pixel
+ * This method will provide the fastest access to pixel
+ * data, but it will NOT support ImageAdaptors. */
+ const PixelType & Value(void) const
+ {
+ RLLine & line = const_cast<Self *>(this)->bi.Value();
+ return line[realIndex].second;
+ }
+
+ /** Move an iterator to the beginning of the region. "Begin" is
+ * defined as the first pixel in the region. */
+ void GoToBegin()
+ {
+ bi.GoToBegin();
+ SetIndexInternal(m_BeginIndex0);
+ }
+
+ /** Move an iterator to the end of the region. "End" is defined as
+ * one pixel past the last pixel of the region. */
+ void GoToEnd()
+ {
+ bi.GoToEnd();
+ m_Index0 = m_BeginIndex0;
+ }
+
+ /** Is the iterator at the beginning of the region? "Begin" is defined
+ * as the first pixel in the region. */
+ bool IsAtBegin(void) const
+ {
+ return m_Index0 == m_BeginIndex0 && bi.IsAtBegin();
+ }
+
+ /** Is the iterator at the end of the region? "End" is defined as one
+ * pixel past the last pixel of the region. */
+ bool IsAtEnd(void) const
+ {
+ return m_Index0 == m_BeginIndex0 && bi.IsAtEnd();
+ }
+
+protected: //made protected so other iterators can access
+
+ /** Set the internal index, realIndex and segmentRemainder. */
+ virtual void SetIndexInternal(const IndexValueType ind0)
+ {
+ m_Index0 = ind0;
+ rlLine = &bi.Value();
+
+ CounterType t = 0;
+ SizeValueType x = 0;
+
+ for (; x < (*rlLine).size(); x++)
+ {
+ t += (*rlLine)[x].first;
+ if (t > m_Index0)
+ break;
+ }
+ realIndex = x;
+ segmentRemainder = t - m_Index0;
+ }
+
+ typename ImageType::ConstWeakPointer m_Image;
+
+ IndexValueType m_Index0; //index into the RLLine
+
+ const RLLine * rlLine;
+
+ mutable IndexValueType realIndex; // index into line's segment
+ mutable IndexValueType segmentRemainder; // how many pixels remain in current segment
+
+ IndexValueType m_BeginIndex0; // index to first pixel in region in relation to buffer start
+ IndexValueType m_EndIndex0; // index to one pixel past last pixel in region in relation to buffer start
+
+ BufferIterator bi; //iterator over internal buffer image
+ typename BufferType::Pointer myBuffer;
+};
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageConstIteratorWithIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageConstIterator < RLEImage<TPixel, VImageDimension, CounterType> >
+{
+ //just inherit constructors
+public:
+
+ /** Image typedef support. */
+ typedef RLEImage<TPixel, VImageDimension, CounterType> ImageType;
+
+ typedef typename itk::ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >::RegionType RegionType;
+
+ void GoToReverseBegin()
+ {
+ this->bi.GoToReverseBegin();
+ this->m_Index0 = this->m_EndIndex0 - 1;
+ SetIndexInternal(this->m_Index0);
+ }
+
+ bool IsAtReverseEnd()
+ { return this->bi.IsAtReverseEnd(); }
+
+ /** Default Constructor. Need to provide a default constructor since we
+ * provide a copy constructor. */
+ ImageConstIteratorWithIndex() :ImageConstIterator< ImageType >(){ }
+
+ /** Copy Constructor. The copy constructor is provided to make sure the
+ * handle to the image is properly reference counted. */
+ ImageConstIteratorWithIndex(const ImageConstIteratorWithIndex & it)
+ {
+ this->ImageConstIterator< ImageType >::operator=(it);
+ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageConstIteratorWithIndex(const ImageType *ptr, const RegionType & region)
+ :ImageConstIterator< ImageType >(ptr, region) { }
+}; //no additional implementation required
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageConstIteratorWithOnlyIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageConstIteratorWithIndex < RLEImage<TPixel, VImageDimension, CounterType> >
+{
+ //just inherit constructors
+public:
+
+ /** Image typedef support. */
+ typedef RLEImage<TPixel, VImageDimension, CounterType> ImageType;
+
+ typedef typename itk::ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >::RegionType RegionType;
+
+ /** Default Constructor. Need to provide a default constructor since we
+ * provide a copy constructor. */
+ ImageConstIteratorWithOnlyIndex() :ImageConstIterator< ImageType >(){ }
+
+
+ /** Copy Constructor. The copy constructor is provided to make sure the
+ * handle to the image is properly reference counted. */
+ ImageConstIteratorWithOnlyIndex(const ImageConstIteratorWithOnlyIndex & it)
+ {
+ this->ImageConstIterator< ImageType >::operator=(it);
+ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageConstIteratorWithOnlyIndex(const ImageType *ptr, const RegionType & region)
+ :ImageConstIterator< ImageType >(ptr, region) { }
+}; //no additional implementation required
+
+} // end namespace itk
+
+#endif //RLEImageConstIterator_h
diff --git a/Logic/RLEImage/RLEImageIterator.h b/Logic/RLEImage/RLEImageIterator.h
new file mode 100644
index 0000000..a725035
--- /dev/null
+++ b/Logic/RLEImage/RLEImageIterator.h
@@ -0,0 +1,154 @@
+#ifndef RLEImageIterator_h
+#define RLEImageIterator_h
+
+#include "RLEImageConstIterator.h"
+#include "itkImageIteratorWithIndex.h"
+
+namespace itk
+{
+/**
+ * \class ImageIterator
+ * \brief A multi-dimensional iterator templated over image type.
+ *
+ * This is a base class of ImageConstIterator that adds write-access
+ * functionality. Please see ImageConstIterator for more information.
+ *
+ */
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+ /** Standard class typedefs. */
+ typedef ImageIterator Self;
+
+ /** Dimension of the image the iterator walks. This constant is needed so
+ * functions that are templated over image iterator type (as opposed to
+ * being templated over pixel type and dimension) can have compile time
+ * access to the dimension of the image that the iterator walks. */
+ itkStaticConstMacro(ImageIteratorDimension, unsigned int, VImageDimension);
+
+ /** Define the superclass */
+ typedef ImageConstIterator< RLEImage<TPixel, VImageDimension, CounterType> > Superclass;
+
+ /** Inherit types from the superclass */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::OffsetType OffsetType;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+ typedef typename Superclass::PixelType PixelType;
+
+ /** Default Constructor. Need to provide a default constructor since we
+ * provide a copy constructor. */
+ ImageIterator(){}
+
+ /** Default Destructor */
+ ~ImageIterator() {}
+
+ /** Copy Constructor. The copy constructor is provided to make sure the
+ * handle to the image is properly reference counted. */
+ ImageIterator(const Self & it) :
+ ImageConstIterator<ImageType>(it) {}
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageIterator(ImageType *ptr, const RegionType & region):
+ ImageConstIterator<ImageType>(ptr, region){}
+
+ /** operator= is provided to make sure the handle to the image is properly
+ * reference counted. */
+ Self & operator=(const Self & it)
+ {
+ this->ImageConstIterator<ImageType>::operator=(it);
+ return *this;
+ }
+
+ /** Set the pixel value.
+ * Changing the RLE structure invalidates all other iterators (except this one). */
+ void Set(const PixelType & value) const
+ {
+ const_cast<ImageType *>(this->m_Image.GetPointer())->
+ SetPixel(*const_cast<typename ImageType::RLLine *>(this->rlLine),
+ this->segmentRemainder, this->realIndex, value);
+ }
+
+ ///** Return a reference to the pixel
+ // * Setting this value would change value of the whole run-length segment.
+ // * If we wanted to safely enable it,
+ // * we would isolate this pixel into its own segment. */
+ //PixelType & Value(void)
+ //{
+ // return myBuffer[m_Index[2]][m_Index[1]][realIndex].second;
+ //}
+
+ /** Get the image that this iterator walks. */
+ ImageType * GetImage() const
+ {
+ // const_cast is needed here because m_Image is declared as a const pointer
+ // in the base class which is the ConstIterator.
+ return const_cast< ImageType * >( this->m_Image.GetPointer() );
+ }
+
+protected:
+
+ /** This constructor is declared protected in order to enforce
+ const-correctness */
+ ImageIterator(const ImageConstIterator< ImageType > & it) :
+ ImageConstIterator<ImageType>(it) {}
+ Self & operator=(const ImageConstIterator< ImageType > & it)
+ {
+ this->ImageConstIterator< ImageType>::operator=(it);
+ return *this;
+ }
+};
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageIteratorWithIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageConstIteratorWithIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+
+ typedef RLEImage<TPixel, VImageDimension, CounterType> ImageType;
+
+ typedef typename itk::ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >::RegionType RegionType;
+
+ /** Default Constructor. Need to provide a default constructor since we
+ * provide a copy constructor. */
+ ImageIteratorWithIndex() :ImageConstIteratorWithIndex< ImageType >(){ }
+
+
+ /** Copy Constructor. The copy constructor is provided to make sure the
+ * handle to the image is properly reference counted. */
+ ImageIteratorWithIndex(const ImageIteratorWithIndex & it)
+ {
+ this->ImageIterator< ImageType >::operator=(it);
+ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageIteratorWithIndex(const ImageType *ptr, const RegionType & region)
+ :ImageConstIteratorWithIndex< ImageType >(ptr, region) { }
+
+ /** Set the pixel value.
+ * Changing the RLE structure invalidates all other iterators (except this one). */
+ void Set(const TPixel & value) const
+ {
+ const_cast<ImageType *>(this->m_Image.GetPointer())->
+ SetPixel(*const_cast<typename ImageType::RLLine *>(this->rlLine),
+ this->segmentRemainder, this->realIndex, value);
+ }
+
+ /** Get the image that this iterator walks. */
+ ImageType * GetImage() const
+ {
+ // const_cast is needed here because m_Image is declared as a const pointer
+ // in the base class which is the ConstIterator.
+ return const_cast< ImageType * >(this->m_Image.GetPointer());
+ }
+}; //no additional implementation required
+} // end namespace itk
+
+#endif //RLEImageIterator_h
diff --git a/Logic/RLEImage/RLEImageRegionConstIterator.h b/Logic/RLEImage/RLEImageRegionConstIterator.h
new file mode 100644
index 0000000..7d46d74
--- /dev/null
+++ b/Logic/RLEImage/RLEImageRegionConstIterator.h
@@ -0,0 +1,220 @@
+#ifndef RLEImageRegionConstIterator_h
+#define RLEImageRegionConstIterator_h
+
+#include "RLEImageConstIterator.h"
+#include "itkImageRegionConstIterator.h"
+#include "itkImageRegionConstIteratorWithIndex.h"
+#include "itkImageRegionConstIteratorWithOnlyIndex.h"
+
+class MultiLabelMeshPipeline;
+
+namespace itk
+{
+/** \class ImageRegionConstIterator
+ * \brief A multi-dimensional iterator templated over image type that walks a
+ * region of pixels.
+ *
+ * ImageRegionConstIterator provides read-only access to image data. It is the
+ * base class for the read/write access ImageRegionIterator.
+ *
+ */
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageRegionConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+ friend class ::MultiLabelMeshPipeline;
+public:
+ /** Standard class typedef. */
+ typedef ImageRegionConstIterator<RLEImage<TPixel, VImageDimension, CounterType> > Self;
+ typedef ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> > Superclass;
+
+ /** Dimension of the image that the iterator walks. This constant is needed so
+ * functions that are templated over image iterator type (as opposed to
+ * being templated over pixel type and dimension) can have compile time
+ * access to the dimension of the image that the iterator walks. */
+ itkStaticConstMacro(ImageIteratorDimension, unsigned int, VImageDimension);
+
+ /**
+ * Index typedef support. While these were already typdef'ed in the superclass,
+ * they need to be redone here for this subclass to compile properly with gcc.
+ */
+ /** Types inherited from the Superclass */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::OffsetType OffsetType;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+ typedef typename Superclass::PixelType PixelType;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(ImageRegionConstIterator, ImageConstIterator);
+
+ /** Default constructor. Needed since we provide a cast constructor. */
+ ImageRegionConstIterator() :ImageConstIterator< ImageType >(){ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageRegionConstIterator(const ImageType *ptr, const RegionType & region):
+ ImageConstIterator< ImageType >(ptr, region) { }
+
+ /** Constructor that can be used to cast from an ImageIterator to an
+ * ImageRegionConstIterator. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageRegionConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageRegionConstIterator. */
+ ImageRegionConstIterator(const ImageIterator< ImageType > & it)
+ {
+ this->ImageConstIterator< ImageType >::operator=(it);
+ //this->ImageConstIterator< ImageType >::operator=(static_cast<const ImageConstIterator<ImageType> >(it));
+ }
+
+ /** Constructor that can be used to cast from an ImageConstIterator to an
+ * ImageRegionConstIterator. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageRegionConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageRegionConstIterator. */
+ ImageRegionConstIterator(const ImageConstIterator< ImageType > & it)
+ {
+ this->ImageConstIterator< ImageType >::operator=(it);
+ }
+
+ /** Increment (prefix) the fastest moving dimension of the iterator's index.
+ * This operator will constrain the iterator within the region (i.e. the
+ * iterator will automatically wrap from the end of the row of the region
+ * to the beginning of the next row of the region) up until the iterator
+ * tries to moves past the last pixel of the region. Here, the iterator
+ * will be set to be one pixel past the end of the region.
+ * \sa operator++(int) */
+ Self &
+ operator++()
+ {
+ this->m_Index0++;
+
+ if (this->m_Index0 >= this->m_EndIndex0)
+ {
+ ++(this->bi);
+ if (!this->bi.IsAtEnd())
+ this->SetIndexInternal(this->m_BeginIndex0);
+ else
+ this->m_Index0 = this->m_BeginIndex0;
+ return *this;
+ }
+
+ this->segmentRemainder--;
+ if (this->segmentRemainder > 0)
+ return *this;
+
+ this->realIndex++;
+ this->segmentRemainder = (*this->rlLine)[this->realIndex].first;
+ return *this;
+ }
+
+ /** Decrement (prefix) the fastest moving dimension of the iterator's index.
+ * This operator will constrain the iterator within the region (i.e. the
+ * iterator will automatically wrap from the beginning of the row of the region
+ * to the end of the next row of the region) up until the iterator
+ * tries to moves past the first pixel of the region. Here, the iterator
+ * will be set to be one pixel past the beginning of the region.
+ * \sa operator--(int) */
+ Self & operator--()
+ {
+ this->m_Index0--;
+
+ if (this->m_Index0 < this->m_BeginIndex0)
+ {
+ --(this->bi);
+ this->SetIndexInternal(this->m_EndIndex0 - 1);
+ return *this;
+ }
+
+ this->segmentRemainder++;
+ if (this->segmentRemainder <= (*this->rlLine)[this->realIndex].first)
+ return *this;
+
+ this->realIndex--;
+ this->segmentRemainder = 1;
+ return *this;
+ }
+};
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageRegionConstIteratorWithIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageRegionConstIterator < RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+ typedef RLEImage<TPixel, VImageDimension, CounterType> ImageType;
+
+ typedef typename itk::ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >::RegionType RegionType;
+
+ /** Default constructor. Needed since we provide a cast constructor. */
+ ImageRegionConstIteratorWithIndex() :ImageRegionConstIterator< ImageType >(){ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageRegionConstIteratorWithIndex(const ImageType *ptr, const RegionType & region) :
+ ImageRegionConstIterator< ImageType >(ptr, region) { }
+
+ void GoToReverseBegin()
+ {
+ this->bi.GoToEnd(); //after last pixel
+ --(this->bi); //go to last valid pixel
+ this->m_Index0 = this->m_EndIndex0 - 1;
+ this->SetIndexInternal(this->m_Index0); //valid index required
+ }
+
+ bool IsAtReverseEnd()
+ {
+ return (this->m_Index0 == this->m_BeginIndex0) && this->bi.IsAtBegin();
+ }
+
+ /** Constructor that can be used to cast from an ImageIterator to an
+ * ImageRegionConstIterator. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageRegionConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageRegionConstIterator. */
+ ImageRegionConstIteratorWithIndex(const ImageIterator< ImageType > & it)
+ {
+ this->ImageRegionConstIterator< ImageType >::operator=(it);
+ }
+
+}; //no additional implementation required
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageRegionConstIteratorWithOnlyIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageRegionConstIteratorWithIndex < RLEImage<TPixel, VImageDimension, CounterType> >
+{
+ //just inherit constructors
+public:
+
+ typedef RLEImage<TPixel, VImageDimension, CounterType> ImageType;
+
+ typedef typename itk::ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >::RegionType RegionType;
+
+ /** Default constructor. Needed since we provide a cast constructor. */
+ ImageRegionConstIteratorWithOnlyIndex() :ImageRegionConstIterator< ImageType >(){ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageRegionConstIteratorWithOnlyIndex(const ImageType *ptr, const RegionType & region) :
+ ImageRegionConstIteratorWithIndex< ImageType >(ptr, region) { }
+
+ /** Constructor that can be used to cast from an ImageIterator to an
+ * ImageRegionConstIterator. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageRegionConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageRegionConstIterator. */
+ ImageRegionConstIteratorWithOnlyIndex(const ImageIterator< ImageType > & it)
+ {
+ this->ImageRegionConstIterator< ImageType >::operator=(it);
+ }
+
+}; //no additional implementation required
+
+} // end namespace itk
+
+#endif //RLEImageRegionConstIterator_h
diff --git a/Logic/RLEImage/RLEImageRegionIterator.h b/Logic/RLEImage/RLEImageRegionIterator.h
new file mode 100644
index 0000000..dda0f3e
--- /dev/null
+++ b/Logic/RLEImage/RLEImageRegionIterator.h
@@ -0,0 +1,139 @@
+#ifndef RLEImageRegionIterator_h
+#define RLEImageRegionIterator_h
+
+#include "RLEImageRegionConstIterator.h"
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "RLEImageIterator.h"
+
+namespace itk
+{
+/** \class ImageRegionIterator
+* \brief A multi-dimensional iterator templated over image type that walks a
+* region of pixels.
+*
+* The itk::ImageRegionIterator is optimized for iteration speed and is the
+* first choice for iterative, pixel-wise operations on an image.
+* ImageRegionIterator is the least specialized of the ITK image iterator
+* classes. ImageRegionIterator is templated over the image type, and is
+* constrained to walk only within the specified region and along a line
+* parallel to one of the coordinate axes, "wrapping" to the next line as it
+* reaches the boundary of the image. To walk the entire image, specify the
+* region to be \c image->GetRequestedRegion().
+*
+* Most of the functionality is inherited from the ImageRegionConstIterator.
+* The current class only adds write access to image pixels.
+*/
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageRegionIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageRegionConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+ /** Standard class typedefs. */
+ typedef ImageRegionIterator Self;
+ typedef ImageRegionConstIterator<RLEImage<TPixel, VImageDimension, CounterType> > Superclass;
+
+ /** Types inherited from the Superclass */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::OffsetType OffsetType;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+ typedef typename Superclass::PixelType PixelType;
+
+ /** Default constructor. Needed since we provide a cast constructor. */
+ ImageRegionIterator() :ImageRegionConstIterator< ImageType >(){ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageRegionIterator(ImageType *ptr, const RegionType & region)
+ :ImageRegionConstIterator< ImageType >(ptr, region) { }
+
+ /** Constructor that can be used to cast from an ImageIterator to an
+ * ImageRegionIterator. Many routines return an ImageIterator but for a
+ * particular task, you may want an ImageRegionIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageRegionIterator. */
+ ImageRegionIterator(const ImageIterator< ImageType > & it)
+ {
+ this->ImageConstIterator< ImageType >::operator=(it);
+ }
+
+ /** Set the pixel value.
+ * Changing the RLE structure invalidates all other iterators (except this one). */
+ void Set(const PixelType & value) const
+ {
+ const_cast<ImageType *>(this->m_Image.GetPointer())->
+ SetPixel(*const_cast<typename ImageType::RLLine *>(this->rlLine),
+ this->segmentRemainder, this->realIndex, value);
+ }
+
+protected:
+ /** the construction from a const iterator is declared protected
+ in order to enforce const correctness. */
+ ImageRegionIterator(const ImageRegionConstIterator< ImageType > & it)
+ {
+ this->ImageConstIterator< ImageType >::operator=(it);
+ }
+ Self & operator=(const ImageRegionConstIterator< ImageType > & it)
+ {
+ this->ImageConstIterator< ImageType >::operator=(it);
+ }
+};
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageRegionIteratorWithIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageRegionConstIteratorWithIndex<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+
+ typedef RLEImage<TPixel, VImageDimension, CounterType> ImageType;
+
+ typedef typename itk::ImageConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >::RegionType RegionType;
+
+ /** Default constructor. Needed since we provide a cast constructor. */
+ ImageRegionIteratorWithIndex() :ImageRegionConstIteratorWithIndex< ImageType >(){ }
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageRegionIteratorWithIndex(ImageType *ptr, const RegionType & region) :
+ ImageRegionConstIteratorWithIndex< ImageType >(ptr, region) { }
+
+ /** Set the pixel value.
+ * Changing the RLE structure invalidates all other iterators (except this one). */
+ void Set(const TPixel & value) const
+ {
+ const_cast<ImageType *>(this->m_Image.GetPointer())->
+ SetPixel(*const_cast<typename ImageType::RLLine *>(this->rlLine),
+ this->segmentRemainder, this->realIndex, value);
+ }
+
+ /** Constructor that can be used to cast from an ImageIterator to an
+ * ImageRegionIteratorWithIndex. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageRegionConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageRegionConstIterator. */
+ ImageRegionIteratorWithIndex(const ImageIterator< ImageType > & it)
+ {
+ this->ImageRegionConstIteratorWithIndex< ImageType >::operator=(it);
+ }
+
+ /** Constructor that can be used to cast from an ImageConstIterator to an
+ * ImageRegionIteratorWithIndex. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageRegionConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageRegionIteratorWithIndex. */
+ ImageRegionIteratorWithIndex(const ImageConstIterator< ImageType > & it)
+ {
+ this->ImageRegionConstIterator< ImageType >::operator=(it);
+ }
+
+}; //no additional implementation required
+} // end namespace itk
+
+#endif //RLEImageRegionIterator_h
diff --git a/Logic/RLEImage/RLEImageScanlineConstIterator.h b/Logic/RLEImage/RLEImageScanlineConstIterator.h
new file mode 100644
index 0000000..6176927
--- /dev/null
+++ b/Logic/RLEImage/RLEImageScanlineConstIterator.h
@@ -0,0 +1,144 @@
+#ifndef RLEImageScanlineConstIterator_h
+#define RLEImageScanlineConstIterator_h
+
+#include "RLEImageRegionConstIterator.h"
+#include "itkImageScanlineIterator.h"
+
+namespace itk
+{
+/** \class ImageScanlineConstIterator
+* \brief A multi-dimensional iterator templated over image type that walks a
+* region of pixels, scanline by scanline or in the direction of the
+* fastest axis.
+*/
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageScanlineConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageRegionConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+ /** Standard class typedef. */
+ typedef ImageScanlineConstIterator Self;
+ typedef ImageRegionConstIterator< RLEImage<TPixel, VImageDimension, CounterType> > Superclass;
+
+ /** Dimension of the image that the iterator walks. This constant is needed so
+ * functions that are templated over image iterator type (as opposed to
+ * being templated over pixel type and dimension) can have compile time
+ * access to the dimension of the image that the iterator walks. */
+ itkStaticConstMacro(ImageIteratorDimension, unsigned int, VImageDimension);
+
+ /**
+ * Index typedef support. While these were already typdef'ed in the superclass,
+ * they need to be redone here for this subclass to compile properly with gcc.
+ */
+ /** Types inherited from the Superclass */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::OffsetType OffsetType;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+ typedef typename Superclass::PixelType PixelType;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(ImageScanlineConstIterator, ImageRegionConstIterator);
+
+ /** Default constructor. Needed since we provide a cast constructor. */
+ ImageScanlineConstIterator()
+ :ImageRegionConstIterator< ImageType >() {}
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageScanlineConstIterator(const ImageType *ptr, const RegionType & region) :
+ ImageRegionConstIterator< ImageType >(ptr, region) {}
+
+ /** Constructor that can be used to cast from an ImageIterator to an
+ * ImageScanlineConstIterator. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageScanlineConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageScanlineConstIterator. */
+ ImageScanlineConstIterator(const ImageIterator< ImageType > & it)
+ :ImageRegionConstIterator< ImageType >(it) {}
+
+ /** Constructor that can be used to cast from an ImageConstIterator to an
+ * ImageScanlineConstIterator. Many routines return an ImageIterator, but for a
+ * particular task, you may want an ImageScanlineConstIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageScanlineConstIterator. */
+ ImageScanlineConstIterator(const ImageConstIterator< ImageType > & it)
+ { this->ImageRegionConstIterator< ImageType >::operator=(it); }
+
+
+ /** Go to the beginning pixel of the current line. */
+ void GoToBeginOfLine(void)
+ {
+ this->m_Index0 = this->m_BeginIndex0;
+ this->realIndex = 0;
+ this->segmentRemainder = (*this->rlLine)[this->realIndex].first;
+ }
+
+ /** Go to the past end pixel of the current line. */
+ void GoToEndOfLine(void)
+ {
+ this->m_Index0 = this->m_EndIndex0;
+ this->realIndex = this->rlLine->size() - 1;
+ this->segmentRemainder = 0;
+ }
+
+ /** Test if the index is at the end of line. */
+ inline bool IsAtEndOfLine(void)
+ {
+ return this->m_Index0 == this->m_EndIndex0;
+ }
+
+ /** Go to the next line. */
+ inline void NextLine(void)
+ {
+ ++(this->bi);
+ if (!this->bi.IsAtEnd())
+ this->SetIndexInternal(this->m_BeginIndex0);
+ else
+ this->m_Index0 = this->m_BeginIndex0; //make this iterator at end too
+ }
+
+ /** Increment (prefix) along the scanline.
+ *
+ * If the iterator is at the end of the scanline ( one past the last
+ * valid element in the row ), then the results are undefined. Which
+ * means is may assert in debug mode or result in an undefined
+ * iterator which may have unknown consequences if used.
+ */
+ Self& operator++()
+ {
+ itkAssertInDebugAndIgnoreInReleaseMacro(!this->IsAtEndOfLine());
+ this->m_Index0++;
+ this->segmentRemainder--;
+ if (this->segmentRemainder > 0)
+ return *this;
+
+ if (this->IsAtEndOfLine())
+ return *this;
+ this->realIndex++;
+ this->segmentRemainder = (*this->rlLine)[this->realIndex].first;
+ return *this;
+ }
+
+ /** Decrement (prefix) along the scanline.
+ *
+ */
+ Self& operator--()
+ {
+ this->m_Index0--;
+ this->segmentRemainder++;
+ if (this->segmentRemainder <= (*this->rlLine)[this->realIndex].first)
+ return *this;
+
+ this->realIndex--;
+ this->segmentRemainder = 1;
+ return *this;
+ }
+};
+} // end namespace itk
+
+#endif //RLEImageScanlineConstIterator_h
diff --git a/Logic/RLEImage/RLEImageScanlineIterator.h b/Logic/RLEImage/RLEImageScanlineIterator.h
new file mode 100644
index 0000000..d1bf564
--- /dev/null
+++ b/Logic/RLEImage/RLEImageScanlineIterator.h
@@ -0,0 +1,80 @@
+#ifndef RLEImageScanlineIterator_h
+#define RLEImageScanlineIterator_h
+
+#include "RLEImageScanlineConstIterator.h"
+#include "RLEImageIterator.h"
+#include "itkImageScanlineIterator.h"
+
+namespace itk
+{
+/** \class ImageScanlineIterator
+* \brief A multi-dimensional iterator templated over image type that walks a
+* region of pixels, scanline by scanline or in the direction of the
+* fastest axis.
+*/
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class ImageScanlineIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+ :public ImageScanlineConstIterator<RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+ /** Standard class typedefs. */
+ typedef ImageScanlineIterator Self;
+ typedef ImageScanlineConstIterator<RLEImage<TPixel, VImageDimension, CounterType> > Superclass;
+
+ /** Types inherited from the Superclass */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::OffsetType OffsetType;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+ typedef typename Superclass::PixelType PixelType;
+
+ /** Default constructor. Needed since we provide a cast constructor. */
+ ImageScanlineIterator()
+ :ImageScanlineConstIterator< ImageType >() {}
+
+ /** Constructor establishes an iterator to walk a particular image and a
+ * particular region of that image. */
+ ImageScanlineIterator(ImageType *ptr, const RegionType & region)
+ :ImageScanlineConstIterator< ImageType >(ptr, region) {}
+
+ /** Constructor that can be used to cast from an ImageIterator to an
+ * ImageScanlineIterator. Many routines return an ImageIterator but for a
+ * particular task, you may want an ImageScanlineIterator. Rather than
+ * provide overloaded APIs that return different types of Iterators, itk
+ * returns ImageIterators and uses constructors to cast from an
+ * ImageIterator to a ImageScanlineIterator. */
+ ImageScanlineIterator(const ImageIterator< ImageType > & it)
+ :ImageScanlineConstIterator< ImageType >(it) {}
+
+ /** Set the pixel value */
+ void Set(const PixelType & value) const
+ {
+ const_cast<ImageType *>(this->m_Image.GetPointer())->
+ SetPixel(*const_cast<typename ImageType::RLLine *>(this->rlLine),
+ this->segmentRemainder, this->realIndex, value);
+ }
+
+ ///** Return a reference to the pixel
+ //* This method will provide the fastest access to pixel
+ //* data, but it will NOT support ImageAdaptors. */
+ //PixelType & Value(void)
+ //{
+ // return myBuffer[m_Index[2]][m_Index[1]][realIndex].second;
+ //}
+
+protected:
+ /** the construction from a const iterator is declared protected
+ in order to enforce const correctness. */
+ ImageScanlineIterator(const ImageScanlineConstIterator< ImageType > & it)
+ :ImageScanlineConstIterator< ImageType >(it) {}
+ Self & operator=(const ImageScanlineConstIterator< ImageType > & it)
+ {
+ this->ImageScanlineConstIterator< ImageType >::operator=(it);
+ return *this;
+ }
+};
+} // end namespace itk
+
+#endif //RLEImageScanlineIterator_h
\ No newline at end of file
diff --git a/Logic/RLEImage/RLERegionOfInterestImageFilter.h b/Logic/RLEImage/RLERegionOfInterestImageFilter.h
new file mode 100644
index 0000000..d708584
--- /dev/null
+++ b/Logic/RLEImage/RLERegionOfInterestImageFilter.h
@@ -0,0 +1,282 @@
+#ifndef RLERegionOfInterestImageFilter_h
+#define RLERegionOfInterestImageFilter_h
+
+#include "itkImageToImageFilter.h"
+#include "itkSmartPointer.h"
+#include "itkRegionOfInterestImageFilter.h"
+#include "RLEImage.h"
+
+namespace itk
+{
+/** \class RegionOfInterestImageFilter
+ * \brief Extract a region of interest from the input image
+ * or convert between itk::Image and RLEImage (a custom region can be used).
+ *
+ * This filter produces an output image of the same dimension as the input
+ * image. The user specifies the region of the input image that will be
+ * contained in the output image. The origin coordinates of the output images
+ * will be computed in such a way that if mapped to physical space, the output
+ * image will overlay the input image with perfect registration. In other
+ * words, a registration process between the output image and the input image
+ * will return an identity transform.
+ *
+ * The region to extract is set using the method SetRegionOfInterest.
+ */
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>, RLEImage<TPixel, VImageDimension, CounterType> >:
+ public ImageToImageFilter< RLEImage<TPixel, VImageDimension, CounterType>, RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+ /** Standard class typedefs. */
+ typedef RegionOfInterestImageFilter Self;
+ typedef RLEImage<TPixel, VImageDimension, CounterType> RLEImageType;
+ typedef RLEImageType ImageType;
+ typedef ImageToImageFilter< RLEImageType, RLEImageType > Superclass;
+ typedef SmartPointer< Self > Pointer;
+ typedef SmartPointer< const Self > ConstPointer;
+ typedef typename Superclass::InputImageRegionType InputImageRegionType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(RegionOfInterestImageFilter, ImageToImageFilter);
+
+ /** Typedef to describe the input image region types. */
+ typedef typename RLEImageType::RegionType RegionType;
+ typedef typename RLEImageType::IndexType IndexType;
+ typedef typename RLEImageType::SizeType SizeType;
+
+ /** Typedef to describe the type of pixel. */
+ typedef typename RLEImageType::PixelType OutputImagePixelType;
+ typedef typename RLEImageType::PixelType InputImagePixelType;
+
+ /** Set/Get the output image region. */
+ itkSetMacro(RegionOfInterest, RegionType);
+ itkGetConstMacro(RegionOfInterest, RegionType);
+
+ /** ImageDimension enumeration */
+ itkStaticConstMacro(ImageDimension, unsigned int, VImageDimension);
+ itkStaticConstMacro(OutputImageDimension, unsigned int, VImageDimension);
+
+#ifdef ITK_USE_CONCEPT_CHECKING
+ // Begin concept checking
+ itkConceptMacro( SameDimensionCheck,
+ ( Concept::SameDimension< ImageDimension, OutputImageDimension > ) );
+ itkConceptMacro( InputConvertibleToOutputCheck,
+ ( Concept::Convertible< InputImagePixelType, OutputImagePixelType > ) );
+ // End concept checking
+#endif
+
+protected:
+ RegionOfInterestImageFilter() {}
+ ~RegionOfInterestImageFilter() {}
+ void PrintSelf(std::ostream & os, Indent indent) const;
+
+ virtual void GenerateInputRequestedRegion();
+
+ virtual void EnlargeOutputRequestedRegion(DataObject *output);
+
+ /** RegionOfInterestImageFilter can produce an image which is a different
+ * size than its input image. As such, RegionOfInterestImageFilter
+ * needs to provide an implementation for
+ * GenerateOutputInformation() in order to inform the pipeline
+ * execution model. The original documentation of this method is
+ * below.
+ *
+ * \sa ProcessObject::GenerateOutputInformaton() */
+ virtual void GenerateOutputInformation();
+
+ /** RegionOfInterestImageFilter can be implemented as a multithreaded filter.
+ * Therefore, this implementation provides a ThreadedGenerateData()
+ * routine which is called for each processing thread. The output
+ * image data is allocated automatically by the superclass prior to
+ * calling ThreadedGenerateData(). ThreadedGenerateData can only
+ * write to the portion of the output image specified by the
+ * parameter "outputRegionForThread"
+ * \sa ImageToImageFilter::ThreadedGenerateData(),
+ * ImageToImageFilter::GenerateData() */
+ void ThreadedGenerateData(const RegionType & outputRegionForThread,
+ ThreadIdType threadId);
+
+private:
+ RegionOfInterestImageFilter(const Self &); //purposely not implemented
+ void operator=(const Self &); //purposely not implemented
+
+ RegionType m_RegionOfInterest;
+};
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class RegionOfInterestImageFilter<Image<TPixel, VImageDimension>, RLEImage<TPixel, VImageDimension, CounterType> > :
+ public ImageToImageFilter< Image<TPixel, VImageDimension>, RLEImage<TPixel, VImageDimension, CounterType> >
+{
+public:
+ /** Standard class typedefs. */
+ typedef RegionOfInterestImageFilter Self;
+ typedef RLEImage<TPixel, VImageDimension, CounterType> RLEImageType;
+ typedef Image<TPixel, VImageDimension> ImageType;
+ typedef ImageToImageFilter< ImageType, RLEImageType > Superclass;
+ typedef SmartPointer< Self > Pointer;
+ typedef SmartPointer< const Self > ConstPointer;
+ typedef typename Superclass::InputImageRegionType InputImageRegionType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(RegionOfInterestImageFilter, ImageToImageFilter);
+
+ /** Typedef to describe the input image region types. */
+ typedef typename RLEImageType::RegionType RegionType;
+ typedef typename RLEImageType::IndexType IndexType;
+ typedef typename RLEImageType::SizeType SizeType;
+
+ /** Typedef to describe the type of pixel. */
+ typedef typename RLEImageType::PixelType OutputImagePixelType;
+ typedef typename RLEImageType::PixelType InputImagePixelType;
+
+ /** Set/Get the output image region. */
+ itkSetMacro(RegionOfInterest, RegionType);
+ itkGetConstMacro(RegionOfInterest, RegionType);
+
+ /** ImageDimension enumeration */
+ itkStaticConstMacro(ImageDimension, unsigned int, VImageDimension);
+ itkStaticConstMacro(OutputImageDimension, unsigned int, VImageDimension);
+
+#ifdef ITK_USE_CONCEPT_CHECKING
+ // Begin concept checking
+ itkConceptMacro(SameDimensionCheck,
+ (Concept::SameDimension< ImageDimension, OutputImageDimension >));
+ itkConceptMacro(InputConvertibleToOutputCheck,
+ (Concept::Convertible< InputImagePixelType, OutputImagePixelType >));
+ // End concept checking
+#endif
+
+protected:
+ RegionOfInterestImageFilter() {}
+ ~RegionOfInterestImageFilter() {}
+ void PrintSelf(std::ostream & os, Indent indent) const;
+
+ virtual void GenerateInputRequestedRegion();
+
+ virtual void EnlargeOutputRequestedRegion(DataObject *output);
+
+ /** RegionOfInterestImageFilter can produce an image which is a different
+ * size than its input image. As such, RegionOfInterestImageFilter
+ * needs to provide an implementation for
+ * GenerateOutputInformation() in order to inform the pipeline
+ * execution model. The original documentation of this method is
+ * below.
+ *
+ * \sa ProcessObject::GenerateOutputInformaton() */
+ virtual void GenerateOutputInformation();
+
+ /** RegionOfInterestImageFilter can be implemented as a multithreaded filter.
+ * Therefore, this implementation provides a ThreadedGenerateData()
+ * routine which is called for each processing thread. The output
+ * image data is allocated automatically by the superclass prior to
+ * calling ThreadedGenerateData(). ThreadedGenerateData can only
+ * write to the portion of the output image specified by the
+ * parameter "outputRegionForThread"
+ * \sa ImageToImageFilter::ThreadedGenerateData(),
+ * ImageToImageFilter::GenerateData() */
+ void ThreadedGenerateData(const RegionType & outputRegionForThread,
+ ThreadIdType threadId);
+
+private:
+ RegionOfInterestImageFilter(const Self &); //purposely not implemented
+ void operator=(const Self &); //purposely not implemented
+
+ RegionType m_RegionOfInterest;
+};
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+class RegionOfInterestImageFilter< RLEImage<TPixel, VImageDimension, CounterType>, Image<TPixel, VImageDimension> > :
+ public ImageToImageFilter< RLEImage<TPixel, VImageDimension, CounterType>, Image<TPixel, VImageDimension> >
+{
+public:
+ /** Standard class typedefs. */
+ typedef RegionOfInterestImageFilter Self;
+ typedef RLEImage<TPixel, VImageDimension, CounterType> RLEImageType;
+ typedef Image<TPixel, VImageDimension> ImageType;
+ typedef ImageToImageFilter< RLEImageType, ImageType > Superclass;
+ typedef SmartPointer< Self > Pointer;
+ typedef SmartPointer< const Self > ConstPointer;
+ typedef typename Superclass::InputImageRegionType InputImageRegionType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(RegionOfInterestImageFilter, ImageToImageFilter);
+
+ /** Typedef to describe the input image region types. */
+ typedef typename RLEImageType::RegionType RegionType;
+ typedef typename RLEImageType::IndexType IndexType;
+ typedef typename RLEImageType::SizeType SizeType;
+
+ /** Typedef to describe the type of pixel. */
+ typedef typename RLEImageType::PixelType OutputImagePixelType;
+ typedef typename RLEImageType::PixelType InputImagePixelType;
+
+ /** Set/Get the output image region. */
+ itkSetMacro(RegionOfInterest, RegionType);
+ itkGetConstMacro(RegionOfInterest, RegionType);
+
+ /** ImageDimension enumeration */
+ itkStaticConstMacro(ImageDimension, unsigned int, VImageDimension);
+ itkStaticConstMacro(OutputImageDimension, unsigned int, VImageDimension);
+
+#ifdef ITK_USE_CONCEPT_CHECKING
+ // Begin concept checking
+ itkConceptMacro(SameDimensionCheck,
+ (Concept::SameDimension< ImageDimension, OutputImageDimension >));
+ itkConceptMacro(InputConvertibleToOutputCheck,
+ (Concept::Convertible< InputImagePixelType, OutputImagePixelType >));
+ // End concept checking
+#endif
+
+protected:
+ RegionOfInterestImageFilter() {}
+ ~RegionOfInterestImageFilter() {}
+ void PrintSelf(std::ostream & os, Indent indent) const;
+
+ virtual void GenerateInputRequestedRegion();
+
+ virtual void EnlargeOutputRequestedRegion(DataObject *output);
+
+ /** RegionOfInterestImageFilter can produce an image which is a different
+ * size than its input image. As such, RegionOfInterestImageFilter
+ * needs to provide an implementation for
+ * GenerateOutputInformation() in order to inform the pipeline
+ * execution model. The original documentation of this method is
+ * below.
+ *
+ * \sa ProcessObject::GenerateOutputInformaton() */
+ virtual void GenerateOutputInformation();
+
+ /** RegionOfInterestImageFilter can be implemented as a multithreaded filter.
+ * Therefore, this implementation provides a ThreadedGenerateData()
+ * routine which is called for each processing thread. The output
+ * image data is allocated automatically by the superclass prior to
+ * calling ThreadedGenerateData(). ThreadedGenerateData can only
+ * write to the portion of the output image specified by the
+ * parameter "outputRegionForThread"
+ * \sa ImageToImageFilter::ThreadedGenerateData(),
+ * ImageToImageFilter::GenerateData() */
+ void ThreadedGenerateData(const RegionType & outputRegionForThread,
+ ThreadIdType threadId);
+
+private:
+ RegionOfInterestImageFilter(const Self &); //purposely not implemented
+ void operator=(const Self &); //purposely not implemented
+
+ RegionType m_RegionOfInterest;
+};
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "RLERegionOfInterestImageFilter.txx"
+#endif
+
+#endif //RLERegionOfInterestImageFilter_h
diff --git a/Logic/RLEImage/RLERegionOfInterestImageFilter.txx b/Logic/RLEImage/RLERegionOfInterestImageFilter.txx
new file mode 100644
index 0000000..7436b6a
--- /dev/null
+++ b/Logic/RLEImage/RLERegionOfInterestImageFilter.txx
@@ -0,0 +1,533 @@
+#ifndef RLERegionOfInterestImageFilter_txx
+#define RLERegionOfInterestImageFilter_txx
+
+#include "itkRegionOfInterestImageFilter.h"
+#include "itkImageAlgorithm.h"
+#include "itkObjectFactory.h"
+#include "itkProgressReporter.h"
+#include "itkImage.h"
+
+namespace itk
+{
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::PrintSelf(std::ostream & os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "RegionOfInterest: " << m_RegionOfInterest << std::endl;
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::GenerateInputRequestedRegion()
+{
+ // call the superclass' implementation of this method
+ Superclass::GenerateInputRequestedRegion();
+
+ // get pointer to the input
+ typename Superclass::InputImagePointer inputPtr =
+ const_cast< RLEImageType * >(this->GetInput());
+
+ if (inputPtr)
+ {
+ // request the region of interest
+ inputPtr->SetRequestedRegion(m_RegionOfInterest);
+ }
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::EnlargeOutputRequestedRegion(DataObject *output)
+{
+ // call the superclass' implementation of this method
+ Superclass::EnlargeOutputRequestedRegion(output);
+
+ // generate everything in the region of interest
+ output->SetRequestedRegionToLargestPossibleRegion();
+}
+
+/**
+* RegionOfInterestImageFilter can produce an image which is a different size
+* than its input image. As such, RegionOfInterestImageFilter needs to provide an
+* implementation for GenerateOutputInformation() in order to inform
+* the pipeline execution model. The original documentation of this
+* method is below.
+*
+* \sa ProcessObject::GenerateOutputInformaton()
+*/
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::GenerateOutputInformation()
+{
+ // do not call the superclass' implementation of this method since
+ // this filter allows the input the output to be of different dimensions
+
+ // get pointers to the input and output
+ typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
+ typename Superclass::InputImageConstPointer inputPtr = this->GetInput();
+
+ if (!outputPtr || !inputPtr)
+ {
+ return;
+ }
+
+ // Set the output image size to the same value as the region of interest.
+ RegionType region;
+ IndexType start;
+ start.Fill(0);
+
+ region.SetSize(m_RegionOfInterest.GetSize());
+ region.SetIndex(start);
+
+ // Copy Information without modification.
+ outputPtr->CopyInformation(inputPtr);
+
+ // Adjust output region
+ outputPtr->SetLargestPossibleRegion(region);
+
+ // Correct origin of the extracted region.
+ IndexType roiStart(m_RegionOfInterest.GetIndex());
+ typename Superclass::OutputImageType::PointType outputOrigin;
+ inputPtr->TransformIndexToPhysicalPoint(roiStart, outputOrigin);
+ outputPtr->SetOrigin(outputOrigin);
+}
+
+/**
+* RegionOfInterestImageFilter can be implemented as a multithreaded filter.
+* Therefore, this implementation provides a ThreadedGenerateData()
+* routine which is called for each processing thread. The output
+* image data is allocated automatically by the superclass prior to
+* calling ThreadedGenerateData(). ThreadedGenerateData can only
+* write to the portion of the output image specified by the
+* parameter "outputRegionForThread"
+*
+* \sa ImageToImageFilter::ThreadedGenerateData(),
+* ImageToImageFilter::GenerateData()
+*/
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::ThreadedGenerateData(const RegionType & outputRegionForThread,
+ ThreadIdType threadId)
+{
+ // Get the input and output pointers
+ const RLEImageType *in = this->GetInput();
+ RLEImageType *out = this->GetOutput();
+
+ // Define the portion of the input to walk for this thread
+ InputImageRegionType inputRegionForThread;
+ inputRegionForThread.SetSize(outputRegionForThread.GetSize());
+
+ IndexType start, end;
+ IndexType roiStart(m_RegionOfInterest.GetIndex());
+ IndexType threadStart(outputRegionForThread.GetIndex());
+ for (unsigned int i = 0; i < VImageDimension; i++)
+ {
+ start[i] = roiStart[i] + threadStart[i];
+ end[i] = roiStart[i] + threadStart[i] + outputRegionForThread.GetSize(i);
+ }
+ inputRegionForThread.SetIndex(start);
+
+ bool copyLines = (in->GetLargestPossibleRegion().GetSize(0) == outputRegionForThread.GetSize(0));
+ typename ImageType::BufferType::RegionType oReg = ImageType::truncateRegion(outputRegionForThread),
+ iReg = ImageType::truncateRegion(inputRegionForThread);
+ ImageRegionConstIterator<typename ImageType::BufferType> iIt(in->GetBuffer(), iReg);
+ ImageRegionIterator<typename ImageType::BufferType> oIt(out->GetBuffer(), oReg);
+
+ while (!oIt.IsAtEnd())
+ {
+ if (copyLines)
+ oIt.Set(iIt.Get());
+ else //determine begin and end iterator and copy range
+ {
+ typename RLEImageType::RLLine &oLine = oIt.Value();
+ oLine.clear();
+ const typename RLEImageType::RLLine &iLine = iIt.Value();
+ CounterType t = 0;
+ SizeValueType x = 0;
+ //find start
+ for (; x < iLine.size(); x++)
+ {
+ t += iLine[x].first;
+ if (t > start[0])
+ break;
+ }
+ assert(x < iLine.size());
+
+ SizeValueType begin = x;
+ if (t >= end[0]) //both begin and end are in this segment
+ {
+ oLine.push_back(
+ typename RLEImageType::RLSegment(end[0] - start[0], iLine[x].second));
+ ++iIt;
+ ++oIt;
+ continue; //next line
+ }
+ else if (t - start[0] < iLine[x].first) //not the first pixel in segment
+ {
+ oLine.push_back(typename RLEImageType::RLSegment(t - start[0], iLine[x].second));
+ begin++; //start copying from next segment
+ }
+
+ //if (t < end[0])
+ for (x++; x < iLine.size(); x++)
+ {
+ t += iLine[x].first;
+ if (t >= end[0])
+ break;
+ }
+ if (t == end[0])
+ oLine.insert(oLine.end(), iLine.begin() + begin, iLine.begin() + x + 1);
+ else //we need to take special care of the last segment
+ {
+ oLine.insert(oLine.end(), iLine.begin() + begin, iLine.begin() + x);
+ oLine.push_back(
+ typename RLEImageType::RLSegment(end[0] + iLine[x].first - t, iLine[x].second));
+ }
+ }
+ ++iIt;
+ ++oIt;
+ }
+}
+
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<Image<TPixel, VImageDimension>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::PrintSelf(std::ostream & os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "RegionOfInterest: " << m_RegionOfInterest << std::endl;
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<Image<TPixel, VImageDimension>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::GenerateInputRequestedRegion()
+{
+ // call the superclass' implementation of this method
+ Superclass::GenerateInputRequestedRegion();
+
+ // get pointer to the input
+ typename Superclass::InputImagePointer inputPtr =
+ const_cast< ImageType * >(this->GetInput());
+
+ if (inputPtr)
+ {
+ // request the region of interest
+ inputPtr->SetRequestedRegion(m_RegionOfInterest);
+ }
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<Image<TPixel, VImageDimension>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::EnlargeOutputRequestedRegion(DataObject *output)
+{
+ // call the superclass' implementation of this method
+ Superclass::EnlargeOutputRequestedRegion(output);
+
+ // generate everything in the region of interest
+ output->SetRequestedRegionToLargestPossibleRegion();
+}
+
+/**
+* RegionOfInterestImageFilter can produce an image which is a different size
+* than its input image. As such, RegionOfInterestImageFilter needs to provide an
+* implementation for GenerateOutputInformation() in order to inform
+* the pipeline execution model. The original documentation of this
+* method is below.
+*
+* \sa ProcessObject::GenerateOutputInformaton()
+*/
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<Image<TPixel, VImageDimension>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::GenerateOutputInformation()
+{
+ // do not call the superclass' implementation of this method since
+ // this filter allows the input the output to be of different dimensions
+
+ // get pointers to the input and output
+ typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
+ typename Superclass::InputImageConstPointer inputPtr = this->GetInput();
+
+ if (!outputPtr || !inputPtr)
+ {
+ return;
+ }
+
+ // Set the output image size to the same value as the region of interest.
+ RegionType region;
+ IndexType start;
+ start.Fill(0);
+
+ region.SetSize(m_RegionOfInterest.GetSize());
+ region.SetIndex(start);
+
+ // Copy Information without modification.
+ outputPtr->CopyInformation(inputPtr);
+
+ // Adjust output region
+ outputPtr->SetLargestPossibleRegion(region);
+
+ // Correct origin of the extracted region.
+ IndexType roiStart(m_RegionOfInterest.GetIndex());
+ typename Superclass::OutputImageType::PointType outputOrigin;
+ inputPtr->TransformIndexToPhysicalPoint(roiStart, outputOrigin);
+ outputPtr->SetOrigin(outputOrigin);
+}
+
+/**
+* RegionOfInterestImageFilter can be implemented as a multithreaded filter.
+* Therefore, this implementation provides a ThreadedGenerateData()
+* routine which is called for each processing thread. The output
+* image data is allocated automatically by the superclass prior to
+* calling ThreadedGenerateData(). ThreadedGenerateData can only
+* write to the portion of the output image specified by the
+* parameter "outputRegionForThread"
+*
+* \sa ImageToImageFilter::ThreadedGenerateData(),
+* ImageToImageFilter::GenerateData()
+*/
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<Image<TPixel, VImageDimension>,
+ RLEImage<TPixel, VImageDimension, CounterType> >
+ ::ThreadedGenerateData(const RegionType & outputRegionForThread,
+ ThreadIdType threadId)
+{
+ // Get the input and output pointers
+ const ImageType *in = this->GetInput();
+ RLEImageType *out = this->GetOutput();
+
+ // Define the portion of the input to walk for this thread
+ InputImageRegionType inputRegionForThread;
+ inputRegionForThread.SetSize(outputRegionForThread.GetSize());
+
+ IndexType start, end;
+ IndexType roiStart(m_RegionOfInterest.GetIndex());
+ IndexType threadStart(outputRegionForThread.GetIndex());
+ for (unsigned int i = 0; i < VImageDimension; i++)
+ {
+ start[i] = roiStart[i] + threadStart[i];
+ end[i] = roiStart[i] + threadStart[i] + outputRegionForThread.GetSize(i);
+ }
+ inputRegionForThread.SetIndex(start);
+
+ typename RLEImageType::BufferType::RegionType oReg = RLEImageType::truncateRegion(outputRegionForThread);
+ ImageRegionConstIterator<ImageType> iIt(in, inputRegionForThread);
+ ImageRegionIterator<typename RLEImageType::BufferType> oIt(out->GetBuffer(), oReg);
+ SizeValueType size0 = outputRegionForThread.GetSize(0);
+ typename RLEImageType::RLLine temp;
+ temp.reserve(size0); //pessimistically preallocate buffer, otherwise reallocations can occur
+
+ while (!oIt.IsAtEnd())
+ {
+ SizeValueType x = 0;
+ temp.clear();
+ while (x < size0)
+ {
+ typename RLEImageType::RLSegment s(0, iIt.Value());
+ while (x < size0 && iIt.Value() == s.second)
+ {
+ x++;
+ s.first++;
+ ++(iIt);
+ }
+ temp.push_back(s);
+ }
+ oIt.Value() = temp;
+ ++oIt;
+ }
+}
+
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ Image<TPixel, VImageDimension> >
+::PrintSelf(std::ostream & os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "RegionOfInterest: " << m_RegionOfInterest << std::endl;
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ Image<TPixel, VImageDimension> >
+::GenerateInputRequestedRegion()
+{
+ // call the superclass' implementation of this method
+ Superclass::GenerateInputRequestedRegion();
+
+ // get pointer to the input
+ typename Superclass::InputImagePointer inputPtr =
+ const_cast< RLEImageType * >( this->GetInput() );
+
+ if ( inputPtr )
+ {
+ // request the region of interest
+ inputPtr->SetRequestedRegion(m_RegionOfInterest);
+ }
+}
+
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ Image<TPixel, VImageDimension> >
+::EnlargeOutputRequestedRegion(DataObject *output)
+{
+ // call the superclass' implementation of this method
+ Superclass::EnlargeOutputRequestedRegion(output);
+
+ // generate everything in the region of interest
+ output->SetRequestedRegionToLargestPossibleRegion();
+}
+
+/**
+ * RegionOfInterestImageFilter can produce an image which is a different size
+ * than its input image. As such, RegionOfInterestImageFilter needs to provide an
+ * implementation for GenerateOutputInformation() in order to inform
+ * the pipeline execution model. The original documentation of this
+ * method is below.
+ *
+ * \sa ProcessObject::GenerateOutputInformaton()
+ */
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ Image<TPixel, VImageDimension> >
+::GenerateOutputInformation()
+{
+ // do not call the superclass' implementation of this method since
+ // this filter allows the input the output to be of different dimensions
+
+ // get pointers to the input and output
+ typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
+ typename Superclass::InputImageConstPointer inputPtr = this->GetInput();
+
+ if ( !outputPtr || !inputPtr )
+ {
+ return;
+ }
+
+ // Set the output image size to the same value as the region of interest.
+ RegionType region;
+ IndexType start;
+ start.Fill(0);
+
+ region.SetSize( m_RegionOfInterest.GetSize() );
+ region.SetIndex(start);
+
+ // Copy Information without modification.
+ outputPtr->CopyInformation(inputPtr);
+
+ // Adjust output region
+ outputPtr->SetLargestPossibleRegion(region);
+
+ // Correct origin of the extracted region.
+ IndexType roiStart( m_RegionOfInterest.GetIndex() );
+ typename Superclass::OutputImageType::PointType outputOrigin;
+ inputPtr->TransformIndexToPhysicalPoint(roiStart, outputOrigin);
+ outputPtr->SetOrigin(outputOrigin);
+}
+
+/**
+ * RegionOfInterestImageFilter can be implemented as a multithreaded filter.
+ * Therefore, this implementation provides a ThreadedGenerateData()
+ * routine which is called for each processing thread. The output
+ * image data is allocated automatically by the superclass prior to
+ * calling ThreadedGenerateData(). ThreadedGenerateData can only
+ * write to the portion of the output image specified by the
+ * parameter "outputRegionForThread"
+ *
+ * \sa ImageToImageFilter::ThreadedGenerateData(),
+ * ImageToImageFilter::GenerateData()
+ */
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void RegionOfInterestImageFilter<RLEImage<TPixel, VImageDimension, CounterType>,
+ Image<TPixel, VImageDimension> >
+::ThreadedGenerateData(const RegionType & outputRegionForThread,
+ ThreadIdType threadId)
+{
+ // Get the input and output pointers
+ const RLEImageType *in = this->GetInput();
+ ImageType *out = this->GetOutput();
+
+ // Define the portion of the input to walk for this thread
+ InputImageRegionType inputRegionForThread;
+ inputRegionForThread.SetSize(outputRegionForThread.GetSize());
+
+ IndexType start, end;
+ IndexType roiStart( m_RegionOfInterest.GetIndex() );
+ IndexType threadStart( outputRegionForThread.GetIndex() );
+ for (unsigned int i = 0; i < VImageDimension; i++)
+ {
+ start[i] = roiStart[i] + threadStart[i];
+ end[i] = roiStart[i] + threadStart[i] + outputRegionForThread.GetSize(i);
+ }
+ inputRegionForThread.SetIndex(start);
+
+ typename RLEImageType::BufferType::RegionType iReg = RLEImageType::truncateRegion(inputRegionForThread);
+ ImageRegionConstIterator<typename RLEImageType::BufferType> iIt(in->GetBuffer(), iReg);
+ ImageRegionIterator<ImageType> oIt(out, outputRegionForThread);
+
+ while (!iIt.IsAtEnd())
+ {
+ const typename RLEImageType::RLLine &iLine = iIt.Value();
+ CounterType t = 0;
+ SizeValueType x = 0;
+ //find start
+ for (; x < iLine.size(); x++)
+ {
+ t += iLine[x].first;
+ if (t > start[0])
+ break;
+ }
+ assert(x < iLine.size());
+
+ SizeValueType begin = x;
+ if (t >= end[0]) //both begin and end are in this segment
+ {
+ for (SizeValueType i = start[0]; i < end[0]; i++)
+ {
+ oIt.Set(iLine[x].second);
+ ++oIt;
+ }
+ ++iIt;
+ continue; //next line
+ }
+ //else handle the beginning segment
+ for (SizeValueType i = start[0]; i < t; i++)
+ {
+ oIt.Set(iLine[x].second);
+ ++oIt;
+ }
+ //now handle middle segments
+ for (x++; x < iLine.size(); x++)
+ {
+ t += iLine[x].first;
+ if (t >= end[0])
+ break;
+ for (SizeValueType i = 0; i < iLine[x].first; i++)
+ {
+ oIt.Set(iLine[x].second);
+ ++oIt;
+ }
+ }
+ //handle the last segment
+ for (SizeValueType i = 0; i < end[0] + iLine[x].first - t; i++)
+ {
+ oIt.Set(iLine[x].second);
+ ++oIt;
+ }
+ ++iIt;
+ }
+}
+} // end namespace itk
+
+#endif //RLERegionOfInterestImageFilter_txx
diff --git a/Logic/Slicing/AdaptiveSlicingPipeline.h b/Logic/Slicing/AdaptiveSlicingPipeline.h
new file mode 100644
index 0000000..4433054
--- /dev/null
+++ b/Logic/Slicing/AdaptiveSlicingPipeline.h
@@ -0,0 +1,187 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: ImageWrapper.txx,v $
+ Language: C++
+ Date: $Date: 2010/10/14 16:21:04 $
+ Version: $Revision: 1.11 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef ADAPTIVESLICINGPIPELINE_H
+#define ADAPTIVESLICINGPIPELINE_H
+
+#include "itkImageToImageFilter.h"
+#include "itkTransform.h"
+#include "itkDataObjectDecorator.h"
+#include "SNAPCommon.h"
+
+template <class TInputImage, class TOutputImage, class TPreviewImage> class IRISSlicer;
+template <class TInputImage, class TOutputImage> class NonOrthogonalSlicer;
+class ImageCoordinateTransform;
+
+using itk::DataObjectDecorator;
+using itk::ProcessObject;
+
+namespace itk {
+template<typename TParametersValueType,
+ unsigned int NInputDimensions,
+ unsigned int NOutputDimensions> class Transform;
+}
+
+
+/**
+ * This filter encapsulates the ITK-SNAP slicing pipeline. It includes both
+ * the straight (orthogonal) slicer and the oblique slicer.
+ *
+ *
+ */
+template <typename TInputImage, typename TOutputImage, typename TPreviewImage>
+class AdaptiveSlicingPipeline
+ : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef AdaptiveSlicingPipeline Self;
+ typedef itk::ImageToImageFilter<TInputImage, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ typedef TInputImage InputImageType;
+ typedef typename InputImageType::ConstPointer InputImagePointer;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+
+ typedef TOutputImage OutputImageType;
+ typedef typename OutputImageType::Pointer OutputImagePointer;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ typedef TPreviewImage PreviewImageType;
+ typedef typename PreviewImageType::Pointer PreviewImagePointer;
+ typedef typename PreviewImageType::PixelType PreviewPixelType;
+ typedef typename PreviewImageType::InternalPixelType PreviewComponentType;
+
+ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension);
+ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension);
+
+ /** Slicers */
+ typedef IRISSlicer<TInputImage,TOutputImage,TPreviewImage> OrthogonalSlicerType;
+ typedef NonOrthogonalSlicer<TInputImage,TOutputImage> NonOrthogonalSlicerType;
+
+ /** Reference space for non-orthogonal slicing */
+ typedef typename itk::ImageBase<InputImageDimension> NonOrthogonalSliceReferenceSpace;
+
+ /** Transform */
+ typedef ImageCoordinateTransform OrthogonalTransformType;
+ typedef itk::Transform<double, InputImageDimension, InputImageDimension> ObliqueTransformType;
+
+ /** Some more typedefs. */
+ typedef typename InputImageType::RegionType InputImageRegionType;
+ typedef typename OutputImageType::RegionType OutputImageRegionType;
+
+ /** Slice index */
+ typedef itk::Index<InputImageDimension> IndexType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(AdaptiveSlicingPipeline, ImageToImageFilter)
+
+ /** Reference image input */
+ itkSetInputMacro(ObliqueReferenceImage, NonOrthogonalSliceReferenceSpace)
+ itkGetInputMacro(ObliqueReferenceImage, NonOrthogonalSliceReferenceSpace)
+
+ /** Preview image input */
+ itkSetInputMacro(PreviewImage, PreviewImageType)
+ itkGetInputMacro(PreviewImage, PreviewImageType)
+
+ /** Orthogonal Transform input */
+ itkSetDecoratedObjectInputMacro(OrthogonalTransform, OrthogonalTransformType)
+ itkGetDecoratedObjectInputMacro(OrthogonalTransform, OrthogonalTransformType)
+
+ /** Oblique Transform input */
+ itkSetDecoratedObjectInputMacro(ObliqueTransform, ObliqueTransformType)
+ itkGetDecoratedObjectInputMacro(ObliqueTransform, ObliqueTransformType)
+
+ /** Which slicing pipeline to use */
+ itkSetMacro(UseOrthogonalSlicing, bool)
+ itkGetMacro(UseOrthogonalSlicing, bool)
+
+ /** Set the slice index for the orthogonal slicer */
+ itkGetMacro(SliceIndex, IndexType)
+ itkSetMacro(SliceIndex, IndexType)
+
+ /** Interpolation type */
+ void SetUseNearestNeighbor(bool flag);
+ bool GetUseNearestNeighbor() const;
+
+ /** Look up intensity at the current slice index. This may update the filter */
+ OutputPixelType LookupIntensityAtSliceIndex(const itk::ImageBase<3> *ref_space);
+
+protected:
+
+ AdaptiveSlicingPipeline();
+ ~AdaptiveSlicingPipeline();
+
+ virtual void VerifyInputInformation() { }
+
+ virtual void GenerateOutputInformation();
+
+ virtual void PropagateRequestedRegion(itk::DataObject *object);
+
+ virtual void CallCopyOutputRegionToInputRegion(InputImageRegionType &destRegion,
+ const OutputImageRegionType &srcRegion);
+
+ virtual void GenerateData();
+
+ itk::SmartPointer<OrthogonalSlicerType> m_OrthogonalSlicer;
+ itk::SmartPointer<NonOrthogonalSlicerType> m_ObliqueSlicer;
+
+ bool m_UseOrthogonalSlicing;
+
+ IndexType m_SliceIndex;
+
+ void MapInputsToSlicers();
+};
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "AdaptiveSlicingPipeline.txx"
+#endif
+
+
+#endif // ADAPTIVESLICINGPIPELINE_H
diff --git a/Logic/Slicing/AdaptiveSlicingPipeline.txx b/Logic/Slicing/AdaptiveSlicingPipeline.txx
new file mode 100644
index 0000000..6309af4
--- /dev/null
+++ b/Logic/Slicing/AdaptiveSlicingPipeline.txx
@@ -0,0 +1,287 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: ImageWrapper.txx,v $
+ Language: C++
+ Date: $Date: 2010/10/14 16:21:04 $
+ Version: $Revision: 1.11 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef ADAPTIVESLICINGPIPELINE_TXX
+#define ADAPTIVESLICINGPIPELINE_TXX
+
+#include "AdaptiveSlicingPipeline.h"
+#include "IRISSlicer.h"
+#include "NonOrthogonalSlicer.h"
+#include "IRISVectorTypesToITKConversion.h"
+
+template<class TInputImage> class AdaptiveSlicingPipeline_PixelFiller
+{
+public:
+ typedef TInputImage ImageType;
+ typedef typename ImageType::PixelType PixelType;
+ typedef typename ImageType::InternalPixelType ComponentType;
+ static void FillPixel(const ImageType *image, PixelType &pixel, ComponentType value)
+ {
+ pixel = value;
+ }
+ static void MakePixel(const ImageType *image, PixelType &pixel, ComponentType *arr)
+ {
+ pixel = *arr;
+ }
+
+};
+
+template<typename TPixel, unsigned int VDim>
+class AdaptiveSlicingPipeline_PixelFiller< itk::VectorImage<TPixel, VDim> >
+{
+public:
+ typedef itk::VectorImage<TPixel, VDim> ImageType;
+ typedef typename ImageType::PixelType PixelType;
+ typedef typename ImageType::InternalPixelType ComponentType;
+ static void FillPixel(const ImageType *image, PixelType &pixel, ComponentType value)
+ {
+ pixel.SetSize(image->GetNumberOfComponentsPerPixel());
+ pixel.Fill(value);
+ }
+ static void MakePixel(const ImageType *image, PixelType &pixel, ComponentType *arr)
+ {
+ pixel.SetSize(image->GetNumberOfComponentsPerPixel());
+ for(int i = 0; i < pixel.GetSize(); i++)
+ pixel.SetElement(i, arr[i]);
+ }
+};
+
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::AdaptiveSlicingPipeline()
+{
+ // Create the two slicer types
+ m_OrthogonalSlicer = OrthogonalSlicerType::New();
+ m_ObliqueSlicer = NonOrthogonalSlicerType::New();
+
+ // Initially use the ortho
+ m_UseOrthogonalSlicing = true;
+}
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::~AdaptiveSlicingPipeline()
+{
+ // Prevent crash from grafting child filter outputs
+ if(this->GetOutput())
+ this->GetOutput()->SetPixelContainer(NULL);
+}
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+void
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::MapInputsToSlicers()
+{
+ if(m_UseOrthogonalSlicing)
+ {
+ m_OrthogonalSlicer->SetInput(this->GetInput());
+ m_OrthogonalSlicer->SetPreviewInput(
+ const_cast<PreviewImageType *>(this->GetPreviewImage()));
+
+ // Inverse transform
+ ImageCoordinateTransform::Pointer tinv = ImageCoordinateTransform::New();
+ this->GetOrthogonalTransform()->ComputeInverse(tinv);
+
+ // Tell slicer in which directions to slice
+ m_OrthogonalSlicer->SetSliceDirectionImageAxis(
+ tinv->GetCoordinateIndexZeroBased(2));
+
+ m_OrthogonalSlicer->SetLineDirectionImageAxis(
+ tinv->GetCoordinateIndexZeroBased(1));
+
+ m_OrthogonalSlicer->SetPixelDirectionImageAxis(
+ tinv->GetCoordinateIndexZeroBased(0));
+
+ m_OrthogonalSlicer->SetPixelTraverseForward(
+ tinv->GetCoordinateOrientation(0) > 0);
+
+ m_OrthogonalSlicer->SetLineTraverseForward(
+ tinv->GetCoordinateOrientation(1) > 0);
+
+ // Set the slice index
+ m_OrthogonalSlicer->SetSliceIndex(
+ m_SliceIndex[m_OrthogonalSlicer->GetSliceDirectionImageAxis()]);
+ }
+ else
+ {
+ m_ObliqueSlicer->SetInput(this->GetInput());
+ m_ObliqueSlicer->SetTransform(this->GetObliqueTransform());
+ m_ObliqueSlicer->SetReferenceImage(this->GetObliqueReferenceImage());
+ }
+}
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+void
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::GenerateOutputInformation()
+{
+ // Make sure the inputs are assigned to the corresponding slicers
+ this->MapInputsToSlicers();
+
+ // Get the outer filter's output
+ OutputImageType *output = this->GetOutput();
+
+ // Use appropriate sub-pipeline
+ if(m_UseOrthogonalSlicing)
+ {
+ m_OrthogonalSlicer->UpdateOutputInformation();
+ m_OrthogonalSlicer->GetOutput()->SetRequestedRegionToLargestPossibleRegion();
+ output->CopyInformation(m_OrthogonalSlicer->GetOutput());
+ }
+ else
+ {
+ m_ObliqueSlicer->UpdateOutputInformation();
+ m_ObliqueSlicer->GetOutput()->SetRequestedRegionToLargestPossibleRegion();
+ output->CopyInformation(m_ObliqueSlicer->GetOutput());
+ }
+
+ // Copy information does not update the requested region, so we must update
+ // it by hand here
+ output->SetRequestedRegionToLargestPossibleRegion();
+}
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+void
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::PropagateRequestedRegion(itk::DataObject *output)
+{
+ // Use appropriate sub-pipeline
+ if(m_UseOrthogonalSlicing)
+ {
+ m_OrthogonalSlicer->PropagateRequestedRegion(output);
+ }
+ else
+ {
+ m_ObliqueSlicer->PropagateRequestedRegion(output);
+ }
+}
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+void
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::CallCopyOutputRegionToInputRegion(
+ InputImageRegionType &destRegion, const OutputImageRegionType &srcRegion)
+{
+ Superclass::CallCopyOutputRegionToInputRegion(destRegion, srcRegion);
+}
+
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+void
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::GenerateData()
+{
+ // Get the outer filter's output
+ OutputImageType *output = this->GetOutput();
+
+ // Use appropriate sub-pipeline
+ if(m_UseOrthogonalSlicing)
+ {
+ m_OrthogonalSlicer->Update();
+ output->Graft(m_OrthogonalSlicer->GetOutput());
+ }
+ else
+ {
+ m_ObliqueSlicer->Update();
+ output->Graft(m_ObliqueSlicer->GetOutput());
+ }
+}
+
+template<typename TInputImage, typename TOutputImage, typename TPreviewImage>
+typename AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>::OutputPixelType
+AdaptiveSlicingPipeline<TInputImage, TOutputImage, TPreviewImage>
+::LookupIntensityAtSliceIndex(const itk::ImageBase<3> *ref_space)
+{
+ OutputImageType *output = this->GetOutput();
+ // Update the filter
+ this->Update();
+
+ // The lookup location
+ Vector3ui cursor(m_SliceIndex);
+
+ if(m_UseOrthogonalSlicing)
+ {
+ // If we are using ortho slicing, we can just sample the slice
+ Vector3ui slice_3d = this->GetOrthogonalTransform()->TransformVoxelIndex(cursor);
+ itk::Index<2> slice_idx; slice_idx[0] = slice_3d[0]; slice_idx[1] = slice_3d[1];
+ return this->GetOutput()->GetPixel(slice_idx);
+ }
+ else
+ {
+ // The cursor may be outside of the slice, so we need to map the location back
+ // to the input image and look up the intensity of the input image.
+
+ // Use the reference space to map the cursor coordinate to physical coordinate
+ itk::Point<double, 3> cursor_point, native_point;
+ ref_space->TransformIndexToPhysicalPoint(to_itkIndex(cursor), cursor_point);
+
+ // Use the transform to map the coordinate into the native space of the input
+ native_point = this->GetObliqueTransform()->TransformPoint(cursor_point);
+
+ // Map the native point to an index
+ itk::ContinuousIndex<double, 3> native_cindex;
+ this->GetInput()->TransformPhysicalPointToContinuousIndex(native_point, native_cindex);
+
+ // Create a pointer to pixel data
+ unsigned int k = this->GetOutput()->GetNumberOfComponentsPerPixel();
+ OutputComponentType *out_arr = new OutputComponentType[k], *dummy = out_arr;
+
+ // Use worker class to interpolate input image - out_arr will be filled
+ typedef typename NonOrthogonalSlicerType::WorkerType WorkerType;
+ WorkerType worker(const_cast<InputImageType *>(this->GetInput()));
+ worker.ProcessVoxel(native_cindex.GetDataPointer(), false, &dummy);
+
+ // Create a pixel to return - we use a specialized class for vector/non-vector
+ OutputPixelType pix;
+ AdaptiveSlicingPipeline_PixelFiller<OutputImageType>
+ ::MakePixel(this->GetOutput(),pix, out_arr);
+
+ delete out_arr;
+ return pix;
+ }
+}
+
+
+
+
+#endif // ADAPTIVESLICINGPIPELINE_TXX
diff --git a/Logic/Slicing/FastLinearInterpolator.h b/Logic/Slicing/FastLinearInterpolator.h
new file mode 100644
index 0000000..1d629cb
--- /dev/null
+++ b/Logic/Slicing/FastLinearInterpolator.h
@@ -0,0 +1,886 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __FastLinearInterpolator_h_
+#define __FastLinearInterpolator_h_
+
+#include "itkVectorImage.h"
+#include "itkNumericTraits.h"
+
+template <class TFloat, class TInputComponentType>
+struct FastLinearInterpolatorOutputTraits
+{
+};
+
+template <class TInputComponentType>
+struct FastLinearInterpolatorOutputTraits<float, TInputComponentType>
+{
+ typedef typename itk::NumericTraits<TInputComponentType>::FloatType OutputComponentType;
+};
+
+template <class TInputComponentType>
+struct FastLinearInterpolatorOutputTraits<double, TInputComponentType>
+{
+ typedef typename itk::NumericTraits<TInputComponentType>::RealType OutputComponentType;
+};
+
+template <class TImageType>
+struct FastWarpCompositeImageFilterInputImageTraits
+{
+};
+
+template <class TPixel, unsigned int VDim>
+struct FastWarpCompositeImageFilterInputImageTraits< itk::Image<TPixel, VDim> >
+{
+ static int GetPointerIncrementSize(const itk::Image<TPixel, VDim> *) { return 1; }
+};
+
+template <class TPixel, unsigned int VDim>
+struct FastWarpCompositeImageFilterInputImageTraits< itk::VectorImage<TPixel, VDim> >
+{
+ static int GetPointerIncrementSize(const itk::VectorImage<TPixel, VDim> *image)
+ {
+ return image->GetNumberOfComponentsPerPixel();
+ }
+};
+
+
+/**
+ * Base class for the fast linear interpolators
+ */
+template<class TImage, class TFloat, unsigned int VDim>
+class FastLinearInterpolatorBase
+{
+public:
+ typedef TImage ImageType;
+ typedef TFloat RealType;
+ typedef typename ImageType::InternalPixelType InputComponentType;
+ typedef FastLinearInterpolatorOutputTraits<TFloat, InputComponentType> OutputTraits;
+ typedef typename OutputTraits::OutputComponentType OutputComponentType;
+ typedef FastWarpCompositeImageFilterInputImageTraits<TImage> InputTraits;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, ImageType::ImageDimension );
+
+ enum InOut { INSIDE, OUTSIDE, BORDER };
+
+ /**
+ * Get the number that should be added to the input pointer when parsing the input and
+ * output images. This will be 1 for itk::Image and Ncomp for itk::VectorImage
+ */
+ int GetPointerIncrement() const { return nComp; }
+
+ FastLinearInterpolatorBase(ImageType *image)
+ {
+ buffer = image->GetBufferPointer();
+ nComp = InputTraits::GetPointerIncrementSize(image);
+ nSampled = nComp;
+ this->Initialize();
+ }
+
+ /**
+ * An alternative constuctor that takes as input the pointer to the raw pixels
+ * data, the number of components per pixel, and optionally the number of
+ * components that will be sampled (default value of 0 is to sample all of the
+ * components.
+ *
+ * If you want to sample every 5-th component, you should offset the buffer pointer
+ * by 5 and then set n_sampled to 1.
+ */
+ FastLinearInterpolatorBase(InputComponentType *buffer_ptr,
+ int n_components, int n_sampled = 0)
+ {
+ buffer = buffer_ptr;
+ nComp = n_components;
+ nSampled = n_sampled == 0 ? n_components : n_sampled;
+ this->Initialize();
+ }
+
+ ~FastLinearInterpolatorBase()
+ {
+ delete [] def_value_store;
+ }
+
+protected:
+
+ // This is the number of components that separate voxels from each other
+ int nComp;
+
+ // This is the number of sampled components - this may be different, for
+ // example if we are only interested in sampling the k-th component from
+ // a vector image
+ int nSampled;
+
+ const InputComponentType *buffer;
+
+ // Default value - for interpolation outside of the image bounds
+ const InputComponentType *def_value;
+ InputComponentType *def_value_store;
+
+ InOut status;
+
+ void Initialize()
+ {
+ def_value_store = new InputComponentType[nSampled];
+ for(int i = 0; i < nSampled; i++)
+ def_value_store[i] = itk::NumericTraits<InputComponentType>::Zero;
+ def_value = def_value_store;
+ }
+
+
+ template <class TInput>
+ inline OutputComponentType lerp(RealType a, const TInput &l, const TInput &h)
+ {
+ return l+((h-l)*a);
+ }
+};
+
+
+/**
+ * Arbitrary dimension fast linear interpolator - meant to be slow
+ */
+template<class TImage, class TFloat, unsigned int VDim>
+class FastLinearInterpolator : public FastLinearInterpolatorBase<TImage, TFloat, VDim>
+{
+public:
+ typedef FastLinearInterpolatorBase<TImage, TFloat, VDim> Superclass;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::OutputComponentType OutputComponentType;
+ typedef typename Superclass::RealType RealType;
+ typedef typename Superclass::InOut InOut;
+ typedef itk::ImageBase<VDim> ImageBaseType;
+
+ FastLinearInterpolator(ImageType *image) : Superclass(image) {}
+
+ FastLinearInterpolator(ImageBaseType *img, InputComponentType *buffer_ptr, int n_components)
+ : Superclass(buffer_ptr, n_components) {}
+
+ InOut InterpolateWithGradient(RealType *cix, OutputComponentType *out, OutputComponentType **grad)
+ { return Superclass::INSIDE; }
+
+ InOut Interpolate(RealType *cix, OutputComponentType *out)
+ { return Superclass::INSIDE; }
+
+ InOut InterpolateNearestNeighbor(RealType *cix, OutputComponentType *out)
+ { return Superclass::INSIDE; }
+
+ TFloat GetMask() { return 0.0; }
+
+ TFloat GetMaskAndGradient(RealType *mask_gradient) { return 0.0; }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramSample(RealType *cix, const InputComponentType *fixptr, THistContainer &hist) {}
+
+ template <class THistContainer>
+ void PartialVolumeHistogramGradientSample(RealType *cix, const InputComponentType *fix_ptr, const THistContainer &hist_w, RealType *out_grad) {}
+
+
+protected:
+};
+
+/**
+ * 3D fast linear interpolator - optimized for speed
+ */
+template <class TImage, class TFloat>
+class FastLinearInterpolator<TImage, TFloat, 3>
+ : public FastLinearInterpolatorBase<TImage, TFloat, 3>
+{
+public:
+ typedef TImage ImageType;
+ typedef FastLinearInterpolatorBase<ImageType, TFloat, 3> Superclass;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::OutputComponentType OutputComponentType;
+ typedef typename Superclass::RealType RealType;
+ typedef typename Superclass::InOut InOut;
+ typedef itk::ImageBase<3> ImageBaseType;
+
+ FastLinearInterpolator(ImageType *image) : Superclass(image)
+ {
+ xsize = image->GetLargestPossibleRegion().GetSize()[0];
+ ysize = image->GetLargestPossibleRegion().GetSize()[1];
+ zsize = image->GetLargestPossibleRegion().GetSize()[2];
+ }
+
+ /**
+ * An alternative constuctor that takes as input the pointer to the raw pixels
+ * data, the number of components per pixel, and optionally the number of
+ * components that will be sampled (default value of 0 is to sample all of the
+ * components.
+ *
+ * If you want to sample every 5-th component, you should offset the buffer pointer
+ * by 5 and then set n_sampled to 1.
+ */
+ FastLinearInterpolator(ImageBaseType *image, InputComponentType *buffer_ptr,
+ int n_components, int n_sampled = 0)
+ : Superclass(buffer_ptr, n_components, n_sampled)
+ {
+ xsize = image->GetLargestPossibleRegion().GetSize()[0];
+ ysize = image->GetLargestPossibleRegion().GetSize()[1];
+ zsize = image->GetLargestPossibleRegion().GetSize()[2];
+ }
+
+ /**
+ * Compute the pointers to the eight corners of the interpolating cube
+ */
+ InOut ComputeCorners(RealType *cix)
+ {
+ const InputComponentType *dp;
+
+ x0 = (int) floor(cix[0]); fx = cix[0] - x0;
+ y0 = (int) floor(cix[1]); fy = cix[1] - y0;
+ z0 = (int) floor(cix[2]); fz = cix[2] - z0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+ z1 = z0 + 1;
+
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize &&
+ z0 >= 0 && z1 < zsize)
+ {
+ // The sample point is completely inside
+ dp = dens(x0, y0, z0);
+ d000 = dp;
+ d100 = dp+this->nComp;
+ dp += xsize*this->nComp;
+ d010 = dp;
+ d110 = dp+this->nComp;
+ dp += xsize*ysize*this->nComp;
+ d011 = dp;
+ d111 = dp+this->nComp;
+ dp -= xsize*this->nComp;
+ d001 = dp;
+ d101 = dp+this->nComp;
+
+ // The mask is one
+ this->status = Superclass::INSIDE;
+ }
+ else if (x0 >= -1 && x1 <= xsize &&
+ y0 >= -1 && y1 <= ysize &&
+ z0 >= -1 && z1 <= zsize)
+ {
+ // The sample point is on the border region
+ d000 = border_check(x0, y0, z0, m000);
+ d001 = border_check(x0, y0, z1, m001);
+ d010 = border_check(x0, y1, z0, m010);
+ d011 = border_check(x0, y1, z1, m011);
+ d100 = border_check(x1, y0, z0, m100);
+ d101 = border_check(x1, y0, z1, m101);
+ d110 = border_check(x1, y1, z0, m110);
+ d111 = border_check(x1, y1, z1, m111);
+
+ // The mask is between 0 and 1
+ this->status = Superclass::BORDER;
+ }
+ else
+ {
+ // The mask is zero
+ this->status = Superclass::OUTSIDE;
+ }
+
+ return this->status;
+ }
+
+ /**
+ * Interpolate at position cix, placing the intensity values in out and gradient
+ * values in grad (in strides of VDim)
+ */
+ InOut InterpolateWithGradient(RealType *cix, OutputComponentType *out, OutputComponentType **grad)
+ {
+ RealType dx00, dx01, dx10, dx11, dxy0, dxy1;
+ RealType dx00_x, dx01_x, dx10_x, dx11_x, dxy0_x, dxy1_x;
+ RealType dxy0_y, dxy1_y;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++, grad++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++)
+ {
+ // Interpolate the image intensity
+ dx00 = Superclass::lerp(fx, *d000, *d100);
+ dx01 = Superclass::lerp(fx, *d001, *d101);
+ dx10 = Superclass::lerp(fx, *d010, *d110);
+ dx11 = Superclass::lerp(fx, *d011, *d111);
+ dxy0 = Superclass::lerp(fy, dx00, dx10);
+ dxy1 = Superclass::lerp(fy, dx01, dx11);
+ *(out++) = Superclass::lerp(fz, dxy0, dxy1);
+
+ // Interpolate the gradient in x
+ dx00_x = *d100 - *d000;
+ dx01_x = *d101 - *d001;
+ dx10_x = *d110 - *d010;
+ dx11_x = *d111 - *d011;
+ dxy0_x = this->lerp(fy, dx00_x, dx10_x);
+ dxy1_x = this->lerp(fy, dx01_x, dx11_x);
+ (*grad)[0] = this->lerp(fz, dxy0_x, dxy1_x);
+
+ // Interpolate the gradient in y
+ dxy0_y = dx10 - dx00;
+ dxy1_y = dx11 - dx01;
+ (*grad)[1] = this->lerp(fz, dxy0_y, dxy1_y);
+
+ // Interpolate the gradient in z
+ (*grad)[2] = dxy1 - dxy0;
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut Interpolate(RealType *cix, OutputComponentType *out)
+ {
+ OutputComponentType dx00, dx01, dx10, dx11, dxy0, dxy1;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++)
+ {
+ // Interpolate the image intensity
+ dx00 = Superclass::lerp(fx, *d000, *d100);
+ dx01 = Superclass::lerp(fx, *d001, *d101);
+ dx10 = Superclass::lerp(fx, *d010, *d110);
+ dx11 = Superclass::lerp(fx, *d011, *d111);
+ dxy0 = Superclass::lerp(fy, dx00, dx10);
+ dxy1 = Superclass::lerp(fy, dx01, dx11);
+ *(out++) = Superclass::lerp(fz, dxy0, dxy1);
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut InterpolateNearestNeighbor(RealType *cix, OutputComponentType *out)
+ {
+ x0 = (int) floor(cix[0] + 0.5);
+ y0 = (int) floor(cix[1] + 0.5);
+ z0 = (int) floor(cix[2] + 0.5);
+
+ if (x0 >= 0 && x0 < xsize &&
+ y0 >= 0 && y0 < ysize &&
+ z0 >= 0 && z0 < zsize)
+ {
+ const InputComponentType *dp = dens(x0, y0, z0);
+ for(int iComp = 0; iComp < this->nSampled; iComp++)
+ {
+ out[iComp] = dp[iComp];
+ }
+ return Superclass::INSIDE;
+ }
+ else return Superclass::OUTSIDE;
+ }
+
+
+ template <class THistContainer>
+ void PartialVolumeHistogramSample(RealType *cix, const InputComponentType *fixptr, THistContainer &hist)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy, fyz = fy * fz, fxz = fx * fz, fxyz = fxy * fz;
+
+ RealType w111 = fxyz;
+ RealType w011 = fyz - fxyz;
+ RealType w101 = fxz - fxyz;
+ RealType w110 = fxy - fxyz;
+ RealType w001 = fz - fxz - w011;
+ RealType w010 = fy - fyz - w110;
+ RealType w100 = fx - fxy - w101;
+ RealType w000 = 1.0 - fx - fy + fxy - w001;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+
+ // Assign the appropriate weight to each part of the histogram
+ hist_line[*d000] += w000;
+ hist_line[*d001] += w001;
+ hist_line[*d010] += w010;
+ hist_line[*d011] += w011;
+ hist_line[*d100] += w100;
+ hist_line[*d101] += w101;
+ hist_line[*d110] += w110;
+ hist_line[*d111] += w111;
+ }
+ }
+ else
+ {
+ for(int iComp = 0; iComp < this->nSampled; iComp++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+ hist_line[0] += 1.0;
+ }
+ }
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramGradientSample(RealType *cix, const InputComponentType *fixptr, const THistContainer &hist_w, RealType *out_grad)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ // Outside values do not contribute to the gradient
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy, fyz = fy * fz, fxz = fx * fz;
+
+ // Some horrendous derivatives here! Wow!
+ RealType w111x = fyz, w111y = fxz, w111z = fxy;
+ RealType w011x = -fyz, w011y = fz - fxz, w011z = fy - fxy;
+ RealType w101x = fz - fyz, w101y = -fxz, w101z = fx - fxy;
+ RealType w110x = fy - fyz, w110y = fx - fxz, w110z = -fxy;
+ RealType w001x = -fz - w011x, w001y = -w011y, w001z = 1 - fx - w011z;
+ RealType w010x = -w110x, w010y = 1 - fz - w110y, w010z = -fy - w110z;
+ RealType w100x = 1 - fy - w101x, w100y = -fx - w101y, w100z = -w101z;
+ RealType w000x = -1 + fy - w001x, w000y = -1 + fx - w001y, w000z = -w001z;
+
+ // Initialize gradient to zero
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+ out_grad[2] = 0.0;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++, fixptr++)
+ {
+ // Just this line in the histogram
+ const RealType *f = hist_w[iComp][*fixptr];
+
+ // Take the weighted sum
+ RealType f000 = f[*d000], f001 = f[*d001], f010 = f[*d010], f011 = f[*d011];
+ RealType f100 = f[*d100], f101 = f[*d101], f110 = f[*d110], f111 = f[*d111];
+
+ out_grad[0] += w000x * f000 + w001x * f001 + w010x * f010 + w011x * f011 +
+ w100x * f100 + w101x * f101 + w110x * f110 + w111x * f111;
+
+ out_grad[1] += w000y * f000 + w001y * f001 + w010y * f010 + w011y * f011 +
+ w100y * f100 + w101y * f101 + w110y * f110 + w111y * f111;
+
+ out_grad[2] += w000z * f000 + w001z * f001 + w010z * f010 + w011z * f011 +
+ w100z * f100 + w101z * f101 + w110z * f110 + w111z * f111;
+ }
+ }
+ else
+ {
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+ out_grad[2] = 0.0;
+ }
+ }
+
+ RealType GetMask()
+ {
+ // Interpolate the mask
+ RealType dx00, dx01, dx10, dx11, dxy0, dxy1;
+ dx00 = this->lerp(fx, m000, m100);
+ dx01 = this->lerp(fx, m001, m101);
+ dx10 = this->lerp(fx, m010, m110);
+ dx11 = this->lerp(fx, m011, m111);
+ dxy0 = this->lerp(fy, dx00, dx10);
+ dxy1 = this->lerp(fy, dx01, dx11);
+ return this->lerp(fz, dxy0, dxy1);
+ }
+
+ RealType GetMaskAndGradient(RealType *mask_gradient)
+ {
+ // Interpolate the mask
+ RealType dx00, dx01, dx10, dx11, dxy0, dxy1;
+ dx00 = this->lerp(fx, m000, m100);
+ dx01 = this->lerp(fx, m001, m101);
+ dx10 = this->lerp(fx, m010, m110);
+ dx11 = this->lerp(fx, m011, m111);
+ dxy0 = this->lerp(fy, dx00, dx10);
+ dxy1 = this->lerp(fy, dx01, dx11);
+ RealType mask = this->lerp(fz, dxy0, dxy1);
+
+ // Compute the gradient of the mask
+ RealType dx00_x, dx01_x, dx10_x, dx11_x, dxy0_x, dxy1_x;
+ dx00_x = m100 - m000;
+ dx01_x = m101 - m001;
+ dx10_x = m110 - m010;
+ dx11_x = m111 - m011;
+ dxy0_x = this->lerp(fy, dx00_x, dx10_x);
+ dxy1_x = this->lerp(fy, dx01_x, dx11_x);
+ mask_gradient[0] = this->lerp(fz, dxy0_x, dxy1_x);
+
+ RealType dxy0_y, dxy1_y;
+ dxy0_y = dx10 - dx00;
+ dxy1_y = dx11 - dx01;
+ mask_gradient[1] = this->lerp(fz, dxy0_y, dxy1_y);
+
+ mask_gradient[2] = dxy1 - dxy0;
+
+ return mask;
+ }
+
+protected:
+
+ inline const InputComponentType *border_check(int X, int Y, int Z, RealType &mask)
+ {
+ if(X >= 0 && X < xsize && Y >= 0 && Y < ysize && Z >= 0 && Z < zsize)
+ {
+ mask = 1.0;
+ return dens(X,Y,Z);
+ }
+ else
+ {
+ mask = 0.0;
+ return this->def_value;
+ }
+ }
+
+ inline const InputComponentType *dens(int X, int Y, int Z)
+ {
+ return this->buffer + this->nComp * (X+xsize*(Y+ysize*Z));
+ }
+
+ // Image size
+ int xsize, ysize, zsize;
+
+ // State of current interpolation
+ const InputComponentType *d000, *d001, *d010, *d011, *d100, *d101, *d110, *d111;
+ RealType m000, m001, m010, m011, m100, m101, m110, m111;
+
+ RealType fx, fy, fz;
+ int x0, y0, z0, x1, y1, z1;
+
+};
+
+
+
+/**
+ * 2D fast linear interpolator - optimized for speed
+ */
+template <class TImage, class TFloat>
+class FastLinearInterpolator<TImage, TFloat, 2>
+ : public FastLinearInterpolatorBase<TImage, TFloat, 2>
+{
+public:
+ typedef TImage ImageType;
+ typedef FastLinearInterpolatorBase<ImageType, TFloat, 2> Superclass;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::OutputComponentType OutputComponentType;
+ typedef typename Superclass::RealType RealType;
+ typedef typename Superclass::InOut InOut;
+ typedef itk::ImageBase<2> ImageBaseType;
+
+ FastLinearInterpolator(ImageType *image) : Superclass(image)
+ {
+ xsize = image->GetLargestPossibleRegion().GetSize()[0];
+ ysize = image->GetLargestPossibleRegion().GetSize()[1];
+ }
+
+ FastLinearInterpolator(ImageBaseType *image, InputComponentType *buffer_ptr,
+ int n_components, int n_sampled = 0)
+ : Superclass(buffer_ptr, n_components, n_sampled)
+ {
+ xsize = image->GetLargestPossibleRegion().GetSize()[0];
+ ysize = image->GetLargestPossibleRegion().GetSize()[1];
+ }
+
+ /**
+ * Compute the pointers to the eight corners of the interpolating cube
+ */
+ InOut ComputeCorners(RealType *cix)
+ {
+ const InputComponentType *dp;
+
+ x0 = (int) floor(cix[0]); fx = cix[0] - x0;
+ y0 = (int) floor(cix[1]); fy = cix[1] - y0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize)
+ {
+ // The sample point is completely inside
+ dp = dens(x0, y0);
+ d00 = dp;
+ d10 = dp+this->nComp;
+ dp += xsize*this->nComp;
+ d01 = dp;
+ d11 = dp+this->nComp;
+
+ // The mask is one
+ this->status = Superclass::INSIDE;
+ }
+ else if (x0 >= -1 && x1 <= xsize &&
+ y0 >= -1 && y1 <= ysize)
+ {
+ // The sample point is on the border region
+ d00 = border_check(x0, y0, m00);
+ d01 = border_check(x0, y1, m01);
+ d10 = border_check(x1, y0, m10);
+ d11 = border_check(x1, y1, m11);
+
+ // The mask is between 0 and 1
+ this->status = Superclass::BORDER;
+ }
+ else
+ {
+ // The mask is zero
+ this->status = Superclass::OUTSIDE;
+ }
+
+ return this->status;
+ }
+
+ /**
+ * Interpolate at position cix, placing the intensity values in out and gradient
+ * values in grad (in strides of VDim)
+ */
+ InOut InterpolateWithGradient(RealType *cix, OutputComponentType *out, OutputComponentType **grad)
+ {
+ RealType dx0, dx1;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++, grad++,
+ d00++, d01++, d10++, d11++)
+ {
+ // Interpolate the image intensity
+ dx0 = Superclass::lerp(fx, *d00, *d10);
+ dx1 = Superclass::lerp(fx, *d01, *d11);
+ *(out++) = Superclass::lerp(fy, dx0, dx1);
+
+ // Interpolate the gradient in x
+ (*grad)[0] = this->lerp(fy, *d10 - *d00, *d11 - *d01);
+
+ // Interpolate the gradient in y
+ (*grad)[1] = dx1 - dx0;
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut Interpolate(RealType *cix, OutputComponentType *out)
+ {
+ OutputComponentType dx0, dx1;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++,
+ d00++, d01++, d10++, d11++)
+ {
+ // Interpolate the image intensity
+ dx0 = Superclass::lerp(fx, *d00, *d10);
+ dx1 = Superclass::lerp(fx, *d01, *d11);
+ *(out++) = Superclass::lerp(fy, dx0, dx1);
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut InterpolateNearestNeighbor(RealType *cix, OutputComponentType *out)
+ {
+ x0 = (int) floor(cix[0] + 0.5);
+ y0 = (int) floor(cix[1] + 0.5);
+
+ if (x0 >= 0 && x0 < xsize && y0 >= 0 && y0 < ysize)
+ {
+ const InputComponentType *dp = dens(x0, y0);
+ for(int iComp = 0; iComp < this->nSampled; iComp++)
+ {
+ out[iComp] = dp[iComp];
+ }
+ return Superclass::INSIDE;
+ }
+ else return Superclass::OUTSIDE;
+ }
+
+
+ template <class THistContainer>
+ void PartialVolumeHistogramSample(RealType *cix, const InputComponentType *fixptr, THistContainer &hist)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy;
+
+ RealType w11 = fxy;
+ RealType w01 = fy - fxy;
+ RealType w10 = fx - fxy;
+ RealType w00 = 1.0 - fx - fy + fxy;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++,
+ d00++, d01++, d10++, d11++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+
+ // Assign the appropriate weight to each part of the histogram
+ hist_line[*d00] += w00;
+ hist_line[*d01] += w01;
+ hist_line[*d10] += w10;
+ hist_line[*d11] += w11;
+ }
+ }
+ else
+ {
+ for(int iComp = 0; iComp < this->nSampled; iComp++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+ hist_line[0] += 1.0;
+ }
+ }
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramGradientSample(RealType *cix, const InputComponentType *fixptr, const THistContainer &hist_w, RealType *out_grad)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ // Outside values do not contribute to the gradient
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Some horrendous derivatives here! Wow!
+ RealType w11x = fy, w11y = fx;
+ RealType w01x = -fy, w01y = 1 - fx;
+ RealType w10x = 1 - fy, w10y = -fx;
+ RealType w00x = fy - 1, w00y = fx - 1;
+
+ // Initialize gradient to zero
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nSampled; iComp++,
+ d00++, d01++, d10++, d11++, fixptr++)
+ {
+ // Just this line in the histogram
+ const RealType *f = hist_w[iComp][*fixptr];
+
+ // Take the weighted sum
+ RealType f00 = f[*d00], f01 = f[*d01];
+ RealType f10 = f[*d10], f11 = f[*d11];
+
+ out_grad[0] += w00x * f00 + w01x * f01 + w10x * f10 + w11x * f11;
+ out_grad[1] += w00y * f00 + w01y * f01 + w10y * f10 + w11y * f11;
+ }
+ }
+ else
+ {
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+ }
+ }
+
+ RealType GetMask()
+ {
+ // Interpolate the mask
+ RealType dx0, dx1;
+ dx0 = this->lerp(fx, m00, m10);
+ dx1 = this->lerp(fx, m01, m11);
+ return this->lerp(fy, dx0, dx1);
+ }
+
+ RealType GetMaskAndGradient(RealType *mask_gradient)
+ {
+ // Interpolate the mask
+ RealType dx0, dx1;
+ dx0 = this->lerp(fx, m00, m10);
+ dx1 = this->lerp(fx, m01, m11);
+ RealType mask = this->lerp(fy, dx0, dx1);
+
+ // Compute the gradient of the mask
+ mask_gradient[0] = this->lerp(fy, m10 - m00, m11 - m01);
+ mask_gradient[1] = dx1 - dx0;
+
+ return mask;
+ }
+
+protected:
+
+ inline const InputComponentType *border_check(int X, int Y, RealType &mask)
+ {
+ if(X >= 0 && X < xsize && Y >= 0 && Y < ysize)
+ {
+ mask = 1.0;
+ return dens(X,Y);
+ }
+ else
+ {
+ mask = 0.0;
+ return this->def_value;
+ }
+ }
+
+ inline const InputComponentType *dens(int X, int Y)
+ {
+ return this->buffer + this->nComp * (X+xsize*Y);
+ }
+
+ // Image size
+ int xsize, ysize;
+
+ // State of current interpolation
+ const InputComponentType *d00, *d01, *d10, *d11;
+ RealType m00, m01, m10, m11;
+
+ RealType fx, fy;
+ int x0, y0, x1, y1;
+};
+
+
+#endif
diff --git a/Logic/Slicing/IRISSlicer.h b/Logic/Slicing/IRISSlicer.h
index d1affcf..b1b55bf 100644
--- a/Logic/Slicing/IRISSlicer.h
+++ b/Logic/Slicing/IRISSlicer.h
@@ -37,6 +37,7 @@
#include <ImageCoordinateTransform.h>
+#include "RLEImageRegionConstIterator.h"
#include <itkImageToImageFilter.h>
#include <itkImageSliceConstIteratorWithIndex.h>
#include <itkImageRegionIteratorWithIndex.h>
@@ -54,7 +55,7 @@
* directions, accomodating different positions of the display space origin in the
* image space.
*/
-template <class TInputImage, class TOutputImage>
+template <class TInputImage, class TOutputImage, class TPreviewImage>
class ITK_EXPORT IRISSlicer
: public itk::ImageToImageFilter<TInputImage, TOutputImage>
{
@@ -70,11 +71,17 @@ public:
typedef typename InputImageType::PixelType InputPixelType;
typedef typename InputImageType::InternalPixelType InputComponentType;
+ typedef TPreviewImage PreviewImageType;
+ typedef typename InputImageType::ConstPointer PreviewImagePointer;
+ typedef typename InputImageType::PixelType PreviewPixelType;
+ typedef typename InputImageType::InternalPixelType PreviewComponentType;
+
typedef TOutputImage OutputImageType;
typedef typename OutputImageType::Pointer OutputImagePointer;
typedef typename OutputImageType::PixelType OutputPixelType;
typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
/** Method for creation through the object factory. */
itkNewMacro(Self)
@@ -117,12 +124,12 @@ public:
the data from the preview input. This is used in the speed preview
framework, but could also be adapted for other features. Setting the
preview input to NULL disables this feature. */
- void SetPreviewInput(InputImageType *input);
+ void SetPreviewInput(PreviewImageType *input);
/**
Get the preview input.
*/
- InputImageType *GetPreviewInput();
+ PreviewImageType *GetPreviewInput();
/**
* Indicate whether the main input should always be bypassed when the preview
@@ -167,6 +174,8 @@ protected:
*/
virtual void GenerateData();
+ template <class TSourceImage> void DoGenerateData(const TSourceImage *source);
+
private:
IRISSlicer(const Self&); //purposely not implemented
void operator=(const Self&); //purposely not implemented
@@ -199,8 +208,173 @@ private:
// void CopySliceLineBackwardPixelBackward(InputIteratorType, OutputImageType *);
};
+//specialization for run-length encoded image
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+class ITK_EXPORT IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage >
+ : public itk::ImageToImageFilter<RLEImage<TPixel, 3, CounterType>, TOutputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef IRISSlicer Self;
+ typedef RLEImage<TPixel, 3, CounterType> InputImageType;
+ typedef itk::ImageToImageFilter<InputImageType, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ typedef typename InputImageType::ConstPointer InputImagePointer;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+
+ typedef TPreviewImage PreviewImageType;
+ typedef typename PreviewImageType::ConstPointer PreviewImagePointer;
+ typedef typename PreviewImageType::PixelType PreviewPixelType;
+ typedef typename PreviewImageType::InternalPixelType PreviewComponentType;
+
+ typedef TOutputImage OutputImageType;
+ typedef typename OutputImageType::Pointer OutputImagePointer;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(IRISSlicer, ImageToImageFilter)
+
+ /** Some more typedefs. */
+ typedef typename InputImageType::RegionType InputImageRegionType;
+ typedef typename OutputImageType::RegionType OutputImageRegionType;
+ typedef itk::ImageSliceConstIteratorWithIndex<InputImageType> InputIteratorType;
+ typedef itk::ImageRegionIteratorWithIndex<OutputImageType> SimpleOutputIteratorType;
+ typedef itk::ImageLinearIteratorWithIndex<OutputImageType> OutputIteratorType;
+
+ /** Set the current slice index */
+ itkSetMacro(SliceIndex, unsigned int);
+ itkGetMacro(SliceIndex, unsigned int);
+
+ /** Set the image axis along which the subsequent slices lie */
+ itkSetMacro(SliceDirectionImageAxis, unsigned int);
+ itkGetMacro(SliceDirectionImageAxis, unsigned int);
+
+ /** Set the image axis along which the subsequent lines in a slice lie */
+ itkSetMacro(LineDirectionImageAxis, unsigned int);
+ itkGetMacro(LineDirectionImageAxis, unsigned int);
+
+ /** Set the image axis along which the subsequent pixels in a line lie */
+ itkSetMacro(PixelDirectionImageAxis, unsigned int);
+ itkGetMacro(PixelDirectionImageAxis, unsigned int);
+
+ /** Set the direction of line traversal */
+ itkSetMacro(LineTraverseForward, bool);
+ itkGetMacro(LineTraverseForward, bool);
+
+ /** Set the direction of pixel traversal */
+ itkSetMacro(PixelTraverseForward, bool);
+ itkGetMacro(PixelTraverseForward, bool);
+
+ /** Add a second `preview' input to the slicer. The slicer will check if
+ the preview input is newer than the main input, and if so, will obtain
+ the data from the preview input. This is used in the speed preview
+ framework, but could also be adapted for other features. Setting the
+ preview input to NULL disables this feature. */
+ void SetPreviewInput(PreviewImageType *input);
+
+ /**
+ Get the preview input.
+ */
+ PreviewImageType *GetPreviewInput();
+
+ /**
+ * Indicate whether the main input should always be bypassed when the preview
+ * input is present. If not, the slicer will use whichever input is newer.
+ */
+ itkGetMacro(BypassMainInput, bool)
+ itkSetMacro(BypassMainInput, bool)
+
+protected:
+
+ IRISSlicer();
+ virtual ~IRISSlicer() {};
+ void PrintSelf(std::ostream &s, itk::Indent indent) const;
+
+ /**
+ * IRISSlicer can produce an image which is a different
+ * resolution than its input image. As such, IRISSlicer
+ * needs to provide an implementation for
+ * GenerateOutputInformation() in order to inform the pipeline
+ * execution model. The original documentation of this method is
+ * below.
+ *
+ * \sa ProcessObject::GenerateOutputInformaton() */
+ virtual void GenerateOutputInformation();
+
+ void GenerateInputRequestedRegion();
+
+ /**
+ * This method maps an input region to an output region
+ */
+ virtual void CallCopyOutputRegionToInputRegion(InputImageRegionType &destRegion,
+ const OutputImageRegionType &srcRegion);
+
+ void GenerateData();
+ //void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
+ // itk::ThreadIdType threadId);
+
+ /** Uncompresses a RLE line into a buffer pointed by out.
+ * After each pixel is written, adds stride to the pointer.
+ * The buffer needs to have enough room.
+ * No error checking is conducted. */
+ inline void uncompressLine(const typename InputImageType::RLLine & line, TPixel *out, long stride)
+ {
+ //complete Run-Length Lines have to be buffered
+ itkAssertOrThrowMacro(this->GetInput()->GetBufferedRegion().GetSize(0)
+ == this->GetInput()->GetLargestPossibleRegion().GetSize(0),
+ "BufferedRegion must contain complete run-length lines!");
+#ifdef _DEBUG
+ int debugCount = 0;
+#endif
+ for (int x = 0; x < line.size(); x++)
+ for (CounterType r = 0; r < line[x].first; r++)
+ {
+#ifdef _DEBUG
+ debugCount++;
+ assert(debugCount <= this->GetInput()->GetLargestPossibleRegion().GetSize(0));
+#endif
+ *out = line[x].second;
+ out += stride;
+ }
+ }
+
+private:
+ IRISSlicer(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // Current slice in each of the dimensions
+ unsigned int m_SliceIndex;
+
+ // Image axis corresponding to the slice direction
+ unsigned int m_SliceDirectionImageAxis;
+
+ // Image axis corresponding to the line direction
+ unsigned int m_LineDirectionImageAxis;
+
+ // Image axis corresponding to the pixel direction
+ unsigned int m_PixelDirectionImageAxis;
+
+ // Whether the line direction is reversed
+ bool m_LineTraverseForward;
+
+ // Whether the pixel direction is reversed
+ bool m_PixelTraverseForward;
+
+ // Whether the main input should always be bypassed
+ bool m_BypassMainInput;
+
+};
+
#ifndef ITK_MANUAL_INSTANTIATION
#include "IRISSlicer.txx"
+#include "IRISSlicer_RLE.txx"
#endif
#endif //__IRISSlicer_h_
diff --git a/Logic/Slicing/IRISSlicer.txx b/Logic/Slicing/IRISSlicer.txx
index 01a169d..e1e5694 100644
--- a/Logic/Slicing/IRISSlicer.txx
+++ b/Logic/Slicing/IRISSlicer.txx
@@ -6,8 +6,8 @@
Date: $Date: 2007/12/30 04:05:15 $
Version: $Revision: 1.6 $
Copyright (c) 2007 Paul A. Yushkevich
-
- This file is part of ITK-SNAP
+
+ This file is part of ITK-SNAP
ITK-SNAP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -29,7 +29,7 @@
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE. See the above copyright notices for more information.
+ PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "itkDefaultPixelAccessor.h"
@@ -39,8 +39,26 @@
#include "itkVectorImage.h"
#include "itkVectorImageToImageAdaptor.h"
-template <class TInputImage, class TOutputImage>
-IRISSlicer<TInputImage, TOutputImage>
+template <class TImage>
+class IRISSlicerComponentHelper
+{
+public:
+ static void SetImageComponents(TImage *image, unsigned int n) {}
+};
+
+template <class TPixel, unsigned int VDim>
+class IRISSlicerComponentHelper< itk::VectorImage<TPixel, VDim> >
+{
+public:
+ static void SetImageComponents(itk::VectorImage<TPixel, VDim> *image, unsigned int n)
+ {
+ image->SetNumberOfComponentsPerPixel(n);
+ }
+};
+
+
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
::IRISSlicer()
{
// Two inputs are allowed (second being the preview input)
@@ -57,7 +75,7 @@ IRISSlicer<TInputImage, TOutputImage>
m_SliceDirectionImageAxis = 2;
m_LineDirectionImageAxis = 1;
m_PixelDirectionImageAxis = 0;
-
+
m_PixelTraverseForward = true;
m_LineTraverseForward = true;
@@ -67,26 +85,27 @@ IRISSlicer<TInputImage, TOutputImage>
m_BypassMainInput = false;
}
-template <class TInputImage, class TOutputImage>
-void IRISSlicer<TInputImage, TOutputImage>
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+void
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
::GenerateOutputInformation()
{
// Get pointers to the inputs and outputs
typename Superclass::InputImageConstPointer inputPtr = this->GetInput();
typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
-
+
// The inputs and outputs should exist
if (!outputPtr || !inputPtr) return;
// Get the input's largest possible region
InputImageRegionType inputRegion = inputPtr->GetLargestPossibleRegion();
-
+
// Arrays to specify the output spacing and origin
double outputSpacing[2];
double outputOrigin[2] = {0.0,0.0};
-
+
// Initialize the output image region
- OutputImageRegionType outputRegion;
+ OutputImageRegionType outputRegion;
outputRegion.SetIndex(0,inputRegion.GetIndex(m_PixelDirectionImageAxis));
outputRegion.SetSize(0,inputRegion.GetSize(m_PixelDirectionImageAxis));
outputRegion.SetIndex(1,inputRegion.GetIndex(m_LineDirectionImageAxis));
@@ -95,17 +114,23 @@ void IRISSlicer<TInputImage, TOutputImage>
// Set the origin and spacing
outputSpacing[0] = inputPtr->GetSpacing()[m_PixelDirectionImageAxis];
outputSpacing[1] = inputPtr->GetSpacing()[m_LineDirectionImageAxis];
-
+
// Set the region of the output slice
outputPtr->SetLargestPossibleRegion(outputRegion);
// Set the spacing and origin
outputPtr->SetSpacing(outputSpacing);
outputPtr->SetOrigin(outputOrigin);
+
+ // If the slice is a vector image, then we must set the number
+ // out output components appropriately
+ IRISSlicerComponentHelper<TOutputImage>::SetImageComponents(
+ outputPtr, inputPtr->GetNumberOfComponentsPerPixel());
}
-template <class TInputImage, class TOutputImage>
-void IRISSlicer<TInputImage, TOutputImage>
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+void
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
::CallCopyOutputRegionToInputRegion(InputImageRegionType &destRegion,
const OutputImageRegionType &srcRegion)
{
@@ -115,11 +140,11 @@ void IRISSlicer<TInputImage, TOutputImage>
// Set the index of the region in that dimension to the number of the slice
destRegion.SetIndex(m_SliceDirectionImageAxis,m_SliceIndex);
- // Compute the bounds of the input region for the other two dimensions (for
- // the case when the output region is not equal to the largest possible
+ // Compute the bounds of the input region for the other two dimensions (for
+ // the case when the output region is not equal to the largest possible
// region (i.e., we are requesting a partial slice)
- // The size of the region does not depend of the direction of axis
+ // The size of the region does not depend of the direction of axis
// traversal
destRegion.SetSize(m_PixelDirectionImageAxis,srcRegion.GetSize(0));
destRegion.SetSize(m_LineDirectionImageAxis,srcRegion.GetSize(1));
@@ -133,8 +158,8 @@ void IRISSlicer<TInputImage, TOutputImage>
{
// This case is a bit trickier. The axis direction is reversed, so
// range [i,...,i+s-1] in the output image corresponds to the range
- // [S-(i+s),S-(i+1)] in the input image, where i is the in-slice index,
- // S is the largest size of the input and s is the requested size of the
+ // [S-(i+s),S-(i+1)] in the input image, where i is the in-slice index,
+ // S is the largest size of the input and s is the requested size of the
// output
destRegion.SetIndex(
m_PixelDirectionImageAxis,
@@ -148,7 +173,7 @@ void IRISSlicer<TInputImage, TOutputImage>
destRegion.SetIndex(m_LineDirectionImageAxis,srcRegion.GetIndex(1));
}
else
- {
+ {
destRegion.SetIndex(
m_LineDirectionImageAxis,
this->GetInput()->GetLargestPossibleRegion().GetSize(m_LineDirectionImageAxis)
@@ -156,9 +181,9 @@ void IRISSlicer<TInputImage, TOutputImage>
}
}
-template <class TInputImage, class TOutputImage>
+template <class TInputImage, class TOutputImage, class TPreviewImage>
void
-IRISSlicer<TInputImage, TOutputImage>
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
::GenerateInputRequestedRegion()
{
// If there is a preview input, and the pipeline of the preview input is
@@ -200,26 +225,21 @@ IRISSlicer<TInputImage, TOutputImage>
#include "itkImageRegionConstIterator.h"
#include "itkImageConstIterator.h"
-/*
-template <class TInputImage, class TOutputImage>
+// This method is templated to allow preview input and actual input to be different
+// types
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+template <class TSourceImage>
void
-IRISSlicer<TInputImage, TOutputImage>
-::ThreadedGenerateData(
- const OutputImageRegionType &outputRegionForThread,
- ThreadIdType threadId)
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
+::DoGenerateData(const TSourceImage *inputPtr)
{
- // Here's the input and output
- const InputImageType *inputPtr = this->GetInput();
- OutputImageType *outputPtr = this->GetOutput();
-
- // Decide if we want to use the preview input instead
- const InputImageType *preview =
- (InputImageType *) this->GetInputs()[1].GetPointer();
+ typedef typename TSourceImage::AccessorFunctorType AccessorFunctorType;
+ typedef typename TSourceImage::AccessorType AccessorType;
+ typedef typename TSourceImage::OffsetValueType OffsetType;
+ typedef typename TSourceImage::InternalPixelType ComponentType;
- if(preview && preview->GetMTime() > inputPtr->GetMTime())
- {
- inputPtr = preview;
- }
+ // The output image
+ OutputImageType *outputPtr = this->GetOutput();
// Allocate (why is this necessary?)
this->AllocateOutputs();
@@ -260,32 +280,26 @@ IRISSlicer<TInputImage, TOutputImage>
xStartVoxel[m_SliceDirectionImageAxis] =
szVol[m_SliceDirectionImageAxis] == 1 ? 0 : m_SliceIndex;
- // Get the offset of the first voxel
- size_t iStart = dot_product(stride_image, xStartVoxel);
-
- // Get the size of the output region (whole slice)
- typename OutputImageType::RegionType rgn = outputPtr->GetBufferedRegion();
- size_t nPixel = rgn.GetSize()[0], nLine = rgn.GetSize()[1];
+ // Get the offset of the first voxel. As pointed out by Roman Grothausmann, the VNL
+ // dot product causes overflow so we compute directly.
+ size_t iStart = 0;
+ for(int i = 0; i < 3; i++)
+ iStart += static_cast<long>(stride_image[i]) * static_cast<long>(xStartVoxel[i]);
// Get pointers to input and output data
- const InputComponentType *pSource = inputPtr->GetBufferPointer();
+ const ComponentType *pSource = inputPtr->GetBufferPointer();
// Set up the output iterator
typedef itk::ImageLinearIteratorWithIndex<OutputImageType> OutIterType;
OutIterType it_out(outputPtr, outputPtr->GetBufferedRegion());
// Get the pixel accessor functor - for unified access to voxels
- typedef typename InputImageType::AccessorFunctorType AccessorFunctorType;
- typedef typename InputImageType::AccessorType AccessorType;
- typedef typename InputImageType::OffsetValueType OffsetType;
AccessorType accessor = inputPtr->GetPixelAccessor();
AccessorFunctorType accessor_functor;
accessor_functor.SetPixelAccessor(accessor);
accessor_functor.SetBegin(pSource);
// Position the source at the first component of the first voxel to traverse
- // OffsetType offset =
- const InputComponentType *pBegin = pSource;
pSource += iStart;
// Main loop: copy data from source to target
@@ -307,119 +321,34 @@ IRISSlicer<TInputImage, TOutputImage>
it_out.NextLine();
pSource += sLineDelta;
}
-
}
-*/
-template <class TInputImage, class TOutputImage>
-void IRISSlicer<TInputImage, TOutputImage>
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+void
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
::GenerateData()
{
// Here's the input and output
const InputImageType *inputPtr = this->GetInput();
- OutputImageType *outputPtr = this->GetOutput();
// Decide if we want to use the preview input instead
- const InputImageType *preview =
- (InputImageType *) this->GetInputs()[1].GetPointer();
-
- if(preview)
- if(m_BypassMainInput || preview->GetMTime() > inputPtr->GetMTime())
- inputPtr = preview;
-
- // Allocate (why is this necessary?)
- this->AllocateOutputs();
-
- // Get the image dimensions
- typename InputImageType::SizeType szVol = inputPtr->GetBufferedRegion().GetSize();
-
- // Set the strides in image coordinates
- Vector3i stride_image(1, szVol[0], szVol[1] * szVol[0]);
+ const PreviewImageType *preview =
+ (PreviewImageType *) this->GetInputs()[1].GetPointer();
- // Scale the strides by the number of components. We can't get it from the
- // image itself since the image may be an ImageAdaptor, so we need to use
- // the pixel container's size
- long nintpix = inputPtr->GetPixelContainer()->Size();
- long nvoxels = szVol[2] * stride_image[2];
- unsigned int ncomp = static_cast<unsigned int>(nintpix/nvoxels);
- stride_image *= ncomp;
-
- // Determine the strides for the pixel step and line step
- int sPixel = (m_PixelTraverseForward ? 1 : -1) *
- stride_image[m_PixelDirectionImageAxis];
- int sLine = (m_LineTraverseForward ? 1 : -1) *
- stride_image[m_LineDirectionImageAxis];
-
- // We never take full line-strides, because as we iterate, we
- // take n pixel-strides before needing to worry about changing
- // the line. Therefore, we compute the step needed to go to the
- // start of next line after taking n pixel-strides
- int sRowOfPixels = sPixel * szVol[m_PixelDirectionImageAxis];
- int sLineDelta = sLine - sRowOfPixels;
-
- // Determine the first voxel that we will traverse
- Vector3i xStartVoxel;
- xStartVoxel[m_PixelDirectionImageAxis] =
- m_PixelTraverseForward ? 0 : szVol[m_PixelDirectionImageAxis] - 1;
- xStartVoxel[m_LineDirectionImageAxis] =
- m_LineTraverseForward ? 0 : szVol[m_LineDirectionImageAxis] - 1;
- xStartVoxel[m_SliceDirectionImageAxis] =
- szVol[m_SliceDirectionImageAxis] == 1 ? 0 : m_SliceIndex;
-
- // Get the offset of the first voxel. As pointed out by Roman Grothausmann, the VNL
- // dot product causes overflow so we compute directly.
- size_t iStart = 0;
- for(int i = 0; i < 3; i++)
- iStart += static_cast<long>(stride_image[i]) * static_cast<long>(xStartVoxel[i]);
-
- // Get the size of the output region (whole slice)
- typename OutputImageType::RegionType rgn = outputPtr->GetBufferedRegion();
- size_t nPixel = rgn.GetSize()[0], nLine = rgn.GetSize()[1];
-
- // Get pointers to input and output data
- const InputComponentType *pSource = inputPtr->GetBufferPointer();
-
- // Set up the output iterator
- typedef itk::ImageLinearIteratorWithIndex<OutputImageType> OutIterType;
- OutIterType it_out(outputPtr, outputPtr->GetBufferedRegion());
-
- // Get the pixel accessor functor - for unified access to voxels
- typedef typename InputImageType::AccessorFunctorType AccessorFunctorType;
- typedef typename InputImageType::AccessorType AccessorType;
- typedef typename InputImageType::OffsetValueType OffsetType;
- AccessorType accessor = inputPtr->GetPixelAccessor();
- AccessorFunctorType accessor_functor;
- accessor_functor.SetPixelAccessor(accessor);
- accessor_functor.SetBegin(pSource);
-
- // Position the source at the first component of the first voxel to traverse
- // OffsetType offset =
- const InputComponentType *pBegin = pSource;
- pSource += iStart;
-
- // Main loop: copy data from source to target
- while(!it_out.IsAtEnd())
+ if(preview &&
+ (m_BypassMainInput || preview->GetMTime() > inputPtr->GetMTime()))
{
- while( !it_out.IsAtEndOfLine() )
- {
- // Use the accessor
- accessor_functor.SetBegin(pSource);
- OutputPixelType val = accessor_functor.Get(*pSource);
-
- // Set the pixel
- it_out.Set(val);
-
- // Go to next pixel
- ++it_out;
- pSource += sPixel;
- }
- it_out.NextLine();
- pSource += sLineDelta;
+ this->DoGenerateData(preview);
+ }
+ else
+ {
+ this->DoGenerateData(inputPtr);
}
}
-template <class TInputImage, class TOutputImage>
-void IRISSlicer<TInputImage, TOutputImage>
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+void
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
::PrintSelf(std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
@@ -432,35 +361,18 @@ void IRISSlicer<TInputImage, TOutputImage>
os << indent << "Pixels Traversed Forward: " << m_PixelTraverseForward << std::endl;
}
-template <class TInputImage, class TOutputImage>
-void IRISSlicer<TInputImage, TOutputImage>
-::SetPreviewInput(InputImageType *input)
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+void
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
+::SetPreviewInput(PreviewImageType *input)
{
this->SetNthInput(1, input);
}
-template <class TInputImage, class TOutputImage>
-typename IRISSlicer<TInputImage, TOutputImage>::InputImageType *
-IRISSlicer<TInputImage, TOutputImage>
+template <class TInputImage, class TOutputImage, class TPreviewImage>
+typename IRISSlicer<TInputImage, TOutputImage,TPreviewImage>::PreviewImageType *
+IRISSlicer<TInputImage, TOutputImage, TPreviewImage>
::GetPreviewInput()
{
- return const_cast<InputImageType *>(this->GetInput(1));
-}
-
-/*
-template <class TInputImage, class TOutputImage>
-void
-IRISSlicer<TInputImage, TOutputImage>
-::BeforeThreadedGenerateData()
-{
-
-}
-
-template <class TInputImage, class TOutputImage>
-void
-IRISSlicer<TInputImage, TOutputImage>
-::AfterThreadedGenerateData()
-{
-
+ return static_cast<PreviewImageType *>(itk::ProcessObject::GetInput(1));
}
-*/
diff --git a/Logic/Slicing/IRISSlicer_RLE.txx b/Logic/Slicing/IRISSlicer_RLE.txx
new file mode 100644
index 0000000..b8ff5c2
--- /dev/null
+++ b/Logic/Slicing/IRISSlicer_RLE.txx
@@ -0,0 +1,353 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: IRISSlicer.txx,v $
+ Language: C++
+ Date: $Date: 2007/12/30 04:05:15 $
+ Version: $Revision: 1.6 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#include "itkDefaultPixelAccessor.h"
+#include "itkDefaultPixelAccessorFunctor.h"
+#include "itkImageAdaptor.h"
+#include "itkImage.h"
+#include "itkVectorImage.h"
+#include "itkVectorImageToImageAdaptor.h"
+
+//now goes version specialized for RLEImage
+//The only difference is ThreadedGenerateData method
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::IRISSlicer()
+{
+ // Two inputs are allowed (second being the preview input)
+ this->SetNumberOfIndexedInputs(2);
+ this->SetPreviewInput(NULL);
+
+ // There is a single input to the filter
+ this->SetNumberOfRequiredInputs(1);
+
+ // There are three outputs from the filter
+ this->SetNumberOfRequiredOutputs(1);
+
+ // Initialize the slice to be along the z-direction in the image
+ m_SliceDirectionImageAxis = 2;
+ m_LineDirectionImageAxis = 1;
+ m_PixelDirectionImageAxis = 0;
+
+ m_PixelTraverseForward = true;
+ m_LineTraverseForward = true;
+
+ // Initialize to a zero slice index
+ m_SliceIndex = 0;
+}
+
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+void IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::GenerateOutputInformation()
+{
+ // Get pointers to the inputs and outputs
+ typename Superclass::InputImageConstPointer inputPtr = this->GetInput();
+ typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
+
+ // The inputs and outputs should exist
+ if (!outputPtr || !inputPtr) return;
+
+ // Get the input's largest possible region
+ InputImageRegionType inputRegion = inputPtr->GetLargestPossibleRegion();
+
+ // Arrays to specify the output spacing and origin
+ double outputSpacing[2];
+ double outputOrigin[2] = { 0.0, 0.0 };
+
+ // Initialize the output image region
+ OutputImageRegionType outputRegion;
+ outputRegion.SetIndex(0, inputRegion.GetIndex(m_PixelDirectionImageAxis));
+ outputRegion.SetSize(0, inputRegion.GetSize(m_PixelDirectionImageAxis));
+ outputRegion.SetIndex(1, inputRegion.GetIndex(m_LineDirectionImageAxis));
+ outputRegion.SetSize(1, inputRegion.GetSize(m_LineDirectionImageAxis));
+
+ // Set the origin and spacing
+ outputSpacing[0] = inputPtr->GetSpacing()[m_PixelDirectionImageAxis];
+ outputSpacing[1] = inputPtr->GetSpacing()[m_LineDirectionImageAxis];
+
+ // Set the region of the output slice
+ outputPtr->SetLargestPossibleRegion(outputRegion);
+
+ // Set the spacing and origin
+ outputPtr->SetSpacing(outputSpacing);
+ outputPtr->SetOrigin(outputOrigin);
+}
+
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+void IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::CallCopyOutputRegionToInputRegion(InputImageRegionType &destRegion,
+ const OutputImageRegionType &srcRegion)
+{
+ // Set the size of the region to 1 in the slice direction
+ destRegion.SetSize(m_SliceDirectionImageAxis, 1);
+
+ // Set the index of the region in that dimension to the number of the slice
+ destRegion.SetIndex(m_SliceDirectionImageAxis, m_SliceIndex);
+
+ // Compute the bounds of the input region for the other two dimensions (for
+ // the case when the output region is not equal to the largest possible
+ // region (i.e., we are requesting a partial slice)
+
+ // The size of the region does not depend of the direction of axis
+ // traversal
+ destRegion.SetSize(m_PixelDirectionImageAxis, srcRegion.GetSize(0));
+ destRegion.SetSize(m_LineDirectionImageAxis, srcRegion.GetSize(1));
+
+ // However, the index of the region does depend on the direction!
+ if (m_PixelTraverseForward)
+ {
+ destRegion.SetIndex(m_PixelDirectionImageAxis, srcRegion.GetIndex(0));
+ }
+ else
+ {
+ // This case is a bit trickier. The axis direction is reversed, so
+ // range [i,...,i+s-1] in the output image corresponds to the range
+ // [S-(i+s),S-(i+1)] in the input image, where i is the in-slice index,
+ // S is the largest size of the input and s is the requested size of the
+ // output
+ destRegion.SetIndex(
+ m_PixelDirectionImageAxis,
+ this->GetInput()->GetLargestPossibleRegion().GetSize(m_PixelDirectionImageAxis)
+ - (srcRegion.GetIndex(0) + srcRegion.GetSize(0)));
+ }
+
+ // Same as above for line index
+ if (m_LineTraverseForward)
+ {
+ destRegion.SetIndex(m_LineDirectionImageAxis, srcRegion.GetIndex(1));
+ }
+ else
+ {
+ destRegion.SetIndex(
+ m_LineDirectionImageAxis,
+ this->GetInput()->GetLargestPossibleRegion().GetSize(m_LineDirectionImageAxis)
+ - (srcRegion.GetIndex(1) + srcRegion.GetSize(1)));
+ }
+}
+
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+void IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::GenerateInputRequestedRegion()
+{
+ // If there is a preview input, and the pipeline of the preview input is
+ // older than the main input, we don't ask the preview to generate a new
+ // slice. Instead, we leave it's requested region as is, so that the
+ // preview does not actually update. This results in a selective behavior,
+ // where we choose the preview if it's newer than the main input, otherwise
+ // we choose the normal input
+
+ // Actually compute what the input region should be
+ InputImageRegionType inputRegion;
+ this->CallCopyOutputRegionToInputRegion(
+ inputRegion, this->GetOutput()->GetRequestedRegion());
+
+ // Get the main input
+ InputImageType *main = const_cast<InputImageType *>(this->GetInput(0));
+
+ // Decide if we want to use the preview input instead
+ InputImageType *preview = const_cast<InputImageType *>(this->GetInput(1));
+
+ if (preview)
+ {
+ if (preview->GetPipelineMTime() > main->GetMTime())
+ {
+ // We want the preview to be updated
+ preview->SetRequestedRegion(inputRegion);
+ }
+ else
+ {
+ // Ignore the preview, prevent it from updating itself needlessly
+ preview->SetRequestedRegion(preview->GetBufferedRegion());
+ }
+
+ main->SetRequestedRegion(inputRegion);
+ }
+}
+
+#include "RLEImageRegionConstIterator.h"
+
+#define sign(forward) (forward ? 1 : -1)
+
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+void IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::GenerateData()
+{
+ // Here's the input and output
+ const InputImageType *inputPtr = this->GetInput();
+ OutputImageType *outputPtr = this->GetOutput();
+
+ // Decide if we want to use the preview input instead
+ const InputImageType *preview =
+ (InputImageType *) this->GetInputs()[1].GetPointer();
+
+ if (preview && preview->GetMTime() > inputPtr->GetMTime())
+ {
+ inputPtr = preview;
+ }
+
+ this->AllocateOutputs();
+
+ // Important: the size needs to be cast to long to avoid problems with
+ // pointer arithmetic on some MSVC versions!
+ long szVol[3];
+ szVol[0] = inputPtr->GetBufferedRegion().GetSize(0);
+ szVol[1] = inputPtr->GetBufferedRegion().GetSize(1);
+ szVol[2] = inputPtr->GetBufferedRegion().GetSize(2);
+
+ // Also cast the output size to long
+ long szSlice[2];
+ szSlice[0] = outputPtr->GetBufferedRegion().GetSize(0);
+ szSlice[1] = outputPtr->GetBufferedRegion().GetSize(1);
+
+ // The sign of the line and pixel traversal directions
+ int s_line = (m_LineTraverseForward) ? 1 : -1;
+ int s_pixel = (m_PixelTraverseForward) ? 1 : -1;
+
+ typename TOutputImage::IndexType oStartInd;
+ oStartInd[1] = (m_LineTraverseForward) ? 0 : szSlice[1] - 1;
+ oStartInd[0] = (m_PixelTraverseForward) ? 0 : szSlice[0] - 1;
+
+ typename OutputImageType::PixelType *outSlice = &outputPtr->GetPixel(oStartInd);
+
+ if (m_SliceDirectionImageAxis == 2) //slicing along z
+ {
+#pragma omp parallel for
+ for (int y = 0; y < szVol[1]; y++)
+ {
+ typename InputImageType::BufferType::IndexType lineIndex = { { y, (int) m_SliceIndex } };
+ const typename InputImageType::RLLine & line = inputPtr->GetBuffer()->GetPixel(lineIndex);
+ if (m_LineDirectionImageAxis == 1) //y is line coordinate
+ {
+ assert(m_PixelDirectionImageAxis == 0); //x is pixel coordinate
+ uncompressLine(line, outSlice + s_line*y*szVol[0], s_pixel * 1);
+ }
+ else if (m_LineDirectionImageAxis == 0) //x is line coordinate
+ {
+ assert(m_PixelDirectionImageAxis == 1); //y is pixel coordinate
+ uncompressLine(line, outSlice + s_pixel*y, s_line*szVol[1]);
+ }
+ else
+ throw itk::ExceptionObject(__FILE__, __LINE__, "SliceDirectionImageAxis and SliceDirectionImageAxis cannot both have a value of 2!", __FUNCTION__);
+ }
+ }
+ else if (m_SliceDirectionImageAxis == 1) //slicing along y
+ {
+#pragma omp parallel for
+ for (int z = 0; z < szVol[2]; z++)
+ {
+ typename InputImageType::BufferType::IndexType lineIndex = { { (int) m_SliceIndex, z } };
+ const typename InputImageType::RLLine & line = inputPtr->GetBuffer()->GetPixel(lineIndex);
+ if (m_LineDirectionImageAxis == 2) //z is line coordinate
+ {
+ assert(m_PixelDirectionImageAxis == 0); //x is pixel coordinate
+ uncompressLine(line, outSlice + s_line*z*szVol[0], s_pixel * 1);
+ }
+ else if (m_LineDirectionImageAxis == 0) //x is line coordinate
+ {
+ assert(m_PixelDirectionImageAxis == 2); //z is pixel coordinate
+ uncompressLine(line, outSlice + s_pixel*z, s_line*szVol[2]);
+ }
+ else
+ throw itk::ExceptionObject(__FILE__, __LINE__, "SliceDirectionImageAxis and SliceDirectionImageAxis cannot both have a value of 1!", __FUNCTION__);
+ }
+ }
+ else //slicing along x, the low-preformance case
+ {
+ assert(m_SliceDirectionImageAxis == 0);
+#pragma omp parallel for
+ for (int z = 0; z < szVol[2]; z++)
+ for (int y = 0; y < szVol[1]; y++)
+ {
+ typename InputImageType::BufferType::IndexType lineIndex = { { y, z } };
+ const typename InputImageType::RLLine & line = inputPtr->GetBuffer()->GetPixel(lineIndex);
+ int t = 0;
+ for (int x = 0; x < line.size(); x++)
+ {
+ t += line[x].first;
+ if (t > m_SliceIndex)
+ {
+ if (m_LineDirectionImageAxis == 2) //z is line coordinate
+ {
+ assert(m_PixelDirectionImageAxis == 1); //y is pixel coordinate
+ *(outSlice + s_line*z*szVol[1] + s_pixel *y) = line[x].second;;
+ }
+ else if (m_LineDirectionImageAxis == 1) //y is line coordinate
+ {
+ assert(m_PixelDirectionImageAxis == 2); //z is pixel coordinate
+ *(outSlice + s_pixel*z + s_line *y*szVol[2]) = line[x].second;
+ }
+ else
+ throw itk::ExceptionObject(__FILE__, __LINE__, "SliceDirectionImageAxis and SliceDirectionImageAxis cannot both have a value of 0!", __FUNCTION__);
+ break;
+ }
+ }
+ }
+ }
+}
+
+//template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+//void IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+//::AfterThreadedGenerateData()
+//{
+//
+//}
+
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+void IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::PrintSelf(std::ostream &os, itk::Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "Slice Image Axis: " << m_SliceDirectionImageAxis << std::endl;
+ os << indent << "Slice Index: " << m_SliceIndex << std::endl;
+ os << indent << "Line Image Axis: " << m_LineDirectionImageAxis << std::endl;
+ os << indent << "Lines Traversed Forward: " << m_LineTraverseForward << std::endl;
+ os << indent << "Pixel Image Axis: " << m_PixelDirectionImageAxis << std::endl;
+ os << indent << "Pixels Traversed Forward: " << m_PixelTraverseForward << std::endl;
+}
+
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+void IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::SetPreviewInput(PreviewImageType *input)
+{
+ this->SetNthInput(1, input);
+}
+
+template< typename TPixel, typename CounterType, class TOutputImage, class TPreviewImage>
+typename IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>::PreviewImageType *
+IRISSlicer<RLEImage<TPixel, 3, CounterType>, TOutputImage, TPreviewImage>
+::GetPreviewInput()
+{
+ return static_cast<PreviewImageType *>(itk::ProcessObject::GetInput(1));
+}
diff --git a/Logic/Slicing/ImageRegionConstIteratorWithIndexOverride.h b/Logic/Slicing/ImageRegionConstIteratorWithIndexOverride.h
new file mode 100644
index 0000000..88d8644
--- /dev/null
+++ b/Logic/Slicing/ImageRegionConstIteratorWithIndexOverride.h
@@ -0,0 +1,74 @@
+#ifndef __ImageRegionConstIteratorWithIndexOverride_h_
+#define __ImageRegionConstIteratorWithIndexOverride_h_
+
+#include "itkImageRegionConstIteratorWithIndex.h"
+#include "itkImageLinearIteratorWithIndex.h"
+
+template <class TIterator>
+class IteratorExtender : public TIterator
+{
+public:
+ typedef IteratorExtender<TIterator> Self;
+ typedef TIterator Superclass;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+
+
+ IteratorExtender(ImageType *image, const RegionType ®ion)
+ : Superclass(image, region) {}
+
+ IteratorExtender(const ImageType *image, const RegionType ®ion)
+ : Superclass(const_cast<ImageType *>(image), region) {}
+
+ const InternalPixelType *GetPosition() { return this->m_Position; }
+
+ const InternalPixelType *GetBeginPosition() { return this->m_Begin; }
+
+ template <class TPixel, unsigned int VDim>
+ TPixel *GetPixelPointer(itk::Image<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels;
+ }
+
+ template <class TPixel, unsigned int VDim>
+ const TPixel *GetPixelPointer(const itk::Image<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels;
+ }
+
+ template <class TPixel, unsigned int VDim>
+ TPixel *GetPixelPointer(itk::VectorImage<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels * image->GetNumberOfComponentsPerPixel();
+ }
+
+ template <class TPixel, unsigned int VDim>
+ const TPixel *GetPixelPointer(const itk::VectorImage<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels * image->GetNumberOfComponentsPerPixel();
+ }
+};
+
+template <class TIterator>
+class IteratorExtenderWithOffset : public IteratorExtender<TIterator>
+{
+public:
+ typedef IteratorExtender<TIterator> Superclass;
+
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename TIterator::OffsetValueType OffsetValueType;
+
+ IteratorExtenderWithOffset(ImageType *image, const RegionType ®ion)
+ : Superclass(image, region) {}
+
+ const OffsetValueType GetOffset(int direction) { return this->m_OffsetTable[direction]; }
+};
+
+
+#endif
diff --git a/Logic/Slicing/IntensityToColorLookupTableImageFilter.cxx b/Logic/Slicing/IntensityToColorLookupTableImageFilter.cxx
index 55d5bdb..0ee0566 100644
--- a/Logic/Slicing/IntensityToColorLookupTableImageFilter.cxx
+++ b/Logic/Slicing/IntensityToColorLookupTableImageFilter.cxx
@@ -63,6 +63,16 @@ AbstractLookupTableImageFilter<TInputImage, TOutputLUT, TComponent>
template<class TInputImage, class TOutputLUT, class TComponent>
void
AbstractLookupTableImageFilter<TInputImage, TOutputLUT, TComponent>
+::AllocateOutputs()
+{
+ const InputImageType *inputPtr = dynamic_cast<const InputImageType * >( this->GetInput() );
+ const LookupTableType *outputPtr = dynamic_cast<const LookupTableType * >( this->GetOutput() );
+ Superclass::AllocateOutputs();
+}
+
+template<class TInputImage, class TOutputLUT, class TComponent>
+void
+AbstractLookupTableImageFilter<TInputImage, TOutputLUT, TComponent>
::GenerateOutputInformation()
{
// Since this method could be called before the upstream pipeline has
diff --git a/Logic/Slicing/IntensityToColorLookupTableImageFilter.h b/Logic/Slicing/IntensityToColorLookupTableImageFilter.h
index 09bc864..f6ba0a6 100644
--- a/Logic/Slicing/IntensityToColorLookupTableImageFilter.h
+++ b/Logic/Slicing/IntensityToColorLookupTableImageFilter.h
@@ -77,14 +77,15 @@ public:
void SetReferenceIntensityRange(double min, double max);
void RemoveReferenceIntensityRange();
+ virtual void AllocateOutputs();
- void GenerateOutputInformation();
+ virtual void GenerateOutputInformation();
- void EnlargeOutputRequestedRegion(itk::DataObject *output);
+ virtual void EnlargeOutputRequestedRegion(itk::DataObject *output);
- void GenerateInputRequestedRegion();
+ virtual void GenerateInputRequestedRegion();
- void ThreadedGenerateData(const OutputImageRegionType ®ion,
+ virtual void ThreadedGenerateData(const OutputImageRegionType ®ion,
itk::ThreadIdType threadId);
diff --git a/Logic/Slicing/LookupTableIntensityMappingFilter.cxx b/Logic/Slicing/LookupTableIntensityMappingFilter.cxx
index a4caa90..abb4df6 100644
--- a/Logic/Slicing/LookupTableIntensityMappingFilter.cxx
+++ b/Logic/Slicing/LookupTableIntensityMappingFilter.cxx
@@ -1,5 +1,5 @@
#include "LookupTableIntensityMappingFilter.h"
-#include <itkImageRegionIterator.h>
+#include "RLEImageRegionIterator.h"
#include <itkRGBAPixel.h>
#include "LookupTableTraits.h"
@@ -53,13 +53,17 @@ LookupTableIntensityMappingFilter<TInputImage, TOutputImage>
m_LookupTable->GetBufferPointer()
- m_LookupTable->GetLargestPossibleRegion().GetIndex()[0];
+ // Range of the input image
+ InputPixelType input_min = m_InputMin->Get();
+ InputPixelType input_max = m_InputMax->Get();
+
// Compute the shift and scale factors that map the input values into
// the LUT values. These are ignored for integral pixel types, but used
// for floating point types
float lutScale;
InputPixelType lutShift;
LookupTableTraits<InputPixelType>::ComputeLinearMappingToLUT(
- m_InputMin->Get(), m_InputMax->Get(), lutScale, lutShift);
+ input_min, input_max, lutScale, lutShift);
// Define the iterators
itk::ImageRegionConstIterator<TInputImage> inputIt(input, region);
@@ -70,12 +74,25 @@ LookupTableIntensityMappingFilter<TInputImage, TOutputImage>
{
// Get the input intensity
InputPixelType xin = inputIt.Get();
+ OutputPixelType xout;
+
+ // TODO: we need to handle out of bounds voxels in non-orthogonal slicing
+ // better than this, i.e., via a special value reserved for such voxels.
+ // Right now, defaulting to zero is a DISASTER!
+ if(xin == 0 && (input_min > 0 || input_max < 0))
+ {
+ // Special case: intensity is actually outside of the min/max range
+ xout.Fill(0);
+ }
+ else
+ {
+ // Find the corresponding LUT offset
+ int lut_offset = LookupTableTraits<InputPixelType>::ComputeLUTOffset(
+ lutScale, lutShift, xin);
+
+ xout = *(lutp + lut_offset);
+ }
- // Find the corresponding LUT offset
- int lut_offset = LookupTableTraits<InputPixelType>::ComputeLUTOffset(
- lutScale, lutShift, xin);
-
- OutputPixelType xout = *(lutp + lut_offset);
outputIt.Set(xout);
++inputIt;
@@ -83,6 +100,56 @@ LookupTableIntensityMappingFilter<TInputImage, TOutputImage>
}
}
+template<class TInputImage, class TOutputImage>
+typename LookupTableIntensityMappingFilter<TInputImage, TOutputImage>::OutputPixelType
+LookupTableIntensityMappingFilter<TInputImage, TOutputImage>
+::MapPixel(const InputPixelType &xin)
+{
+ // Make sure all the inputs are up to date
+ m_InputMin->Update();
+ m_InputMax->Update();
+ m_LookupTable->Update();
+
+ // Get the pointer to the zero value in the LUT
+ OutputPixelType *lutp =
+ m_LookupTable->GetBufferPointer()
+ - m_LookupTable->GetLargestPossibleRegion().GetIndex()[0];
+
+ // Range of the input image
+ InputPixelType input_min = m_InputMin->Get();
+ InputPixelType input_max = m_InputMax->Get();
+
+ // Compute the shift and scale factors that map the input values into
+ // the LUT values. These are ignored for integral pixel types, but used
+ // for floating point types
+ float lutScale;
+ InputPixelType lutShift;
+ LookupTableTraits<InputPixelType>::ComputeLinearMappingToLUT(
+ input_min, input_max, lutScale, lutShift);
+
+ // Get the input intensity
+ OutputPixelType xout;
+
+ // TODO: we need to handle out of bounds voxels in non-orthogonal slicing
+ // better than this, i.e., via a special value reserved for such voxels.
+ // Right now, defaulting to zero is a DISASTER!
+ if(xin == 0 && (input_min > 0 || input_max < 0))
+ {
+ // Special case: intensity is actually outside of the min/max range
+ xout.Fill(0);
+ }
+ else
+ {
+ // Find the corresponding LUT offset
+ int lut_offset = LookupTableTraits<InputPixelType>::ComputeLUTOffset(
+ lutScale, lutShift, xin);
+
+ xout = *(lutp + lut_offset);
+ }
+
+ return xout;
+}
+
// Declare specific instances that will exist
template class LookupTableIntensityMappingFilter<
itk::Image<short, 2>, itk::Image< itk::RGBAPixel<unsigned char> > >;
diff --git a/Logic/Slicing/LookupTableIntensityMappingFilter.h b/Logic/Slicing/LookupTableIntensityMappingFilter.h
index 1ea3d36..acff9d6 100644
--- a/Logic/Slicing/LookupTableIntensityMappingFilter.h
+++ b/Logic/Slicing/LookupTableIntensityMappingFilter.h
@@ -49,6 +49,9 @@ public:
void ThreadedGenerateData(const OutputImageRegionType ®ion,
itk::ThreadIdType threadId);
+ /** Process a single pixel */
+ OutputPixelType MapPixel(const InputPixelType &pixel);
+
protected:
LookupTableIntensityMappingFilter();
diff --git a/Logic/Slicing/NonOrthogonalSlicer.h b/Logic/Slicing/NonOrthogonalSlicer.h
new file mode 100644
index 0000000..1c8039a
--- /dev/null
+++ b/Logic/Slicing/NonOrthogonalSlicer.h
@@ -0,0 +1,285 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: ImageWrapper.txx,v $
+ Language: C++
+ Date: $Date: 2010/10/14 16:21:04 $
+ Version: $Revision: 1.11 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef NONORTHOGONALSLICER_H
+#define NONORTHOGONALSLICER_H
+
+#include "itkImageToImageFilter.h"
+#include "itkTransform.h"
+#include "itkDataObjectDecorator.h"
+
+using itk::DataObjectDecorator;
+using itk::ProcessObject;
+
+template <typename TPixel, unsigned int VDim, typename CounterType> class RLEImage;
+template <typename TImage, typename TFloat, unsigned int VDim> class FastLinearInterpolator;
+
+namespace itk
+{
+template <typename TImage, typename TAccessor> class ImageAdaptor;
+template <typename TPixelType, unsigned int Dimension> class VectorImageToImageAdaptor;
+}
+
+/**
+ * This is a helper traits class that can be used to modify the access of pixel
+ * data in the input image by the NonOrthogonalSlicer.
+ */
+template <typename TInputImage, typename TOutputImage>
+class NonOrthogonalSlicerPixelAccessTraitsWorker
+{
+public:
+ typedef typename TOutputImage::InternalPixelType OutputComponentType;
+
+ NonOrthogonalSlicerPixelAccessTraitsWorker(TInputImage *image);
+ ~NonOrthogonalSlicerPixelAccessTraitsWorker();
+
+ inline void ProcessVoxel(double *cix, bool use_nn, OutputComponentType **out_ptr);
+
+ inline void SkipVoxels(int n, OutputComponentType **out_ptr);
+
+protected:
+
+ // The interpolator object used internally
+ typedef FastLinearInterpolator<TInputImage, double, TInputImage::ImageDimension> Interpolator;
+ Interpolator m_Interpolator;
+
+ // Number of components
+ int m_NumComponents;
+
+ // Temporary buffer
+ double *m_Buffer;
+};
+
+
+/**
+ * This is an alternative slicer that functions more or less like an ITK
+ * ResampleImageFilter, but more efficiently and directly maps the data to
+ * a 2D slice.
+ *
+ * The filter takes a transform and a reference image from which the slice is
+ * generated.
+ */
+template <typename TInputImage, typename TOutputImage>
+class NonOrthogonalSlicer
+ : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef NonOrthogonalSlicer Self;
+ typedef itk::ImageToImageFilter<TInputImage, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ typedef TInputImage InputImageType;
+ typedef typename InputImageType::ConstPointer InputImagePointer;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+
+ typedef TOutputImage OutputImageType;
+ typedef typename OutputImageType::Pointer OutputImagePointer;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(NonOrthogonalSlicer, ImageToImageFilter)
+
+ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension);
+ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension);
+
+ typedef itk::ImageBase<InputImageDimension> ReferenceImageBaseType;
+
+ /** Transform */
+ typedef itk::Transform<double, InputImageDimension, InputImageDimension> TransformType;
+
+ /** Some more typedefs. */
+ typedef typename InputImageType::RegionType InputImageRegionType;
+ typedef typename OutputImageType::RegionType OutputImageRegionType;
+
+ /** The worker class */
+ typedef NonOrthogonalSlicerPixelAccessTraitsWorker<TInputImage, TOutputImage> WorkerType;
+
+ /** Reference image input */
+ itkSetInputMacro(ReferenceImage, ReferenceImageBaseType)
+ itkGetInputMacro(ReferenceImage, ReferenceImageBaseType)
+
+ /** Transform input */
+ itkSetDecoratedObjectInputMacro(Transform, TransformType)
+ itkGetDecoratedObjectInputMacro(Transform, TransformType)
+
+ /** Interpolation type */
+ itkSetMacro(UseNearestNeighbor, bool)
+ itkGetMacro(UseNearestNeighbor, bool)
+
+protected:
+
+ NonOrthogonalSlicer();
+ ~NonOrthogonalSlicer();
+
+ /** The traits class */
+
+ virtual void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId);
+
+ virtual void VerifyInputInformation() { }
+
+ virtual void GenerateOutputInformation();
+
+ virtual void GenerateInputRequestedRegion();
+
+private:
+
+ bool m_UseNearestNeighbor;
+};
+
+
+
+
+/**
+ * An specialization of the traits class for RLE images
+ */
+template <typename TPixel, typename CounterType, typename TOutputImage>
+class NonOrthogonalSlicerPixelAccessTraitsWorker<
+ RLEImage<TPixel, 3, CounterType>, TOutputImage>
+{
+public:
+
+ typedef RLEImage<TPixel, 3, CounterType> InputImageType;
+ typedef typename TOutputImage::InternalPixelType OutputComponentType;
+
+ NonOrthogonalSlicerPixelAccessTraitsWorker(InputImageType *adaptor) {}
+ ~NonOrthogonalSlicerPixelAccessTraitsWorker() {}
+
+ inline void ProcessVoxel(double *cix, bool use_nn, OutputComponentType **out_ptr)
+ {
+ assert(0);
+ }
+
+ inline void SkipVoxels(int n, OutputComponentType **out_ptr) {}
+};
+
+
+/**
+ * An specialization of the traits class for Image adapters
+ */
+template <typename TPixelType, unsigned int Dimension, typename TAccessor, typename TOutputImage>
+class NonOrthogonalSlicerPixelAccessTraitsWorker<
+ itk::ImageAdaptor<itk::VectorImage<TPixelType, Dimension>, TAccessor>, TOutputImage>
+{
+public:
+
+ typedef itk::VectorImage<TPixelType, Dimension> VectorImageType;
+ typedef itk::ImageAdaptor<VectorImageType, TAccessor> AdaptorType;
+ typedef typename TOutputImage::InternalPixelType OutputComponentType;
+
+ NonOrthogonalSlicerPixelAccessTraitsWorker(AdaptorType *adaptor);
+ ~NonOrthogonalSlicerPixelAccessTraitsWorker();
+
+ inline void ProcessVoxel(double *cix, bool use_nn, OutputComponentType **out_ptr);
+ inline void SkipVoxels(int n, OutputComponentType **out_ptr);
+
+protected:
+
+ // The interpolator object used internally
+ typedef FastLinearInterpolator<VectorImageType, double, AdaptorType::ImageDimension> Interpolator;
+ Interpolator m_Interpolator;
+
+ // Number of components
+ int m_NumComponents;
+
+ // Temporary buffer for interpolation
+ double *m_Buffer;
+
+ // Temporary buffer for computing the derived quantity
+ typename VectorImageType::PixelType m_VectorPixel;
+
+ // A pointer to the adaptor
+ typename AdaptorType::Pointer m_Adaptor;
+};
+
+
+
+
+
+/**
+ * This is a helper traits class that can be used to modify the access of pixel
+ * data in the input image by the NonOrthogonalSlicer.
+ */
+template <typename TPixelType, unsigned int Dimension, typename TOutputImage>
+class NonOrthogonalSlicerPixelAccessTraitsWorker<
+ itk::VectorImageToImageAdaptor<TPixelType, Dimension>, TOutputImage>
+{
+public:
+ typedef itk::VectorImageToImageAdaptor<TPixelType, Dimension> AdaptorType;
+ typedef typename TOutputImage::InternalPixelType OutputComponentType;
+
+ NonOrthogonalSlicerPixelAccessTraitsWorker(AdaptorType *adaptor);
+ ~NonOrthogonalSlicerPixelAccessTraitsWorker();
+
+ inline void ProcessVoxel(double *cix, bool use_nn, OutputComponentType **out_ptr);
+ inline void SkipVoxels(int n, OutputComponentType **out_ptr);
+
+protected:
+
+ // The actual image type
+ typedef typename AdaptorType::InternalImageType InternalImageType;
+
+ // The interpolator object used internally
+ typedef FastLinearInterpolator<InternalImageType, double, AdaptorType::ImageDimension> Interpolator;
+ Interpolator m_Interpolator;
+
+ // Number of components
+ int m_NumComponents, m_ExtractComponent;
+
+ // Temporary buffer
+ double m_BufferValue;
+};
+
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "NonOrthogonalSlicer.txx"
+#endif
+
+#endif // NONORTHOGONALSLICER_H
diff --git a/Logic/Slicing/NonOrthogonalSlicer.txx b/Logic/Slicing/NonOrthogonalSlicer.txx
new file mode 100644
index 0000000..5077b26
--- /dev/null
+++ b/Logic/Slicing/NonOrthogonalSlicer.txx
@@ -0,0 +1,431 @@
+/*=========================================================================
+
+ Program: ITK-SNAP
+ Module: $RCSfile: ImageWrapper.txx,v $
+ Language: C++
+ Date: $Date: 2010/10/14 16:21:04 $
+ Version: $Revision: 1.11 $
+ Copyright (c) 2007 Paul A. Yushkevich
+
+ This file is part of ITK-SNAP
+
+ ITK-SNAP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+ -----
+
+ Copyright (c) 2003 Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef NONORTHOGONALSLICER_TXX
+#define NONORTHOGONALSLICER_TXX
+
+#include "NonOrthogonalSlicer.h"
+#include "FastLinearInterpolator.h"
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+
+template <class TInputImage, class TOutputImage>
+NonOrthogonalSlicer<TInputImage, TOutputImage>
+::NonOrthogonalSlicer()
+ : m_UseNearestNeighbor(false)
+{
+}
+
+template <class TInputImage, class TOutputImage>
+NonOrthogonalSlicer<TInputImage, TOutputImage>
+::~NonOrthogonalSlicer()
+{
+}
+
+template <class TInputImage, class TOutputImage>
+void
+NonOrthogonalSlicer<TInputImage, TOutputImage>
+::GenerateOutputInformation()
+{
+ // The output information can be left to defaults because it's in screen
+ // space anyway and just does not matter for any geometry
+ OutputImagePointer output = this->GetOutput();
+ typename OutputImageType::SpacingType out_spacing;
+ typename OutputImageType::DirectionType out_direction;
+ typename OutputImageType::PointType out_origin;
+
+ out_origin.Fill(0.0);
+ out_spacing.Fill(1.0);
+ out_direction.SetIdentity();
+
+ // Set up the output region
+ OutputImageRegionType out_region;
+ for(int d = 0; d < ImageDimension; d++)
+ {
+ out_region.SetIndex(d, this->GetReferenceImage()->GetLargestPossibleRegion().GetIndex(d));
+ out_region.SetSize(d, this->GetReferenceImage()->GetLargestPossibleRegion().GetSize(d));
+ }
+
+ output->SetSpacing(out_spacing);
+ output->SetOrigin(out_origin);
+ output->SetDirection(out_direction);
+ output->SetLargestPossibleRegion(out_region);
+ output->SetNumberOfComponentsPerPixel(this->GetInput()->GetNumberOfComponentsPerPixel());
+}
+
+template <class TInputImage, class TOutputImage>
+void
+NonOrthogonalSlicer<TInputImage, TOutputImage>
+::GenerateInputRequestedRegion()
+{
+ // get pointers to the input and output
+ InputImageType *inputPtr =
+ const_cast< InputImageType * >( this->GetInput() );
+
+ // Request the entire input image
+ if(inputPtr)
+ inputPtr->SetRequestedRegionToLargestPossibleRegion();
+}
+
+
+template <class TInputImage, class TOutputImage>
+void
+NonOrthogonalSlicer<TInputImage, TOutputImage>
+::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ // The input 3D image volume
+ InputImageType *input = const_cast<InputImageType *>(this->GetInput());
+
+ // The reference image
+ const ReferenceImageBaseType *reference = this->GetReferenceImage();
+
+ // The transform
+ const TransformType *transform = this->GetTransform();
+
+ // Length of the line along which we are going to be sampling
+ int line_len = outputRegionForThread.GetSize(0);
+
+ // Create an iterator over the output image
+ typedef itk::ImageLinearIteratorWithIndex<OutputImageType> IterBase;
+ typedef IteratorExtender<IterBase> IterType;
+
+ // Determine the appropriate float/double type for the interpolator.
+ typedef typename itk::NumericTraits<OutputComponentType>::MeasurementVectorType::ValueType FloatType;
+
+ // Get the extents of the image cube that can be sampled
+ itk::ContinuousIndex<double, InputImageDimension> cixCubeStart, cixCubeEnd;
+ for(int d = 0; d < InputImageDimension; d++)
+ {
+ cixCubeStart[d] = input->GetBufferedRegion().GetIndex()[d] - 0.5;
+ cixCubeEnd[d] =
+ input->GetBufferedRegion().GetIndex()[d] +
+ input->GetBufferedRegion().GetSize()[d] - 0.5;
+ }
+
+ // Create a fast interpolator for the input image - via the traits, allowing for
+ // partial specialization for imageadapters and other such things
+ WorkerType worker(input);
+
+ // Whether to use nn
+ bool use_nn = this->GetUseNearestNeighbor();
+
+ // Loop over the lines in the input image
+ for(IterType it(this->GetOutput(), outputRegionForThread); !it.IsAtEnd(); it.NextLine())
+ {
+ // Get a pointer to the output pixels for this line
+ OutputComponentType *outPixelPtr = it.GetPixelPointer(this->GetOutput());
+
+ // Get the index of the first pixel - this is in 2D
+ typename OutputImageType::IndexType outIndex = it.GetIndex();
+
+ // Get the 3D index of the first pixel of the line
+ typename ReferenceImageBaseType::IndexType idxStart;
+ idxStart.Fill(0.0);
+ for(int d = 0; d < ImageDimension; d++)
+ idxStart[d] = outIndex[d];
+
+ // Get the 3D index of the second pixel of the line
+ typename ReferenceImageBaseType::IndexType idxNext = idxStart;
+ idxNext[0] += 1;
+
+ // Convert to a physical point relative to refernece image
+ typename ReferenceImageBaseType::PointType pRefStart, pRefNext;
+ reference->TransformIndexToPhysicalPoint(idxStart, pRefStart);
+ reference->TransformIndexToPhysicalPoint(idxNext, pRefNext);
+
+ // Apply the affine transform to this point - so it's relative to the input image
+ typename ReferenceImageBaseType::PointType pInpStart, pInpNext;
+ pInpStart = transform->TransformPoint(pRefStart);
+ pInpNext = transform->TransformPoint(pRefNext);
+
+ // Compute the sample point in input image space and the step
+ itk::ContinuousIndex<double, InputImageDimension> cixSample, cixNext, cixStep;
+ input->TransformPhysicalPointToContinuousIndex(pInpStart, cixSample);
+ input->TransformPhysicalPointToContinuousIndex(pInpNext, cixNext);
+
+ for(int d = 0; d < InputImageDimension; d++)
+ cixStep[d] = cixNext[d] - cixSample[d];
+
+ // Determine the starting and ending indices for the line
+ int kStart = 0, kEnd = line_len - 1;
+ bool skipLine = false;
+ for(int d = 0; d < InputImageDimension; d++)
+ {
+ double x0 = cixCubeStart[d], x1 = cixCubeEnd[d], dx = cixStep[d], x = cixSample[d];
+
+ // TODO: this may behave badly for small voxel sizes
+ if(fabs(dx) < 1.0e-5)
+ {
+ if(x < x0 || x > x1)
+ {
+ skipLine = true;
+ break;
+ }
+ }
+ else
+ {
+ double z0 = (x0 - x) / dx, z1 = (x1 - x) / dx;
+ if(z1 > z0)
+ {
+ kStart = std::max(kStart, (int) floor(z0));
+ kEnd = std::min(kEnd, (int) ceil(z1));
+ }
+ else
+ {
+ kStart = std::max(kStart, (int) floor(z1));
+ kEnd = std::min(kEnd, (int) ceil(z0));
+ }
+ }
+ }
+
+ if(kStart >= line_len || kEnd <= 0)
+ skipLine = true;
+
+ // Deal with skipped lines
+ if(skipLine)
+ {
+ worker.SkipVoxels(line_len, &outPixelPtr);
+ }
+ else
+ {
+ // Skip the starting voxels
+ if(kStart > 0)
+ {
+ // Skip the voxels
+ worker.SkipVoxels(kStart, &outPixelPtr);
+
+ // Update the sample location
+ for(int d = 0; d < InputImageDimension; d++)
+ cixSample[d] += kStart * cixStep[d];
+ }
+
+ // Process the voxels that cross the image cube
+ for(int i = kStart; i <= kEnd; i++)
+ {
+ worker.ProcessVoxel(cixSample.GetDataPointer(), use_nn, &outPixelPtr);
+
+ // Update the sample location
+ for(int d = 0; d < InputImageDimension; d++)
+ cixSample[d] += cixStep[d];
+ }
+
+ // Process the rest
+ if(kEnd < line_len - 1)
+ {
+ worker.SkipVoxels((line_len - 1) - kEnd, &outPixelPtr);
+ }
+ }
+ }
+}
+
+template <class TInputImage, class TOutputImage>
+NonOrthogonalSlicerPixelAccessTraitsWorker<TInputImage, TOutputImage>
+::NonOrthogonalSlicerPixelAccessTraitsWorker(TInputImage *image)
+ : m_Interpolator(image)
+{
+ m_NumComponents = m_Interpolator.GetPointerIncrement();
+ m_Buffer = new double[m_NumComponents];
+}
+
+template <class TInputImage, class TOutputImage>
+NonOrthogonalSlicerPixelAccessTraitsWorker<TInputImage, TOutputImage>
+::~NonOrthogonalSlicerPixelAccessTraitsWorker()
+{
+ delete m_Buffer;
+}
+
+template <class TInputImage, class TOutputImage>
+void
+NonOrthogonalSlicerPixelAccessTraitsWorker<TInputImage, TOutputImage>
+::ProcessVoxel(double *cix, bool use_nn, OutputComponentType **out_ptr)
+{
+ // Perform the interpolation
+ typename Interpolator::InOut status =
+ use_nn
+ ? m_Interpolator.InterpolateNearestNeighbor(cix, m_Buffer)
+ : m_Interpolator.Interpolate(cix, m_Buffer);
+
+ if(status == Interpolator::INSIDE)
+ {
+ for(int k = 0; k < m_NumComponents; k++)
+ *(*out_ptr)++ = static_cast<OutputComponentType>(m_Buffer[k]);
+ }
+ else
+ {
+ // TODO: this is problematic!!!!
+ for(int k = 0; k < m_NumComponents; k++)
+ *(*out_ptr)++ = 0; //itk::NumericTraits<OutputComponentType>::Zero;
+ }
+}
+
+template <class TInputImage, class TOutputImage>
+void
+NonOrthogonalSlicerPixelAccessTraitsWorker<TInputImage, TOutputImage>
+::SkipVoxels(int n, OutputComponentType **out_ptr)
+{
+ int n_total = n * m_NumComponents;
+ for(int k = 0; k < n_total; k++)
+ *(*out_ptr)++ = 0; //itk::NumericTraits<OutputComponentType>::Zero;
+}
+
+
+
+/*
+ * Traits for the component extracting image adaptor. Note that in the call to the
+ * constructor for the interpolator, we are passing the buffer pointer offset by
+ * the component index, and only requesting a single component to be sampled
+ */
+template <typename TPixelType, unsigned int Dimension, typename TOutputImage>
+NonOrthogonalSlicerPixelAccessTraitsWorker<itk::VectorImageToImageAdaptor<TPixelType, Dimension>, TOutputImage>
+::NonOrthogonalSlicerPixelAccessTraitsWorker(AdaptorType *adaptor)
+ : m_Interpolator(adaptor,
+ adaptor->GetBufferPointer() + adaptor->GetPixelAccessor().GetExtractComponentIdx(),
+ adaptor->GetPixelAccessor().GetVectorLength(), 1)
+{
+ m_NumComponents = m_Interpolator.GetPointerIncrement();
+ m_ExtractComponent = adaptor->GetPixelAccessor().GetExtractComponentIdx();
+}
+
+template <typename TPixelType, unsigned int Dimension, typename TOutputImage>
+NonOrthogonalSlicerPixelAccessTraitsWorker<itk::VectorImageToImageAdaptor<TPixelType, Dimension>, TOutputImage>
+::~NonOrthogonalSlicerPixelAccessTraitsWorker()
+{
+}
+
+template <typename TPixelType, unsigned int Dimension, typename TOutputImage>
+void
+NonOrthogonalSlicerPixelAccessTraitsWorker<itk::VectorImageToImageAdaptor<TPixelType, Dimension>, TOutputImage>
+::ProcessVoxel(double *cix, bool use_nn, OutputComponentType **out_ptr)
+{
+ // Perform the interpolation
+ typename Interpolator::InOut status =
+ use_nn
+ ? m_Interpolator.InterpolateNearestNeighbor(cix, &m_BufferValue)
+ : m_Interpolator.Interpolate(cix, &m_BufferValue);
+
+ if(status == Interpolator::INSIDE)
+ {
+ *(*out_ptr)++ = static_cast<OutputComponentType>(m_BufferValue);
+ }
+ else
+ {
+ *(*out_ptr)++ = 0;
+ }
+}
+
+template <typename TPixelType, unsigned int Dimension, typename TOutputImage>
+void
+NonOrthogonalSlicerPixelAccessTraitsWorker<itk::VectorImageToImageAdaptor<TPixelType, Dimension>, TOutputImage>
+::SkipVoxels(int n, OutputComponentType **out_ptr)
+{
+ for(int i = 0; i < n; i++)
+ *(*out_ptr)++ = 0;
+}
+
+/*
+ * Traits for a generic image adaptor with a vector image base
+ */
+template <typename TPixelType, unsigned int Dimension, typename TAccessor, typename TOutputImage>
+NonOrthogonalSlicerPixelAccessTraitsWorker<
+ itk::ImageAdaptor<itk::VectorImage<TPixelType, Dimension>, TAccessor>, TOutputImage>
+::NonOrthogonalSlicerPixelAccessTraitsWorker(AdaptorType *adaptor)
+ : m_Interpolator(adaptor, adaptor->GetBufferPointer(),
+ adaptor->GetPixelAccessor().GetVectorLength()),
+ m_VectorPixel(adaptor->GetPixelAccessor().GetVectorLength()),
+ m_Adaptor(adaptor)
+{
+ m_NumComponents = m_Interpolator.GetPointerIncrement();
+ m_Buffer = new double[m_NumComponents];
+}
+
+template <typename TPixelType, unsigned int Dimension, typename TAccessor, typename TOutputImage>
+NonOrthogonalSlicerPixelAccessTraitsWorker<
+ itk::ImageAdaptor<itk::VectorImage<TPixelType, Dimension>, TAccessor>, TOutputImage>
+::~NonOrthogonalSlicerPixelAccessTraitsWorker()
+{
+ delete m_Buffer;
+}
+
+template <typename TPixelType, unsigned int Dimension, typename TAccessor, typename TOutputImage>
+void
+NonOrthogonalSlicerPixelAccessTraitsWorker<
+ itk::ImageAdaptor<itk::VectorImage<TPixelType, Dimension>, TAccessor>, TOutputImage>
+::ProcessVoxel(double *cix, bool use_nn, OutputComponentType **out_ptr)
+{
+ // Perform the interpolation
+ typename Interpolator::InOut status =
+ use_nn
+ ? m_Interpolator.InterpolateNearestNeighbor(cix, m_Buffer)
+ : m_Interpolator.Interpolate(cix, m_Buffer);
+
+ const typename VectorImageType::PixelType &vpref = m_VectorPixel;
+
+ if(status == Interpolator::INSIDE)
+ {
+ // Cast to the vector pixel type
+ for(int i = 0; i < m_NumComponents; i++)
+ m_VectorPixel[i] = static_cast<OutputComponentType>(m_Buffer[i]);
+
+ // Apply the accessor
+ *(*out_ptr)++ =
+ m_Adaptor->GetPixelAccessor().Get(m_VectorPixel.GetDataPointer());
+ }
+ else
+ {
+ *(*out_ptr)++ = 0;
+ }
+}
+
+
+template <typename TPixelType, unsigned int Dimension, typename TAccessor, typename TOutputImage>
+void
+NonOrthogonalSlicerPixelAccessTraitsWorker<
+ itk::ImageAdaptor<itk::VectorImage<TPixelType, Dimension>, TAccessor>, TOutputImage>
+::SkipVoxels(int n, OutputComponentType **out_ptr)
+{
+ for(int i = 0; i < n; i++)
+ *(*out_ptr)++ = 0;
+}
+
+
+
+#endif // NONORTHOGONALSLICER_TXX
diff --git a/Logic/Slicing/RGBALookupTableIntensityMappingFilter.cxx b/Logic/Slicing/RGBALookupTableIntensityMappingFilter.cxx
index bbd62e8..59613b7 100644
--- a/Logic/Slicing/RGBALookupTableIntensityMappingFilter.cxx
+++ b/Logic/Slicing/RGBALookupTableIntensityMappingFilter.cxx
@@ -1,5 +1,5 @@
#include "RGBALookupTableIntensityMappingFilter.h"
-#include "itkImageRegionIterator.h"
+#include "RLEImageRegionIterator.h"
template<class TInputImage>
RGBALookupTableIntensityMappingFilter<TInputImage>
@@ -32,35 +32,86 @@ RGBALookupTableIntensityMappingFilter<TInputImage>
// Get the output
OutputImageType *output = this->GetOutput(0);
+ // Lookup table range
+ int lut_min = m_LookupTable->GetLargestPossibleRegion().GetIndex()[0];
+ int lut_max = lut_min + m_LookupTable->GetLargestPossibleRegion().GetSize()[0] - 1;
+
// Get the pointer to the zero value in the LUT
- OutputComponentType *lutp =
- m_LookupTable->GetBufferPointer()
- - m_LookupTable->GetLargestPossibleRegion().GetIndex()[0];
+ OutputComponentType *lutp = m_LookupTable->GetBufferPointer() - lut_min;
// Define the iterators
typedef itk::ImageRegionConstIterator<InputImageType> InputIteratorType;
- std::vector<InputIteratorType> inputIt;
- for(int d = 0; d < 3; d++)
- inputIt.push_back(InputIteratorType(inputs[d], region));
+ InputIteratorType it0(inputs[0], region);
+ InputIteratorType it1(inputs[1], region);
+ InputIteratorType it2(inputs[2], region);
+
itk::ImageRegionIterator<OutputImageType> outputIt(output, region);
// Perform the intensity mapping using the LUT (no bounds checking!)
while( !outputIt.IsAtEnd() )
{
OutputPixelType xout;
- for(int d = 0; d < 3; d++)
+
+ InputPixelType xin0 = it0.Get();
+ InputPixelType xin1 = it1.Get();
+ InputPixelType xin2 = it2.Get();
+
+ // TODO: we need to handle out of bounds voxels in non-orthogonal slicing
+ // better than this, i.e., via a special value reserved for such voxels.
+ // Right now, defaulting to zero is a DISASTER!
+ if(xin0 == 0 && xin1 == 0 && xin2 == 0 && (lut_min > 0 || lut_max < 0))
+ {
+ xout.Fill(0);
+ }
+ else
{
- InputPixelType xin = inputIt[d].Get();
- xout[d] = *(lutp + xin);
- ++inputIt[d];
+ xout[0] = *(lutp + xin0);
+ xout[1] = *(lutp + xin1);
+ xout[2] = *(lutp + xin2);
+ xout[3] = 255; // alpha = 1
}
- xout[3] = 255; // alpha = 1
outputIt.Set(xout);
+ ++it0; ++it1; ++it2;
++outputIt;
}
}
+template<class TInputImage>
+typename RGBALookupTableIntensityMappingFilter<TInputImage>::OutputPixelType
+RGBALookupTableIntensityMappingFilter<TInputImage>
+::MapPixel(const InputPixelType &xin0, const InputPixelType &xin1, const InputPixelType &xin2)
+{
+ // Update the lookup table
+ m_LookupTable->Update();
+
+ // Lookup table range
+ int lut_min = m_LookupTable->GetLargestPossibleRegion().GetIndex()[0];
+ int lut_max = lut_min + m_LookupTable->GetLargestPossibleRegion().GetSize()[0] - 1;
+
+ // Get the pointer to the zero value in the LUT
+ OutputComponentType *lutp = m_LookupTable->GetBufferPointer() - lut_min;
+
+ OutputPixelType xout;
+
+ // TODO: we need to handle out of bounds voxels in non-orthogonal slicing
+ // better than this, i.e., via a special value reserved for such voxels.
+ // Right now, defaulting to zero is a DISASTER!
+ if(xin0 == 0 && xin1 == 0 && xin2 == 0 && (lut_min > 0 || lut_max < 0))
+ {
+ xout.Fill(0);
+ }
+ else
+ {
+ xout[0] = *(lutp + xin0);
+ xout[1] = *(lutp + xin1);
+ xout[2] = *(lutp + xin2);
+ xout[3] = 255; // alpha = 1
+ }
+
+ return xout;
+}
+
// Declare specific instances that will exist
template class RGBALookupTableIntensityMappingFilter< itk::Image<short, 2> >;
diff --git a/Logic/Slicing/RGBALookupTableIntensityMappingFilter.h b/Logic/Slicing/RGBALookupTableIntensityMappingFilter.h
index 6d98b09..186313b 100644
--- a/Logic/Slicing/RGBALookupTableIntensityMappingFilter.h
+++ b/Logic/Slicing/RGBALookupTableIntensityMappingFilter.h
@@ -43,6 +43,10 @@ public:
void ThreadedGenerateData(const OutputImageRegionType ®ion,
itk::ThreadIdType threadId);
+ /** Process a single pixel */
+ OutputPixelType MapPixel(const InputPixelType &xin0, const InputPixelType &xin1, const InputPixelType &xin2);
+
+
protected:
RGBALookupTableIntensityMappingFilter();
diff --git a/README.git b/README.git
new file mode 100644
index 0000000..fcf6153
--- /dev/null
+++ b/README.git
@@ -0,0 +1,9 @@
+*** GIT Users: Read This ***
+
+After cloning ITK-SNAP from Git, you need to execute the following commands
+to initialize the submodules. ITK-SNAP will not compile without submodules
+
+ git submodule init
+ git submodule update
+
+Alternatively, pass the '--recursive' option to git clone
diff --git a/README.html b/README.html
index 1ee9af6..a60552f 100644
--- a/README.html
+++ b/README.html
@@ -1,80 +1,30 @@
-<HTML>
-<HEAD>
-<TITLE> ITK-SNAP Readme </TITLE>
-</HEAD>
-<BODY>
-
-<H1> ITK-SNAP README (Versions 2.1.4 and Later) </H1>
-
-<h3>Welcome!</h3> <p>
-
-Thank you for your interest in ITK-SNAP! By using this program you are joining
-an ever growing community of SNAP users from research labs around the world.
-ITK-SNAP is an open-source software application made possible by the
-committment of dedicated developers, who often make contributions on a
-volunteer basis. Original development of ITK-SNAP was supported by the U.S.
-National Library of Medicine through PO 467-MZ-202446-1. Subsequent
-development of ITK-SNAP version 2.0 has been supported by the U.S. National
-Institute of Biomedical Imaging and BioEngineering and the NIH Blueprint for
-Neuroscience through grant 1 R03 EB008200-01.
-</p>
-
-<p> ITK-SNAP is the result of over 10 years of work by many researchers,
-software engineers and students. Please visit the <a
- href="http:/itksnap.org/credits.php">Credits page</a> to learn about the
-people that made ITK-SNAP possible!</p>
-
-<h3> Get Involved! </h3>
-
-<p> Your participation as a user, developer or sponsor is essential to
-ensuring a bright future for ITK-SNAP future. There are many ways in which you
-can contribute to the stability and quality of this program: </p>
-
+<h2 id="itk-snap-readme">ITK-SNAP README</h2>
+<h3 id="welcome">Welcome!</h3>
+<p>Thank you for your interest in ITK-SNAP! By using this program you are joining an ever growing community of users from research labs around the world. ITK-SNAP is an open-source software application made possible by the committment of dedicated developers, who often make contributions on a volunteer basis. Original development of ITK-SNAP was supported by the U.S. National Library of Medicine through PO 467-MZ-202446-1. Subsequent development of ITK-SNAP version 2.0 has been supported [...]
+<p>ITK-SNAP is the result of over 15 years of work by many researchers, software engineers and students. Please visit the <a href="http:/itksnap.org/credits.php">Credits page</a> to learn about the team that made ITK-SNAP possible!</p>
+<h3 id="please-remember-to-cite-itk-snap">Please remember to cite ITK-SNAP</h3>
+<p>Citations in published work is the best way for us to gauge ITK-SNAP impact. As of the Fall of 2016, ITK-SNAP has been cited by <a href="https://goo.gl/YoM9EK">over 2000 papers</a>. If you use ITK-SNAP for a publication, please cite the paper below.</p>
<ul>
- <li> By participating in the <a
- href="http://www.itksnap.org/forum.php">ITK-SNAP User and Developer Mailing Lists</a>.</li>
- <li> By reporting bugs and suggesting enhancements using the <a
- href="http://www.itksnap.org/pmwiki/pmwiki.php?n=BugTracker">Bug Tracker</a>.</li>
- <li> By joining the ITK-SNAP Development Team on <a
- href="http://sourceforge.net/projects/itk-snap">SourceForge.net</a> and contributing source code.
- </li>
+<li>Paul A. Yushkevich, Joseph Piven, Heather Cody Hazlett, Rachel Gimpel Smith, Sean Ho, James C. Gee, and Guido Gerig. User-guided 3D active contour segmentation of anatomical structures: Significantly improved efficiency and reliability. Neuroimage. 2006 Jul 1; 31(3):1116-28.</li>
</ul>
-
-<h3> Getting Started </h3>
-
+<h3 id="getting-started">Getting Started</h3>
+<p>ITK-SNAP was designed for ease of use. There are several resources to get started with it.</p>
<ul>
- <li> If you downloaded ITK-SNAP as a binary executable from <a
- href="http://www.itksnap.org/download/snap">itksnap.org</a>, you can get
- started by reading the <a
- href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.HomePage">online tutorial</a>.</li>
-
- <li> If you downloaded ITK-SNAP source code, please follow these <a
- href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.BuildingITK-SNAP">online
- instructions</a> to compile and build ITK-SNAP.</li>
-
- <li> You will need a 3D image to use this software. Images with which
- ITK-SNAP is known to work can be downloaded from <a
- href="http://www.itksnap.org/download/snap">http://www.itksnap.org/download/snap
-</a></li>
+<li>If you downloaded ITK-SNAP as a binary executable from <a href="http://www.itksnap.org/download/snap">itksnap.org</a>, you can get started by reading the <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.HomePage">online tutorials</a>.</li>
+<li>If you downloaded ITK-SNAP source code, please follow these <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.BuildingITK-SNAP">online instructions</a> to compile and build ITK-SNAP.</li>
+<li>You will need a 3D image to use this software. Images with which ITK-SNAP is known to work are found on the <a href="http://www.itksnap.org/download/snap">ITK-SNAP download page</a></li>
+<li>There are also materials (images, exercises, video) from training courses available on the <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.SNAP3">documentation page</a>.</li>
</ul>
-
-<h3> Asking Questions </h3>
-
-<p>
-If you have a question about ITK-SNAP, here are the places where you may find an
-answer. Keep in mind that since ITK-SNAP in not a commercial product, it may take a
-little while for an answer to arrive!
-</p>
-
+<h3 id="asking-questions">Asking Questions</h3>
+<p>If you have a question about ITK-SNAP, here are the places where you may find an answer. Keep in mind that since ITK-SNAP in not a commercial product, it may take some time for an answer to arrive.</p>
<ul>
- <li> If you have an question about how to use SNAP or why it's not doing what
- it should, post in on the <a
- href="http://www.itksnap.org/forum.php">ITK-SNAP Users' List</a>.</li>
-
- <li> If you have a technical question or a questing related to ITK, join the
- <a href="http://www.itk.org/HTML/MailingLists.htm">ITK Users' Mailing List</a> and
- post your question there. </li>
+<li>If you have an question about how to use SNAP or why it's not doing what it should, post in on the <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=MailingLists">ITK-SNAP Users' List</a>.</li>
+<li>If you have a technical question or a questing related to ITK, join the <a href="http://www.itk.org/HTML/MailingLists.htm">ITK Users' Mailing List</a> and post your question there.</li>
+</ul>
+<h3 id="get-involved">Get Involved!</h3>
+<p>Your participation as a user, developer or sponsor is essential to ensuring a bright future for ITK-SNAP. There are many ways in which you can contribute to the stability and quality of this program:</p>
+<ul>
+<li>By participating in the <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=MailingLists">ITK-SNAP User and Developer Mailing Lists</a>.</li>
+<li>By reporting bugs and suggesting enhancements using the <a href="http://www.itksnap.org/pmwiki/pmwiki.php?n=Main.BugTracker">Bug Tracker</a>.</li>
+<li>By joining the ITK-SNAP Development Team on <a href="http://sourceforge.net/projects/itk-snap">SourceForge.net</a> and contributing source code.</li>
</ul>
-
-</BODY>
-</HTML>
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e04f12d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,67 @@
+ITK-SNAP README
+-----------------
+
+### Welcome!
+
+Thank you for your interest in ITK-SNAP! By using this program you are joining
+an ever growing community of users from research labs around the world.
+ITK-SNAP is an open-source software application made possible by the
+committment of dedicated developers, who often make contributions on a
+volunteer basis. Original development of ITK-SNAP was supported by the U.S.
+National Library of Medicine through PO 467-MZ-202446-1. Subsequent
+development of ITK-SNAP version 2.0 has been supported by the U.S. National
+Institute of Biomedical Imaging and BioEngineering and the NIH Blueprint for
+Neuroscience through grant 1 R03 EB008200-01. Development of ITK-SNAP versions
+3.0 - 3.6 has been supported by the NIH grant 1R01 EB014346.
+
+ITK-SNAP is the result of over 15 years of work by many researchers, software
+engineers and students. Please visit the [Credits page](http:/itksnap.org/credits.php)
+to learn about the team that made ITK-SNAP possible!
+
+### Please remember to cite ITK-SNAP
+
+Citations in published work is the best way for us to gauge ITK-SNAP impact.
+As of the Fall of 2016, ITK-SNAP has been cited by [over 2000 papers](https://goo.gl/YoM9EK).
+If you use ITK-SNAP for a publication, please cite the paper below.
+
+* Paul A. Yushkevich, Joseph Piven, Heather Cody Hazlett, Rachel Gimpel Smith,
+ Sean Ho, James C. Gee, and Guido Gerig. User-guided 3D active contour
+ segmentation of anatomical structures: Significantly improved efficiency and
+ reliability. Neuroimage. 2006 Jul 1; 31(3):1116-28.
+
+### Getting Started
+
+ITK-SNAP was designed for ease of use. There are several resources to get
+started with it.
+
+* If you downloaded ITK-SNAP as a binary executable from
+ [itksnap.org](http://www.itksnap.org/download/snap), you can get started
+ by reading the [online tutorials](http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.HomePage).
+* If you downloaded ITK-SNAP source code, please follow these [online
+ instructions](http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.BuildingITK-SNAP)
+ to compile and build ITK-SNAP.
+* You will need a 3D image to use this software. Images with which ITK-SNAP
+ is known to work are found on the [ITK-SNAP download page](http://www.itksnap.org/download/snap)
+* There are also materials (images, exercises, video) from training courses
+ available on the [documentation page](http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.SNAP3).
+
+### Asking Questions
+
+If you have a question about ITK-SNAP, here are the places where you may find an answer. Keep in mind that since ITK-SNAP in not a commercial product, it may take some time for an answer to arrive.
+
+* If you have an question about how to use SNAP or why it's not doing what
+ it should, post in on the [ITK-SNAP Users' List](http://www.itksnap.org/pmwiki/pmwiki.php?n=MailingLists).
+* If you have a technical question or a questing related to ITK, join the
+ [ITK Users' Mailing List](http://www.itk.org/HTML/MailingLists.htm) and
+ post your question there.
+
+### Get Involved!
+
+Your participation as a user, developer or sponsor is essential to ensuring a
+bright future for ITK-SNAP. There are many ways in which you can contribute to
+the stability and quality of this program:
+
+* By participating in the [ITK-SNAP User and Developer Mailing Lists](http://www.itksnap.org/pmwiki/pmwiki.php?n=MailingLists).
+* By reporting bugs and suggesting enhancements using the [Bug Tracker](http://www.itksnap.org/pmwiki/pmwiki.php?n=Main.BugTracker).
+* By joining the ITK-SNAP Development Team on [SourceForge.net](http://sourceforge.net/projects/itk-snap) and contributing source code.
+
diff --git a/ReleaseNotes.txt b/ReleaseNotes.md
similarity index 83%
rename from ReleaseNotes.txt
rename to ReleaseNotes.md
index 782e122..79796b4 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.md
@@ -1,15 +1,81 @@
+ITK-SNAP Release Notes - Version 3.6.0
======================================
- InsightSNAP Release Notes
- Version 3.4.0
-======================================
------------------
-1. New Features
------------------
+## Version 3.6.0
+----
+
+The main focus of this release is on making it easier to work with multiple images that have different size and resolution. This is particularly useful for viewing and segmenting MRI studies, where a single session contains many scans with different parameters. Important new features include the ability to load multiple images of different dimensions, voxel size, and orientation into a single ITK-SNAP window; automatic and manual **registration**; and enhanced support for DICOM format im [...]
+
+### New features
+
+- Multiple images with different dimensions, voxel size, and orientation can be visualized in the same ITK-SNAP window. When additional images are loaded, they are represented in memory in their native resolution, and resampled on the fly to match the screen resolution.
+
+ - This means that you can use information from two MRI modalities to guide manual segmentation. You can load a T1-weighted image with 1.0mm isotropic resolution and a T2-weighted image with 0.4mm x 0.4mm x 2.0mm resolution, and use the full information from both of these images for your segmentation.
+
+ - The manual segmentation is still performed in the voxel space of the main image.
+
+- A new registration tool is available under *Tools->Registration*. The tool provides both interactive manual registration and automatic affine and rigid registration.
+
+ - Manual registration includes rotation/translation/scaling widgets and a mouse-based interactive registration mode, where moving the mouse over the 'moving' image performs rotation and translation. Rotation is performed by turning a 'wheel' widget, and very small rotations are possible. The center of rotation can be set by moving the cursor.
+
+ - Automatic registration is quite fast. It allows rigid and affine registration. It supports mutual information (inter-modality) and patch cross-correlation (intra-modality) metrics. Optionally, a mask can be provided, over which the metric is computed. It is easy to generate masks using the segmentation interpolation tool (see below). Masks are useful when the extent of the images is different, e.g., one includes neck and another does not.
+
+ - Registration results can be saved as matrix files compatible with ANTS, Convert3D and Greedy tools. Using Convert3D, they can be converted to FLIRT-compatible transform files. Registration results are also automatically saved in workspace files. Images can also be resliced into the space of the main image.
+
+- DICOM functionality is greatly improved.
+
+ - After you load a 3D volume from a DICOM image directory, you can load other 3D volumes in the same directory quickly using the new *File->Add Another DICOM Series* submenu.
+
+ - Listing of DICOM directories (in the Open Image wizard) is much faster than before. This really makes a difference when opening images on DVDs and USB sticks.
+
+ - Dragging and dropping a DICOM file onto the ITK-SNAP window on Windows and Mac now shows the list of 3D volumes in the directory, allowing you to choose a volume to open.
+
+- A new tool for interpolating segmentations between slices under *Tools->Interpolate Labels*.
+
+ - For images with thin slices and gradually changing anatomy, you can segment every fifth slice and fill in the missing slices using this new tool. Details of the algorithm are provided in an [Insight Journal article](http://insight-journal.org/browse/publication/977). Interpolation can be performed for a specific label or for all labels.
+
+- Label visibility can be changed 'en masse' in the Label Editor, i.e., you can make all labels visible or hidden. This helps find a label in segmentation with many labels that occlude each other.
+
+- A new ''grid'' mode is provided for visualizing displacement fields. Displacement fields in formats used by [ANTS](http://stnava.github.io/ANTs/) and [Greedy](https://sites.google.com/view/greedyreg/about) are currently supported.
+
+- The user interface automatically scales to reasonable size on very high DPI displays, such as the Miscrosoft Surface (tm). Environment flag QT_SCALE_FACTOR can be used to override.
-1.1. New in Version 3.4.0
-----------------------------------------------
+### Programmatic Enhancements
+- ITK-SNAP now includes Convert3D and Greedy registration tools as submodules. This is already used to support registration functionality, but in the future we will be adding more Convert3D-based functionality, such as filtering, etc.
+
+- The ImageWrapper class includes a dual slicing module that selects between orthogonal and non-orthogonal slicing based on image orientation. This is significantly cleaned up relative to earlier versions.
+
+- ITK-SNAP now compatible with Qt 5.6, which is the standard for the next 3 years
+
+### Bug Fixes
+
+- Fixed issue with *Workspace->Save As...* where you would be asked to save the current workspace (thx Laura W).
+- Fixed issue where DICOM data was not loading correctly when workspace was moved in the filesystem (thx Vincent F).
+- Fixed issue with loading of NRRD images on some systems due to non-US locale (thx Johan B)
+- Fixed behavior of *Ctrl-W*, so that when there are open dialog windows this keystroke closes them rather than the current image layer (thx Karl B)
+- Fixed issue with multi-component images, where every 100th component showed up as 'Magnitude' (this Phil. C)
+- Fixed OpenGL rendering issues on Windows 10 on some cards
+- Fixed computation of zoom factor on Retina displays
+- Fixed behavior of File Save browse dialogs on MacOS when saving .nii.gz files
+- Fixed issues with unnecessary prompting to save segmentations
+- Improved OpenGL compatibility, particularly in VTK-managed windows
+- Fixed issues with images >4GB not opening on Windows. This requires building ITK with ITK_USE_64BITS_IDS=TRUE
+- Fixed problems with histories not showing up in file dialogs
+- Fixed bug where scalpel tool did not work for anisotropic images
+- Several other fixes for specific crashes.
+
+### Known Issues
+
+ - Some advanced features (label editor advanced tools, window shrinking) have
+ not yet been ported to Qt. They will be ported in future versions, and
+ expanded in the process. There is a plan to integrate SNAP and C3D which
+ will provide much richer image processing support for images and segmentations.
+
+ - The cutplane in the scalpel tool is too big when the camera is zoomed in and
+ it needs a flip button. Also with dark labels you can't see the handle.
+## Version 3.4.0
+----
This release was largely focused on improving the user experience through extensive
changes to the GUI. We made access to the new features introduced in versions 3.0 and
3.2 more intuitive. The loading and display of multiple images has been extensively
@@ -21,11 +87,11 @@ functionality such as image registration within SNAP, and a smaller memory footp
but these will become available in the 3.6 release. The main focus of 3.4 is
on usability.
-1.1.1. New functionality in this release
+### New Features
- The controls for selecting segmentation labels have been brought back to the
main SNAP panel, where they were in versions 1 and 2. This will make the
- interface more familiar to long-time users.
+ interface more familiar to long-time users.
- The interface for loading and viewing additional image layers has been
extensively redesigned. When more than one anatomical image is loaded into
@@ -33,7 +99,7 @@ on usability.
rest of the images as thumbnails. Clicking the thumbnails makes them full
size. This new thumbnail mode is far more intuitive for working with
multiple images than the method of semi-transparent overlays employed in
- previous versions.
+ previous versions.
- The IO dialog for opening additional images promts whether the user would
like the image to be shown side by side with an existing image or as a
@@ -91,8 +157,7 @@ on usability.
- Training examples drawn in random forest classificaiton mode are now retained
and can be reused to label multiple structures.
-
-1.1.2. Programmatic Improvements
+### Programmatic Improvements
- Refactored the software to allow multiple images that occupy different
anatomical space to be loaded in the same ITK-SNAP session. This
@@ -114,16 +179,14 @@ on usability.
- Restored ability to build on 64 bit Windows
-
-1.2. New in Version 3.2.0
-----------------------------------------------
-
+## Version 3.2.0
+----
The main new feature introduced in this release is supervised classification.
The release also is built against Qt5 (3.0 used Qt4.8), which resulted in a lot
of changes to compilation and packaging. Quite a few bugs have been fixed and
the release should be more stable than 3.0.
-1.2.1. New functionality in this release
+### New Features
- Added a supervised classification presegmentation mode. This mode allows
the user to compute the speed image by marking examples of two or more
@@ -154,8 +217,8 @@ the release should be more stable than 3.0.
- Improved volumes & statistics computation speed and display formatting
-1.2.2. Bug Fixes
-
+### Bug Fixes
+
- Fixed a bug with small bubbles not growing in snake mode.
- Refactored the DICOM input code and fixed several errors in the process.
@@ -168,7 +231,7 @@ the release should be more stable than 3.0.
- Fixed several dozen smaller bugs
-1.2.3. Programmatic Improvements
+### Programmatic Improvements
- Migrated to Qt5 as well as up to date versions of ITK (4.5) and VTK (6.1)
@@ -179,14 +242,13 @@ the release should be more stable than 3.0.
-1.3. New in Version 3.0.0
-----------------------------------------------
-
+## Version 3.0.0
+----
This is a major new release of ITK-SNAP. The user interface has been completely
rewritten using the Qt platform, and new functionality for multi-modal image
segmentation has been added.
-1.3.1. New functionality for multi-modality image segmentation
+### New functionality for multi-modality image segmentation
- SNAP is no longer limited to just scalar-valued and RGB-valued images. An
image with any number of components can be loaded into SNAP. The GUI provides
@@ -214,7 +276,7 @@ segmentation has been added.
The user selects the desired number of clusters (i.e., tissue classes) and
once the clusters are initialized, chooses the cluster of interest.
-1.3.2. New features in the Qt-based GUI
+### New features in the Qt-based GUI
- The graphical user interface (GUI) uses Qt, a much more powerful toolkit
than the FLTK toolkit in the previous versions. The new GUI is much richer
@@ -260,7 +322,7 @@ segmentation has been added.
filtering labels by name, assinging foreground/background labels directly
from the dialog.
-1.3.3. Improvements to 3D rendering window
+### Improvements to 3D rendering window
- The 3D rendering window now uses the VTK toolkit for rendering. This will
make it easier to introduce new functionality (such as volume rendering)
@@ -280,7 +342,7 @@ segmentation has been added.
- The scalpel tool uses VTK's 3D cutplane widget that can be rotated and moved
after the cut has been drawn.
-1.3.4. Other new features
+### Other new features
- Reduced memory footprint for large images. The previous version of SNAP
would allocate on the order of 6 bytes for every voxel in the main image.
@@ -296,7 +358,7 @@ segmentation has been added.
lists all the series with their dimensions and other meta-data, making it
easier to determine which series one wishes to load.
-1.3.5. Programmatic improvements
+### Programmatic improvements
- The SNAP code has been extensively refactored. There is a new "model" layer
separating the Qt GUI from the "logic" layer. This layer is agnostic to the
@@ -308,22 +370,21 @@ segmentation has been added.
reduces the amount of Qt-aware code.
-1.4. New in Version 2.4.0
-----------------------------------------------
-
+## Version 2.4.0
+----
This is the last planned release of the FLTK-based version of ITK-SNAP. It adds
minimal new functionality and addressed a number of bugs reported in the last
year. The subsequent releases of ITK-SNAP will be based on the Qt platform and
will have the 3.x version number.
-1.4.1. New Features and UI Improvements
+### New Features and UI Improvements
- Ported the dependency on ITK 3.20.1 to ITK 4.2 on all operating systems:
Mac OS X, Linux, and Windows.
- ITK-SNAP can read and write MRC images now.
-1.4.2. Bug Fixes and Stability Improvements
+### Bug Fixes and Stability Improvements
- Fixed a problem with the RAI code which was not updating after reorienting.
@@ -348,16 +409,13 @@ will have the 3.x version number.
- Corrected a bug in the code with SparseLevelSet filter being used
instead of ParallelSparse.
-======================================
-
-1.5. New in Version 2.2.0
-----------------------------------------------
-
+### Version 2.2.0
+----
This is largely a maintenance release, with a few usability enhancements based
on user feedback. The main change programmatically is 64 bit support on Linux,
MacOS and Windows.
-1.5.1. New Features and UI Improvements
+### New Features and UI Improvements
- 64 bit versions of the software are available for Linux, Windows and Mac.
These versions are now built nightly and will be distributed on
@@ -420,7 +478,7 @@ MacOS and Windows.
- Ability to save segmentation mesh in active contour mode
-1.5.2. Bug Fixes and Stability Improvements
+### Bug Fixes and Stability Improvements
- Fixed a problem with certain operations being very slow because of the way
the progress bars were displayed. Preprocessing, mesh rendering and mesh
@@ -438,10 +496,9 @@ MacOS and Windows.
- Fixed problem with bubbles not being spherical for certain image orientations
-1.6. New in Version 2.0
-----------------------------------------------
-
-1.6.1. New Features and UI Improvements
+## Version 2.0
+----
+### New Features and UI Improvements
- Support for multiple image layers. Users can now load gray and RGB images
as overlays on top of the main image layer. For example, one can display a
@@ -498,21 +555,11 @@ MacOS and Windows.
if you want to save the segmentation image before exiting. Of course this may not
always work, but it should make a lot of frustrated users a little less frustrated.
- - Reduced the memory footprint. There is still room for improvement, of course. Currently,
- ITK-SNAP requires 6 bytes per voxel in manual segmentation mode. More memory is needed for
- mesh rendering, and a lot more for automatic segmentation. When loading images in 32-bit
- or 64-bit formats, more memory may be required at the time of image IO. That is because
- ITK NIFTI reader (and maybe other readers) keeps a second copy of the image in memory
- during IO. This memory is immediately deallocated though.
+ - Reduced the memory footprint. There is still room for improvement, of course. Currently, ITK-SNAP requires 6 bytes per voxel in manual segmentation mode. More memory is needed for mesh rendering, and a lot more for automatic segmentation. When loading images in 32-bit or 64-bit formats, more memory may be required at the time of image IO. That is because ITK NIFTI reader (and maybe other readers) keeps a second copy of the image in memory during IO. This memory is immediately dealloc [...]
- - Unified navigation modes. The crosshair mode allows zoom and pan (RMB/MMB), and has an
- auto-pan feature when you move the crosshair close to the edge of the slice window.
- The zoom/pan mode is redundant, but we left it in place for backward compatibility.
- In the zoom/pan mode, zoom is RMB, pan is LMB, crosshair motion is MMB. In all other
- modes, crosshair motion is accessible through MMB as well.
+ - Unified navigation modes. The crosshair mode allows zoom and pan (RMB/MMB), and has an auto-pan feature when you move the crosshair close to the edge of the slice window. The zoom/pan mode is redundant, but we left it in place for backward compatibility. In the zoom/pan mode, zoom is RMB, pan is LMB, crosshair motion is MMB. In all other modes, crosshair motion is accessible through MMB as well.
-
-1.6.2. Bug Fixes
+### Bug Fixes
- Fixed an issue with SNAP reading certain types of image twice from disc. This should
speed up the reading of floating point images, for example.
@@ -532,16 +579,15 @@ MacOS and Windows.
- Please see the bug tracker on itksnap.org for the full listing of bug fixes.
-1.6.3. Website Changes
+### Website Changes
- The itksnap.org website has been Wikified. Content can now be edited on the fly.
-1.7. New in Version 1.8
-----------------------------------------------
-
-1.7.1. New Features and UI Improvements
+## Version 1.8
+----
+### New Features and UI Improvements
- Support for reading floating point images of arbitrary range. SNAP still
represents gray images internally as signed short, but now it can load a
@@ -591,11 +637,11 @@ MacOS and Windows.
- 3D Meshes generated and rendered by SNAP are now represented in NIFTI world
coordinates. Previously, the coordinates were computed using the formula
- x_mesh = x_voxel * spacing + origin
+ x_mesh = x_voxel * spacing + origin
In version 1.8 and beyond, the mesh coordinates are computed as
- x_mesh = nifti_sform_matrix * [x_voxel; 1]
+ x_mesh = nifti_sform_matrix * [x_voxel; 1]
This means that the meshes output by earlier versions of SNAP may be
translated and rotated relative to the meshes output by version 1.8. This will
@@ -669,7 +715,7 @@ MacOS and Windows.
- Documented existing keyboard shortcuts and added some new ones. Available
shortcuts can be listed by selecting Help->Shortcuts.
-1.7.2. Programmatic/Distribution Changes
+### Programmatic/Distribution Changes
- SNAP is now built against ITK 3.8, offering several improvements, especially
in how image orientation is handled.
@@ -686,19 +732,17 @@ MacOS and Windows.
- Level set fix for ITK 3.8 fixes automatic segmentation's weird behavior
-1.8. New in Version 1.6.0.1
----------------------------
-
-1.8.1. Bug Fixes
+## Version 1.6.0.1
+----
+### Bug Fixes
- Major bug in release 1.6.0 involving disabled cursor movement in snake
segmentation mode has been resolved.
-1.9. New in Version 1.6.0
--------------------------
-
-1.9.1. New Features and UI Improvements
+## Version 1.6.0
+----
+### New Features and UI Improvements
- You can now save a sequence of all axial, coronal or sagittal slices with
overlays as PNG files (File->Save->Screenshot Series).
@@ -747,11 +791,11 @@ MacOS and Windows.
- Shortened/simplified some of the menu items
-1.9.2. Bug Fixes
+### Bug Fixes
- Various bugs have been fixed :)
-1.9.3. Distribution Changes
+### Distribution Changes
- SNAP website fully migrated to sourceforge.net
@@ -761,10 +805,9 @@ MacOS and Windows.
- Linux binaries will be available starting with 1.6.0
-1.10. New in Version 1.4.1
--------------------------
-
-1.10.1. New Features and UI Improvements
+## Version 1.4.1
+----
+### New Features and UI Improvements
- Added paintbrush tool to the main toolbar. Paintbrush can be used to quickly
touch up segmentations. Left mouse button paints with selected label, right
@@ -783,7 +826,7 @@ MacOS and Windows.
different segmentation from the same viewpoint. Press 's' in the 3D window
to save the camera state and 'r' to restore it.
-1.10.2. Bug Fixes
+### Bug Fixes
- MAJOR: fixed bug that was causing crashes on Win32 during polygon drawing
(thanks to Jeff Tsao for this bug fix!)
@@ -801,7 +844,7 @@ MacOS and Windows.
- Fixed crash when changing bubble radius and then going back to
preprocessing mode
-1.10.3. Distribution Changes
+### Distribution Changes
- Interim SNAP releases are now hosted on SourceForge. ITK repository will only
be used to host major releases (like 1.6). This allows us to check stuff in
@@ -813,10 +856,9 @@ MacOS and Windows.
own and the download size is reduced
-1.11. New in Version 1.4
------------------------
-
-1.11.1. New Features and User Interface Improvements
+## Version 1.4
+----
+### New Features and User Interface Improvements
- New and improved label editor. You can easily switch between labels while in
the editor and the interface for adding new labels is more intuitive. You
@@ -855,7 +897,7 @@ MacOS and Windows.
- The tutorial has been updated to reflect the new features.
-1.11.2. Bug Fixes.
+### Bug Fixes.
- SNAP should crash a lot less than before
@@ -871,20 +913,19 @@ MacOS and Windows.
- Lots of other small bugs have been fixed!
-1.11.3. Programmatic Enhancements
+### Programmatic Enhancements
- SNAP and IRIS now share the sameset of OpenGL windows. This should prevent
crashes on some platforms.
-1.11.4. Other
+### Other
- SNAP available as a universal (Intel/PPC) binary for MacOS at itksnap.org
-1.11. New in Version 1.2
------------------------
-
-1.11.1. User Interface Improvements
+## Version 1.2
+----
+### User Interface Improvements
- The ability to switch between 4-view mode and single view mode. Each of the
slice views and the 3D view can be expanded to occupy the entire SNAP window.
@@ -904,7 +945,7 @@ MacOS and Windows.
- SNAP remembers the last ROI used for each image.
-1.11.2. Programmatic Improvements
+### Programmatic Improvements
- The level set segmentation pipeline has been rewritten, taking advantage of
the stop and go functionality of ITK finite difference filters. This means
@@ -914,7 +955,7 @@ MacOS and Windows.
machine automatically activates and deactivates UI widgets based on a set of
flags. Rules such as Flag A => Flag B can be added to the state machine.
-1.11.3. Bug Fixes
+### Bug Fixes
- Slice views update correctly when the SNAP window is resized
@@ -924,14 +965,4 @@ MacOS and Windows.
advection term.
-2. Known Issues
------------------
-
- - Some advanced features (label editor advanced tools, window shrinking) have
- not yet been ported to Qt. They will be ported in future versions, and
- expanded in the process. There is a plan to integrate SNAP and C3D which
- will provide much richer image processing support for images and segmentations.
-
- - The cutplane in the scalpel tool is too big when the camera is zoomed in and
- it needs a flip button. Also with dark labels you can't see the handle.
diff --git a/Submodules/c3d/.gitignore b/Submodules/c3d/.gitignore
new file mode 100644
index 0000000..f72dd12
--- /dev/null
+++ b/Submodules/c3d/.gitignore
@@ -0,0 +1,16 @@
+# VIM swap files
+.*.sw?
+
+# MACOS files
+.DS_Store*
+Thumbs.db
+
+# Backup files
+*.bak
+*.un~
+*~
+
+# Qt Creator stuff
+CMakeLists.txt.user*
+CTestConfig.cmake.user*
+*.autosave
diff --git a/Submodules/c3d/CMakeLists.txt b/Submodules/c3d/CMakeLists.txt
new file mode 100644
index 0000000..d57a264
--- /dev/null
+++ b/Submodules/c3d/CMakeLists.txt
@@ -0,0 +1,219 @@
+PROJECT(CONVERT3D)
+
+# If we are building as a sub-project we skip all of this extra stuff
+IF(NOT CONVERT3D_BUILD_AS_SUBPROJECT)
+
+ # Nonsense for cmake 2.6 compatibility
+ CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
+ if(COMMAND cmake_policy)
+ cmake_policy(SET CMP0003 NEW)
+ endif(COMMAND cmake_policy)
+
+ # Do we want to build the UI
+ OPTION(BUILD_GUI "Do you want to build the Qt-based C3D user interfaace?" OFF)
+
+ # Get ITK
+ FIND_PACKAGE(ITK REQUIRED)
+ INCLUDE(${ITK_USE_FILE})
+
+ENDIF(NOT CONVERT3D_BUILD_AS_SUBPROJECT)
+
+# Include the library file
+INCLUDE(${CONVERT3D_SOURCE_DIR}/ConvertNDLibrary.cmake)
+
+IF(NOT CONVERT3D_BUILD_AS_SUBPROJECT)
+
+ # Command-line executables
+ ADD_EXECUTABLE(c3d Convert3DMain.cxx)
+ TARGET_LINK_LIBRARIES(c3d ${C3D_LINK_LIBRARIES})
+
+ ADD_EXECUTABLE(c2d Convert2DMain.cxx)
+ TARGET_LINK_LIBRARIES(c2d ${C3D_LINK_LIBRARIES})
+
+ ADD_EXECUTABLE(c4d Convert4DMain.cxx)
+ TARGET_LINK_LIBRARIES(c4d ${C3D_LINK_LIBRARIES})
+
+ ADD_EXECUTABLE(c3d_affine_tool utilities/AffineTransformTool.cxx)
+ TARGET_LINK_LIBRARIES(c3d_affine_tool ${C3D_LINK_LIBRARIES})
+
+ SET(CMDLINE_TARGETS c2d c3d c4d c3d_affine_tool)
+
+
+ IF(WIN32)
+ ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
+ ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
+ SOURCE_GROUP("Adapter Sources" REGULAR_EXPRESSION "adapters/*cxx")
+ SOURCE_GROUP("Adapter Headers" REGULAR_EXPRESSION "adapters/*h")
+ ENDIF(WIN32)
+
+ # Pull in FFTW
+ FIND_LIBRARY(FFTW_LIB fftw3f)
+
+ # =====================================================
+ # OPTIONAL GUI BUILD
+ # =====================================================
+ IF(BUILD_GUI)
+
+ # CMake modules taken from the GUI dir
+ SET(CMAKE_MODULE_PATH ${CONVERT3D_SOURCE_DIR}/gui/CMake)
+
+ #--------------------------------------------------------------------------------
+ # Find Qt5
+ #--------------------------------------------------------------------------------
+ FIND_PACKAGE(Qt5Widgets)
+ SET(CONVERT3D_QT5_INCLUDE_DIRS ${Qt5Widgets_INCLUDE_DIRS})
+ SET(CONVERT3D_QT5_LIBRARIES Qt5::Widgets)
+
+ #--------------------------------------------------------------------------------
+ # Specify source files and headers
+ #--------------------------------------------------------------------------------
+ INCLUDE_DIRECTORIES(
+ ${CONVERT3D_SOURCE_DIR}/gui
+ ${CONVERT3D_BINARY_DIR}
+ ${CONVERT3D_QT5_INCLUDE_DIRS}
+ )
+
+ SET(UI_QT_CXX
+ gui/CommandEditor.cxx
+ gui/ConvertSyntaxHighlighter.cxx
+ gui/HistoryDialog.cxx
+ gui/MainWindow.cxx
+ gui/SettingsDialog.cxx
+ gui/main.cxx)
+
+ SET(UI_MOC_HEADERS
+ gui/CommandEditor.h
+ gui/ConvertSyntaxHighlighter.h
+ gui/HistoryDialog.h
+ gui/MainWindow.h
+ gui/SettingsDialog.h)
+
+ SET(UI_FORMS
+ gui/HistoryDialog.ui
+ gui/MainWindow.ui
+ gui/SettingsDialog.ui)
+
+ SET(UI_NONMOC_HEADERS)
+
+ # Wrap the QT input files
+ QT5_WRAP_UI(UI_FORM_HEADERS ${UI_FORMS})
+ QT5_WRAP_CPP(UI_WRAPPED_MOC_HEADERS ${UI_MOC_HEADERS})
+
+ # Configure the OS-specific parts of the GUI exe
+ IF(APPLE)
+ SET(C3DGUI_OSX_ICON ${CONVERT3D_SOURCE_DIR}/gui/resources/macos/c3dgui.icns)
+ SET(UI_OS_EXTRAS ${C3DGUI_OSX_ICON})
+ ENDIF(APPLE)
+
+ #--------------------------------------------------------------------------------
+ # Define main GUI executable
+ #--------------------------------------------------------------------------------
+ SET(C3DGUI_MAIN_SRC ${UI_QT_CXX} ${UI_WRAPPED_MOC_HEADERS} ${UI_MOC_HEADERS}
+ ${UI_NONMOC_HEADERS} ${UI_FORM_HEADERS} ${UI_OS_EXTRAS})
+
+ # Define the main executable
+ SET(C3DGUI_BUNDLE_NAME "Convert3DGUI")
+
+ # Configure the executable's sources and libraries
+ ADD_EXECUTABLE(${C3DGUI_BUNDLE_NAME} WIN32 MACOSX_BUNDLE ${C3DGUI_MAIN_SRC})
+ TARGET_LINK_LIBRARIES(${C3DGUI_BUNDLE_NAME} ${CONVERT3D_QT5_LIBRARIES} ${C3D_LINK_LIBRARIES})
+
+ #--------------------------------------------------------------------------------
+ # Install the application
+ #--------------------------------------------------------------------------------
+
+ # On Apple, configure the application icon
+ IF(APPLE)
+
+ # set how it shows up in the Info.plist file
+ SET(MACOSX_BUNDLE_ICON_FILE c3dgui.icns)
+
+ # set where in the bundle to put the icns file
+ SET_SOURCE_FILES_PROPERTIES(${C3DGUI_OSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+
+ # Set up the Info.plist file
+ SET_TARGET_PROPERTIES(${C3DGUI_BUNDLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST
+ ${CONVERT3D_SOURCE_DIR}/gui/resources/macos/Info.plist)
+
+ # on Apple, the bundle is at the root of the
+ # install tree, and on other platforms it'll go into the bin directory.
+ INSTALL(TARGETS ${C3DGUI_BUNDLE_NAME} BUNDLE DESTINATION . COMPONENT Runtime)
+
+ # Install the command line programs inside of the application
+ INSTALL(TARGETS ${CMDLINE_TARGETS} DESTINATION ${C3DGUI_BUNDLE_NAME}.app/Contents/bin)
+
+ # Include the qt4 dependent libraries
+ include(DeployQt5)
+ install_qt5_executable(${C3DGUI_BUNDLE_NAME}.app)
+
+ ELSEIF(WIN32)
+
+ # Set the PATH to include Qt libraries
+ GET_FILENAME_COMPONENT(QT_BINARY_DIR "${Qt5Core_DIR}/../../../bin" ABSOLUTE)
+ GET_FILENAME_COMPONENT(QT_LIBRARY_DIR "${Qt5Core_DIR}/../../" ABSOLUTE)
+
+ # Install to the bin directory
+ INSTALL(TARGETS ${C3DGUI_BUNDLE_NAME} RUNTIME DESTINATION bin)
+
+ # Install the command line programs inside of the application
+ INSTALL(TARGETS ${CMDLINE_TARGETS} DESTINATION bin)
+
+ # Include the qt4 dependent libraries
+ include(DeployQt5)
+
+ # Make sure the GIF plugin is included
+ get_property(QT_WIN_PLUGIN TARGET Qt5::QWindowsIntegrationPlugin PROPERTY LOCATION_RELEASE)
+
+ # Install with the plugin
+ install_qt5_executable(bin/${C3DGUI_BUNDLE_NAME}.exe "${QT_WIN_PLUGIN}")
+
+ # On windows, we have to configure NSIS
+ SET(CPACK_NSIS_INSTALLED_ICON_NAME "${C3DGUI_BUNDLE_NAME}.exe")
+ SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} Convert3D")
+ SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.itksnap.org/c3d")
+ SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.itksnap.org/c3d")
+
+ # CMake does not yet know to install into (x64) program files or not
+ IF(CMAKE_CL_64)
+ SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
+ ENDIF(CMAKE_CL_64)
+
+ # Give it a windowsy directory name
+ SET(CPACK_PACKAGE_INSTALL_DIRECTORY "Convert3D")
+
+ # On Win32, the executable is the actual exe
+ SET(CPACK_PACKAGE_EXECUTABLES ${C3DGUI_BUNDLE_NAME} "Convert3D GUI")
+
+ ELSE()
+
+ SET(C3DGUI_EXE "c3d_gui")
+ SET(C3DGUI_MAIN_INSTALL_DIR lib/c3d_gui-${C3D_VERSION_FULL})
+ SET(CPACK_PACKAGE_EXECUTABLES ${C3DGUI_EXE} ${C3DGUI_BUNDLE_NAME})
+
+ INSTALL(TARGETS ${C3DGUI_BUNDLE_NAME} RUNTIME DESTINATION ${C3DGUI_MAIN_INSTALL_DIR})
+
+ # Install the command line programs inside of the application
+ INSTALL(TARGETS ${CMDLINE_TARGETS} DESTINATION bin)
+
+ # On Linux, we generate forward shared executable
+ SUBDIRS(gui/Utilities/Forwarding)
+
+ include(DeployQt5)
+ install_qt5_executable(${C3DGUI_MAIN_INSTALL_DIR}/${C3DGUI_BUNDLE_NAME} "qgif")
+
+ ENDIF()
+
+ ELSE(BUILD_GUI)
+
+ INSTALL(TARGETS c3d c2d c4d RUNTIME DESTINATION bin)
+
+ ENDIF(BUILD_GUI)
+
+ # Do the packaging
+ INCLUDE(cmake/Package.cmake)
+
+ # CDash Support
+ ENABLE_TESTING()
+ INCLUDE(CTest)
+
+ENDIF(NOT CONVERT3D_BUILD_AS_SUBPROJECT)
diff --git a/Submodules/c3d/COPYING.txt b/Submodules/c3d/COPYING.txt
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/Submodules/c3d/COPYING.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/CTestConfig.cmake b/Submodules/c3d/CTestConfig.cmake
similarity index 53%
copy from CTestConfig.cmake
copy to Submodules/c3d/CTestConfig.cmake
index 2a7898b..71549f1 100644
--- a/CTestConfig.cmake
+++ b/Submodules/c3d/CTestConfig.cmake
@@ -1,15 +1,14 @@
## This file should be placed in the root directory of your project.
## Then modify the CMakeLists.txt file in the root directory of your
## project to incorporate the testing dashboard.
-##
-## # The following are required to submit to the CDash dashboard:
+## # The following are required to uses Dart and the Cdash dashboard
## ENABLE_TESTING()
## INCLUDE(CTest)
-
-set(CTEST_PROJECT_NAME "ITK-SNAP 3.4")
-set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC")
+set(CTEST_PROJECT_NAME "convert3d")
+set(CTEST_NIGHTLY_START_TIME "01:00:00 EST")
set(CTEST_DROP_METHOD "http")
-set(CTEST_DROP_SITE "itksnap.org")
-set(CTEST_DROP_LOCATION "/cdash/submit.php?project=ITK-SNAP+3.4")
+set(CTEST_DROP_SITE "www.itksnap.org")
+set(CTEST_DROP_LOCATION "/cdash/submit.php?project=convert3d")
set(CTEST_DROP_SITE_CDASH TRUE)
+
diff --git a/Submodules/c3d/Convert2DMain.cxx b/Submodules/c3d/Convert2DMain.cxx
new file mode 100644
index 0000000..a972381
--- /dev/null
+++ b/Submodules/c3d/Convert2DMain.cxx
@@ -0,0 +1,39 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Convert2DMain.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ConvertImageND.h"
+#include "itkVoxBoCUBImageIOFactory.h"
+#include "itkPovRayDF3ImageIOFactory.h"
+
+int main(int argc, char *argv[])
+{
+ // Load the ITK factories
+ itk::ObjectFactoryBase::RegisterFactory(itk::VoxBoCUBImageIOFactory::New());
+ itk::ObjectFactoryBase::RegisterFactory(itk::PovRayDF3ImageIOFactory::New());
+
+ ImageConverter<double, 2> convert;
+ return convert.ProcessCommandLine(argc, argv);
+}
+
diff --git a/Submodules/c3d/Convert3DMain.cxx b/Submodules/c3d/Convert3DMain.cxx
new file mode 100644
index 0000000..243ec4f
--- /dev/null
+++ b/Submodules/c3d/Convert3DMain.cxx
@@ -0,0 +1,38 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Convert3DMain.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ConvertImageND.h"
+#include "itkVoxBoCUBImageIOFactory.h"
+#include "itkPovRayDF3ImageIOFactory.h"
+
+int main(int argc, char *argv[])
+{
+ // Load the ITK factories
+ itk::ObjectFactoryBase::RegisterFactory(itk::VoxBoCUBImageIOFactory::New());
+ itk::ObjectFactoryBase::RegisterFactory(itk::PovRayDF3ImageIOFactory::New());
+
+ ImageConverter<double, 3> convert;
+ return convert.ProcessCommandLine(argc, argv);
+}
diff --git a/Submodules/c3d/Convert4DMain.cxx b/Submodules/c3d/Convert4DMain.cxx
new file mode 100644
index 0000000..fe4b732
--- /dev/null
+++ b/Submodules/c3d/Convert4DMain.cxx
@@ -0,0 +1,38 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Convert3DMain.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ConvertImageND.h"
+#include "itkVoxBoCUBImageIOFactory.h"
+#include "itkPovRayDF3ImageIOFactory.h"
+
+int main(int argc, char *argv[])
+{
+ // Load the ITK factories
+ itk::ObjectFactoryBase::RegisterFactory(itk::VoxBoCUBImageIOFactory::New());
+ itk::ObjectFactoryBase::RegisterFactory(itk::PovRayDF3ImageIOFactory::New());
+
+ ImageConverter<double, 4> convert;
+ return convert.ProcessCommandLine(argc, argv);
+}
diff --git a/Submodules/c3d/ConvertException.h b/Submodules/c3d/ConvertException.h
new file mode 100644
index 0000000..2ed0726
--- /dev/null
+++ b/Submodules/c3d/ConvertException.h
@@ -0,0 +1,63 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertException.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ConvertException_h_
+#define __ConvertException_h_
+
+#include <exception>
+#include <string>
+#include <cstdarg>
+
+class ConvertException : public std::exception
+{
+public:
+ ConvertException(const char *fmt, ...)
+ {
+ char buffer[1024];
+ va_list parg;
+ va_start(parg, fmt);
+ vsprintf(buffer, fmt, parg);
+ va_end(parg);
+ message=buffer;
+ }
+
+ virtual ~ConvertException() throw() {}
+
+ virtual const char *what() const throw()
+ { return message.c_str(); }
+
+private:
+ std::string message;
+};
+
+class StackAccessException : public ConvertException
+{
+public:
+ StackAccessException()
+ : ConvertException("Image Stack Access Exception") {}
+
+};
+
+#endif // __ConvertException_h_
diff --git a/Submodules/c3d/ConvertImageND.cxx b/Submodules/c3d/ConvertImageND.cxx
new file mode 100644
index 0000000..f596b4a
--- /dev/null
+++ b/Submodules/c3d/ConvertImageND.cxx
@@ -0,0 +1,2609 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertImageND.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ConvertImageND.h"
+
+#include "AddImages.h"
+#include "AlignByLandmarks.h"
+#include "AntiAliasImage.h"
+#include "ApplyMetric.h"
+#include "BiasFieldCorrectionN4.h"
+#include "BinaryHoleFill.h"
+#include "BinaryImageCentroid.h"
+#include "BinaryMathOperation.h"
+#include "Canny.h"
+#include "ClipImageIntensity.h"
+#include "ComputeFFT.h"
+#include "ComputeMoments.h"
+#include "ComputeOverlaps.h"
+#include "ConnectedComponents.h"
+#include "ConvertAdapter.h"
+#include "CoordinateMap.h"
+#include "CopyTransform.h"
+#include "CreateImage.h"
+#include "CreateInterpolator.h"
+#include "DicomSeriesList.h"
+#include "ExtractRegion.h"
+#include "ExtractSlice.h"
+#include "FlipImage.h"
+#include "GeneralLinearModel.h"
+#include "HessianObjectness.h"
+#include "HistogramMatch.h"
+#include "ImageERF.h"
+#include "ImageGradient.h"
+#include "ImageLaplacian.h"
+#include "LabelOverlapMeasures.h"
+#include "LabelStatistics.h"
+#include "LandmarksToSpheres.h"
+#include "LaplacianSharpening.h"
+#include "LevelSetSegmentation.h"
+#include "MathematicalMorphology.h"
+#include "MeanFilter.h"
+#include "MedianFilter.h"
+#include "MixtureModel.h"
+#include "MRFVote.h"
+#include "MultiplyImages.h"
+#include "NormalizedCrossCorrelation.h"
+#include "NormalizeLocalWindow.h"
+#include "OverlayLabelImage.h"
+#include "PadImage.h"
+#include "PeronaMalik.h"
+#include "PrintImageInfo.h"
+#include "Rank.h"
+#include "ReadImage.h"
+#include "ReciprocalImage.h"
+#include "ReorderStack.h"
+#include "ReplaceIntensities.h"
+#include "ResampleImage.h"
+#include "ResliceImage.h"
+#include "RFApply.h"
+#include "RFTrain.h"
+#include "SampleImage.h"
+#include "ScalarToRGB.h"
+#include "ScaleShiftImage.h"
+#include "SetOrientation.h"
+#include "SetSform.h"
+#include "SignedDistanceTransform.h"
+#include "SLICSuperVoxel.h"
+#include "SmoothImage.h"
+#include "SplitMultilabelImage.h"
+#include "StapleAlgorithm.h"
+#include "TestImage.h"
+#include "ThresholdImage.h"
+#include "TileImages.h"
+#include "TrimImage.h"
+#include "UnaryMathOperation.h"
+#include "UpdateMetadataKey.h"
+#include "Vote.h"
+#include "VoxelwiseComponentFunction.h"
+#include "VoxelwiseRegression.h"
+#include "WarpImage.h"
+#include "WarpLabelImage.h"
+#include "WriteImage.h"
+#include "WeightedSum.h"
+#include "WeightedSumVoxelwise.h"
+#include "WrapDimension.h"
+
+#include <cstring>
+#include <algorithm>
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+
+// Support for regular expressions via KWSYS in ITK
+#include <itksys/RegularExpression.hxx>
+
+// Documentation manual
+#include "Documentation.h"
+
+// Markdown documentation string generated at compile-time
+// this looks a little odd, but works - the include file contains raw bytes
+unsigned char c3d_md[] = {
+ #include "markdown_docs.h"
+ 0x00
+};
+
+using namespace itksys;
+
+extern const char *ImageConverter_VERSION_STRING;
+
+// Helper function: read a double, throw exception if unreadable
+double myatof(char *str)
+{
+ char *end = 0;
+ double d = strtod(str, &end);
+ if (*end != 0)
+ throw "strtod conversion failed";
+ return d;
+};
+
+
+std::string str_to_lower(const char *input)
+{
+ std::string s(input);
+ std::transform(s.begin(), s.end(), s.begin(), (int(*)(int)) tolower);
+ return s;
+}
+
+// Check whether value is a valid float (no leading spaces allowed)
+bool is_double(const char *input)
+{
+ std::istringstream iss(input);
+ double f;
+ iss >> std::noskipws >> f; // noskipws considers leading whitespace invalid
+
+ // Check the entire string was consumed and if either failbit or badbit is set
+ return iss.eof() && !iss.fail();
+}
+
+
+
+ /*
+ out << "Command Listing: " << endl;
+ out << " -accum" << endl;
+ out << " -add" << endl;
+ out << " -align-landmarks, -alm" << endl;
+ out << " -anisotropic-diffusion, -ad" << endl;
+ out << " -antialias, -alias" << endl;
+ out << " -as, -set" << endl;
+ out << " -atan2" << endl;
+ out << " -background" << endl;
+ out << " -biascorr" << endl;
+ out << " -binarize" << endl;
+ *** out << " -canny" << endl;
+ out << " -centroid" << endl;
+ out << " -clear" << endl;
+ out << " -clip" << endl;
+ out << " -colormap, -color-map" << endl;
+ out << " -connected-components, -connected, -comp" << endl;
+ out << " -coordinate-map-voxel, -cmv" << endl;
+ out << " -coordinate-map-physical, -cmp" << endl;
+ out << " -copy-transform, -ct" << endl;
+ out << " -cos" << endl;
+ out << " -create" << endl;
+ out << " -dilate" << endl;
+ out << " -divide" << endl;
+ out << " -dup" << endl;
+ out << " -endaccum" << endl;
+ out << " -endfor" << endl;
+ out << " -erode" << endl;
+ out << " -erf" << endl;
+ out << " -exp" << endl;
+ *** out << " -fft" << endl;
+ out << " -flip" << endl;
+ out << " -foreach" << endl;
+ out << " -glm" << endl;
+ *** out << " -hessobj, -hessian-objectness" << endl;
+ *** out << " -histmatch, -histogram-match" << endl;
+ out << " -holefill, -hf" << endl;
+ out << " -info" << endl;
+ out << " -info-full" << endl;
+ out << " -insert, -ins" << endl;
+ out << " -interpolation, -interp, -int" << endl;
+ out << " -iterations" << endl;
+ *** out << " -label-overlap" << std::endl;
+ out << " -label-statistics, -lstat" << endl;
+ out << " -landmarks-to-spheres, -lts" << endl;
+ out << " -laplacian, -laplace" << endl;
+ out << " -levelset" << endl;
+ out << " -levelset-curvature" << endl;
+ out << " -levelset-advection" << endl;
+ out << " -ln, -log" << endl;
+ out << " -log10" << endl;
+ out << " -max, -maximum" << endl;
+ out << " -mcs, -multicomponent-split" << endl;
+ out << " -mean" << endl;
+ out << " -merge" << endl;
+ *** out << " -mf, -mean-filter" << endl;
+ out << " -mi, -mutual-info" << endl;
+ out << " -min, -minimum" << endl;
+ out << " -mixture, -mixture-model" << endl;
+ out << " -multiply, -times" << endl;
+ out << " -n4, -n4-bias-correction" << endl;
+ out << " -ncc, -normalized-cross-correlation" << endl;
+ out << " -nmi, -normalized-mutual-info" << endl;
+ *** out << " -nlw, -normwin, -normalize-local-window" << endl;
+ *** out << " -normpdf" << endl;
+ out << " -noround" << endl;
+ out << " -nospm" << endl;
+ out << " -o" << endl;
+ out << " -omc, -output-multicomponent" << endl;
+ out << " -oo, -output-multiple" << endl;
+ out << " -orient" << endl;
+ out << " -origin" << endl;
+ out << " -origin-voxel" << endl;
+ out << " -overlap" << endl;
+ out << " -overlay-label-image, -oli" << endl;
+ out << " -pad" << endl;
+ out << " -percent-intensity-mode, -pim" << endl;
+ *** out << " -pixel" << endl;
+ out << " -pop" << endl;
+ out << " -popas" << endl;
+ out << " -probe" << endl;
+ out << " -push, -get" << endl;
+ out << " -rank" << endl;
+ out << " -reciprocal" << endl;
+ out << " -region" << endl;
+ out << " -reorder" << endl;
+ out << " -replace" << endl;
+ out << " -resample" << endl;
+ out << " -resample-mm" << endl;
+ out << " -reslice-itk" << endl;
+ out << " -reslice-matrix" << endl;
+ out << " -reslice-identity" << endl;
+ *** out << " -rf-train" << endl;
+ *** out << " -rf-apply" << endl;
+ out << " -rms" << endl;
+ out << " -round" << endl;
+ out << " -scale" << endl;
+ out << " -set-sform" << endl;
+ out << " -shift" << endl;
+ out << " -signed-distance-transform, -sdt" << endl;
+ out << " -sin" << endl;
+ out << " -slice" << endl;
+ out << " -smooth" << endl;
+ out << " -spacing" << endl;
+ out << " -split" << endl;
+ out << " -sqrt" << endl;
+ out << " -staple" << endl;
+ out << " -spm" << endl;
+ out << " -stretch" << endl;
+ *** out << " -subtract" << endl;
+ *** out << " -supervoxel, -sv" << endl;
+ out << " -test-image" << endl;
+ out << " -test-probe" << endl;
+ out << " -threshold, -thresh" << endl;
+ out << " -tile" << endl;
+ out << " -trim" << endl;
+ out << " -trim-to-size" << endl;
+ out << " -type" << endl;
+ out << " -verbose" << endl;
+ *** out << " -version" << endl;
+ out << " -vote" << endl;
+ out << " -vote-label" << endl;
+ out << " -voxel-sum" << endl;
+ out << " -voxel-integral, -voxel-int" << endl;
+ out << " -voxelwise-regression, -voxreg" << endl;
+ out << " -warp" << endl;
+ out << " -wrap" << endl;
+ out << " -weighted-sum, -wsum" << endl;
+ out << " -weighted-sum-voxelwise, -wsv" << endl;
+ */
+
+/**
+ * Parameters for the various algorithms. Stored in a separate structure
+ * in order to reduce number of variables declared in the header
+ */
+template <class TPixel, unsigned int VDim>
+struct ConvertAlgorithmParameters
+{
+ // Root mean square error for anti-aliasing algorithm
+ double m_AntiAliasRMS;
+
+ // Level set algorithm parameters
+ LevelSetParameters m_LevelSet;
+
+ // Random forest parameters
+ RFParameters<TPixel, VDim> m_RandomForest;
+
+ ConvertAlgorithmParameters()
+ {
+ m_AntiAliasRMS = 0.07;
+ }
+};
+
+
+template<class TPixel, unsigned int VDim>
+ImageConverter<TPixel,VDim>
+::ImageConverter()
+ : verbose(&devnull)
+{
+ // Initialize to defaults
+ m_TypeId = "float";
+ m_Background = 0.0;
+ m_RoundFactor = 0.5;
+ m_FlagSPM = false;
+ m_UseCompression = false;
+ m_MultiComponentSplit = false;
+ m_Iterations = 0;
+ m_LoopType = LOOP_NONE;
+ m_PercentIntensityMode = PIM_QUANTILE;
+
+ // Create the parameters
+ m_Param = new ParameterType();
+
+ // Documentation initially NULL to not waste time parsing it
+ m_Documentation = NULL;
+
+ // Create an interpolator
+ m_Interpolation = "linear";
+ CreateInterpolator<TPixel, VDim>(this).CreateLinear();
+}
+
+
+template<class TPixel, unsigned int VDim>
+ImageConverter<TPixel,VDim>
+::~ImageConverter()
+{
+ delete m_Param;
+ if(m_Documentation)
+ delete m_Documentation;
+}
+
+
+
+
+template<class TPixel, unsigned int VDim>
+void
+ImageConverter<TPixel, VDim>
+::PrintCommandListing(std::ostream &out)
+{
+ if(!m_Documentation)
+ m_Documentation = new Documentation(c3d_md);
+
+ // Print the automatically generated command listing
+ m_Documentation->PrintCommandListing(out);
+
+ // Print additional information on getting help
+ out << "Getting help:" << std::endl;
+
+ out << " "
+ << std::setw(32) << std::left
+ << "-h"
+ << ": List commands" << std::endl;
+
+ out << " "
+ << std::setw(32) << std::left
+ << "-h command"
+ << ": Print help on command (e.g. -h add)" << std::endl;
+
+ out << " "
+ << std::setw(32) << std::left
+ << "-manual"
+ << ": Print complete reference manual" << std::endl;
+}
+
+template<class TPixel, unsigned int VDim>
+void
+ImageConverter<TPixel, VDim>
+::PrintCommandHelp(std::ostream &out, const char *command)
+{
+ if(!m_Documentation)
+ m_Documentation = new Documentation(c3d_md);
+
+ if(!m_Documentation->PrintCommandHelp(out, command))
+ {
+ out << "No help available for command " << command << std::endl;
+ }
+}
+
+template<class TPixel, unsigned int VDim>
+void
+ImageConverter<TPixel, VDim>
+::PrintManual(std::ostream &out)
+{
+ if(!m_Documentation)
+ m_Documentation = new Documentation(c3d_md);
+
+ m_Documentation->PrintManual(out);
+}
+
+template<class TPixel, unsigned int VDim>
+int
+ImageConverter<TPixel, VDim>
+::ProcessCommand(int argc, char *argv[])
+{
+ // Get the first command
+ string cmd = argv[0];
+
+ // cout << "COMMAND: " << cmd << endl;
+
+ // Commands in alphabetical order
+ if (cmd == "-accum")
+ {
+ if (this->m_LoopType != LOOP_NONE)
+ throw ConvertException("Nested -foreach and -accum loops are not allowed");
+
+ this->m_LoopType = LOOP_ACCUM;
+ return this->AccumulateLoop(argc, argv);
+ }
+
+ else if (cmd == "-add")
+ {
+ BinaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(BinaryMathOperation<TPixel, VDim>::ADD);
+ return 0;
+ }
+
+ else if (cmd == "-align-landmarks" || cmd == "-alm")
+ {
+ int dof = atoi(argv[1]);
+ std::string fnout = argv[2];
+ AlignByLandmarks<TPixel,VDim> adapter(this);
+ adapter(dof, fnout);
+ return 2;
+ }
+
+ else if (cmd == "-anisotropic-diffusion" || cmd == "-ad")
+ {
+ double cond = atof(argv[1]);
+ int niter = atoi(argv[2]);
+ PeronaMalik<TPixel, VDim> adapter(this);
+ adapter(cond, (size_t) niter);
+ return 2;
+ }
+
+ // Anti-alias a binary image, turning it into a smoother floating point image;
+ // the argument is the iso-surface value
+ // This command is affected by -iterations and -rms flags
+ else if (cmd == "-antialias" || cmd == "-alias")
+ {
+ AntiAliasImage<TPixel, VDim> adapter(this);
+ adapter(atof(argv[1]), m_Param->m_AntiAliasRMS);
+ return 1;
+ }
+
+ // Associate variable name with image currently at the top of
+ // the stack
+ else if (cmd == "-as" || cmd == "-set")
+ {
+ string var(argv[1]);
+ if (m_ImageStack.size() == 0)
+ throw ConvertException("No image to assign to variable %s", var.c_str());
+ m_ImageVars[var] = m_ImageStack.back();
+ return 1;
+ }
+
+ else if (cmd == "-atan2")
+ {
+ BinaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(BinaryMathOperation<TPixel, VDim>::ATAN2);
+ return 0;
+ }
+
+ else if (cmd == "-background")
+ {
+ m_Background = atof(argv[1]);
+ *verbose << "Background value set to " << m_Background << endl;
+ return 1;
+ }
+
+ else if (cmd == "-biascorr" || cmd == "-n4" || cmd == "-n4-bias-correction" )
+ {
+ BiasFieldCorrectionN4<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ // f(x) = (x == xBackground) ? 0 : 1
+ else if (cmd == "-binarize")
+ {
+ ThresholdImage<TPixel, VDim> adapter(this);
+ adapter(m_Background, m_Background, 0.0, 1.0);
+ return 0;
+ }
+
+ else if (cmd == "-canny")
+ {
+ Canny<TPixel, VDim> adapter(this);
+ RealVector sigma = ReadRealSize(argv[1]);
+ double tLower = ReadIntensityValue(argv[1]);
+ double tUpper = ReadIntensityValue(argv[3]);
+
+ adapter(sigma, tLower, tUpper);
+ return 3;
+ }
+
+
+ else if (cmd == "-centroid")
+ {
+ BinaryImageCentroid<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-connected-components" || cmd == "-connected" || cmd == "-comp")
+ {
+ ConnectedComponents<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-clear")
+ {
+ *verbose << "Clearing the stack" << endl;
+ m_ImageStack.clear();
+ return 0;
+ }
+
+ else if (cmd == "-clip")
+ {
+ double iMin = ReadIntensityValue(argv[1]);
+ double iMax = ReadIntensityValue(argv[2]);
+ ClipImageIntensity<TPixel, VDim>(this)(iMin, iMax);
+ return 2;
+ }
+
+ else if (cmd == "-colormap" || cmd == "-color-map")
+ {
+ std::string cmname = argv[1];
+ ScalarToRGB<TPixel, VDim>(this)(cmname);
+ return 1;
+ }
+
+ else if (cmd == "-compress")
+ {
+ m_UseCompression = true;
+ return 0;
+ }
+
+ else if (cmd == "-no-compress")
+ {
+ m_UseCompression = false;
+ return 0;
+ }
+
+ else if (cmd == "-coordinate-map-voxel" || cmd == "-cmv")
+ {
+ CoordinateMap<TPixel,VDim>(this)(false);
+ return 0;
+ }
+
+ else if (cmd == "-coordinate-map-physical" || cmd == "-cmp")
+ {
+ CoordinateMap<TPixel,VDim>(this)(true);
+ return 0;
+ }
+
+ else if (cmd == "-copy-transform" || cmd == "-ct")
+ {
+ CopyTransform<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-cos")
+ {
+ UnaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(&vcl_cos);
+ return 0;
+ }
+
+ // Create a new image with given size and voxel size
+ else if (cmd == "-create")
+ {
+ SizeType dims = ReadSizeVector(argv[1]);
+ RealVector voxel = ReadRealSize(argv[2]);
+ CreateImage<TPixel, VDim> adapter(this);
+ adapter(dims, voxel);
+ return 2;
+ }
+
+ else if (cmd == "-dicom-series-list")
+ {
+ DicomSeriesList<TPixel, VDim> adapter(this);
+ adapter(argv[1]);
+ return 1;
+ }
+
+ else if (cmd == "-dicom-series-read")
+ {
+ typedef ReadImage<TPixel, VDim> Adapter;
+ typename Adapter::ImageInfo info;
+
+ info.dicom_series_id = argv[2];
+
+ Adapter adapter(this);
+ adapter(argv[1], info);
+
+ return 2;
+ }
+
+ else if (cmd == "-dilate")
+ {
+ MathematicalMorphology<TPixel,VDim> adapter(this);
+ adapter(false, atof(argv[1]), ReadSizeVector(argv[2]));
+ return 2;
+ }
+
+ else if (cmd == "-divide")
+ {
+ BinaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(BinaryMathOperation<TPixel, VDim>::DIVIDE);
+ return 0;
+ }
+
+ else if (cmd == "-dup" || cmd == "-duplicate")
+ {
+ m_ImageStack.push_back(m_ImageStack.back());
+ return 0;
+ }
+
+ else if (cmd == "-endaccum")
+ {
+ // This command ends the accum loop
+ if (this->m_LoopType != LOOP_ACCUM)
+ throw ConvertException("Out of place -endaccum command");
+ this->m_LoopType = LOOP_NONE;
+ return 0;
+ }
+
+ else if (cmd == "-endfor")
+ {
+ // This command ends the for loop
+ if (this->m_LoopType != LOOP_FOREACH)
+ throw ConvertException("Out of place -endfor command");
+ this->m_LoopType = LOOP_NONE;
+ return 0;
+ }
+
+ else if (cmd == "-erode")
+ {
+ MathematicalMorphology<TPixel,VDim> adapter(this);
+ adapter(true, atof(argv[1]), ReadSizeVector(argv[2]));
+ return 2;
+ }
+
+ else if (cmd == "-erf")
+ {
+ double thresh = atof(argv[1]);
+ double scale = atof(argv[2]);
+ ImageERF<TPixel, VDim> adapter(this);
+ adapter(thresh, scale);
+ return 2;
+ }
+
+ else if (cmd == "-exp")
+ {
+ UnaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(&vcl_exp);
+ return 0;
+ }
+
+ else if (cmd == "-fft")
+ {
+ ComputeFFT<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-flip")
+ {
+ string flipax = argv[1];
+ FlipImage<TPixel, VDim> adapter(this);
+ adapter(flipax);
+ return 1;
+ }
+
+ else if (cmd == "-foreach")
+ {
+ if (this->m_LoopType != LOOP_NONE)
+ throw ConvertException("Nested loops are not allowed");
+ this->m_LoopType = LOOP_FOREACH;
+ return this->ForEachLoop(argc, argv);
+ }
+
+ else if (cmd == "-glm")
+ {
+ string mat(argv[1]);
+ string con(argv[2]);
+ GeneralLinearModel<TPixel, VDim> adapter(this);
+ adapter(mat, con);
+ return 2;
+ }
+
+ else if (cmd == "-grad" || cmd == "-gradient")
+ {
+ ImageGradient<TPixel,VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-h" || cmd == "-help" || cmd == "--help")
+ {
+ if(argc > 1 && argv[1][0] != '-')
+ {
+ PrintCommandHelp(std::cout, argv[1]);
+ return 1;
+ }
+ else
+ {
+ PrintCommandListing(std::cout);
+ return 0;
+ }
+ }
+
+ else if(cmd == "-hf" || cmd == "-holefill")
+ {
+ double foreground = atof(argv[1]);
+ bool full_conn = atoi(argv[2]);
+
+ BinaryHoleFill<TPixel, VDim> adapter(this);
+ adapter(foreground, full_conn);
+
+ return 2;
+ }
+
+ else if (cmd == "-hessobj" || cmd == "-hessian-objectness")
+ {
+ int dimension = atoi(argv[1]);
+ double minscale = atof(argv[2]);
+ double maxscale = atof(argv[3]);
+
+ HessianObjectness<TPixel, VDim> adapter(this);
+ adapter(dimension, minscale, maxscale);
+
+ return 3;
+ }
+
+ else if (cmd == "-histmatch" || cmd == "-histogram-match")
+ {
+ size_t nmatch = atoi(argv[1]);
+ HistogramMatch<TPixel, VDim> adapter(this);
+ adapter(nmatch);
+ return 1;
+ }
+
+ else if (cmd == "-info")
+ {
+ PrintImageInfo<TPixel, VDim> adapter(this);
+ adapter(false);
+ return 0;
+ }
+
+ else if (cmd == "-info-full")
+ {
+ PrintImageInfo<TPixel, VDim> adapter(this);
+ adapter(true);
+ return 0;
+ }
+
+ else if (cmd == "-insert" || cmd == "-ins")
+ {
+ string var(argv[1]);
+ size_t pos = (size_t) atoi(argv[2]);
+ typename ImageVariableMap::iterator img = m_ImageVars.find(var);
+
+ // Check if the variable exists
+ if (img == m_ImageVars.end())
+ throw ConvertException("No image assigned to variable %s", var.c_str());
+
+ // Check if the position is ok
+ if (m_ImageStack.size() > pos)
+ throw ConvertException("Can not insert at position %i in stack of size %i", pos, m_ImageStack.size());
+
+ // Insert at the appropriate place
+ typename vector<ImagePointer>::iterator it = m_ImageStack.end();
+ for(size_t i = 0; i < pos; i++) --it;
+ m_ImageStack.insert(it, img->second);
+
+ return 2;
+ }
+
+ else if (cmd == "-interpolation" || cmd == "-interp" || cmd == "-int")
+ {
+ // Adapter that creates interpolators
+ CreateInterpolator<TPixel, VDim> adapter(this);
+
+ // Interpret the interpolation type
+ m_Interpolation = str_to_lower(argv[1]);
+
+ if (m_Interpolation == "nearestneighbor" || m_Interpolation == "nn" || m_Interpolation == "0")
+ {
+ adapter.CreateNN();
+ return 1;
+ }
+ else if (m_Interpolation == "linear" || m_Interpolation == "1")
+ {
+ adapter.CreateLinear();
+ return 1;
+ }
+ else if (m_Interpolation == "cubic" || m_Interpolation == "3")
+ {
+ adapter.CreateCubic();
+ return 1;
+ }
+ else if (m_Interpolation == "sinc")
+ {
+ adapter.CreateSinc();
+ return 1;
+ }
+ else if (m_Interpolation == "gaussian")
+ {
+ RealVector sigma = ReadRealSize(argv[2]);
+ adapter.CreateGaussian(sigma);
+ return 2;
+ }
+ else if (m_Interpolation == "multilabel" || m_Interpolation == "ml")
+ {
+ RealVector sigma = ReadRealSize(argv[2]);
+ adapter.CreateMultiLabel(sigma);
+ return 2;
+ }
+ else
+ {
+ throw ConvertException("Unknown interpolation type: %s", m_Interpolation.c_str());
+ }
+ }
+
+ else if (cmd == "-iterations")
+ {
+ m_Iterations = static_cast<size_t>(atoi(argv[1]));
+ return 1;
+ }
+
+ else if (cmd == "-label-overlap")
+ {
+ LabelOverlapMeasures<TPixel, VDim>(this)();
+ return 0;
+ }
+
+ else if (cmd == "-label-statistics" || cmd == "-lstat")
+ {
+ LabelStatistics<TPixel, VDim>(this)();
+ return 0;
+ }
+
+ else if (cmd == "-landmarks-to-spheres" || cmd == "-lts")
+ {
+ char *fnland = argv[1];
+ double radius = atof(argv[2]);
+ LandmarksToSpheres<TPixel, VDim> (this)(fnland, radius);
+ return 2;
+ }
+
+ else if (cmd == "-laplacian" || cmd == "-laplace")
+ {
+ ImageLaplacian<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-levelset")
+ {
+ int nIter = atoi(argv[1]);
+ LevelSetSegmentation<TPixel, VDim> adapter(this);
+ adapter(nIter, m_Param->m_LevelSet);
+ return 1;
+ }
+
+ else if (cmd == "-levelset-curvature")
+ {
+ m_Param->m_LevelSet.CurvatureWeight = atof(argv[1]);
+ return 1;
+ }
+
+ else if (cmd == "-levelset-advection")
+ {
+ m_Param->m_LevelSet.AdvectionWeight = atof(argv[1]);
+ return 1;
+ }
+
+ else if (cmd == "-ln" || cmd == "-log")
+ {
+ UnaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(&vcl_log);
+ return 0;
+ }
+
+ else if (cmd == "-log10")
+ {
+ UnaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(&vcl_log10);
+ return 0;
+ }
+
+ else if (cmd == "-manual")
+ {
+ this->PrintManual(std::cout);
+ return 0;
+ }
+
+ else if (cmd == "-maximum" || cmd == "-max")
+ {
+ BinaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(BinaryMathOperation<TPixel, VDim>::MAXIMUM);
+ return 0;
+ }
+
+ else if (cmd == "-mcs" || cmd == "-multicomponent-split")
+ {
+ m_MultiComponentSplit = true; return 0;
+ }
+
+ else if (cmd == "-mean")
+ {
+ size_t n = m_ImageStack.size();
+ for(size_t i = 1; i < n; i++)
+ {
+ AddImages<TPixel,VDim> adapter(this);
+ adapter();
+ }
+ ScaleShiftImage<TPixel, VDim> scaler(this);
+ scaler(1.0 / n, 0.0);
+ return 0;
+ }
+
+ else if(cmd == "-median" || cmd == "-median-filter")
+ {
+ MedianFilter<TPixel, VDim> adapter(this);
+ SizeType radius = this->ReadSizeVector(argv[1]);
+ adapter(radius);
+ return 1;
+ }
+
+ else if (cmd == "-merge")
+ {
+ Vote<TPixel, VDim> adapter(this);
+ adapter(true);
+ return 0;
+ }
+
+ else if (cmd == "-mf" || cmd == "-mean-filter")
+ {
+ MeanFilter<TPixel, VDim> adapter(this);
+ SizeType sz = ReadSizeVector(argv[1]);
+ adapter(sz);
+ return 1;
+ }
+
+ else if (cmd == "-mi" || cmd == "-mutual-info")
+ {
+ ApplyMetric<TPixel, VDim> adapter(this);
+ int nret = 0;
+ string fnf("none");
+ string fnm("none");
+ if (argc > 1)
+ {
+ fnm = argv[1];
+ nret = 1;
+ }
+ if (argc == 3)
+ {
+ fnf = argv[2];
+ nret = 2;
+ }
+ adapter("MI", fnf.c_str(), fnm.c_str());
+ return nret;
+ }
+
+ else if (cmd == "-minimum" || cmd == "-min")
+ {
+ BinaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(BinaryMathOperation<TPixel, VDim>::MINIMUM);
+ return 0;
+ }
+
+ else if (cmd == "-mixture" || cmd == "-mixture-model")
+ {
+ int ncomp = atoi(argv[1]);
+ if (ncomp == 0)
+ throw ConvertException("Incorrect specification of mixture model initialization");
+
+ std::vector<double> mu, sigma;
+ for(int i = 0; i < ncomp; i++)
+ {
+ mu.push_back(ReadIntensityValue(argv[2 + 2 * i]));
+ sigma.push_back(ReadIntensityValue(argv[3 + 2 * i]));
+ }
+
+ MixtureModel<TPixel, VDim> adapter(this);
+ adapter(mu, sigma);
+
+ return 1 + 2 * ncomp;
+ }
+
+ else if (cmd == "-mmi" || cmd == "-mattes-mutual-info")
+ {
+ ApplyMetric<TPixel, VDim> adapter(this);
+ int nret = 0;
+ string fnf("none");
+ string fnm("none");
+ if (argc > 1)
+ {
+ fnm = argv[1];
+ nret = 1;
+ }
+ if (argc == 3)
+ {
+ fnf = argv[2];
+ nret = 2;
+ }
+ adapter("MMI", fnf.c_str(), fnm.c_str());
+ return nret;
+ }
+ else if (cmd == "-msq" || cmd == "-mean-square")
+ {
+ ApplyMetric<TPixel, VDim> adapter(this);
+ int nret = 0;
+ string fnf("none");
+ string fnm("none");
+ if (argc > 1)
+ {
+ fnm = argv[1];
+ nret = 1;
+ }
+ if (argc == 3)
+ {
+ fnf = argv[2];
+ nret = 2;
+ }
+ adapter("MSQ", fnf.c_str(), fnm.c_str());
+ return nret;
+ }
+ else if (cmd == "-multiply" || cmd == "-times")
+ {
+ BinaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(BinaryMathOperation<TPixel, VDim>::MULTIPLY);
+ return 0;
+ }
+
+ else if (cmd == "-ncc" || cmd == "-normalized-cross-correlation")
+ {
+ SizeType sz = ReadSizeVector(argv[1]);
+ NormalizedCrossCorrelation<TPixel,VDim> adapter(this);
+ adapter(sz);
+ return 1;
+ }
+
+ else if (cmd == "-ncor" || cmd == "-normalized-correlation")
+ {
+ ApplyMetric<TPixel, VDim> adapter(this);
+ int nret = 0;
+ string fnf("none");
+ string fnm("none");
+ if (argc > 1)
+ {
+ fnm = argv[1];
+ nret = 1;
+ }
+ if (argc == 3)
+ {
+ fnf = argv[2];
+ nret = 2;
+ }
+ adapter("NCOR", fnf.c_str(), fnm.c_str());
+ return nret;
+ }
+ else if (cmd == "-nmi" || cmd == "-normalized-mutual-info")
+ {
+ ApplyMetric<TPixel, VDim> adapter(this);
+ int nret = 0;
+ string fnf("none");
+ string fnm("none");
+ if (argc > 1)
+ {
+ fnm = argv[1];
+ nret = 1;
+ }
+ if (argc == 3)
+ {
+ fnf = argv[2];
+ nret = 2;
+ }
+ adapter("NMI", fnf.c_str(), fnm.c_str());
+ return nret;
+ }
+
+ else if (cmd == "-nomcs" || cmd == "-no-multicomponent-split")
+ {
+ m_MultiComponentSplit = false; return 0;
+ }
+
+ else if (cmd == "-nlw" || cmd == "-normwin" || cmd == "-normalize-local-window")
+ {
+ NormalizeLocalWindow<TPixel, VDim> adapter(this);
+ SizeType radius = ReadSizeVector(argv[1]);
+ adapter(radius);
+ return 1;
+ }
+
+ else if (cmd == "-normpdf")
+ {
+ // Compute normal PDF of intensity values given sigma and mu
+ double mu = atof(argv[1]);
+ double s = atof(argv[2]);
+
+ // Subtract mu
+ ScaleShiftImage<TPixel, VDim> scale1(this);
+ scale1(1.0, -mu);
+
+ // Square
+ m_ImageStack.push_back(m_ImageStack.back());
+ MultiplyImages<TPixel, VDim> times(this);
+ times();
+
+ // Scale by -1/2s
+ ScaleShiftImage<TPixel, VDim> scale2(this);
+ scale2(-0.5 / s, 0.0);
+
+ // Exponentiate
+ UnaryMathOperation<TPixel, VDim> exp1(this);
+ exp1(&vcl_exp);
+
+ // Scale by factor
+ ScaleShiftImage<TPixel, VDim> scale3(this);
+ scale3(1.0 / sqrt(2 * vnl_math::pi * s * s), 0.0);
+ return 2;
+ }
+
+ else if (cmd == "-noround")
+ { m_RoundFactor = 0.0; return 0; }
+
+ // Enable SPM extensions
+ else if (cmd == "-nospm")
+ { m_FlagSPM = false; return 0; }
+
+ // Overwrite / Output command - save the image without checking if
+ // it already exists.
+ else if (cmd == "-o" || cmd == "-output")
+ {
+ WriteImage<TPixel, VDim> adapter(this);
+ adapter(argv[1], true);
+ return 1;
+ }
+
+ else if (cmd == "-omc" || cmd == "-output-multicomponent")
+ {
+ // Number of components (all by default)
+ int nc, np;
+
+ // A parameter can be optionally specified (how many components)
+ RegularExpression re("^[0-9]+$");
+ if (re.find(argv[1]))
+ { nc = atoi(argv[1]); np=2; }
+ else
+ { nc = m_ImageStack.size(); np = 1; }
+
+ // Create a writer
+ WriteImage<TPixel, VDim> adapter(this);
+ adapter.WriteMultiComponent(argv[np], nc);
+ return np;
+ }
+
+ else if (cmd == "-orient")
+ {
+ SetOrientation<TPixel,VDim> adapter(this);
+ adapter(argv[1]);
+ return 1;
+ }
+
+ // Write mulptiple images
+ else if (cmd == "-oo" || cmd == "-output-multiple")
+ {
+ // Check if the argument is a printf pattern
+ char buffer[1024];
+ sprintf(buffer, argv[1],0);
+ if (strcmp(buffer, argv[1]))
+ {
+ // A pattern is specified. For each image on the stack, use pattern
+ for(size_t i = 0; i < m_ImageStack.size(); i++)
+ {
+ sprintf(buffer, argv[1], i);
+ WriteImage<TPixel, VDim> adapter(this);
+ adapter(buffer, true, i);
+ }
+ return 1;
+ }
+ else
+ {
+ // Filenames are specified. Find out how many there are
+ size_t nfiles = 0;
+ for(int i = 1; i < argc; i++)
+ {
+ if (argv[i][0] != '-') nfiles++; else break;
+ }
+
+
+ if (nfiles == 0)
+ throw ConvertException("No files specified to -oo command");
+
+ if (nfiles > m_ImageStack.size())
+ throw ConvertException("Too many files specified to -oo command");
+
+ for(size_t j = 0; j < nfiles; j++)
+ {
+ WriteImage<TPixel, VDim> adapter(this);
+ adapter(argv[j+1], true, m_ImageStack.size() - nfiles + j);
+ }
+
+ return nfiles;
+ }
+ }
+
+ else if (cmd == "-orient")
+ {
+ // Read an RAS code
+ RegularExpression re("[raslpi]{3}");
+ if (re.find(str_to_lower(argv[1])))
+ { cout << "You supplied a RAS code" << endl; }
+ else
+ { cout << "I am expecting a matrix" << endl; }
+ return 1;
+ }
+
+
+ else if (cmd == "-origin")
+ {
+ RealVector org = ReadRealVector(argv[1], true);
+ m_ImageStack.back()->SetOrigin(org.data_block());
+ return 1;
+ }
+
+ else if (cmd == "-origin-voxel")
+ {
+ RealVector vec = ReadRealVector(argv[1], false);
+ cout << "VOX : " << vec << endl;
+
+ // Get physical coordinate of this voxel
+ vnl_matrix_fixed<double, VDim+1, VDim+1> mat =
+ m_ImageStack.back()->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix();
+ RealVector org = -vec;
+ m_ImageStack.back()->SetOrigin(org.data_block());
+ cout << "ORG : " << org << endl;
+ return 1;
+ }
+
+ else if (cmd == "-overlap")
+ {
+ double label = atof(argv[1]);
+ ComputeOverlaps<TPixel, VDim> adapter(this);
+ adapter(label);
+ return 1;
+ }
+
+ else if (cmd == "-overlay-label-image" || cmd == "-oli")
+ {
+ double opacity = atof(argv[2]);
+ OverlayLabelImage<TPixel, VDim> adapter(this);
+ adapter(argv[1], opacity);
+ return 2;
+ }
+
+ else if (cmd == "-pad")
+ {
+ // specify two sizes that give the padding in x,y,z
+ // pad is the offset (in voxels) from the edge of the image to the
+ // padExtent. Example: -pad 1x1x1vox 0x0x0vox pads on the left, posterior, inferior sides
+ // by one voxel -pad 10x10x10% 10x10x10% adds 10% on all sides
+ IndexType padExtentLower, padExtentUpper;
+
+ padExtentLower = ReadIndexVector(argv[1]);
+ padExtentUpper = ReadIndexVector(argv[2]);
+
+ float padValue = atof(argv[3]);
+
+ *verbose << "Padding image #" << m_ImageStack.size() << endl;
+
+ PadImage<TPixel, VDim> adapter(this);
+ adapter(padExtentLower, padExtentUpper, padValue);
+ return 3;
+ }
+
+ else if (cmd == "-pca")
+ {
+ ComputeMoments<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-percent-intensity-mode" || cmd == "-pim")
+ {
+ // What does % mean when specifying intensities
+ string pim = str_to_lower(argv[1]);
+ if (pim == "quantile" || pim == "q")
+ m_PercentIntensityMode = PIM_QUANTILE;
+ else if (pim == "foregroundquantile" || pim == "fq")
+ m_PercentIntensityMode = PIM_FGQUANTILE;
+ else if (pim == "range" || pim == "r")
+ m_PercentIntensityMode = PIM_RANGE;
+ else
+ throw ConvertException("Wrong -percent-intensity-mode spec %s. See help.", pim.c_str());
+ return 1;
+ }
+
+ else if (cmd == "-pixel")
+ {
+ // Get a pixel value - no interpolation
+ typename RegionType::IndexType idx = ReadIndexVector(argv[1]);
+ try
+ {
+ double pix = m_ImageStack.back()->GetPixel(idx);
+ cout << "Pixel " << idx << " has value " << pix << endl;
+ }
+ catch(...)
+ {
+ cerr << "Error: pixel " << idx << " can not be examined!" << endl;
+ }
+ return 1;
+ }
+
+ else if (cmd == "-pop")
+ {
+ *verbose << "Removing (popping) the last image from the stack" << endl;
+ m_ImageStack.pop_back();
+ return 0;
+ }
+
+ else if (cmd == "-popas")
+ {
+ string var(argv[1]);
+ if (m_ImageStack.size() == 0)
+ throw ConvertException("No image to assign to variable %s", var.c_str());
+ m_ImageVars[var] = m_ImageStack.back();
+ m_ImageStack.pop_back();
+ return 1;
+ }
+
+ else if (cmd == "-probe")
+ {
+ // Get the probe point
+ RealVector x = ReadRealVector(argv[1], true);
+ SampleImage<TPixel, VDim> adapter(this);
+ adapter(x);
+ return 1;
+ }
+
+ else if (cmd == "-push" || cmd == "-get")
+ {
+ string var(argv[1]);
+ typename ImageVariableMap::iterator img = m_ImageVars.find(var);
+ if (img == m_ImageVars.end())
+ throw ConvertException("No image assigned to variable %s", var.c_str());
+ m_ImageStack.push_back(img->second);
+ return 1;
+ }
+
+ else if (cmd == "-rank")
+ {
+ Rank<TPixel,VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-reciprocal")
+ {
+ // Multiply by the reciprocal (for time being at least)
+ ReciprocalImage<TPixel, VDim>(this)();
+
+ return 0;
+ }
+
+ else if (cmd == "-region")
+ {
+ // Get the position and index for the region
+ RegionType bbox;
+ bbox.SetIndex(ReadIndexVector(argv[1]));
+ bbox.SetSize(ReadSizeVector(argv[2]));
+
+ *verbose << "Extracting Subregion in #" << m_ImageStack.size() << endl;
+ ExtractRegion<TPixel, VDim> adapter(this);
+ adapter(bbox);
+ return 2;
+ }
+
+
+ else if (cmd == "-reorder")
+ {
+ // Get the parameter, treat it as a float
+ size_t k = 0;
+ double k_frac = atof(argv[1]);
+ if (k_frac > 0 && k_frac < 1)
+ k = (size_t) (0.5 + k_frac * m_ImageStack.size());
+ else if (k_frac >= 1)
+ k = (size_t) atoi(argv[1]);
+ else
+ throw ConvertException("Parameter %s to the '-reorder' command is invalid", argv[1]);
+
+ ReorderStack<TPixel, VDim> adapter(this);
+ adapter(k);
+
+ return 1;
+
+ }
+
+ else if (cmd == "-rf-apply")
+ {
+ // Get the filename for the training output
+ std::string rf_file = argv[1];
+
+ // Get the current parameters
+ RFApply<TPixel, VDim> adapter(this);
+ adapter(rf_file.c_str());
+
+ return 1;
+ }
+
+ else if (cmd == "-rf-train")
+ {
+ // Get the filename for the training output
+ std::string rf_file = argv[1];
+
+ // Get the current parameters
+ RFTrain<TPixel, VDim> adapter(this);
+ adapter(rf_file.c_str(), m_Param->m_RandomForest);
+
+ return 1;
+ }
+
+ else if (cmd == "-rf-param-patch")
+ {
+ SizeType patch_radius = ReadSizeVector(argv[1]);
+ m_Param->m_RandomForest.patch_radius = patch_radius;
+ return 1;
+ }
+
+ else if (cmd == "-rf-param-usexyz")
+ {
+ m_Param->m_RandomForest.use_coordinate_features = true;
+ return 0;
+ }
+
+ else if (cmd == "-rf-param-nousexyz")
+ {
+ m_Param->m_RandomForest.use_coordinate_features = false;
+ return 0;
+ }
+
+ else if (cmd == "-rf-param-ntrees")
+ {
+ m_Param->m_RandomForest.forest_size = atoi(argv[1]);
+ return 1;
+ }
+
+ else if (cmd == "-rf-param-treedepth")
+ {
+ m_Param->m_RandomForest.tree_depth = atoi(argv[1]);
+ return 1;
+ }
+
+ else if (cmd == "-set-sform")
+ {
+ string fn_tran( argv[1] );
+
+ *verbose << "Setting sform of image #" << m_ImageStack.size() << endl;
+ SetSform<TPixel, VDim> adapter(this);
+ adapter( fn_tran );
+
+ return 1;
+ }
+
+ else if (cmd == "-replace")
+ {
+ vector<double> vReplace;
+
+ // Read a list of numbers from the command line
+ for(int i = 1; i < argc; i++)
+ {
+ try
+ { vReplace.push_back(myatof(argv[i])); }
+ catch(...)
+ { break; }
+ }
+
+ // Make sure the number of replacement rules is even
+ if (vReplace.size() % 2 == 1)
+ {
+ cerr << "The number of parameters to '-replace' must be even!" << endl;
+ throw -1;
+ }
+
+ // Replace the intensities with values supplie
+ ReplaceIntensities<TPixel, VDim> adapter(this);
+ adapter(vReplace);
+
+ return vReplace.size();
+ }
+
+ // Resample command. Retain the bounding box of the image
+ // while changing voxel size
+ else if (cmd == "-resample")
+ {
+ SizeType sz = ReadSizeVector(argv[1]);
+ ResampleImage<TPixel, VDim> adapter(this);
+ adapter(sz);
+ return 1;
+ }
+
+ else if (cmd == "-resample-mm")
+ {
+ RealVector vox = ReadRealSize(argv[1]);
+ SizeType sz = m_ImageStack.back()->GetBufferedRegion().GetSize();
+ for(size_t i = 0; i < VDim; i++)
+ {
+ sz[i] = static_cast<size_t>((0.5 + sz[i] * m_ImageStack.back()->GetSpacing()[i]) / vox[i]);
+ }
+ ResampleImage<TPixel, VDim> adapter(this);
+ adapter(sz);
+ return 1;
+ }
+
+ else if (cmd == "-reslice-itk")
+ {
+ string fn_tran(argv[1]);
+ ResliceImage<TPixel, VDim> adapter(this);
+ adapter("itk", fn_tran);
+ return 1;
+ }
+
+ else if (cmd == "-reslice-matrix")
+ {
+ string fn_tran(argv[1]);
+ ResliceImage<TPixel, VDim> adapter(this);
+ adapter("matrix", fn_tran);
+ return 1;
+ }
+
+ else if (cmd == "-reslice-identity")
+ {
+ ResliceImage<TPixel, VDim> adapter(this);
+ adapter("identity", "");
+ return 0;
+ }
+
+ else if (cmd == "-rgb2hsv")
+ {
+ VoxelwiseComponentFunction<TPixel, VDim> adapter(this);
+ adapter("rgb2hsv");
+ return 0;
+ }
+
+ else if (cmd == "-rms")
+ { m_Param->m_AntiAliasRMS = atof(argv[1]); return 1; }
+
+ else if (cmd == "-round")
+ { m_RoundFactor = 0.5; return 0; }
+
+ // No else if here because of a windows compiler error (blocks nested too deeply)
+ if (cmd == "-scale")
+ {
+ double factor = atof(argv[1]);
+ ScaleShiftImage<TPixel, VDim> adapter(this);
+ adapter(factor, 0.0);
+ return 1;
+ }
+
+
+ else if (cmd == "-set-sform")
+ {
+ string fn_tran( argv[1] );
+
+ *verbose << "Setting sform of image #" << m_ImageStack.size() << endl;
+ SetSform<TPixel, VDim> adapter(this);
+ adapter( fn_tran );
+
+ return 1;
+ }
+
+ else if (cmd == "-sin")
+ {
+ UnaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(&vcl_sin);
+ return 0;
+ }
+
+ else if (cmd == "-slice")
+ {
+ string axis( argv[1] );
+ char * pos = argv[2];
+
+ ExtractSlice<TPixel, VDim> adapter(this);
+ adapter(axis, pos);
+
+ return 2;
+ }
+
+ else if (cmd == "-sharpen")
+ {
+ LaplacianSharpening<TPixel,VDim> adapter(this);
+ adapter();
+
+ return 0;
+ }
+
+ else if (cmd == "-shift")
+ {
+ double x = atof(argv[1]);
+ ScaleShiftImage<TPixel, VDim> adapter(this);
+ adapter(1.0, x);
+ return 1;
+ }
+
+ else if (cmd == "-signed-distance-transform" || cmd == "-sdt")
+ {
+ SignedDistanceTransform<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ // Gaussian smoothing command
+ else if (cmd == "-smooth")
+ {
+ RealVector stdev = ReadRealSize(argv[1]);
+ SmoothImage<TPixel, VDim> adapter(this);
+ adapter(stdev, false);
+ return 1;
+ }
+
+ else if (cmd == "-smooth-fast")
+ {
+ RealVector stdev = ReadRealSize(argv[1]);
+ SmoothImage<TPixel, VDim> adapter(this);
+ adapter(stdev, true);
+ return 1;
+ }
+
+ else if (cmd == "-spacing")
+ {
+ RealVector org = ReadRealSize(argv[1]);
+ m_ImageStack.back()->SetSpacing(org.data_block());
+ return 1;
+ }
+
+ else if (cmd == "-split")
+ {
+ SplitMultilabelImage<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ else if (cmd == "-sqrt")
+ {
+ UnaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(&vcl_sqrt);
+ return 0;
+ }
+
+ else if (cmd == "-staple")
+ {
+ // Perform the STAPLE algorithm on the data
+ double ival = atof(argv[1]);
+ StapleAlgorithm<TPixel, VDim> adapter(this);
+ adapter(ival);
+ return 1;
+ }
+
+ // Enable SPM extensions
+ else if (cmd == "-spm")
+ { m_FlagSPM = true; return 0; }
+
+ else if (cmd == "-subtract")
+ {
+ BinaryMathOperation<TPixel, VDim> adapter(this);
+ adapter(BinaryMathOperation<TPixel, VDim>::SUBTRACT);
+ return 0;
+ }
+
+ else if (cmd == "-supervoxel" || cmd == "-sv")
+ {
+ SLICSuperVoxel<TPixel,VDim> adapter(this);
+ int samples = atoi(argv[1]);
+ double m = atof(argv[2]);
+ adapter(samples, m);
+ return 2;
+ }
+
+ // Stretch the intensity range
+ else if (cmd == "-stretch")
+ {
+ double u1 = ReadIntensityValue(argv[1]);
+ double u2 = ReadIntensityValue(argv[2]);
+ double v1 = ReadIntensityValue(argv[3]);
+ double v2 = ReadIntensityValue(argv[4]);
+ double a = (v2 - v1) / (u2 - u1);
+ double b = v1 - u1 * a;
+ ScaleShiftImage<TPixel, VDim> adapter(this);
+ adapter(a, b);
+ return 4;
+ }
+
+
+ // Test image equality
+ else if (cmd == "-test-image")
+ {
+ // Check if the next argument is a tolerance value
+ double tol = 1e-8;
+ int np = 0;
+ RegularExpression re("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)$");
+ if (argc > 1 && re.find(argv[1]))
+ { tol = atoi(argv[1]); np++; }
+
+ TestImage<TPixel, VDim> adapter(this);
+ adapter(true, true, tol);
+ return np;
+ }
+
+ else if (cmd == "-test-probe")
+ {
+ // Just like the probe command
+ RealVector x = ReadRealVector(argv[1], true);
+ double v_test = atof(argv[2]);
+ double tol = 1e-8;
+ int np = 2;
+
+ // Read the optional tolerance value
+ RegularExpression re("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)$");
+ if (argc > 1 && re.find(argv[1]))
+ { tol = atoi(argv[1]); np++; }
+
+ // Probe
+ SampleImage<TPixel, VDim> adapter(this);
+ adapter(x);
+
+ // Compare and exit
+ double abs_diff = std::fabs(v_test - adapter.GetResult());
+ if(abs_diff > tol)
+ {
+ cout << "Probe test failed, absolute difference = " << abs_diff << endl;
+ exit(1);
+ }
+ else
+ exit(0);
+ }
+
+ // Thresholding
+ else if (cmd == "-threshold" || cmd == "-thresh")
+ {
+ double u1 = strcmp(argv[1],"-inf") == 0 ? -vnl_huge_val(0.0) : ReadIntensityValue(argv[1]);
+ double u2 = strcmp(argv[2],"inf") == 0 ? vnl_huge_val(0.0) : ReadIntensityValue(argv[2]);
+ double v1 = ReadIntensityValue(argv[3]);
+ double v2 = ReadIntensityValue(argv[4]);
+ ThresholdImage<TPixel, VDim> adapter(this);
+ adapter(u1, u2, v1, v2);
+ return 4;
+ }
+
+ // Tiling
+ else if (cmd == "-tile")
+ {
+ TileImages<TPixel, VDim> adapter(this);
+ adapter(std::string(argv[1]));
+ return 1;
+ }
+
+ // Trim the image (trim background values from the margin)
+ else if (cmd == "-trim")
+ {
+ // Read the size of the wrap region
+ RealVector margin = ReadRealSize(argv[1]);
+
+ // Trim the image accordingly
+ TrimImage<TPixel, VDim> adapter(this);
+ adapter(margin, TrimImage<TPixel, VDim>::SPECIFY_MARGIN);
+
+ // Return the number of arguments consumed
+ return 1;
+ }
+
+ else if (cmd == "-trim-to-size")
+ {
+ // Read the size of the trim region
+ RealVector size = ReadRealSize(argv[1]);
+
+ // Trim the image accordingly
+ TrimImage<TPixel, VDim> adapter(this);
+ adapter(size, TrimImage<TPixel, VDim>::SPECIFY_FINALSIZE);
+
+ // Return the number of arguments consumed
+ return 1;
+ }
+
+ // Output type specification
+ else if (cmd == "-type")
+ {
+ m_TypeId = str_to_lower(argv[1]);
+ return 1;
+ }
+
+ // Verbose mode
+ else if (cmd == "-verbose")
+ { verbose = &std::cout; return 0; }
+
+ else if (cmd == "-noverbose")
+ { verbose = &devnull; return 0; }
+
+ else if (cmd == "-version")
+ {
+ cout << "Version " << ImageConverter_VERSION_STRING << endl;
+ return 0;
+ }
+
+ else if (cmd == "-vote")
+ {
+ Vote<TPixel, VDim> adapter(this);
+ adapter(false);
+ return 0;
+ }
+
+ else if (cmd == "-vote-mrf")
+ {
+ double beta = atof(argv[1]);
+ size_t iter = atoi(argv[2]);
+ MRFVote<TPixel, VDim> adapter(this);
+ adapter(beta, iter, false);
+ return 2;
+ }
+
+ else if (cmd == "-vote-label")
+ {
+ UpdateMetadataKey<TPixel, VDim> adapter(this);
+ adapter("CND:VOTE_LABEL",argv[1]);
+ return 1;
+ }
+
+ else if (cmd == "-voxel-sum")
+ {
+ // Simply print the sum of all voxels in the image
+ double sum = 0;
+ size_t n = m_ImageStack.back()->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t i = 0; i < n; i++)
+ sum += m_ImageStack.back()->GetBufferPointer()[i];
+ cout << "Voxel Sum: " << sum << endl;
+ return 0;
+ }
+
+ else if (cmd == "-voxel-integral" || cmd == "-voxel-int")
+ {
+ // Simply print the sum of all voxels in the image
+ double sum = 0;
+ size_t n = m_ImageStack.back()->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t i = 0; i < n; i++)
+ sum += m_ImageStack.back()->GetBufferPointer()[i];
+ double vol = 1.0;
+ for(size_t d = 0; d < VDim; d++)
+ vol *= m_ImageStack.back()->GetSpacing()[d];
+ cout << "Voxel Integral: " << sum * vol << endl;
+ return 0;
+ }
+
+ else if (cmd == "-voxelwise-regression" || cmd == "-voxreg")
+ {
+ // Get the order
+ size_t order = atoi(argv[1]);
+ VoxelwiseRegression<TPixel, VDim> adapter(this);
+ adapter(order);
+ return 1;
+ }
+
+ // Warp image
+ else if (cmd == "-warp")
+ {
+ WarpImage<TPixel, VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ // Warp label image
+ else if (cmd == "-warp-label" || cmd=="-warplabel" || cmd=="-wl")
+ {
+ RealVector stdev = ReadRealSize(argv[1]);
+ WarpLabelImage<TPixel, VDim> adapter(this);
+ adapter(stdev);
+ return 1;
+ }
+
+ // Wrap image around
+ else if (cmd == "-wrap")
+ {
+ IndexType iwrap = ReadIndexVector(argv[1]);
+ WrapDimension<TPixel, VDim> adapter(this);
+ adapter(iwrap);
+ return 1;
+ }
+
+ else if (cmd == "-weighted-sum" || cmd == "-wsum")
+ {
+ std::vector<double> weights;
+ for(int i = 1; i < argc; i++)
+ if (is_double(argv[i]))
+ weights.push_back(atof(argv[i]));
+ else break;
+ WeightedSum<TPixel,VDim> adapter(this);
+ adapter(weights);
+ return weights.size();
+ }
+
+ else if (cmd == "-weighted-sum-voxelwise" || cmd == "-wsv")
+ {
+ WeightedSumVoxelwise<TPixel,VDim> adapter(this);
+ adapter();
+ return 0;
+ }
+
+ // Unknown command
+ else
+ { cerr << "Unknown command " << cmd << endl; throw -1; }
+
+ cerr << "Fell through!" << endl;
+ throw -1;
+}
+
+
+template<class TPixel, unsigned int VDim>
+int
+ImageConverter<TPixel, VDim>
+::ProcessCommandLine(int argc, char *argv[])
+{
+ // Disable multithreading
+ itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
+
+ // The last command
+ std::string lastCommand;
+
+ // Check the number of arguments
+ if (argc == 1)
+ {
+ cerr << "PICSL convert3d tool - from the creators of ITK-SNAP" << endl;
+ cerr << "For full documentation and usage examples, see" << endl;
+ cerr << " http://www.itksnap.org/c3d" << endl;
+ cerr << "To get help on available commands, call" << endl;
+ cerr << " " << argv[0] << " -h" << endl;
+ return -1;
+ }
+
+ // Try processing command line
+ try
+ {
+ // The last parameter in the command line is the output file
+ string fnOutput = argv[argc-1];
+
+ // Command line processing
+ for(int i = 1; i < argc; ++i)
+ {
+ string cmd = argv[i];
+ if (cmd[0] == '-')
+ {
+ // Save the last command (for exceptions, etc)
+ lastCommand = argv[i];
+
+ // A command has been supplied
+ i += ProcessCommand(argc-i, argv+i);
+ }
+ else
+ {
+ lastCommand = "";
+
+ // An image file name has been provided. If this image is followed by commands
+ // read it and push in the pipeline.
+ if (i != argc-1)
+ {
+ ReadImage<TPixel, VDim> adapter(this);
+ adapter(argv[i]);
+ }
+ else
+ {
+ // Write the image, but in safe mode
+ WriteImage<TPixel, VDim> adapter(this);
+ adapter(argv[i], false);
+ }
+ }
+ }
+ return 0;
+ }
+ catch (StackAccessException &)
+ {
+ cerr << "Not enough images on the stack for the requested command." << endl;
+ cerr << " Requested command: " << lastCommand << endl;
+ cerr << " Note: C3D requires image operands to precede commands." << endl;
+ cerr << " message can be caused by incorrect usage, such as" << endl;
+ cerr << " c3d -command image.nii " << endl;
+ cerr << " instead of " << endl;
+ cerr << " c3d image.nii -command" << endl;
+ return -1;
+ }
+
+ catch (std::exception &exc)
+ {
+ cerr << "Exception caught of type " << typeid(exc).name() << endl;
+ if(lastCommand.size())
+ cerr << " When processing command: " << lastCommand << endl;
+ cerr << " Exception detail: " << exc.what() << endl;
+ return -1;
+ }
+ catch (...)
+ {
+ cerr << "Unknown exception caught by convert3d" << endl;
+ if(lastCommand.size())
+ cerr << " When processing command: " << lastCommand << endl;
+ return -1;
+ }
+}
+
+bool str_ends_with(const std::string &s, const std::string &pattern)
+{
+ size_t ipos = s.rfind(pattern);
+ return(ipos == s.size() - pattern.size());
+}
+
+// How the specification is made
+enum VecSpec { PHYSICAL, VOXELS, PERCENT, NONE };
+
+template<unsigned int VDim>
+void ReadVecSpec(const char *vec_in, vnl_vector_fixed<double,VDim> &vout, VecSpec &type)
+{
+ // Set up the regular expressions for numerical string parsing
+ RegularExpression re1(
+ "([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)(mm|vox|%)?");
+ RegularExpression re2(
+ "([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)x"
+ "([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)(mm|vox|%)?");
+ RegularExpression re3(
+ "([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)x"
+ "([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)x"
+ "([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)(mm|vox|%)?");
+
+ // Lowercase string
+ string vec = str_to_lower(vec_in);
+ string spec;
+
+ // Check if it's a single-component specification
+ if (VDim == 2 && re2.find(vec))
+ {
+ vout[0] = atof(re2.match(1).c_str());
+ vout[1] = atof(re2.match(3).c_str());
+ spec = re2.match(5);
+ }
+ else if (VDim == 3 && re3.find(vec))
+ {
+ vout[0] = atof(re3.match(1).c_str());
+ vout[1] = atof(re3.match(3).c_str());
+ vout[2] = atof(re3.match(5).c_str());
+ spec = re3.match(7);
+ }
+ else if (re1.find(vec))
+ {
+ vout.fill(atof(re1.match(1).c_str()));
+ spec = re1.match(3);
+ }
+ else throw ConvertException("Invalid vector specification %s", vec_in);
+
+ // Get the type of spec. Luckily, all suffixes have the same length
+ switch(spec.length()) {
+ case 0: type = NONE; break;
+ case 1: type = PERCENT; break;
+ case 2: type = PHYSICAL; break;
+ case 3: type = VOXELS; break;
+ default: throw ConvertException("Internal error in VecSpec code");
+ }
+}
+
+template<class TPixel, unsigned int VDim>
+typename ImageConverter<TPixel, VDim>::RealVector
+ImageConverter<TPixel, VDim>
+::ReadRealVector(const char *vec_in, bool is_point)
+{
+ // Output vector
+ RealVector x, scale, offset;
+ VecSpec type;
+
+ // Read the vector
+ ReadVecSpec(vec_in, x, type);
+
+ // Check the type of the vector
+ if (type != VOXELS && type != PHYSICAL && type != PERCENT)
+ throw ConvertException(
+ "Invalid vector spec %s (must end with 'mm' or 'vox' or '%' )", vec_in);
+
+ // If in percent, scale by voxel size
+ if (type == PERCENT)
+ {
+ for(size_t i = 0; i < VDim; i++)
+ x[i] *= m_ImageStack.back()->GetBufferedRegion().GetSize()[i] / 100.0;
+ type = VOXELS;
+ }
+
+ // If the vector is in vox units, map it to physical units
+ if (type == VOXELS)
+ {
+ // Get the matrix
+ typename ImageType::TransformMatrixType MP =
+ m_ImageStack.back()->GetVoxelSpaceToRASPhysicalSpaceMatrix();
+
+ // Create the vector to multiply by
+ vnl_vector_fixed<double, VDim+1> X, XP;
+ for(size_t d = 0; d < VDim; d++)
+ X[d] = x[d];
+ X[VDim] = is_point ? 1.0 : 0.0;
+
+ // Apply matrix
+ XP = MP * X;
+ for(size_t d = 0; d < VDim; d++)
+ x[d] = XP[d];
+ }
+
+ return x;
+}
+
+template<class TPixel, unsigned int VDim>
+typename ImageConverter<TPixel, VDim>::RealVector
+ImageConverter<TPixel, VDim>
+::ReadRealSize(const char *vec_in)
+{
+ RealVector x = ReadRealVector(vec_in, false);
+ for(size_t d = 0; d < VDim; d++)
+ x[d] = fabs(x[d]);
+ return x;
+}
+
+template<class TPixel, unsigned int VDim>
+TPixel
+ImageConverter<TPixel, VDim>
+::ReadIntensityValue(const char *vec)
+{
+ // Check if the input is infinity first
+ if (!strcmp(vec, "inf") || !strcmp(vec, "+inf") || !strcmp(vec, "Inf") || !strcmp(vec, "+Inf"))
+ return vnl_huge_val(0.0);
+ else if (!strcmp(vec, "-inf") || !strcmp(vec, "-Inf"))
+ return -vnl_huge_val(0.0);
+
+ // Read the double part
+ char *endptr;
+
+ // Read the floating point part
+ TPixel val = (TPixel) strtod(vec, &endptr);
+
+ // Check validity
+ if (endptr == vec)
+ throw ConvertException("Can't convert %s to an intensity spec", vec);
+
+ // Check if there is a '%' specification
+ if (*endptr == '%')
+ {
+ if (m_PercentIntensityMode == PIM_QUANTILE || m_PercentIntensityMode == PIM_FGQUANTILE)
+ {
+ // Check valid quantile
+ if (val < 0.0 || val > 100.0)
+ throw ConvertException("Invalid quantile spec %s, must be between 0 and 100", vec);
+
+ // Compute the specified quantile
+ double qtile = 0.01 * val;
+ if (m_ImageStack.size() == 0)
+ throw ConvertException("Can't use intensity quantile spec with no image on stack");
+
+ // Allocate an array for sorting
+ size_t n = m_ImageStack.back()->GetBufferedRegion().GetNumberOfPixels();
+ TPixel *asort = new TPixel[n], *p = asort, *q = m_ImageStack.back()->GetBufferPointer();
+
+ // Copy intensity values that are legit, ignore nans
+ for(size_t i = 0; i < n; i++, q++)
+ {
+ // We don't include nans and if FGQUANTILE, background values
+ if (!vnl_math_isnan(*q))
+ if (m_PercentIntensityMode == PIM_QUANTILE || *q != m_Background)
+ {*p = *q; ++p;}
+ }
+
+ // Get the size of the sort array
+ size_t np = p - asort;
+ if (np == 0)
+ {
+ if (m_PercentIntensityMode == PIM_QUANTILE)
+ throw ConvertException("Quantile could not be computed because the image has only NANs");
+ else
+ throw ConvertException("Foreground quantile could not be computed because the image has only background");
+ }
+
+ // Sort the acceptable values
+ std::sort(asort, p);
+
+ // Get the quantile
+ size_t k = (size_t) (qtile * np);
+ val = asort[k];
+ delete asort;
+
+ if (m_PercentIntensityMode == PIM_QUANTILE)
+ *verbose << "Quantile " << qtile << " maps to " << val << endl;
+ else
+ *verbose << "Foreground quantile " << qtile << " (over "
+ << np << " voxels) maps to " << val << endl;
+ }
+ else
+ {
+ // A range specification. We need the min and max of the image
+ double imin = numeric_limits<double>::max();
+ double imax = - numeric_limits<double>::max();
+ size_t n = m_ImageStack.back()->GetBufferedRegion().GetNumberOfPixels();
+ TPixel *q = m_ImageStack.back()->GetBufferPointer();
+ for(size_t i = 0; i < n; i++, q++)
+ {
+ if (*q < imin) imin = *q;
+ if (*q > imax) imax = *q;
+ }
+
+ double rspec = val * 0.01;
+ val = imin + rspec * (imax - imin);
+ *verbose << "Intensity range spec " << rspec << " maps to " << val << endl;
+ }
+ }
+
+ return val;
+}
+
+template<class TPixel, unsigned int VDim>
+typename ImageConverter<TPixel, VDim>::SizeType
+ImageConverter<TPixel, VDim>
+::ReadSizeVector(const char *vec_in)
+{
+ // Create a copy of the input string
+ char *vec = new char[strlen(vec_in) + 1];
+ strcpy(vec, vec_in);
+
+ size_t i;
+
+ typename ImageType::SizeType sz;
+
+ // Check if the string ends with %
+ if (str_ends_with(vec, "%"))
+ {
+ // Read floating point size
+ RealVector factor;
+ char *tok = strtok(vec, "x%");
+ for(i = 0; i < VDim && tok != NULL; i++)
+ {
+ factor[i] = atof(tok);
+ if (factor[i] < 0)
+ throw ConvertException("Negative percent size specification: %s", vec_in);
+ tok = strtok(NULL, "x%");
+ }
+
+ if (i == 1)
+ factor.fill(factor[0]);
+
+ // Get the size of the image in voxels
+ for(size_t i = 0; i < VDim; i++)
+ sz[i] = (unsigned long)(m_ImageStack.back()->GetBufferedRegion().GetSize(i) * 0.01 * factor[i] + 0.5);
+ }
+ else
+ {
+ // Find all the 'x' in the string
+ char *tok = strtok(vec, "x");
+ for(size_t i = 0; i < VDim; i++)
+ {
+ if (tok == NULL)
+ throw ConvertException("Invalid size specification: %s", vec_in);
+ int x = atoi(tok);
+ if (x < 0)
+ throw ConvertException("Negative size specification: %s", vec_in);
+ sz[i] = (unsigned long)(x);
+ tok = strtok(NULL, "x");
+ }
+ }
+
+ delete vec;
+ return sz;
+}
+
+
+template<class TPixel, unsigned int VDim>
+typename ImageConverter<TPixel, VDim>::IndexType
+ImageConverter<TPixel, VDim>
+::ReadIndexVector(const char *vec_in)
+{
+ // Create a copy of the input string
+ char *vec = new char[strlen(vec_in)];
+ strcpy(vec, vec_in);
+
+ size_t i;
+
+ typename ImageType::IndexType idx;
+
+ // Check if the string ends with %
+ if (str_ends_with(vec, "%"))
+ {
+ // Read floating point size
+ RealVector factor;
+ char *tok = strtok(vec, "x%");
+ for(i = 0; i < VDim && tok != NULL; i++)
+ {
+ factor[i] = atof(tok);
+ tok = strtok(NULL, "x%");
+ }
+
+ if (i == 1)
+ factor.fill(factor[0]);
+
+ // Get the size of the image in voxels
+ for(size_t i = 0; i < VDim; i++)
+ idx[i] = (long)(m_ImageStack.back()->GetBufferedRegion().GetSize(i) * 0.01 * factor[i] + 0.5);
+ }
+ else
+ {
+ // Find all the 'x' in the string
+ char *tok = strtok(vec, "x");
+ for(size_t i = 0; i < VDim; i++)
+ {
+ if (tok == NULL)
+ throw ConvertException("Invalid index specification: %s", vec_in);
+ int x = atoi(tok);
+ idx[i] = (long)(x);
+ tok = strtok(NULL, "x");
+ }
+ }
+
+ delete vec;
+ return idx;
+}
+
+template<class TPixel, unsigned int VDim>
+void
+ImageConverter<TPixel, VDim>
+::CopyImage()
+{
+ // Get the input image
+ ImagePointer input = m_ImageStack.back();
+
+ // Simply make a copy of the input image on the stack
+ ImagePointer output = ImageType::New();
+ output->SetRegions(input->GetBufferedRegion());
+ output->SetSpacing(input->GetSpacing());
+ output->SetOrigin(input->GetOrigin());
+ output->SetDirection(input->GetDirection());
+ output->SetMetaDataDictionary(input->GetMetaDataDictionary());
+ output->Allocate();
+
+ // Copy everything
+ size_t n = input->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t i = 0; i < n; i++)
+ output->GetBufferPointer()[i] = input->GetBufferPointer()[i];
+
+ // Put on the end of the stack
+ m_ImageStack.pop_back();
+ m_ImageStack.push_back(output);
+}
+
+template<class TPixel, unsigned int VDim>
+void
+ImageConverter<TPixel, VDim>
+::GetBoundingBox(ImageType *image, RealVector &bb0, RealVector &bb1)
+{
+ for(size_t i = 0; i < VDim; i++)
+ {
+ bb0[i] = image->GetOrigin()[i];
+ bb1[i] = bb0[i] + image->GetSpacing()[i] * image->GetBufferedRegion().GetSize()[i];
+ }
+}
+
+template<class TPixel, unsigned int VDim>
+void
+ImageConverter<TPixel, VDim>
+::PrintMatrix(std::ostream &sout, vnl_matrix<double> mat, const char *fmt, const char *prefix)
+{
+ // Print each row and column of the matrix
+ char buffer[256];
+ for(size_t i = 0; i < mat.rows(); i++)
+ {
+ sout << prefix;
+ for(size_t j = 0; j < mat.columns(); j++)
+ {
+ sprintf(buffer, fmt, mat(i,j));
+ sout << buffer;
+ }
+ sout << endl;
+ }
+}
+
+template<class TPixel, unsigned int VDim>
+size_t
+ImageConverter<TPixel, VDim>
+::ForEachLoop(int argc, char *argv[])
+{
+ size_t narg = 0;
+
+ // Note: this is a rather lame implementation that uses recursion with
+ // a state variable to repeat a range of command a bunch of times
+
+ // Back up the current stack
+ ImageStack<ImageType> in_stack = m_ImageStack, out_stack;
+
+ // Print out what's going on
+ *verbose << "Repeating commands for all " << in_stack.size() << " images" << endl;
+
+ // Loop over all images
+ for(size_t i = 0; i < in_stack.size(); i++)
+ {
+ narg = 1;
+
+ // Set up the image stack
+ m_ImageStack.clear();
+ m_ImageStack.push_back(in_stack[i]);
+
+ // Set the in-loop flag
+ m_LoopType = LOOP_FOREACH;
+
+ // When the -endfor is encountered, the InLoop flag will be switched
+ while(m_LoopType == LOOP_FOREACH)
+ narg += 1 + this->ProcessCommand(argc-narg, argv+narg);
+
+ // Place the result (if any) on the output stack
+ if (m_ImageStack.size() > 1)
+ throw ConvertException("Commands in the -foreach clause may not produce multiple outputs");
+ else if (m_ImageStack.size() == 1)
+ out_stack.push_back(m_ImageStack.back());
+ }
+
+ // Update the stack
+ m_ImageStack = out_stack;
+
+ // Return the number of arguments to the next command
+ return narg - 1;
+}
+
+/**
+ * The -accum function allows us to apply binary operations like -add to a list of
+ * images. The commands inside the -accum/-endaccum block are repeated for consecutive
+ * pairs of images, i.e., 1 2 , then result(1,2) and 3, and so on. Within each block,
+ * there are always two images on the stack, and the block must produce one image
+ */
+template<class TPixel, unsigned int VDim>
+size_t
+ImageConverter<TPixel, VDim>
+::AccumulateLoop(int argc, char *argv[])
+{
+ size_t narg = 0;
+
+ // If there are less than two images on the stack, the accum command will not be run.
+ if (m_ImageStack.size() < 2)
+ {
+ throw ConvertException("Too few images on the stack for the -accum command, two or more images are required!");
+ }
+
+ // Back up the current stack
+ ImageStack<ImageType> in_stack = m_ImageStack;
+
+ // Print out what's going on
+ *verbose << "Accumulating result of binary operation for all " << in_stack.size() << " images" << endl;
+
+ // Pop the last image off the stack and place it in the temporary stack
+ m_ImageStack.clear();
+ m_ImageStack.push_back(in_stack.back());
+ in_stack.pop_back();
+
+ // Loop until input stack is empty
+ while(in_stack.size())
+ {
+ // Keep track of the position in the argument list
+ narg = 1;
+
+ // Push the next image on the stack
+ m_ImageStack.push_back(in_stack.back());
+ in_stack.pop_back();
+
+ // Set the in-loop flag
+ m_LoopType = LOOP_ACCUM;
+
+ // When the -endfor is encountered, the InLoop flag will be switched
+ while(m_LoopType == LOOP_ACCUM)
+ narg += 1 + this->ProcessCommand(argc-narg, argv+narg);
+
+ // Place the result (if any) on the output stack
+ if (m_ImageStack.size() != 1)
+ throw ConvertException("Commands in the -accum clause must produce exactly one output");
+ }
+
+ // Return the number of arguments to the next command
+ return narg - 1;
+}
+
+template<class TPixel, unsigned int VDim>
+typename ImageConverter<TPixel, VDim>::LabelToRGBAMap
+ImageConverter<TPixel, VDim>
+::ReadLabelToRGBAMap(const char *fname)
+{
+ // Create a stream for reading the file
+ ifstream fin(fname);
+ string line;
+
+ // Create a temporary array of color labels
+ LabelToRGBAMap lmap;
+
+ // Check that the file is readable
+ if (!fin.good())
+ throw ConvertException("Label file %s can not be read", fname);
+
+ // Read each line of the file separately
+ for(size_t iLine=0; !fin.eof(); iLine++)
+ {
+ // Read the line into a string
+ std::getline(fin,line);
+
+ // Check if the line is a comment or a blank line
+ if (line[0] == '#' || line.length() == 0)
+ continue;
+
+ // Create a stream to parse that string
+ istringstream iss(line);
+ iss.exceptions(std::ios::badbit | std::ios::failbit);
+
+ try
+ {
+ // Read in the elements of the file
+ vnl_vector_fixed<double, 4> rgba;
+ double idx;
+ iss >> idx;
+ iss >> rgba[0];
+ iss >> rgba[1];
+ iss >> rgba[2];
+ iss >> rgba[3];
+
+ // Add the color label to the list
+ lmap[idx] = rgba;
+ }
+ catch( std::exception )
+ {
+ // create an exeption string
+ throw ConvertException("Error reading file %s on line %d", fname, iLine+1);
+ }
+ }
+
+ // Return the map
+ return lmap;
+}
+
+template<class TPixel, unsigned int VDim>
+bool
+ImageConverter<TPixel, VDim>
+::CheckStackSameDimensions(size_t n)
+{
+ if (n == 0)
+ n = m_ImageStack.size();
+
+ if (m_ImageStack.size() < n || n == 0)
+ throw ConvertException("Too few images on the stack for consistency check");
+
+ size_t top = n - 1;
+ for(size_t i = 0; i < n; i++)
+ {
+ if (m_ImageStack[top - i]->GetBufferedRegion() != m_ImageStack[top]->GetBufferedRegion())
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template<class TPixel, unsigned int VDim>
+void
+ImageConverter<TPixel, VDim>
+::SetVariable(std::string name, ImagePointer image)
+{
+ m_ImageVars[name] = image;
+}
+
+template<class TPixel, unsigned int VDim>
+typename ImageConverter<TPixel, VDim>::ImageType *
+ImageConverter<TPixel, VDim>
+::GetVariable(std::string name)
+{
+ if(m_ImageVars.find(name) != m_ImageVars.end())
+ return m_ImageVars[name];
+ else
+ return NULL;
+}
+
+template class ImageConverter<double, 2>;
+template class ImageConverter<double, 3>;
+template class ImageConverter<double, 4>;
+
diff --git a/Submodules/c3d/ConvertImageND.h b/Submodules/c3d/ConvertImageND.h
new file mode 100644
index 0000000..db4b55d
--- /dev/null
+++ b/Submodules/c3d/ConvertImageND.h
@@ -0,0 +1,204 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertImageND.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ConvertImageND_h_
+#define __ConvertImageND_h_
+
+#include "itkOrientedRASImage.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkInterpolateImageFunction.h"
+#include "ImageStack.h"
+#include "ConvertException.h"
+
+#include <iostream>
+#include <cctype>
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <cstdlib>
+
+using namespace std;
+
+template <class TPixel, unsigned int VDim> class ConvertAdapter;
+
+template <class TPixel, unsigned int VDim> struct ConvertAlgorithmParameters;
+class Documentation;
+
+template<class TPixel, unsigned int VDim>
+class ImageConverter
+{
+public:
+
+ // Image typedef
+ typedef itk::OrientedRASImage<TPixel, VDim> ImageType;
+ typedef itk::Image<TPixel, VDim> UnorientedImageType;
+ typedef typename ImageType::Pointer ImagePointer;
+ typedef typename ImageType::SizeType SizeType;
+ typedef typename ImageType::IndexType IndexType;
+ typedef typename ImageType::RegionType RegionType;
+ typedef vnl_vector_fixed<double, VDim> RealVector;
+ typedef vnl_vector_fixed<int, VDim> IntegerVector;
+ typedef std::map<TPixel, vnl_vector_fixed<double, 4> > LabelToRGBAMap;
+
+ // Complex stuff
+ typedef std::complex<TPixel> ComplexPixel;
+ typedef itk::OrientedRASImage<ComplexPixel, VDim> ComplexImageType;
+
+ // Iterators
+ typedef itk::ImageRegionIteratorWithIndex<ImageType> Iterator;
+ typedef itk::ImageRegionConstIteratorWithIndex<ImageType> ConstIterator;
+
+ // Interpolator
+ typedef itk::InterpolateImageFunction<ImageType, double> Interpolator;
+
+ ImageConverter();
+ ~ImageConverter();
+ int ProcessCommandLine(int argc, char *argv[]);
+
+ friend class ConvertAdapter<TPixel, VDim>;
+
+ // Copy image on stack
+ void CopyImage();
+
+ // Get bounding box of an image
+ void GetBoundingBox(ImageType *image, RealVector &bb0, RealVector &bb1);
+
+ // Check if N images on the stack have the same dimensions as the top image
+ // (pass zero to check all images on the stack)
+ bool CheckStackSameDimensions(size_t n);
+
+ // Read label to RGBA mapping from file (SNAP format)
+ static LabelToRGBAMap ReadLabelToRGBAMap(const char *fname);
+
+ // Print a matrix in a nice way
+ void PrintMatrix(
+ std::ostream &sout, vnl_matrix<double> mat,
+ const char *fmt = "%10.4f ", const char *prefix = "");
+
+ // Set label set for split/merge operations
+ typedef std::vector<double> LabelSet;
+ LabelSet &GetSplitLabelSet()
+ { return m_SplitLabelSet; }
+
+ Interpolator *GetInterpolator() const
+ { return m_Interpolator; }
+
+ void SetInterpolator(Interpolator *interp)
+ { m_Interpolator = interp; }
+
+ // Read vectors, etc from command line
+ SizeType ReadSizeVector(const char *vec);
+ IndexType ReadIndexVector(const char *vec);
+ RealVector ReadRealVector(const char *vec, bool is_point);
+ RealVector ReadRealSize(const char *vec);
+ TPixel ReadIntensityValue(const char *vec);
+
+ void PrintManual(std::ostream &out);
+ void PrintCommandListing(std::ostream &out);
+ void PrintCommandHelp(std::ostream &out, const char *command);
+
+ // Add variable
+ void SetVariable(std::string name, ImagePointer image);
+
+ // Get variable
+ ImageType *GetVariable(std::string name);
+
+private:
+
+ // Internal functions
+ int ProcessCommand(int argc, char *argv[]);
+
+
+ // Templated write function
+ template<class TOutPixel> void TemplatedWriteImage(const char *file, double xRoundFactor);
+
+ // Map of variable names
+ typedef map<string, ImagePointer> ImageVariableMap;
+ ImageVariableMap m_ImageVars;
+
+ // Image interpolator for all interpolation commands
+ itk::SmartPointer<Interpolator> m_Interpolator;
+
+ // Implementation of the 'foreach' loop
+ size_t ForEachLoop(int argc, char *argv[]);
+
+ // Implementation of the 'accum' loop
+ size_t AccumulateLoop(int argc, char *argv[]);
+
+ // Type of loop we are in currently
+ enum LoopType { LOOP_NONE = 0, LOOP_FOREACH, LOOP_ACCUM };
+ LoopType m_LoopType;
+
+ // Label set for split/merge
+ LabelSet m_SplitLabelSet;
+
+public:
+
+ // Stack of images from the command line
+ ImageStack<ImageType> m_ImageStack;
+
+ // Typeid of the image to be saved
+ string m_TypeId;
+
+ // Interpolation type
+ string m_Interpolation;
+
+ // Background intensity
+ double m_Background;
+
+ // Rounding factor (not used for float and double) is 0.5 unless -noround was specified
+ double m_RoundFactor;
+
+ // Whether SPM extensions are used
+ bool m_FlagSPM;
+
+ // Whether compression is used by default
+ bool m_UseCompression;
+
+ // Whether multicomponent images are split on read
+ bool m_MultiComponentSplit;
+
+ // Number of iterations for various algorithms
+ size_t m_Iterations;
+
+ // Parameters for various algorithms
+ typedef ConvertAlgorithmParameters<TPixel, VDim> ParameterType;
+ ParameterType *m_Param;
+
+ // How % is handled for intensity specs
+ enum PercentIntensityMode { PIM_QUANTILE, PIM_FGQUANTILE, PIM_RANGE };
+ PercentIntensityMode m_PercentIntensityMode;
+
+ // Verbose output stream
+ std::ostringstream devnull;
+ std::ostream *verbose;
+
+ // Documentation object
+ Documentation *m_Documentation;
+};
+
+#endif
+
diff --git a/Submodules/c3d/ConvertImageVersion.cxx.in b/Submodules/c3d/ConvertImageVersion.cxx.in
new file mode 100644
index 0000000..fc2d27f
--- /dev/null
+++ b/Submodules/c3d/ConvertImageVersion.cxx.in
@@ -0,0 +1,2 @@
+const char *ImageConverter_VERSION_STRING =
+ "@C3D_VERSION_MAJOR at .@C3D_VERSION_MINOR at .@C3D_VERSION_PATCH@";
diff --git a/Submodules/c3d/ConvertNDLibrary.cmake b/Submodules/c3d/ConvertNDLibrary.cmake
new file mode 100644
index 0000000..7fd2840
--- /dev/null
+++ b/Submodules/c3d/ConvertNDLibrary.cmake
@@ -0,0 +1,145 @@
+# This is an include file to use when C3D library is to be built inside of
+# another CMake project (i.e., ITK-SNAP). It is expected that the parent
+# cmake file will take care of finding ITK
+
+# Versioning information
+SET(C3D_VERSION_MAJOR 1)
+SET(C3D_VERSION_MINOR 1)
+SET(C3D_VERSION_PATCH 0)
+SET(C3D_VERSION_FULL "${C3D_VERSION_MAJOR}.${C3D_VERSION_MINOR}.${C3D_VERSION_PATCH}")
+
+# Include directories
+INCLUDE_DIRECTORIES(${CONVERT3D_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${CONVERT3D_SOURCE_DIR}/adapters)
+INCLUDE_DIRECTORIES(${CONVERT3D_SOURCE_DIR}/itkextras/)
+INCLUDE_DIRECTORIES(${CONVERT3D_SOURCE_DIR}/itkextras/VoxBoIO)
+INCLUDE_DIRECTORIES(${CONVERT3D_SOURCE_DIR}/itkextras/PovRayIO)
+INCLUDE_DIRECTORIES(${CONVERT3D_SOURCE_DIR}/itkextras/RandomForest)
+INCLUDE_DIRECTORIES(${CONVERT3D_SOURCE_DIR}/utilities/doc)
+INCLUDE_DIRECTORIES(${CONVERT3D_BINARY_DIR})
+
+IF(WIN32)
+ ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
+ ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
+ SOURCE_GROUP("Adapter Sources" REGULAR_EXPRESSION "adapters/*cxx")
+ SOURCE_GROUP("Adapter Headers" REGULAR_EXPRESSION "adapters/*h")
+ENDIF(WIN32)
+
+# Markdown documentation compiled into the C code
+# modified from: https://github.com/starseeker/tinyscheme-cmake/blob/master/CMakeLists.txt
+# # Rather than load the init.scm file at run time,
+# # with the uncertainties as to where exactly the file
+# # resides, use William Ahern's hexdump to generate
+# # an embeddable version. Build our own copy of hexdump
+# # to ensure consistent behavior and portability.
+# # See http://25thandclement.com/~william/projects/hexdump.c.html
+ADD_EXECUTABLE(markdown_to_hex utilities/hexdump.c)
+set_property(TARGET markdown_to_hex APPEND PROPERTY COMPILE_DEFINITIONS "HEXDUMP_MAIN")
+ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/markdown_docs.h
+ COMMAND markdown_to_hex -i ${CMAKE_CURRENT_SOURCE_DIR}/doc/c3d.md > ${CMAKE_CURRENT_BINARY_DIR}/markdown_docs.h
+ DEPENDS markdown_to_hex ${CMAKE_CURRENT_SOURCE_DIR}/doc/c3d.md)
+ADD_CUSTOM_TARGET(markdown_docs ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/markdown_docs.h)
+
+SET(SOURCES
+ adapters/AddImages.cxx
+ adapters/AlignByLandmarks.cxx
+ adapters/AntiAliasImage.cxx
+ adapters/ApplyMetric.cxx
+ adapters/BinaryHoleFill.cxx
+ adapters/BiasFieldCorrectionN4.cxx
+ adapters/BinaryImageCentroid.cxx
+ adapters/BinaryMathOperation.cxx
+ adapters/Canny.cxx
+ adapters/ClipImageIntensity.cxx
+ adapters/ComputeFFT.cxx
+ adapters/ComputeMoments.cxx
+ adapters/ComputeOverlaps.cxx
+ adapters/ConnectedComponents.cxx
+ adapters/CoordinateMap.cxx
+ adapters/CopyTransform.cxx
+ adapters/CreateImage.cxx
+ adapters/CreateInterpolator.cxx
+ adapters/DicomSeriesList.cxx
+ adapters/ExtractRegion.cxx
+ adapters/ExtractSlice.cxx
+ adapters/FlipImage.cxx
+ adapters/HessianObjectness.cxx
+ adapters/HistogramMatch.cxx
+ adapters/ImageERF.cxx
+ adapters/ImageGradient.cxx
+ adapters/ImageLaplacian.cxx
+ adapters/GeneralLinearModel.cxx
+ adapters/LabelOverlapMeasures.cxx
+ adapters/LabelStatistics.cxx
+ adapters/LandmarksToSpheres.cxx
+ adapters/LaplacianSharpening.cxx
+ adapters/LevelSetSegmentation.cxx
+ adapters/MathematicalMorphology.cxx
+ adapters/MeanFilter.cxx
+ adapters/MedianFilter.cxx
+ adapters/MixtureModel.cxx
+ adapters/MRFVote.cxx
+ adapters/MultiplyImages.cxx
+ adapters/NormalizeLocalWindow.cxx
+ adapters/NormalizedCrossCorrelation.cxx
+ adapters/OverlayLabelImage.cxx
+ adapters/PadImage.cxx
+ adapters/PeronaMalik.cxx
+ adapters/PrintImageInfo.cxx
+ adapters/Rank.cxx
+ adapters/ReadImage.cxx
+ adapters/ReciprocalImage.cxx
+ adapters/ReorderStack.cxx
+ adapters/ReplaceIntensities.cxx
+ adapters/ResampleImage.cxx
+ adapters/ResliceImage.cxx
+ adapters/RFApply.cxx
+ adapters/RFTrain.cxx
+ adapters/SampleImage.cxx
+ adapters/ScaleShiftImage.cxx
+ adapters/ScalarToRGB.cxx
+ adapters/SetSform.cxx
+ adapters/SetOrientation.cxx
+ adapters/SignedDistanceTransform.cxx
+ adapters/SLICSuperVoxel.cxx
+ adapters/SmoothImage.cxx
+ adapters/SplitMultilabelImage.cxx
+ adapters/StapleAlgorithm.cxx
+ adapters/TestImage.cxx
+ adapters/ThresholdImage.cxx
+ adapters/TileImages.cxx
+ adapters/TrimImage.cxx
+ adapters/UnaryMathOperation.cxx
+ adapters/UpdateMetadataKey.cxx
+ adapters/Vote.cxx
+ adapters/VoxelwiseComponentFunction.cxx
+ adapters/VoxelwiseRegression.cxx
+ adapters/WarpImage.cxx
+ adapters/WarpLabelImage.cxx
+ adapters/WeightedSum.cxx
+ adapters/WeightedSumVoxelwise.cxx
+ adapters/WrapDimension.cxx
+ adapters/WriteImage.cxx
+ ${CONVERT3D_BINARY_DIR}/ConvertImageVersion.cxx)
+
+# Configure the version file
+CONFIGURE_FILE(
+ ${CONVERT3D_SOURCE_DIR}/ConvertImageVersion.cxx.in
+ ${CONVERT3D_BINARY_DIR}/ConvertImageVersion.cxx @ONLY IMMEDIATE)
+
+# Get the extra stuff compiled
+SUBDIRS(${CONVERT3D_SOURCE_DIR}/itkextras)
+
+ADD_LIBRARY(cnd_adapters ${SOURCES})
+
+ADD_LIBRARY(cnd_driver
+ ConvertImageND.cxx
+ utilities/doc/Documentation.cxx)
+
+ADD_LIBRARY(cnd_api api/ConvertAPI.cxx)
+
+ADD_DEPENDENCIES(cnd_driver markdown_docs)
+
+SET(C3D_LINK_LIBRARIES
+ cnd_driver cnd_adapters ${ITK_LIBRARIES} ITKVoxBoIO ITKPovRayIO)
diff --git a/Submodules/c3d/ImageStack.h b/Submodules/c3d/ImageStack.h
new file mode 100644
index 0000000..d05ecd8
--- /dev/null
+++ b/Submodules/c3d/ImageStack.h
@@ -0,0 +1,103 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ImageStack.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ImageStack_h_
+#define __ImageStack_h_
+
+#include <itkSmartPointer.h>
+#include <ConvertException.h>
+
+/** A wrapper around vector<ImageType> that throws exceptions */
+template <class ImageType>
+class ImageStack
+{
+public:
+
+ typedef itk::SmartPointer<ImageType> ImagePointer;
+ typedef std::vector<ImagePointer> StackType;
+ typedef typename StackType::iterator iterator;
+ typedef typename StackType::const_iterator const_iterator;
+
+ void push_back(ImageType *src)
+ {
+ m_Stack.push_back(src);
+ }
+
+ void pop_back()
+ {
+ if(m_Stack.size())
+ m_Stack.pop_back();
+ else throw StackAccessException();
+ }
+
+ ImageType* back() const
+ {
+ if(m_Stack.size())
+ return m_Stack.back();
+ else throw StackAccessException();
+ }
+
+ ImageType* front() const
+ {
+ if(m_Stack.size())
+ return m_Stack.front();
+ else throw StackAccessException();
+ }
+
+ ImageType * operator [] (size_t n) const
+ {
+ if(m_Stack.size() > n)
+ return m_Stack[n];
+ else throw StackAccessException();
+ }
+
+ size_t size() const
+ {
+ return m_Stack.size();
+ }
+
+ void clear()
+ {
+ m_Stack.clear();
+ }
+
+ iterator end() { return m_Stack.end(); }
+ const_iterator end() const { return m_Stack.end(); }
+
+ iterator begin() { return m_Stack.begin(); }
+ const_iterator begin() const { return m_Stack.begin(); }
+
+ void insert(iterator &it, ImageType *image)
+ {
+ m_Stack.insert(it, image);
+ }
+
+protected:
+
+ StackType m_Stack;
+};
+
+
+#endif
diff --git a/Submodules/c3d/README.txt b/Submodules/c3d/README.txt
new file mode 100644
index 0000000..ba02b79
--- /dev/null
+++ b/Submodules/c3d/README.txt
@@ -0,0 +1,19 @@
+====================
+ADDING A NEW FEATURE
+====================
+
+1. Create an adapter class using script file in adapters/generators:
+
+ cd adapters/generators
+ bash runme.sh FeatureName "(int param1, int param2)"
+ mv FeatureName.* ..
+
+2. Add the new adapter to CMakeLists
+
+3. Add the new adapter's header file to ConvertImageND.cxx
+
+4. Add an invocation of the new adapter to ImageConverter::ProcessCommand
+
+5. Edit the code in the adapters () operator
+
+6. Update the documentation on the Wiki
diff --git a/Submodules/c3d/adapters/.ResliceImage.cxx.swp b/Submodules/c3d/adapters/.ResliceImage.cxx.swp
new file mode 100644
index 0000000..2cfc99e
Binary files /dev/null and b/Submodules/c3d/adapters/.ResliceImage.cxx.swp differ
diff --git a/Submodules/c3d/adapters/AddImages.cxx b/Submodules/c3d/adapters/AddImages.cxx
new file mode 100644
index 0000000..bf4f37a
--- /dev/null
+++ b/Submodules/c3d/adapters/AddImages.cxx
@@ -0,0 +1,65 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: AddImages.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "AddImages.h"
+#include "itkAddImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+AddImages<TPixel, VDim>
+::operator() ()
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < 2)
+ {
+ cerr << "Binary operations require two images on the stack" << endl;
+ throw -1;
+ }
+
+ // Get the last two images
+ ImagePointer i1 = c->m_ImageStack[c->m_ImageStack.size() - 1];
+ ImagePointer i2 = c->m_ImageStack[c->m_ImageStack.size() - 2];
+
+ // Write something
+ *c->verbose << "Adding #" << c->m_ImageStack.size() - 1 << " and "
+ << c->m_ImageStack.size() - 2 << endl;
+
+ // Perform the multiplication
+ typedef itk::AddImageFilter<ImageType, ImageType, ImageType> FilterType;
+ typename FilterType::Pointer flt = FilterType::New();
+ flt->SetInput1(i1);
+ flt->SetInput2(i2);
+ flt->Update();
+
+ // Replace the images with the product
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(flt->GetOutput());
+}
+
+// Invocations
+template class AddImages<double, 2>;
+template class AddImages<double, 3>;
+template class AddImages<double, 4>;
diff --git a/Submodules/c3d/adapters/AddImages.h b/Submodules/c3d/adapters/AddImages.h
new file mode 100644
index 0000000..9a4d3c6
--- /dev/null
+++ b/Submodules/c3d/adapters/AddImages.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: AddImages.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __AddImages_h_
+#define __AddImages_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class AddImages : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ AddImages(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/AlignByLandmarks.cxx b/Submodules/c3d/adapters/AlignByLandmarks.cxx
new file mode 100644
index 0000000..39051ea
--- /dev/null
+++ b/Submodules/c3d/adapters/AlignByLandmarks.cxx
@@ -0,0 +1,243 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: AlignByLandmarks.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "AlignByLandmarks.h"
+#include "itkLabelImageToLabelMapFilter.h"
+#include "itkShapeLabelObject.h"
+#include "itkLabelMap.h"
+#include "itkShapeLabelMapFilter.h"
+
+#include <vnl/vnl_matrix.h>
+#include <vnl/vnl_vector.h>
+#include <vnl/algo/vnl_svd.h>
+#include <iostream>
+#include <fstream>
+
+template <class TPixel, unsigned int VDim>
+typename AlignByLandmarks<TPixel, VDim>::CentroidMap
+AlignByLandmarks<TPixel, VDim>
+::ExtractCentroids(ImageType *imLabel)
+{
+ typedef unsigned long LabelType;
+ typedef itk::ShapeLabelObject<LabelType, VDim> LabelObjectType;
+ typedef itk::LabelMap<LabelObjectType> LabelMapType;
+
+ typedef itk::LabelImageToLabelMapFilter<ImageType, LabelMapType> ConverterType;
+ typename ConverterType::Pointer fltConvert = ConverterType::New();
+ fltConvert->SetInput(imLabel);
+ fltConvert->SetBackgroundValue(0);
+
+ typedef itk::ShapeLabelMapFilter<LabelMapType> ShapeFilterType;
+ typename ShapeFilterType::Pointer fltShape = ShapeFilterType::New();
+ fltShape->SetInput(fltConvert->GetOutput());
+
+ fltShape->Update();
+
+ CentroidMap cm;
+
+ LabelMapType *labelMap = fltConvert->GetOutput();
+ typename LabelMapType::LabelObjectVectorType vecLabel = labelMap->GetLabelObjects();
+ for(unsigned int i = 0; i < vecLabel.size(); i++)
+ {
+ const LabelObjectType *labelObject = vecLabel[i];
+ itk::Point<double, VDim> ctr = labelObject->GetCentroid();
+
+ // Map the point to RAS coordinate space
+ itk::ContinuousIndex<double, VDim> cidx;
+ imLabel->TransformPhysicalPointToContinuousIndex(ctr, cidx);
+ imLabel->TransformContinuousIndexToRASPhysicalPoint(cidx, ctr);
+
+ cm[labelObject->GetLabel()] = ctr;
+ }
+
+ return cm;
+}
+
+template <class TPixel, unsigned int VDim>
+void
+AlignByLandmarks<TPixel, VDim>
+::operator() (int dof, std::string fn_output)
+{
+ // Get image from stack
+ if(c->m_ImageStack.size() < 2)
+ throw ConvertException("Too few images on the stack for landmark alignment");
+
+ ImagePointer im_moving = c->m_ImageStack.back();
+ c->m_ImageStack.pop_back();
+ ImagePointer im_fixed = c->m_ImageStack.back();
+ c->m_ImageStack.pop_back();
+
+ // Check DOF
+ if(dof != 6 && dof != 7 && dof != 12)
+ throw ConvertException("Landmark alignment degrees of freedom parameter is wrong");
+
+ *c->verbose << "Computing landmark alignment between images" << std::endl;
+ *c->verbose << " Degrees of freedom: " << dof << std::endl;
+ *c->verbose << " Output file: " << fn_output << std::endl;
+
+ // Extract the centroids of the landmarks
+ CentroidMap cm_fixed = ExtractCentroids(im_fixed);
+ CentroidMap cm_moving = ExtractCentroids(im_moving);
+
+ // Find common labels
+ std::set<unsigned long> common;
+ for(typename CentroidMap::iterator it = cm_fixed.begin(); it != cm_fixed.end(); ++it)
+ {
+ if(cm_moving.find(it->first) != cm_moving.end())
+ common.insert(it->first);
+ }
+
+ // Construct matrices A and B containing the points
+ vnl_matrix<double> A0(common.size(), VDim), B0(common.size(), VDim), A, B;
+ vnl_vector<double> cA(VDim), cB(VDim);
+ cA.fill(0); cB.fill(0);
+ int i = 0;
+ double totaldist = 0;
+ *c->verbose << " Distances before alignment: " << std::endl;
+ for(typename std::set<unsigned long>::iterator it = common.begin(); it!=common.end(); ++it)
+ {
+ unsigned long label = *it;
+ for(int j = 0; j < VDim; j++)
+ {
+ A0(i,j) = cm_fixed[label][j];
+ B0(i,j) = cm_moving[label][j];
+ }
+
+ double dist = (A0.get_row(i) - B0.get_row(i)).magnitude();
+ totaldist += dist;
+ *c->verbose << " Label " << label << ": "
+ << A0.get_row(i) << " " << B0.get_row(i) << std::endl;
+ *c->verbose << " Label " << label << ": " << dist << std::endl;
+ cA += A0.get_row(i);
+ cB += B0.get_row(i);
+ i++;
+ }
+ *c->verbose << " Total: " << totaldist << std::endl;
+
+ // We are solving for T(A) - B. Compute the translation and remove center from the data
+ A = A0; B = B0;
+ cA /= A.rows(); cB /= A.rows();
+ vnl_vector<double> translation = cB - cA;
+ for(i = 0; i < A0.rows(); i++)
+ {
+ A.set_row(i, A0.get_row(i) - cA);
+ B.set_row(i, B0.get_row(i) - cB);
+ }
+
+ // The output transform components: affine component M and translation b
+ vnl_matrix<double> out_M;
+ vnl_vector<double> out_b;
+
+ // Split by model
+ if(dof == 6)
+ {
+ // Just solve for the rotation component
+ vnl_svd<double> svd(A.transpose() * B);
+ vnl_matrix<double> R = svd.U() * svd.V().transpose();
+
+ // Get the transform components
+ out_M = R.transpose();
+ }
+
+ else if(dof == 7)
+ {
+ // Recover the uniform scaling of the data. To do this, compute the root mean
+ // squared distance in both datasets
+ double rmsd_A = 0, rmsd_B = 0;
+ for(i = 0; i < A.rows(); i++)
+ {
+ rmsd_A += (A.get_row(i) - cA).squared_magnitude();
+ rmsd_B += (B.get_row(i) - cB).squared_magnitude();
+ }
+ rmsd_A = sqrt(rmsd_A / A.rows());
+ rmsd_B = sqrt(rmsd_B / B.rows());
+
+ // Remove the scale from the matrices
+ A /= rmsd_A; B /= rmsd_B;
+
+ // Solve for the rotation
+ vnl_svd<double> svd(A.transpose() * B);
+ vnl_matrix<double> R = svd.U() * svd.V().transpose();
+
+ // Get the transform components
+ out_M = R.transpose() * (rmsd_B/rmsd_A);
+ }
+
+ else if(dof == 12)
+ {
+ // Here we just need to solve for the entire matrix M
+ vnl_matrix<double> Q(VDim * VDim, VDim * VDim);
+ vnl_matrix<double> Qb(VDim, VDim), P;
+ Q.fill(0.0); Qb = A.transpose() * A;
+ for(int i = 0; i < VDim; i++)
+ Q.update(Qb, i * VDim, i * VDim);
+
+ P = B.transpose() * A;
+ vnl_vector<double> vP(P.data_block(), VDim * VDim);
+
+ vnl_vector<double> vR = vnl_svd<double>(Q).solve(vP);
+ out_M = vnl_matrix<double>(vR.data_block(), VDim, VDim);
+ }
+
+ // Put together a 4 x 4 transform
+
+ // Put together into a 4x4 affine transform
+ vnl_matrix<double> out_mat(VDim+1, VDim+1);
+ out_mat.set_identity();
+ out_mat.update(out_M, 0, 0);
+ vnl_vector<double> tmat = out_mat.get_column(VDim);
+ tmat.update(cB - out_M * cA, 0);
+ out_mat.set_column(VDim, tmat);
+
+ // Save it
+ std::ofstream fout(fn_output.c_str());
+ for(size_t i = 0; i < 4; i++)
+ for(size_t j = 0; j < 4; j++)
+ fout << out_mat[i][j] << (j < 3 ? " " : "\n");
+ fout.close();
+
+ // Compute the final distances
+ *c->verbose << " Distances after alignment: " << std::endl;
+ totaldist = 0;
+ i = 0;
+ for(typename std::set<unsigned long>::iterator it = common.begin(); it!=common.end(); ++it)
+ {
+ unsigned long label = *it;
+ vnl_vector<double> x(4), y;
+ x.fill(1);
+ x.update(A0.get_row(i), 0);
+ y = out_mat * x;
+ double dist = (y.extract(3) - B0.get_row(i)).magnitude();
+ *c->verbose << " Label " << label << ": " << dist << std::endl;
+ totaldist += dist;
+ i++;
+ }
+ *c->verbose << " Total: " << totaldist << std::endl;
+}
+
+// Invocations
+template class AlignByLandmarks<double, 2>;
+template class AlignByLandmarks<double, 3>;
+template class AlignByLandmarks<double, 4>;
diff --git a/Submodules/c3d/adapters/AlignByLandmarks.h b/Submodules/c3d/adapters/AlignByLandmarks.h
new file mode 100644
index 0000000..5ffd6d7
--- /dev/null
+++ b/Submodules/c3d/adapters/AlignByLandmarks.h
@@ -0,0 +1,54 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: AlignByLandmarks.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __AlignByLandmarks_h_
+#define __AlignByLandmarks_h_
+
+#include "ConvertAdapter.h"
+#include <map>
+#include "itkPoint.h"
+
+template<class TPixel, unsigned int VDim>
+class AlignByLandmarks : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ AlignByLandmarks(Converter *c) : c(c) {}
+
+ void operator() (int dof, std::string fn_output);
+
+private:
+ Converter *c;
+
+ typedef std::map<unsigned long, itk::Point<double, VDim> > CentroidMap;
+
+ CentroidMap ExtractCentroids(ImageType *image);
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/AntiAliasImage.cxx b/Submodules/c3d/adapters/AntiAliasImage.cxx
new file mode 100644
index 0000000..ca4a843
--- /dev/null
+++ b/Submodules/c3d/adapters/AntiAliasImage.cxx
@@ -0,0 +1,64 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: AntiAliasImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "AntiAliasImage.h"
+#include "itkAntiAliasBinaryImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+AntiAliasImage<TPixel, VDim>
+::operator() (double xIsoSurface, double rms)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Report what the filter is doing
+ *c->verbose << "Anti-aliasing #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Root Mean Square error: " << rms << endl;
+ *c->verbose << " Iterations: ";
+ if(c->m_Iterations == 0)
+ *c->verbose << "Unlimited" << endl;
+ else
+ *c->verbose << c->m_Iterations << endl;
+
+ // Apply antialiasing to the image
+ typedef itk::AntiAliasBinaryImageFilter<ImageType,ImageType> AntiFilterType;
+ typename AntiFilterType::Pointer fltAnti = AntiFilterType::New();
+ fltAnti->SetInput(input);
+ fltAnti->SetMaximumRMSError(rms);
+ if(c->m_Iterations > 0)
+ fltAnti->SetNumberOfIterations(c->m_Iterations);
+ fltAnti->SetIsoSurfaceValue(xIsoSurface);
+ // fltAnti->AddObserver(itk::ProgressEvent(),command);
+ fltAnti->Update();
+
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltAnti->GetOutput());
+}
+
+// Invocations
+template class AntiAliasImage<double, 2>;
+template class AntiAliasImage<double, 3>;
+template class AntiAliasImage<double, 4>;
diff --git a/Submodules/c3d/adapters/AntiAliasImage.h b/Submodules/c3d/adapters/AntiAliasImage.h
new file mode 100644
index 0000000..1020b70
--- /dev/null
+++ b/Submodules/c3d/adapters/AntiAliasImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: AntiAliasImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __AntiAliasImage_h_
+#define __AntiAliasImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class AntiAliasImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ AntiAliasImage(Converter *c) : c(c) {}
+
+ void operator() (double xIsoSurface, double rms);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ApplyMetric.cxx b/Submodules/c3d/adapters/ApplyMetric.cxx
new file mode 100644
index 0000000..6185c16
--- /dev/null
+++ b/Submodules/c3d/adapters/ApplyMetric.cxx
@@ -0,0 +1,470 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ApplyMetric.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include <string>
+#include <iostream>
+#include "ApplyMetric.h"
+#include "itkNormalizedCorrelationImageToImageMetric.h"
+#include "itkMutualInformationHistogramImageToImageMetric.h"
+#include "itkNormalizedMutualInformationHistogramImageToImageMetric.h"
+#include "itkMattesMutualInformationImageToImageMetric.h"
+#include "itkMeanSquaresImageToImageMetric.h"
+#include "itkImageRegionConstIteratorWithIndex.h"
+#include "itkAffineTransform.h"
+#include <itkTransformFileReader.h>
+#include <itkTransformFactory.h>
+#include "itkResampleImageFilter.h"
+#include "gsGSAffine3DTransform.h"
+#include <vnl/vnl_inverse.h>
+#include <vnl/algo/vnl_matrix_inverse.h>
+
+
+template <class TPixel, unsigned int VDim>
+void
+ApplyMetric<TPixel, VDim>
+::ReadMatrix(const char *fname, itk::Matrix<double,VDim+1,VDim+1> &mat)
+ {
+ ifstream fin(fname);
+ for(size_t i = 0; i < VDim+1; i++)
+ for(size_t j = 0; j < VDim+1; j++)
+ if(fin.good())
+ {
+ fin >> mat[i][j];
+ }
+ else
+ {
+ throw ConvertException("Unable to read matrix %s", fname);
+ }
+ fin.close();
+ }
+
+
+template <class TPixel, unsigned int VDim>
+void
+ApplyMetric<TPixel, VDim>
+::Flip_LPS_to_RAS( itk::Matrix<double,VDim+1,VDim+1> &matrix,
+ itk::Matrix<double,VDim,VDim> &amat,
+ itk::Vector<double, VDim> &aoff)
+{
+
+ // Convert lps to ras
+ vnl_vector<double> v_ras_to_lps(VDim, 1.0);
+ v_ras_to_lps[0] = v_ras_to_lps[1] = -1.0;
+ vnl_diag_matrix<double> m_ras_to_lps(v_ras_to_lps);
+
+ vnl_matrix<double> amatvnl = amat.GetVnlMatrix();
+ amatvnl = m_ras_to_lps * amatvnl * m_ras_to_lps;
+ vnl_vector_fixed<double, VDim > aoffs ;
+ vnl_vector_fixed<double, VDim + 1> aoffl ;
+ aoffs = m_ras_to_lps * aoff.GetVnlVector();
+ aoffl.fill(1.0);
+ for (size_t i=0; i<VDim; i++)
+ aoffl(i) = aoffs(i);
+
+ matrix.GetVnlMatrix().set_identity();
+ matrix.GetVnlMatrix().update( amatvnl, 0, 0);
+ matrix.GetVnlMatrix().set_column(VDim, aoffl);
+
+
+}
+
+template <class TPixel, unsigned int VDim>
+void
+ApplyMetric<TPixel, VDim>
+::Flip_RAS_to_LPS( itk::Matrix<double,VDim+1,VDim+1> &matrix,
+ itk::Matrix<double,VDim,VDim> &amat,
+ itk::Vector<double, VDim> &aoff)
+{
+ // Get the transform matrix and the offset vector
+ vnl_matrix<double> A_ras = matrix.GetVnlMatrix().extract(VDim, VDim);
+ vnl_vector<double> b_ras = matrix.GetVnlMatrix().extract(VDim, 1, 0, VDim).get_column(0);
+
+ // Extrernal matrices are assumed to be RAS to RAS, so we must convert to LPS to LPS
+ vnl_vector<double> v_lps_to_ras(VDim, 1.0);
+ v_lps_to_ras[0] = v_lps_to_ras[1] = -1.0;
+ vnl_diag_matrix<double> m_lps_to_ras(v_lps_to_ras);
+ vnl_matrix<double> A_lps = m_lps_to_ras * A_ras * m_lps_to_ras;
+ vnl_vector<double> b_lps = m_lps_to_ras * b_ras;
+
+ amat = A_lps;
+ aoff.SetVnlVector(b_lps);
+}
+
+
+
+template <class TPixel, unsigned int VDim>
+void
+ApplyMetric<TPixel, VDim>
+::operator() (const char *metric_name, const char *fn_ftran, const char *fn_mtran)
+{
+ //typedef itk::AffineTransform<double, VDim> TransformType;
+ // Two images must be on a stack
+ if(c->m_ImageStack.size() < 2)
+ throw ConvertException("Two images required for metric computation");
+
+
+
+ // Get the images
+ ImagePointer iref = c->m_ImageStack[c->m_ImageStack.size() - 2];
+ ImagePointer imov = c->m_ImageStack[c->m_ImageStack.size() - 1];
+
+ *c->verbose << "Fixed Image Transform: " << fn_ftran << endl;
+ *c->verbose << "Moving Image Transform: " << fn_mtran << endl;
+
+ // Create the appropriate metric
+ typedef itk::ImageToImageMetric<ImageType, ImageType> MetricType;
+ typename MetricType::Pointer metric;
+ if(!strcmp(metric_name,"MI"))
+ {
+ metric =
+ itk::MutualInformationHistogramImageToImageMetric<
+ ImageType,ImageType>::New();
+ }
+ else if(!strcmp(metric_name,"MMI"))
+ {
+ metric =
+ itk::MattesMutualInformationImageToImageMetric<
+ ImageType,ImageType>::New();
+ }
+ else if(!strcmp(metric_name,"MSQ"))
+ {
+ metric =
+ itk::MeanSquaresImageToImageMetric<
+ ImageType,ImageType>::New();
+ }
+ else if(!strcmp(metric_name,"NCOR"))
+ {
+ metric =
+ itk::NormalizedCorrelationImageToImageMetric<
+ ImageType,ImageType>::New();
+ }
+ else if(!strcmp(metric_name,"NMI"))
+ {
+ metric =
+ itk::NormalizedMutualInformationHistogramImageToImageMetric<
+ ImageType,ImageType>::New();
+ }
+ else throw ConvertException("Unknown metric %s", metric_name);
+
+ // Configure the identity transform
+ typename TransformType::Pointer tran = TransformType::New();
+
+
+ double mvalue;
+
+ if (!strcmp(fn_mtran,"none"))
+ {
+ tran->SetIdentity();
+ metric->SetInterpolator(NNInterpolatorType::New());
+ }
+ else if (!strcmp(fn_ftran,"none"))
+ {
+ // Read the matrix
+ itk::Matrix<double,VDim+1,VDim+1> matrix;
+ itk::Matrix<double,VDim,VDim> amat;
+ itk::Vector<double, VDim> aoff;
+
+ ReadMatrix(fn_mtran, matrix);
+ Flip_RAS_to_LPS(matrix, amat, aoff);
+
+ // Put the values in the transform
+ tran->SetMatrix(amat);
+ tran->SetOffset(aoff);
+ metric->SetInterpolator(LinInterpolatorType::New());
+ }
+
+ if (!strcmp(fn_ftran,"none"))
+ {
+ //metric->DebugOn();
+
+ // Configure the metric
+ metric->SetMovingImage(imov);
+ metric->SetFixedImage(iref);
+ metric->SetTransform(tran);
+ metric->SetFixedImageRegion(iref->GetBufferedRegion());
+ metric->Initialize();
+
+ //std::cout << "metric transform parameters: " << metric->GetTransform()->GetParameters() << endl;
+ mvalue = metric->GetValue(tran->GetParameters());
+ }
+ else
+ {
+
+ // Create the image
+ ImagePointer halfway = ImageType::New();
+ CreateHalfwayImageSpace( iref, imov, halfway );
+
+ typename TransformType::Pointer ftran = TransformType::New();
+ typename TransformType::Pointer mtran = TransformType::New();
+
+ // Read the matrix
+ itk::Matrix<double,VDim+1,VDim+1> fmatrix;
+ itk::Matrix<double,VDim,VDim> famat;
+ itk::Vector<double, VDim> faoff;
+
+ ReadMatrix(fn_ftran, fmatrix);
+ Flip_RAS_to_LPS(fmatrix, famat, faoff);
+
+ // Put the values in the transform
+ ftran->SetMatrix(famat);
+ ftran->SetOffset(faoff);
+
+ // Read the matrix
+ itk::Matrix<double,VDim+1,VDim+1> mmatrix;
+ itk::Matrix<double,VDim,VDim> mamat;
+ itk::Vector<double, VDim> maoff;
+
+ ReadMatrix(fn_mtran, mmatrix);
+ Flip_RAS_to_LPS(mmatrix, mamat, maoff);
+
+ // Put the values in the transform
+ mtran->SetMatrix(mamat);
+ mtran->SetOffset(maoff);
+
+ mvalue = GetValueInternalSymmetric( iref, imov, halfway,
+ ftran, mtran, metric_name );
+
+
+ }
+
+ // Print the value
+ cout << metric_name << " = " << mvalue << endl;
+
+ // Get image from stack
+ // ImagePointer img = c->m_ImageStack.back();
+
+ // Do some processing ...
+ // ImagePointer result = ...;
+
+ // Put result on stack
+ // c->m_ImageStack.pop_back();
+ // c->m_ImageStack.push_back(result);
+}
+
+template <class TPixel, unsigned int VDim>
+void
+ApplyMetric<TPixel, VDim>
+::CreateHalfwayImageSpace( ImagePointer fixed, ImagePointer moving, ImagePointer halfway)
+{
+// c3d_affine_tool -sform $TP1 -sform $TP0 -inv -mult -sqrt -sform $TP0 -mult -o $WDIR/hwspace.mat
+
+ MatrixType mfixed = fixed->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix();
+ MatrixType mmoving = moving->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix();
+
+ MatrixType mcomb = mmoving * vnl_matrix_inverse<double>(mfixed);
+ // Peform Denman-Beavers iteration
+ MatrixType Z, Y = mcomb;
+ Z.set_identity();
+
+ for(size_t i = 0; i < 16; i++)
+ {
+ MatrixType Ynext = 0.5 * (Y + vnl_matrix_inverse<double>(Z));
+ MatrixType Znext = 0.5 * (Z + vnl_matrix_inverse<double>(Y));
+ Y = Ynext;
+ Z = Znext;
+ }
+
+ MatrixType mhalf = Y * mfixed;
+
+
+ halfway->SetBufferedRegion(fixed->GetBufferedRegion());
+ // Set the voxel size
+ halfway->SetSpacing(fixed->GetSpacing());
+ halfway->Allocate();
+ halfway->FillBuffer( 0.0 );
+
+ // Set the matrix
+ halfway->SetVoxelSpaceToRASPhysicalSpaceMatrix( mhalf );
+
+}
+
+template <class TPixel, unsigned int VDim>
+double
+ApplyMetric<TPixel, VDim>
+::GetValueInternalSymmetric( ImagePointer fixed, ImagePointer moving, ImagePointer halfway,
+ TransformPointer ftran,
+ TransformPointer mtran, const char * metric_name )
+{
+
+ typedef itk::ImageRegionConstIteratorWithIndex< ImageType > HalfwayIteratorType;
+ typedef itk::Point<double, VDim> InputPointType;
+ typedef itk::Point<double, VDim> OutputPointType;
+ bool subtractmean = false;
+ double measure = 0.0;
+ int nPixels = 0;
+ typename LinInterpolatorType::Pointer movinginterpolator = LinInterpolatorType::New();
+ typename LinInterpolatorType::Pointer fixedinterpolator = LinInterpolatorType::New();
+ movinginterpolator->SetInputImage( moving );
+ fixedinterpolator->SetInputImage( fixed );
+
+ if(!strcmp(metric_name,"MSQ"))
+ {
+ //std::cerr << "Internal method: " << parameters << std::endl;
+
+ HalfwayIteratorType ti( halfway , halfway->GetBufferedRegion() );
+
+
+ typename ImageType::IndexType index;
+
+
+
+ while(!ti.IsAtEnd())
+ {
+
+ index = ti.GetIndex();
+
+ InputPointType inputPoint;
+ halfway->TransformIndexToPhysicalPoint( index, inputPoint );
+
+ OutputPointType transformedFixedPoint = ftran->TransformPoint( inputPoint );
+
+ if( !fixedinterpolator->IsInsideBuffer( transformedFixedPoint ))
+ {
+ ++ti;
+ continue;
+ }
+
+ OutputPointType transformedMovingPoint = mtran->TransformPoint( inputPoint );
+
+ if( !movinginterpolator->IsInsideBuffer( transformedMovingPoint ) )
+ {
+ ++ti;
+ continue;
+ }
+
+ if( movinginterpolator->IsInsideBuffer( transformedMovingPoint ) &&
+ fixedinterpolator->IsInsideBuffer( transformedFixedPoint ))
+ {
+ const double movingValue = movinginterpolator->Evaluate( transformedMovingPoint );
+ const double fixedValue = fixedinterpolator->Evaluate( transformedFixedPoint );
+ nPixels++;
+ const double diff = movingValue - fixedValue;
+ measure += diff * diff;
+ }
+
+ ++ti;
+ }
+
+ if( !nPixels )
+ {
+ throw ConvertException("All the points mapped to outside of the moving image");
+ }
+ else
+ {
+ measure /= nPixels;
+ }
+ }
+ else if(!strcmp(metric_name,"NCOR"))
+ {
+
+ typedef itk::ImageRegionConstIteratorWithIndex< ImageType > HalfwayIteratorType;
+
+
+ HalfwayIteratorType ti( halfway , halfway->GetBufferedRegion() );
+
+ typename ImageType::IndexType index;
+
+
+ double sff = 0.0;
+ double smm = 0.0;
+ double sfm = 0.0;
+ double sf = 0.0;
+ double sm = 0.0;
+
+ while(!ti.IsAtEnd())
+ {
+
+ index = ti.GetIndex();
+
+ InputPointType inputPoint;
+ halfway->TransformIndexToPhysicalPoint( index, inputPoint );
+ OutputPointType transformedFixedPoint = ftran->TransformPoint( inputPoint );
+
+ if( !fixedinterpolator->IsInsideBuffer( transformedFixedPoint ))
+ {
+ ++ti;
+ continue;
+ }
+
+ OutputPointType transformedMovingPoint = mtran->TransformPoint( inputPoint );
+
+ if( !movinginterpolator->IsInsideBuffer( transformedMovingPoint ) )
+ {
+ ++ti;
+ continue;
+ }
+
+ if( movinginterpolator->IsInsideBuffer( transformedMovingPoint ) &&
+ fixedinterpolator->IsInsideBuffer( transformedFixedPoint ))
+ {
+ const double movingValue = movinginterpolator->Evaluate( transformedMovingPoint );
+ const double fixedValue = fixedinterpolator->Evaluate( transformedFixedPoint );
+
+ sff += fixedValue * fixedValue;
+ smm += movingValue * movingValue;
+ sfm += fixedValue * movingValue;
+ if ( subtractmean )
+ {
+ sf += fixedValue;
+ sm += movingValue;
+ }
+ nPixels++;
+ }
+
+ ++ti;
+ }
+
+ if ( subtractmean && nPixels > 0 )
+ {
+ sff -= ( sf * sf / nPixels );
+ smm -= ( sm * sm / nPixels );
+ sfm -= ( sf * sm / nPixels );
+ }
+
+ const double denom = -1.0 * vcl_sqrt(sff * smm );
+
+ if( nPixels > 0 && denom != 0.0)
+ {
+ measure = sfm / denom;
+ }
+ else
+ {
+ measure = 0.0;
+ }
+
+ }
+ else
+ throw ConvertException("Metric %s not supported for symmetric computation", metric_name);
+
+ return measure;
+
+}
+
+
+
+// Invocations
+template class ApplyMetric<double, 2>;
+template class ApplyMetric<double, 3>;
+template class ApplyMetric<double, 4>;
diff --git a/Submodules/c3d/adapters/ApplyMetric.h b/Submodules/c3d/adapters/ApplyMetric.h
new file mode 100644
index 0000000..bf771df
--- /dev/null
+++ b/Submodules/c3d/adapters/ApplyMetric.h
@@ -0,0 +1,73 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ApplyMetric.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ApplyMetric_h_
+#define __ApplyMetric_h_
+
+#include "ConvertAdapter.h"
+#include <itkAffineTransform.h>
+#include "itkNearestNeighborInterpolateImageFunction.h"
+#include "itkLinearInterpolateImageFunction.h"
+
+
+template<class TPixel, unsigned int VDim>
+class ApplyMetric : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ typedef vnl_matrix<double> MatrixType;
+ typedef typename itk::AffineTransform<double, VDim> TransformType;
+ typedef typename TransformType::Pointer TransformPointer;
+ // Interpolators
+ typedef itk::NearestNeighborInterpolateImageFunction<ImageType,double> NNInterpolatorType;
+ typedef itk::LinearInterpolateImageFunction< ImageType, double > LinInterpolatorType;
+
+ ApplyMetric(Converter *c) : c(c) {}
+
+ void operator() (const char *metric, const char *ftran_fn, const char *mtran_fn);
+ void ReadMatrix(const char *fname, itk::Matrix<double,VDim+1,VDim+1> &mat);
+ void CreateHalfwayImageSpace( ImagePointer fixed, ImagePointer moving, ImagePointer halfway);
+ double GetValueInternalSymmetric( ImagePointer fixed, ImagePointer moving, ImagePointer halfway,
+ TransformPointer ftran,
+ TransformPointer mtran, const char * metric_name );
+ void Flip_LPS_to_RAS( itk::Matrix<double,VDim+1,VDim+1> &matrix,
+ itk::Matrix<double,VDim,VDim> &amat,
+ itk::Vector<double, VDim> &aoff);
+ void Flip_RAS_to_LPS( itk::Matrix<double,VDim+1,VDim+1> &matrix,
+ itk::Matrix<double,VDim,VDim> &amat,
+ itk::Vector<double, VDim> &aoff);
+
+
+
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/BiasFieldCorrectionN4.cxx b/Submodules/c3d/adapters/BiasFieldCorrectionN4.cxx
new file mode 100644
index 0000000..75525f1
--- /dev/null
+++ b/Submodules/c3d/adapters/BiasFieldCorrectionN4.cxx
@@ -0,0 +1,220 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BiasFieldCorrectionN4.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "BiasFieldCorrectionN4.h"
+
+#include "itkBSplineControlPointImageFilter.h"
+#include "itkConstantPadImageFilter.h"
+#include "itkDivideImageFilter.h"
+#include "itkExpImageFilter.h"
+#include "itkExtractImageFilter.h"
+#include "itkN4BiasFieldCorrectionImageFilter.h"
+#include "itkOtsuThresholdImageFilter.h"
+#include "itkShrinkImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+BiasFieldCorrectionN4<TPixel, VDim>
+::operator() ()
+{
+
+ // Get image from stack
+ ImagePointer mri = c->m_ImageStack.back();
+ c->m_ImageStack.pop_back();
+
+ // Bias filter
+ typedef itk::N4BiasFieldCorrectionImageFilter<ImageType, ImageType, ImageType> CorrecterType;
+ typename CorrecterType::Pointer correcter = CorrecterType::New();
+
+ // distance (in mm) of the mesh resolution at the base level
+ float splineDistance = 100;
+
+ typename CorrecterType::ArrayType numberOfControlPoints;
+
+ typename ImageType::IndexType inputImageIndex =
+ mri->GetLargestPossibleRegion().GetIndex();
+ typename ImageType::SizeType inputImageSize =
+ mri->GetLargestPossibleRegion().GetSize();
+
+ typename ImageType::PointType newOrigin = mri->GetOrigin();
+
+ itk::SizeValueType lowerBound[VDim];
+ itk::SizeValueType upperBound[VDim];
+
+ for( unsigned int d = 0; d < VDim; d++ )
+ {
+ float domain = static_cast<float>( mri->
+ GetLargestPossibleRegion().GetSize()[d] - 1 ) * mri->GetSpacing()[d];
+ unsigned int numberOfSpans = static_cast<unsigned int>(
+ vcl_ceil( domain / splineDistance ) );
+ unsigned long extraPadding = static_cast<unsigned long>( ( numberOfSpans *
+ splineDistance - domain ) / mri->GetSpacing()[d] + 0.5 );
+ lowerBound[d] = static_cast<unsigned long>( 0.5 * extraPadding );
+ upperBound[d] = extraPadding - lowerBound[d];
+ newOrigin[d] -= ( static_cast<float>( lowerBound[d] ) *
+ mri->GetSpacing()[d] );
+
+ numberOfControlPoints[d] = numberOfSpans + correcter->GetSplineOrder();
+ }
+ correcter->SetNumberOfControlPoints( numberOfControlPoints );
+
+ typedef itk::ConstantPadImageFilter<ImageType, ImageType> PadderType;
+ typename PadderType::Pointer padder = PadderType::New();
+ padder->SetInput( mri );
+ padder->SetPadLowerBound( lowerBound );
+ padder->SetPadUpperBound( upperBound );
+ padder->SetConstant( 0 );
+ padder->Update();
+
+ // Set up a filter to shrink image by a factor
+ typedef itk::ShrinkImageFilter<ImageType, ImageType> ShrinkerType;
+ typename ShrinkerType::Pointer shrinker = ShrinkerType::New();
+ shrinker->SetInput( padder->GetOutput() );
+ shrinker->SetShrinkFactors( 4 );
+ shrinker->Update();
+
+ // Compute mask using Otsu threshold
+ typedef itk::OtsuThresholdImageFilter<ImageType, ImageType> ThresholderType;
+ typename ThresholderType::Pointer otsu = ThresholderType::New();
+ otsu->SetInput( padder->GetOutput() );
+ otsu->SetNumberOfHistogramBins( 200 );
+ otsu->SetInsideValue( 0 );
+ otsu->SetOutsideValue( 1 );
+ otsu->Update();
+ ImagePointer mask = otsu->GetOutput();
+
+ typedef itk::ConstantPadImageFilter<ImageType, ImageType> MaskPadderType;
+ typename MaskPadderType::Pointer maskPadder = MaskPadderType::New();
+ maskPadder->SetInput( otsu->GetOutput() );
+ maskPadder->SetPadLowerBound( lowerBound );
+ maskPadder->SetPadUpperBound( upperBound );
+ maskPadder->SetConstant( 0 );
+ maskPadder->Update();
+
+ // Shrink the mask
+ typename ShrinkerType::Pointer maskshrinker = ShrinkerType::New();
+ maskshrinker->SetInput( maskPadder->GetOutput() );
+ maskshrinker->SetShrinkFactors( 4 );
+ maskshrinker->Update();
+
+ correcter->SetInput( shrinker->GetOutput() );
+ correcter->SetMaskImage( maskshrinker->GetOutput() );
+
+ // These parameters are pretty standard
+ correcter->SetSplineOrder( 3 );
+ correcter->SetNumberOfHistogramBins( 200 );
+ correcter->SetBiasFieldFullWidthAtHalfMaximum( 0.15 );
+ correcter->SetConvergenceThreshold( 0.001 );
+ correcter->SetWienerFilterNoise( 0.01 );
+ correcter->SetBiasFieldFullWidthAtHalfMaximum( 0.15 );
+
+ // You will probably want to have an option for the maximum number of
+ // iterations at each level, the shrink factor, and the spline distance.
+ typename CorrecterType::ArrayType numberOfFittingLevels;
+ numberOfFittingLevels.Fill( 3 );
+ correcter->SetNumberOfFittingLevels( numberOfFittingLevels );
+ typename CorrecterType::VariableSizeArrayType maximumNumberOfIterations;
+ maximumNumberOfIterations.SetSize( 3 );
+ maximumNumberOfIterations[0] = 100;
+ maximumNumberOfIterations[1] = 50;
+ maximumNumberOfIterations[2] = 50;
+ correcter->SetMaximumNumberOfIterations( maximumNumberOfIterations );
+
+ // Progress meter
+ // typedef CommandIterationUpdate<CorrecterType> CommandType;
+ // typename CommandType::Pointer observer = CommandType::New();
+ // correcter->AddObserver( itk::IterationEvent(), observer );
+ correcter->Update();
+
+ /**
+ * Reconstruct the bias field at full image resolution. Divide
+ * the original input image by the bias field to get the final
+ * corrected image.
+ */
+ typedef itk::BSplineControlPointImageFilter<typename
+ CorrecterType::BiasFieldControlPointLatticeType, typename
+ CorrecterType::ScalarImageType> BSplinerType;
+
+ typename BSplinerType::Pointer bspliner = BSplinerType::New();
+ bspliner->SetInput( correcter->GetLogBiasFieldControlPointLattice() );
+ bspliner->SetSplineOrder( correcter->GetSplineOrder() );
+ bspliner->SetSize( mri->GetLargestPossibleRegion().GetSize() );
+ bspliner->SetOrigin( mri->GetOrigin() );
+ bspliner->SetDirection( mri->GetDirection() );
+ bspliner->SetSpacing( mri->GetSpacing() );
+ bspliner->Update();
+
+ typename ImageType::Pointer logField = ImageType::New();
+ logField->SetOrigin( bspliner->GetOutput()->GetOrigin() );
+ logField->SetSpacing( bspliner->GetOutput()->GetSpacing() );
+ logField->SetRegions( bspliner->GetOutput()->GetLargestPossibleRegion().GetSize() );
+ logField->SetDirection( bspliner->GetOutput()->GetDirection() );
+ logField->Allocate();
+
+ itk::ImageRegionIterator<typename CorrecterType::ScalarImageType> ItB(
+ bspliner->GetOutput(),
+ bspliner->GetOutput()->GetLargestPossibleRegion() );
+ itk::ImageRegionIterator<ImageType> ItF( logField,
+ logField->GetLargestPossibleRegion() );
+ for( ItB.GoToBegin(), ItF.GoToBegin(); !ItB.IsAtEnd(); ++ItB, ++ItF )
+ {
+ ItF.Set( ItB.Get()[0] );
+ }
+
+ typedef itk::ExpImageFilter<ImageType, ImageType> ExpFilterType;
+ typename ExpFilterType::Pointer expFilter = ExpFilterType::New();
+ expFilter->SetInput( logField );
+ expFilter->Update();
+
+ typedef itk::DivideImageFilter<ImageType, ImageType, ImageType> DividerType;
+ typename DividerType::Pointer divider = DividerType::New();
+ divider->SetInput1( mri );
+ divider->SetInput2( expFilter->GetOutput() );
+ divider->Update();
+
+ typename ImageType::RegionType inputRegion;
+ inputRegion.SetIndex( inputImageIndex );
+ inputRegion.SetSize( inputImageSize );
+
+ typedef itk::ExtractImageFilter<ImageType, ImageType> CropperType;
+ typename CropperType::Pointer cropper = CropperType::New();
+ cropper->SetInput( divider->GetOutput() );
+ cropper->SetExtractionRegion( inputRegion );
+ cropper->Update();
+
+ typename CropperType::Pointer biasFieldCropper = CropperType::New();
+ biasFieldCropper->SetInput( expFilter->GetOutput() );
+ biasFieldCropper->SetExtractionRegion( inputRegion );
+ biasFieldCropper->Update();
+
+ // Update
+ c->m_ImageStack.push_back( cropper->GetOutput() );
+// c->m_ImageStack.push_back( biasFieldCropper->GetOutput() );
+}
+
+// Invocations
+template class BiasFieldCorrectionN4<double, 2>;
+template class BiasFieldCorrectionN4<double, 3>;
+template class BiasFieldCorrectionN4<double, 4>;
diff --git a/Submodules/c3d/adapters/BiasFieldCorrectionN4.h b/Submodules/c3d/adapters/BiasFieldCorrectionN4.h
new file mode 100644
index 0000000..fbdb127
--- /dev/null
+++ b/Submodules/c3d/adapters/BiasFieldCorrectionN4.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BiasFieldCorrectionN4.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __BiasFieldCorrectionN4_h_
+#define __BiasFieldCorrectionN4_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class BiasFieldCorrectionN4 : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ BiasFieldCorrectionN4(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/BinaryHoleFill.cxx b/Submodules/c3d/adapters/BinaryHoleFill.cxx
new file mode 100644
index 0000000..1ed0492
--- /dev/null
+++ b/Submodules/c3d/adapters/BinaryHoleFill.cxx
@@ -0,0 +1,58 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BinaryHoleFill.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "BinaryHoleFill.h"
+#include "itkBinaryFillholeImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+BinaryHoleFill<TPixel, VDim>
+::operator() (double foreground, bool full_conn)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Apply the fill hole algorithm
+ typedef itk::BinaryFillholeImageFilter<ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(img);
+ filter->SetForegroundValue(foreground);
+ filter->SetFullyConnected(full_conn);
+
+ // Some debug message
+ *c->verbose << "Performing binary hole fill for intensity value " << foreground
+ << " in # " << c->m_ImageStack.size() << endl;
+
+ filter->Update();
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class BinaryHoleFill<double, 2>;
+template class BinaryHoleFill<double, 3>;
+template class BinaryHoleFill<double, 4>;
diff --git a/Submodules/c3d/adapters/BinaryHoleFill.h b/Submodules/c3d/adapters/BinaryHoleFill.h
new file mode 100644
index 0000000..4c66bd4
--- /dev/null
+++ b/Submodules/c3d/adapters/BinaryHoleFill.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BinaryHoleFill.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __BinaryHoleFill_h_
+#define __BinaryHoleFill_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class BinaryHoleFill : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ BinaryHoleFill(Converter *c) : c(c) {}
+
+ void operator() (double foreground, bool full_conn);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/BinaryImageCentroid.cxx b/Submodules/c3d/adapters/BinaryImageCentroid.cxx
new file mode 100644
index 0000000..84f7601
--- /dev/null
+++ b/Submodules/c3d/adapters/BinaryImageCentroid.cxx
@@ -0,0 +1,68 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BinaryImageCentroid.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "BinaryImageCentroid.h"
+
+template <class TPixel, unsigned int VDim>
+void
+BinaryImageCentroid<TPixel, VDim>
+::operator() ()
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ *c->verbose << "Computing centroid of #" << c->m_ImageStack.size() << endl;
+
+ // Find all pixels that do not match background
+ itk::ContinuousIndex<double, VDim> center_idx;
+ center_idx.Fill(0.0);
+ size_t n = 0;
+ for(Iterator it(img, img->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ if(it.Get() != c->m_Background)
+ {
+ n++;
+ for(size_t d = 0; d < VDim; d++)
+ center_idx[d] += it.GetIndex()[d];
+ }
+ }
+
+ // Find the centroid in voxel units
+ for(size_t d = 0; d < VDim; d++)
+ center_idx[d] /= n;
+
+ // Find the centroid in Nifti units
+ itk::Point<double, VDim> center_pt;
+ img->TransformContinuousIndexToRASPhysicalPoint(center_idx, center_pt);
+
+ // Print the resuts
+ cout << "CENTROID_VOX " << center_idx << endl;
+ cout << "CENTROID_MM " << center_pt << endl;
+}
+
+// Invocations
+template class BinaryImageCentroid<double, 2>;
+template class BinaryImageCentroid<double, 3>;
+template class BinaryImageCentroid<double, 4>;
diff --git a/Submodules/c3d/adapters/BinaryImageCentroid.h b/Submodules/c3d/adapters/BinaryImageCentroid.h
new file mode 100644
index 0000000..40dafa2
--- /dev/null
+++ b/Submodules/c3d/adapters/BinaryImageCentroid.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BinaryImageCentroid.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __BinaryImageCentroid_h_
+#define __BinaryImageCentroid_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class BinaryImageCentroid : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ BinaryImageCentroid(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/BinaryMathOperation.cxx b/Submodules/c3d/adapters/BinaryMathOperation.cxx
new file mode 100644
index 0000000..5fdbc4c
--- /dev/null
+++ b/Submodules/c3d/adapters/BinaryMathOperation.cxx
@@ -0,0 +1,99 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BinaryMathOperation.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "BinaryMathOperation.h"
+
+#include "itkAddImageFilter.h"
+#include "itkSubtractImageFilter.h"
+#include "itkMultiplyImageFilter.h"
+#include "itkDivideImageFilter.h"
+#include "itkMinimumImageFilter.h"
+#include "itkMaximumImageFilter.h"
+#include "itkAtan2ImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+BinaryMathOperation<TPixel, VDim>
+::operator() (Operation op)
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < 2)
+ {
+ cerr << "Binary operations require two images on the stack" << endl;
+ throw -1;
+ }
+
+ // Get the last two images
+ ImagePointer i1 = c->m_ImageStack[c->m_ImageStack.size() - 1];
+ ImagePointer i2 = c->m_ImageStack[c->m_ImageStack.size() - 2];
+
+ // Write something
+ *c->verbose << "Adding #" << c->m_ImageStack.size() - 1 << " and "
+ << c->m_ImageStack.size() - 2 << endl;
+
+ // Select the operation
+ typedef typename itk::ImageToImageFilter<ImageType, ImageType> BaseFilterType;
+ typename BaseFilterType::Pointer filter;
+
+ switch(op)
+ {
+ case ADD:
+ filter = itk::AddImageFilter<ImageType, ImageType, ImageType>::New();
+ break;
+ case ATAN2:
+ filter = itk::Atan2ImageFilter<ImageType, ImageType, ImageType>::New();
+ break;
+ case SUBTRACT:
+ filter = itk::SubtractImageFilter<ImageType, ImageType, ImageType>::New();
+ break;
+ case MULTIPLY:
+ filter = itk::MultiplyImageFilter<ImageType, ImageType, ImageType>::New();
+ break;
+ case DIVIDE:
+ filter = itk::DivideImageFilter<ImageType, ImageType, ImageType>::New();
+ break;
+ case MINIMUM:
+ filter = itk::MinimumImageFilter<ImageType, ImageType, ImageType>::New();
+ break;
+ case MAXIMUM:
+ filter = itk::MaximumImageFilter<ImageType, ImageType, ImageType>::New();
+ break;
+ }
+
+ // Run the filter
+ filter->SetInput(0, i1);
+ filter->SetInput(1, i2);
+ filter->Update();
+
+ // Replace the images with the product
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class BinaryMathOperation<double, 2>;
+template class BinaryMathOperation<double, 3>;
+template class BinaryMathOperation<double, 4>;
diff --git a/Submodules/c3d/adapters/BinaryMathOperation.h b/Submodules/c3d/adapters/BinaryMathOperation.h
new file mode 100644
index 0000000..8adb7fe
--- /dev/null
+++ b/Submodules/c3d/adapters/BinaryMathOperation.h
@@ -0,0 +1,60 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: BinaryMathOperation.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __BinaryMathOperation_h_
+#define __BinaryMathOperation_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class BinaryMathOperation : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ // Operations supported by this class
+ enum Operation
+ {
+ ADD = 0,
+ ATAN2,
+ SUBTRACT,
+ MULTIPLY,
+ DIVIDE,
+ MAXIMUM,
+ MINIMUM
+ };
+
+ BinaryMathOperation(Converter *c) : c(c) {}
+
+ void operator() (Operation op);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/Canny.cxx b/Submodules/c3d/adapters/Canny.cxx
new file mode 100644
index 0000000..90b40ca
--- /dev/null
+++ b/Submodules/c3d/adapters/Canny.cxx
@@ -0,0 +1,86 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Canny.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "Canny.h"
+
+#include "itkCannyEdgeDetectionImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+Canny<TPixel, VDim>
+::operator() (const RealVector &sigma, double tLower, double tUpper)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Create the filter
+ typedef itk::CannyEdgeDetectionImageFilter<ImageType, ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(img);
+
+ // Set up the variance
+ typename FilterType::ArrayType var;
+ for(int d = 0; d < VDim; d++)
+ var[d] = sigma[d] * sigma[d];
+ filter->SetVariance(var);
+
+ // Set the thresholds
+ filter->SetLowerThreshold(tLower);
+ filter->SetUpperThreshold(tUpper);
+
+ *c->verbose << "Performing Canny edge detection on #" << c->m_ImageStack.size() << std::endl;
+ *c->verbose << " Variance : " << var << std::endl;
+ *c->verbose << " Lower Threshold : " << tLower << std::endl;
+ *c->verbose << " Upper Threshold : " << tUpper << std::endl;
+
+ // Do some processing ...
+ filter->Update();
+
+ // Iterate through the output points and save them
+ FILE *fcanny = fopen("canny.obj","wt");
+ for(Iterator it(filter->GetOutput(), filter->GetOutput()->GetBufferedRegion());
+ !it.IsAtEnd(); ++it)
+ {
+ if(it.Get())
+ {
+ itk::Point<double, VDim> p;
+ filter->GetOutput()->TransformIndexToRASPhysicalPoint(it.GetIndex(), p);
+ fprintf(fcanny, "v ");
+ for(int i = 0; i < VDim; i++) fprintf(fcanny, "%f ", p[i]);
+ for(int i = 0; i < VDim; i++) fprintf(fcanny, "255 ");
+ fprintf(fcanny, "\n");
+ }
+ }
+ fclose(fcanny);
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class Canny<double, 2>;
+template class Canny<double, 3>;
+template class Canny<double, 4>;
diff --git a/Submodules/c3d/adapters/Canny.h b/Submodules/c3d/adapters/Canny.h
new file mode 100644
index 0000000..fe8e734
--- /dev/null
+++ b/Submodules/c3d/adapters/Canny.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __Canny_h_
+#define __Canny_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class Canny : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ Canny(Converter *c) : c(c) {}
+
+ void operator() (const RealVector &sigma, double tLower, double tUpper);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ClipImageIntensity.cxx b/Submodules/c3d/adapters/ClipImageIntensity.cxx
new file mode 100644
index 0000000..255b7c0
--- /dev/null
+++ b/Submodules/c3d/adapters/ClipImageIntensity.cxx
@@ -0,0 +1,55 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ClipImageIntensity.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ClipImageIntensity.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ClipImageIntensity<TPixel, VDim>
+::operator() (double iMin, double iMax)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ *c->verbose << "Clipping out-of-range intensities in #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Intensity range: " << iMin << " to " << iMax << endl;
+
+ // Simply replace values outside the clip range with new values
+ for(Iterator it(img, img->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ if(it.Value() < iMin)
+ it.Set(iMin);
+ else if(it.Value() > iMax)
+ it.Set(iMax);
+ }
+
+ // Update the image
+ img->Modified();
+}
+
+// Invocations
+template class ClipImageIntensity<double, 2>;
+template class ClipImageIntensity<double, 3>;
+template class ClipImageIntensity<double, 4>;
diff --git a/Submodules/c3d/adapters/ClipImageIntensity.h b/Submodules/c3d/adapters/ClipImageIntensity.h
new file mode 100644
index 0000000..f7c13e7
--- /dev/null
+++ b/Submodules/c3d/adapters/ClipImageIntensity.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ClipImageIntensity.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ClipImageIntensity_h_
+#define __ClipImageIntensity_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ClipImageIntensity : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ClipImageIntensity(Converter *c) : c(c) {}
+
+ void operator() (double iMin, double iMax);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ComputeFFT.cxx b/Submodules/c3d/adapters/ComputeFFT.cxx
new file mode 100644
index 0000000..f23f4f1
--- /dev/null
+++ b/Submodules/c3d/adapters/ComputeFFT.cxx
@@ -0,0 +1,85 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ComputeFFT.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ComputeFFT.h"
+#include "itkRealToHalfHermitianForwardFFTImageFilter.h"
+#include "itkComplexToRealImageFilter.h"
+#include "itkComplexToImaginaryImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ComputeFFT<TPixel, VDim>
+::operator() ()
+{
+ // Compute the fourier transform of the image. This will take one image
+ // off the stack and place two images (real, imag) on the stack
+ ImagePointer image = c->m_ImageStack.back();
+
+ // Create the fourier transform filter
+ typedef itk::Image<ComplexPixel, VDim> UnorientedComplexImageType;
+ typedef itk::RealToHalfHermitianForwardFFTImageFilter<ImageType> FFTFilter;
+ typename FFTFilter::Pointer fltFourier = FFTFilter::New();
+
+ // Get the real and imaginary components
+ typedef itk::ComplexToRealImageFilter<UnorientedComplexImageType,ImageType> RealFilter;
+ typedef itk::ComplexToImaginaryImageFilter<UnorientedComplexImageType,ImageType> ImagFilter;
+ typename RealFilter::Pointer fltReal = RealFilter::New();
+ typename ImagFilter::Pointer fltImag = ImagFilter::New();
+
+ // Set inputs and outputs
+ cout << "DOING FFT" << endl;
+ try
+ {
+ fltFourier->SetInput(image);
+ fltFourier->Update();
+ }
+ catch(itk::ExceptionObject &exc)
+ {
+ cerr << "Exception caught : " << exc;
+ cerr << endl;
+ return;
+ }
+ cout << "DID MAIN PART" << endl;
+
+ fltReal->SetInput(fltFourier->GetOutput());
+ fltImag->SetInput(fltFourier->GetOutput());
+
+ // Compute the transforms
+ fltReal->Update();
+ fltImag->Update();
+ cout << "DID FFT" << endl;
+
+ // Pop the last guy from the stack and push the real and imag
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltReal->GetOutput());
+ c->m_ImageStack.push_back(fltImag->GetOutput());
+ cout << "FINISHED STACK" << endl;
+
+}
+
+// Invocations
+template class ComputeFFT<double, 2>;
+template class ComputeFFT<double, 3>;
+template class ComputeFFT<double, 4>;
diff --git a/Submodules/c3d/adapters/ComputeFFT.h b/Submodules/c3d/adapters/ComputeFFT.h
new file mode 100644
index 0000000..119e360
--- /dev/null
+++ b/Submodules/c3d/adapters/ComputeFFT.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ComputeFFT.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ComputeFFT_h_
+#define __ComputeFFT_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ComputeFFT : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ComputeFFT(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ComputeMoments.cxx b/Submodules/c3d/adapters/ComputeMoments.cxx
new file mode 100644
index 0000000..a8c1daa
--- /dev/null
+++ b/Submodules/c3d/adapters/ComputeMoments.cxx
@@ -0,0 +1,101 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ComputeMoments.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ComputeMoments.h"
+#include <vnl/algo/vnl_symmetric_eigensystem.h>
+#include <vnl/vnl_matrix.h>
+
+template <class TPixel, unsigned int VDim>
+void
+ComputeMoments<TPixel, VDim>
+::operator() ()
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Count the number of non-zero voxels
+ int n = 0;
+ for(Iterator it(img, img->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ if(it.Value() != c->m_Background)
+ n++;
+ }
+
+ // Allocate the matrix for the PCA
+ typedef vnl_matrix<double> Mat;
+ typedef vnl_vector<double> Vec;
+ Mat x(n, VDim);
+ Vec x_row_sum(VDim, 0.0), x_mean(VDim, 0.0), x_row(VDim, 0.0);
+
+ // Place the NIFTI coordinates of all points into the matrix
+ int k = 0;
+ for(Iterator it(img, img->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ if(it.Value() != c->m_Background)
+ {
+ itk::Point<double, VDim> pos;
+ img->TransformIndexToRASPhysicalPoint(it.GetIndex(), pos);
+ for(int i = 0; i < VDim; i++)
+ x(k, i) = pos[i];
+
+ x_row_sum += x.get_row(k);
+
+ k++;
+ }
+ }
+
+ // Compute the mean of the points
+ x_mean = x_row_sum / n;
+
+ // Subtract the mean from all the points
+ for(int k = 0; k < n; k++)
+ x.set_row(k, x.get_row(k) - x_mean);
+
+ // Compute A`A
+ Mat xTx = x.transpose() * x;
+
+ // Perform the eigen-analysis
+ vnl_symmetric_eigensystem<double> eig(xTx);
+
+ // Report the eigen-vectors and eigen-values
+ std::cout << "Centroid: " << x_mean << std::endl;
+ for(int j = 0; j < VDim; j++)
+ {
+ std::cout << "Mode " << j << " eigenvalue: " << eig.get_eigenvalue(j) << std::endl;
+ std::cout << "Mode " << j << " eigenvector: " << eig.get_eigenvector(j) << std::endl;
+ }
+
+ // Do some processing ...
+ // ImagePointer result = ...;
+
+ // Put result on stack
+ // c->m_ImageStack.pop_back();
+ // c->m_ImageStack.push_back(result);
+}
+
+// Invocations
+template class ComputeMoments<double, 2>;
+template class ComputeMoments<double, 3>;
+template class ComputeMoments<double, 4>;
diff --git a/Submodules/c3d/adapters/ComputeMoments.h b/Submodules/c3d/adapters/ComputeMoments.h
new file mode 100644
index 0000000..97cae47
--- /dev/null
+++ b/Submodules/c3d/adapters/ComputeMoments.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ComputeMoments_h_
+#define __ComputeMoments_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ComputeMoments : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ComputeMoments(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ComputeOverlaps.cxx b/Submodules/c3d/adapters/ComputeOverlaps.cxx
new file mode 100644
index 0000000..9786f8f
--- /dev/null
+++ b/Submodules/c3d/adapters/ComputeOverlaps.cxx
@@ -0,0 +1,97 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ComputeOverlaps.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ComputeOverlaps.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ComputeOverlaps<TPixel, VDim>
+::operator() (double v)
+{
+ // There must be two images available on the stack!
+ if(c->m_ImageStack.size() < 2)
+ {
+ cerr << "Overlap requires two images on the stack!" << endl;
+ throw -1;
+ }
+
+ // Get the last two images
+ ImagePointer i2 = c->m_ImageStack[c->m_ImageStack.size() - 1];
+ ImagePointer i1 = c->m_ImageStack[c->m_ImageStack.size() - 2];
+
+ // Report what the filter is doing
+ *c->verbose << "Computing overlap #" << c->m_ImageStack.size() - 1
+ << " and #" << c->m_ImageStack.size() << endl;
+
+ // The images must have the same size
+ if(i1->GetBufferedRegion() != i2->GetBufferedRegion())
+ {
+ cerr << "Overlap requires the images to be of the same dimensions!" << endl;
+ throw -1;
+ }
+
+ // Create iterators for the two images
+ ConstIterator it1(i1, i1->GetBufferedRegion());
+ ConstIterator it2(i2, i2->GetBufferedRegion());
+ double eps = 0.000001;
+
+ // Counters of the overlap scores
+ long n1 = 0, n2 = 0, n12 = 0;
+
+ // Iterate over all pixels
+ for(; !it1.IsAtEnd(); ++it1, ++it2)
+ {
+ // Read the values
+ double v1 = it1.Get();
+ double v2 = it2.Get();
+
+ // Compare the values to target (within machine error)
+ bool m1 = (v1 == v) || (fabs(2 * (v1 - v) / (v1 + v)) < eps);
+ bool m2 = (v2 == v) || (fabs(2 * (v2 - v) / (v2 + v)) < eps);
+ if(m1) n1++;
+ if(m2) n2++;
+ if(m1 && m2) n12++;
+ }
+
+ // Report the overlaps on one line
+ double xDice = n12 * 2.0 / (n1 + n2);
+ double xRobust = n12 * 1.0 / (n1 + n2 - n12);
+
+ cout << "OVL: " << v << ", " << n1 << ", " << n2 << ", " << n12;
+ cout << ", " << xDice << ", " << xRobust << endl;
+
+ // Print the overlap to c->verbose channel
+ *c->verbose << " Matching voxels in first image: " << n1 << endl;
+ *c->verbose << " Matching voxels in second image: " << n2 << endl;
+ *c->verbose << " Size of overlap region: " << n12 << endl;
+ *c->verbose << " Dice similarity coefficient: " << xDice << endl;
+ *c->verbose << " Intersection / ratio: " << xRobust << endl;
+
+}
+
+// Invocations
+template class ComputeOverlaps<double, 2>;
+template class ComputeOverlaps<double, 3>;
+template class ComputeOverlaps<double, 4>;
diff --git a/Submodules/c3d/adapters/ComputeOverlaps.h b/Submodules/c3d/adapters/ComputeOverlaps.h
new file mode 100644
index 0000000..beda59b
--- /dev/null
+++ b/Submodules/c3d/adapters/ComputeOverlaps.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ComputeOverlaps.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ComputeOverlaps_h_
+#define __ComputeOverlaps_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ComputeOverlaps : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ComputeOverlaps(Converter *c) : c(c) {}
+
+ void operator() (double v);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ConnectedComponents.cxx b/Submodules/c3d/adapters/ConnectedComponents.cxx
new file mode 100644
index 0000000..dd186d0
--- /dev/null
+++ b/Submodules/c3d/adapters/ConnectedComponents.cxx
@@ -0,0 +1,95 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConnectedComponents.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ConnectedComponents.h"
+#include "ThresholdImage.h"
+#include "itkConnectedComponentImageFilter.h"
+#include "itkRelabelComponentImageFilter.h"
+#include "itkCastImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ConnectedComponents<TPixel, VDim>
+::operator() ()
+{
+ // The image is assumed to be binary. If background is non-zero, call binarize
+ // to map the background to zero
+ if(c->m_Background != 0.0)
+ {
+ ThresholdImage<TPixel, VDim> thresh(c);
+ thresh(c->m_Background, c->m_Background, 0.0, 1.0);
+ }
+
+ // Get the last image on the stack
+ ImagePointer image = c->m_ImageStack.back();
+
+ // Integer image typedef
+ typedef itk::OrientedRASImage<int, VDim> IntImageType;
+
+ // Construct the connected components filter
+ typedef itk::ConnectedComponentImageFilter<ImageType, IntImageType> CCFilter;
+
+ // Relabel the components
+ typedef itk::RelabelComponentImageFilter<IntImageType, IntImageType> RCFilter;
+
+ // Describe what we are doing
+ *c->verbose << "Computing connected components of #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Calling ConnectedComponentImageFilter" << endl;
+
+ // Plug in the filter's components
+ typename CCFilter::Pointer fltConnect = CCFilter::New();
+ fltConnect->SetInput(image);
+ fltConnect->SetFullyConnected(false);
+ fltConnect->Update();
+
+ // Describe what we are doing
+ *c->verbose << " Calling RelabelComponentImageFilter" << endl;
+
+ // Relabel and order components
+ typename RCFilter::Pointer fltRelabel = RCFilter::New();
+ fltRelabel->SetInput(fltConnect->GetOutput());
+ fltRelabel->Update();
+
+ // Print the statistics about the connected components
+ long szpx = fltRelabel->GetSizeOfObjectInPixels(1);
+ cout << " There are " <<
+ fltRelabel->GetNumberOfObjects() << " connected components." << endl;
+ cout << " Largest component has " << szpx << " pixels." << endl;
+
+ // We have to convert the image back to the native type
+ typedef itk::CastImageFilter<IntImageType, ImageType> CastFilter;
+ typename CastFilter::Pointer fltCast = CastFilter::New();
+ fltCast->SetInput(fltRelabel->GetOutput());
+ fltCast->Update();
+
+ // Store the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltCast->GetOutput());
+}
+
+// Invocations
+template class ConnectedComponents<double, 2>;
+template class ConnectedComponents<double, 3>;
+template class ConnectedComponents<double, 4>;
diff --git a/Submodules/c3d/adapters/ConnectedComponents.h b/Submodules/c3d/adapters/ConnectedComponents.h
new file mode 100644
index 0000000..5a31c56
--- /dev/null
+++ b/Submodules/c3d/adapters/ConnectedComponents.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConnectedComponents.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ConnectedComponents_h_
+#define __ConnectedComponents_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ConnectedComponents : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ConnectedComponents(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ConvertAdapter.h b/Submodules/c3d/adapters/ConvertAdapter.h
new file mode 100644
index 0000000..f8a79ec
--- /dev/null
+++ b/Submodules/c3d/adapters/ConvertAdapter.h
@@ -0,0 +1,63 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertAdapter.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ConvertAdapter_h_
+#define __ConvertAdapter_h_
+
+#include "ConvertImageND.h"
+
+// Common typedefs for all child classes
+#define CONVERTER_STANDARD_TYPEDEFS \
+ typedef ImageConverter<TPixel, VDim> Converter; \
+ typedef typename Converter::ImageType ImageType; \
+ typedef typename Converter::UnorientedImageType UnorientedImageType; \
+ typedef typename Converter::ImagePointer ImagePointer; \
+ typedef typename Converter::SizeType SizeType; \
+ typedef typename Converter::IndexType IndexType; \
+ typedef typename Converter::IntegerVector IntegerVector; \
+ typedef typename Converter::RealVector RealVector; \
+ typedef typename Converter::RegionType RegionType; \
+ typedef typename Converter::ComplexPixel ComplexPixel; \
+ typedef typename Converter::ComplexImageType ComplexImageType; \
+ typedef typename Converter::Iterator Iterator; \
+ typedef typename Converter::ConstIterator ConstIterator;
+
+/**
+ * Parent class for all adapters
+ */
+template<class TPixel, unsigned int VDim>
+class ConvertAdapter
+{
+public:
+
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ConvertAdapter() {}
+
+protected:
+
+};
+
+#endif
diff --git a/Submodules/c3d/adapters/CoordinateMap.cxx b/Submodules/c3d/adapters/CoordinateMap.cxx
new file mode 100644
index 0000000..a17cf8d
--- /dev/null
+++ b/Submodules/c3d/adapters/CoordinateMap.cxx
@@ -0,0 +1,83 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CoordinateMap.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "CoordinateMap.h"
+
+template <class TPixel, unsigned int VDim>
+void
+CoordinateMap<TPixel, VDim>
+::operator() (bool physical)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ *c->verbose << "Replacing #" << c->m_ImageStack.size()
+ << " with " << VDim << "coordinate maps" << endl;
+
+ // Create three new images
+ ImagePointer coord[VDim];
+ Iterator it[VDim];
+
+ for(size_t i = 0; i < VDim; i++)
+ {
+ coord[i] = ImageType::New();
+ coord[i]->SetRegions(img->GetBufferedRegion());
+ coord[i]->CopyInformation(img);
+ coord[i]->Allocate();
+ it[i] = Iterator(coord[i], img->GetBufferedRegion());
+ }
+
+ while(!it[0].IsAtEnd())
+ {
+ IndexType idx = it[0].GetIndex();
+ if(physical)
+ {
+ typename ImageType::PointType p;
+ img->TransformIndexToRASPhysicalPoint(idx, p);
+ for(size_t i = 0; i < VDim; i++)
+ it[i].Set(p[i]);
+ }
+ else
+ {
+ for(size_t i = 0; i < VDim; i++)
+ it[i].Set(idx[i]);
+ }
+ for(size_t i = 0; i < VDim; i++)
+ ++it[i];
+ }
+
+ // Do some processing ...
+ // ImagePointer result = ...;
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ for(size_t i = 0; i < VDim; i++)
+ c->m_ImageStack.push_back(coord[i]);
+}
+
+// Invocations
+template class CoordinateMap<double, 2>;
+template class CoordinateMap<double, 3>;
+template class CoordinateMap<double, 4>;
diff --git a/Submodules/c3d/adapters/CoordinateMap.h b/Submodules/c3d/adapters/CoordinateMap.h
new file mode 100644
index 0000000..174bd02
--- /dev/null
+++ b/Submodules/c3d/adapters/CoordinateMap.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CoordinateMap.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __CoordinateMap_h_
+#define __CoordinateMap_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class CoordinateMap : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ CoordinateMap(Converter *c) : c(c) {}
+
+ void operator() (bool physical);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/CopyTransform.cxx b/Submodules/c3d/adapters/CopyTransform.cxx
new file mode 100644
index 0000000..579cdba
--- /dev/null
+++ b/Submodules/c3d/adapters/CopyTransform.cxx
@@ -0,0 +1,67 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CopyTransform.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "CopyTransform.h"
+
+template <class TPixel, unsigned int VDim>
+void
+CopyTransform<TPixel, VDim>
+::operator() ()
+{
+ // Pop off the last image
+ size_t n = c->m_ImageStack.size();
+ if(n < 2)
+ {
+ throw string("Two images must be on the stack");
+ }
+
+ // Get data source and transform source
+ ImagePointer dsrc = c->m_ImageStack[n-1];
+ ImagePointer tsrc = c->m_ImageStack[n-2];
+
+ // Make sure dimensions match
+ if(dsrc->GetBufferedRegion().GetSize() != tsrc->GetBufferedRegion().GetSize())
+ {
+ throw string("Dimensions of images must match");
+ }
+
+ // Print out what is being done
+ *c->verbose << "Copying transform from #" << n-2 << " to #" << n-1 << endl;
+
+ // Update the data source
+ dsrc->SetOrigin(tsrc->GetOrigin());
+ dsrc->SetSpacing(tsrc->GetSpacing());
+ dsrc->SetDirection(tsrc->GetDirection());
+
+ // Push and pop
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(dsrc);
+}
+
+// Invocations
+template class CopyTransform<double, 2>;
+template class CopyTransform<double, 3>;
+template class CopyTransform<double, 4>;
diff --git a/Submodules/c3d/adapters/CopyTransform.h b/Submodules/c3d/adapters/CopyTransform.h
new file mode 100644
index 0000000..60a0baa
--- /dev/null
+++ b/Submodules/c3d/adapters/CopyTransform.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CopyTransform.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __CopyTransform_h_
+#define __CopyTransform_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class CopyTransform : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ CopyTransform(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/CreateImage.cxx b/Submodules/c3d/adapters/CreateImage.cxx
new file mode 100644
index 0000000..6dfaa49
--- /dev/null
+++ b/Submodules/c3d/adapters/CreateImage.cxx
@@ -0,0 +1,59 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CreateImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "CreateImage.h"
+#include "itkImage.h"
+
+template <class TPixel, unsigned int VDim>
+void
+CreateImage<TPixel, VDim>
+::operator()(SizeType dims, RealVector voxelSize)
+{
+ // Create a new region
+ RegionType region;
+ region.SetSize(dims);
+
+ // Create the image
+ ImagePointer img = ImageType::New();
+ img->SetRegions(region);
+ img->Allocate();
+ img->FillBuffer(c->m_Background);
+
+ // Set the voxel size
+ img->SetSpacing(voxelSize.data_block());
+
+ // Report
+ *c->verbose << "Creating #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Dimensions: " << dims << endl;
+ *c->verbose << " Spacing: " << voxelSize << endl;
+
+ // Push the image into the stack
+ c->m_ImageStack.push_back(img);
+}
+
+// Invocations
+template class CreateImage<double, 2>;
+template class CreateImage<double, 3>;
+template class CreateImage<double, 4>;
diff --git a/Submodules/c3d/adapters/CreateImage.h b/Submodules/c3d/adapters/CreateImage.h
new file mode 100644
index 0000000..0807117
--- /dev/null
+++ b/Submodules/c3d/adapters/CreateImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CreateImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __CreateImage_h_
+#define __CreateImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class CreateImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ CreateImage(Converter *c) : c(c) {}
+
+ void operator() (SizeType dims, RealVector voxelSize);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/CreateInterpolator.cxx b/Submodules/c3d/adapters/CreateInterpolator.cxx
new file mode 100644
index 0000000..bc94fb0
--- /dev/null
+++ b/Submodules/c3d/adapters/CreateInterpolator.cxx
@@ -0,0 +1,96 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CreateInterpolator.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "CreateInterpolator.h"
+#include "itkInterpolateImageFunction.h"
+#include "itkNearestNeighborInterpolateImageFunction.h"
+#include "itkLinearInterpolateImageFunction.h"
+#include "itkBSplineInterpolateImageFunction.h"
+#include "itkGaussianInterpolateImageFunction.h"
+#include "itkLabelImageGaussianInterpolateImageFunction.h"
+#include "itkWindowedSincInterpolateImageFunction.h"
+
+template <class TPixel, unsigned int VDim>
+void
+CreateInterpolator<TPixel, VDim>
+::CreateNN()
+{
+ typedef itk::NearestNeighborInterpolateImageFunction<ImageType,double> NNInterpolatorType;
+ c->SetInterpolator(NNInterpolatorType::New());
+}
+
+template <class TPixel, unsigned int VDim>
+void
+CreateInterpolator<TPixel, VDim>
+::CreateLinear()
+{
+ typedef itk::LinearInterpolateImageFunction<ImageType,double> LinearInterpolatorType;
+ c->SetInterpolator(LinearInterpolatorType::New());
+}
+
+template <class TPixel, unsigned int VDim>
+void
+CreateInterpolator<TPixel, VDim>
+::CreateCubic()
+{
+ typedef itk::BSplineInterpolateImageFunction<ImageType,double> CubicInterpolatorType;
+ c->SetInterpolator(CubicInterpolatorType::New());
+}
+
+template <class TPixel, unsigned int VDim>
+void
+CreateInterpolator<TPixel, VDim>
+::CreateSinc()
+{
+ typedef itk::WindowedSincInterpolateImageFunction<ImageType, 4> SincInterpolatorType;
+ c->SetInterpolator(SincInterpolatorType::New());
+}
+
+template <class TPixel, unsigned int VDim>
+void
+CreateInterpolator<TPixel, VDim>
+::CreateGaussian(RealVector sigma)
+{
+ typedef itk::GaussianInterpolateImageFunction<ImageType, double> GaussianInterpolatorType;
+ typename GaussianInterpolatorType::Pointer gi = GaussianInterpolatorType::New();
+ gi->SetParameters(sigma.data_block(), 4.0);
+ c->SetInterpolator(gi);
+}
+
+template <class TPixel, unsigned int VDim>
+void
+CreateInterpolator<TPixel, VDim>
+::CreateMultiLabel(RealVector sigma)
+{
+ typedef itk::LabelImageGaussianInterpolateImageFunction<ImageType, double> InterpolatorType;
+ typename InterpolatorType::Pointer gi = InterpolatorType::New();
+ gi->SetParameters(sigma.data_block(), 4.0);
+ c->SetInterpolator(gi);
+}
+
+// Invocations
+template class CreateInterpolator<double, 2>;
+template class CreateInterpolator<double, 3>;
+template class CreateInterpolator<double, 4>;
diff --git a/Submodules/c3d/adapters/CreateInterpolator.h b/Submodules/c3d/adapters/CreateInterpolator.h
new file mode 100644
index 0000000..7908a96
--- /dev/null
+++ b/Submodules/c3d/adapters/CreateInterpolator.h
@@ -0,0 +1,61 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CreateInterpolator.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __CreateInterpolator_h_
+#define __CreateInterpolator_h_
+
+#include "ConvertAdapter.h"
+#include "itkSmartPointer.h"
+
+namespace itk {
+ template <class TImage, class TCoordRep> class InterpolateImageFunction;
+}
+
+template<class TPixel, unsigned int VDim>
+class CreateInterpolator : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ CreateInterpolator (Converter *c) : c(c) {}
+
+ // Helper function: get interpolator based on current flag values
+ typedef itk::InterpolateImageFunction<ImageType, double> InterpolatorType;
+
+ void CreateNN();
+ void CreateLinear();
+ void CreateCubic();
+ void CreateSinc();
+ void CreateGaussian(RealVector sigma);
+ void CreateMultiLabel(RealVector sigma);
+
+private:
+ itk::SmartPointer<InterpolatorType> m_Interp;
+ Converter *c;
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/DicomSeriesList.cxx b/Submodules/c3d/adapters/DicomSeriesList.cxx
new file mode 100644
index 0000000..e6546cf
--- /dev/null
+++ b/Submodules/c3d/adapters/DicomSeriesList.cxx
@@ -0,0 +1,124 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: DicomSeriesList.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "gdcmTag.h"
+#include "gdcmFile.h"
+#include "gdcmReader.h"
+#include "gdcmStringFilter.h"
+
+#include "DicomSeriesList.h"
+#include "itkIOCommon.h"
+#include <itksys/SystemTools.hxx>
+#include "itkGDCMImageIO.h"
+#include "itkGDCMSeriesFileNames.h"
+#include <set>
+
+
+template <class TPixel, unsigned int VDim>
+void
+DicomSeriesList<TPixel, VDim>
+::operator() (const char *dicom_dir)
+{
+ const gdcm::Tag tagRows(0x0028, 0x0010);
+ const gdcm::Tag tagCols(0x0028, 0x0011);
+ const gdcm::Tag tagDesc(0x0008, 0x103e);
+ const gdcm::Tag tagTextDesc(0x0028, 0x0010);
+ const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000E);
+ const gdcm::Tag tagSeriesNumber(0x0020,0x0011);
+ const gdcm::Tag tagAcquisitionNumber(0x0020,0x0012);
+ const gdcm::Tag tagInstanceNumber(0x0020,0x0013);
+
+ // Get the directory where to search for the series
+ std::string series_dir = dicom_dir;
+ if(!itksys::SystemTools::FileIsDirectory(dicom_dir))
+ series_dir = itksys::SystemTools::GetParentDirectory(dicom_dir);
+
+ // Use the ITK stuff for parsing
+ typename itk::GDCMSeriesFileNames::Pointer gdcm_series = itk::GDCMSeriesFileNames::New();
+ gdcm_series->SetUseSeriesDetails(true);
+ gdcm_series->SetDirectory(series_dir);
+
+ std::cout
+ << "SeriesNumber"
+ << "\tDimensions"
+ << "\tNumImages"
+ << "\tSeriesDescription"
+ << "\tSeriesID"
+ << std::endl;
+
+ // List all the unique series ids
+ const itk::SerieUIDContainer uids = gdcm_series->GetSeriesUIDs();
+ for(int i = 0; i < uids.size(); i++)
+ {
+ // Get the filenames for this serie
+ const itk::FilenamesContainer &fc = gdcm_series->GetFileNames(uids[i]);
+ if(!fc.size())
+ continue;
+
+ // Print out each series in order
+
+ // Get tags for this file
+ std::set<gdcm::Tag> tagset;
+ tagset.insert(tagRows);
+ tagset.insert(tagCols);
+ tagset.insert(tagSeriesNumber);
+ tagset.insert(tagDesc);
+
+ // Read the tags
+ gdcm::Reader reader;
+ reader.SetFileName(fc.front().c_str());
+ bool read = false;
+
+ try { read = reader.ReadSelectedTags(tagset); }
+ catch(...) { read = false; }
+
+ if(read)
+ {
+ gdcm::StringFilter sf;
+ sf.SetFile(reader.GetFile());
+
+ // Read series description
+ std::cout << sf.ToString(tagSeriesNumber);
+
+ // Read the dimensions
+ ostringstream oss;
+ oss << sf.ToString(tagRows) << "x"
+ << sf.ToString(tagCols) << "x"
+ << fc.size();
+
+ std::cout << "\t" << oss.str();
+ std::cout << "\t" << fc.size();
+ std::cout << "\t" << sf.ToString(tagDesc);
+ std::cout << "\t" << uids[i];
+ }
+
+ std::cout << std::endl;
+ }
+}
+
+// Invocations
+template class DicomSeriesList<double, 2>;
+template class DicomSeriesList<double, 3>;
+template class DicomSeriesList<double, 4>;
diff --git a/Submodules/c3d/adapters/DicomSeriesList.h b/Submodules/c3d/adapters/DicomSeriesList.h
new file mode 100644
index 0000000..4e62abd
--- /dev/null
+++ b/Submodules/c3d/adapters/DicomSeriesList.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __DicomSeriesList_h_
+#define __DicomSeriesList_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class DicomSeriesList : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ DicomSeriesList(Converter *c) : c(c) {}
+
+ void operator() (const char *dicom_dir);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ExtractRegion.cxx b/Submodules/c3d/adapters/ExtractRegion.cxx
new file mode 100644
index 0000000..7e97e9b
--- /dev/null
+++ b/Submodules/c3d/adapters/ExtractRegion.cxx
@@ -0,0 +1,61 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ExtractRegion.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ExtractRegion.h"
+#include "itkRegionOfInterestImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ExtractRegion<TPixel, VDim>
+::operator() (RegionType bbox)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Make sure the bounding box is within the contents of the image
+ bbox.Crop(input->GetBufferedRegion());
+
+ // Report the bounding box size
+ *c->verbose << " Extracting bounding box " << bbox.GetIndex() << " " << bbox.GetSize() << endl;
+
+ // Chop off the region
+ typedef itk::RegionOfInterestImageFilter<ImageType, ImageType> TrimFilter;
+ typename TrimFilter::Pointer fltTrim = TrimFilter::New();
+ fltTrim->SetInput(input);
+ fltTrim->SetRegionOfInterest(bbox);
+ fltTrim->Update();
+
+ // What happened to the origin of the image?
+ ImagePointer output = fltTrim->GetOutput();
+
+ // Update the image stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(output);
+}
+
+// Invocations
+template class ExtractRegion<double, 2>;
+template class ExtractRegion<double, 3>;
+template class ExtractRegion<double, 4>;
diff --git a/Submodules/c3d/adapters/ExtractRegion.h b/Submodules/c3d/adapters/ExtractRegion.h
new file mode 100644
index 0000000..5b9d6fb
--- /dev/null
+++ b/Submodules/c3d/adapters/ExtractRegion.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ExtractRegion.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ExtractRegion_h_
+#define __ExtractRegion_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ExtractRegion : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ExtractRegion(Converter *c) : c(c) {}
+
+ void operator() (RegionType bbox);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ExtractSlice.cxx b/Submodules/c3d/adapters/ExtractSlice.cxx
new file mode 100644
index 0000000..d162083
--- /dev/null
+++ b/Submodules/c3d/adapters/ExtractSlice.cxx
@@ -0,0 +1,216 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ExtractSlice.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ExtractSlice.h"
+#include "ExtractRegion.h"
+#include <string>
+#include <sstream>
+#include <iostream>
+#include "itkExtractImageFilter.h"
+#include "itkImageFileWriter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ExtractSlice<TPixel, VDim>
+::ExtractOneSlice(ImageType *image, unsigned int slicedir, int slicepos)
+{
+ // Say what we are doing
+ static const char axis[] = "XYZW";
+ *c->verbose << "Extracting slice " << slicepos << " along " << axis[slicedir]
+ << " axis in image #" << c->m_ImageStack.size()-1 << endl;
+
+ // Use the extractor to extract the actual region
+ RegionType rslice = image->GetBufferedRegion();
+ rslice.SetSize(slicedir, 1);
+ rslice.SetIndex(slicedir, slicepos);
+
+ // Push the big image back on the stack
+ c->m_ImageStack.push_back(image);
+
+ ExtractRegion<TPixel, VDim> extractor(c);
+ extractor(rslice);
+
+ // If slicing in the last dimension, we are done
+ if(slicedir == VDim - 1)
+ return;
+
+ // Now, transpose the image to make the last dimension 1
+ // (this is like MATLAB's reshape command)
+ std::vector<size_t> reorder;
+ for(size_t i = 0; i < VDim; i++)
+ if(i != slicedir)
+ reorder.push_back(i);
+ reorder.push_back(slicedir); // 0 -> 1,2,0 1 -> 0,2,1 ...
+
+ // Create a new image for the slice
+ ImagePointer imnew = ImageType::New();
+ ImagePointer imext = c->m_ImageStack.back();
+ imnew->CopyInformation(image);
+
+ // Create new region, origin, spacing for the image
+ RegionType reg;
+ typename ImageType::PointType org;
+ typename ImageType::SpacingType spc;
+ typename ImageType::DirectionType dir;
+
+ for(size_t i = 0; i < VDim; i++)
+ {
+ int j = reorder[i];
+ reg.SetSize(i, imext->GetBufferedRegion().GetSize(j));
+ reg.SetIndex(i, imext->GetBufferedRegion().GetIndex(j));
+ org[i] = imext->GetOrigin()[i]; // not a bug!
+ spc[i] = imext->GetSpacing()[j];
+ for(size_t k = 0; k < VDim; k++)
+ dir(k, i) = imext->GetDirection()(k, j);
+ }
+
+ imnew->SetRegions(reg);
+ imnew->SetOrigin(org);
+ imnew->SetSpacing(spc);
+ imnew->SetDirection(dir);
+
+ // Copy all the data
+ imnew->Allocate();
+ Iterator itrg(imnew, reg);
+ ConstIterator isrc(imext, imext->GetBufferedRegion());
+ for(; !isrc.IsAtEnd(); ++isrc, ++itrg)
+ itrg.Set(isrc.Get());
+
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(imnew);
+}
+
+template <class TPixel, unsigned int VDim>
+void
+ExtractSlice<TPixel, VDim>
+::operator() (string axis, char* position)
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < 1)
+ throw ConvertException("No images on stack");
+
+ // Get the image
+ ImagePointer image = c->m_ImageStack.back();
+ SizeType size = image->GetBufferedRegion().GetSize();
+
+ // Process the first parameter
+ unsigned int slicedir;
+ if (!axis.compare("x") || !axis.compare("0"))
+ slicedir = 0;
+ else if (!axis.compare("y") || !axis.compare("1"))
+ slicedir = 1;
+ else if (!axis.compare("z") || !axis.compare("2"))
+ slicedir = 2;
+ else if (!axis.compare("w") || !axis.compare("3") || !axis.compare("t"))
+ slicedir = 3;
+ else
+ throw ConvertException("first parameter to -slice must be x,y,z or w");
+
+ // Now determine the pattern of the second parameter. Allowed formats are
+ // 1. 15 // slice 15 (0-based indexing)
+ // 2. -1 // slice N-1
+ // 3. 20% // slice at 20% of the stack
+ // 4. 12:-4 // range, every slice
+ // 5. 12:3:-4 // range, every third slice
+
+ // Split the string on the ':'
+ std::string piece;
+ std::stringstream source(position);
+ std::vector<int> pos_list;
+ while(std::getline(source, piece, ':'))
+ {
+ int slicepos;
+
+ // Process the percentage
+ if(piece[piece.size()-1] == '%')
+ {
+ piece = piece.substr(0, piece.size()-1);
+ double percent_pos = atof(piece.c_str());
+ slicepos = (int)(0.5 + (percent_pos / 100.0) * (size[slicedir] -1));
+ }
+ else
+ {
+ slicepos = atoi(piece.c_str());
+ }
+
+ pos_list.push_back(slicepos);
+ }
+
+ // Now we have one, two or three numbers parsed
+ int pos_first, pos_step = 1, pos_last;
+ if(pos_list.size() == 1)
+ {
+ pos_first = pos_step = pos_last = pos_list[0];
+ }
+ else if(pos_list.size() == 2)
+ {
+ pos_first = pos_list[0];
+ pos_last = pos_list[1];
+ }
+ else if(pos_list.size() == 3)
+ {
+ pos_first = pos_list[0];
+ pos_step = pos_list[1];
+ pos_last = pos_list[2];
+ }
+
+ // Deal with negative values for start/stop
+ if(pos_first < 0)
+ pos_first = size[slicedir] + pos_first;
+
+ if(pos_last < 0)
+ pos_last = size[slicedir] + pos_last;
+
+ // Deal with the sign of the step
+ if((pos_first < pos_last && pos_step < 0)
+ || (pos_first > pos_last && pos_step > 0))
+ {
+ pos_step *= -1;
+ }
+
+ // Make sure all is legit
+ if(pos_first < pos_last && pos_step <= 0)
+ throw ConvertException(
+ "Wrong slice list specification %d:%d:%d for -slice command! Step should be positive.",
+ pos_first, pos_step, pos_last);
+
+ if(pos_first > pos_last && pos_step >= 0)
+ throw ConvertException(
+ "Wrong slice list specification %d:%d:%d for -slice command! Step should be negative.",
+ pos_first, pos_step, pos_last);
+
+ // Remove the image from stack
+ c->m_ImageStack.pop_back();
+
+ // Now extract each slice
+ for(int i = pos_first; i <= pos_last; i+=pos_step)
+ this->ExtractOneSlice(image, slicedir, i);
+}
+
+// Invocations
+template class ExtractSlice<double, 3>;
+template class ExtractSlice<double, 4>;
+template class ExtractSlice<double, 2>;
+
diff --git a/Submodules/c3d/adapters/ExtractSlice.h b/Submodules/c3d/adapters/ExtractSlice.h
new file mode 100644
index 0000000..7cf482c
--- /dev/null
+++ b/Submodules/c3d/adapters/ExtractSlice.h
@@ -0,0 +1,51 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ExtractSlice.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+
+#ifndef __ExtractSlice_h_
+#define __ExtractSlice_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ExtractSlice : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ExtractSlice(Converter *c) : c(c) {}
+
+ void operator() (string axis, char* pos);
+
+private:
+ Converter *c;
+
+ void ExtractOneSlice(ImageType *image, unsigned int slicedir, int slicepos);
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/FlipImage.cxx b/Submodules/c3d/adapters/FlipImage.cxx
new file mode 100644
index 0000000..95e347a
--- /dev/null
+++ b/Submodules/c3d/adapters/FlipImage.cxx
@@ -0,0 +1,65 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: FlipImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "FlipImage.h"
+#include "itkFlipImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+FlipImage<TPixel, VDim>
+::operator() (string axis)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Create a flip filter
+ typedef itk::FlipImageFilter<ImageType> FlipType;
+ typename FlipType::Pointer flipper = FlipType::New();
+ typename FlipType::FlipAxesArrayType flipax;
+
+ // Set up the axes to flip
+ for(size_t i = 0; i < VDim; i++)
+ if(axis.find('x'+i) != string::npos || axis.find('X'+i) != string::npos)
+ flipax[i] = true;
+ else
+ flipax[i] = false;
+
+ // Say what we are doing
+ *c->verbose << "Flipping #" << c->m_ImageStack.size()-1 << " about " << flipax << endl;
+
+ // Do some processing ...
+ flipper->SetInput(img);
+ flipper->SetFlipAxes(flipax);
+ flipper->Update();
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(flipper->GetOutput());
+}
+
+// Invocations
+template class FlipImage<double, 2>;
+template class FlipImage<double, 3>;
+template class FlipImage<double, 4>;
diff --git a/Submodules/c3d/adapters/FlipImage.h b/Submodules/c3d/adapters/FlipImage.h
new file mode 100644
index 0000000..b7cf09c
--- /dev/null
+++ b/Submodules/c3d/adapters/FlipImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: FlipImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __FlipImage_h_
+#define __FlipImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class FlipImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ FlipImage(Converter *c) : c(c) {}
+
+ void operator() (string axis);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/GeneralLinearModel.cxx b/Submodules/c3d/adapters/GeneralLinearModel.cxx
new file mode 100644
index 0000000..5aa31ed
--- /dev/null
+++ b/Submodules/c3d/adapters/GeneralLinearModel.cxx
@@ -0,0 +1,91 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: GeneralLinearModel.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "GeneralLinearModel.h"
+#include "vnl/vnl_file_matrix.h"
+#include "vnl/vnl_rank.h"
+#include "vnl/algo/vnl_matrix_inverse.h"
+
+template <class TPixel, unsigned int VDim>
+void
+GeneralLinearModel<TPixel, VDim>
+::operator() (string fn_matrix, string fn_contrast)
+{
+ // Read the matrix from file
+ vnl_file_matrix<double> mat(fn_matrix.c_str());
+ if(!mat)
+ throw string("Unable to read matrix from file given");
+
+ // Read the contrast from file
+ vnl_file_matrix<double> con(fn_contrast.c_str());
+ if(!con)
+ throw string("Unable to read contrast from file given");
+
+ // Check that the number of images matches
+ if(c->m_ImageStack.size() != mat.rows())
+ throw string("Matrix number of rows does not match stack size");
+
+ // Check that the columns in matrix match contrast vector
+ if(con.columns() != mat.columns())
+ throw string("Matrix and contrast vector must have same number of columns");
+
+ *c->verbose << "Running GLM on " << mat.rows() << " images" << endl;
+ *c->verbose << " design matrix: " << mat << endl;
+ *c->verbose << " contrast vector: " << con << endl;
+
+ // Some matrices
+ size_t rank = vnl_rank(mat, vnl_rank_row);
+ vnl_matrix<double> A =
+ vnl_matrix_inverse<double>(mat.transpose() * mat).pinverse(rank);
+
+ // Load all images into a Y matrix (can we do this)
+ size_t n = c->m_ImageStack.front()->GetBufferedRegion().GetNumberOfPixels();
+ vnl_matrix<double> Y(mat.rows(), n);
+ for(size_t i = 0; i < mat.rows(); i++)
+ {
+ TPixel *pix = c->m_ImageStack[i]->GetBufferPointer();
+ for(size_t j = 0; j < n; j++)
+ { Y(i,j) = pix[j]; }
+ }
+
+ // Compute bhat = Ax * Y
+ vnl_matrix<double> bhat = (A * mat.transpose()) * Y;
+
+ // Compute the contrast
+ vnl_matrix<double> res = con * bhat;
+
+ // Populate the first image
+ ImagePointer iout = c->m_ImageStack.front();
+ for(size_t i = 0; i < n; i++)
+ iout->GetBufferPointer()[i] = res(0,i);
+
+ c->m_ImageStack.clear();
+ c->m_ImageStack.push_back(iout);
+}
+
+// Invocations
+template class GeneralLinearModel<double, 2>;
+template class GeneralLinearModel<double, 3>;
+template class GeneralLinearModel<double, 4>;
diff --git a/Submodules/c3d/adapters/GeneralLinearModel.h b/Submodules/c3d/adapters/GeneralLinearModel.h
new file mode 100644
index 0000000..64cdd4b
--- /dev/null
+++ b/Submodules/c3d/adapters/GeneralLinearModel.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: GeneralLinearModel.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __GeneralLinearModel_h_
+#define __GeneralLinearModel_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class GeneralLinearModel : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ GeneralLinearModel(Converter *c) : c(c) {}
+
+ void operator() (string fn_matrix, string fn_contrast);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/HessianObjectness.cxx b/Submodules/c3d/adapters/HessianObjectness.cxx
new file mode 100644
index 0000000..d6a61ec
--- /dev/null
+++ b/Submodules/c3d/adapters/HessianObjectness.cxx
@@ -0,0 +1,87 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: HessianObjectness.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "HessianObjectness.h"
+#include "itkHessianRecursiveGaussianImageFilter.h"
+#include "itkMultiScaleHessianBasedMeasureImageFilter.h"
+#include "itkHessianToObjectnessMeasureImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+HessianObjectness<TPixel, VDim>
+::operator() (int dimension, double minscale, double maxscale)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Define the filter
+ typedef typename itk::NumericTraits<TPixel>::RealType RealPixelType;
+ typedef itk::SymmetricSecondRankTensor<RealPixelType, VDim> HessianPixelType;
+ typedef itk::Image<HessianPixelType, VDim> HessianImageType;
+
+ typedef itk::HessianToObjectnessMeasureImageFilter<HessianImageType, ImageType>
+ ObjectnessFilterType;
+
+ typedef itk::MultiScaleHessianBasedMeasureImageFilter<ImageType, HessianImageType, ImageType>
+ MultiScaleEnhancementFilterType;
+
+ // Create the objectness filter
+ typename ObjectnessFilterType::Pointer of = ObjectnessFilterType::New();
+ of->SetScaleObjectnessMeasure(true); // why?
+ of->SetBrightObject(dimension > 0);
+ of->SetObjectDimension(abs(dimension));
+ of->SetAlpha(0.5);
+ of->SetBeta(0.5);
+ of->SetGamma(5.0);
+
+ // Create the main filter
+ typename MultiScaleEnhancementFilterType::Pointer filter = MultiScaleEnhancementFilterType::New();
+ filter->SetInput(img);
+ filter->SetHessianToMeasureFilter(of);
+ filter->SetSigmaStepMethodToLogarithmic();
+ filter->SetSigmaMaximum(maxscale);
+ filter->SetSigmaMinimum(minscale);
+ filter->SetNumberOfSigmaSteps(minscale == maxscale ? 1 : 10);
+
+ // Run the filter
+ *c->verbose << "Extracting Hessian Objectness from #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Object dimension: " << of->GetObjectDimension() << endl;
+ *c->verbose << " Object type: " << (of->GetBrightObject() ? "bright" : "dark") << endl;
+ *c->verbose << " Sigma range: " << filter->GetSigmaMinimum() << " " << filter->GetSigmaMaximum() << endl;
+
+ filter->Update();
+
+ // Do some processing ...
+ ImagePointer result = filter->GetOutput();
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(result);
+}
+
+// Invocations
+template class HessianObjectness<double, 2>;
+template class HessianObjectness<double, 3>;
+template class HessianObjectness<double, 4>;
diff --git a/Submodules/c3d/adapters/HessianObjectness.h b/Submodules/c3d/adapters/HessianObjectness.h
new file mode 100644
index 0000000..ea155b4
--- /dev/null
+++ b/Submodules/c3d/adapters/HessianObjectness.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: HessianObjectness.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __HessianObjectness_h_
+#define __HessianObjectness_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class HessianObjectness : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ HessianObjectness(Converter *c) : c(c) {}
+
+ void operator() (int codimension, double minscale, double maxscale);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/HistogramMatch.cxx b/Submodules/c3d/adapters/HistogramMatch.cxx
new file mode 100644
index 0000000..dc9b7f0
--- /dev/null
+++ b/Submodules/c3d/adapters/HistogramMatch.cxx
@@ -0,0 +1,69 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: HistogramMatch.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "HistogramMatch.h"
+#include "itkHistogramMatchingImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+HistogramMatch<TPixel, VDim>
+::operator() (int nmatch)
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < 2)
+ {
+ cerr << "Reslice operation requires two images on the stack" << endl;
+ throw -1;
+ }
+
+ // Get the reference and source images
+ ImagePointer iref = c->m_ImageStack[c->m_ImageStack.size() - 2];
+ ImagePointer isrc = c->m_ImageStack.back();
+
+ // Create the filter
+ typedef itk::HistogramMatchingImageFilter<ImageType, ImageType> HistogramFilter;
+ typename HistogramFilter::Pointer filter = HistogramFilter::New();
+
+ filter->SetReferenceImage(iref);
+ filter->SetSourceImage(isrc);
+ filter->SetNumberOfMatchPoints(nmatch);
+ filter->ThresholdAtMeanIntensityOff();
+
+ *c->verbose << "Histogram matching #" << c->m_ImageStack.size()
+ << " to reference" << c->m_ImageStack.size() - 1 << endl;
+ *c->verbose << " Number of match points: " << filter->GetNumberOfMatchPoints() << endl;
+ *c->verbose << " Number of histogram levels: " << filter->GetNumberOfHistogramLevels() << endl;
+
+ filter->Update();
+
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class HistogramMatch<double, 2>;
+template class HistogramMatch<double, 3>;
+template class HistogramMatch<double, 4>;
diff --git a/Submodules/c3d/adapters/HistogramMatch.h b/Submodules/c3d/adapters/HistogramMatch.h
new file mode 100644
index 0000000..c2d1db8
--- /dev/null
+++ b/Submodules/c3d/adapters/HistogramMatch.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: HistogramMatch.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __HistogramMatch_h_
+#define __HistogramMatch_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class HistogramMatch : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ HistogramMatch(Converter *c) : c(c) {}
+
+ void operator()(int nmatch);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ImageERF.cxx b/Submodules/c3d/adapters/ImageERF.cxx
new file mode 100644
index 0000000..e7cb94e
--- /dev/null
+++ b/Submodules/c3d/adapters/ImageERF.cxx
@@ -0,0 +1,58 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ImageERF.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ImageERF.h"
+#include "vnl/vnl_erf.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ImageERF<TPixel, VDim>
+::operator() (double thresh, double scale)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Use iterator to apply erf
+ itk::ImageRegionIteratorWithIndex<ImageType>
+ it(input, input->GetBufferedRegion());
+ for(; !it.IsAtEnd(); ++it)
+ {
+ double x = it.Value();
+ double y = vnl_erf((x - thresh) / scale);
+ it.Set(y);
+ }
+
+ // Say what we are doing
+ *c->verbose << "Taking ERF of #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " y = erf((x - " << thresh << ") / scale)" << endl;
+
+ // Updated
+ input->Modified();
+}
+
+// Invocations
+template class ImageERF<double, 2>;
+template class ImageERF<double, 3>;
+template class ImageERF<double, 4>;
diff --git a/Submodules/c3d/adapters/ImageERF.h b/Submodules/c3d/adapters/ImageERF.h
new file mode 100644
index 0000000..bbdcc50
--- /dev/null
+++ b/Submodules/c3d/adapters/ImageERF.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ImageERF.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ImageERF_h_
+#define __ImageERF_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ImageERF : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ImageERF(Converter *c) : c(c) {}
+
+ void operator() (double thresh, double scale);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ImageGradient.cxx b/Submodules/c3d/adapters/ImageGradient.cxx
new file mode 100644
index 0000000..b2effd7
--- /dev/null
+++ b/Submodules/c3d/adapters/ImageGradient.cxx
@@ -0,0 +1,84 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ImageGradient.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ImageGradient.h"
+#include "itkGradientImageFilter.h"
+#include "itkVectorIndexSelectionCastImageFilter.h"
+#include "itkShiftScaleImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ImageGradient<TPixel, VDim>
+::operator() ()
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+ c->m_ImageStack.pop_back();
+
+ // Define the gradient image filter
+ typedef itk::GradientImageFilter<ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetUseImageSpacing(true);
+ filter->SetUseImageDirection(true);
+ filter->SetInput(img);
+ filter->Update();
+
+ // Report
+ *c->verbose << "Taking the gradient of #" << c->m_ImageStack.size()
+ << " (in physical space)" << std::endl;
+
+ // Break into components
+ for(int i = 0; i < VDim; i++)
+ {
+ // Extract component
+ typedef itk::VectorIndexSelectionCastImageFilter<
+ typename FilterType::OutputImageType, ImageType> CompFilterType;
+
+ typename CompFilterType::Pointer fltComp = CompFilterType::New();
+ fltComp->SetInput(filter->GetOutput());
+ fltComp->SetIndex(i);
+ fltComp->Update();
+
+ // Deal with RAS/LPS
+ if(VDim >= 3 && i < 2)
+ {
+ typedef itk::ShiftScaleImageFilter<ImageType, ImageType> ScaleFilter;
+ typename ScaleFilter::Pointer fltScale = ScaleFilter::New();
+ fltScale->SetInput(fltComp->GetOutput());
+ fltScale->SetScale(-1.0);
+ fltScale->Update();
+ c->m_ImageStack.push_back(fltScale->GetOutput());
+ }
+ else
+ {
+ c->m_ImageStack.push_back(fltComp->GetOutput());
+ }
+ }
+}
+
+// Invocations
+template class ImageGradient<double, 2>;
+template class ImageGradient<double, 3>;
+template class ImageGradient<double, 4>;
diff --git a/Submodules/c3d/adapters/ImageGradient.h b/Submodules/c3d/adapters/ImageGradient.h
new file mode 100644
index 0000000..231a1a2
--- /dev/null
+++ b/Submodules/c3d/adapters/ImageGradient.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ImageGradient_h_
+#define __ImageGradient_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ImageGradient : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ImageGradient(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ImageLaplacian.cxx b/Submodules/c3d/adapters/ImageLaplacian.cxx
new file mode 100644
index 0000000..d38523c
--- /dev/null
+++ b/Submodules/c3d/adapters/ImageLaplacian.cxx
@@ -0,0 +1,55 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ImageLaplacian.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ImageLaplacian.h"
+#include "itkLaplacianImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ImageLaplacian<TPixel, VDim>
+::operator() ()
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Describe what we are doing
+ *c->verbose << "Taking Laplacian of #" << c->m_ImageStack.size() << endl;
+
+ // Create a smoothing kernel and use it
+ typedef itk::LaplacianImageFilter<ImageType,ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(input);
+ filter->UseImageSpacingOn();
+ filter->Update();
+
+ // Save the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class ImageLaplacian<double, 2>;
+template class ImageLaplacian<double, 3>;
+template class ImageLaplacian<double, 4>;
diff --git a/Submodules/c3d/adapters/ImageLaplacian.h b/Submodules/c3d/adapters/ImageLaplacian.h
new file mode 100644
index 0000000..457a4af
--- /dev/null
+++ b/Submodules/c3d/adapters/ImageLaplacian.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ImageLaplacian.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ImageLaplacian_h_
+#define __ImageLaplacian_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ImageLaplacian : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ImageLaplacian(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/LabelOverlapMeasures.cxx b/Submodules/c3d/adapters/LabelOverlapMeasures.cxx
new file mode 100644
index 0000000..799ffed
--- /dev/null
+++ b/Submodules/c3d/adapters/LabelOverlapMeasures.cxx
@@ -0,0 +1,135 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LabelOverlapMeasures.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "LabelOverlapMeasures.h"
+#include "itkLabelOverlapMeasuresImageFilter.h"
+
+#include <iomanip>
+
+template <class TPixel, unsigned int VDim>
+void
+LabelOverlapMeasures<TPixel, VDim>
+::operator() ()
+{
+ // Get images from stack
+ size_t n = c->m_ImageStack.size();
+ if( n < 2 )
+ throw ConvertException( "Label overlap measures require two image inputs" );
+ ImagePointer target = c->m_ImageStack[n-1];
+ ImagePointer source = c->m_ImageStack[n-2];
+
+ // Create a short image for the labels
+ typedef itk::Image<short, VDim> LabelImageType;
+ typename LabelImageType::Pointer slabSource = LabelImageType::New();
+ typename LabelImageType::Pointer slabTarget = LabelImageType::New();
+
+ // Allocate the images
+ slabSource->SetRegions( source->GetBufferedRegion() );
+ slabSource->Allocate();
+
+ slabTarget->SetRegions( target->GetBufferedRegion() );
+ slabTarget->Allocate();
+
+ // Round off doubles to create labels
+ size_t nvS = slabSource->GetBufferedRegion().GetNumberOfPixels();
+ for( size_t i = 0; i < nvS; i++ )
+ {
+ slabSource->GetBufferPointer()[i]
+ = (short) ( source->GetBufferPointer()[i] + 0.5 );
+ }
+
+ size_t nvT = slabTarget->GetBufferedRegion().GetNumberOfPixels();
+ for( size_t i = 0; i < nvT; i++ )
+ {
+ slabTarget->GetBufferPointer()[i]
+ = (short) ( target->GetBufferPointer()[i] + 0.5 );
+ }
+
+ // Create the label statistics fltOverlap
+ typedef itk::LabelOverlapMeasuresImageFilter<LabelImageType> OverlapFilter;
+ typename OverlapFilter::Pointer filter = OverlapFilter::New();
+
+ // Set the inputs
+ filter->SetSourceImage( slabSource );
+ filter->SetTargetImage( slabTarget );
+
+ // Update the fltOverlap
+ filter->Update();
+
+ std::cout << " "
+ << "************ All Labels *************" << std::endl;
+ std::cout << std::setw( 10 ) << " "
+ << std::setw( 17 ) << "Total"
+ << std::setw( 17 ) << "Union (jaccard)"
+ << std::setw( 17 ) << "Mean (dice)"
+ << std::setw( 17 ) << "Volume sim."
+ << std::setw( 17 ) << "False negative"
+ << std::setw( 17 ) << "False positive" << std::endl;
+ std::cout << std::setw( 10 ) << " ";
+ std::cout << std::setw( 17 ) << filter->GetTotalOverlap();
+ std::cout << std::setw( 17 ) << filter->GetUnionOverlap();
+ std::cout << std::setw( 17 ) << filter->GetMeanOverlap();
+ std::cout << std::setw( 17 ) << filter->GetVolumeSimilarity();
+ std::cout << std::setw( 17 ) << filter->GetFalseNegativeError();
+ std::cout << std::setw( 17 ) << filter->GetFalsePositiveError();
+ std::cout << std::endl;
+
+ std::cout << " "
+ << "************ Individual Labels *************" << std::endl;
+ std::cout << std::setw( 10 ) << "Label"
+ << std::setw( 17 ) << "Target"
+ << std::setw( 17 ) << "Union (jaccard)"
+ << std::setw( 17 ) << "Mean (dice)"
+ << std::setw( 17 ) << "Volume sim."
+ << std::setw( 17 ) << "False negative"
+ << std::setw( 17 ) << "False positive" << std::endl;
+
+ typename OverlapFilter::MapType labelMap = filter->GetLabelSetMeasures();
+ typename OverlapFilter::MapType::const_iterator it;
+ for( it = labelMap.begin(); it != labelMap.end(); ++it )
+ {
+ if( (*it).first == 0 )
+ {
+ continue;
+ }
+
+ int label = (*it).first;
+
+ std::cout << std::setw( 10 ) << label;
+ std::cout << std::setw( 17 ) << filter->GetTargetOverlap( label );
+ std::cout << std::setw( 17 ) << filter->GetUnionOverlap( label );
+ std::cout << std::setw( 17 ) << filter->GetMeanOverlap( label );
+ std::cout << std::setw( 17 ) << filter->GetVolumeSimilarity( label );
+ std::cout << std::setw( 17 ) << filter->GetFalseNegativeError( label );
+ std::cout << std::setw( 17 ) << filter->GetFalsePositiveError( label );
+ std::cout << std::endl;
+ }
+
+}
+
+// Invocations
+template class LabelOverlapMeasures<double, 2>;
+template class LabelOverlapMeasures<double, 3>;
+template class LabelOverlapMeasures<double, 4>;
diff --git a/Submodules/c3d/adapters/LabelOverlapMeasures.h b/Submodules/c3d/adapters/LabelOverlapMeasures.h
new file mode 100644
index 0000000..d281aa9
--- /dev/null
+++ b/Submodules/c3d/adapters/LabelOverlapMeasures.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LabelOverlapMeasures.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __LabelOverlapMeasures_h_
+#define __LabelOverlapMeasures_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class LabelOverlapMeasures : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ LabelOverlapMeasures(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/LabelStatistics.cxx b/Submodules/c3d/adapters/LabelStatistics.cxx
new file mode 100644
index 0000000..9630cbe
--- /dev/null
+++ b/Submodules/c3d/adapters/LabelStatistics.cxx
@@ -0,0 +1,106 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LabelStatistics.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "LabelStatistics.h"
+#include "itkLabelStatisticsImageFilter.h"
+#include <set>
+
+template <class TPixel, unsigned int VDim>
+void
+LabelStatistics<TPixel, VDim>
+::operator() ()
+{
+ // Get images from stack
+ size_t n = c->m_ImageStack.size();
+ if(n < 2)
+ throw ConvertException("Label statistics require two image inputs");
+ ImagePointer label = c->m_ImageStack[n-1];
+ ImagePointer image = c->m_ImageStack[n-2];
+
+ // Create a short image for the labels
+ typedef itk::Image<short, VDim> LabelImageType;
+ typename LabelImageType::Pointer slab = LabelImageType::New();
+
+ // Allocate the image
+ slab->CopyInformation(label);
+ slab->SetRegions(label->GetBufferedRegion());
+ slab->Allocate();
+
+ // Accumulator of label values
+ std::set<short> sval;
+
+ // Round off doubles to create labels
+ size_t nv = label->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t i = 0; i < nv; i++)
+ {
+ slab->GetBufferPointer()[i] = (short) (label->GetBufferPointer()[i] + 0.5);
+ sval.insert(slab->GetBufferPointer()[i]);
+ }
+
+ // Create the label statistics filter
+ typedef itk::LabelStatisticsImageFilter<ImageType, LabelImageType> StatFilter;
+ typename StatFilter::Pointer fltStat = StatFilter::New();
+
+ // Set the inputs
+ fltStat->SetInput(image);
+ fltStat->SetLabelInput(slab);
+
+ // Update the filter
+ fltStat->Update();
+
+ // Get the voxel dimensions
+ double dim = 1.0;
+ for(size_t i = 0; i < VDim; i++)
+ dim *= label->GetSpacing()[i];
+
+ // Get the number of labels .
+ printf("LabelID Mean StdD Max Min Count Vol(mm^%1d) Extent(Vox)\n", VDim);
+ for(set<short>::iterator it = sval.begin(); it != sval.end(); ++it)
+ {
+ // printf("xxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx");
+ printf("%5i %10.5f %10.5f %10.5f %10.5f %10lu %12.3f ",
+ (int) *it,
+ fltStat->GetMean(*it),
+ fltStat->GetSigma(*it),
+ fltStat->GetMaximum(*it),
+ fltStat->GetMinimum(*it),
+ (long unsigned) fltStat->GetCount(*it),
+ fltStat->GetCount(*it) * dim);
+
+ // Bounding box
+ typename StatFilter::BoundingBoxType bbox = fltStat->GetBoundingBox(*it);
+ for(size_t i = 0; i < VDim; i++)
+ {
+ printf(" %5lu", 1 + bbox[i*2+1] - bbox[i*2]);
+ }
+ printf("\n");
+ }
+}
+
+// Invocations
+template class LabelStatistics<double, 2>;
+template class LabelStatistics<double, 3>;
+template class LabelStatistics<double, 4>;
+
diff --git a/Submodules/c3d/adapters/LabelStatistics.h b/Submodules/c3d/adapters/LabelStatistics.h
new file mode 100644
index 0000000..45248d5
--- /dev/null
+++ b/Submodules/c3d/adapters/LabelStatistics.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LabelStatistics.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __LabelStatistics_h_
+#define __LabelStatistics_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class LabelStatistics : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ LabelStatistics(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/LandmarksToSpheres.cxx b/Submodules/c3d/adapters/LandmarksToSpheres.cxx
new file mode 100644
index 0000000..a5746b8
--- /dev/null
+++ b/Submodules/c3d/adapters/LandmarksToSpheres.cxx
@@ -0,0 +1,175 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LandmarksToSpheres.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "LandmarksToSpheres.h"
+#include <cstdio>
+#include <fstream>
+
+template <class TPixel, unsigned int VDim>
+void
+LandmarksToSpheres<TPixel, VDim>
+::operator() (const char *fnland, double radius)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Open the landmarks file
+ ifstream fin(fnland);
+ if(!fin.is_open())
+ throw ConvertException("Unable to read file %s", fnland);
+
+ // Define a landmark
+ typedef itk::Point<double, VDim> PointType;
+ typedef std::pair<PointType, double> Landmark;
+ std::list<Landmark> lms;
+
+ // Line buffer
+ std::string line;
+ char *sub_buffer = new char[1024];
+
+ // Read each landmark in turn
+ while(std::getline(fin, line))
+ {
+ PointType x; double label = 0; int rc;
+
+ // Allow old format x y z label
+ if(VDim == 2)
+ rc = sscanf(line.c_str(), "%lf %lf %lf", &x[0], &x[1], &label);
+ else if(VDim == 3)
+ rc = sscanf(line.c_str(), "%lf %lf %lf %lf", &x[0], &x[1], &x[2], &label);
+ else if(VDim == 4)
+ rc = sscanf(line.c_str(), "%lf %lf %lf %lf %lf", &x[0], &x[1], &x[2], &x[3], &label);
+
+ // Successfully read the line
+ if (rc == VDim + 1)
+ {
+ lms.push_back(make_pair(x, label));
+ continue;
+ }
+
+ // Split the line into a vector and a label
+ rc = sscanf(line.c_str(), "%s %lf", sub_buffer, &label);
+ if(rc == 2)
+ {
+ // Try reading a real vector from buffer
+ try
+ {
+ RealVector vec = c->ReadRealVector(sub_buffer, true);
+ *(c->verbose) << vec << std::endl;
+ for(int i = 0; i < VDim; i++)
+ x[i] = vec[i];
+ lms.push_back(make_pair(x, label));
+ continue;
+ }
+ catch(...) {}
+ }
+
+ throw ConvertException("Error reading line %d in file %s", lms.size(), fnland);
+ }
+
+ fin.close();
+ delete sub_buffer;
+
+ // How many landmarks?
+ if(lms.size() == 0)
+ throw ConvertException("No landmarks were read from %s", fnland);
+ *c->verbose << "Placing " << lms.size() << " landmarks in #"
+ << c->m_ImageStack.size() << std::endl;
+
+ // Now we basically take the SNAP code for filling landmarks
+ for(typename std::list<Landmark>::iterator it = lms.begin();
+ it != lms.end(); it++)
+ {
+ // The landmark
+ PointType x = it->first;
+ double label = it->second;
+
+ // Convert the landmark center to a continuous index
+ itk::ContinuousIndex<double, VDim> idxCenter;
+ img->TransformRASPhysicalPointToContinuousIndex(x, idxCenter);
+
+ // We'll track the bounds for this bubble
+ itk::ContinuousIndex<double, VDim> idxUpper = idxCenter, idxLower = idxCenter;
+
+ // Iterate over all permutations of (-1,1) for x, y and z
+ unsigned int nperm = 1 << VDim;
+ for(unsigned int iperm = 0; iperm < nperm; iperm++)
+ {
+ // Create a point at the vertex of the cube/square with radius r
+ PointType xTest = x;
+ for(unsigned int i = 0; i < VDim; i++)
+ {
+ int sign = ((1 << i) & iperm) > 0 ? 1 : -1;
+ xTest[i] += sign * radius;
+ }
+
+ // Map point to index space
+ itk::ContinuousIndex<double, VDim> cTest;
+ img->TransformRASPhysicalPointToContinuousIndex(xTest, cTest);
+
+ // Update the bounding box
+ for(unsigned int i = 0; i < VDim; i++)
+ {
+ idxLower[i] = (idxLower[i] > cTest[i]) ? cTest[i] : idxLower[i];
+ idxUpper[i] = (idxUpper[i] < cTest[i]) ? cTest[i] : idxUpper[i];
+ }
+ }
+
+ // Create a region in which to create the bubble
+ SizeType szBubble; IndexType idxBubble;
+ for(unsigned int i = 0; i < VDim; i++)
+ {
+ idxBubble[i] = floor(idxLower[i]);
+ szBubble[i] = ceil(idxUpper[i]) - floor(idxLower[i]);
+ }
+ RegionType regBubble(idxBubble, szBubble);
+
+ // Crop by the image region and skip if fully outside
+ bool cropped = regBubble.Crop(img->GetBufferedRegion());
+ if(!cropped)
+ {
+ *c->verbose << " Landmark " << x << " fully outside image region" << std::endl;
+ continue;
+ }
+
+ // Iterate over this region
+ double rsq = radius * radius;
+ for(Iterator itb(img, regBubble); !itb.IsAtEnd(); ++itb)
+ {
+ // Get the physical point for this index
+ PointType pit;
+ img->TransformIndexToRASPhysicalPoint(itb.GetIndex(), pit);
+
+ // Check if it's in the bubble
+ if(pit.SquaredEuclideanDistanceTo(x) <= rsq)
+ itb.Set(label);
+ }
+ }
+}
+
+// Invocations
+template class LandmarksToSpheres<double, 2>;
+template class LandmarksToSpheres<double, 3>;
+template class LandmarksToSpheres<double, 4>;
diff --git a/Submodules/c3d/adapters/LandmarksToSpheres.h b/Submodules/c3d/adapters/LandmarksToSpheres.h
new file mode 100644
index 0000000..df94e65
--- /dev/null
+++ b/Submodules/c3d/adapters/LandmarksToSpheres.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LandmarksToSpheres.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __LandmarksToSpheres_h_
+#define __LandmarksToSpheres_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class LandmarksToSpheres : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ LandmarksToSpheres(Converter *c) : c(c) {}
+
+ void operator() (const char *fnland, double radius);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/LaplacianSharpening.cxx b/Submodules/c3d/adapters/LaplacianSharpening.cxx
new file mode 100644
index 0000000..6fbfefe
--- /dev/null
+++ b/Submodules/c3d/adapters/LaplacianSharpening.cxx
@@ -0,0 +1,53 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LaplacianSharpening.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "LaplacianSharpening.h"
+#include <itkLaplacianSharpeningImageFilter.h>
+
+template <class TPixel, unsigned int VDim>
+void
+LaplacianSharpening<TPixel, VDim>
+::operator() ()
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Create a filter
+ typedef itk::LaplacianSharpeningImageFilter<ImageType,ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(img);
+
+ *c->verbose << "Applying Laplacian sharpening to #" << c->m_ImageStack.size()-1 << std::endl;
+ filter->Update();
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class LaplacianSharpening<double, 2>;
+template class LaplacianSharpening<double, 3>;
+template class LaplacianSharpening<double, 4>;
diff --git a/Submodules/c3d/adapters/LaplacianSharpening.h b/Submodules/c3d/adapters/LaplacianSharpening.h
new file mode 100644
index 0000000..cd003be
--- /dev/null
+++ b/Submodules/c3d/adapters/LaplacianSharpening.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __LaplacianSharpening_h_
+#define __LaplacianSharpening_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class LaplacianSharpening : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ LaplacianSharpening(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/LevelSetSegmentation.cxx b/Submodules/c3d/adapters/LevelSetSegmentation.cxx
new file mode 100644
index 0000000..5b3d274
--- /dev/null
+++ b/Submodules/c3d/adapters/LevelSetSegmentation.cxx
@@ -0,0 +1,192 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LevelSetSegmentation.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "LevelSetSegmentation.h"
+#include "itkSegmentationLevelSetImageFilter.h"
+#include "itkSegmentationLevelSetFunction.h"
+#include "itkShiftScaleImageFilter.h"
+#include "itkImageAlgorithm.h"
+
+template<class TImageType, class TFeatureImageType = TImageType>
+class MyLevelSetFunction :
+ public itk::SegmentationLevelSetFunction<TImageType, TFeatureImageType>
+{
+public:
+ // All the regular filter stuff
+ typedef MyLevelSetFunction Self;
+ typedef itk::SegmentationLevelSetFunction<
+ TImageType, TFeatureImageType> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ // The input/output image types
+ typedef TImageType InputImageType;
+
+ // New pointer
+ itkNewMacro(Self);
+
+ // Calculate speed image - just copy the feature image
+ virtual void CalculateSpeedImage()
+ {
+ itk::ImageAlgorithm::Copy( this->GetFeatureImage(),
+ this->GetSpeedImage(),
+ this->GetFeatureImage()->GetRequestedRegion(),
+ this->GetFeatureImage()->GetRequestedRegion() );
+ }
+
+protected:
+
+ MyLevelSetFunction() {};
+ ~MyLevelSetFunction() {};
+
+ virtual void PrintSelf(std::ostream &os, itk::Indent indent) const
+ { os << indent << "MyLevelSetFunction"; }
+
+private:
+
+};
+
+
+template<class TInputImage,
+ class TFeatureImage,
+ class TOuputPixelType = double>
+class MyLevelSetFilter :
+ public itk::SegmentationLevelSetImageFilter<
+ TInputImage, TFeatureImage, TOuputPixelType>
+{
+public:
+ // All the regular filter stuff
+ typedef MyLevelSetFilter Self;
+ typedef itk::SegmentationLevelSetImageFilter<
+ TInputImage, TFeatureImage, TOuputPixelType> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ // Some typedefs of image types
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::OutputImageType OutputImageType;
+ typedef typename Superclass::FeatureImageType FeatureImageType;
+
+ // Input pixel values
+ typedef typename InputImageType::PixelType InputPixelType;
+
+ // Standard ITK Macros
+ itkTypeMacro(MyLevelSetFilter, itk::SegmentationLevelSetFilter);
+ itkNewMacro(Self);
+
+protected:
+ MyLevelSetFilter() {};
+ ~MyLevelSetFilter() {};
+
+ virtual void PrintSelf(std::ostream &os, itk::Indent indent) const
+ { os << indent << "MyLevelSetFilter"; }
+
+private:
+
+ // Unimplemented copy stuff
+ MyLevelSetFilter(const Self &s);
+ void operator=(const Self &s);
+};
+
+void DumpProgress(itk::Object *object, const itk::EventObject &obj, void *client_data)
+{
+ itk::ProcessObject *po = (itk::ProcessObject *)object;
+ cout << po->GetProgress() << endl;
+}
+
+
+template <class TPixel, unsigned int VDim>
+void
+LevelSetSegmentation<TPixel, VDim>
+::operator() (int nIter, const LevelSetParameters ¶m)
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < 2)
+ {
+ cerr << "Level set segmentation requires two images on the stack!" << endl;
+ throw -1;
+ }
+
+ // Get the last two images
+ ImagePointer i1 = c->m_ImageStack[c->m_ImageStack.size() - 1];
+ ImagePointer i2 = c->m_ImageStack[c->m_ImageStack.size() - 2];
+
+ // Report what the filter is doing
+ *c->verbose << "Running level set segmentation (";
+ *c->verbose << "#" << c->m_ImageStack.size() - 1 << " is speed, ";
+ *c->verbose << "#" << c->m_ImageStack.size() << " is init)" << endl;
+
+ // Create a segmentation filter
+ typedef MyLevelSetFilter<UnorientedImageType, UnorientedImageType, TPixel> SegFilter;
+ typename SegFilter::Pointer fltSegment = SegFilter::New();
+
+ // Set up the radius
+ itk::Size<VDim> rad; rad.Fill(1);
+
+ // Create the function
+ typedef MyLevelSetFunction<UnorientedImageType> SegFunction;
+ typename SegFunction::Pointer fnSegment = SegFunction::New();
+ fnSegment->SetCurvatureWeight(param.CurvatureWeight);
+ fnSegment->SetAdvectionWeight(param.AdvectionWeight);
+ fnSegment->SetPropagationWeight(1.0);
+ fnSegment->Initialize(rad);
+ fnSegment->SetSpeedImage(i2);
+
+ // Set the inputs to the segmentation filter
+ fltSegment->SetSegmentationFunction(fnSegment);
+ fltSegment->SetInput(i1);
+ fltSegment->SetFeatureImage(i2);
+ fltSegment->SetNumberOfLayers(3);
+ fltSegment->SetIsoSurfaceValue(0.0);
+ fltSegment->SetMaximumRMSError(1.0e-4);
+ fltSegment->SetNumberOfIterations(nIter);
+
+ *c->verbose << " NIterations: " << nIter << endl;
+ *c->verbose << " Curv Weight: " << param.CurvatureWeight << endl;
+ *c->verbose << " Adv Weight: " << param.AdvectionWeight << endl;
+
+ // Execute the filter
+ fltSegment->Update();
+
+ // Finally, map to an oriented image
+ typedef itk::ShiftScaleImageFilter<UnorientedImageType, ImageType> DummyFilter;
+ typename DummyFilter::Pointer fltDummy = DummyFilter::New();
+ fltDummy->SetInput(fltSegment->GetOutput());
+ fltDummy->SetScale(1.0);
+ fltDummy->SetShift(0.0);
+ fltDummy->Update();
+
+ // Take the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltDummy->GetOutput());
+
+ *c->verbose << "Level set done after" << fltSegment->GetElapsedIterations() << " iterations" << endl;
+}
+
+// Invocations
+template class LevelSetSegmentation<double, 2>;
+template class LevelSetSegmentation<double, 3>;
+template class LevelSetSegmentation<double, 4>;
diff --git a/Submodules/c3d/adapters/LevelSetSegmentation.h b/Submodules/c3d/adapters/LevelSetSegmentation.h
new file mode 100644
index 0000000..6b24000
--- /dev/null
+++ b/Submodules/c3d/adapters/LevelSetSegmentation.h
@@ -0,0 +1,60 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: LevelSetSegmentation.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __LevelSetSegmentation_h_
+#define __LevelSetSegmentation_h_
+
+#include "ConvertAdapter.h"
+
+struct LevelSetParameters
+{
+ double CurvatureWeight;
+ double AdvectionWeight;
+
+ LevelSetParameters()
+ {
+ CurvatureWeight = 0.2;
+ AdvectionWeight = 0.0;
+ }
+};
+
+template<class TPixel, unsigned int VDim>
+class LevelSetSegmentation : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ LevelSetSegmentation(Converter *c) : c(c) {}
+
+ void operator() (int nIter, const LevelSetParameters ¶m);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/MRFVote.cxx b/Submodules/c3d/adapters/MRFVote.cxx
new file mode 100644
index 0000000..586cbaf
--- /dev/null
+++ b/Submodules/c3d/adapters/MRFVote.cxx
@@ -0,0 +1,189 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MRFVote.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "MRFVote.h"
+#include "itkImageRandomNonRepeatingIteratorWithIndex.h"
+#include "itkNeighborhoodIterator.h"
+
+
+template <class TPixel, unsigned int VDim>
+void
+MRFVote<TPixel, VDim>
+::operator() (double beta, size_t niter, bool flagUseSplitLabelSet)
+{
+ // Create a maximum image
+ ImagePointer i0 = c->m_ImageStack[0];
+
+ // Number of imaged voted over
+ size_t nl = c->m_ImageStack.size();
+
+ // If this is in response to a split command, retrieve the label set
+ typename Converter::LabelSet lset;
+ if(flagUseSplitLabelSet)
+ {
+ if(nl != c->GetSplitLabelSet().size())
+ throw ConvertException(
+ "Merge failed: number of images on the stack (%i) different "
+ "from the number of split labels (%i)",
+ nl, c->GetSplitLabelSet().size());
+ lset = c->GetSplitLabelSet();
+ }
+
+ // Otherwise, the label mapping is identity
+ else
+ {
+ for(size_t i = 0; i < nl; i++)
+ lset.push_back((double) i);
+ }
+
+ // Create a vote image (short for now)
+ ImagePointer ilabel = ImageType::New();
+ ilabel->SetRegions(i0->GetBufferedRegion());
+ ilabel->SetOrigin(i0->GetOrigin());
+ ilabel->SetSpacing(i0->GetSpacing());
+ ilabel->SetDirection(i0->GetDirection());
+ ilabel->Allocate();
+ ilabel->FillBuffer(lset[0]);
+
+ // Say something
+ *c->verbose << "Collapsing " << c->m_ImageStack.size() <<
+ " images into a multi-label image via maximum voting" << endl;
+
+ // For each of the images, update the vote
+ for(size_t j = 1; j < nl; j++)
+ {
+ // Get the next image pointer
+ ImagePointer ij = c->m_ImageStack[j];
+
+ // Check the image dimensions
+ if(ij->GetBufferedRegion() != ilabel->GetBufferedRegion())
+ throw ConvertException("All voting images must have same dimensions");
+
+ // Pairwise voting
+ size_t n = ilabel->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t k = 0; k < n; k++)
+ {
+ double ibest = i0->GetBufferPointer()[k];
+ if(ij->GetBufferPointer()[k] > ibest)
+ {
+ ibest = ij->GetBufferPointer()[k];
+ ilabel->GetBufferPointer()[k] = j;
+ }
+ }
+ }
+
+ // The result of the voting gives us the initial guess. Now perform
+ // ICM (Besag 1986, http://www.stat.duke.edu/~scs/Courses/Stat376/Papers/GibbsFieldEst/BesagDirtyPicsJRSSB1986.pdf)
+
+ // Define an inner region (no boundary voxels)
+ RegionType r_inner = ilabel->GetBufferedRegion();
+ for(size_t d = 0; d < VDim; d++)
+ {
+ r_inner.SetIndex(d, r_inner.GetIndex(d)+1);
+ r_inner.SetSize(d, r_inner.GetSize(d)-2);
+ }
+
+ // Allocate array for neighborhood histogram
+ double *nhist = new double[nl];
+
+ // Create neighborhood iterator
+ typedef itk::NeighborhoodIterator<ImageType> NeighborIterType;
+ typename NeighborIterType::RadiusType radius;
+ radius.Fill(1);
+ NeighborIterType nit(radius, ilabel, r_inner);
+ nit.SetNeedToUseBoundaryCondition(false);
+
+ // Do some iterations
+ for(size_t q = 0; q < niter; q++)
+ {
+ // Keep track of number of updates
+ size_t n_upd = 0;
+
+ // Iterate over the inner region
+ typedef itk::ImageRandomNonRepeatingIteratorWithIndex<ImageType> RandIterType;
+ RandIterType rit(ilabel, r_inner);
+ rit.SetNumberOfSamples(r_inner.GetNumberOfPixels());
+ for(; !rit.IsAtEnd(); ++rit)
+ {
+ // Current pixel value
+ TPixel x_i = rit.Value();
+
+ // Clear the neighborhood histogram
+ for(size_t j = 0; j < nl; j++)
+ nhist[j] = 0.0;
+
+ // Make up for the fact that the current voxel will be counted
+ nhist[(int)x_i] = -1;
+
+ // Iterate the neighborhood
+ nit.SetLocation(rit.GetIndex()); // Slow , change later
+ for(size_t k = 0; k < nit.Size(); k++)
+ nhist[(int)nit.GetPixel(k)]++;
+
+ // For each candidate label value, compute conditional posterior
+ int j_best = x_i;
+ double post_best = 1e100;
+ for(int j = 0; j < nl; j++)
+ {
+ double p = c->m_ImageStack[j]->GetPixel(rit.GetIndex());
+ if(p > 0)
+ {
+ double likelihood = -log(p);
+ double prior = - beta * nhist[j];
+ double post = likelihood + prior;
+ if(post_best > post)
+ { post_best = post; j_best=j; }
+ }
+ }
+ if(x_i != j_best)
+ {
+ rit.Set(j_best);
+ n_upd++;
+ }
+ }
+ if(n_upd == 0)
+ {
+ *c->verbose << " Early convergence after " << q << " iterations" << endl;
+ break;
+ }
+ }
+
+ // Relabel the image using /split labels
+ if(flagUseSplitLabelSet)
+ for(size_t k = 0; k < ilabel->GetBufferedRegion().GetNumberOfPixels(); k++)
+ ilabel->GetBufferPointer()[k] = lset[(int) ilabel->GetBufferPointer()[k]];
+
+ // Clear histogram
+ delete[] nhist;
+
+ // Put result on stack
+ c->m_ImageStack.clear();
+ c->m_ImageStack.push_back(ilabel);
+}
+
+// Invocations
+template class MRFVote<double, 2>;
+template class MRFVote<double, 3>;
+template class MRFVote<double, 4>;
diff --git a/Submodules/c3d/adapters/MRFVote.h b/Submodules/c3d/adapters/MRFVote.h
new file mode 100644
index 0000000..11bea3a
--- /dev/null
+++ b/Submodules/c3d/adapters/MRFVote.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MRFVote.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __MRFVote_h_
+#define __MRFVote_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class MRFVote : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ MRFVote(Converter *c) : c(c) {}
+
+ void operator() (double beta, size_t niter, bool flagUseSplitLabelSet);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/MathematicalMorphology.cxx b/Submodules/c3d/adapters/MathematicalMorphology.cxx
new file mode 100644
index 0000000..9b73625
--- /dev/null
+++ b/Submodules/c3d/adapters/MathematicalMorphology.cxx
@@ -0,0 +1,81 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MathematicalMorphology.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "MathematicalMorphology.h"
+#include "itkBinaryDilateImageFilter.h"
+#include "itkBinaryErodeImageFilter.h"
+#include "itkBinaryBallStructuringElement.h"
+
+template <class TPixel, unsigned int VDim>
+void
+MathematicalMorphology<TPixel, VDim>
+::operator() (bool erode, TPixel value, SizeType radius)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ *c->verbose << "Applying " << (erode ? "erosion" : "dilation") << " to #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Foreground value : " << value << endl;
+ *c->verbose << " Ball radius : " << radius << endl;
+
+ // Define the structuring element
+ typedef itk::BinaryBallStructuringElement<TPixel, VDim> Element;
+ Element elt;
+ elt.SetRadius(radius);
+ elt.CreateStructuringElement();
+
+ // Chose the right filter
+ typedef itk::BinaryMorphologyImageFilter<ImageType, ImageType, Element> FilterType;
+ ImagePointer output;
+ if(erode)
+ {
+ typedef itk::BinaryErodeImageFilter<ImageType, ImageType, Element> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(img);
+ filter->SetErodeValue(value);
+ filter->SetKernel(elt);
+ filter->Update();
+ output = filter->GetOutput();
+ }
+ else
+ {
+ typedef itk::BinaryDilateImageFilter<ImageType, ImageType, Element> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(img);
+ filter->SetDilateValue(value);
+ filter->SetKernel(elt);
+ filter->Update();
+ output = filter->GetOutput();
+ }
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(output);
+}
+
+// Invocations
+template class MathematicalMorphology<double, 2>;
+template class MathematicalMorphology<double, 3>;
+template class MathematicalMorphology<double, 4>;
diff --git a/Submodules/c3d/adapters/MathematicalMorphology.h b/Submodules/c3d/adapters/MathematicalMorphology.h
new file mode 100644
index 0000000..a6a186a
--- /dev/null
+++ b/Submodules/c3d/adapters/MathematicalMorphology.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MathematicalMorphology.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __MathematicalMorphology_h_
+#define __MathematicalMorphology_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class MathematicalMorphology : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ MathematicalMorphology(Converter *c) : c(c) {}
+
+ void operator() (bool erode, TPixel value, SizeType size);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/MeanFilter.cxx b/Submodules/c3d/adapters/MeanFilter.cxx
new file mode 100644
index 0000000..e77c757
--- /dev/null
+++ b/Submodules/c3d/adapters/MeanFilter.cxx
@@ -0,0 +1,61 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MeanFilter.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "MeanFilter.h"
+#include <itkMeanImageFilter.h>
+
+template <class TPixel, unsigned int VDim>
+void
+MeanFilter<TPixel, VDim>
+::operator() (const SizeType &radius)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Do some processing ...
+ typedef itk::MeanImageFilter<ImageType, ImageType> FilterType;
+ typename FilterType::RadiusType rad;
+
+ for(int i = 0; i < VDim; i++)
+ rad[i] = radius[i];
+
+ *c->verbose << "Applying mean filter with radius " << rad << " to #" << c->m_ImageStack.size() << std::endl;
+
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetRadius(rad);
+ filter->SetInput(img);
+ filter->Update();
+
+ // ImagePointer result = ...;
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class MeanFilter<double, 2>;
+template class MeanFilter<double, 3>;
+template class MeanFilter<double, 4>;
diff --git a/Submodules/c3d/adapters/MeanFilter.h b/Submodules/c3d/adapters/MeanFilter.h
new file mode 100644
index 0000000..f92dde7
--- /dev/null
+++ b/Submodules/c3d/adapters/MeanFilter.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __MeanFilter_h_
+#define __MeanFilter_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class MeanFilter : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ MeanFilter(Converter *c) : c(c) {}
+
+ void operator() (const SizeType &radius);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/MedianFilter.cxx b/Submodules/c3d/adapters/MedianFilter.cxx
new file mode 100644
index 0000000..403e2ff
--- /dev/null
+++ b/Submodules/c3d/adapters/MedianFilter.cxx
@@ -0,0 +1,58 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MedianFilter.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "MedianFilter.h"
+#include "itkMedianImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+MedianFilter<TPixel, VDim>
+::operator() (const SizeType &radius)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Report
+ *c->verbose << "Applying median filtering to #" << c->m_ImageStack.size()
+ << " with neighborhood radius " << radius << std::endl;
+
+ // Do some processing ...
+ typedef itk::MedianImageFilter<ImageType, ImageType> MedianFilterType;
+ typename MedianFilterType::Pointer filter = MedianFilterType::New();
+ filter->SetRadius(radius);
+ filter->SetInput(img);
+ filter->Update();
+
+ ImagePointer result = filter->GetOutput();
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(result);
+}
+
+// Invocations
+template class MedianFilter<double, 2>;
+template class MedianFilter<double, 3>;
+template class MedianFilter<double, 4>;
diff --git a/Submodules/c3d/adapters/MedianFilter.h b/Submodules/c3d/adapters/MedianFilter.h
new file mode 100644
index 0000000..42e8e0c
--- /dev/null
+++ b/Submodules/c3d/adapters/MedianFilter.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __MedianFilter_h_
+#define __MedianFilter_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class MedianFilter : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ MedianFilter(Converter *c) : c(c) {}
+
+ void operator() (const SizeType &radius);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/MixtureModel.cxx b/Submodules/c3d/adapters/MixtureModel.cxx
new file mode 100644
index 0000000..41eabf9
--- /dev/null
+++ b/Submodules/c3d/adapters/MixtureModel.cxx
@@ -0,0 +1,124 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MixtureModel.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "MixtureModel.h"
+
+#include "itkImageToListSampleAdaptor.h"
+#include "itkGaussianMixtureModelComponent.h"
+#include "itkExpectationMaximizationMixtureModelEstimator.h"
+#include "itkComposeImageFilter.h"
+
+using namespace itk;
+using namespace itk::Statistics;
+using namespace std;
+
+template <class TPixel, unsigned int VDim>
+void
+MixtureModel<TPixel, VDim>
+::operator() (std::vector<double> mu, std::vector<double> sigma)
+{
+ // Get image from stack and create a statistics sample
+ ImagePointer img = c->m_ImageStack.back();
+
+ // creat a new image with array pixel type from the source
+ typedef itk::FixedArray< TPixel, 1 > ArrayPixelType ;
+ typedef itk::Image< ArrayPixelType, VDim > ArrayPixelImageType ;
+ typedef itk::ComposeImageFilter< ImageType, ArrayPixelImageType > ImageCastFilterType ;
+ typename ImageCastFilterType::Pointer castFilter = ImageCastFilterType::New() ;
+ castFilter->SetInput(img);
+ castFilter->Update() ;
+
+ typedef ImageToListSampleAdaptor<ArrayPixelImageType> DataSampleType;
+ typename DataSampleType::Pointer sample = DataSampleType::New();
+ sample->SetImage(castFilter->GetOutput());
+
+ // Component definition
+ typedef GaussianMixtureModelComponent<DataSampleType> ComponentType;
+ typedef typename ComponentType::Pointer ComponentPointer;
+ vector<ComponentPointer> comps;
+
+ // Create components and initial proportions
+ itk::Array<double> initprop(mu.size());
+ for(size_t i = 0; i < mu.size(); i++)
+ {
+ ComponentPointer cmp = ComponentType::New();
+ cmp->SetSample(sample);
+
+ itk::Array<double> param(2);
+ param[0] = mu[i];
+ param[1] = sigma[i] * sigma[i];
+ cmp->SetParameters(param);
+ comps.push_back(cmp);
+
+ initprop[i] = 1.0 / mu.size();
+ }
+
+ // Explain what we are doing
+ *c->verbose << "Running Gaussian Mixture Modeling on #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Initial Parameters : " << endl;
+ for(size_t i = 0; i < mu.size(); i++)
+ {
+ *c->verbose << " " <<
+ "Class " << i << ": " <<
+ "mu = " << mu[i] << "; " <<
+ "sigma = " << sigma[i] << "; " <<
+ "alpha = " << initprop[i] << "; " << endl;
+
+ }
+
+ typedef ExpectationMaximizationMixtureModelEstimator<DataSampleType> EstimatorType;
+ typename EstimatorType::Pointer estimator = EstimatorType::New();
+
+ estimator->SetSample(sample);
+ estimator->SetMaximumIteration(100);
+ estimator->SetInitialProportions(initprop);
+ for(size_t i = 0; i < mu.size(); i++)
+ estimator->AddComponent(comps[i]);
+
+
+ estimator->Update();
+
+ *c->verbose << " Estimated Parameters : " << endl;
+ for(size_t i = 0; i < mu.size(); i++)
+ {
+ *c->verbose << " " <<
+ "Class " << i << ": " <<
+ "mu = " << comps[i]->GetFullParameters()[0] << "; " <<
+ "sigma = " << sqrt(comps[i]->GetFullParameters()[1]) << "; " <<
+ "alpha = " << (estimator->GetProportions())[i] << "; " << endl;
+ }
+
+ // Do some processing ...
+ // ImagePointer result = ...;
+
+ // Put result on stack
+ // c->m_ImageStack.pop_back();
+ // c->m_ImageStack.push_back(result);
+}
+
+// Invocations
+template class MixtureModel<double, 2>;
+template class MixtureModel<double, 3>;
+template class MixtureModel<double, 4>;
diff --git a/Submodules/c3d/adapters/MixtureModel.h b/Submodules/c3d/adapters/MixtureModel.h
new file mode 100644
index 0000000..bd562f4
--- /dev/null
+++ b/Submodules/c3d/adapters/MixtureModel.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MixtureModel.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __MixtureModel_h_
+#define __MixtureModel_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class MixtureModel : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ MixtureModel(Converter *c) : c(c) {}
+
+ void operator() (std::vector<double> mu, std::vector<double> sigma);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/MultiplyImages.cxx b/Submodules/c3d/adapters/MultiplyImages.cxx
new file mode 100644
index 0000000..e287171
--- /dev/null
+++ b/Submodules/c3d/adapters/MultiplyImages.cxx
@@ -0,0 +1,66 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MultiplyImages.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "MultiplyImages.h"
+#include "itkMultiplyImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+MultiplyImages<TPixel, VDim>
+::operator() ()
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < 2)
+ {
+ cerr << "Binary operations require two images on the stack" << endl;
+ throw -1;
+ }
+
+ // Get the last two images
+ ImagePointer i1 = c->m_ImageStack[c->m_ImageStack.size() - 1];
+ ImagePointer i2 = c->m_ImageStack[c->m_ImageStack.size() - 2];
+
+ // Write something
+ *c->verbose << "Multiplying #" << c->m_ImageStack.size() - 1
+ << " by #" << c->m_ImageStack.size() << endl;
+
+ // Perform the multiplication
+ typedef itk::MultiplyImageFilter<ImageType, ImageType, ImageType> FilterType;
+ typename FilterType::Pointer flt = FilterType::New();
+ flt->SetInput1(i1);
+ flt->SetInput2(i2);
+ flt->Update();
+
+ // Replace the images with the product
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(flt->GetOutput());
+
+}
+
+// Invocations
+template class MultiplyImages<double, 2>;
+template class MultiplyImages<double, 3>;
+template class MultiplyImages<double, 4>;
diff --git a/Submodules/c3d/adapters/MultiplyImages.h b/Submodules/c3d/adapters/MultiplyImages.h
new file mode 100644
index 0000000..4a8624e
--- /dev/null
+++ b/Submodules/c3d/adapters/MultiplyImages.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MultiplyImages.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __MultiplyImages_h_
+#define __MultiplyImages_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class MultiplyImages : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ MultiplyImages(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/NormalizeLocalWindow.cxx b/Submodules/c3d/adapters/NormalizeLocalWindow.cxx
new file mode 100644
index 0000000..d381dd4
--- /dev/null
+++ b/Submodules/c3d/adapters/NormalizeLocalWindow.cxx
@@ -0,0 +1,128 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: NormalizeLocalWindow.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "NormalizeLocalWindow.h"
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include "itkBinaryFunctorImageFilter.h"
+#include "itkVectorImage.h"
+#include "VectorImageTools.h"
+
+template <class TInputPixel, class TOutputPixel>
+class NormalizeLocalWindowImageToStatsFunctor
+{
+public:
+ // The first parameter is the image, and the second parameter is the mask
+ TOutputPixel operator() (const TInputPixel &x, const TInputPixel &m)
+ {
+ TOutputPixel c;
+ c[0] = m;
+ c[1] = x * m;
+ c[2] = x * x * m;
+ return c;
+ }
+
+ bool operator != (const NormalizeLocalWindowImageToStatsFunctor<TInputPixel, TOutputPixel> &other)
+ { return false; }
+};
+
+template <class TInputPixel1, class TInputPixel2, class TOutputPixel>
+class NormalizedLocalWindowStatsToResultFunctor
+{
+public:
+ typedef NormalizedLocalWindowStatsToResultFunctor<TInputPixel1, TInputPixel2, TOutputPixel> Self;
+
+ TOutputPixel operator() (const TInputPixel1 &c, const TInputPixel2 &x)
+ {
+ // Get the raw data
+ double sum_m = c[0];
+ double sum_mx = c[1];
+ double sum_mxx = c[2];
+
+ // If no mask, return 0
+ if(sum_m == 0)
+ return 0;
+
+ // Compute the standard deviation
+ double var_xx = (sum_mxx - sum_mx * sum_mx / sum_m) / sum_m;
+
+ // Compute the difference
+ return (x - sum_mx / sum_m) / sqrt(var_xx);
+ }
+
+ bool operator != (const Self &other)
+ { return false; }
+};
+
+
+template <class TPixel, unsigned int VDim>
+void
+NormalizeLocalWindow<TPixel, VDim>
+::operator() (SizeType radius)
+{
+ // This filter replaces the intensity g(x) at each voxel by (g-mu)/sigma, where mu and sigma
+ // are the mean and standard deviation of intensity in a neighborhood. To compute the filter,
+ // we need to compute these sliding window statistics
+ //
+ // The first input is the image and the second input is a mask to which the computation of the
+ // statistics is restricted. The statistics are only computed inside of the mask
+ // Get two images from stack
+ ImagePointer image = c->m_ImageStack[c->m_ImageStack.size()-2];
+ ImagePointer mask = c->m_ImageStack[c->m_ImageStack.size()-1];
+
+ // Alternative approach
+ typedef itk::Vector<TPixel, 3> StatsVector;
+ typedef itk::Image<StatsVector, VDim> StatsImage;
+ typedef NormalizeLocalWindowImageToStatsFunctor<TPixel,StatsVector> StatsFunctor;
+ typedef itk::BinaryFunctorImageFilter<ImageType, ImageType, StatsImage, StatsFunctor> StatsFilter;
+ typename StatsFilter::Pointer fltStats = StatsFilter::New();
+ fltStats->SetInput1(image);
+ fltStats->SetInput2(mask);
+ fltStats->Update();
+
+ // Masquerade as a vector image
+ typedef itk::VectorImage<TPixel, VDim> VecImageType;
+ typename VecImageType::Pointer vecImage = WrapImageOfVectorAsVectorImage(fltStats->GetOutput());
+
+ // Perform the accumulation
+ typename VecImageType::Pointer imgAccum = AccumulateNeighborhoodSumsInPlace(vecImage.GetPointer(), radius);
+
+ typedef NormalizedLocalWindowStatsToResultFunctor<typename VecImageType::PixelType, TPixel, TPixel> NormalizeFunctor;
+ typedef itk::BinaryFunctorImageFilter<VecImageType, ImageType, ImageType, NormalizeFunctor> NormalizeFilter;
+ typename NormalizeFilter::Pointer fltNorm = NormalizeFilter::New();
+
+ fltNorm->SetInput1(imgAccum);
+ fltNorm->SetInput2(image);
+ fltNorm->Update();
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltNorm->GetOutput());
+}
+
+// Invocations
+template class NormalizeLocalWindow<double, 2>;
+template class NormalizeLocalWindow<double, 3>;
+template class NormalizeLocalWindow<double, 4>;
diff --git a/Submodules/c3d/adapters/NormalizeLocalWindow.h b/Submodules/c3d/adapters/NormalizeLocalWindow.h
new file mode 100644
index 0000000..48b6395
--- /dev/null
+++ b/Submodules/c3d/adapters/NormalizeLocalWindow.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: NormalizeLocalWindow.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __NormalizeLocalWindow_h_
+#define __NormalizeLocalWindow_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class NormalizeLocalWindow : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ NormalizeLocalWindow(Converter *c) : c(c) {}
+
+ void operator() (SizeType radius);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/NormalizedCrossCorrelation.cxx b/Submodules/c3d/adapters/NormalizedCrossCorrelation.cxx
new file mode 100644
index 0000000..889ef6a
--- /dev/null
+++ b/Submodules/c3d/adapters/NormalizedCrossCorrelation.cxx
@@ -0,0 +1,131 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: NormalizedCrossCorrelation.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "NormalizedCrossCorrelation.h"
+#include "itkConstNeighborhoodIterator.h"
+
+#include "itkUnaryFunctorImageFilter.h"
+#include "itkBinaryFunctorImageFilter.h"
+#include "itkMeanImageFilter.h"
+
+#include "itkImageRegionSplitterDirection.h"
+#include "itkTimeProbe.h"
+
+#include "itkImageFileWriter.h"
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include "VectorImageTools.h"
+
+
+template <class TInputPixel, class TOutputPixel>
+class ImageToStatsFunctor
+{
+public:
+ TOutputPixel operator() (const TInputPixel &a, const TInputPixel &b)
+ {
+ TOutputPixel c;
+ c[0] = a;
+ c[1] = b;
+ c[2] = a*b;
+ c[3] = a*a;
+ c[4] = b*b;
+ c[5] = 1.0;
+ return c;
+ }
+
+ bool operator != (const ImageToStatsFunctor<TInputPixel, TOutputPixel> &other)
+ { return false; }
+};
+
+template <class TInputPixel, class TOutputPixel>
+class StatsToNCCFunctor
+{
+public:
+ StatsToNCCFunctor(int n = 0) { this->m_N = n; }
+
+ TOutputPixel operator() (const TInputPixel &c)
+ {
+ double a = c[0], b = c[1], ab = c[2], a2 = c[3], b2 = c[4], n = c[5];
+ double Sab = (ab - a * b / n);
+ double Saa = (a2 - a * a / n);
+ double Sbb = (b2 - b * b / n);
+ return Sab / sqrt(Saa * Sbb);
+ }
+
+ bool operator != (const StatsToNCCFunctor<TInputPixel, TOutputPixel> &other)
+ { return m_N != other.m_N; }
+
+private:
+ int m_N;
+};
+
+template <class TPixel, unsigned int VDim>
+void
+NormalizedCrossCorrelation<TPixel, VDim>
+::operator() (itk::Size<VDim> radius)
+{
+ // Get two images from stack
+ ImagePointer i1 = c->m_ImageStack[c->m_ImageStack.size()-1];
+ ImagePointer i2 = c->m_ImageStack[c->m_ImageStack.size()-2];
+
+ // Alternative approach
+ typedef itk::Vector<TPixel, 6> StatsVector;
+ typedef itk::Image<StatsVector, VDim> StatsImage;
+ typedef ImageToStatsFunctor<TPixel,StatsVector> StatsFunctor;
+ typedef itk::BinaryFunctorImageFilter<ImageType, ImageType, StatsImage, StatsFunctor> StatsFilter;
+ typename StatsFilter::Pointer fltStats = StatsFilter::New();
+ fltStats->SetInput1(i1);
+ fltStats->SetInput2(i2);
+ fltStats->Update();
+
+ // Masquerade as a vector image
+ typedef itk::VectorImage<TPixel, VDim> VecImageType;
+ typename VecImageType::Pointer vecImage = WrapImageOfVectorAsVectorImage(fltStats->GetOutput());
+
+ // Perform the accumulation
+ typename VecImageType::Pointer imgAccum = AccumulateNeighborhoodSumsInPlace(vecImage.GetPointer(), radius);
+
+ typedef StatsToNCCFunctor<typename VecImageType::PixelType, TPixel> NCCFunctor;
+ typedef itk::UnaryFunctorImageFilter<VecImageType, ImageType, NCCFunctor> NCCFilter;
+ typename NCCFilter::Pointer fltNCC = NCCFilter::New();
+
+ int n = 1;
+ for(int i = 0; i < VDim; i++)
+ n *= 2 * radius[i] + 1;
+
+ NCCFunctor funk(n);
+ fltNCC->SetInput(imgAccum);
+ fltNCC->SetFunctor(funk);
+ fltNCC->Update();
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltNCC->GetOutput());
+}
+
+// Invocations
+template class NormalizedCrossCorrelation<double, 2>;
+template class NormalizedCrossCorrelation<double, 3>;
+template class NormalizedCrossCorrelation<double, 4>;
diff --git a/Submodules/c3d/adapters/NormalizedCrossCorrelation.h b/Submodules/c3d/adapters/NormalizedCrossCorrelation.h
new file mode 100644
index 0000000..dbfa99c
--- /dev/null
+++ b/Submodules/c3d/adapters/NormalizedCrossCorrelation.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: NormalizedCrossCorrelation.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __NormalizedCrossCorrelation_h_
+#define __NormalizedCrossCorrelation_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class NormalizedCrossCorrelation : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ NormalizedCrossCorrelation(Converter *c) : c(c) {}
+
+ void operator() (itk::Size<VDim> radius);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/OverlayLabelImage.cxx b/Submodules/c3d/adapters/OverlayLabelImage.cxx
new file mode 100644
index 0000000..20cd2fd
--- /dev/null
+++ b/Submodules/c3d/adapters/OverlayLabelImage.cxx
@@ -0,0 +1,90 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: OverlayLabelImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "OverlayLabelImage.h"
+
+template <class TPixel, unsigned int VDim>
+void
+OverlayLabelImage<TPixel, VDim>
+::operator() (const char *fnLUT, double opacity)
+{
+ // Check consistency
+ if(!c->CheckStackSameDimensions(2))
+ throw ConvertException(
+ "Images have inconsistent dimensions for -overlay-label-image");
+
+ // Get images off the stack
+ size_t n = c->m_ImageStack.size();
+ ImagePointer imLabel = c->m_ImageStack[n-1];
+ ImagePointer imGray = c->m_ImageStack[n-2];
+
+ // Load the lookup table
+ typename ImageConverter<TPixel, VDim>::LabelToRGBAMap lmap =
+ c->ReadLabelToRGBAMap(fnLUT);
+
+ // Explain what we are doing
+ *c->verbose << "Overlaying labels in #" << (n-1) << " over image #" << (n-2) << endl;
+
+ // Create output RGB images
+ ImagePointer rgb[3];
+ for(size_t d = 0; d < 3; d++)
+ {
+ // Allocate image
+ rgb[d] = ImageType::New();
+ rgb[d]->SetRegions(imGray->GetBufferedRegion());
+ rgb[d]->CopyInformation(imGray);
+ rgb[d]->Allocate();
+
+ // Iterate
+ ConstIterator ig(imGray, imGray->GetBufferedRegion());
+ ConstIterator il(imLabel, imLabel->GetBufferedRegion());
+ Iterator ic(rgb[d], rgb[d]->GetBufferedRegion());
+ for(; !ig.IsAtEnd(); ++ig, ++il, ++ic)
+ {
+ // Find the color entry
+ typename ImageConverter<TPixel, VDim>::LabelToRGBAMap::iterator ilut =
+ lmap.find(il.Get());
+ vnl_vector_fixed<double, 4> color(0.0);
+ if(ilut != lmap.end())
+ color = ilut->second;
+
+ // Blend (like OpenGL)
+ double alpha = color[3] * opacity;
+ ic.Set((1-alpha) * ig.Get() + alpha * color[d]);
+ }
+ }
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(rgb[0]);
+ c->m_ImageStack.push_back(rgb[1]);
+ c->m_ImageStack.push_back(rgb[2]);
+}
+
+// Invocations
+template class OverlayLabelImage<double, 2>;
+template class OverlayLabelImage<double, 3>;
+template class OverlayLabelImage<double, 4>;
diff --git a/Submodules/c3d/adapters/OverlayLabelImage.h b/Submodules/c3d/adapters/OverlayLabelImage.h
new file mode 100644
index 0000000..83b9a3e
--- /dev/null
+++ b/Submodules/c3d/adapters/OverlayLabelImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: OverlayLabelImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __OverlayLabelImage_h_
+#define __OverlayLabelImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class OverlayLabelImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ OverlayLabelImage(Converter *c) : c(c) {}
+
+ void operator() (const char *fnLUT, double opacity);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/PadImage.cxx b/Submodules/c3d/adapters/PadImage.cxx
new file mode 100644
index 0000000..d08e04d
--- /dev/null
+++ b/Submodules/c3d/adapters/PadImage.cxx
@@ -0,0 +1,78 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: PadImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "PadImage.h"
+#include <itkConstantPadImageFilter.h>
+
+template <class TPixel, unsigned int VDim>
+void
+PadImage<TPixel, VDim>
+::operator() (IndexType padExtentLower, IndexType padExtentUpper, float padValue)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ typedef itk::ConstantPadImageFilter< ImageType, ImageType > ConstantPad;
+ typename ConstantPad::Pointer padFilter = ConstantPad::New();
+
+ // Pad first three dimensions only
+ itk::SizeValueType lowerBound[VDim];
+ itk::SizeValueType upperBound[VDim];
+
+ for (int i = 0; i < 3 && i < VDim; i++) {
+ lowerBound[i] = padExtentLower[i];
+ upperBound[i] = padExtentUpper[i];
+ }
+
+ for (unsigned int i = 3; i < VDim; i++) {
+ lowerBound[i] = 0;
+ upperBound[i] = 0;
+ }
+
+ padFilter->SetPadLowerBound(lowerBound);
+ padFilter->SetPadUpperBound(upperBound);
+
+ padFilter->SetConstant(static_cast<typename ImageType::PixelType>(padValue));
+
+ padFilter->SetInput(img);
+
+ padFilter->Update();
+
+ ImagePointer output = padFilter->GetOutput();
+
+ // ITK 3.20 fixes the origin for you, don't mess with it
+ // cout << "INP_ORG: " << img->GetOrigin() << endl;
+ // cout << "OUT_ORG: " << output->GetOrigin() << endl;
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(output);
+
+}
+
+// Invocations
+template class PadImage<double, 2>;
+template class PadImage<double, 3>;
+template class PadImage<double, 4>;
diff --git a/Submodules/c3d/adapters/PadImage.h b/Submodules/c3d/adapters/PadImage.h
new file mode 100644
index 0000000..1ab3fb0
--- /dev/null
+++ b/Submodules/c3d/adapters/PadImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: PadImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __PadImage_h_
+#define __PadImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class PadImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ PadImage(Converter *c) : c(c) {}
+
+ void operator() (IndexType padExtentLower, IndexType padExtentUpper, float padValue);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/PeronaMalik.cxx b/Submodules/c3d/adapters/PeronaMalik.cxx
new file mode 100644
index 0000000..289b7e3
--- /dev/null
+++ b/Submodules/c3d/adapters/PeronaMalik.cxx
@@ -0,0 +1,57 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: PeronaMalik.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "PeronaMalik.h"
+#include "itkGradientAnisotropicDiffusionImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+PeronaMalik<TPixel, VDim>
+::operator() (double conductance, size_t nIter)
+{
+ // Get an image off the stack
+ ImagePointer image = c->m_ImageStack.back();
+
+ // Create a filter
+ typedef itk::GradientAnisotropicDiffusionImageFilter<
+ ImageType,ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+
+ *c->verbose << "Performing anisotropic diffusion on #" << c->m_ImageStack.size() << endl;
+ filter->SetInput(image);
+ filter->SetConductanceParameter(conductance);
+ filter->SetNumberOfIterations(nIter);
+ filter->SetTimeStep(0.0125);
+ filter->UseImageSpacingOn();
+ filter->Update();
+
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class PeronaMalik<double, 2>;
+template class PeronaMalik<double, 3>;
+template class PeronaMalik<double, 4>;
diff --git a/Submodules/c3d/adapters/PeronaMalik.h b/Submodules/c3d/adapters/PeronaMalik.h
new file mode 100644
index 0000000..d8244d6
--- /dev/null
+++ b/Submodules/c3d/adapters/PeronaMalik.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: PeronaMalik.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __PeronaMalik_h_
+#define __PeronaMalik_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class PeronaMalik : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ PeronaMalik(Converter *c) : c(c) {}
+
+ void operator() (double conductance, size_t nIter);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/PrintImageInfo.cxx b/Submodules/c3d/adapters/PrintImageInfo.cxx
new file mode 100644
index 0000000..9895139
--- /dev/null
+++ b/Submodules/c3d/adapters/PrintImageInfo.cxx
@@ -0,0 +1,260 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: PrintImageInfo.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "PrintImageInfo.h"
+#include "itkMetaDataDictionary.h"
+#include "itkMetaDataObject.h"
+#include "itkSpatialOrientation.h"
+
+template<class AnyType>
+bool
+try_print_metadata(itk::MetaDataDictionary &mdd, string key, AnyType deflt)
+ {
+ AnyType value = deflt;
+ if(itk::ExposeMetaData<AnyType>(mdd, key, value))
+ {
+ cout << " " << key << " = " << value << endl;
+ return true;
+ }
+ else return false;
+ }
+
+string
+get_rai_code(itk::SpatialOrientation::ValidCoordinateOrientationFlags code)
+ {
+ std::map<itk::SpatialOrientation::ValidCoordinateOrientationFlags, string> m_CodeToString;
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIP] = "RIP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIP] = "LIP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP] = "RSP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSP] = "LSP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIA] = "RIA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIA] = "LIA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSA] = "RSA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSA] = "LSA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRP] = "IRP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILP] = "ILP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRP] = "SRP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLP] = "SLP";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRA] = "IRA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILA] = "ILA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRA] = "SRA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLA] = "SLA";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI] = "RPI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPI] = "LPI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI] = "RAI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI] = "LAI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPS] = "RPS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS] = "LPS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS] = "RAS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAS] = "LAS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRI] = "PRI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLI] = "PLI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARI] = "ARI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALI] = "ALI";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRS] = "PRS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLS] = "PLS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARS] = "ARS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALS] = "ALS";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPR] = "IPR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPR] = "SPR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAR] = "IAR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAR] = "SAR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPL] = "IPL";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPL] = "SPL";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAL] = "IAL";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAL] = "SAL";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIR] = "PIR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSR] = "PSR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIR] = "AIR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASR] = "ASR";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIL] = "PIL";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSL] = "PSL";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIL] = "AIL";
+ m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASL] = "ASL";
+ return m_CodeToString[code];
+ }
+
+
+template <class TPixel, unsigned int VDim>
+std::string
+PrintImageInfo<TPixel, VDim>
+::GetRAICodeFromDirectionMatrix(vnl_matrix_fixed<double, VDim, VDim> dir)
+{
+ // Compute the RAI code from the direction matrix
+ char rai[VDim+1]; rai[VDim] = 0;
+ bool oblique = false;
+
+ char codes[3][2] = { {'R', 'L'}, {'A', 'P'}, {'I', 'S'}};
+
+ for(size_t i = 0; i < VDim; i++)
+ {
+ // Get the direction cosine for voxel direction i
+ vnl_vector_fixed<double, VDim> dcos = dir.get_column(i);
+ double dabsmax = dcos.inf_norm();
+
+ for(size_t j = 0; j < VDim; j++)
+ {
+ double dabs = fabs(dcos[j]);
+ size_t dsgn = dcos[j] > 0 ? 0 : 1;
+
+ if(dabs == 1.0)
+ {
+ rai[i] = codes[j][dsgn];
+ }
+ else if(dabs == dabsmax)
+ {
+ oblique = true;
+ rai[i] = codes[j][dsgn];
+ }
+ }
+ }
+
+ if(oblique)
+ {
+ std::ostringstream sout;
+ sout << "Oblique, closest to " << rai;
+ return sout.str();
+ }
+ else return string(rai);
+}
+
+template <class TPixel, unsigned int VDim>
+void
+PrintImageInfo<TPixel, VDim>
+::operator() (bool full)
+{
+ // Get the input image
+ ImagePointer image = c->m_ImageStack.back();
+
+ // Print the image number
+ cout << "Image #" << c->m_ImageStack.size() << ":";
+
+ // Compute the bounding box
+ RealVector bb0, bb1, ospm;
+ for(size_t i = 0; i < VDim; i++)
+ {
+ bb0[i] = image->GetOrigin()[i];
+ bb1[i] = bb0[i] + image->GetSpacing()[i] * image->GetBufferedRegion().GetSize()[i];
+ ospm[i] = -image->GetOrigin()[i] / image->GetSpacing()[i];
+ }
+
+ // Compute the intensity range of the image
+ size_t n = image->GetBufferedRegion().GetNumberOfPixels();
+ double *vox = image->GetBufferPointer();
+ double iMax = vox[0], iMin = vox[0], iMean = vox[0];
+ for(size_t i = 1; i < n; i++)
+ {
+ iMax = (iMax > vox[i]) ? iMax : vox[i];
+ iMin = (iMin < vox[i]) ? iMin : vox[i];
+ iMean += vox[i];
+ }
+ iMean /= n;
+
+ // Short or long?
+ if(!full)
+ {
+ cout << " dim = " << image->GetBufferedRegion().GetSize() << "; ";
+ cout << " bb = {[" << bb0 << "], [" << bb1 << "]}; ";
+ cout << " vox = " << image->GetSpacing() << "; ";
+ cout << " range = [" << iMin << ", " << iMax << "]; ";
+ cout << " orient = " << GetRAICodeFromDirectionMatrix(image->GetDirection().GetVnlMatrix()) << endl;
+ cout << endl;
+ }
+ else
+ {
+ cout << endl;
+ cout << " Image Dimensions : " << image->GetBufferedRegion().GetSize() << endl;
+ cout << " Bounding Box : " << "{[" << bb0 << "], [" << bb1 << "]}" << endl;
+ cout << " Voxel Spacing : " << image->GetSpacing() << endl;
+ cout << " Intensity Range : [" << iMin << ", " << iMax << "]" << endl;
+ cout << " Mean Intensity : " << iMean << endl;
+ cout << " Canon. Orientation : " << GetRAICodeFromDirectionMatrix(image->GetDirection().GetVnlMatrix()) << endl;
+ cout << " Direction Cos Mtx. : " << endl;
+ c->PrintMatrix(cout, image->GetDirection().GetVnlMatrix());
+
+ // Print NIFTI s-form matrix (check against freesurfer's MRIinfo)
+ cout << " Voxel->RAS x-form : " << endl;
+ c->PrintMatrix(cout,
+ image->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix(), "%12.5f ", " ");
+
+ //
+ // Print metadata
+ cout << " Image Metadata: " << endl;
+ itk::MetaDataDictionary &mdd = image->GetMetaDataDictionary();
+ itk::MetaDataDictionary::ConstIterator itMeta;
+ for(itMeta = mdd.Begin(); itMeta != mdd.End(); ++itMeta)
+ {
+ // Get the metadata as a generic object
+ string key = itMeta->first, v_string;
+ itk::SpatialOrientation::ValidCoordinateOrientationFlags v_oflags =
+ itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_INVALID;
+
+ if(itk::ExposeMetaData<string>(mdd, key, v_string))
+ {
+ // For some weird reason, some of the strings returned by this method
+ // contain '\0' characters. We will replace them by spaces
+ std::ostringstream sout("");
+ for(unsigned int i=0;i<v_string.length();i++)
+ if(v_string[i] >= ' ') sout << v_string[i];
+ v_string = sout.str();
+
+ // Make sure the value has more than blanks
+ if(v_string.find_first_not_of(" ") != v_string.npos)
+ cout << " " << key << " = " << v_string << endl;
+ }
+ else if(itk::ExposeMetaData(mdd, key, v_oflags))
+ {
+ cout << " " << key << " = " << get_rai_code(v_oflags) << endl;
+ }
+ else
+ {
+ bool rc = false;
+ if(!rc) rc |= try_print_metadata<double>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<float>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<int>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<unsigned int>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<long>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<unsigned long>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<short>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<unsigned short>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<char>(mdd, key, 0);
+ if(!rc) rc |= try_print_metadata<unsigned char>(mdd, key, 0);
+
+ if(!rc)
+ {
+ cout << " " << key << " of unsupported type "
+ << itMeta->second->GetMetaDataObjectTypeName() << endl;
+ }
+ }
+ }
+
+ }
+
+}
+
+// Invocations
+template class PrintImageInfo<double, 2>;
+template class PrintImageInfo<double, 3>;
+template class PrintImageInfo<double, 4>;
diff --git a/Submodules/c3d/adapters/PrintImageInfo.h b/Submodules/c3d/adapters/PrintImageInfo.h
new file mode 100644
index 0000000..f09337c
--- /dev/null
+++ b/Submodules/c3d/adapters/PrintImageInfo.h
@@ -0,0 +1,49 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: PrintImageInfo.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __PrintImageInfo_h_
+#define __PrintImageInfo_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class PrintImageInfo : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ PrintImageInfo(Converter *c) : c(c) {}
+
+ void operator() (bool full);
+
+private:
+ Converter *c;
+
+ std::string GetRAICodeFromDirectionMatrix(vnl_matrix_fixed<double, VDim, VDim> dir);
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/RFApply.cxx b/Submodules/c3d/adapters/RFApply.cxx
new file mode 100644
index 0000000..2181944
--- /dev/null
+++ b/Submodules/c3d/adapters/RFApply.cxx
@@ -0,0 +1,82 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: RFApply.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "RFApply.h"
+#include "RandomForestClassifyImageFilter.h"
+#include "itkVectorImage.h"
+#include <iostream>
+
+template <class TPixel, unsigned int VDim>
+void
+RFApply<TPixel, VDim>
+::operator() (const char *train_file)
+{
+ // Copy all images on the stack to a temp array
+ std::vector<ImagePointer> features;
+ for(int k = 0; k < c->m_ImageStack.size(); k++)
+ features.push_back(c->m_ImageStack[k]);
+ c->m_ImageStack.clear();
+
+ // Vector image support
+ typedef itk::VectorImage<TPixel, VDim> VectorImageType;
+
+ // Define the random forest classification filter
+ typedef RandomForestClassifyImageFilter<ImageType, VectorImageType, ImageType, TPixel> FilterType;
+
+ // Create a classifier object
+ typedef typename FilterType::ClassifierType RFClassifierType;
+ typename RFClassifierType::Pointer classifier = RFClassifierType::New();
+
+ // Read the classifier object from disk
+ ifstream in_file(train_file);
+ classifier->Read(in_file);
+ in_file.close();
+
+ // Create the filter for this label (TODO: this is wasting computation)
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Add all the images on the stack to the filter
+ for(int j = 0; j < features.size(); j++)
+ filter->AddScalarImage(features[j]);
+
+ // Pass the classifier to the filter
+ filter->SetClassifier(classifier);
+
+ // Set the filter behavior
+ filter->SetGenerateClassProbabilities(true);
+
+ // Run the filter for this set of weights
+ filter->Update();
+
+ // Append all the outputs to the stack
+ for(int k = 0; k < filter->GetNumberOfIndexedOutputs(); k++)
+ c->m_ImageStack.push_back(filter->GetOutput(k));
+
+}
+
+// Invocations
+template class RFApply<double, 2>;
+template class RFApply<double, 3>;
+template class RFApply<double, 4>;
diff --git a/Submodules/c3d/adapters/RFApply.h b/Submodules/c3d/adapters/RFApply.h
new file mode 100644
index 0000000..8ea6922
--- /dev/null
+++ b/Submodules/c3d/adapters/RFApply.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __RFApply_h_
+#define __RFApply_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class RFApply : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ RFApply(Converter *c) : c(c) {}
+
+ void operator() (const char *train_file);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/RFTrain.cxx b/Submodules/c3d/adapters/RFTrain.cxx
new file mode 100644
index 0000000..c73d2b8
--- /dev/null
+++ b/Submodules/c3d/adapters/RFTrain.cxx
@@ -0,0 +1,176 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: RFTrain.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "RFTrain.h"
+#include "ImageCollectionToImageFilter.h"
+#include "itkVectorImage.h"
+#include "Library/classifier.h"
+#include "Library/classification.h"
+#include "RandomForestClassifier.h"
+#include <iostream>
+
+template <class TPixel, unsigned int VDim>
+void
+RFTrain<TPixel, VDim>
+::operator() (const char * train_file, const ParametersType ¶m)
+{
+ // There should be at least two images on the stack
+ if(c->m_ImageStack.size() < 2)
+ throw ConvertException("At least two images (data/label) required to train RF classifier");
+
+ // Get the label image
+ ImagePointer imgLabel = c->m_ImageStack.back();
+
+ // Iterator for grouping images into a multi-component image
+ typedef itk::VectorImage<TPixel, VDim> VectorImageType;
+ typedef ImageCollectionConstRegionIteratorWithIndex<
+ ImageType, VectorImageType> CollectionIter;
+
+ // Get the segmentation image - which determines the samples
+ typedef itk::ImageRegionConstIteratorWithIndex<ImageType> LabelIter;
+
+ // Shrink the buffered region by radius because we can't handle BCs
+ itk::ImageRegion<VDim> reg = imgLabel->GetBufferedRegion();
+ reg.ShrinkByRadius(param.patch_radius);
+
+ // We need to iterate throught the label image once to determine the
+ // number of samples to allocate.
+ unsigned long nSamples = 0;
+ for(LabelIter lit(imgLabel, reg); !lit.IsAtEnd(); ++lit)
+ if(lit.Value())
+ nSamples++;
+
+ // Create an iterator for going over all the anatomical image data
+ CollectionIter cit(reg);
+ cit.SetRadius(param.patch_radius);
+
+ // Add all the anatomical images to this iterator
+ for(int i = 0; i < c->m_ImageStack.size() - 1; i++)
+ cit.AddImage(c->m_ImageStack[i]);
+
+ // Get the number of components
+ int nComp = cit.GetTotalComponents();
+ int nPatch = cit.GetNeighborhoodSize();
+ int nColumns = nComp * nPatch;
+
+ // Are we using coordinate informtion
+ if(param.use_coordinate_features)
+ nColumns += VDim;
+
+ // Create a new sample
+ typedef MLData<TPixel, TPixel> SampleType;
+ SampleType *sample = new SampleType(nSamples, nColumns);
+
+ // Now fill out the samples
+ int iSample = 0;
+ for(LabelIter lit(imgLabel, reg); !lit.IsAtEnd(); ++lit, ++cit)
+ {
+ TPixel label = lit.Value();
+ if(label)
+ {
+ // Fill in the data
+ std::vector<TPixel> &column = sample->data[iSample];
+ int k = 0;
+ for(int i = 0; i < nComp; i++)
+ for(int j = 0; j < nPatch; j++)
+ column[k++] = cit.NeighborValue(i,j);
+
+ // Add the coordinate features if used
+ if(param.use_coordinate_features)
+ for(int d = 0; d < VDim; d++)
+ column[k++] = lit.GetIndex()[d];
+
+ // Fill in the label
+ sample->label[iSample] = label;
+
+ ++iSample;
+ }
+ }
+
+ // Check that the sample has at least two distinct labels
+ bool isValidSample = false;
+ for(int iSample = 1; iSample < sample->Size(); iSample++)
+ if(sample->label[iSample] != sample->label[iSample-1])
+ { isValidSample = true; break; }
+
+ // Now there is a valid sample. The text task is to train the classifier
+ if(!isValidSample)
+ throw ConvertException("A classifier cannot be trained because the training "
+ "data contain fewer than two classes. Please label "
+ "examples of two or more tissue classes in the image.");
+
+ // Set up the classifier parameters
+ TrainingParameters params;
+
+ // TODO:
+ params.treeDepth = param.tree_depth;
+ params.treeNum = param.forest_size;
+ params.candidateNodeClassifierNum = 10;
+ params.candidateClassifierThresholdNum = 10;
+ params.subSamplePercent = 0;
+ params.splitIG = 0.1;
+ params.leafEntropy = 0.05;
+ params.verbose = true;
+
+ // Cap the number of training voxels at some reasonable number
+ if(sample->Size() > 10000)
+ params.subSamplePercent = 100 * 10000.0 / sample->Size();
+ else
+ params.subSamplePercent = 0;
+
+ // Create the classification engine
+ typedef RandomForestClassifier<TPixel, TPixel, VDim> RFClassifierType;
+ typedef typename RFClassifierType::RFAxisClassifierType RFAxisClassifierType;
+ typedef Classification<TPixel, TPixel, RFAxisClassifierType> ClassificationType;
+
+ typename RFClassifierType::Pointer classifier = RFClassifierType::New();
+ ClassificationType classification;
+
+ // Perform classifier training
+ classification.Learning(
+ params, *sample,
+ *classifier->GetForest(),
+ classifier->GetValidLabel(),
+ classifier->GetClassToLabelMapping());
+
+ // Reset the class weights to the number of classes and assign default
+ int n_classes = classifier->GetClassToLabelMapping().size(), n_fore = 0, n_back = 0;
+ classifier->GetClassWeights().resize(n_classes, -1.0);
+
+ // Store the patch radius in the classifier - this remains fixed until
+ // training is repeated
+ classifier->SetPatchRadius(param.patch_radius);
+ classifier->SetUseCoordinateFeatures(param.use_coordinate_features);
+
+ // Dump the classifier to a file
+ ofstream out_file(train_file);
+ classifier->Write(out_file);
+ out_file.close();
+}
+
+// Invocations
+template class RFTrain<double, 2>;
+template class RFTrain<double, 3>;
+template class RFTrain<double, 4>;
diff --git a/Submodules/c3d/adapters/RFTrain.h b/Submodules/c3d/adapters/RFTrain.h
new file mode 100644
index 0000000..4d69e39
--- /dev/null
+++ b/Submodules/c3d/adapters/RFTrain.h
@@ -0,0 +1,68 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __RFTrain_h_
+#define __RFTrain_h_
+
+#include "ConvertAdapter.h"
+
+template <class TPixel, unsigned int VDim>
+struct RFParameters
+{
+ itk::Size<VDim> patch_radius;
+ bool use_coordinate_features;
+ int tree_depth;
+ int forest_size;
+
+ RFParameters()
+ {
+ patch_radius.Fill(0);
+ use_coordinate_features = false;
+ tree_depth = 30;
+ forest_size = 50;
+ }
+};
+
+template<class TPixel, unsigned int VDim>
+class RFTrain : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ // Parameters
+ typedef RFParameters<TPixel, VDim> ParametersType;
+
+ RFTrain(Converter *c) : c(c) {}
+
+ void operator() (const char * train_file, const ParametersType ¶m);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/Rank.cxx b/Submodules/c3d/adapters/Rank.cxx
new file mode 100644
index 0000000..a32bf73
--- /dev/null
+++ b/Submodules/c3d/adapters/Rank.cxx
@@ -0,0 +1,86 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Rank.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "Rank.h"
+#include "itkAddImageFilter.h"
+#include "itkMetaDataObject.h"
+
+
+template <class TPixel, unsigned int VDim>
+void
+Rank<TPixel, VDim>
+::operator() ()
+{
+ // Create a maximum image
+ ImagePointer i0 = c->m_ImageStack[0];
+
+ // This image replaces each voxel in the input image
+ // with the rank (from 0 to n-1).
+
+ // Say something
+ *c->verbose << "Ranking " << c->m_ImageStack.size() <<
+ " images. " << endl;
+
+
+ // For each of the images, update the vote
+ size_t n = c->m_ImageStack.size();
+ for(size_t j = 1; j < n; j++)
+ {
+ // Get the next image pointer
+ ImagePointer ij = c->m_ImageStack[j];
+
+ // Check the image dimensions
+ if(ij->GetBufferedRegion() != c->m_ImageStack.back()->GetBufferedRegion())
+ throw ConvertException("All images must have same dimensions");
+ }
+
+ size_t nvox = c->m_ImageStack.back()->GetBufferedRegion().GetNumberOfPixels();
+ std::vector< std::pair<double, size_t> > sortvec(n);
+
+ for(size_t k = 0; k < nvox; k++)
+ {
+ for(size_t j = 0; j < n; j++)
+ {
+ sortvec[j] = std::pair<double,size_t>(c->m_ImageStack[j]->GetBufferPointer()[k], j);
+ }
+
+ if(k == 189552)
+ for(size_t j = 0; j < n; j++)
+ printf("J = %li, v = %f, r = %li\n", j, sortvec[j].first, sortvec[j].second);
+
+
+ std::sort(sortvec.begin(), sortvec.end());
+
+ for(size_t j = 0; j < n; j++)
+ {
+ c->m_ImageStack[sortvec[j].second]->GetBufferPointer()[k] = n - j;
+ }
+ }
+}
+
+// Invocations
+template class Rank<double, 2>;
+template class Rank<double, 3>;
+template class Rank<double, 4>;
diff --git a/Submodules/c3d/adapters/Rank.h b/Submodules/c3d/adapters/Rank.h
new file mode 100644
index 0000000..c9f0a54
--- /dev/null
+++ b/Submodules/c3d/adapters/Rank.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Rank.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __Rank_h_
+#define __Rank_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class Rank : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ Rank(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ReadImage.cxx b/Submodules/c3d/adapters/ReadImage.cxx
new file mode 100644
index 0000000..642a2ad
--- /dev/null
+++ b/Submodules/c3d/adapters/ReadImage.cxx
@@ -0,0 +1,201 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReadImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ReadImage.h"
+#include "itkIOCommon.h"
+#include "itkImageFileReader.h"
+#include "itkMetaDataDictionary.h"
+#include "itkMetaDataObject.h"
+#include "itkGDCMSeriesFileNames.h"
+#include "itkGDCMImageIO.h"
+#include "itkImageSeriesReader.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ReadImage<TPixel, VDim>
+::operator() (const char *file, const ImageInfo &info)
+{
+ // IO Base object
+ typename itk::ImageIOBase::Pointer iobase;
+
+ // Special handling for DICOM series
+ if(info.dicom_series_id)
+ {
+ // Get the directory where to search for the series
+ std::string series_dir = file;
+ if(!itksys::SystemTools::FileIsDirectory(file))
+ series_dir = itksys::SystemTools::GetParentDirectory(file);
+
+ // NOTE: for the time being, we are relying on GDCMSeriesFileNames for
+ // proper sorting of the DICOM data. This is marked as deprecated in GDCM 2
+ typename itk::GDCMSeriesFileNames::Pointer gdcm_series = itk::GDCMSeriesFileNames::New();
+ gdcm_series->SetUseSeriesDetails(true);
+ gdcm_series->SetDirectory(series_dir);
+
+ // Use the series provided by the user
+ std::vector<std::string> dicom_files = gdcm_series->GetFileNames(info.dicom_series_id);
+
+ // Read the information from the first filename
+ if(dicom_files.size() == 0)
+ throw ConvertException("Error: DICOM series not found. "
+ "Directory '%s' does not appear to contain a "
+ "series of DICOM images.", series_dir.c_str());
+
+ // Report
+ *c->verbose << "Reading #" << (1 + c->m_ImageStack.size()) << " from DICOM series "
+ << info.dicom_series_id << " in " << series_dir << endl;
+
+ iobase = itk::GDCMImageIO::New();
+ iobase->SetFileName(dicom_files[0]);
+
+ // Read the image information
+ iobase->ReadImageInformation();
+
+ // Create an image series reader
+ typedef itk::ImageSeriesReader<ImageType> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+
+ // Set the filenames and read
+ reader->SetFileNames(dicom_files);
+ reader->SetImageIO(iobase);
+
+ // Try reading this file
+ try { reader->Update(); }
+ catch(itk::ExceptionObject &exc)
+ {
+ throw ConvertException("Error reading DICOM series %s\nITK exception: %s", file, exc.GetDescription());
+ }
+
+ ImagePointer image = reader->GetOutput();
+ c->m_ImageStack.push_back(image);
+
+ // Copy the metadata from the first scan in the series
+ /*
+ const typename ReaderType::DictionaryArrayType *darr =
+ reader->GetMetaDataDictionaryArray();
+ if(darr->size() > 0)
+ m_NativeImage->SetMetaDataDictionary(*((*darr)[0]));
+ */
+ }
+ else
+ {
+ // Report
+ *c->verbose << "Reading #" << (1 + c->m_ImageStack.size()) << " from " << file << endl;
+
+ // Delegate to the Factory (based on filename, etc)
+ iobase = itk::ImageIOFactory::CreateImageIO(file, itk::ImageIOFactory::ReadMode);
+
+ if(!iobase)
+ throw ConvertException("Unable to read image %s; IO factory can not create IO object.", file);
+
+ iobase->SetFileName(file);
+
+ // Read the image information
+ iobase->ReadImageInformation();
+
+ // Handle SPM origin if SPM flag is specified
+ // TODO: get rid of this code, nifti is more reliable
+ string ext = itksys::SystemTools::GetFilenameExtension(file);
+ if((ext == ".hdr" || ext == ".img.gz" || ext == ".img") && c->m_FlagSPM)
+ {
+ string temp;
+ if(itk::ExposeMetaData<std::string>(
+ iobase->GetMetaDataDictionary(), itk::ITK_FileOriginator, temp))
+ {
+ // Read the SPM-style origin
+ *c->verbose << " Applying SPM origin :";
+ for(size_t i=0; i < VDim; i++)
+ {
+ double sitk = iobase->GetSpacing(i);
+ short xspm = (temp[2*i+1] << 8) + temp[2*i];
+ double oitk = -sitk * xspm;
+ *c->verbose << xspm << " ";
+ iobase->SetOrigin(i, oitk);
+ }
+ *c->verbose << endl;
+ }
+ }
+
+ // If the image has multiple components, we need to handle it specially
+ if(iobase->GetNumberOfComponents() > 1 && c->m_MultiComponentSplit)
+ {
+ // Read the multi-component image
+ typedef itk::VectorImage<TPixel, VDim> VectorImageType;
+ typedef itk::ImageFileReader<VectorImageType> VectorReader;
+ typename VectorReader::Pointer reader = VectorReader::New();
+ reader->SetFileName(file);
+ reader->SetImageIO(iobase);
+ try { reader->Update(); }
+ catch(itk::ExceptionObject &exc)
+ {
+ throw ConvertException("Error reading image %s\nITK exception: %s", file, exc.GetDescription());
+ }
+
+ // Report
+ *c->verbose << " Splitting " << iobase->GetNumberOfComponents() << "-component image." << endl;
+
+ // Split the vector image into component images
+ typename VectorImageType::Pointer vec = reader->GetOutput();
+ size_t ncomp = vec->GetVectorLength();
+ for(size_t i = 0; i < ncomp; i++)
+ {
+ ImagePointer icomp = ImageType::New();
+ icomp->CopyInformation(vec);
+ icomp->SetRegions(vec->GetBufferedRegion());
+ icomp->Allocate();
+ TPixel *src = vec->GetBufferPointer() + i;
+ TPixel *dst = icomp->GetBufferPointer();
+ TPixel *end = dst + vec->GetBufferedRegion().GetNumberOfPixels();
+ for(; dst < end; dst++, src+=ncomp) *dst = *src;
+
+ // Push the image on the stack
+ c->m_ImageStack.push_back(icomp);
+ }
+ }
+ else
+ {
+ // Set up the reader
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(file);
+ reader->SetImageIO(iobase);
+
+ // Try reading this file
+ try { reader->Update(); }
+ catch(itk::ExceptionObject &exc)
+ {
+ throw ConvertException("Error reading image %s\nITK exception: %s", file, exc.GetDescription());
+ }
+
+ ImagePointer image = reader->GetOutput();
+ c->m_ImageStack.push_back(image);
+ }
+ }
+}
+
+// Invocations
+template class ReadImage<double, 2>;
+template class ReadImage<double, 3>;
+template class ReadImage<double, 4>;
diff --git a/Submodules/c3d/adapters/ReadImage.h b/Submodules/c3d/adapters/ReadImage.h
new file mode 100644
index 0000000..fa9d13c
--- /dev/null
+++ b/Submodules/c3d/adapters/ReadImage.h
@@ -0,0 +1,58 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReadImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ReadImage_h_
+#define __ReadImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ReadImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ // Structure for additional information for reading images
+ struct ImageInfo
+ {
+ char *dicom_series_id;
+ ImageInfo() : dicom_series_id(NULL) {}
+ };
+
+ ReadImage(Converter *c) : c(c) {}
+
+ /**
+ * Second parameter is extra information for special IO needs, like DICOM
+ */
+ void operator() (const char *file, const ImageInfo &info = ImageInfo());
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ReciprocalImage.cxx b/Submodules/c3d/adapters/ReciprocalImage.cxx
new file mode 100644
index 0000000..8002147
--- /dev/null
+++ b/Submodules/c3d/adapters/ReciprocalImage.cxx
@@ -0,0 +1,63 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReciprocalImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ReciprocalImage.h"
+#include "itkUnaryFunctorImageFilter.h"
+
+template <class TIn, class TOut>
+class ReciprocalFunctor
+{
+public:
+ TOut operator() (const TIn &a)
+ { return (TOut) (1.0 / a); }
+};
+
+template <class TPixel, unsigned int VDim>
+void
+ReciprocalImage<TPixel, VDim>
+::operator() ()
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Verbose
+ *c->verbose << "Taking the reciprocal of #" << c->m_ImageStack.size() << endl;
+
+ // Simply go through and divide
+ typedef ReciprocalFunctor<TPixel, TPixel> FunctorType;
+ typedef itk::UnaryFunctorImageFilter<ImageType, ImageType, FunctorType> FilterType;
+
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(img);
+ filter->Update();
+
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class ReciprocalImage<double, 2>;
+template class ReciprocalImage<double, 3>;
+template class ReciprocalImage<double, 4>;
diff --git a/Submodules/c3d/adapters/ReciprocalImage.h b/Submodules/c3d/adapters/ReciprocalImage.h
new file mode 100644
index 0000000..7db0dab
--- /dev/null
+++ b/Submodules/c3d/adapters/ReciprocalImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReciprocalImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ReciprocalImage_h_
+#define __ReciprocalImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ReciprocalImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ReciprocalImage(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ReorderStack.cxx b/Submodules/c3d/adapters/ReorderStack.cxx
new file mode 100644
index 0000000..3bcd1c3
--- /dev/null
+++ b/Submodules/c3d/adapters/ReorderStack.cxx
@@ -0,0 +1,62 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReorderStack.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ReorderStack.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ReorderStack<TPixel, VDim>
+::operator() (size_t k)
+{
+ // Get the number of images on the stack
+ size_t n = c->m_ImageStack.size();
+
+ // Make sure n is divisible by k
+ if(n % k != 0)
+ throw ConvertException("Can not reorder %d images using stride of %d;"
+ " %d is not divisible by %d.", n, k, n, k);
+
+ // Explain what we are doing
+ *c->verbose << "Reordering " << n << " images with stride of " << k << endl;
+
+ // Make a copy of the stack
+ ImageStack<ImageType> temp_stack;
+ for(size_t i=0; i < c->m_ImageStack.size(); i++)
+ {
+ temp_stack.push_back(c->m_ImageStack[i]);
+ }
+
+ c->m_ImageStack.clear();
+
+ // Traverse the stack
+ for(size_t i = 0; i < k; i++)
+ for(size_t j = i; j < n; j += k)
+ c->m_ImageStack.push_back(temp_stack[j]);
+}
+
+// Invocations
+template class ReorderStack<double, 2>;
+template class ReorderStack<double, 3>;
+template class ReorderStack<double, 4>;
diff --git a/Submodules/c3d/adapters/ReorderStack.h b/Submodules/c3d/adapters/ReorderStack.h
new file mode 100644
index 0000000..c59e9c3
--- /dev/null
+++ b/Submodules/c3d/adapters/ReorderStack.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReorderStack.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ReorderStack_h_
+#define __ReorderStack_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ReorderStack : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ReorderStack(Converter *c) : c(c) {}
+
+ void operator() (size_t n);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ReplaceIntensities.cxx b/Submodules/c3d/adapters/ReplaceIntensities.cxx
new file mode 100644
index 0000000..8d3886e
--- /dev/null
+++ b/Submodules/c3d/adapters/ReplaceIntensities.cxx
@@ -0,0 +1,79 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReplaceIntensities.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ReplaceIntensities.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ReplaceIntensities<TPixel, VDim>
+::operator() (vector<double> &xRule)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // This is a slightly sensitive procedure. We need to set an epsilon so that
+ // intensities that are within machine precision are treated as equal. We define
+ // this as 2 (a - b) / (a + b) < eps.
+ double epsilon = 0.000001;
+
+ // Report what we are doing
+ *c->verbose << "Replacing intensities in #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Replacement Rules: ";
+ for(size_t i = 0; i < xRule.size(); i+=2)
+ *c->verbose << xRule[i] << " -> " << xRule[i+1] << "; ";
+ *c->verbose << endl;
+
+ // Create an iterator to process the image
+ long nReps = 0;
+ for(Iterator it(input, input->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ // Get the pixel value
+ TPixel val = it.Get();
+
+ // Repeat over the rules
+ for(size_t k = 0; k < xRule.size(); k += 2)
+ {
+ double u = xRule[k], v = xRule[k+1];
+ if(
+ (vnl_math_isnan(val) && vnl_math_isnan(u)) ||
+ (val == u) ||
+ fabs(2*(val-u)/(val+u)) < epsilon)
+ {
+ it.Set(v);
+ nReps++;
+ break;
+ }
+ }
+ }
+
+ // Report what the filter is doing
+ *c->verbose << " Replacements Made: " << nReps << endl;
+
+}
+
+// Invocations
+template class ReplaceIntensities<double, 2>;
+template class ReplaceIntensities<double, 3>;
+template class ReplaceIntensities<double, 4>;
diff --git a/Submodules/c3d/adapters/ReplaceIntensities.h b/Submodules/c3d/adapters/ReplaceIntensities.h
new file mode 100644
index 0000000..524e130
--- /dev/null
+++ b/Submodules/c3d/adapters/ReplaceIntensities.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ReplaceIntensities.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ReplaceIntensities_h_
+#define __ReplaceIntensities_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ReplaceIntensities : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ReplaceIntensities(Converter *c) : c(c) {}
+
+ void operator() (vector<double> &xRule);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ResampleImage.cxx b/Submodules/c3d/adapters/ResampleImage.cxx
new file mode 100644
index 0000000..e26cf82
--- /dev/null
+++ b/Submodules/c3d/adapters/ResampleImage.cxx
@@ -0,0 +1,90 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ResampleImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ResampleImage.h"
+#include "itkResampleImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ResampleImage<TPixel, VDim>
+::operator() (SizeType &sz)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Build the resampling filter
+ typedef itk::ResampleImageFilter<ImageType,ImageType> ResampleFilterType;
+ typename ResampleFilterType::Pointer fltSample = ResampleFilterType::New();
+
+ // Initialize the resampling filter with an identity transform
+ fltSample->SetInput(input);
+ fltSample->SetTransform(itk::IdentityTransform<double,VDim>::New());
+ fltSample->SetInterpolator(c->GetInterpolator());
+
+ // Compute the spacing of the new image
+ typename ImageType::SpacingType spc_pre = input->GetSpacing();
+ typename ImageType::SpacingType spc_post = spc_pre;
+ for(size_t i = 0; i < VDim; i++)
+ spc_post[i] *= input->GetBufferedRegion().GetSize()[i] * 1.0 / sz[i];
+
+ // Get the bounding box of the input image
+ typename ImageType::PointType origin_pre = input->GetOrigin();
+
+ // Recalculate the origin. The origin describes the center of voxel 0,0,0
+ // so that as the voxel size changes, the origin will change as well.
+ typename ImageType::SpacingType off_pre = (input->GetDirection() * spc_pre) * 0.5;
+ typename ImageType::SpacingType off_post = (input->GetDirection() * spc_post) * 0.5;
+ typename ImageType::PointType origin_post = origin_pre - off_pre + off_post;
+
+ // Set the image sizes and spacing.
+ fltSample->SetSize(sz);
+ fltSample->SetOutputSpacing(spc_post);
+ fltSample->SetOutputOrigin(origin_post);
+ fltSample->SetOutputDirection(input->GetDirection());
+
+ // Set the unknown intensity to positive value
+ fltSample->SetDefaultPixelValue(c->m_Background);
+
+ // Describe what we are doing
+ *c->verbose << "Resampling #" << c->m_ImageStack.size() << " to have" << sz << " voxels." << endl;
+ *c->verbose << " Interpolation method: " << c->m_Interpolation << endl;
+ *c->verbose << " Background intensity: " << c->m_Background << endl;
+ *c->verbose << " Input spacing: " << spc_pre << endl;
+ *c->verbose << " Input origin: " << origin_pre << endl;
+ *c->verbose << " Output spacing: " << spc_post << endl;
+ *c->verbose << " Output origin: " << origin_post << endl;
+
+ // Perform resampling
+ fltSample->UpdateLargestPossibleRegion();
+
+ // Change the source to the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltSample->GetOutput());
+}
+
+// Invocations
+template class ResampleImage<double, 2>;
+template class ResampleImage<double, 3>;
+template class ResampleImage<double, 4>;
diff --git a/Submodules/c3d/adapters/ResampleImage.h b/Submodules/c3d/adapters/ResampleImage.h
new file mode 100644
index 0000000..f24fbc4
--- /dev/null
+++ b/Submodules/c3d/adapters/ResampleImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ResampleImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ResampleImage_h_
+#define __ResampleImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ResampleImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ResampleImage(Converter *c) : c(c) {}
+
+ void operator() (SizeType &size);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ResliceImage.cxx b/Submodules/c3d/adapters/ResliceImage.cxx
new file mode 100644
index 0000000..2c0c400
--- /dev/null
+++ b/Submodules/c3d/adapters/ResliceImage.cxx
@@ -0,0 +1,182 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ResliceImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ResliceImage.h"
+#include <string>
+#include <iostream>
+#include <itkAffineTransform.h>
+#include <itkTransformFileReader.h>
+#include <itkTransformFactory.h>
+#include "itkResampleImageFilter.h"
+#include "gsGSAffine3DTransform.h"
+
+#include <itkTransformFactory.h>
+
+template<unsigned int VDim>
+void ReadMatrix(const char *fname, itk::Matrix<double,VDim+1,VDim+1> &mat)
+ {
+ ifstream fin(fname);
+ for(size_t i = 0; i < VDim+1; i++)
+ for(size_t j = 0; j < VDim+1; j++)
+ if(fin.good())
+ {
+ fin >> mat[i][j];
+ }
+ else
+ {
+ throw ConvertException("Unable to read matrix %s", fname);
+ }
+ fin.close();
+ }
+
+template <class TPixel, unsigned int VDim>
+void
+ResliceImage<TPixel, VDim>
+::operator() (string format, string fn_tran)
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < 2)
+ {
+ cerr << "Reslice operation requires two images on the stack" << endl;
+ throw -1;
+ }
+
+ // Get the reference and source images
+ ImagePointer iref = c->m_ImageStack[c->m_ImageStack.size() - 2];
+ ImagePointer isrc = c->m_ImageStack.back();
+
+ // Create an initial identity transform
+ typedef itk::AffineTransform<double, VDim> TranType;
+ typename TranType::Pointer atran = TranType::New();
+ atran->SetIdentity();
+
+ // Read transform based on type
+ if(format=="itk")
+ {
+ typedef itk::MatrixOffsetTransformBase<double, VDim, VDim> MOTBType;
+ typedef itk::AffineTransform<double, VDim> AffTran;
+ itk::TransformFactory<MOTBType>::RegisterTransform();
+ itk::TransformFactory<AffTran>::RegisterTransform();
+
+ itk::TransformFileReader::Pointer fltReader = itk::TransformFileReader::New();
+ fltReader->SetFileName(fn_tran);
+ fltReader->Update();
+
+ itk::TransformBase *base = fltReader->GetTransformList()->front();
+ MOTBType *motb = dynamic_cast<MOTBType *>(base);
+
+ if(motb)
+ {
+ atran->SetMatrix(motb->GetMatrix());
+ atran->SetOffset(motb->GetOffset());
+ }
+ }
+ else if(format=="matrix")
+ {
+ // Read the matrix
+ itk::Matrix<double,VDim+1,VDim+1> matrix;
+ ReadMatrix<VDim>(fn_tran.c_str(), matrix);
+
+ // Get the transform matrix and the offset vector
+ vnl_matrix<double> A_ras = matrix.GetVnlMatrix().extract(VDim, VDim);
+ vnl_vector<double> b_ras = matrix.GetVnlMatrix().extract(VDim, 1, 0, VDim).get_column(0);
+
+ // Extrernal matrices are assumed to be RAS to RAS, so we must convert to LPS to LPS
+ vnl_vector<double> v_lps_to_ras(VDim, 1.0);
+ v_lps_to_ras[0] = v_lps_to_ras[1] = -1.0;
+ vnl_diag_matrix<double> m_lps_to_ras(v_lps_to_ras);
+ vnl_matrix<double> A_lps = m_lps_to_ras * A_ras * m_lps_to_ras;
+ vnl_vector<double> b_lps = m_lps_to_ras * b_ras;
+
+ // Stick these into the itk matrix/vector
+ itk::Matrix<double,VDim,VDim> amat(A_lps);
+ itk::Vector<double, VDim> aoff;
+ aoff.SetVnlVector(b_lps);
+
+ // Put the values in the transform
+ atran->SetMatrix(amat);
+ atran->SetOffset(aoff);
+ }
+
+ // Build the resampling filter
+ typedef itk::ResampleImageFilter<ImageType,ImageType> ResampleFilterType;
+ typename ResampleFilterType::Pointer fltSample = ResampleFilterType::New();
+
+ // Initialize the resampling filter with an identity transform
+ fltSample->SetInput(isrc);
+ fltSample->SetTransform(atran);
+
+ // Set the unknown intensity to positive value
+ fltSample->SetDefaultPixelValue(c->m_Background);
+
+ // Set the interpolator
+ fltSample->SetInterpolator(c->GetInterpolator());
+
+ // Calculate where the transform is taking things
+ itk::ContinuousIndex<double, VDim> idx[3];
+ for(size_t i = 0; i < VDim; i++)
+ {
+ idx[0][i] = 0.0;
+ idx[1][i] = iref->GetBufferedRegion().GetSize(i) / 2.0;
+ idx[2][i] = iref->GetBufferedRegion().GetSize(i) - 1.0;
+ }
+ for(size_t j = 0; j < VDim; j++)
+ {
+ itk::ContinuousIndex<double, VDim> idxmov;
+ itk::Point<double, VDim> pref, pmov;
+ iref->TransformContinuousIndexToPhysicalPoint(idx[j], pref);
+ pmov = atran->TransformPoint(pref);
+ isrc->TransformPhysicalPointToContinuousIndex(pmov, idxmov);
+ *c->verbose << " Reference voxel " << idx[j] << " => moving voxel " << idxmov << endl;
+ }
+
+ // Describe what we are doing
+ *c->verbose << "Reslicing #" << c->m_ImageStack.size()
+ << " with reference" << c->m_ImageStack.size() - 1 << endl;
+ *c->verbose << " Interpolation method: " << c->m_Interpolation << endl;
+ *c->verbose << " Background intensity: " << c->m_Background << endl;
+ *c->verbose << " Affine Transform: " << endl;
+ vnl_matrix<double> amat(VDim+1, VDim+1, 0);
+ vnl_vector<double> atmp(VDim+1, 0);
+ amat.update(atran->GetMatrix().GetVnlMatrix(), 0, 0);
+ atmp.update(atran->GetOffset().GetVnlVector(), 0);
+ amat.set_column(VDim, atmp);
+ c->PrintMatrix(*c->verbose, amat, "%12.5f ", " ");
+
+ // Set the spacing, origin, direction of the output
+ fltSample->UseReferenceImageOn();
+ fltSample->SetReferenceImage(iref);
+ fltSample->Update();
+
+ // Change the source to the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltSample->GetOutput());
+}
+
+// Invocations
+template class ResliceImage<double, 2>;
+template class ResliceImage<double, 3>;
+template class ResliceImage<double, 4>;
diff --git a/Submodules/c3d/adapters/ResliceImage.h b/Submodules/c3d/adapters/ResliceImage.h
new file mode 100644
index 0000000..4be3ac2
--- /dev/null
+++ b/Submodules/c3d/adapters/ResliceImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ResliceImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ResliceImage_h_
+#define __ResliceImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ResliceImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ResliceImage(Converter *c) : c(c) {}
+
+ void operator() (string format, string fn_tran);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SLICSuperVoxel.cxx b/Submodules/c3d/adapters/SLICSuperVoxel.cxx
new file mode 100644
index 0000000..0d461c6
--- /dev/null
+++ b/Submodules/c3d/adapters/SLICSuperVoxel.cxx
@@ -0,0 +1,79 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SLICSuperVoxel.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SLICSuperVoxel.h"
+#include "itkSLICSuperVoxelImageFilter.h"
+#include "itkGradientMagnitudeImageFilter.h"
+#include "itkScalarConnectedComponentImageFilter.h"
+#include "itkCastImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+SLICSuperVoxel<TPixel, VDim>
+::operator() (int svPerDim, double m)
+{
+ // Get the image from the stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Compute the gradient of the image
+ typedef itk::GradientMagnitudeImageFilter<ImageType, ImageType> GradFilter;
+ typename GradFilter::Pointer gradFilter = GradFilter::New();
+ gradFilter->SetInput(img);
+ gradFilter->Update();
+
+ // Create the main filter
+ typedef itk::SLICSuperVoxelImageFilter<ImageType, ImageType, ImageType> SLICFilter;
+ typename SLICFilter::Pointer fltSlic = SLICFilter::New();
+ fltSlic->SetInput(img);
+ fltSlic->SetGradientImage(gradFilter->GetOutput());
+ fltSlic->SetMParameter(m);
+ fltSlic->SetSeedsPerDimension(svPerDim);
+ fltSlic->Update();
+
+ // Run a scalar connected components filter
+ typedef itk::Image<short, VDim> IntegerImage;
+ typedef itk::ScalarConnectedComponentImageFilter<ImageType, IntegerImage> CompFilter;
+ typename CompFilter::Pointer fltComp = CompFilter::New();
+ fltComp->SetInput(fltSlic->GetOutput());
+ fltComp->SetDistanceThreshold(0.0);
+ // fltComp->Update();
+
+ typedef itk::CastImageFilter<IntegerImage, ImageType> CastFilter;
+ typename CastFilter::Pointer fltCast = CastFilter::New();
+ fltCast->SetInput(fltComp->GetOutput());
+ // fltCast->Update();
+
+ // Do some processing ...
+ // ImagePointer result = ...;
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltSlic->GetOutput());
+}
+
+// Invocations
+template class SLICSuperVoxel<double, 2>;
+template class SLICSuperVoxel<double, 3>;
+template class SLICSuperVoxel<double, 4>;
diff --git a/Submodules/c3d/adapters/SLICSuperVoxel.h b/Submodules/c3d/adapters/SLICSuperVoxel.h
new file mode 100644
index 0000000..ad6fe0c
--- /dev/null
+++ b/Submodules/c3d/adapters/SLICSuperVoxel.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SLICSuperVoxel_h_
+#define __SLICSuperVoxel_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class SLICSuperVoxel : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ SLICSuperVoxel(Converter *c) : c(c) {}
+
+ void operator() (int svPerDim, double m);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SampleImage.cxx b/Submodules/c3d/adapters/SampleImage.cxx
new file mode 100644
index 0000000..3368e3b
--- /dev/null
+++ b/Submodules/c3d/adapters/SampleImage.cxx
@@ -0,0 +1,60 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SampleImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SampleImage.h"
+
+template <class TPixel, unsigned int VDim>
+void
+SampleImage<TPixel, VDim>
+::operator() (const RealVector &x)
+{
+ // Create a point
+ itk::Point<double, VDim> p;
+ for(size_t i = 0; i < VDim; i++)
+ p[i] = x[i];
+
+ // Map the point to a continuous index
+ ImagePointer image = c->m_ImageStack.back();
+ itk::ContinuousIndex<double, VDim> cidx;
+ image->TransformRASPhysicalPointToContinuousIndex(p, cidx);
+
+ // Describe what we are doing
+ *c->verbose << "Probing #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Physical (RAS) Coordinates: " << p << endl;
+ *c->verbose << " Voxel Coordinates : " << cidx << endl;
+
+ // Use the interpolator in c
+ c->GetInterpolator()->SetInputImage(image);
+ m_Result = c->GetInterpolator()->EvaluateAtContinuousIndex(cidx);
+ *c->verbose << " Using " << c->m_Interpolation << " interpolation" << endl;
+
+ // Print out the interpolated value
+ cout << "Interpolated image value at " << x << " is " << m_Result << endl;
+}
+
+// Invocations
+template class SampleImage<double, 2>;
+template class SampleImage<double, 3>;
+template class SampleImage<double, 4>;
diff --git a/Submodules/c3d/adapters/SampleImage.h b/Submodules/c3d/adapters/SampleImage.h
new file mode 100644
index 0000000..ddb69d3
--- /dev/null
+++ b/Submodules/c3d/adapters/SampleImage.h
@@ -0,0 +1,52 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SampleImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SampleImage_h_
+#define __SampleImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class SampleImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ SampleImage(Converter *c) : c(c) {}
+
+ void operator() (const RealVector &x);
+
+ // After the adapter executes, we can obtain the result
+ itkGetMacro(Result, double);
+
+private:
+ Converter *c;
+
+ double m_Result;
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ScalarToRGB.cxx b/Submodules/c3d/adapters/ScalarToRGB.cxx
new file mode 100644
index 0000000..39fcc51
--- /dev/null
+++ b/Submodules/c3d/adapters/ScalarToRGB.cxx
@@ -0,0 +1,96 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ScalarToRGB.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ScalarToRGB.h"
+#include "itkScalarToRGBColormapImageFilter.h"
+#include "itkVectorIndexSelectionCastImageFilter.h"
+#include <map>
+
+template <class TPixel, unsigned int VDim>
+void
+ScalarToRGB<TPixel, VDim>
+::operator() (const std::string &colormap)
+{
+ // Check the input parameters
+ typedef itk::RGBPixel<unsigned char> RGBPixel;
+ typedef itk::Image<RGBPixel, VDim> RGBImageType;
+ typedef itk::ScalarToRGBColormapImageFilter<ImageType, RGBImageType> RGBFilterType;
+ typedef typename RGBFilterType::ColormapEnumType ColormapEnumType;
+ typedef std::map<std::string, ColormapEnumType> ColormapNameMap;
+ ColormapNameMap clmap;
+
+ // Set up the map
+ clmap["red"]=RGBFilterType::Red;
+ clmap["green"]=RGBFilterType::Green;
+ clmap["blue"]=RGBFilterType::Blue;
+ clmap["grey"]=RGBFilterType::Grey;
+ clmap["hot"]=RGBFilterType::Hot;
+ clmap["cool"]=RGBFilterType::Cool;
+ clmap["spring"]=RGBFilterType::Spring;
+ clmap["summer"]=RGBFilterType::Summer;
+ clmap["autumn"]=RGBFilterType::Autumn;
+ clmap["winter"]=RGBFilterType::Winter;
+ clmap["copper"]=RGBFilterType::Copper;
+ clmap["jet"]=RGBFilterType::Jet;
+ clmap["hsv"]=RGBFilterType::HSV;
+ clmap["overunder"]=RGBFilterType::OverUnder;
+
+ // Look it up
+ typename ColormapNameMap::iterator it = clmap.find(colormap);
+ if(it == clmap.end())
+ throw ConvertException("Unknown colormap %s", colormap.c_str());
+
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Create the filter
+ typename RGBFilterType::Pointer filter = RGBFilterType::New();
+ filter->SetInput(img);
+ filter->SetColormap(it->second);
+
+ // Verbose
+ *c->verbose << "Mapping #" << c->m_ImageStack.size()
+ << " to RGB using color map " << colormap << endl;
+
+ // Do it!
+ filter->Update();
+
+ // Split into RGB components.
+ c->m_ImageStack.pop_back();
+ for(int i = 0; i < 3; i++)
+ {
+ typedef itk::VectorIndexSelectionCastImageFilter<RGBImageType, ImageType> CompFilterType;
+ typename CompFilterType::Pointer fltComp = CompFilterType::New();
+ fltComp->SetInput(filter->GetOutput());
+ fltComp->SetIndex(i);
+ fltComp->Update();
+ c->m_ImageStack.push_back(fltComp->GetOutput());
+ }
+}
+
+// Invocations
+template class ScalarToRGB<double, 2>;
+template class ScalarToRGB<double, 3>;
+template class ScalarToRGB<double, 4>;
diff --git a/Submodules/c3d/adapters/ScalarToRGB.h b/Submodules/c3d/adapters/ScalarToRGB.h
new file mode 100644
index 0000000..6a22451
--- /dev/null
+++ b/Submodules/c3d/adapters/ScalarToRGB.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ScalarToRGB.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ScalarToRGB_h_
+#define __ScalarToRGB_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ScalarToRGB : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ScalarToRGB(Converter *c) : c(c) {}
+
+ void operator() (const std::string &colormap);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ScaleShiftImage.cxx b/Submodules/c3d/adapters/ScaleShiftImage.cxx
new file mode 100644
index 0000000..a660b08
--- /dev/null
+++ b/Submodules/c3d/adapters/ScaleShiftImage.cxx
@@ -0,0 +1,63 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ScaleShiftImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ScaleShiftImage.h"
+#include "itkShiftScaleImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ScaleShiftImage<TPixel, VDim>
+::operator() (double a, double b)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Say what we are doing
+ *c->verbose << "Scaling #" << c->m_ImageStack.size() << " by " << a << " and adding " << b << endl;
+
+ // If a=0, this means setting the image to a constant
+ if(a == 0.0)
+ {
+ c->CopyImage();
+ c->m_ImageStack.back()->FillBuffer(b);
+ return;
+ }
+
+ // Create and run filter
+ typedef itk::ShiftScaleImageFilter<ImageType, ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(input);
+ filter->SetScale(a);
+ filter->SetShift(b / a);
+ filter->Update();
+
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class ScaleShiftImage<double, 2>;
+template class ScaleShiftImage<double, 3>;
+template class ScaleShiftImage<double, 4>;
diff --git a/Submodules/c3d/adapters/ScaleShiftImage.h b/Submodules/c3d/adapters/ScaleShiftImage.h
new file mode 100644
index 0000000..7b8741b
--- /dev/null
+++ b/Submodules/c3d/adapters/ScaleShiftImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ScaleShiftImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ScaleShiftImage_h_
+#define __ScaleShiftImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ScaleShiftImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ScaleShiftImage(Converter *c) : c(c) {}
+
+ void operator() (double a, double b);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SetOrientation.cxx b/Submodules/c3d/adapters/SetOrientation.cxx
new file mode 100644
index 0000000..69f5b47
--- /dev/null
+++ b/Submodules/c3d/adapters/SetOrientation.cxx
@@ -0,0 +1,86 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SetOrientation.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SetOrientation.h"
+
+template <class TPixel, unsigned int VDim>
+void
+SetOrientation<TPixel, VDim>
+::operator() (std::string rai)
+{
+ // Check the RAI code validity
+ if(rai.length() != 3)
+ throw ConvertException("Orientation code %s is not 3 characters long", rai.c_str());
+
+ // Only valid for 3D images
+ if(VDim != 3)
+ throw ConvertException("Orientation codes only valid for 3D images");
+
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Create a direction matrix
+ vnl_matrix_fixed<double, 3, 3> eye, dm;
+ eye.set_identity(); dm.set_identity();
+
+ // RAI codes
+ char codes[3][2] = { {'R', 'L'}, {'A', 'P'}, {'I', 'S'}};
+
+ for(size_t i = 0; i < 3; i++)
+ {
+ bool matched = false;
+ for(size_t j = 0; j < 3; j++)
+ {
+ for(size_t k = 0; k < 2; k++)
+ {
+ if(toupper(rai[i]) == codes[j][k])
+ {
+ // Set the row of the direction matrix
+ dm.set_column(i, (k==0 ? 1.0 : -1.0) * eye.get_row(j));
+
+ // Clear that code (so that we catch orientation codes like RRS)
+ codes[j][0] = codes[j][1] = 'X';
+
+ // We found a code for i
+ matched = true;
+ }
+ }
+ }
+
+ if(!matched)
+ throw ConvertException("Orientation code %s is invalid", rai.c_str());
+ }
+
+ // Explain what's being done
+ *c->verbose << "Setting orientation of " << c->m_ImageStack.size() << " to " << rai << endl;
+
+ // Set the direction in the image
+ img->SetDirection(itk::Matrix<double,VDim,VDim>(dm));
+}
+
+// Invocations
+template class SetOrientation<double, 2>;
+template class SetOrientation<double, 3>;
+template class SetOrientation<double, 4>;
diff --git a/Submodules/c3d/adapters/SetOrientation.h b/Submodules/c3d/adapters/SetOrientation.h
new file mode 100644
index 0000000..4a5967e
--- /dev/null
+++ b/Submodules/c3d/adapters/SetOrientation.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SetOrientation.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SetOrientation_h_
+#define __SetOrientation_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class SetOrientation : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ SetOrientation(Converter *c) : c(c) {}
+
+ void operator() (std::string rai);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SetSform.cxx b/Submodules/c3d/adapters/SetSform.cxx
new file mode 100644
index 0000000..3f11458
--- /dev/null
+++ b/Submodules/c3d/adapters/SetSform.cxx
@@ -0,0 +1,89 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SetSform.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SetSform.h"
+#include <string>
+#include <iostream>
+#include <itkAffineTransform.h>
+#include <itkTransformFileReader.h>
+#include <itkTransformFactory.h>
+#include "itkResampleImageFilter.h"
+#include "gsGSAffine3DTransform.h"
+
+#include <itkTransformFactory.h>
+
+
+template<unsigned int VDim>
+void MyReadMatrix(const char *fname, itk::Matrix<double,VDim+1,VDim+1> &mat)
+ {
+ ifstream fin(fname);
+ for(size_t i = 0; i < VDim+1; i++)
+ for(size_t j = 0; j < VDim+1; j++)
+ if(fin.good())
+ {
+ fin >> mat[i][j];
+ }
+ else
+ {
+ throw ConvertException("Unable to read matrix %s", fname);
+ }
+ fin.close();
+ }
+
+
+template <class TPixel, unsigned int VDim>
+void
+SetSform<TPixel, VDim>
+::operator() (string fn_tran)
+{
+
+ // Check input availability
+ if(c->m_ImageStack.size() < 1)
+ {
+ cerr << "No image to set the sform" << endl;
+ throw -1;
+ }
+
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Get the transform
+ itk::Matrix<double,VDim+1,VDim+1> matrix;
+ MyReadMatrix<VDim>(fn_tran.c_str(), matrix);
+ vnl_matrix<double> mat(VDim+1,VDim+1,0.0);
+ mat.update( matrix.GetVnlMatrix());
+
+ // Set the matrix
+ img->SetVoxelSpaceToRASPhysicalSpaceMatrix( mat );
+
+ // Put result on stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(img);
+}
+
+// Invocations
+template class SetSform<double, 2>;
+template class SetSform<double, 3>;
+template class SetSform<double, 4>;
diff --git a/Submodules/c3d/adapters/SetSform.h b/Submodules/c3d/adapters/SetSform.h
new file mode 100644
index 0000000..2940a8e
--- /dev/null
+++ b/Submodules/c3d/adapters/SetSform.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SetSform.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SetSform_h_
+#define __SetSform_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class SetSform : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ SetSform(Converter *c) : c(c) {}
+
+ void operator() (string fn_tran);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SignedDistanceTransform.cxx b/Submodules/c3d/adapters/SignedDistanceTransform.cxx
new file mode 100644
index 0000000..b9b5b13
--- /dev/null
+++ b/Submodules/c3d/adapters/SignedDistanceTransform.cxx
@@ -0,0 +1,68 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SignedDistanceTransform.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SignedDistanceTransform.h"
+#include "ThresholdImage.h"
+#include "itkSignedMaurerDistanceMapImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+SignedDistanceTransform<TPixel, VDim>
+::operator() ()
+{
+ // The image is assumed to be binary. If background is non-zero, call binarize
+ // to map the background to zero
+ if(c->m_Background != 0.0)
+ {
+ ThresholdImage<TPixel, VDim> thresh(c);
+ thresh(c->m_Background, c->m_Background, 0.0, 1.0);
+ }
+
+ // Get the last image on the stack
+ ImagePointer image = c->m_ImageStack.back();
+
+ // Construct the connected components filter
+ typedef itk::SignedMaurerDistanceMapImageFilter<ImageType, ImageType> Filter;
+
+ // Describe what we are doing
+ *c->verbose << "Computing signed distance function of #" << c->m_ImageStack.size() << endl;
+
+ // Plug in the filter's components
+ typename Filter::Pointer flt = Filter::New();
+ flt->SetInput(image);
+ flt->SetUseImageSpacing(true);
+ flt->SquaredDistanceOff();
+ flt->Update();
+
+ // Store the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(flt->GetOutput());
+
+}
+
+// Invocations
+template class SignedDistanceTransform<double, 2>;
+template class SignedDistanceTransform<double, 3>;
+template class SignedDistanceTransform<double, 4>;
diff --git a/Submodules/c3d/adapters/SignedDistanceTransform.h b/Submodules/c3d/adapters/SignedDistanceTransform.h
new file mode 100644
index 0000000..249e8f3
--- /dev/null
+++ b/Submodules/c3d/adapters/SignedDistanceTransform.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SignedDistanceTransform.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SignedDistanceTransform_h_
+#define __SignedDistanceTransform_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class SignedDistanceTransform : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ SignedDistanceTransform(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SimpleElasticRegistration.cxx b/Submodules/c3d/adapters/SimpleElasticRegistration.cxx
new file mode 100644
index 0000000..cb148e5
--- /dev/null
+++ b/Submodules/c3d/adapters/SimpleElasticRegistration.cxx
@@ -0,0 +1,733 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SimpleElasticRegistration.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SimpleElasticRegistration.h"
+#include "itkFFTWRealToComplexConjugateImageFilter.h"
+#include "itkFFTWComplexConjugateToRealImageFilter.h"
+#include "itkCastImageFilter.h"
+#include "itkMultiplyImageFilter.h"
+#include "itkImageFileWriter.h"
+#include <vnl/vnl_random.h>
+
+extern "C" {
+#include "cg_user.h"
+}
+
+template <class TPixel, unsigned int VDim>
+double
+cg_func(double *x, long int n)
+{
+ SimpleElasticRegistration<TPixel, VDim> *o = SimpleElasticRegistration<TPixel, VDim>::glob_ref;
+ o->SetX(x);
+ return o->ComputeObjectiveAndGradient(false);
+}
+
+template <class TPixel, unsigned int VDim>
+double
+cg_valgrad(double *g, double *x, long int n)
+{
+ // Compute the objective
+ SimpleElasticRegistration<TPixel, VDim> *o = SimpleElasticRegistration<TPixel, VDim>::glob_ref;
+ o->SetX(x);
+ double rc = o->ComputeObjectiveAndGradient(true);
+
+ // Fill in the gradient
+ for(size_t d = 0; d < VDim; d++)
+ for(size_t i = 0; i < o->fft_n; i++, g++)
+ *g = o->graw[d][i];
+
+ return rc;
+}
+
+template <class TPixel, unsigned int VDim>
+void
+cg_grad(double *g, double *x, long int n)
+{
+ cg_valgrad<TPixel, VDim>(g, x, n);
+}
+
+
+template <class TPixel, unsigned int VDim>
+SimpleElasticRegistration<TPixel, VDim> *
+SimpleElasticRegistration<TPixel, VDim>::glob_ref = NULL;
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::operator() ()
+{
+ // Get reference and moving images from stack
+ this->imov = c->m_ImageStack.back(); c->m_ImageStack.pop_back();
+ this->imsk = c->m_ImageStack.back(); c->m_ImageStack.pop_back();
+ this->iref = c->m_ImageStack.back(); c->m_ImageStack.pop_back();
+
+ // Save a pointer to self for optimization
+ glob_ref = this;
+
+ // Initialize parameters
+ alpha = 40.96; gamma = 1; sigma = 2.0;
+
+ // Initialize the Fourier transform code
+ // We have some trouble. The FFTW format is row-major and ITK is column-major
+ // We simply reverse the size and spacing arrays to deal with it
+ double fft_sp[VDim];
+ for(size_t d = 0; d < VDim; d++)
+ {
+ fft_szc[d] = fft_sz[d] = iref->GetBufferedRegion().GetSize()[VDim - (d+1)];
+ fft_sp[d] = iref->GetSpacing()[VDim - (d+1)];
+ }
+
+ // Adjust the size along the last dimension (per FFTW)
+ fft_szc[VDim-1] = fft_sz[VDim-1] / 2 + 1;
+
+ // Compute size of real/complex arrays
+ fft_n = iref->GetBufferedRegion().GetNumberOfPixels();
+ fft_nc = fft_n * fft_szc[VDim-1] / fft_sz[VDim - 1];
+
+ // Create the output complex array
+ fcmp = (myfftw_complex *) myfftw_malloc(fft_nc * sizeof(myfftw_complex));
+
+ // Initialize all the vector fields
+ for(size_t d = 0; d < VDim; d++)
+ {
+ // The displacement field that's optimized over
+ vraw[d] = (VecType *) myfftw_malloc(fft_n * sizeof(VecType));
+ v[d] = FloatImage::New();
+ v[d]->SetRegions(iref->GetBufferedRegion());
+ v[d]->GetPixelContainer()->SetImportPointer(vraw[d], fft_n, true);
+ v[d]->FillBuffer(0.0f);
+
+ // The gradient vector field
+ graw[d] = (VecType *) myfftw_malloc(fft_n * sizeof(VecType));
+ g[d] = FloatImage::New();
+ g[d]->SetRegions(iref->GetBufferedRegion());
+ g[d]->GetPixelContainer()->SetImportPointer(graw[d], fft_n, true);
+ g[d]->FillBuffer(0.0f);
+
+ // Create the FFT plans for each FFT we will perform
+ f_fft_v[d] = myfftw_plan_dft_r2c(VDim, fft_sz, vraw[d], fcmp, FFTW_ESTIMATE);
+ f_fft_g[d] = myfftw_plan_dft_r2c(VDim, fft_sz, graw[d], fcmp, FFTW_ESTIMATE);
+
+ // Create frequency arrays and cosine arrays for laplacian computation
+ size_t N = fft_sz[d];
+ double c = 6.283185307179586 / (N * fft_sp[d]);
+ kx[d].set_size(N);
+ cx[d].set_size(N);
+ for(size_t j = 0; j <= N - N/2; j++)
+ kx[d][j] = c * j;
+ for(size_t j = 1; j <= N/2; j++)
+ kx[d][N-j] = -kx[d][j];
+ for(size_t j = 0; j < N; j++)
+ {
+ cx[d][j] = 2.0 * (1.0 - cos(kx[d][j]));
+ }
+ }
+
+ // Create the temporary float array for FFT
+ vtmp = (VecType *) myfftw_malloc(fft_n * sizeof(VecType));
+
+ // Create the inverse fft
+ i_fft = myfftw_plan_dft_c2r(VDim, fft_sz, fcmp, vtmp, FFTW_ESTIMATE);
+
+ // Create the sampling function
+ giref = Interpolator::New();
+ giref->SetSplineOrder(3);
+ giref->SetInputImage(iref);
+
+ gimov = Interpolator::New();
+ gimov->SetSplineOrder(3);
+ gimov->SetInputImage(imov);
+
+ // Integrate the mask to save time
+ double mask_sum = 0.0;
+ for(Iterator itmsk(imsk, imsk->GetBufferedRegion()); !itmsk.IsAtEnd(); ++itmsk)
+ mask_sum += itmsk.Value();
+ if(mask_sum <= 0.0)
+ throw(ConvertException("Mask is empty or negative in SimpleElasticRegistration"));
+ this->mask_scale = 1.0 / mask_sum;
+
+ // Test interpolator derivative
+ itk::ContinuousIndex<double, VDim> q;
+ for(size_t d = 0; d < VDim; d++)
+ q[d] = iref->GetBufferedRegion().GetIndex()[d] + 0.5 * iref->GetBufferedRegion().GetSize()[d];
+ cout << "F[q] " << gimov->EvaluateAtContinuousIndex(q) << endl;
+ cout << "G[q] " << gimov->EvaluateDerivativeAtContinuousIndex(q) << endl;
+ for(size_t d = 0; d < VDim; d++)
+ {
+ double eps = 1.0e-6;
+ itk::ContinuousIndex<double, VDim> dq = q;
+ dq[d] = q[d] + eps / imov->GetSpacing()[d];
+ double f1 = gimov->EvaluateAtContinuousIndex(dq);
+ dq[d] = q[d] - eps / imov->GetSpacing()[d];
+ double f2 = gimov->EvaluateAtContinuousIndex(dq);
+ cout << "Dx = " << (f1-f2) / (2 * eps) << endl;
+ }
+
+ // Another test
+ {
+ // Define point x in reference space
+ itk::Index<VDim> idx;
+ for(size_t d = 0; d < VDim; d++)
+ idx[d] = iref->GetBufferedRegion().GetIndex()[d] + iref->GetBufferedRegion().GetSize()[d] / 2;
+ itk::Point<double, VDim> x;
+ itk::ContinuousIndex<double, VDim> qx, qy;
+ iref->TransformIndexToRASPhysicalPoint(idx, x);
+ iref->TransformRASPhysicalPointToContinuousIndex(x, qx);
+ cout << "X = " << x << endl;
+ cout << "QX = " << qx << endl;
+
+ // Define the vector v
+ double w[] = {4.0, 4.0, 4.0};
+ itk::Point<double, VDim> y;
+ for(size_t d = 0; d < VDim; d++)
+ y[d] = x[d] + w[d];
+ iref->TransformRASPhysicalPointToContinuousIndex(y, qy);
+ cout << "Y = " << y << endl;
+ cout << "QY = " << qy << endl;
+
+ double f0 = pow(giref->EvaluateAtContinuousIndex(qx) - gimov->EvaluateAtContinuousIndex(qy),2);
+
+ // Define an offset vector for gradient computation
+ double off[] = {0.5, -1.0, 2.0}, eps = 1.0e-6;
+
+ // Compute the gradient
+ itk::CovariantVector<double, VDim> gmov = gimov->EvaluateDerivativeAtContinuousIndex(qy);
+ cout << "f0 " << f0 << endl;
+ cout << "G[y] " << gmov << endl;
+
+ double ad = 0.0;
+ for(size_t d = 0; d < 3; d++)
+ {
+ ad += off[d] * -2.0 * (giref->EvaluateAtContinuousIndex(qx) - gimov->EvaluateAtContinuousIndex(qy)) * gmov[d];
+ }
+
+ // Shift by off
+ for(size_t d = 0; d < VDim; d++)
+ y[d] = x[d] + w[d] + eps * off[d];
+ iref->TransformRASPhysicalPointToContinuousIndex(y, qy);
+ double f1 = pow(giref->EvaluateAtContinuousIndex(qx) - gimov->EvaluateAtContinuousIndex(qy),2);
+ cout << "qy " << qy << endl;
+ cout << "f1 - f0 " << f1 - f0 << endl;
+
+ // Shift by off
+ for(size_t d = 0; d < VDim; d++)
+ y[d] = x[d] + w[d] - eps * off[d];
+ iref->TransformRASPhysicalPointToContinuousIndex(y, qy);
+ double f2 = pow(giref->EvaluateAtContinuousIndex(qx) - gimov->EvaluateAtContinuousIndex(qy),2);
+ cout << "qy " << qy << endl;
+ cout << "f2 - f0 " << f2 - f0 << endl;
+
+ double nd = (f1 - f2) / (eps * 2.0);
+
+ cout << "AD = " << ad << ", CD = " << nd << " DIFF " << ad - nd << endl;
+
+ }
+
+ // Test Fourier
+ // TestFourier();
+ // TestGradientComputation();
+ // TestGradient2();
+
+ // Reset the vector field to zero
+ for(size_t i = 0; i < fft_n; i++)
+ for(size_t d = 0; d < VDim; d++)
+ vraw[d][i] = 0.0;
+
+ // Compute the objective function
+ *c->verbose << "Performing elastic registration" << endl;
+ *c->verbose << " Initial Objective " << ComputeObjectiveAndGradient(true) << endl;
+
+ // Perform optimization
+ double *xopt = new double[fft_n * VDim];
+ memset(xopt, 0, sizeof(double) * fft_n * VDim);
+
+ cg_parameter cg_parm;
+ cg_default(&cg_parm);
+ cg_parm.PrintLevel=0;
+ cg_parm.maxit_fac = 100.0 / (VDim * fft_n);
+ cg_descent(xopt, fft_n * VDim, NULL, &cg_parm, 1.e-8,
+ cg_func<TPixel,VDim>,
+ cg_grad<TPixel,VDim>,
+ cg_valgrad<TPixel,VDim>, NULL);
+
+ // Do simple gradient descent
+ double dt = 0.01;
+ for(size_t i = 0; i < 40; i++)
+ {
+ for(size_t d = 0; d < 3; d++)
+ {
+ for(size_t k = 0; k < fft_n; k++)
+ {
+ vraw[d][k] -= graw[d][k] * dt;
+ }
+ }
+
+ double f = ComputeObjectiveAndGradient(true);
+ *c->verbose << " Iteration " << i << "\t Objective " << f << endl;
+ }
+
+ // Save transformed image
+ for(size_t d = 0; d < 3; d++)
+ {
+ Iterator itref(iref, iref->GetBufferedRegion());
+ size_t k = 0;
+ for(; !itref.IsAtEnd(); ++itref, ++k)
+ {
+ // Check if we are inside the mask
+ double vmask = imsk->GetBufferPointer()[k];
+ if(vmask == 0.0)
+ {
+ itref.Set(0.0);
+ continue;
+ }
+
+ // Get the index in reference space
+ itk::Point<double, VDim> p;
+ itk::ContinuousIndex<double, VDim> q, qr;
+ IndexType idx_ref = itref.GetIndex();
+ iref->TransformIndexToRASPhysicalPoint(idx_ref, p);
+ iref->TransformRASPhysicalPointToContinuousIndex(p, qr);
+ for(size_t d = 0; d < VDim; d++)
+ p[d] += vraw[d][k];
+ imov->TransformRASPhysicalPointToContinuousIndex(p, q);
+
+ // Get the two image values
+ if(gimov->IsInsideBuffer(q))
+ {
+ // Update the objective
+ double I_mov = gimov->EvaluateAtContinuousIndex(q);
+ itref.Set(I_mov);
+ }
+ else
+ {
+ itref.Set(0.0);
+ }
+ }
+ }
+
+ // Do some processing ...
+ // ImagePointer result = ...;
+
+ string name[] = {"warpxvec.nii", "warpyvec.nii", "warpzvec.nii"};
+ for(size_t d = 0; d < 3; d++)
+ SaveRaw(vraw[d], name[d].c_str());
+
+ // Put result on stack
+ // c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(iref);
+}
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::SetX(double *x)
+{
+ // X is a vector over which we optimize. The actual deformation vector
+ // has the form V = K X, where K is the inverse of L'L, i.e., X is the
+ // solution of the PDE LL V = X
+
+ // Repeat over each dimension
+ for(size_t d = 0; d < VDim; d++)
+ {
+ // Copy x[d] into v[d]
+ for(size_t i = 0; i < fft_n; i++, x++)
+ vraw[d][i] = *x;
+
+ // Solve the PDE
+ SolvePDE(f_fft_v[d], true);
+
+ // Put the solution into v
+ for(size_t i = 0; i < fft_n; i++)
+ vraw[d][i] = vtmp[i] / fft_n;
+ }
+}
+
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::SolvePDE(myfftw_plan &plan, bool forward)
+{
+ // Take the forward FFT of v[d] into fcmp
+ myfftw_execute(plan);
+
+ // Create index
+ int index[VDim];
+ for(size_t d=0;d<VDim;d++)
+ index[d]=0;
+
+ // Apply operator LL to v, where L = (gamma I - alpha del)
+ for(size_t j = 0; j < fft_nc; j++)
+ {
+ double w = gamma;
+ for(size_t d = 0; d < VDim; d++)
+ w += alpha * cx[d][index[d]];
+ double wtotal = w * w;
+
+ if(forward)
+ wtotal = 1.0 / wtotal;
+
+ fcmp[j][0] *= wtotal;
+ fcmp[j][1] *= wtotal;
+
+ // Increment index
+ for(size_t d = VDim-1; d >= 0; d--)
+ {
+ if(++index[d] == fft_szc[d])
+ index[d] = 0;
+ else
+ break;
+ }
+ }
+
+ // Take the inverse fft, put it into vtmp
+ myfftw_execute(i_fft);
+}
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::SaveRaw(VecType *v, const char *fn)
+{
+ typedef itk::Image<VecType, VDim> IType;
+ typedef itk::ImageFileWriter<IType> WType;
+ typename IType::Pointer i = IType::New();
+ itk::ImageRegion<VDim> r;
+ for(size_t d = 0; d < VDim; d++)
+ {
+ r.SetIndex(d,0);
+ r.SetSize(d, iref->GetBufferedRegion().GetSize(d));
+ }
+ i->SetRegions(r);
+ i->GetPixelContainer()->SetImportPointer(v, false);
+ typename WType::Pointer w = WType::New();
+ w->SetFileName(fn);
+ w->SetInput(i);
+ w->Update();
+}
+
+
+template <class TPixel, unsigned int VDim>
+double
+SimpleElasticRegistration<TPixel, VDim>
+::ComputeObjectiveAndGradient(bool need_gradient)
+{
+ // Compute the gradient of the image objective
+ double f_img = 0.0;
+ double sfactor = 2.0 / (sigma * sigma);
+ Iterator itref(iref, iref->GetBufferedRegion());
+ size_t k = 0;
+
+ // Clear the gradient
+ if(need_gradient)
+ {
+ for(size_t d = 0; d < VDim; d++)
+ for(size_t i = 0; i < fft_n; i++)
+ graw[d][i] = 0.0;
+ }
+
+ for(; !itref.IsAtEnd(); ++itref, ++k)
+ {
+ // Check if we are inside the mask
+ double vmask = imsk->GetBufferPointer()[k];
+ if(vmask == 0.0)
+ continue;
+
+ // Get the index in reference space
+ itk::Point<double, VDim> p;
+ itk::ContinuousIndex<double, VDim> q, qr;
+ IndexType idx_ref = itref.GetIndex();
+ iref->TransformIndexToRASPhysicalPoint(idx_ref, p);
+ iref->TransformRASPhysicalPointToContinuousIndex(p, qr);
+ for(size_t d = 0; d < VDim; d++)
+ p[d] += vraw[d][k];
+ imov->TransformRASPhysicalPointToContinuousIndex(p, q);
+
+ // Get the two image values
+ if(gimov->IsInsideBuffer(q))
+ {
+ // Update the objective
+ double I_ref = giref->EvaluateAtContinuousIndex(qr);
+ double I_mov = gimov->EvaluateAtContinuousIndex(q);
+ f_img += vmask * (I_ref - I_mov) * (I_ref - I_mov);
+
+ // Update the gradient of the objective
+ if(need_gradient)
+ {
+ itk::CovariantVector<double, VDim> gidx, gmov = gimov->EvaluateDerivativeAtContinuousIndex(q);
+ // imov->TransformLocalVectorToPhysicalVector(gmov, gidx);
+ for(size_t d = 0; d < VDim; d++)
+ {
+ // Sfactor = 2.0 / sigma^2
+ graw[d][k] = - sfactor * vmask * (I_ref - I_mov) * gmov[d];
+
+ }
+ }
+ }
+ }
+
+ // Compute the regularization component of the energy
+ double f_reg = 0.0;
+
+ for(size_t d = 0; d < VDim; d++)
+ {
+ // Take the forward FFT of v[d] into fcmp
+ SolvePDE(f_fft_v[d], false);
+
+ // Compute the sum of the norm over all voxels
+ double normv = 0.0;
+ for(size_t i = 0; i < fft_n; i++)
+ normv += vtmp[i] * v[d]->GetBufferPointer()[i];
+
+ // Correct for scaling introduced by the FFT
+ normv /= fft_n;
+
+ // Add the norm to the total objective
+ f_reg += normv;
+
+ // If gradient is needed, solve the PDE with the previously computed gradient
+ if(need_gradient)
+ {
+ // Take the gradient vector in L2 space and map it to Sobolev space
+ SolvePDE(f_fft_g[d], true);
+
+ // Update the gradient as g = 2 v + K (g_int)
+ for(size_t i = 0; i < fft_n; i++)
+ graw[d][i] = 2.0 * vraw[d][i] + vtmp[i] / fft_n;
+
+ // Update the gradient as g = 2 L L v - g_int
+ // for(size_t i = 0; i < fft_n; i++)
+ // graw[d][i] = 2.0 * vtmp[i] / fft_n + graw[d][i];
+
+
+ }
+ }
+
+ double f_total = f_img / (sigma * sigma) + f_reg;
+
+ printf("RMSE(mask) = %8.4f VMAG(vol) = %8.4f TOTAL(vol) = %8.4f \n",
+ sqrt(f_img * mask_scale),
+ sqrt(f_reg / fft_n),
+ f_total / fft_n);
+
+ return f_total;
+}
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::RandomVectorField()
+{
+ vnl_random randy;
+
+ // Fill the vectors
+ for(size_t d = 0; d < VDim; d++)
+ {
+ // Create index
+ int index[VDim];
+ for(size_t q=0;q<VDim;q++)
+ index[q]=0;
+
+ for(size_t i = 0; i < fft_nc; i++)
+ {
+ bool lowfrq = true;
+ for(size_t q = 0; q < VDim; q++)
+ if(index[q] > 5) lowfrq = false;
+ if(lowfrq)
+ {
+ fcmp[i][0] = randy.drand32(-1.0, 1.0);
+ fcmp[i][1] = randy.drand32(-1.0, 1.0);
+ }
+ else
+ {
+ fcmp[i][0] = 0.0;
+ fcmp[i][1] = 0.0;
+ }
+
+ // Increment index
+ for(size_t d = VDim-1; d >= 0; d--)
+ {
+ if(++index[d] == fft_szc[d])
+ index[d] = 0;
+ else
+ break;
+ }
+ }
+
+ myfftw_execute(i_fft);
+ for(size_t i = 0; i < fft_n; i++)
+ vraw[d][i] = 4 * vtmp[i] / 125;
+ }
+
+ typename itk::ImageFileWriter<FloatImage>::Pointer writer = itk::ImageFileWriter<FloatImage>::New();
+ writer->SetInput(v[0]);
+ writer->SetFileName("dfield.nii");
+ writer->Update();
+}
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::TestGradientComputation()
+{
+ // Create a random vector field
+ RandomVectorField();
+
+ // Evaluate the function and the gradient
+ double fc = this->ComputeObjectiveAndGradient(true);
+
+ // Random number generator
+ vnl_random randy;
+ double eps = 1.0e-4;
+
+ // Create a random offset in V and compute directional derivative in the
+ // direction of the offset
+ VecType *offraw[VDim];
+ for(size_t d = 0; d < VDim; d++)
+ offraw[d] = (VecType *) myfftw_malloc(sizeof(VecType) * fft_n);
+
+ // Compute forward and backward finite differences
+ for(size_t k = 0; k < 10; k++)
+ {
+ double dirderiv = 0.0;
+ for(size_t d = 0; d < VDim; d++)
+ {
+ for(size_t i = 0; i < fft_n; i++)
+ {
+ offraw[d][i] = randy.drand32(-1.0, 1.0);
+ dirderiv += graw[d][i] * offraw[d][i];
+ }
+
+ // Now solve the PDE, i.e., replace offset in L2 space with offset in Sobolev
+ myfftw_plan poff = myfftw_plan_dft_r2c(VDim, fft_sz, offraw[d], fcmp, FFTW_ESTIMATE);
+ SolvePDE(poff, true);
+ myfftw_destroy_plan(poff);
+ for(size_t i = 0; i < fft_n; i++)
+ offraw[d][i] = vtmp[i] / fft_n;
+ }
+
+ double f[2], doff[] = {1.0, -1.0};
+ for(size_t q = 0; q < 2; q++)
+ {
+ // Apply offset to v (in Sobolev space)
+ for(size_t d = 0; d < VDim; d++)
+ for(size_t i = 0; i < fft_n; i++)
+ vraw[d][i] += doff[q] * eps * offraw[d][i];
+
+ // Compute offset
+ f[q] = ComputeObjectiveAndGradient(false);
+
+ // Unapply offset to v (in Sobolev space)
+ for(size_t d = 0; d < VDim; d++)
+ for(size_t i = 0; i < fft_n; i++)
+ vraw[d][i] -= doff[q] * eps * offraw[d][i];
+ }
+
+ // Compute the deriv
+ double cdiffder = (f[0] - f[1]) / (2 * eps);
+
+ cout << "FUNCTION " << fc << "\t";
+ cout << "ANALYTIC " << dirderiv << "\t";
+ cout << "NUMERIC " << cdiffder << endl;
+ }
+}
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::TestGradient2()
+{
+ vnl_random randy;
+
+ // Create a random array X
+ double *X = new double[VDim * fft_n];
+ for(size_t i = 0; i < VDim * fft_n; i++)
+ X[i] = randy.drand32(-40.0, 40.0);
+
+ // Create an array to store the gradient
+ double *G = new double[VDim * fft_n];
+ double *O = new double[VDim * fft_n];
+
+ // Call the same function the optimizer calls
+ double fc = cg_valgrad<TPixel,VDim>(G, X, VDim * fft_n);
+ cout << "TestGradient2 Solution " << fc << endl;
+ SaveRaw(vraw[0], "gradient2field.nii");
+
+ for(size_t q = 0; q < 10; q++)
+ {
+ // Create a random offset vector
+ for(size_t i = 0; i < VDim * fft_n; i++)
+ O[i] = randy.drand32(-1.0, 1.0);
+
+ // Compute the variational derivative in the offset direction
+ double ad= 0.0;
+ for(size_t i = 0; i < VDim * fft_n; i++)
+ ad += O[i] * G[i];
+
+ // Compute central difference approximation
+ double f[2], dx[] = {-1.0, 1.0}, eps = 1.0e-4;
+ for(size_t s = 0; s < 2; s++)
+ {
+ // add the offset
+ for(size_t i = 0; i < VDim * fft_n; i++)
+ X[i] += dx[s] * eps * O[i];
+
+ // compute objective
+ f[s] = cg_func<TPixel,VDim>(X, VDim * fft_n);
+
+ // subtract the offset
+ for(size_t i = 0; i < VDim * fft_n; i++)
+ X[i] -= dx[s] * eps * O[i];
+ }
+
+ double nd = (f[1] - f[0]) / (2 * eps);
+ printf("ANALYTIC %12.8f \t NUMERIC %12.8f \t DIFFERENCE %12.8f\n", ad, nd, ad-nd);
+ }
+
+ delete X;
+ delete O;
+ delete G;
+}
+
+template <class TPixel, unsigned int VDim>
+void
+SimpleElasticRegistration<TPixel, VDim>
+::TestFourier()
+{
+ // Create a plan for the reference image
+ myfftw_plan plan = myfftw_plan_dft_r2c(VDim, fft_sz, iref->GetBufferPointer(), fcmp, FFTW_ESTIMATE);
+ SolvePDE(plan, true);
+ for(size_t i = 0; i < fft_n; i++) vtmp[i] /= fft_n;
+ SaveRaw(vtmp, "iref_pde_forward.nii");
+ SolvePDE(plan, false);
+ for(size_t i = 0; i < fft_n; i++) vtmp[i] /= fft_n;
+ SaveRaw(vtmp, "iref_pde_inverse.nii");
+}
+
+
+// Invocations
+template class SimpleElasticRegistration<double, 2>;
+template class SimpleElasticRegistration<double, 3>;
+template class SimpleElasticRegistration<double, 4>;
diff --git a/Submodules/c3d/adapters/SimpleElasticRegistration.h b/Submodules/c3d/adapters/SimpleElasticRegistration.h
new file mode 100644
index 0000000..7213f93
--- /dev/null
+++ b/Submodules/c3d/adapters/SimpleElasticRegistration.h
@@ -0,0 +1,133 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SimpleElasticRegistration.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SimpleElasticRegistration_h_
+#define __SimpleElasticRegistration_h_
+
+#include "ConvertAdapter.h"
+#include "itkCovariantVector.h"
+#include "itkBSplineInterpolateImageFunction.h"
+#include <fftw3.h>
+
+typedef double VecType;
+
+#define DOUBLE_FFT
+#ifdef DOUBLE_FFT
+
+#define myfftw_complex fftw_complex
+#define myfftw_plan fftw_plan
+#define myfftw_malloc fftw_malloc
+#define myfftw_execute fftw_execute
+#define myfftw_plan_dft_r2c fftw_plan_dft_r2c
+#define myfftw_plan_dft_c2r fftw_plan_dft_c2r
+#define myfftw_destroy_plan fftw_destroy_plan
+
+#else
+
+#define myfftw_complex fftwf_complex
+#define myfftw_plan fftwf_plan
+#define myfftw_malloc fftwf_malloc
+#define myfftw_execute fftwf_execute
+#define myfftw_plan_dft_r2c fftwf_plan_dft_r2c
+#define myfftw_plan_dft_c2r fftwf_plan_dft_c2r
+#define myfftw_destroy_plan fftwf_destroy_plan
+
+#endif
+
+template<class TPixel, unsigned int VDim>
+class SimpleElasticRegistration : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ // Other typedefs
+ typedef itk::CovariantVector<VecType, VDim> VectorType;
+ typedef itk::OrientedRASImage<VectorType, VDim> VectorImageType;
+ typedef typename VectorImageType::Pointer VectorImagePointer;
+ typedef typename itk::ImageRegionIteratorWithIndex<VectorImageType> VectorIterator;
+ typedef itk::OrientedRASImage<VecType, VDim> FloatImage;
+
+ // typedef itk::GaussianInterpolateImageFunction<ImageType, double> GaussInterpolator;
+ typedef itk::BSplineInterpolateImageFunction<ImageType, double> Interpolator;
+
+ SimpleElasticRegistration(Converter *c) : c(c) {}
+
+ void operator() ();
+
+
+
+ Converter *c;
+
+ double ComputeObjectiveAndGradient(bool need_gradient);
+ static SimpleElasticRegistration<TPixel, VDim> *glob_ref;
+
+ void SetX(double *x);
+ void TestGradientComputation();
+ void TestGradient2();
+ void RandomVectorField();
+ void SaveRaw(VecType *, const char *);
+ void TestFourier();
+
+ // Meaning of 'forward' flag:
+ // true: solve LL x = y for unknown x
+ // false: compute y = LL x for unknown y
+ void SolvePDE(myfftw_plan &plan, bool forward);
+
+ // Stuff for optimization
+ ImagePointer iref, imsk, imov;
+ typename FloatImage::Pointer ikernel;
+
+ // The vector field can't be stored as an image of vectors because of FFT. Instead,
+ // we store it as a bunch of floating point images
+ typename FloatImage::Pointer v[VDim], g[VDim];
+
+ // Interpolators
+ typename Interpolator::Pointer giref, gimov;
+
+ // FFTW plans
+ myfftw_plan f_fft_v[VDim], f_fft_g[VDim], i_fft;
+
+ // Sizes of arrays for fft, etc
+ int fft_sz[VDim], fft_szc[VDim];
+ size_t fft_n, fft_nc;
+
+ // Scaling arrays for laplacian operator computations
+ vnl_vector<double> kx[VDim], cx[VDim];
+
+ // Complex array
+ myfftw_complex *fcmp;
+ VecType *vraw[VDim], *graw[VDim], *vtmp;
+
+ // Alpha, gamma factors
+ double alpha, gamma, sigma;
+
+
+ // Mask scaling factor
+ double mask_scale;
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SmoothImage.cxx b/Submodules/c3d/adapters/SmoothImage.cxx
new file mode 100644
index 0000000..13b2ddb
--- /dev/null
+++ b/Submodules/c3d/adapters/SmoothImage.cxx
@@ -0,0 +1,88 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SmoothImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SmoothImage.h"
+#include "itkDiscreteGaussianImageFilter.h"
+#include "itkSmoothingRecursiveGaussianImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+SmoothImage<TPixel, VDim>
+::operator() (RealVector &stdev, bool do_recursive)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Which filter to use
+ if(do_recursive)
+ {
+ // Describe what we are doing
+ *c->verbose << "Fast recursive smoothing #" << c->m_ImageStack.size() << " with std.dev. " << stdev << endl;
+
+ // Create a smoothing filter
+ typedef itk::SmoothingRecursiveGaussianImageFilter<ImageType, ImageType> FilterType;
+ typename FilterType::Pointer fltSmooth = FilterType::New();
+ typename FilterType::SigmaArrayType sarray;
+
+ for(int i = 0; i < VDim; i++)
+ sarray[i] = stdev[i];
+
+ fltSmooth->SetInput(input);
+ fltSmooth->SetSigmaArray(sarray);
+ fltSmooth->Update();
+
+ // Save the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(fltSmooth->GetOutput());
+ }
+ else
+ {
+ // Describe what we are doing
+ *c->verbose << "Smoothing #" << c->m_ImageStack.size() << " with std.dev. " << stdev << endl;
+
+ // Create a smoothing kernel and use it
+ typedef itk::DiscreteGaussianImageFilter<ImageType,ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ typename FilterType::ArrayType variance;
+
+ for(size_t i = 0; i < VDim; i++)
+ variance[i] = stdev[i] * stdev[i];
+
+ filter->SetInput(input);
+ filter->SetVariance(variance);
+ filter->SetUseImageSpacingOn();
+ filter->Update();
+
+ // Save the output
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+ }
+}
+
+// Invocations
+template class SmoothImage<double, 2>;
+template class SmoothImage<double, 3>;
+template class SmoothImage<double, 4>;
+
diff --git a/Submodules/c3d/adapters/SmoothImage.h b/Submodules/c3d/adapters/SmoothImage.h
new file mode 100644
index 0000000..961f419
--- /dev/null
+++ b/Submodules/c3d/adapters/SmoothImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SmoothImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SmoothImage_h_
+#define __SmoothImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class SmoothImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ SmoothImage(Converter *c) : c(c) {}
+
+ void operator() (RealVector &stdev, bool do_recursive);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/SplitMultilabelImage.cxx b/Submodules/c3d/adapters/SplitMultilabelImage.cxx
new file mode 100644
index 0000000..8f68f19
--- /dev/null
+++ b/Submodules/c3d/adapters/SplitMultilabelImage.cxx
@@ -0,0 +1,85 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SplitMultilabelImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "SplitMultilabelImage.h"
+#include "ThresholdImage.h"
+#include "UpdateMetadataKey.h"
+#include <set>
+#include "vnl/vnl_math.h"
+
+using namespace std;
+
+template <class TPixel, unsigned int VDim>
+void
+SplitMultilabelImage<TPixel, VDim>
+::operator() ()
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Create a list of all the finite values in the image
+ set<double> sval;
+ for(ConstIterator it(img, img->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ double val = it.Get();
+ if(vnl_math_isfinite(val))
+ sval.insert(val);
+ }
+
+ // The number of finite values should be reasonable
+ if(sval.size() > 255)
+ throw ConvertException("Number of labels passed on to -split exceeds 255");
+ else if(sval.size() == 0)
+ throw ConvertException("No finite labels passed on to -split");
+
+ // A report
+ *c->verbose << "Splitting #" << c->m_ImageStack.size() << " into "
+ << sval.size() << " binary images" << endl;
+
+ // Pop the image
+ c->m_ImageStack.pop_back();
+
+ // Clear the label set
+ c->GetSplitLabelSet();
+
+ // Generate a bunch of binary copies (unfortunately, we store them as double)
+ for(set<double>::iterator it = sval.begin(); it != sval.end(); ++it)
+ {
+ // Push our image back on the stack
+ c->m_ImageStack.push_back(img);
+
+ // Perform thresholding
+ ThresholdImage<TPixel, VDim> thresh(c);
+ thresh(*it, *it, 1.0, 0.0);
+
+ // Add label to the label set
+ c->GetSplitLabelSet().push_back(*it);
+ }
+}
+
+// Invocations
+template class SplitMultilabelImage<double, 2>;
+template class SplitMultilabelImage<double, 3>;
+template class SplitMultilabelImage<double, 4>;
diff --git a/Submodules/c3d/adapters/SplitMultilabelImage.h b/Submodules/c3d/adapters/SplitMultilabelImage.h
new file mode 100644
index 0000000..65b9414
--- /dev/null
+++ b/Submodules/c3d/adapters/SplitMultilabelImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: SplitMultilabelImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __SplitMultilabelImage_h_
+#define __SplitMultilabelImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class SplitMultilabelImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ SplitMultilabelImage(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/StapleAlgorithm.cxx b/Submodules/c3d/adapters/StapleAlgorithm.cxx
new file mode 100644
index 0000000..3bde1b2
--- /dev/null
+++ b/Submodules/c3d/adapters/StapleAlgorithm.cxx
@@ -0,0 +1,71 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: StapleAlgorithm.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "StapleAlgorithm.h"
+#include "itkSTAPLEImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+StapleAlgorithm<TPixel, VDim>
+::operator() (double ival)
+{
+ size_t i;
+
+ // Create a STAPLE filter
+ typedef itk::STAPLEImageFilter<ImageType, ImageType> Filter;
+ typename Filter::Pointer fltStaple = Filter::New();
+
+ // Add each of the images on the stack to the filter
+ for(i = 0; i < c->m_ImageStack.size(); i++)
+ fltStaple->SetInput(i, c->m_ImageStack[i]);
+
+ // Configure the STAPLE filter
+ fltStaple->SetForegroundValue(ival);
+
+ // Describe what we are doing
+ *c->verbose << "Executing STAPLE EM Algorithm on " << c->m_ImageStack.size() << " images." << endl;
+
+ // Plug in the filter's components
+ fltStaple->Update();
+
+ // Dump sensitivity/specificity values
+ *c->verbose << " Elapsed Iterations: " << fltStaple->GetElapsedIterations() << endl;
+ for(i = 0; i < c->m_ImageStack.size(); i++)
+ {
+ *c->verbose << " Rater " << i
+ << ": Sensitivity = " << fltStaple->GetSensitivity(i)
+ << "; Specificity = " << fltStaple->GetSpecificity(i) << endl;
+ }
+
+ // Store the output
+ c->m_ImageStack.clear();
+ c->m_ImageStack.push_back(fltStaple->GetOutput());
+
+}
+
+// Invocations
+template class StapleAlgorithm<double, 2>;
+template class StapleAlgorithm<double, 3>;
+template class StapleAlgorithm<double, 4>;
diff --git a/Submodules/c3d/adapters/StapleAlgorithm.h b/Submodules/c3d/adapters/StapleAlgorithm.h
new file mode 100644
index 0000000..8d0b5df
--- /dev/null
+++ b/Submodules/c3d/adapters/StapleAlgorithm.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: StapleAlgorithm.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __StapleAlgorithm_h_
+#define __StapleAlgorithm_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class StapleAlgorithm : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ StapleAlgorithm(Converter *c) : c(c) {}
+
+ void operator() (double ival);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/TestImage.cxx b/Submodules/c3d/adapters/TestImage.cxx
new file mode 100644
index 0000000..96b34eb
--- /dev/null
+++ b/Submodules/c3d/adapters/TestImage.cxx
@@ -0,0 +1,94 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: TestImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "TestImage.h"
+#include <itkAbsoluteValueDifferenceImageFilter.h>
+#include <itkMinimumMaximumImageFilter.h>
+
+template <class TPixel, unsigned int VDim>
+void
+TestImage<TPixel, VDim>
+::operator() (bool test_header, bool test_voxels, double tol)
+{
+ // There must be two images on the stack
+ if(c->m_ImageStack.size() < 2)
+ throw ConvertException("Two images are requred for the test command");
+
+ // Load the two images from the stack
+ ImagePointer x = c->m_ImageStack.back(); c->m_ImageStack.pop_back();
+ ImagePointer y = c->m_ImageStack.back(); c->m_ImageStack.pop_back();
+
+ // Compare the headers
+ if(test_header)
+ {
+ double max_abs_diff = 0;
+ RegionType rx = x->GetLargestPossibleRegion(), ry = y->GetLargestPossibleRegion();
+ for(int i = 0; i < VDim; i++)
+ {
+ max_abs_diff = vnl_math_max(max_abs_diff, vnl_math_abs((double) (rx.GetSize()[i] - ry.GetSize()[i])));
+ max_abs_diff = vnl_math_max(max_abs_diff, vnl_math_abs((double) (rx.GetIndex()[i] - ry.GetIndex()[i])));
+ max_abs_diff = vnl_math_max(max_abs_diff, vnl_math_abs((double) (x->GetOrigin()[i] - y->GetOrigin()[i])));
+ max_abs_diff = vnl_math_max(max_abs_diff, vnl_math_abs((double) (x->GetSpacing()[i] - y->GetSpacing()[i])));
+ for(int j = 0; j < VDim; j++)
+ {
+ max_abs_diff = vnl_math_max(max_abs_diff, vnl_math_abs((double) (x->GetDirection()[i][j] - y->GetDirection()[i][j])));
+ }
+ }
+
+ if(max_abs_diff > tol)
+ {
+ std::cout << "Image header test failed. Max abs difference: " << max_abs_diff << endl;
+ std::exit(1);
+ }
+ }
+
+ if(test_voxels)
+ {
+ typedef typename itk::AbsoluteValueDifferenceImageFilter<ImageType, ImageType, ImageType> DiffFilter;
+ typedef typename itk::MinimumMaximumImageFilter<ImageType> MinMaxFilter;
+ typename DiffFilter::Pointer fltDiff = DiffFilter::New();
+ fltDiff->SetInput(0, x);
+ fltDiff->SetInput(1, y);
+
+ typename MinMaxFilter::Pointer fltMinMax = MinMaxFilter::New();
+ fltMinMax->SetInput(fltDiff->GetOutput());
+ fltMinMax->Update();
+
+ double max_abs_diff = fltMinMax->GetMaximumOutput()->Get();
+ if(max_abs_diff > tol)
+ {
+ std::cout << "Image voxel test failed. Max abs difference: " << max_abs_diff << endl;
+ std::exit(1);
+ }
+ }
+
+ // Test succeeded
+ exit(0);
+}
+
+// Invocations
+template class TestImage<double, 2>;
+template class TestImage<double, 3>;
+template class TestImage<double, 4>;
diff --git a/Submodules/c3d/adapters/TestImage.h b/Submodules/c3d/adapters/TestImage.h
new file mode 100644
index 0000000..561134d
--- /dev/null
+++ b/Submodules/c3d/adapters/TestImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: TestImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __TestImage_h_
+#define __TestImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class TestImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ TestImage(Converter *c) : c(c) {}
+
+ void operator() (bool test_header, bool test_image, double tol);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/ThresholdImage.cxx b/Submodules/c3d/adapters/ThresholdImage.cxx
new file mode 100644
index 0000000..9f6c7cd
--- /dev/null
+++ b/Submodules/c3d/adapters/ThresholdImage.cxx
@@ -0,0 +1,59 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ThresholdImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ThresholdImage.h"
+#include "itkBinaryThresholdImageFilter.h"
+
+template <class TPixel, unsigned int VDim>
+void
+ThresholdImage<TPixel, VDim>
+::operator() (double u1, double u2, double vIn, double vOut)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Say what we are doing
+ *c->verbose << "Thresholding #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Mapping range [" << u1 << ", " << u2 << "] to " << vIn << endl;
+ *c->verbose << " Values outside are mapped to " << vOut << endl;
+
+ // Do the thresholding
+ typedef itk::BinaryThresholdImageFilter<ImageType, ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(input);
+ filter->SetLowerThreshold(u1);
+ filter->SetUpperThreshold(u2);
+ filter->SetInsideValue(vIn);
+ filter->SetOutsideValue(vOut);
+ filter->Update();
+
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(filter->GetOutput());
+}
+
+// Invocations
+template class ThresholdImage<double, 2>;
+template class ThresholdImage<double, 3>;
+template class ThresholdImage<double, 4>;
diff --git a/Submodules/c3d/adapters/ThresholdImage.h b/Submodules/c3d/adapters/ThresholdImage.h
new file mode 100644
index 0000000..455e208
--- /dev/null
+++ b/Submodules/c3d/adapters/ThresholdImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ThresholdImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __ThresholdImage_h_
+#define __ThresholdImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class ThresholdImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ ThresholdImage(Converter *c) : c(c) {}
+
+ void operator() (double u1, double u2, double vIn, double vOut);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/TileImages.cxx b/Submodules/c3d/adapters/TileImages.cxx
new file mode 100644
index 0000000..acf2067
--- /dev/null
+++ b/Submodules/c3d/adapters/TileImages.cxx
@@ -0,0 +1,94 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: TileImages.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "TileImages.h"
+
+#include <itkTileImageFilter.h>
+
+template <class TPixel, unsigned int VDim>
+void
+TileImages<TPixel, VDim>
+::operator() (const std::string &tileParam)
+{
+ // Create the tile filter
+ typedef typename itk::TileImageFilter<ImageType, ImageType> TileFilterType;
+ typename TileFilterType::Pointer fltTile = TileFilterType::New();
+
+ // Add all of the images as input to the tile filter
+ for(int i = 0; i < c->m_ImageStack.size(); i++)
+ {
+ fltTile->SetInput(i, c->m_ImageStack[i]);
+ }
+
+ // Set the layout. The layout can either be a letter (x, y, z) which means tiling
+ // in one dimension (i.e., tile a bunch of pngs) or it can be an integer vector
+ // 2x2x0 which matches the input of the tile filter
+ typename TileFilterType::LayoutArrayType loArray;
+
+ if(tileParam == "x" || tileParam == "X" || tileParam == "0")
+ {
+ loArray.Fill(1);
+ loArray[0] = c->m_ImageStack.size();
+ }
+ else if(tileParam == "y" || tileParam == "Y" || tileParam == "1")
+ {
+ loArray.Fill(1);
+ loArray[1] = c->m_ImageStack.size();
+ }
+ else if(tileParam == "z" || tileParam == "Z" || tileParam == "2")
+ {
+ if(VDim < 3) throw ConvertException("Can not tile in z-dimension using c2d, use c3d");
+ loArray.Fill(1);
+ loArray[2] = c->m_ImageStack.size();
+ }
+ else if(tileParam == "w" || tileParam == "W" || tileParam == "t" || tileParam == "T" || tileParam == "3")
+ {
+ if(VDim < 4) throw ConvertException("Can not tile in w-dimension using c3d, use c4d");
+ loArray.Fill(1);
+ loArray[3] = c->m_ImageStack.size();
+ }
+ else
+ {
+ SizeType sz = c->ReadSizeVector(tileParam.c_str());
+ for(int i = 0; i < VDim; i++)
+ loArray[i] = sz[i];
+ }
+
+ fltTile->SetLayout(loArray);
+
+ *c->verbose << "Tiling " << c->m_ImageStack.size()
+ << " images using layout " << loArray << endl;
+
+ fltTile->Update();
+
+ // Put result on stack
+ c->m_ImageStack.clear();
+ c->m_ImageStack.push_back(fltTile->GetOutput());
+}
+
+// Invocations
+template class TileImages<double, 2>;
+template class TileImages<double, 3>;
+template class TileImages<double, 4>;
diff --git a/Submodules/c3d/adapters/TileImages.h b/Submodules/c3d/adapters/TileImages.h
new file mode 100644
index 0000000..9819a84
--- /dev/null
+++ b/Submodules/c3d/adapters/TileImages.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: TileImages.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __TileImages_h_
+#define __TileImages_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class TileImages : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ TileImages(Converter *c) : c(c) {}
+
+ void operator() (const std::string &tileParam);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/TrimImage.cxx b/Submodules/c3d/adapters/TrimImage.cxx
new file mode 100644
index 0000000..b23bff0
--- /dev/null
+++ b/Submodules/c3d/adapters/TrimImage.cxx
@@ -0,0 +1,104 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: TrimImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "TrimImage.h"
+#include "ExtractRegion.h"
+
+template<unsigned int VDim>
+void ExpandRegion(itk::ImageRegion<VDim> ®ion, const itk::Index<VDim> &idx)
+{
+ if(region.GetNumberOfPixels() == 0)
+ {
+ region.SetIndex(idx);
+ for(size_t i = 0; i < VDim; i++)
+ region.SetSize(i, 1);
+ }
+ else {
+ for(size_t i = 0; i < VDim; i++)
+ {
+ if(region.GetIndex(i) > idx[i])
+ {
+ region.SetSize(i, region.GetSize(i) + (region.GetIndex(i) - idx[i]));
+ region.SetIndex(i, idx[i]);
+ }
+ else if(region.GetIndex(i) + (long) region.GetSize(i) <= idx[i]) {
+ region.SetSize(i, 1 + idx[i] - region.GetIndex(i));
+ }
+ }
+ }
+}
+
+template <class TPixel, unsigned int VDim>
+void
+TrimImage<TPixel, VDim>
+::operator() (const RealVector &vec, TrimMode mode)
+{
+ // Get the input image
+ ImagePointer input = c->m_ImageStack.back();
+
+ // Debugging info
+ *c->verbose << "Trimming #" << c->m_ImageStack.size() << endl;
+
+
+ // Initialize the bounding box
+ RegionType bbox;
+
+ // Find the extent of the non-background region of the image
+ Iterator it(input, input->GetBufferedRegion());
+ for( ; !it.IsAtEnd(); ++it)
+ if(it.Value() != c->m_Background)
+ ExpandRegion(bbox, it.GetIndex());
+
+ // Pad the region by radius specified by user
+ if(mode == SPECIFY_MARGIN)
+ {
+ *c->verbose << " Wrapping non-background voxels with margin of " << vec << " mm." << endl;
+ typename ImageType::SizeType radius;
+ for(size_t i = 0; i < VDim; i++)
+ radius[i] = (int) ceil(vec[i] / input->GetSpacing()[i]);
+ bbox.PadByRadius(radius);
+ }
+ else if(mode == SPECIFY_FINALSIZE)
+ {
+ *c->verbose << " Wrapping non-background voxels to create a region of size " << vec << " mm." << endl;
+ // Compute the radius to pad to
+ for(size_t i = 0; i < VDim; i++)
+ {
+ int sznew = (int) (0.5 + vec[i] / input->GetSpacing()[i]);
+ int ixnew = bbox.GetIndex()[i] + bbox.GetSize()[i]/2 - sznew/2;
+ bbox.SetIndex(i, ixnew);
+ bbox.SetSize(i, sznew);
+ }
+ }
+
+ // Use the extract region code for the rest
+ ExtractRegion<TPixel, VDim> extract(c);
+ extract(bbox);
+}
+
+// Invocations
+template class TrimImage<double, 2>;
+template class TrimImage<double, 3>;
+template class TrimImage<double, 4>;
diff --git a/Submodules/c3d/adapters/TrimImage.h b/Submodules/c3d/adapters/TrimImage.h
new file mode 100644
index 0000000..ff50aac
--- /dev/null
+++ b/Submodules/c3d/adapters/TrimImage.h
@@ -0,0 +1,52 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: TrimImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __TrimImage_h_
+#define __TrimImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class TrimImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ TrimImage(Converter *c) : c(c) {}
+
+ enum TrimMode {
+ SPECIFY_MARGIN,
+ SPECIFY_FINALSIZE };
+
+ void operator() (const RealVector &vec, TrimMode mode);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/UnaryMathOperation.cxx b/Submodules/c3d/adapters/UnaryMathOperation.cxx
new file mode 100644
index 0000000..3e2526c
--- /dev/null
+++ b/Submodules/c3d/adapters/UnaryMathOperation.cxx
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: UnaryMathOperation.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "UnaryMathOperation.h"
+
+template <class TPixel, unsigned int VDim>
+void
+UnaryMathOperation<TPixel, VDim>
+::operator() (double (*func) (double))
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Print debug info
+ *c->verbose << "Applying unary math operation to #" << c->m_ImageStack.size() << endl;
+
+ // Apply the appropriate math operation (in place)
+ Iterator it(img, img->GetBufferedRegion());
+ for(; !it.IsAtEnd(); ++it)
+ it.Set(func(it.Get()));
+}
+
+// Invocations
+template class UnaryMathOperation<double, 2>;
+template class UnaryMathOperation<double, 3>;
+template class UnaryMathOperation<double, 4>;
diff --git a/Submodules/c3d/adapters/UnaryMathOperation.h b/Submodules/c3d/adapters/UnaryMathOperation.h
new file mode 100644
index 0000000..94e04d4
--- /dev/null
+++ b/Submodules/c3d/adapters/UnaryMathOperation.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: UnaryMathOperation.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __UnaryMathOperation_h_
+#define __UnaryMathOperation_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class UnaryMathOperation : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ UnaryMathOperation(Converter *c) : c(c) {}
+
+ void operator() (double (*func)(double));
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/UpdateMetadataKey.cxx b/Submodules/c3d/adapters/UpdateMetadataKey.cxx
new file mode 100644
index 0000000..0812ad1
--- /dev/null
+++ b/Submodules/c3d/adapters/UpdateMetadataKey.cxx
@@ -0,0 +1,53 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: UpdateMetadataKey.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "UpdateMetadataKey.h"
+#include "itkMetaDataDictionary.h"
+#include "itkMetaDataObject.h"
+
+template <class TPixel, unsigned int VDim>
+void
+UpdateMetadataKey<TPixel, VDim>
+::operator() (const char *key, const char *value)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // Report what we are doing
+ *c->verbose << "Updating metadata in #" << c->m_ImageStack.size() << endl;
+ *c->verbose << " Setting key " << key << " to value " << value << endl;
+
+ // Update the metadata values
+ itk::MetaDataDictionary &mdd = img->GetMetaDataDictionary();
+ typedef itk::MetaDataObject<string> StringMetaData;
+ typename StringMetaData::Pointer mdval = StringMetaData::New();
+ mdval->SetMetaDataObjectValue(value);
+ mdd[key] = mdval;
+}
+
+// Invocations
+template class UpdateMetadataKey<double, 2>;
+template class UpdateMetadataKey<double, 3>;
+template class UpdateMetadataKey<double, 4>;
diff --git a/Submodules/c3d/adapters/UpdateMetadataKey.h b/Submodules/c3d/adapters/UpdateMetadataKey.h
new file mode 100644
index 0000000..1643311
--- /dev/null
+++ b/Submodules/c3d/adapters/UpdateMetadataKey.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: UpdateMetadataKey.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __UpdateMetadataKey_h_
+#define __UpdateMetadataKey_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class UpdateMetadataKey : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ UpdateMetadataKey(Converter *c) : c(c) {}
+
+ void operator() (const char *key, const char *value);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/Vote.cxx b/Submodules/c3d/adapters/Vote.cxx
new file mode 100644
index 0000000..8724b43
--- /dev/null
+++ b/Submodules/c3d/adapters/Vote.cxx
@@ -0,0 +1,104 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Vote.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "Vote.h"
+#include "itkAddImageFilter.h"
+#include "itkMetaDataObject.h"
+
+
+template <class TPixel, unsigned int VDim>
+void
+Vote<TPixel, VDim>
+::operator() (bool flagUseSplitLabelSet)
+{
+ // Create a maximum image
+ ImagePointer i0 = c->m_ImageStack[0];
+
+ // If this is in response to a split command, retrieve the label set
+ typename Converter::LabelSet lset;
+ if(flagUseSplitLabelSet)
+ {
+ if(c->m_ImageStack.size() != c->GetSplitLabelSet().size())
+ throw ConvertException(
+ "Merge failed: number of images on the stack (%i) different "
+ "from the number of split labels (%i)",
+ c->m_ImageStack.size(), c->GetSplitLabelSet().size());
+ lset = c->GetSplitLabelSet();
+ }
+
+ // Otherwise, the label mapping is identity
+ else
+ {
+ for(size_t i = 0; i < c->m_ImageStack.size(); i++)
+ lset.push_back((double) i);
+ }
+
+ // Create a vote image
+ ImagePointer ilabel = ImageType::New();
+ ilabel->SetRegions(i0->GetBufferedRegion());
+ ilabel->SetOrigin(i0->GetOrigin());
+ ilabel->SetSpacing(i0->GetSpacing());
+ ilabel->SetDirection(i0->GetDirection());
+ ilabel->Allocate();
+ ilabel->FillBuffer(lset[0]);
+
+ // Say something
+ *c->verbose << "Collapsing " << c->m_ImageStack.size() <<
+ " images into a multi-label image via maximum voting" << endl;
+
+ // For each of the images, update the vote
+ for(size_t j = 1; j < c->m_ImageStack.size(); j++)
+ {
+ // Get the next image pointer
+ ImagePointer ij = c->m_ImageStack[j];
+
+ // Check the image dimensions
+ if(ij->GetBufferedRegion() != ilabel->GetBufferedRegion())
+ throw ConvertException("All voting images must have same dimensions");
+
+ // Get the label corresponding to this image
+ double xVoteVal = lset[j];
+
+ // Pairwise voting
+ size_t n = ilabel->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t k = 0; k < n; k++)
+ {
+ if(ij->GetBufferPointer()[k] > i0->GetBufferPointer()[k])
+ {
+ i0->GetBufferPointer()[k] = ij->GetBufferPointer()[k];
+ ilabel->GetBufferPointer()[k] = xVoteVal;
+ }
+ }
+ }
+
+ // Replace the images with the product
+ c->m_ImageStack.clear();
+ c->m_ImageStack.push_back(ilabel);
+}
+
+// Invocations
+template class Vote<double, 2>;
+template class Vote<double, 3>;
+template class Vote<double, 4>;
diff --git a/Submodules/c3d/adapters/Vote.h b/Submodules/c3d/adapters/Vote.h
new file mode 100644
index 0000000..882d453
--- /dev/null
+++ b/Submodules/c3d/adapters/Vote.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: Vote.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __Vote_h_
+#define __Vote_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class Vote : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ Vote(Converter *c) : c(c) {}
+
+ void operator() (bool flagUseSplitLabelSet);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/VoxelwiseComponentFunction.cxx b/Submodules/c3d/adapters/VoxelwiseComponentFunction.cxx
new file mode 100644
index 0000000..345f165
--- /dev/null
+++ b/Submodules/c3d/adapters/VoxelwiseComponentFunction.cxx
@@ -0,0 +1,186 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: VoxelwiseComponentFunction.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "VoxelwiseComponentFunction.h"
+
+#include "itkComposeImageFilter.h"
+#include "itkNthElementImageAdaptor.h"
+#include "itkCastImageFilter.h"
+#include "itkUnaryFunctorImageFilter.h"
+
+namespace VoxelwiseComponentFunctionNamespace {
+
+/**
+ * Functor for RGB to HSV conversion
+ */
+
+template <class TPixel, unsigned int VDim>
+class RGB_to_HSV_Functor
+{
+public:
+ typedef itk::FixedArray<TPixel, 3> InputPixelType;
+ typedef InputPixelType OutputPixelType;
+
+ OutputPixelType operator() (const InputPixelType &pix)
+ {
+ OutputPixelType out;
+
+ double r = pix[0], g = pix[1], b = pix[2];
+ TPixel &h = out[0], &s = out[1], &v = out[2];
+
+ double c_min, c_max, delta;
+
+ c_min = r < g ? r : g;
+ c_min = c_min < b ? c_min : b;
+
+ c_max = r > g ? r : g;
+ c_max = c_max > b ? c_max : b;
+
+ v = c_max; // v
+ delta = c_max - c_min;
+
+ if (delta < 0.00001)
+ {
+ s = 0;
+ h = 0; // undefined, maybe nan?
+ return out;
+ }
+ if( c_max > 0.0 )
+ {
+ // NOTE: if Max is == 0, this divide would cause a crash
+ s = (delta / c_max); // s
+ }
+ else
+ {
+ // if c_max is 0, then r = g = b = 0
+ // s = 0, v is undefined
+ s = 0.0;
+ h = NAN; // its now undefined
+ return out;
+ }
+ if( r >= c_max ) // > is bogus, just keeps compilor happy
+ h = ( g - b ) / delta; // between yellow & magenta
+ else
+ if( g >= c_max )
+ h = 2.0 + ( b - r ) / delta; // between cyan & yellow
+ else
+ h = 4.0 + ( r - g ) / delta; // between magenta & cyan
+
+ h *= 60.0; // degrees
+
+ if( h < 0.0 )
+ h += 360.0;
+
+ return out;
+ }
+};
+
+
+}; // namespace
+
+using namespace VoxelwiseComponentFunctionNamespace;
+
+// This function does the actual work
+template <class TPixel, unsigned int VDim>
+template <class TFunctor>
+void
+VoxelwiseComponentFunction<TPixel, VDim>
+::Apply(const TFunctor &functor, const char *func_name)
+{
+ // Number of components needed
+ typedef typename TFunctor::InputPixelType InputPixel;
+ typedef typename TFunctor::OutputPixelType OutputPixel;
+ int n_comp = InputPixel::Dimension;
+ int n_out_comp = OutputPixel::Dimension;
+ int n_stack = c->m_ImageStack.size();
+
+ // Get the images from the stack in forward order
+ if(n_stack < n_comp)
+ throw ConvertException("Too few components on the stack for VoxelwiseComponentFunction");
+
+ // Create the filter
+ typedef itk::Image<InputPixel, VDim> InputCompositeType;
+ typedef itk::Image<OutputPixel, VDim> OutputCompositeType;
+ typedef itk::ComposeImageFilter<ImageType, InputCompositeType> InputComposer;
+ typename InputComposer::Pointer compose = InputComposer::New();
+
+ // Take the last n_comp components
+ for(int k = 0; k < n_comp; k++)
+ compose->SetInput(k, c->m_ImageStack[n_stack + k - n_comp]);
+
+ // Create the unary filter
+ typedef itk::UnaryFunctorImageFilter<InputCompositeType, OutputCompositeType, TFunctor> Mapper;
+ typename Mapper::Pointer mapper = Mapper::New();
+ mapper->SetInput(compose->GetOutput());
+
+ // Explain what we are doing
+ *c->verbose
+ << "Applying voxelwise function " << func_name << " to #"
+ << n_stack - n_comp << " - #" << n_stack - 1 << endl;
+
+ // Update the mapper
+ mapper->Update();
+
+ // Pop the old components
+ for (int i = 0; i < n_comp; i++)
+ c->m_ImageStack.pop_back();
+
+ // Push the new ones
+ for (int i = 0; i < n_out_comp; i++)
+ {
+ typedef itk::NthElementImageAdaptor<OutputCompositeType, typename ImageType::PixelType> CompAdaptor;
+ typename CompAdaptor::Pointer adaptor = CompAdaptor::New();
+ adaptor->SelectNthElement(i);
+ adaptor->SetImage(mapper->GetOutput());
+
+ typedef itk::CastImageFilter<CompAdaptor, ImageType> Caster;
+ typename Caster::Pointer caster = Caster::New();
+ caster->SetInput(adaptor);
+ caster->Update();
+
+ c->m_ImageStack.push_back(caster->GetOutput());
+ }
+}
+
+
+template <class TPixel, unsigned int VDim>
+void
+VoxelwiseComponentFunction<TPixel, VDim>
+::operator() (const char *function)
+{
+ // Delegate to the right function
+ if(!strcmp(function, "rgb2hsv"))
+ {
+ RGB_to_HSV_Functor<TPixel, VDim> functor;
+ this->Apply(functor, function);
+ }
+ else
+ throw ConvertException("Unknown function %s in -voxelwise-function", function);
+}
+
+// Invocations
+template class VoxelwiseComponentFunction<double, 2>;
+template class VoxelwiseComponentFunction<double, 3>;
+template class VoxelwiseComponentFunction<double, 4>;
diff --git a/Submodules/c3d/adapters/VoxelwiseComponentFunction.h b/Submodules/c3d/adapters/VoxelwiseComponentFunction.h
new file mode 100644
index 0000000..a0c9139
--- /dev/null
+++ b/Submodules/c3d/adapters/VoxelwiseComponentFunction.h
@@ -0,0 +1,51 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __VoxelwiseComponentFunction_h_
+#define __VoxelwiseComponentFunction_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class VoxelwiseComponentFunction : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ VoxelwiseComponentFunction(Converter *c) : c(c) {}
+
+ void operator() (const char *function);
+
+private:
+
+ template <class TFunctor> void Apply(const TFunctor &functor, const char *func_name);
+
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/VoxelwiseRegression.cxx b/Submodules/c3d/adapters/VoxelwiseRegression.cxx
new file mode 100644
index 0000000..678cf77
--- /dev/null
+++ b/Submodules/c3d/adapters/VoxelwiseRegression.cxx
@@ -0,0 +1,73 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: VoxelwiseRegression.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "VoxelwiseRegression.h"
+#include "vnl/vnl_file_matrix.h"
+#include "vnl/vnl_rank.h"
+#include "vnl/algo/vnl_matrix_inverse.h"
+
+template <class TPixel, unsigned int VDim>
+void
+VoxelwiseRegression<TPixel, VDim>
+::operator() (size_t order)
+{
+ // Two images are needed from the stack
+ ImagePointer X = c->m_ImageStack[c->m_ImageStack.size() - 1];
+ ImagePointer Y = c->m_ImageStack[c->m_ImageStack.size() - 2];
+
+ // Build the design matrix and observation matrix
+ size_t n = X->GetBufferedRegion().GetNumberOfPixels();
+ vnl_matrix<double> design(n, order), observ(n, 1);
+ TPixel *px = X->GetBufferPointer(); TPixel *py = Y->GetBufferPointer();
+ for(size_t i = 0; i < n; i++, px++, py++)
+ {
+ TPixel vx = *px, vxj = 1.0;
+ for(size_t j = 0; j < order; j++)
+ {
+ design(i,j) = vxj;
+ vxj *= vx;
+ }
+ observ(i, 0) = *py;
+ }
+
+ // Execute GLM on the two matrices
+ size_t rank = vnl_rank(design, vnl_rank_row);
+
+ // Compute A
+ vnl_matrix<double> A =
+ vnl_matrix_inverse<double>(design.transpose() * design).pinverse(rank);
+
+ // Compute bhat = Ax * Y
+ vnl_matrix<double> bhat = (A * design.transpose()) * observ;
+
+ // We should report R^2, etc.
+ for(size_t j = 0; j < order; j++)
+ cout << "REGCOEFF[" << j << "] = " << bhat(j,0) << endl;
+}
+
+// Invocations
+template class VoxelwiseRegression<double, 2>;
+template class VoxelwiseRegression<double, 3>;
+template class VoxelwiseRegression<double, 4>;
diff --git a/Submodules/c3d/adapters/VoxelwiseRegression.h b/Submodules/c3d/adapters/VoxelwiseRegression.h
new file mode 100644
index 0000000..499c674
--- /dev/null
+++ b/Submodules/c3d/adapters/VoxelwiseRegression.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: VoxelwiseRegression.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __VoxelwiseRegression_h_
+#define __VoxelwiseRegression_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class VoxelwiseRegression : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ VoxelwiseRegression(Converter *c) : c(c) {}
+
+ void operator() (size_t order);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/WarpImage.cxx b/Submodules/c3d/adapters/WarpImage.cxx
new file mode 100644
index 0000000..2e99aeb
--- /dev/null
+++ b/Submodules/c3d/adapters/WarpImage.cxx
@@ -0,0 +1,105 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WarpImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "WarpImage.h"
+#include "itkWarpImageFilter.h"
+#include "CreateInterpolator.h"
+
+template <class TPixel, unsigned int VDim>
+void
+WarpImage<TPixel, VDim>
+::operator() ()
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < VDim + 1)
+ {
+ cerr << "Warp operation requires " << VDim+1 << " images on the stack" << endl;
+ throw -1;
+ }
+
+ // Write something
+ *c->verbose << "Warping image #" << c->m_ImageStack.size() << endl;
+
+ // Get the image to warp
+ ImagePointer isrc = c->m_ImageStack[c->m_ImageStack.size() - 1];
+
+ // Store the direction temporarily
+ // itk::Matrix<double, VDim, VDim> dirin = isrc->GetDirection(), dirtemp;
+ // dirtemp.SetIdentity();
+ // isrc->SetDirection(dirtemp);
+
+ // Index of the first warp image
+ size_t iwarp = c->m_ImageStack.size() - (VDim + 1);
+
+ // Create a deformation field
+ typedef itk::Vector<TPixel, VDim> VectorType;
+ typedef itk::OrientedRASImage<VectorType, VDim> FieldType;
+ typename FieldType::Pointer field = FieldType::New();
+ field->CopyInformation(c->m_ImageStack[iwarp]);
+ field->SetRegions(c->m_ImageStack[iwarp]->GetBufferedRegion());
+ field->Allocate();
+ size_t nvox = field->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t d = 0; d < VDim; d++)
+ {
+ ImagePointer warp = c->m_ImageStack[iwarp + d];
+ if(warp->GetBufferedRegion() != field->GetBufferedRegion())
+ throw ConvertException("Warp field components have different dimensions");
+ for(size_t i = 0; i < nvox; i++)
+ field->GetBufferPointer()[i][d] = warp->GetBufferPointer()[i];
+ }
+
+ // Create the warp filter
+ typedef itk::WarpImageFilter<ImageType, ImageType, FieldType> WarpType;
+ typename WarpType::Pointer fltWarp = WarpType::New();
+ fltWarp->SetInput(isrc);
+ fltWarp->SetDisplacementField(field);
+
+ // Create interpolator
+ fltWarp->SetInterpolator(c->GetInterpolator());
+
+ // Update the warp fileter
+ fltWarp->SetOutputSpacing(field->GetSpacing());
+ fltWarp->SetOutputOrigin(field->GetOrigin());
+ fltWarp->SetOutputDirection(field->GetDirection());
+ fltWarp->SetEdgePaddingValue(c->m_Background);
+ fltWarp->Update();
+
+ // Update the output image
+ //
+ ImagePointer imgout = fltWarp->GetOutput();
+
+ // Drop image and warps from stack
+ c->m_ImageStack.pop_back();
+ for(size_t d = 0; d < VDim; d++)
+ c->m_ImageStack.pop_back();
+
+ // Add the warped image to the stack
+ c->m_ImageStack.push_back(imgout);
+}
+
+// Invocations
+template class WarpImage<double, 2>;
+template class WarpImage<double, 3>;
+template class WarpImage<double, 4>;
diff --git a/Submodules/c3d/adapters/WarpImage.h b/Submodules/c3d/adapters/WarpImage.h
new file mode 100644
index 0000000..3cbdc93
--- /dev/null
+++ b/Submodules/c3d/adapters/WarpImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WarpImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __WarpImage_h_
+#define __WarpImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class WarpImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ WarpImage(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/WarpLabelImage.cxx b/Submodules/c3d/adapters/WarpLabelImage.cxx
new file mode 100644
index 0000000..ef89e9c
--- /dev/null
+++ b/Submodules/c3d/adapters/WarpLabelImage.cxx
@@ -0,0 +1,154 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WarpLabelImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "WarpLabelImage.h"
+#include "itkWarpImageFilter.h"
+#include "CreateInterpolator.h"
+#include "ThresholdImage.h"
+#include "SmoothImage.h"
+#include <set>
+
+template <class TPixel, unsigned int VDim>
+void
+WarpLabelImage<TPixel, VDim>
+::operator() (RealVector &stdev)
+{
+ // Check input availability
+ if(c->m_ImageStack.size() < VDim + 1)
+ {
+ cerr << "Warp operation requires " << VDim+1 << " images on the stack" << endl;
+ throw -1;
+ }
+
+ // Write something
+ *c->verbose << "Warping image label-wise #" << c->m_ImageStack.size() << endl;
+
+ // Index of the first warp image
+ size_t iwarp = c->m_ImageStack.size() - (VDim + 1);
+
+
+ // Get the image to warp
+ ImagePointer isrc = c->m_ImageStack[c->m_ImageStack.size() - 1];
+
+ // Create a deformation field
+ typedef itk::Vector<TPixel, VDim> VectorType;
+ typedef itk::OrientedRASImage<VectorType, VDim> FieldType;
+ typename FieldType::Pointer field = FieldType::New();
+ field->CopyInformation(c->m_ImageStack[iwarp]);
+ field->SetRegions(c->m_ImageStack[iwarp]->GetBufferedRegion());
+ field->Allocate();
+ size_t nvox = field->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t d = 0; d < VDim; d++)
+ {
+ ImagePointer warp = c->m_ImageStack[iwarp + d];
+ if(warp->GetBufferedRegion() != field->GetBufferedRegion())
+ throw ConvertException("Warp field components have different dimensions");
+ for(size_t i = 0; i < nvox; i++)
+ field->GetBufferPointer()[i][d] = warp->GetBufferPointer()[i];
+ }
+
+ // Create the warp filter
+ typedef itk::WarpImageFilter<ImageType, ImageType, FieldType> WarpType;
+ typename WarpType::Pointer fltWarp = WarpType::New();
+ fltWarp->SetDisplacementField(field);
+
+ // Create interpolator
+ fltWarp->SetInterpolator(c->GetInterpolator());
+
+ // Update the warp fileter
+ fltWarp->SetOutputSpacing(field->GetSpacing());
+ fltWarp->SetOutputOrigin(field->GetOrigin());
+ fltWarp->SetOutputDirection(field->GetDirection());
+ fltWarp->SetEdgePaddingValue(c->m_Background);
+
+ // Find all unique labels in the input image
+ std::set<TPixel> labels;
+ size_t n = isrc->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t i = 0; i < n; i++)
+ labels.insert(isrc->GetBufferPointer()[i]);
+
+ // Create the output image
+ ImagePointer ilabel = ImageType::New();
+ ilabel->CopyInformation(field);
+ ilabel->SetRegions(field->GetBufferedRegion());
+ ilabel->Allocate();
+
+ // Create the 'score' image
+ ImagePointer iscore = ImageType::New();
+ iscore->SetRegions(field->GetBufferedRegion());
+ iscore->Allocate();
+ iscore->FillBuffer(0.0);
+
+ // For each unique label in the input image, threshold that image
+ for(typename std::set<TPixel>::iterator it = labels.begin(); it!=labels.end(); ++it)
+ {
+ // Current label value
+ TPixel val = *it;
+
+ // Threshold the image (get only current label)
+ ThresholdImage<TPixel, VDim> thresh(c);
+ thresh(val, val, 1, 0);
+
+ // Smooth the image
+ SmoothImage<TPixel, VDim> smooth(c);
+ smooth(stdev, true);
+
+ // Apply the warp to the smoothed image
+ fltWarp->SetInput(c->m_ImageStack.back());
+ fltWarp->Update();
+
+ // Iterate over voxels, update the score and label images
+ ImagePointer iwarped = fltWarp->GetOutput();
+ size_t nvox = iwarped->GetBufferedRegion().GetNumberOfPixels();
+ TPixel *pscore = iscore->GetBufferPointer(),
+ *pwarped = iwarped->GetBufferPointer(),
+ *plabel = ilabel->GetBufferPointer();
+ for(size_t i = 0; i < nvox; i++)
+ {
+ if(pscore[i] < pwarped[i])
+ {
+ pscore[i] = pwarped[i];
+ plabel[i] = val;
+ }
+ }
+
+ // Replace everything on the stack
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(isrc);
+ }
+
+ // Drop image and warps from stack
+ c->m_ImageStack.pop_back();
+ for(size_t d = 0; d < VDim; d++)
+ c->m_ImageStack.pop_back();
+
+ // Store the result on the stack
+ c->m_ImageStack.push_back(ilabel);
+}
+
+// Invocations
+template class WarpLabelImage<double, 2>;
+template class WarpLabelImage<double, 3>;
+template class WarpLabelImage<double, 4>;
diff --git a/Submodules/c3d/adapters/WarpLabelImage.h b/Submodules/c3d/adapters/WarpLabelImage.h
new file mode 100644
index 0000000..8c8a10f
--- /dev/null
+++ b/Submodules/c3d/adapters/WarpLabelImage.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WarpLabelImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __WarpLabelImage_h_
+#define __WarpLabelImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class WarpLabelImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ WarpLabelImage(Converter *c) : c(c) {}
+
+ void operator()(RealVector &stdev);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/WeightedSum.cxx b/Submodules/c3d/adapters/WeightedSum.cxx
new file mode 100644
index 0000000..d24ac4e
--- /dev/null
+++ b/Submodules/c3d/adapters/WeightedSum.cxx
@@ -0,0 +1,76 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WeightedSum.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "WeightedSum.h"
+
+template <class TPixel, unsigned int VDim>
+void
+WeightedSum<TPixel, VDim>
+::operator() (std::vector<double> weights)
+{
+ // The vector size must not exceed number of images
+ size_t nw = weights.size();
+ if(nw > c->m_ImageStack.size())
+ throw ConvertException("Too many weights specified for -weighted-sum command");
+ if(nw == 0)
+ throw ConvertException("No weights specified for -weighted-sum command");
+
+ ImagePointer iref = c->m_ImageStack.back();
+
+ // Check dimensions
+ if(!c->CheckStackSameDimensions(nw))
+ throw ConvertException("Images on the stack don't have same dimensions");
+
+ *c->verbose << "Taking weighted sum of images #"
+ << c->m_ImageStack.size() - nw << " to #" << c->m_ImageStack.size() << endl;
+
+ // Scale the last image
+ size_t nvox = iref->GetBufferedRegion().GetNumberOfPixels();
+ double w = weights[nw-1];
+ *c->verbose << " Scaling #" << c->m_ImageStack.size() << " by " << w << endl;
+ TPixel *ptrg = c->m_ImageStack[nw - 1]->GetBufferPointer();
+ for(size_t i = 0; i < nvox; i++)
+ ptrg[i] *= w;
+
+ // Add the rest of the images
+ for(size_t i = 1; i < nw; i++)
+ {
+ double w = weights[nw-(i+1)];
+ *c->verbose << " Scaling #" << c->m_ImageStack.size()-i << " by " << w << endl;
+ TPixel *p = c->m_ImageStack[nw - (i+1)]->GetBufferPointer();
+ for(size_t i = 0; i < nvox; i++)
+ ptrg[i] += w * p[i];
+ }
+
+ // Drop all but the last image
+ for(size_t i = 0; i < nw; i++)
+ c->m_ImageStack.pop_back();
+ c->m_ImageStack.push_back(iref);
+}
+
+// Invocations
+template class WeightedSum<double, 2>;
+template class WeightedSum<double, 3>;
+template class WeightedSum<double, 4>;
diff --git a/Submodules/c3d/adapters/WeightedSum.h b/Submodules/c3d/adapters/WeightedSum.h
new file mode 100644
index 0000000..86c056a
--- /dev/null
+++ b/Submodules/c3d/adapters/WeightedSum.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WeightedSum.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __WeightedSum_h_
+#define __WeightedSum_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class WeightedSum : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ WeightedSum(Converter *c) : c(c) {}
+
+ void operator() (std::vector<double> weights);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/WeightedSumVoxelwise.cxx b/Submodules/c3d/adapters/WeightedSumVoxelwise.cxx
new file mode 100644
index 0000000..8be2611
--- /dev/null
+++ b/Submodules/c3d/adapters/WeightedSumVoxelwise.cxx
@@ -0,0 +1,74 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WeightedSumVoxelwise.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "WeightedSumVoxelwise.h"
+
+template <class TPixel, unsigned int VDim>
+void
+WeightedSumVoxelwise<TPixel, VDim>
+::operator() ()
+{
+ // Check input validity
+ if((c->m_ImageStack.size() % 2) == 1)
+ throw ConvertException("Number of images on stack incompatible with"
+ " weighted averaging (must be even");
+
+ if(!c->CheckStackSameDimensions(0))
+ throw ConvertException("Images on the stack don't have same dimensions");
+
+ // Make a copy of the last image on the stack
+ ImagePointer isum = ImageType::New();
+ isum->SetRegions(c->m_ImageStack.back()->GetBufferedRegion());
+ isum->CopyInformation(c->m_ImageStack.back());
+ isum->Allocate();
+ isum->FillBuffer(0);
+
+ *c->verbose << "Taking weighted sum of " << c->m_ImageStack.size() / 2 << " images.";
+
+ // Compute the weighted sum, two images at a time
+ while(c->m_ImageStack.size())
+ {
+ // Get images from the stack
+ ImagePointer img = c->m_ImageStack.back();
+ c->m_ImageStack.pop_back();
+ ImagePointer wgt = c->m_ImageStack.back();
+ c->m_ImageStack.pop_back();
+
+ // Compute the weighted sum
+ Iterator is(img, img->GetBufferedRegion());
+ Iterator iw(wgt, wgt->GetBufferedRegion());
+ Iterator it(isum, isum->GetBufferedRegion());
+ for(; !is.IsAtEnd(); ++is, ++it, ++iw)
+ it.Set(it.Get() + is.Get() * iw.Get());
+ }
+
+ // Put result on stack
+ c->m_ImageStack.push_back(isum);
+}
+
+// Invocations
+template class WeightedSumVoxelwise<double, 2>;
+template class WeightedSumVoxelwise<double, 3>;
+template class WeightedSumVoxelwise<double, 4>;
diff --git a/Submodules/c3d/adapters/WeightedSumVoxelwise.h b/Submodules/c3d/adapters/WeightedSumVoxelwise.h
new file mode 100644
index 0000000..c506148
--- /dev/null
+++ b/Submodules/c3d/adapters/WeightedSumVoxelwise.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WeightedSumVoxelwise.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __WeightedSumVoxelwise_h_
+#define __WeightedSumVoxelwise_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class WeightedSumVoxelwise : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ WeightedSumVoxelwise(Converter *c) : c(c) {}
+
+ void operator() ();
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/WrapDimension.cxx b/Submodules/c3d/adapters/WrapDimension.cxx
new file mode 100644
index 0000000..35689d8
--- /dev/null
+++ b/Submodules/c3d/adapters/WrapDimension.cxx
@@ -0,0 +1,90 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WrapDimension.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "WrapDimension.h"
+#include "itkWrapPadImageFilter.h"
+#include "itkRegionOfInterestImageFilter.h"
+#include "vnl/vnl_finite.h"
+
+template <class TPixel, unsigned int VDim>
+void
+WrapDimension<TPixel, VDim>
+::operator() (const IndexType &xWrap)
+{
+ // Get image from stack
+ ImagePointer img = c->m_ImageStack.back();
+
+ // We will wrap the image in place. We must first calculate the offset
+ // in the flattened image that corresponds to the wrap
+ int k = 0, n = 1;
+ for(size_t i = 0; i < VDim; i++)
+ {
+ k += xWrap[i] * n;
+ n *= img->GetBufferedRegion().GetSize(i);
+ }
+
+ // Compute the GCD of number of voxels, offset
+ int gcd = vnl_finite_int<0>::gcd(abs(k), n);
+ int m = n / gcd;
+
+ // Explain what we are doing
+ *c->verbose << "Wrapping image #" << c->m_ImageStack.size() << " by " << xWrap << endl;
+ *c->verbose << " Rotate in memory by " << k << " bytes." << endl;
+ *c->verbose << " GCD(" << n << "," << k << ") = " << gcd << endl;
+
+ // Get data pointer
+ TPixel *ptr = img->GetBufferPointer();
+
+ // There are gcd different starting points for the wrapping
+ for(int i = 0; i < gcd; i++)
+ {
+ TPixel q = ptr[i];
+ int p = i;
+ for(int j = 1; j < m; j++)
+ {
+ int pnext = (p + k) % n;
+ ptr[p] = ptr[pnext];
+ p = pnext;
+ if(p < 0)
+ p = p + n;
+ }
+ ptr[p] = q;
+ }
+
+ // Change the origin. Wrapping by (dx,dy,dz) means that voxel (0,0,0) is changed
+ // to the value of voxel (dx,dy,dz). So, the coordinate of (dx,dy,dz) should be
+ // the new origin.
+ itk::Point<double, VDim> org;
+ img->TransformIndexToPhysicalPoint(xWrap, org);
+ img->SetOrigin(org);
+
+ // Flag our image as modified
+ img->Modified();
+}
+
+// Invocations
+template class WrapDimension<double, 2>;
+template class WrapDimension<double, 3>;
+template class WrapDimension<double, 4>;
diff --git a/Submodules/c3d/adapters/WrapDimension.h b/Submodules/c3d/adapters/WrapDimension.h
new file mode 100644
index 0000000..93dcaf7
--- /dev/null
+++ b/Submodules/c3d/adapters/WrapDimension.h
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WrapDimension.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __WrapDimension_h_
+#define __WrapDimension_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class WrapDimension : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ WrapDimension(Converter *c) : c(c) {}
+
+ void operator() (const IndexType &xWrap);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/WriteImage.cxx b/Submodules/c3d/adapters/WriteImage.cxx
new file mode 100644
index 0000000..085112f
--- /dev/null
+++ b/Submodules/c3d/adapters/WriteImage.cxx
@@ -0,0 +1,247 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WriteImage.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "WriteImage.h"
+#include "itksys/SystemTools.hxx"
+#include "itkImageFileWriter.h"
+#include "itkIOCommon.h"
+#include "itkMetaDataDictionary.h"
+#include "itkMetaDataObject.h"
+
+template<class TPixel, unsigned int VDim>
+template<class TOutPixel>
+void
+WriteImage<TPixel, VDim>
+::TemplatedWriteImage(const char *file, double xRoundFactor, int pos)
+{
+ // Get the input image
+ if(c->m_ImageStack.size() == 0)
+ throw ConvertException("No data has been generated! Can't write to %s", file);
+
+ // Get the image at the given position
+ if(pos < 0) pos = c->m_ImageStack.size() - 1;
+ ImagePointer input = c->m_ImageStack[pos];
+
+ // Create the output image
+ typedef itk::OrientedRASImage<TOutPixel, VDim> OutputImageType;
+ typename OutputImageType::Pointer output = OutputImageType::New();
+ output->SetRegions(input->GetBufferedRegion());
+ output->SetSpacing(input->GetSpacing());
+ output->SetOrigin(input->GetOrigin());
+ output->SetDirection(input->GetDirection());
+ output->SetMetaDataDictionary(input->GetMetaDataDictionary());
+ output->Allocate();
+
+ // Describe what we are doing
+ *c->verbose << "Writing #" << pos+1 << " to file " << file << endl;
+ *c->verbose << " Output voxel type: " << c->m_TypeId << "[" << typeid(TOutPixel).name() << "]" << endl;
+ *c->verbose << " Rounding off: " << (xRoundFactor == 0.0 ? "Disabled" : "Enabled") << endl;
+
+ // Set the SPM originator header
+ MakeSPMOriginFix(input, output);
+
+ // Copy everything, rounding if the pixel type is integer
+ size_t n = input->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t i = 0; i < n; i++)
+ output->GetBufferPointer()[i] = (TOutPixel) (input->GetBufferPointer()[i] + xRoundFactor);
+
+ // Set the file notes for this image
+ itk::EncapsulateMetaData<string>(
+ output->GetMetaDataDictionary(),itk::ITK_FileNotes,
+ std::string("Created by Convert3D"));
+
+ // Write the image out
+ typedef itk::ImageFileWriter<OutputImageType> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetInput(output);
+ writer->SetFileName(file);
+ writer->SetUseCompression(c->m_UseCompression);
+ try { writer->Update(); }
+ catch (itk::ExceptionObject &exc) {
+ cerr << "Error writing image to " << file << endl;
+ cerr << "ITK Exception: " << exc << endl;
+ throw -1;
+ }
+}
+
+template<class TPixel, unsigned int VDim>
+void
+WriteImage<TPixel, VDim>
+::MakeSPMOriginFix(itk::ImageBase<VDim> *input, itk::ImageBase<VDim> *output)
+{
+ // Set the SPM originator header
+ if(c->m_FlagSPM)
+ {
+ size_t i;
+ string originator;
+ originator.resize(VDim * 2);
+
+ // Compute the SPM-style origin of the image
+ *c->verbose << " Setting SPM origin field to:";
+ for(i = 0; i < VDim; i++)
+ {
+ short ospm = (short)(0.5 - input->GetOrigin()[i] / input->GetSpacing()[i]);
+ originator[2*i] = (char)(ospm & 0x00ff);
+ originator[2*i+1] = (char)(ospm >> 8);
+ *c->verbose << ospm << " ";
+ }
+ originator[2*i] = 0;
+ *c->verbose << endl;
+
+ itk::EncapsulateMetaData<string>(
+ output->GetMetaDataDictionary(),itk::ITK_FileOriginator,originator);
+ }
+}
+
+
+template<class TPixel, unsigned int VDim>
+template<class TOutPixel>
+void
+WriteImage<TPixel, VDim>
+::TemplatedWriteMultiComponentImage(const char *file, double xRoundFactor, int pstart)
+{
+ size_t ncomp = c->m_ImageStack.size() - pstart;
+ if(ncomp <= 0)
+ throw ConvertException("No data has been generated! Can't write to %s", file);
+
+ // Get the top image on the stack (for reference information)
+ ImagePointer itop = c->m_ImageStack.back();
+
+ // Check compatibility
+ for(size_t i = 0; i < ncomp-1; i++)
+ if(c->m_ImageStack[pstart+i]->GetBufferedRegion().GetSize() !=
+ itop->GetBufferedRegion().GetSize())
+ {
+ throw ConvertException("Multicomponent output error: mismatch in image dimensions");
+ }
+
+ // Define the output image type
+ typedef itk::VectorImage<TOutPixel, VDim> OutputImageType;
+ typename OutputImageType::Pointer output = OutputImageType::New();
+ output->CopyInformation(itop);
+ output->SetRegions(itop->GetBufferedRegion());
+ output->SetNumberOfComponentsPerPixel(ncomp);
+ output->Allocate();
+
+ // Describe what we are doing
+ *c->verbose << "Writing Images" << pstart+1 << " to " << c->m_ImageStack.size() << " to multicomponent file " << file << endl;
+ *c->verbose << " Output voxel type: " << c->m_TypeId << "[" << typeid(TOutPixel).name() << "]" << endl;
+ *c->verbose << " Rounding off: " << (xRoundFactor == 0.0 ? "Disabled" : "Enabled") << endl;
+
+ // Set the SPM originator header
+ MakeSPMOriginFix(itop, output);
+
+ // Copy everything, rounding if the pixel type is integer
+ size_t n = itop->GetBufferedRegion().GetNumberOfPixels();
+ for(size_t j = 0; j < ncomp; j++)
+ {
+ TPixel *buf = c->m_ImageStack[pstart+j]->GetBufferPointer();
+ TOutPixel *out = output->GetBufferPointer() + j;
+ for(size_t i = 0; i < n; i++, buf++, out+=ncomp)
+ *out = (TOutPixel) (*buf + xRoundFactor);
+ }
+
+ // Write the image out
+ typedef itk::ImageFileWriter<OutputImageType> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetInput(output);
+ writer->SetFileName(file);
+ writer->SetUseCompression(c->m_UseCompression);
+ try { writer->Update(); }
+ catch (itk::ExceptionObject &exc)
+ {
+ throw ConvertException("Error writing image to %s\nITK Exception: %s", file, exc.GetDescription());
+ }
+}
+
+template <class TPixel, unsigned int VDim>
+void
+WriteImage<TPixel, VDim>
+::WriteMultiComponent(const char *file, int ncomp)
+{
+ // Get the position of the first image to include
+ int pos = c->m_ImageStack.size() - ncomp;
+
+ if(c->m_TypeId == "char" || c->m_TypeId == "byte")
+ TemplatedWriteMultiComponentImage<char>(file, c->m_RoundFactor, pos);
+ if(c->m_TypeId == "uchar" || c->m_TypeId == "ubyte")
+ TemplatedWriteMultiComponentImage<unsigned char>(file, c->m_RoundFactor, pos);
+
+ if(c->m_TypeId == "short")
+ TemplatedWriteMultiComponentImage<short>(file, c->m_RoundFactor, pos);
+ if(c->m_TypeId == "ushort")
+ TemplatedWriteMultiComponentImage<unsigned short>(file, c->m_RoundFactor, pos);
+
+ if(c->m_TypeId == "int")
+ TemplatedWriteMultiComponentImage<int>(file, c->m_RoundFactor, pos);
+ if(c->m_TypeId == "uint")
+ TemplatedWriteMultiComponentImage<unsigned int>(file, c->m_RoundFactor, pos);
+
+ if(c->m_TypeId == "float")
+ TemplatedWriteMultiComponentImage<float>(file, 0.0, pos);
+ if(c->m_TypeId == "double")
+ TemplatedWriteMultiComponentImage<double>(file, 0.0, pos);
+}
+
+
+template <class TPixel, unsigned int VDim>
+void
+WriteImage<TPixel, VDim>
+::operator() (const char *file, bool force, int pos)
+{
+ // Unless in 'force' mode, check if the image already exists
+ if(!force && itksys::SystemTools::FileExists(file))
+ {
+ cerr << "File " << file << " already exists. Use -o option to override!" << endl;
+ throw -1;
+ }
+
+ if(c->m_TypeId == "char" || c->m_TypeId == "byte")
+ TemplatedWriteImage<char>(file, c->m_RoundFactor, pos);
+ if(c->m_TypeId == "uchar" || c->m_TypeId == "ubyte")
+ TemplatedWriteImage<unsigned char>(file, c->m_RoundFactor, pos);
+
+ if(c->m_TypeId == "short")
+ TemplatedWriteImage<short>(file, c->m_RoundFactor, pos);
+ if(c->m_TypeId == "ushort")
+ TemplatedWriteImage<unsigned short>(file, c->m_RoundFactor, pos);
+
+ if(c->m_TypeId == "int")
+ TemplatedWriteImage<int>(file, c->m_RoundFactor, pos);
+ if(c->m_TypeId == "uint")
+ TemplatedWriteImage<unsigned int>(file, c->m_RoundFactor, pos);
+
+ if(c->m_TypeId == "float")
+ TemplatedWriteImage<float>(file, 0.0, pos);
+ if(c->m_TypeId == "double")
+ TemplatedWriteImage<double>(file, 0.0, pos);
+}
+
+
+
+// Invocations
+template class WriteImage<double, 2>;
+template class WriteImage<double, 3>;
+template class WriteImage<double, 4>;
diff --git a/Submodules/c3d/adapters/WriteImage.h b/Submodules/c3d/adapters/WriteImage.h
new file mode 100644
index 0000000..80de950
--- /dev/null
+++ b/Submodules/c3d/adapters/WriteImage.h
@@ -0,0 +1,58 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: WriteImage.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __WriteImage_h_
+#define __WriteImage_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class WriteImage : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ WriteImage(Converter *c) : c(c) {}
+
+ void operator() (const char *file, bool force, int pos=-1);
+
+ void WriteMultiComponent(const char *file, int ncomp);
+
+private:
+ Converter *c;
+
+ template <class TOutPixel>
+ void TemplatedWriteImage(const char *file, double xRoundFactor, int pos);
+
+ template <class TOutPixel>
+ void TemplatedWriteMultiComponentImage(const char *file, double xRoundFactor, int pstart);
+
+ void MakeSPMOriginFix(itk::ImageBase<VDim> *input, itk::ImageBase<VDim> *output);
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/generator/ConvertAdapterTemplate.cxx.in b/Submodules/c3d/adapters/generator/ConvertAdapterTemplate.cxx.in
new file mode 100644
index 0000000..7a02ab5
--- /dev/null
+++ b/Submodules/c3d/adapters/generator/ConvertAdapterTemplate.cxx.in
@@ -0,0 +1,47 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "%fn%.h"
+
+template <class TPixel, unsigned int VDim>
+void
+%fn%<TPixel, VDim>
+::operator() (%param%)
+{
+ // Get image from stack
+ // ImagePointer img = c->m_ImageStack.back();
+
+ // Do some processing ...
+ // ImagePointer result = ...;
+
+ // Put result on stack
+ // c->m_ImageStack.pop_back();
+ // c->m_ImageStack.push_back(result);
+}
+
+// Invocations
+template class %fn%<double, 2>;
+template class %fn%<double, 3>;
+template class %fn%<double, 4>;
diff --git a/Submodules/c3d/adapters/generator/ConvertAdapterTemplate.h.in b/Submodules/c3d/adapters/generator/ConvertAdapterTemplate.h.in
new file mode 100644
index 0000000..300323c
--- /dev/null
+++ b/Submodules/c3d/adapters/generator/ConvertAdapterTemplate.h.in
@@ -0,0 +1,48 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: %fn%.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __%function%_h_
+#define __%function%_h_
+
+#include "ConvertAdapter.h"
+
+template<class TPixel, unsigned int VDim>
+class %function% : public ConvertAdapter<TPixel, VDim>
+{
+public:
+ // Common typedefs
+ CONVERTER_STANDARD_TYPEDEFS
+
+ %function%(Converter *c) : c(c) {}
+
+ void operator() (%param%);
+
+private:
+ Converter *c;
+
+};
+
+#endif
+
diff --git a/Submodules/c3d/adapters/generator/runme.sh b/Submodules/c3d/adapters/generator/runme.sh
new file mode 100644
index 0000000..5f0aea8
--- /dev/null
+++ b/Submodules/c3d/adapters/generator/runme.sh
@@ -0,0 +1,4 @@
+fn=$1
+pt=$2
+cat ConvertAdapterTemplate.h.in | sed -e "s/%function%/$fn/" -e "s/%param%/$pt/" | tee $fn.h
+cat ConvertAdapterTemplate.cxx.in | sed -e "s/%fn%/$fn/" -e "s/%param%/$pt/" | tee $fn.cxx
diff --git a/Submodules/c3d/api/ConvertAPI.cxx b/Submodules/c3d/api/ConvertAPI.cxx
new file mode 100644
index 0000000..55fccf8
--- /dev/null
+++ b/Submodules/c3d/api/ConvertAPI.cxx
@@ -0,0 +1,220 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertImageND.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "ConvertAPI.h"
+#include "ConvertImageND.h"
+#include "itkSmartPointer.h"
+#include "itkImage.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <shellapi.h>
+#else
+#include <wordexp.h>
+#endif
+
+/**
+ * Code to split a string into argc/argv with some shell expansion
+ * from http://stackoverflow.com/questions/1706551/parse-string-into-argv-argc
+ */
+char **split_commandline(const char *cmdline, int *argc)
+{
+ int i;
+ char **argv = NULL;
+ assert(argc);
+
+ if (!cmdline)
+ {
+ return NULL;
+ }
+
+ // Posix.
+#ifndef _WIN32
+ {
+ wordexp_t p;
+
+ // Note! This expands shell variables.
+ if (wordexp(cmdline, &p, 0))
+ {
+ return NULL;
+ }
+
+ *argc = p.we_wordc;
+
+ if (!(argv = (char **) calloc(*argc, sizeof(char *))))
+ {
+ goto fail;
+ }
+
+ for (i = 0; i < p.we_wordc; i++)
+ {
+ if (!(argv[i] = strdup(p.we_wordv[i])))
+ {
+ goto fail;
+ }
+ }
+
+ wordfree(&p);
+
+ return argv;
+fail:
+ wordfree(&p);
+ }
+#else // WIN32
+ {
+ wchar_t **wargs = NULL;
+ size_t needed = 0;
+ wchar_t *cmdlinew = NULL;
+ size_t len = strlen(cmdline) + 1;
+
+ if (!(cmdlinew = (wchar_t *) calloc(len, sizeof(wchar_t))))
+ goto fail;
+
+ if (!MultiByteToWideChar(CP_ACP, 0, cmdline, -1, cmdlinew, len))
+ goto fail;
+
+ if (!(wargs = CommandLineToArgvW(cmdlinew, argc)))
+ goto fail;
+
+ if (!(argv = (char **) calloc(*argc, sizeof(char *))))
+ goto fail;
+
+ // Convert from wchar_t * to ANSI char *
+ for (i = 0; i < *argc; i++)
+ {
+ // Get the size needed for the target buffer.
+ // CP_ACP = Ansi Codepage.
+ needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
+ NULL, 0, NULL, NULL);
+
+ if (!(argv[i] = (char *) malloc(needed)))
+ goto fail;
+
+ // Do the conversion.
+ needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
+ argv[i], needed, NULL, NULL);
+ }
+
+ if (wargs) LocalFree(wargs);
+ if (cmdlinew) free(cmdlinew);
+ return argv;
+
+fail:
+ if (wargs) LocalFree(wargs);
+ if (cmdlinew) free(cmdlinew);
+ }
+#endif // WIN32
+
+ if (argv)
+ {
+ for (i = 0; i < *argc; i++)
+ {
+ if (argv[i])
+ {
+ free(argv[i]);
+ }
+ }
+
+ free(argv);
+ }
+
+ return NULL;
+}
+
+template <class TPixel, unsigned int VDim>
+ConvertAPI<TPixel, VDim>
+::ConvertAPI()
+{
+ m_Converter = new ConverterType();
+}
+
+template <class TPixel, unsigned int VDim>
+ConvertAPI<TPixel,VDim>
+::~ConvertAPI()
+{
+ delete m_Converter;
+}
+
+template <class TPixel, unsigned int VDim>
+void
+ConvertAPI<TPixel,VDim>
+::AddImage(const char *varname, ImageType *image)
+{
+ typedef typename ConverterType::ImageType InternalImage;
+ typename InternalImage::Pointer img_ras = InternalImage::New();
+ img_ras->Graft(image);
+
+ m_Converter->SetVariable(varname, img_ras);
+}
+
+template <class TPixel, unsigned int VDim>
+typename ConvertAPI<TPixel,VDim>::ImageType *
+ConvertAPI<TPixel,VDim>
+::GetImage(const char *varname)
+{
+ return m_Converter->GetVariable(varname);
+}
+
+template <class TPixel, unsigned int VDim>
+bool
+ConvertAPI<TPixel,VDim>
+::Execute(const char *cmdline, std::ostream &sout)
+{
+ int argc = 0;
+ char **argv = split_commandline(cmdline, &argc);
+
+ if(!argv)
+ {
+ m_Error = "Error parsing the command line expression";
+ return false;
+ }
+
+ try
+ {
+ int rc = m_Converter->ProcessCommandLine(argc, argv);
+ if(rc)
+ {
+ m_Error = "Convert3D returned a non-zero return code. Check output for errors";
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ catch(std::exception &exc)
+ {
+ m_Error = exc.what();
+ return false;
+ }
+ catch(...)
+ {
+ m_Error = "Unspecified failure. Check output for more information.";
+ return false;
+ }
+}
+
+template class ConvertAPI<double, 2>;
+template class ConvertAPI<double, 3>;
+template class ConvertAPI<double, 4>;
diff --git a/Submodules/c3d/api/ConvertAPI.h b/Submodules/c3d/api/ConvertAPI.h
new file mode 100644
index 0000000..6d985d5
--- /dev/null
+++ b/Submodules/c3d/api/ConvertAPI.h
@@ -0,0 +1,98 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertImageND.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __ConvertAPI_h_
+#define __ConvertAPI_h_
+
+#include <iostream>
+#include <string>
+
+namespace itk {
+ template <class TPixel, unsigned int VDim> class Image;
+}
+
+template <class TPixel, unsigned int VDim> class ImageConverter;
+
+/**
+ * This header file provides the C++ interface to the c3d API.
+ *
+ * It allows a third-party program to call C3D in its own process space, and
+ * to pass images to C3D through memory (rather than through files). In the
+ * future, it will also support adding custom commands from the third party
+ * software.
+ *
+ * The third-party program should be able to interface with C3D by just including
+ * this header file and linking with libraries cnd_api, cnd_driver and cnd_adapters
+ */
+template <class TPixel, unsigned int VDim>
+class ConvertAPI
+{
+public:
+
+ /** Standard ITK image - used to pass data in and out of the API */
+ typedef itk::Image<TPixel, VDim> ImageType;
+
+ ConvertAPI();
+ ~ConvertAPI();
+
+ /**
+ * Pass an image as a variable to C3D. You can then access that variable using
+ * the '-push' command during the call to Execute. This class will hold on to this
+ * image until it is destroyed.
+ */
+ void AddImage(const char *varname, ImageType *image);
+
+ /**
+ * Execute a command in c3d. This is just like a regular command-line, and the
+ * output will be captured to the provided stream.
+ *
+ * Return Value: if an exception is caught during command execution, this will
+ * return false, and you can access the error text using GetError()
+ */
+ bool Execute(const char *command, std::ostream &sout);
+
+ /**
+ * Get the exception string
+ */
+ std::string GetError() const { return m_Error; }
+
+ /**
+ * Get a variable created by C3D during command execution, for example using
+ * '-as' or '-popas' commands
+ */
+ ImageType *GetImage(const char *varname);
+
+private:
+
+ // Private instance of the image converter - not directly exposed through the API
+ typedef ImageConverter<TPixel, VDim> ConverterType;
+ ConverterType *m_Converter;
+
+ // Error text from last execution
+ std::string m_Error;
+};
+
+
+
+#endif // __ConvertAPI_h_
diff --git a/Submodules/c3d/cmake/Package.cmake b/Submodules/c3d/cmake/Package.cmake
new file mode 100644
index 0000000..90532e6
--- /dev/null
+++ b/Submodules/c3d/cmake/Package.cmake
@@ -0,0 +1,124 @@
+# ----------------------------------------------------------------
+# INSTALLATION AND PACKAGING with CPack
+# ----------------------------------------------------------------
+
+# On Win32, we must include the redistributable
+IF(MSVC_VERSION GREATER 1399)
+ FIND_PROGRAM(VCREDIST_X86 vcredist_x86.exe)
+ IF(VCREDIST_X86)
+ INSTALL(FILES ${VCREDIST_X86} DESTINATION bin)
+ SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
+ "ExecWait '\\\"$INSTDIR\\\\bin\\\\vcredist_x86.exe\\\" /passive'"
+ "CreateShortCut 'c:\\\\Windows\\\\system32\\\\cmd.exe' '$INSTDIR\\\\c3dshell.lnk'")
+ ENDIF(VCREDIST_X86)
+ENDIF(MSVC_VERSION GREATER 1399)
+
+# Allow package generation
+SET(CPACK_PACKAGE_NAME "c3d")
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "C3D Medical Image Processing Tool")
+SET(CPACK_PACKAGE_VENDOR "itksnap.org")
+SET(CPACK_PACKAGE_VERSION_MAJOR "${C3D_VERSION_MAJOR}")
+SET(CPACK_PACKAGE_VERSION_MINOR "${C3D_VERSION_MINOR}")
+SET(CPACK_PACKAGE_VERSION_PATCH "${C3D_VERSION_PATCH}")
+SET(CPACK_NSIS_MODIFY_PATH ON)
+
+
+# Shamelessly stolen from ParaView_
+SET(CPACK_SOURCE_PACKAGE_FILE_NAME "c3d-${C3D_VERSION_FULL}")
+IF (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown")
+ EXEC_PROGRAM(uname ARGS "-m" OUTPUT_VARIABLE CMAKE_SYSTEM_PROCESSOR)
+ENDIF (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown")
+IF(NOT DEFINED CPACK_SYSTEM_NAME)
+ SET(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR})
+ENDIF(NOT DEFINED CPACK_SYSTEM_NAME)
+IF(${CPACK_SYSTEM_NAME} MATCHES Windows)
+ IF(CMAKE_CL_64)
+ SET(CPACK_SYSTEM_NAME win64-${CMAKE_SYSTEM_PROCESSOR})
+ ELSE(CMAKE_CL_64)
+ SET(CPACK_SYSTEM_NAME win32-${CMAKE_SYSTEM_PROCESSOR})
+ ENDIF(CMAKE_CL_64)
+ENDIF(${CPACK_SYSTEM_NAME} MATCHES Windows)
+
+# For Apple, we need to base the filename on the architecture
+IF(CMAKE_SYSTEM_NAME MATCHES Darwin)
+ IF(NOT DEFINED CMAKE_OSX_ARCHITECTURES)
+ MESSAGE(ERROR "CMAKE_OSX_ARCHITECTURES must be defined")
+ ENDIF(NOT DEFINED CMAKE_OSX_ARCHITECTURES)
+ STRING(REPLACE ";" "-" ARCH "${CMAKE_OSX_ARCHITECTURES}")
+ SET(CPACK_SYSTEM_NAME "MacOS-${ARCH}")
+ MESSAGE(STATUS " ARCH ${ARCH}")
+ MESSAGE(STATUS " CMAKE_OSX_ARCHITECTURES ${CMAKE_OSX_ARCHITECTURES}")
+ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin)
+
+MESSAGE(STATUS " CPACK_SYSTEM_NAME ${CPACK_SYSTEM_NAME}")
+
+SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}")
+
+MESSAGE(STATUS " CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}")
+
+# Show GPL license
+SET(CPACK_RESOURCE_FILE_LICENSE "${CONVERT3D_SOURCE_DIR}/COPYING.txt")
+
+IF(WIN32 AND NOT UNIX)
+
+ SET(CPACK_GENERATOR "NSIS")
+ SET(CPACK_EXTENSION "exe")
+
+ELSE(WIN32 AND NOT UNIX)
+
+ # Set the generator to either STGZ or Apple
+ IF(NOT APPLE)
+ SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "pauly2 at mail.med.upenn.edu")
+ SET(CPACK_GENERATOR "TGZ")
+ SET(CPACK_EXTENSION "tar.gz")
+ ELSE(NOT APPLE)
+ SET(CPACK_GENERATOR "DragNDrop")
+ SET(CPACK_EXTENSION "dmg")
+ ENDIF(NOT APPLE)
+
+ENDIF(WIN32 AND NOT UNIX)
+
+#--------------------------------------------------------------------------------
+# Construct the name of the package
+SET(CPACK_PACKAGE_FILE_NAME_WEXT "${CPACK_PACKAGE_FILE_NAME}.${CPACK_EXTENSION}")
+
+
+INCLUDE(CPack)
+
+#--------------------------------------------------------------------------------
+# Uploading code to SourceForge
+#--------------------------------------------------------------------------------
+
+#--------------------------------------------------------------------------------
+# Configure SCP
+
+FIND_PROGRAM(SCP_PROGRAM NAMES scp DOC "Location of the scp program (optional)")
+MARK_AS_ADVANCED(SCP_PROGRAM)
+
+SET(SCP_ARGUMENTS "-v" CACHE STRING "Optional arguments to the scp command for uploads to SourceForge")
+MARK_AS_ADVANCED(SCP_ARGUMENTS)
+
+SET(SCP_USERNAME "" CACHE STRING "SourceForge.net account id for uploads")
+MARK_AS_ADVANCED(SCP_USERNAME)
+
+SET(NIGHTLY_TARGET "c3d-nightly-${CPACK_SYSTEM_NAME}.${CPACK_EXTENSION}")
+
+SET(SCP_ROOT "frs.sourceforge.net:/home/frs/project/c/c3/c3d/c3d")
+
+#--------------------------------------------------------------------------------
+# Create targets
+
+ADD_CUSTOM_TARGET(upload_nightly
+ VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS}
+ ${CPACK_PACKAGE_FILE_NAME_WEXT} ${SCP_USERNAME},c3d@${SCP_ROOT}/Nightly/${NIGHTLY_TARGET}
+ DEPENDS ${CPACK_TARGET}
+ WORKING_DIRECTORY ${SNAP_BINARY_DIR}
+ COMMENT "Uploading package ${CPACK_PACKAGE_FILE_NAME_WEXT} to SourceForge.net as ${NIGHTLY_TARGET}")
+
+ADD_CUSTOM_TARGET(upload_experimental
+ VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS} ${CPACK_PACKAGE_FILE_NAME_WEXT} ${SCP_USERNAME},c3d@${SCP_ROOT}/Experimental
+ DEPENDS ${CPACK_TARGET}
+ WORKING_DIRECTORY ${SNAP_BINARY_DIR}
+ COMMENT "Uploading package ${CPACK_PACKAGE_FILE_NAME_WEXT} to SourceForge.net to Experimental directory")
+
+
diff --git a/Submodules/c3d/doc/c3d.md b/Submodules/c3d/doc/c3d.md
new file mode 100644
index 0000000..4fce0ad
--- /dev/null
+++ b/Submodules/c3d/doc/c3d.md
@@ -0,0 +1,1503 @@
+# [Convert3D][1] Documentation
+
+[TOC]
+
+### What's New?
+
+* **-cos**,**-sin**,**-atan2** commands
+* **-align-landmarks** command
+* **-color-map** command
+* **-min**, **-max** commands
+* **-accum**, **-endaccum** loop structure
+* **-tile** command, great for stacking TIFFs into a 3D volume
+* **-test-xxx** set of commands
+* **-holefill** command
+* New **c4d** command
+* **-slice** command extended to support range of slices (e.g., *5:10*, *0:-1*, *0:2:-1*)
+
+### About convert3d
+
+**Convert3d** is a command-line tool for converting 3D images between common file formats. The tool also includes a growing list of commands for image manipulation, such as thresholding and resampling. The tool can also be used to obtain information about image files.
+
+The simplest way to use **convert3d** is to convert images between formats. Here are some examples. Note that you must use the `-o` command to specify the output of the conversion.
+
+ c3d input.img -o output.vtk
+ c3d hello.cub -o hello.img
+ c3d float.img -type short -o short.img
+
+**Convert3d** works in a way like a [Revese Polish notation][2] calculator. This means that actions must be preceeded by their inputs: instead of writing 'a + b', we must write 'a b +'. In our case, the inputs are image files, and the actions are specified on the command line. For example, to add a pair of images, we would issue the following command:
+
+ c3d input1.img input2.img -add -o output.img
+
+Here, `input1.img` and `input2.img` are a pair of image files (in Analyze format), while `-add` and `-o` are commands. The command `-add` takes two input images, adds them, and generates a single output image. The command `-o` (short for output) takes one input image and writes it to a filename specified after the command (in this case, `output.img`).
+
+The behavior of **convert3d** commands can be affected by options. For example, the `-resample` command is affected by the `-interpolation` option. In order for the option to affect a command, *the option must precede the command on the command line*. For example:
+
+ c3d input.img -interpolation Cubic -resample 50x30x40vox -o output.img
+
+All image processing is done in double-precision reals. However you can use the **-type** option to save as any data type. By default, image intensities are rounded to the nearest integer (not rounded down) when saving as a integral data type.
+
+### Command autocompletion for Bash users
+
+Users of the bash shell can take advantage of its command autocompletion features. When this is enabled, you can type on the command line
+
+ c3d input.img -re[TAB]
+
+and as you hit the TAB key, the list of possible completions will be presented to you. This way you will have to go back to this reference less often. To enable this feature, download the script [bashcomp.sh][3] and add the following line to your **.profile** script;
+
+ source SOMEDIR/bashcomp.sh
+
+where SOMEDIR should be replaced by the directory where you saved the script.
+
+### Coordinate Frames and NIFTI
+
+**Convert3d** is NIFTI-compatible. The transformation between the image coordinates and physical coordinates is read from the NIFTI header and maintained through the pipeline. The physical coordinates are in the **RAS** frame: *x* increases from left to right, *y* from posterior to anterior, *z* from inferior to superior. You can display the coordinate transformation using the **-info-full** command.
+
+ $ c3d.exe input.nii -info-full
+ Image #1:
+ Image Dimensions : [176, 255, 216]
+ Bounding Box : {[87.2353 103.015 -108.064], [263.235 358.015 107.936]}
+ Voxel Spacing : [1, 1, 1]
+ Intensity Range : [5, 1775]
+ Mean Intensity : 103.175
+ Voxel->RAS x-form :
+ 1.00000 -0.00000 -0.00000 -87.23527
+ -0.00000 1.00000 -0.00000 -103.01535
+ 0.00000 0.00000 1.00000 -108.06398
+ 0.00000 0.00000 0.00000 1.00000
+ Image Metadata:
+ ITK_InputFilterName = NiftiImageIO
+ ITK_OnDiskStorageTypeName = short
+
+### Usage Examples
+
+To convert an image from Metaimage format to short-valued Analyze format use
+
+ c3d input.mha -type short -o output.img.gz
+
+To apply some arithmetic to the image, do the following
+
+ c3d input.mha -scale 4096 -shift 2048 -type short -o output.img.gz
+
+To resample and Gaussian blur an image the following command is used
+
+ c3d input.hdr -interpolation Linear -resample 256x256x192
+ -smooth 3vox -o output.img.gz
+
+To compute **a+2b** with a pair of images, do
+
+ c3d a.img b.img -scale 2 -add -o c.img
+
+### Multilabel Images
+
+Multilabel (ML) images are images where each voxel is assigned a label, chosen from a relatively small set of labels. Labels are typically integers, although they can be floating point numbers. For example, labels can be used to identify anatomical structures in an image: label 1 may correspond to the white matter, 2 to the gray matter, 3 to CSF, and 0 to the background.
+
+When processing ML images, one must take certain care to make sure that numerical operations do not blend labels into meaningless values. For instance, smoothing a ML image will create intermediate values at places where two labels are near each other. A similar issue comes up when resampling or reslicing these images. However, **convert3d** provides commands that split the ML image into a set of binary images (one for each label), allowing you to perform operations on each label separat [...]
+
+For example, to smooth the components of a ML image separately, we can use the following command
+
+ c3d ml.img -split -foreach -smooth 3mm -endfor -merge -o mlsmooth.img
+
+The **-merge** command implements voting between individual label images. A label assigned to a voxel during merge is that of the label image with highest intensity at that voxel. The values of the labels assigned during the **-merge** command are remembered from the time the **-split** command is run. So if the image *ml.img* has labels *1, 4, 10* then the output image will also have the same labels.
+
+When applying spatial transformations to ML images, one may choose to use nearest neighbor interpolation:
+
+ c3d ref.img ml.img -interpolation NearestNeighbor -reslice-matrix affine.mat -o mltran.img
+
+However, this causes some aliasing of the results. It is sometimes useful to assume that labels are *fuzzy*, and to apply linear or cubic interpolation to the fuzzy labels. Here is how we can apply an affine transformation to a ML image this way:
+
+ c3d ref.img -popas ref ml.img -split -foreach -smooth 3mm
+ -insert ref 1 -reslice-matrix affine.mat -endfor -merge-o mltran.img
+
+This command is somewhat complex, mainly because the **-reslice-matrix** command requires a reference image as the first operand, and we have to use named images to insert it on the stack in the right place.
+
+### Commands: Image Input/Output and Information
+
+Passing an image on the command line (without any switches) results in that image being read and placed at the end of the stack. For example:
+
+ c3d myimage.nii -info another.nii -info
+
+will result in information printed for both images. At the end, `myimage.nii` will be in the first position on the stack and `another.nii` will be at the end of the stack.
+
+#### -dicom-series-list: List image series in a DICOM directory
+
+Syntax: `-dicom-series-list <directory>
+
+Prints out a table of DICOM series ids and corresponding image information to standard output.
+
+#### -dicom-series-read: Read a DICOM image series
+
+Syntax: `-dicom-series-read <directory> <series_id>`
+
+Imports a specific DICOM image series from a directory containing DICOM files. The **directory** parameter may also point to one of the DICOM files in the directory.
+The **seried_id** is a string identifier for the series that can be obtained by calling **-dicom-series-list**
+
+#### -info: Display brief image information
+
+Syntax: `-info`
+
+Prints brief information about the last image on the stack. Does not affect the stack.
+
+ c3d image.hdr -info
+
+Use with the **-foreach** command to get information on multiple images
+
+ c3d images*.nii -foreach -info -endfor
+
+#### -info-full: Display verbose image information
+
+Syntax: `-info-full`
+
+Prints extended information about the last image on the stack, such as the metadata dictionary. For example,
+
+ c3d image.hdr -info-full
+
+#### -mcs, -multicomponent-split: Enable splitting of multi-component images on read
+
+Syntax: `-mcs`
+
+Enable reading of multi-component images. By default, when a multi-component image is encountered, the components are combined into a single image. Setting the **-mcs** flag changes this behavior, and each of the components is loaded sequentially. See the section below on multi-component image support.
+
+ $ c3d -mcs rgb.mha -foreach -probe 110x110x80mm -endfor
+ Interpolated image value at 110 110 80 is 1
+ Interpolated image value at 110 110 80 is 66
+ Interpolated image value at 110 110 80 is 29
+
+ $ c3d rgb.mha -foreach -probe 110x110x80mm -endfor
+ Interpolated image value at 110 110 80 is 49.5198
+
+#### -nomcs, -no-multicomponent-split: Disable splitting of multi-component images on read
+
+Syntax: `-nomcs`
+
+Used to reverse the effect of previous **-mcs** command.
+
+#### -o: Output (write) last image on the stack to image file
+
+Syntax: `-o filename`
+
+Write image, overriding an existing image. Without the **-o** option, **convert3d** will write an image only if it does not exist. The **-o** options protects input images from being accidentally deleted. Here we copy an image, changing format:
+
+ c3d image1.mha -o image2.nii
+
+The **-o** option can also be used to save an intermediate image in the stack:
+
+ c3d image1.img -threshold 1 10 1 0 -o thresh.img -resample 50% -o final.img
+
+
+#### -omc, -output-multicomponent: Output multiple images to single file
+
+Syntax: `-omc [number] filename`
+
+Write multiple images on the **Convert3d** stack as a single multi-component image file. If the optional number *n* is specified, only the last *n* images on the stack will be used. Not all file formats support multi-component output. NIFTI is the safest bet.
+
+ c3d red.nii green.nii blue.nii -omc rgb.mha
+
+For 2D images, this command can be used to generate color PNG files:
+
+ c3d image.nii -slice z 50% -colormap jet -type uchar -omc colorslice.png
+
+#### -oo: Output multiple images to multiple files
+
+Syntax: `-oo image_list` or `-oo image_spec`
+
+Write all images on the **convert3d** stack as multiple files. There are two ways to use this command. The first is to supply a list of file names, separated by spaces:
+
+ c3d labelimage.nii -split -oo labelA.nii labelB.nii labelC.nii
+
+In the above example, the image at the end of the stack will be saved as *labelC.nii*, the image next to the end of the stack will be saved as *labelB.nii* and so on.
+
+The second way to use the **-oo** command is to supply a pattern for the output filenames. In this case, all the images on the stack will be written. The format for the pattern is the same as for the [C++ printf command][8]. For example, the following command
+
+ c3d labelimage.nii -split -oo label%02d.nii
+
+will generate images *label00.nii*, *label01.nii*, *label02.nii* and so on. The image at the end of the stack will have the highest number, and the image at the beginning of the stack will have number 00.
+
+
+### Commands: Stack Manipulation and Flow Control
+
+These commands are used to manipulate the **convert3d** stack. The stack is a linear array of images. Every time an image is specified on the command line, it is loaded and placed at the end of the stack. Most operations take one image from the end of the stack, apply some operation to it, and place the result on the end of the stack. Certain commands like **-levelset** and **-reslice-matrix** take two images from the end of the stack as the input and replace them with a single output. S [...]
+
+Sometimes, for complex operations, it is useful to change the order of the images on the stack, to duplicate images, or to execute the same command multiple times. The stack manipulation and flow control commands allow you to complete complex tasks without saving intermediate images to the disk.
+
+#### -accum, -endaccum: Accumulate operations over all images
+
+Syntax: `-accum command-list -endaccum`
+
+Apply a binary operation (such as addition or multiplication) to all the images on the stack in a cumulative fashion. The command(s) will be applied to the last and second-to-last images on the stack, then to the result of this operation and the third-to-last image on the stack and so on. Below is the example of using the command to add multiple images.
+
+ c3d image*.nii -accum -add -endaccum -o sum.nii
+
+#### -as: Assign image at the end of the stack to a variable
+
+Syntax: `-as var`
+
+Associates the image currently at the end of the stack with variable name 'var'. This allows you to retrieve the image later on the command line using the **-push** command. The **-as** and **-push** commands are useful when you need to use a certain image more than once during a convert3d operation. For example, if you want to compute the distance transform of a binary image and mask it so that the values outside of the binary image region have value 0, you would use the following command:
+
+ c3d binary.img -as A -sdt -push A -times -o masked_distance.img
+
+#### -clear: Clear the image stack
+
+Syntax: `-clear`
+
+Clears the image stack. Images assigned a name with the **-as** command will remain in memory.
+
+#### -foreach, -endfor: Loop commands over all images on the stack
+
+Syntax: `-foreach commands-list -endfor`
+
+This command forces the commands between **-foreach** and **-endfor** to be applied to every image on the stack. The main use of this command is to automate processing of multiple datasets. For example,
+
+ c3d epi*.nii -foreach -smooth 3mm -endfor -oo epism%03d.nii
+
+#### -insert: Insert image anywhere in the stack
+
+Syntax: `-insert var pos`
+
+This command is similar to **-push**, but allows you to insert the image associated with 'var' at any position in the stack, counting from the end. When 'pos' is 0, the image is placed at the end of the stack (same as **-push**). When pos is one, the image is placed at the next-to-end position, and so on.
+
+#### -pop: Remove last image from the stack
+
+Syntax: `-pop`
+
+Removes the last image from the image stack. Images assigned a name with the **-as** command will remain in memory.
+
+#### -popas: Remove last image from the stack and assign to variable
+
+Syntax: `-popas var`
+
+Removes the last image from the stack, but also assigns it the name 'var', keeping the image in memory. Same as calling **-as** *var* followed by **-pop**.
+
+#### -push: Place variable at the end of the stack
+
+Syntax: `-push var`
+
+Places the image associated with variable name 'var' on end of the image stack. Variable names are assigned using the **-as** command. The **-as** and **-push** commands are useful when you need to use a certain image more than once during a convert3d operation. For example, if you want to compute the distance transform of a binary image and mask it so that the values outside of the binary image region have value 0, you would use the following command:
+
+ c3d binary.img -as A -sdt -push A -times -o masked_distance.img
+
+#### -reorder: Rearrange images on the stack
+
+Syntax: `-reorder k` or `-reorder fraction`
+
+Rearranges images in the stack, such that images that are k positions apart become next to each other on the stack. In other words, if the original order of the images is 1, 2, ..., n, the new order of the images becomes 1, 1+k, 1+2k, ..., 2, 2+k, 2+2k, ..., k, 2k, ... n. Of course, n must be divisible by k. As an alternative to specifying k, you can specify a floating point number (i.e., **-reorder** 0.5), in which case k is obtained by multiplying n by the floating point number and rou [...]
+
+The following three commands are equivalent:
+
+ c3d a1.nii a2.nii a3.nii a4.nii b1.nii b2.nii b3.nii b4.nii -reorder 4 ...
+ c3d a1.nii a2.nii a3.nii a4.nii b1.nii b2.nii b3.nii b4.nii -reorder 0.5 ...
+ c3d a1.nii b1.nii a2.nii b2.nii a3.nii b3.nii a4.nii b4.nii ...
+
+The **-reorder** command us useful when you specify two sets of images using wildcards and then want to perform pairwise operations on the images. For example
+
+ c3d weight*.nii gray*.nii -reorder 0.5 -weighted-sum-voxelwise -o wsum.nii
+
+is equivalent to the command
+
+ c3d weight1.nii gray1.nii weight2.nii gray2.nii ... -weighted-sum-voxelwise -o wsum.nii
+
+#### -dup: Duplicate the last image on the stack
+
+Syntax: `-dup`
+
+Duplicates the image at the end of the stack. This is equivalent to **-as var -push var**, but shorter. An example is when you want to pass an image as both arguments to a binary operator, e.g., computing the square of the image intensity:
+
+ c3d input.img -dup -times -o square.img
+
+### Commands: Voxelwise Calculations
+
+#### -add: Voxelwise image addition
+
+Syntax: `-add`
+
+Adds the last two images on the stack, and places the sum at the end of the stack.
+
+ # Add two images: x = a + b
+ c3d a.img b.img -add -o x.img
+
+ # Add three images, x = (a + b) + c in the first example, x = a + (b + c) in the second
+ c3d a.img b.img -add c.img -add -o x.img
+ c3d a.img b.img c.img -add -add -o x.img
+
+ # Subtract two images, using -scale command: x = a - b
+ c3d a.img b.img -scale -1 -add -o x.img
+
+#### -atan2: Voxelwise angle from sine and cosine
+
+Syntax: `-atan2`
+
+Computes the angle in radians from images containing sine and cosine. This is a voxel-wise operation. It requires two images on the stack (sine followed by cosine):
+
+ c3d sin_theta.nii.gz cos_theta.nii.gz -atan2 -o theta.nii.gz
+
+#### -clip: Clip image intensity to range
+
+Syntax: `-clip iMin iMax`
+
+Clips image intensities, so that the values below *iMin* are converted to *iMin* and values greater than *iMax* are converted to *iMax*. This is useful for eliminating hyperintensities in images. Values *iMin* and *iMax* are intensity specifications (see below).
+
+ c3d mri.img -clip 1000 8000 -o mriclip01.img // Clips below and above
+ c3d mri.img -clip -inf 8000 -o mriclip02.img // Clips above only
+ c3d mri.img -clip -inf 95% -o mriclip03.img // Clips at 95th percentile
+
+#### -cos: Voxelwise cosine
+
+Syntax: `-sin`
+
+Replaces the last image on the stack with the cosine trigonometric operation applied to all voxels. Input must be in radians.
+
+#### -divide: Voxelwise image division
+
+Syntax: `-divide`
+
+Divides one image by another. For instance to compute C = A / B, use the command
+
+ c3d A.img B.img -divide -o C.img
+
+Divison may generate infinite and not-a-number (NaN) values if B contains zeros. You can use **-replace** to get rid of these values
+
+ c3d A.img B.img -divide -replace inf 1000 -inf -1000 NaN 0 -o C2.img
+
+#### -exp: Voxelwise natural exponent
+
+Syntax: `-exp`
+
+Computes exponent of each voxel in the last image on the stack.
+
+ c3d input.img -exp -o output.img
+
+#### -erf: Standard error function
+
+Syntax: `-erf mu sigma`
+
+Computes the standard error function. This is useful for applying soft thresholds. The function computes y = erf((x - mu)/sigma).
+
+ c3d input.img -erf 5 2 -o erf.img
+
+#### -log, -ln: Voxelwise natural logarithm
+
+Syntax: `-log`
+
+Computes natural logarithm of each voxel in the last image on the stack.
+
+#### -log10: Voxelwise base 10 logarithm
+
+Syntax: `-log10`
+
+Computes base 10 logarithm of each voxel in the last image on the stack.
+
+#### -max: Voxel-wise maximum of two images
+
+Syntax: `-max`
+
+Computes the voxel-wise maximum of two images. Can be used with the **-accum** command to compute maximum of all images.
+
+ c3d i1.nii i2.nii -max -o max12.nii
+ c3d i1.nii i2.nii i3.nii i4.nii -accum -max -endaccum -o max1234.nii
+
+#### -min: Voxel-wise minimum of two images
+
+Syntax: `-min`
+
+Computes the voxel-wise minimum of two images. Can be used with the **-accum** command to compute minimum of all images.
+
+ c3d i1.nii i2.nii -min -o min12.nii
+ c3d i1.nii i2.nii i3.nii i4.nii -accum -min -endaccum -o min1234.nii
+
+#### -mean: Mean of all images on the stack
+
+Syntax: `-mean `
+
+Computes the mean of all the images on the stack. All images on the stack are replaced with the mean image.
+
+ c3d image_*.nii -mean -o mean.nii
+
+#### -multiply, -times: Multiply two images
+
+Syntax: `-multiply`
+
+Multiply two images voxel-by-voxel. The operation is applied to the last two images on the stack.
+
+ # Compute x = a * b
+ c3d a.img b.img -multiply -o x.img
+
+ # Compute x = a * (b + c) using add and -multiply
+ c3d a.img b.img c.img -multiply -add -o x.img
+
+Combine with the **-dup** command to compute voxelwise square of the image
+
+ # Compute x = a^2
+ c3d a.img -dup -multiply -o x.img
+
+#### -reciprocal: Image voxelwise reciprocal
+
+Syntax: `-reciprocal `
+
+Computes the reciprocal of an image. For instance to compute B = 1 / A, use the command
+
+ c3d A.img -reciprocal -o B.img
+
+#### -replace: Replace intensities in image
+
+Syntax: `-replace I1 J1 I2 J2 ... `
+
+Replace intensity I1 by J1, I2 by J2 and so on. Allowed values of intensity include **nan**, **inf** and **-inf**.
+
+ c3d img1.img -replace 1 128 nan 0.0 -o img2.img
+
+#### -rgb2hsv: Convert RGB image to HSV image
+
+Syntax `-rgb2hsv`
+
+Takes the last three images on the stack and treats them as red, green, and blue channels. Outputs three images corresponding to hue, saturation, value. To read color images you need the ***-msc*** command.
+
+ c3d -mcs color.png -rgb2hsv -omc hsv.png
+
+#### -rms: Voxelwise vector norm
+
+Syntax: `-rms`
+
+Computes RMS (root mean square) of all images on the stack. The command takes the square of each image on the stack, adds all the squared images and takes the square root of the result. This is very useful for statistical operations. Images must have the same size.
+
+ c3d img1.img img2.img img3.img img4.img -rms -o rms.img
+
+The equivalent of this command is
+
+ c3d img1.img img2.img img3.img img4.img -foreach -dup -times -endfor \
+ -accum -add -endaccum -sqrt -o rms.img
+
+#### -scale: Scale intensity by constant factor
+
+Syntax: `-scale <factor>`
+
+Multiplies the intensity of each voxel in the last image on the stack by the given factor.
+
+ c3d img1.img -scale 0.5 -o img2.img
+
+#### -shift: Shift image intensity by constant
+
+Syntax: `-shift <constant>`
+
+Adds the given constant to every voxel.
+
+ c3d img1.img -shift 100 -o img2.img
+
+#### -sin: Sine
+
+Syntax: `-sin`
+
+Replaces the last image on the stack with the sine trigonometric operation applied to all voxels. Input must be in radians.
+
+#### -sqrt: Take square root of image
+
+Syntax: `-sqrt `
+
+Computes square root of each voxel in the image.
+
+ c3d input.img -sqrt -o output.img
+
+#### -stretch: Stretch image intensities linearly
+
+Syntax: `-stretch <u1 u2 v1 v2> `
+
+Stretches the intensities in the image linearly, such that u1 maps to v1 and u2 maps to v2. The linear transformation is applied to all intensities in the image, whether inside the range or not. For example, to map a floating point image with intensities in interval (0,1) to the full range of an unsigned short image, use
+
+ c3d input.img -stretch 0.0 1.0 0 65535 -type ushort -o output.img
+
+#### -thresh, -threshold: Binary thresholding
+
+Syntax: `-thresh <u1 u2 vIn vOut> `
+
+Thresholds the image, setting voxels whose intensity is in the range [u1,u2] to vIn and all other voxels to vOut. Values *u1* and *u2* are intensity specifications (see below). This means that you can supply values **inf** and **-inf** for u1 and u2 to construct a one-sided threshold. You can also specify *u1* and *u2* as percentiles.
+ c3d in.img -threshold -inf 128 1 0 -o out.img
+ c3d in.img -threshold 64 128 1 0 -o out.img
+ c3d in.img -threshold 20% 40% 1 0 -o out.img
+
+#### -voxel-sum: Print sum of all voxel intensities
+
+Syntax: `-voxel-sum `
+
+Print the sum of all voxels in the image.
+
+ $ c3d image.img -voxel-sum
+ Voxel Sum: 200923123
+
+#### -voxel-integral: Print volume integral of all voxel intensities
+
+Syntax: `-voxel-integral`
+
+Like **-voxel-sum**, but multiplies the sum of voxel intensities by voxel volume. This is useful for computing volumes of objects represented by binary images. The result is in 'ml'.
+
+ $ c3d image.img -voxel-integral
+ Voxel Integral: 2341
+
+#### -wsum, -weighted-sum: Weighed sum of images with constant weights
+
+Syntax: `-wsum weight1 weight2 ... weightN `
+
+Computes weighted sum of the last N images on the stack.
+
+ c3d image1.nii image2.nii image3.nii -wsum 0.2 0.7 0.1 -o wsum.nii
+
+This command is particularly useful for combining components in a multicomponent image. For example, for an RGB image, we can convert it to grayscale (using [ImageMagick][13] formula) as follows:
+
+ c3d -mcs rgb.nii -wsum 0.29900 0.58700 0.11400 -o gray.nii
+
+#### -wsv, -weighed-sum-voxelwise: Weighed sum of images with spatially varying weights
+
+Syntax: `-wsv `
+
+Computes weighted sum of N weight images and N scalar images. The images must be interleaved on the stack. All images on the stack are used.
+
+ c3d weight1.nii image1.nii weight2.nii image2.nii weight3.nii image3.nii -wsv -o mysum.nii.gz
+
+The **-reorder** command can simplify loading the images:
+
+ c3d weight*.nii image*.nii -reorder 0.5 -wsv -o mysum.nii.gz
+
+### Commands: Image Header Manipulation
+
+#### -copy-transform: Copy header information
+
+Syntax: `-copy-transform`
+
+Copies the image header, specifically the image to physical space transform (origin, spacing, direction cosines), from the first image to the second image. This is best done with NIFTI images, which store this information well. In the example below, *out.nii* will have the same header as *first.nii* and the same intensities as *second.nii*.
+
+ c3d first.nii second.nii -copy-transform -o out.nii
+
+#### -orient: Change image orientation
+
+Syntax: `-orient CODE`
+
+Set the orientation of the image using one of 48 canonical orientations. The orientation describes the mapping from the voxel coordinate system (i,j,k) to the physical coordinate system (x,y,z). In the voxel coordinate system, i runs along columns of voxels, j runs along rows of voxels, and k runs along slices of voxels. It is assumed (by the NIFTI convention) that the axes of the physical coordinate system run as follows: x from (L)eft to (R)ight, y from (P)osterior to (A)nterior, z fro [...]
+
+The CODE passed in is a three-letter code consisting of letters RLAPSI. Each letter describes the anatomical direction corresponding to the voxel coordinates (i,j,k). For example, code RAI means that i runs from Right to Left, j from Anterior to Posterior, and k from Inferior to Superior.
+
+ c3d input.img -orient RAI -o output.img
+ c3d input.img -orient SAL -o output.img
+
+This command has the same behavior as the 'Reorient Image' menu option in ITK-SNAP.
+
+#### -origin: Set image origin
+
+Syntax: `-origin vector `
+
+Set the origin of the image. The origin is the world coordinate (in NIfTI coordinate space) of the center of the voxel (0,0,0) in the image. The origin should be specified in millimeters.
+
+ c3d input.img -origin 100x100x100mm -o output.img
+
+#### -origin-voxel: Assign image origin to a voxel
+
+Syntax: `-origin-voxel vector `
+
+Set the origin of the image by specifying the voxel coordinates of the center of the patient (RAS) coordinate system. The vector should be specified in voxel units.
+
+ c3d input.img -origin-voxel 60x70x35 -o output.img
+ c3d input.img -origin-voxel 50% -o output.img # image centered around origin
+
+#### -set-sform: Set the transform to physical space
+
+Syntax: `-set-sform <sform.mat> `
+
+Sets the Nifti sform of the last image on the stack to the 4x4 matrix provided.
+
+#### -spacing: Set voxel spacing
+
+Syntax: `-spacing <vector> `
+
+Sets the voxel spacing of the image. This should always be a vector with positive components. For example, to set the spacing of the image to 1mm isotropic, use the command below. This command only changes the header of the image, not its contents.
+
+ c3d img.nii -spacing 1x1x1mm -o out.img
+
+
+### Commands: Image Processing
+
+The following commands invoke an action that is applied to images. Unary commands apply the action to the last image on the stack, binary commands apply to the last two images and so on. Commands are affected by options, which are listed separately.
+
+#### -ad, -anisotropic-diffusion: Perona-Malik anisotropic diffusion filter
+
+Syntax: `-ad conductance n_iter`
+
+Executes the Perona-Malik anisotropic diffusion algorithm on the image. This smoothes the image, but with edge preservation. *Conductance* is a number between 0 and 1 that determines how well edges are preserved. *n_iter* is the number of iterations, which affects the scale of the smoothing.
+
+ c3d x.img -ad 0.1 100 -o ad.img
+
+#### -biascorr: Automatic MRI bias field correction
+
+Syntax: `-biascorr`
+
+Performs automatic bias field correction for MRI images. This feature uses the [N3 implementation in ITK by Dr. Tustison][4], based on the N3 algorithm by Sled et al.
+
+ c3d mri.nii.gz -biascorr -o mricorr.nii.gz
+
+#### -alm, -align-landmarks: Align images based on landmark matching
+
+Syntax: `-alm dof outfile`
+
+Performs rigid or affine alignment between to sets of landmark images. A landmark image is an image where for every intensity value, the centroid of all voxels with that intensity represents a landmark. Landmarks can be created using the paintbrush tool in ITK-SNAP (they can be spheres, cubes, etc). The first image on the stack is the target/fixed/reference image, and the second is the moving image. The parameters are the degrees of freedom, which is a number (6 for rigid, 7 for rigid+sc [...]
+
+ c3d fixed_landmarks.nii moving_landmarks.nii -alm 6 rigid.mat
+ c3d fixed.nii moving.nii -reslice-matrix rigid.mat -o moving_resliced_to_fixed.nii
+
+#### -binarize: Convert image to binary
+
+Syntax: `-binarize`
+
+Converts an image to binary by mapping all background values (the background is 0 by default and can be changed by the option **-background**) to 0 and all non-background values to 1. The **-binarize** command is shorthand for the **-threshold** command.
+
+ c3d test.img -binarize -o binary.img
+ c3d -background 10 -binarize -o binary.img
+ c3d test.img -threshold 10 10 0 1 // equivalent to above command
+
+#### -canny: Canny edge detector
+
+Syntax: `-canny <sigma_vector> <t_lower> <t_upper>`
+
+Performs edge detection on the last image on the stack using the Canny filter. The parameters are a vector of standard deviations defining the scale of the edges detected and lower and upper thresholds for edge selection. See documentation on the [ITK Canny Filter][14].
+
+#### -centroid: Report centroid of foreground voxels
+
+Syntax: `-centroid`
+
+Reports the centroid, in physical coordinates, of all foreground voxels in the image.
+
+ c3d binaryimage.img -centroid // centroid of all non-0 voxels
+ c3d grayimage.img -thresh 1000 7000 1 0 -centroid 1 // centroid of all voxels in range 1000-7000
+ c3d labelimage.img -thresh 5 5 1 0 -centroid // centroid of all voxels with label 5
+ c3d labelimage.img -split -foreach -centroid -endfor // centroids of all labels (including 0)
+
+#### -color-map, -colormap: Convert scalar image to RGB using color map
+
+Syntax: `-color-map ColormapName`
+
+Converts a scalar image to a color (RGB) image using a specified color map. The output of the command are three images, containing the red, green and blue channels of the RGB image. The mapping uses the range of the input image, e.g., using the **jet** color map, the lowest intensity pixel in the image will be mapped to blue, and the highest intesnity pixel will be mapped to red. The admissible color maps are **hot,cool,spring,summer,autumn,winter,copper,jet,hsv,red,green,blue,grey,overu [...]
+
+ c3d scalar.nii.gz -slice z 50% -flip y -color-map jet -type uchar -omc colorslice.png
+
+#### -comp, -connected-components: Compute connected components
+
+Syntax: `-comp`
+
+Computes the connected components of a binary image. Each connected component is assigned an integer index. Indices are ordered by the size of the component, so the component assigned index 1 is the largest. The background is assigned index 0. To select the largest connected component, combine the call to **-comp** with a call to **-threshold**.
+
+ c3d binary.img -comp -o comp.img
+ c3d binary.img -comp -threshold 1 1 1 0 -o largest_comp.img
+
+#### -cmv, -coordinate-map-voxel: Generate voxel coordinate maps (voxel units)
+
+Syntax: `-cmv`
+
+For a *N*-dimensional image, replaces the last image on the stack with *N* images. The *k*-th output image at each voxel contains the $k$-th coordinate of that voxel, in voxel units.
+
+ c3d image.nii -cmv -oo coordmap%d.nii.gz
+
+One can use this command to split a brain segmentation image into a left hemisphere segmentation and a right hemisphere segmentation (assuming the X coordinate corresponds to the right-left axis)
+
+ c3d seg.nii -as SEG -cmv -pop -pop -thresh 50% inf 1 0 -as MASK \
+ -push SEG -times -o seg_left.nii.gz \
+ -push MASK -replace 1 0 0 1 \
+ -push SEG -times -o seg_right.nii.gz
+
+#### -cmp, -coordinate-map-physical: Generate voxel coordinate maps (voxel units)
+
+Syntax: `-cmp`
+
+This command is similar to **-cmv** (**-coordinate-map-voxel**), but the output will contain the physical coordinates of the voxels, in the NIFTI (RAS) coordinate frame.
+
+#### -create: Generate blank image
+
+Syntax: `-create dimensions voxel_size`
+
+Creates a new blank image with specified dimensions and voxel size, and places it at the end of the stack. The image is set to the current background value, which is 0 by default but can be overwritten with the **-background** command. The origin of the image can be changed with the **-origin** command.
+
+ c3d -create 256x256x160 1x1x1mm -o newimage.img
+ c3d -background 128 -create 256x256x160 1x1x1mm -origin 128x128x80mm -o newimage.img
+
+#### -dilate: Binary dilation
+
+Syntax: `-dilate <label> <radius_vector>`
+
+Applies the dilation [mathematical morphology][5] operation to a binary image. The first parameter is the intensity value of the object that is to be dilated. The second is the radius of the dilation structuring element in 3D.
+
+ c3d binary.img -dilate 255 3x3x3vox -o newimage.img
+
+#### -erode: Binary erosion
+
+Syntax: `-erode <label> <radius_vector>`
+
+Applies erosion [mathematical morphology][5] operation to a binary image. The first parameter is the intensity value of the object that is to be eroded. The second is the radius of the erosion structuring element in 3D.
+
+ c3d binary.img -erode 255 3x3x3vox -o newimage.img
+
+#### -fft: Fast Fourier transform
+
+Syntax `-fft`
+
+Computes the Fourier transform of a real-valued image at the end of the stack. The image is replaced by the real and imaginary components of the FFT. This command is only available if **convert3d** is compiled with the FFTW library support.
+
+ c3d image.nii -fft -oo real.nii imag.nii
+
+#### -flip: Flip image around an axis
+
+Syntax: `-flip axes`
+
+Flips the image around specified axes. The parameter 'axes' may be any combination of characters 'x', 'y', and 'z'; the order does not matter.
+
+ c3d input.img -flip xy -o output.img
+
+#### -glm: General linear model
+
+Syntax: `-glm design_matrix_file contrast_vector_file`
+
+Applies voxel-wise general linear model to a set of images. More precisely, the general linear model solves the following system: $Y = X \beta + \epsilon$, where Y are the observations (a list of n images, where each voxel is treated as an independent observation); X is the $n x k$ design matrix, where $k$ is the number of factors; $\beta$ is a set of $k$ unknown images (factors) and $\epsilon$ is the error term. The command will compute the $\beta$ images and return a weighted sum of th [...]
+
+ echo "1 67.00" > design_mat.txt
+ echo "1 75.00" >> design_mat.txt
+ echo "1 80.00" >> design_mat.txt
+ echo "1 83.00" >> design_mat.txt
+ echo "0 1" >> contrast_vec.txt
+ c3d time1.img time2.img time3.img time4.img -glm design_mat.txt contrast_vec.txt -o regress.img
+
+#### -grad, -gradient: Image gradient
+
+Syntax `-grad`
+
+Computes the gradient of the image. Each component of the gradient is placed on the stack in order (x,y,z). The gradient is computed in physical RAS coordinates, taking into account image spacing and orientation. In other words, the gradient is the vector in physical space orthogonal to the isocontours of the image. No smoothing is performed, so it is a good idea to smooth the image first before computing the gradient.
+
+ c3d myimage.nii -smooth 1.2vox -grad -oo grad_comp_%02d.nii
+
+#### -hessobj, -hessian-objectness: Hessian objectness filter
+
+Syntax: `-hessobj <dimension> <min_scale> <max_scale>`
+
+Also known as the Frangi vesselness filter, this filter can be used to highlight tube-like, sheet-like and blob-like objects in the image. For details, see documentation to the [corresponging ITK class][HTOMIF].
+
+Parameter `dimension` is an integer that determines the kind of features that are highlighed. Use 0 for blobs, 1 for tubes, 2 for pancakes, etc. The min and max scale parameters are floating point values, giving the scale of the features highlighted, in physical units. Typically, just one scale is used.
+
+ # Detect vessel-like structures at scale 0.5mm
+ c3d image.nii.gz -hessobj 1 0.5 0.5
+
+ [HTOMIF] http://www.itk.org/Doxygen/html/classitk_1_1HessianToObjectnessMeasureImageFilter.html
+
+#### -holefill: Fill holes in binary image
+
+Syntax: `-holefill intensity_value [0|1] `
+
+Apply the binary hole filling algorithm to a particular intensity value in the image. The input image is typically a binary image or a multi-label segmentation image. Holes (voxels not matching the specified intensity value that are completely contained by voxels matching this value) are filled. The second parameter specifies what type of topological connectivity is used to determine holes. The value 0 uses the default algorithm in ITK (face connectivity) and 1 uses the full connectivity [...]
+
+ c3d segmentation.nii.gz -holefill 5 0 -type uchar -o filledlabel5.nii.gz
+
+#### -lstat, -label-statistics: Display segmentation volumes and intensity statistics
+
+Syntax: `-lstat`
+
+Given a grayscale image and a multilabel (or binary) image, this command computes the statistics for every label in the latter, including volumes, average grayscale intensity, etc. For instance, if image *mri.nii* is a medical image and *seg.nii* is a multilabel segmentation of the image with labels 0, 1 and 4, the following command can be used to print the statistics of the intensity of *mri.nii* for each of the labels
+
+ c3d mri.nii seg.nii -lstat
+
+The output contains the mean, standard deviation, maximum intensity and minimum intensity for each label. If you just need volumes from a multi-label image, use **-dup** command as follows:
+
+ c3d seg.nii -dup -lstat
+
+#### -lms, -landmarks-to-spheres: Generate image of spheres from landmark data
+
+Syntax: `-lm <landmark_file> <radius>`
+
+Reads a list of landmarks from a file and for each landmark draws a sphere with the landmark at the center. Spheres are drawn over the last image on the stack. Landmarks are specified in a file with four entries per line: *x y z value*, where *(x,y,z)* give the landmark position in physical units (mm) and value is the image value that the sphere will be filled with. The radius (for now) is specified in physical units (mm). The example below first creates a landmark file, and then calls c [...]
+
+ echo 14.5 23.12 44.53 1 > landmarks.txt
+ echo 76.3 43.12 34.32 2 >> landmarks.txt
+ ...
+ c3d image.img -scale 0 -lms landmarks.txt 3 -o landspheres.img
+
+The command also supports an alternative format for the landmarks that allows specification in voxel
+units and percent
+
+ echo 50%x50%x50% 1 > landmarks.txt
+ echo 25x25x30vox 1 >> landmarks.txt
+ echo 10x10x45mm 2 >> landmarks.txt
+ ...
+ c3d image.img -scale 0 -lms landmarks.txt 3 -o landspheres.img
+
+#### -laplacian, -laplace: Laplacian filter
+
+Syntax: `-laplacian`
+
+Applies the Laplacian filter to the image. Used to detect ridges of intensity. Typically, used with the **-smooth** option to obtain the equivalent of convolving the image with the *Laplacian of the Gaussian (LoG)* kernel:
+
+ c3d input.img -smooth 1.2vox -laplacian -o output.img
+
+#### -levelset: Level set segmentation
+
+Syntax: `-levelset n_iter `
+
+Perform level set segmentation for *n\_iter* iterations, like in ITK-SNAP. The last image on the stack is treated as the initialization image and the next-to-last image on the stack is the speed image. Both images should be in the range between -1 and 1. Here is how the signs of the different images are interpreted
+
+| | Speed Image | Initialization Image | Output Image |
+| -- | ------------- | -------------------- | ------------ |
+| +1 | Foreground | Outside | Outside |
+| -1 | Background | Inside | Inside |
+
+Here is an example where you have the speed and the initialization given:
+
+ c3d speed.img initial.img -levelset-curvature 0.5 -levelset 100 -o seg.img
+
+Here is an example of segmenting the ventricles in an MRI image, where the ventricles and other CSF have intensity below 715. The image seg_bubbles.nii.gz in this example is a binary image of the initialization seeds (1 inside the seeds, 0 outside).
+
+ c3d brain.nii.gz -erf 715 100 -scale -1 seg_bubbles.nii.gz \
+ -replace 0 1 1 -1 -levelset-curvature 0.2 -levelset 500 \
+ -thresh -inf 0 1 0 -o segmentation.nii.gz
+
+Another example of smoothing a binary image that is useful for cleaning up manual segmentations. Here the speed image is positive inside the binary object, and the initialization is negative inside the object. The command writes out both the level set image (whose 0-level set is the smoothed boundary of the binary object) and the smoothed binary object
+
+ c3d binary.img -threshold 1 inf 1 -1 -binary.img 1 inf 1 -1 \
+ -levelset-curvature 1.5 -levelset 100 -o levelset.img \
+ -thresh -inf 0 1 0 -o smoothed_binary.img
+
+#### -mf, -mean-filter: Mean filter
+
+Syntax: `-mf <radius_vector>`
+
+Applies the mean filter: the intensity of each voxel is replaced by the mean of the intensities in the neighborhood of size specified by the radius parameter. For example, the following code will apply the mean filter with the 5x5x5 neighborhood.
+
+ c3d in.nii -mf 2x2x2 -o filtered.nii
+
+#### -median, -median-filter: Median filter
+
+Syntax: `-median <radius_vector>`
+
+Applies the median filter: the intensity of each voxel is replaced by the median of the intensities in the neighborhood of size specified by the radius parameter. For example, the following code will apply the median filter with the 5x5x5 neighborhood.
+
+ c3d in.nii -median 2x2x2 -o median.nii
+
+#### -merge: Merge images from previous split command
+
+Syntax: `-merge`
+
+Works in conjunction with the **-split** command. Has similar behavior to **-vote**, except that label values are carried from the input to the **-split** command.
+
+#### -msq, -mean-square: Compute mean square difference metric
+
+Syntax: `-msq [movtransform.mat] [reftransform.mat]`
+
+Compute the mean square difference metric between the last two images on the stack. If an optional *movtransform.mat* file is provided, the metric is computed by applying the transform to the moving image. If, in addition to *movtransform.mat*, the optional *reftransform.mat* file is also provided -- the moving transform is applied to the moving image, the ref transform is applied to the reference image, and the metric is computed in an image space that is physically halfway between the [...]
+
+ # Compute metric between ref.nii and mov.nii
+ c3d ref.nii mov.nii -msq
+
+ # Compute metric between ref.nii and mov.nii after applying transform to mov.nii
+ c3d ref.nii mov.nii -msq tmov.mat
+
+ # Compute metric between ref.nii and mov.nii in a neutral space after applying transforms to both
+ c3d ref.nii mov.nii -msq tmov.mat tref.mat
+
+#### -mi, -mutual-info: Compute mutual informaiton metric
+
+Syntax: `-mi [movtransform.mat] [reftransform.mat]`
+
+Compute the mutual information metric between the last two images on the stack. See documentation for **-msq**.
+
+#### -mmi, -mattes-mutual-info: Compute mutual informaiton metric
+
+Syntax: `-nmi [movtransform.mat] [reftransform.mat]`
+
+Compute the Mattes mutual information metric between the last two images on the stack. See documentation for **-msq**.
+
+#### -nmi, -normalized-mutual-info: Compute mutual informaiton metric
+
+Syntax: `-nmi [movtransform.mat] [reftransform.mat]`
+
+Compute the normalized mutual information metric between the last two images on the stack. See documentation for **-msq**.
+
+#### -ncor, -normalized-correlation: Compute normalized correlation metric
+
+Syntax: `-ncor [movtransform.mat] [reftransform.mat]`
+
+ : Compute the normalized correlation metric between the last two images on the stack. See documentation for **-msq***.
+
+#### -nlw, -normalize-local-window: Standardize image intensity using local neighborhood
+
+Syntax: `-nlw <radius>`
+
+This command takes as inputs an image and a mask image. At each voxel, the mean of the local neighborhood is subtracted, and the result is divided by the standard deviation of the neighborhood. The mean and standard deviation are computed only over the masked region. You might also want to multiply by the mask.
+
+ c3d gray.nii.gz mask.nii.gz -nlw 10x10x10 -o residual.nii.gz
+
+#### -oli, -overlay-label-image: Overlay segmentation image on grayscale image
+
+Syntax: `-oli lookup_table_file opacity`
+
+This command takes a grayscale image and a label image (i.e. image with a set of discrete values) and produces red, green and blue components of a color image. The resulting color image is an overlay of the labels over the grey image. The first parameter (*lookup\_table*) is a text file with entries in the format
+
+ label_value red green blue alpha
+
+Alpha values must be between 0 and 1. Red, green and blue values should be on the same order as the intensity of the grey image (typically 0-255). The text file is compatible with ITK-SNAP and can be generated using the ITK-SNAP `Segmentation->Save Label Descriptions` command. The second parameter (*opacity*) is between 0 and 1 and sets the overall opacity of the overlay. The output of this command is similar to the way ITK-SNAP presents segmentation data on top of grayscale images.
+
+ c3d gray.nii.gz -stretch 2% 98% 0 255 -clip 0 255 seg.nii.gz -oli labels.txt 0.5 -omc rgb.nii.gz
+
+Note: this command does not interpolate between entries in the lookup table. It should not be used for images with a continuous intensity spectrum.
+
+Here is a more complex example, used to visualize a segmentation result. We do a few things in this command: trim grayscale and segmentation images to an ROI around the object of interest; map intensity range of the grayscale image to 0-255; extract slices through the middle of the cropped images; overlay segmentation on the grayscale image; and save as a color PNG file.
+
+ c3d seg.nii.gz -trim 20x20x0vox -as S gray.nii.gz -stretch 2% 98% 0 255 -clip 0 255 \\
+ -reslice-identity -push S -foreach -slice z 50% -flip xy -endfor \\
+ -oli labels.txt 0.5 -type uchar -omc ovl.png
+
+#### -overlap: Compute relative overlap between binary images
+
+Syntax: `-overlap Z`
+
+Compute relative overlap between labels in the last two images on the stack. Overlap is computed for a given label **Z**, i.e., the number of voxels that are equal to **Z** in both images is computed and divided by either the average number of voxels equal to **Z** in both images (to get Dice coefficient) or by the size of the region where at least one of the images is equal to **Z** (Jaccard coefficient).
+
+The command below computes overlap for label 255.
+
+ c3d -verbose seg1.img seg2.img -overlap 255
+
+The output of the command is in the following terse format, with the last two values giving Dice and Jaccard coefficients, respectively.
+
+ OVL: 1, 2383, 2474, 1807, 0.744081, 0.592459
+
+Use the flag **-verbose** to get full information.
+
+ Matching voxels in first image: 2383
+ Matching voxels in second image: 2474
+ Size of overlap region: 1807
+ Dice similarity coefficient: 0.744081
+ Intersection / ratio: 0.592459
+
+This command does not alter the stack.
+
+#### -pad: Pad image with constant value
+
+Syntax: `-pad <padlower> <padupper> <value> `
+
+Pads the image by a given percentage or number of voxels. The *padlower* dimension pads along the zero faces of the image, and the *padupper* dimension pads along the upper faces of the image. For example to add 1 voxel to the left side of an image, do
+
+ c3d img1.nii -pad 1x0x0vox 0x0x0vox 0 -o padded.nii
+
+while
+
+ c3d img1.nii -pad 2x2x4vox 0% 0 -o padded.nii
+
+adds two voxels padding to the left and posterior sides, and four slices to the bottom of the image. Note that the first argument changes the location of voxel (0,0,0) and thus the origin of the output image will be changed to maintain anatomical alignment between the padded and original images.
+
+Normally you will want to pad with zeros, but you can pad with any constant value, eg :
+
+ c3d img1.nii -pad 10% 10% 1 -o padded.nii
+
+Adds 10% to all sides of the image, and fills the new voxels with the value 1.
+
+
+#### -pca: Principal components analysis of foreground voxels
+
+Syntax: `-pca`
+
+Similar to the *-centroid* command, computes the centroid and prinicipal components of the foregrond voxels in the image. For example if the image is a binary image of an ellipsoid, this will report the center and the principal axes of the ellipsoid, in physical NIFTI coordinates.
+
+ c3d binaryimage.img -pca // centroid of all non-0 voxels
+ c3d labelimage.img -thresh 5 5 1 0 -pca // centroid of all voxels with label 5
+ c3d labelimage.img -split -foreach -pca -endfor // centroids of all labels (including 0)
+
+#### -probe: Report image intensity at a voxel
+
+Syntax: `-probe <point_spec>`
+
+Prints the value of the image at the position specified by the parameter `point_spec`, which may be in physical units or voxel units:
+
+ c3d img1.img -probe 128x120x160vox
+ c3d img1.img -interpolation NearestNeighbor -probe 60x60x60mm
+ c3d img1.img -probe 50%
+
+#### -rank: Voxelwise ranking of intensity values
+
+Syntax: `-rank `
+
+This command takes N images as the input (all the images on the stack are used). It also generates N images as the output. For voxel k in image j, it assigns it a label based on its rank among the values of voxel k in all N images. If the voxel has highest intensity in image j, then the j'th output will have value 1.
+
+ c3d img1.img img2.img ... imgN.img -rank -oo rank%d.img
+
+#### -rf-train: Train Random Forest classifier
+
+Syntax: `-rf-train <classifier_file>`
+
+This command trains a classifier using an implementation of the [Breyman et al. Random Forest Algorithm][Br2001], with modifications proposed by [Criminisi and Shotton][Cr2004]. The stack must contain one or more images of features (e.g., grayscale images), followed by a multi-label image. The latter must have at least two non-zero labels corresponding to different classes. The classifier is trained on a voxel by voxel basis. All voxels with label *L* are treated as the examples of class [...]
+
+ # Training with two MRI modalities as features and default parameters
+ c3d t1_mri.nii t2_mri.nii segmentation.nii -rf-train myforest1.rf
+
+ # Training with patches as features (see docs for -rf-param-patch)
+ c3d ultrasound.nii seg.nii -rf-param-patch 2x2x2 -rf-train myforest2.rf
+
+ # Applying the classifier
+ c3d ultrasound.nii -rf-apply myforest2.rf -omc class_prob.nii.gz
+
+The commands are meant to replicate the "classification" pre-segmentation mode in ITK-SNAP, i.e., extending a rough example segmentation to the entire image domain. It is possible to also use the commands to train classifiers jointly on data from multiple subjects, each with its own segmentation, as long as the images from the different subjects occupy the same image space and can be stacked into a 4-dimensional image. For example:
+
+ # Train using MRI and segmentations from N subjects
+ c4d mri_subj*.nii -stack w -popas ALLMRI \
+ seg_subj*.nii -stach w -popas ALLSEG \
+ -rf-param-patch 2x2x2x0 \
+ -push ALLMRI -push ALLSEG -rf-train myforest.rf
+
+ # Apply using single MRI
+ c4d mri_new.nii -rf-apply myforest.rf -omc classprob.nii
+
+ [Br2001] Breiman, L. (2001). Random forests. Machine learning, 45(1), 5-32.
+ [Cr2004] Criminisi, A., & Shotton, J. (2013). Decision forests for computer vision and medical image analysis. Springer Science & Business Media
+
+#### -rf-apply: Apply Random Forest classifier
+
+Syntax: `-rf-apply <classifier_file>`
+
+This command applies a classifier trained previously by **-rf-train**. The stack must contain the same number of feature images as when training. The images will be removed from the stack and replaced with a set of K probability images, where K is the number of classes during training. See examples under **-rf-train** for usage.
+
+#### -region: Extract region from image
+
+Syntax: `-region vOrigin vSize `
+
+Extract a rectangular region from the image. The first parameter is the position of the corner of the region, and the second is the size of the region.
+
+ c3d img1.img -region 20x20x20vox 50x60x70vox -o img2.img
+ c3d img1.img -region 25% 50% -o img3.img
+
+#### -resample: Resample image to new dimensions
+
+Syntax: `-resample <dimensions> `
+
+Resamples the image, keeping the bounding box the same, but changing the number of voxels in the image. The dimensions can be specified as a percentage, for example to double the number of voxels in each direction. The **-interpolation** flag affects how sampling is performed.
+
+ c3d img1.img -resample 123x142x200 -o img2.img
+ c3d img1.img -resample 200% -o img2.img
+ c3d img1.img -resample 100x100x200% -o img2.img
+ c3d img1.img -background 4.0 -interpolation Cubic -resample 123x142x200 -o img2.img
+
+#### -resample-mm: Resample image to new resolution
+
+Syntax: `-resample-mm <voxel_size> `
+
+Resamples the image as in **-resample**, but the user specifies the new voxel size rather than dimensions. This may not be precise, so the bounding box of the image may change. A warning will be generated in that case.
+
+ c3d img1.img -resample-mm 1.0x1.5x1.5mm -o img2.img
+
+#### -reslice-matrix: Resample image using affine transform
+
+Syntax: `reslice-matrix <transform_file>`
+
+Applies affine transform to an image. The first image on the command line is the reference image. The output image will have same dimensions and voxel-to-space transform as the reference image. The second parameter is the moving image, i.e., the image to be resliced. The transform file is a 4x4 matrix that gives a transform *from the physical space of the reference image to the physical space of the moving image*. To generate a transform file, you must use some sort of registration algor [...]
+
+ c3d reference_image.nii moving_image.nii -reslice-itk transform_file.txt -o output_image.nii
+
+There is a separate tool **c3d_affine_tool** for manipulating these matrix files.
+
+Here is an example of using **-reslice-matrix** with SPM. In MATLAB, you would execute the following commands
+
+ p = spm_coreg('reference.nii', 'moving.nii');
+ m = inv(spm_matrix(p(:)'));
+ save -ascii ref2mov_mat.txt
+
+Then to reslice using **convert3d**, run
+
+ c3d reference.nii moving.nii -reslice-matrix ref2mov.txt -o resliced.nii
+
+The related command **-reslice-itk** can be used to use affine transforms computed by ANTs software.
+
+#### -reslice-itk: Resample image using affine transform
+
+Syntax: `-reslice-itk <transform_file> `
+
+Applies affine (or other) transform in ITK (ANTs) format to an image. See notes to **-reslice-matrix** for usage.
+
+#### -reslice-identity: Resample image using identity transform
+
+Syntax: `-reslice-identity `
+
+Applies the **-reslice-matrix** command with the identity transform. This is useful when you have two scans of the same subject with different coordinate transformations to patient space and you want to resample one scan in the space of another scan. For example, if you have T1 and T2 images in different coordinate frames, and want to reslice the T2 image into the space of the T1
+
+ c3d t1.nii t2.nii -reslice-identity -o t2_in_t1_space.nii
+
+#### -sdt, -signed-distance-transform: Signed distance transform of a binary image
+
+Syntax: `-sdt`
+
+Computes the signed distance transform of a binary image. Voxels where the binary image is non-zero will have negative values and voxels where the binary image is zero will have negative values. The magnitude of the value will be the approximate Euclidean distance to the boundary of the object represented by the binary image.
+
+ c3d binary.img -sdt -o dist.img
+
+#### -sharpen: Sharpen edges in the image
+
+Syntax: `-sharpen`
+
+Applies the Laplacian sharpening filter from ITK, which accentuates the edges in the image.
+
+ c3d input.nii.gz -sharpen -o output.nii.gz
+
+#### -slice: Extract slices from an image
+
+Syntax: `-slice axis position_spec`
+
+Extracts a slice along the specified axis (x,y or z). The position specifier **position_spec** can be a single slice or a range of slices. For a single slice, it can be specified as a number or a percentage. Numbering is zero-based, i.e, the first slice is slice 0, the last slice is N-1, where N is the number of slices. For a range, use MATLAB notation first:step:last. The slice is placed on the stack as an image with size 1 in the last dimension. You can save the slice as a 2D PNG image.
+
+ c3d input.img -slice x 128 -o myslice.nii.gz
+ c3d input.img -slice y 50% myslice.nii.gz
+ c3d input.img -slice z 25% -type uchar -stretch 0 2000 0 255 -o myslice.png
+ c3d input.img -slice z 0:-1 -oo slice%0d.nii.gz
+ c3d input.img -slice z 20%:10%:80% -oo slice%0d.nii.gz
+
+With the new command **c4d**, the **-slice** command can be used to extract volumes from a 4D image. This can be useful to reformat a 4D NIFTI image as a 3D multi-component NIFTI image, using the command
+
+ c4d input4d.nii.gz -slice w 0:-1 -omc output3d_multicomp.nii.gz
+
+#### -smooth: Gaussian smoothing
+
+Syntax: `-smooth <sigma_vector> `
+
+Applies Gaussian smoothing to the image. The parameter vector specifies the standard deviation of the Gaussian kernel. Also see [Vector Format Specification][10] below.
+
+ c3d img1.img -smooth 2x1x1vox -o out.img
+
+#### -smooth-fast: Fast approximate Gaussian smoothing
+
+Syntax: `-smooth-fast <sigma_vector> `
+
+Applies Gaussian smoothing to the image using the fast [Deriche recursive smoothing algorithm][15]. The parameter vector specifies the standard deviation of the Gaussian kernel. Also see [Vector Format Specification][10] below.
+
+ c3d img1.img -smooth-fast 20x10x10vox -o out.img
+
+#### -split: Split multi-label image into binary images
+
+Syntax: `-split`
+
+This command takes a multilabel image (one with a small number of discrete intensity levels), and replaces it with a set of binary images, one for each of the levels. The images can later be recombined using the **-merge** command. The labels corresponding to each binary image are remembered by **convert3d** so that when **-merge** is called, the labels are faithfully reassigned. The **-merge** command treats each input as a probability image, and selects at each voxel the label that has [...]
+
+ c3d multilabel.nii -split -foreach -smooth 3mm -endfor -merge -o ml_smooth.nii
+
+Also of note is that the **-split** command will disregard infinite intensity values. So if you want to apply voting to a subset of the labels, you can replace labels you do not care about with *inf*, for example, using the **-thresh** command.
+
+#### -staple: STAPLE algorithm to combine segmentations
+
+Syntax: `-staple <intensity_value> `
+
+Runs the ITK implementation of the STAPLE algorithm ([See Paper][11]). STAPLE generates an estimate of the 'true' segmentation of a structure given a set of segmentations by different raters. This command treats all images on the stack as inputs. Each image is considered to be a segmentation by a different rater. The parameter *intensity_value* specifies the label in the segmentation images corresponding to the structure of interest (e.g., the segmentation image may have value 1 correspo [...]
+
+ c3d -verbose rater1.img rater2.img rater3.img -staple 1 -o probmap.img
+ c3d -verbose rater*.img -staple 1 -threshold 0.5 inf 1 0 -o bin_segm.img
+
+#### -test-image, -test-probe: Test condition
+
+Syntax: `-test-image [tolerance]` and `-test-probe <vector> <value> [tolerance]`
+
+These advanced commands (with more to come in the future) are primarily meant to allow testing of **c3d**. However, they can also be used for flow control in shell scripts (e.g., **bash** shell). The commands check a certain aspect of the **c3d** state and cause the program to exit with either return code 0 if the test succeeded or a non-zero return code if the test failed.
+
+**-test-image** tests if the last two images on the stack are identical (both in terms of data and header). Returns 0 if the images are identical. The optional tolerance parameter has default value 1e-8.
+
+ c3d input1.img input2.img -test-image
+
+**-test-probe** is similar to the **-probe** command. It tests if the value of the last image on the stack at the position given by **vector** is equal to the **test_value**. An optional tolerance value may be specified, the default is 1e-8.
+
+ c3d input1.img -test-probe 40x40x20vox 1.0 1e-6
+
+#### -tile: Tile and stack multiple images into one
+
+Syntax: `-tile <tile_spec>`
+
+Tiles multiple images into a single image -- including stacking slices into a 3D volume. The command takes all images on the stack and produces a single tiled image. The **tile_spec** parameter can either specify a coordinate axis (x, y, or z) along which to tile the images, or a layout vector (e.g., **4x4**) which specifies the tiling along each coordinate. Passing 0 for the last value in the layout vector determines the value based on the number of images currently loaded. For example, [...]
+
+ c3d slices*.png -tile z -o volume.nii.gz
+
+And to arrange the same 2D slices into a 2D montage of 4 images per row, we would use the **c2d** command as follows:
+
+ c2d slices*.png -tile 4x0 -type uchar -o montage.png
+
+#### -trim: Trim background region of image
+
+Syntax: `-trim <margin_vector>`
+
+Use this command to trim background in an image. When most of the image is filled by background, this command will find the smallest rectangular region that contains all of the non-background voxels in the image. I will then expand this region by the margin of the size specified, and return the resulting region as the new image. For example, this command will trim an image, leaving a 5-voxel margin of background values on all sides
+
+ c3d in.img -trim 5vox -o out.img
+
+#### -trim-to-size: Trim image to given size
+
+Syntax: `-trim-to-size <size_vector>`
+
+Like **-trim**, this command trims the background in an image. However, instead of **-trim**, you specify the target size of the output region. The actual region may be smaller if the specified region falls outside the boundaries of the input image. For example, if you want a 64x64x128 image containing all the foreground pixels in your image, call
+
+ c3d in.img -trim-to-size 64x64x128vox -o out.img
+
+#### -vote: Vote among images on the stack
+
+Syntax: `-vote `
+
+This command takes all images on the stack as arguments and at each voxel *(i,j,k)* returns the index of the image for which the image value at *(i,j,k)* is the greatest. This is most useful when combining probability maps into a single label image. If images prob1.img, prob2.img, etc. give the probability of label 1, 2, etc. over the image domain, the **-vote** command will return the most probable label at each voxel.
+
+ c3d prob1.img prob2.img prob3.img -vote -type uchar -o label.img
+
+The value assigned to each image is based on its position from the bottom of the stack, with zero indicating bottom-most image. In the example above, the output image has values 0 for voxels where prob1.img is highest, 1 for prob2.img and 2 for prob3.img. Also see the related commands **-split** and **-merge**.
+
+#### -voxreg, -voxelwise-regression: Regression between two images
+
+Syntax: `-voxreg regression_order `
+
+Perform regression between corresponding voxels in two images. This command takes two images as input, X and Y. This command finds parameters b\_0, b\_1, ..., b\_k, such that Y is best approximated by b\_0 + b\_1 * X + b\_2 * X^2 + ... + b_k * X^k. Here is an example of linear regression.
+
+ $ c3d Y.nii X.nii -voxreg 2
+ REGCOEFF[0] = 5.56935
+ REGCOEFF[1] = 0.844024
+
+ $ c3d Y.nii X.nii -scale 0.844024 -shift 5.56935 -voxreg 2
+ REGCOEFF[0] = 0
+ REGCOEFF[1] = 1
+
+#### -wrap: Wrap (rotate) image
+
+Syntax: `-wrap <vector> `
+
+Wrap image around one or more voxel dimensions. Wrapping is typically used to correct for MRI wrap-around artifacts. The vector must have integer components, possibly negative. For example,
+
+ c3d badmri.nii.gz -wrap 0x20x0 -o fixedmri.nii.gz
+
+will wrap the image in the second voxel dimension by 20 voxels (i.e., voxel at 10x40x20 will me moved to the position 10x20x20).
+
+
+### Commands: Options and Parameters
+
+Options change the behavior of commands that *appear later on the command line*. This is very important. Specifying options after the command will have no effect.
+
+#### -background: Specify background intensity
+
+Syntax: `-background <value> `
+
+Sets the background intensity for interpolation and other operations where some default background value is needed. Default is 0.
+
+#### -compress, -no-compress: Enable/disable compression for some image files
+
+Syntax: `-compress` or `-no-compress`
+
+Turns on compressing for image file formats that support it. For some file formats, like NIFTI (.nii), compression is enabled automatically when the filename includes the **.gz** extension. For other formats, like MetaImage, you need to specify **-compress** to enable compression. The following two commands save the image as compressed NIFTI and MetaImage files:
+
+ c3d input.nii -o output.nii.gz
+ c3d input.nii -compress -o output.mha
+
+#### -interpolation: Set interpolation mode
+
+Syntax: `-interpolation <NearestNeighbor|Linear|Cubic|Sinc|Gaussian> [param]`
+
+Specifies the interpolation used with **-resample** and other commands. Default is **Linear**. Gaussian interpolation takes as the parameter the standard deviation of the Gaussian filter (e.g, 1mm). Gaussian interpolation is very similar in result to first smoothing an image with a Gaussian filter and then reslicing it with linear interpolation, but is more accurate and has less aliasing artifacts. It is also slower, and should only be used with small sigmas (a few voxels across).
+
+Shorthand 0 can be used for *NearestNeighbor*, 1 for *Linear* and 3 for *Cubic*. For example:
+
+ c3d -int 3 test.nii -resample 200x200x200% -o cubic_supersample.nii
+
+
+#### -noround, -round: Floating point rounding behavior
+
+Syntax: `-noround` or `-round `
+
+By default, **convert3d** will round floating point values when converting to an integer, short or byte image. This command specifies that rounding should not be used. Rounding is used to avoid numerical errors stemming from the internal floating point representation.
+
+ c3d image1.img -type short -noround image2.img
+
+
+#### -pim, -percent-intensity-mode: Set behavior of % specifier
+
+Syntax: `-pim Quantile | q | ForegroundQuantile | fq | Range | r`
+
+This options changes how the percent sign (%) is interpreted when specifying intensity values. **Quantile (q)** means that `10%` describes the 10th percentile of all intensity values in the image (i.e., 10% of the voxels have lower intensity). **ForegroundQuantile (fq)** is similar, but voxels with background intensity (see **-background** option) are excluded from the percentile computation. **Range (r)** changes the meaning of percent sign from percentile to the range between the minim [...]
+
+ $ c3d comp01.png -verbose -pim Quantile -verbose -threshold 75% inf 1 0
+ Quantile 0.75 maps to 18
+
+ $ c3d comp01.png -verbose -pim ForegroundQuantile -verbose -threshold 75% inf 1 0
+ Foreground quantile 0.75 (over 37467 voxels) maps to 58
+
+ $ c3d comp01.png -verbose -pim Range -verbose -threshold 75% inf 1 0
+ Intensity range spec 0.75 maps to 191.25
+
+#### -rf-param-patch: Random Forest training patch size
+
+Syntax: `-rf-param-patch <size_spec>`
+
+Set the radius of the patch used to generate features for the RF classifier. By default this is zero, which means that just the intensity of each voxel is used as a feature. Setting this to non-zero values will result in neighboring intensities also being used as features, and can improve classification in presence of complex image texture. The patch size in each dimension is (2 * radius + 1). See **-rf-train** command for details.
+
+ # Set patch size to 5x5x5
+ c3d ... -rf-param-patch 2x2x2 ... -rf-train myforest.rf
+
+#### -rf-param-usexyz: Random Forest coordinate features
+
+Syntax: `-rf-param-usexyz`
+
+Use the coordinates of voxels as additional features. This allows some geometric relations between different labels to be learned. Equivalent to the corresponding ITK-SNAP option.
+
+#### -rf-param-treedepth: Random Forest tree depth
+
+Syntax: `-rf-param-treedepth <integer>`
+
+Sets the depth of the trees in the classifier. Default value is 30. Deeper trees can learn on more complex data but require more time.
+
+#### -rf-param-ntrees: Random Forest forest size
+
+Syntax: `-rf-param-ntrees <integer>`
+
+Sets the number of trees in the forest. Default value is 50. Larger forests are more robust but more time to train and apply.
+
+#### -spm, -nospm: SPM compatibility in Analyze output
+
+Syntax: `-spm` or `-nospm `
+
+These options specify whether use the SPM extension to the Analyze (.hdr,.img) format. When this option is on, the origin field stored by SPM in the Analyze header will be correctly interpreted. When saving analyze files, the origin will be set correctly. The default is equivalent to the **-nospm** option. Best to avoid this issue altogether by using NIFTI and SPM5 or later.
+
+ c3d -spm in.hdr out.img.gz
+
+
+#### -type: Specify pixel type for image output
+
+Syntax: `-type < char | uchar | short | ushort | int | uint | float | double > `
+
+Specifies the pixel type for the output image. By default, images are written in floating point (**float**) format. The type does not affect how images are processed, only how they are saved.
+
+ c3d image1.img -type short image2.img
+
+Some images require data in certain types. For example, to save PNG images, uchar or ushort type must be specified.
+
+#### -verbose: Enable verbose output of commands
+
+Syntax: `-verbose`
+
+Commands entered after the **-verbose** command will print debugging information. This can be turned off with **-noverbose**.
+
+### Parameter Specifications
+
+
+#### Vectors
+
+Certain commands, like **-smooth**, take vectors as parameters. Vectors can be specified in voxels and in spacing units (mm). The following are some examples of allowed specifications:
+
+ c3d ... -smooth 1.0x1.2x0.9mm ... // Anisotropic, specified in mm (spacing units)
+ c3d ... -smooth 1.0mm // Isotropic, specified in mm (spacing units)
+ c3d ... -smooth 1.0x1.2x0.9vox ... // Anisotropic, specified in voxels
+ c3d ... -smooth 3vox // Isotropic, specified in voxels
+
+#### Geometry
+
+Geometry specifications are similar to the ones used in ImageMagick. They have the format **S1xS2xS3+O1+O2+O3**, where S1,S2,S3 give the size of the region and O1,O2,O3 give the offset. The offset can be omitted, and will default to one.
+
+#### Intensity Values
+
+Intensity values can be specified as floating point values. Additionally, some commands support values **-inf** and **inf** for negative and positive infinity. Furthermore, intensity can be specified as a percentile of overall image intensity (computed in the last image on the **convert3d** stack). Here are some examples using the **-clip** command:
+
+ c3d ... -clip 100 400 // Floating point lower and upper bounds
+ c3d ... -clip -inf 400 // Infinite lower bound
+ c3d ... -clip -inf 90% // Upper bound specified as a percentile
+
+With the **-percent-intensity-mode** (shorthand **-pim**) option, you have fine control over how the percent sign is interpreted when specifying intensities. By default, `10%` means 10th percentile of all intensities in the image. With **-pim ForegroundQuantile**, the quantile is computed only over voxels that are different from the background value (specified with **-background** option. This is useful if you need a percentile over a masked region of an image, etc. Lastly, with **-pim R [...]
+
+### File Format Notes
+
+**convert3d** supports all the file formats that ITK can read and write. In addition it supports some additional formats and format extensions.
+
+#### .hdr, .img, .img.gz
+
+Analyze files saved in SPM will by default be assigned a zero origin (as ITK does). But with the **-spm** option passed in before the filename, the origin will be correctly set.
+
+#### .cub
+VoxBo CUB files can be read and written
+
+#### .df3
+PovRay 3D density files can be written. They must be used with integral types (e.g., **-type ushort**)
+
+### Support for Multicomponent Images
+
+This section discusses images with multiple components, where each voxel contains more than one value. For example color (RGB) images and diffusion tensor images are multi-component images. C3D can read and write multi-component images, but at the present, it can only represent these images internally as separate single-component images.
+
+Use the **-mcs** flag to tell **convert3d** that when it encounters a multi-component image file, it should load each of the components as separate images. If you don't use this flag, only the first component of a multi-component image will be read in.
+
+ c3d -mcs ...
+
+For example, suppose *rgb.mha* is a three-component color image. If you read it with the **-mcs** flag, **convert3d** will create three volumes, one for the red component, one for the green and one for the blue. You can use the **-foreach** construct to apply operations to the components. You can then save the components into a multi-component image with the **-omc** command. Here is an example:
+
+ c3d -mcs rgb.mha -foreach -scale 2 -shift 1 -endfor -omc 3 rgbscaled.mha
+
+Here is another example, where we take an RGB image and extract red, green and blue channels.
+
+ c3d -verbose rgb.mha -o blue.nii.gz -pop -o green.nii.gz -pop -o red.nii.gz
+
+The components are being saved in reverse order. That's because when the image is read, the red component is placed at the top of the stack, then the green component is placed on the top of the red component, and finally, the blue component is placed at the top of the green component. So the blue component is at the top of the stack. Since **convert3d** commands like **-o** apply to the image at the top of the stack, the blue image is saved first, then removed (popped off) the stack, fol [...]
+
+With the advent of the **-oo** command (output multiple images), the command line becomes a little cleaner:
+
+ c3d -mcs rgb.mha -oo red.nii.gz green.nii.gz blue.nii.gz
+
+or, if you want to name the component images as **comp00.nii.gz**, **comp01.nii.gz**, **comp02.nii.gz**:
+
+ c3d -mcs rgb.mha -oo comp%02d.nii.gz
+
+To create an RGB image from component images, we would use the following command:
+
+ c3d red.nii.gz green.nii.gz blue.nii.gz -omc 3 rgb.mha
+
+### Commands with Variable Number of Parameters
+
+Some more recently added commands in **c3d** take multiple parameters and apply them to multiple images on the stack. As a rule, the last parameter is applied to the topmost image on the stack, the second to last parameter is applied to second from the top image on the stack, and so on.
+
+For example, the \``\`-weighted-sum''' command computes a weighted sum of several images:
+
+ c3d A.nii B.nii C.nii D.nii -weighted-sum 0.1 0.2 0.3 0.4 -o W.nii
+
+The result of this command will be 0.1 * A + 0.2 * B + 0.3 * C + 0.4 * D. The equivalent way to compute this expression is
+
+ c3d A.nii -scale 0.1 B.nii -scale 0.2 -add C.nii -scale 0.3 -add D.nii -scale 0.4 -add -o W.nii
+
+Another example is the **-oo** command, which allows us to save several images at once.
+
+### How to Add a Command to C3D (For Software Developers)
+
+This section describes the steps you must typically take to add a new command to C3D. Almost all commands are implemented in an adapter object. An adapter is simply a C++ class that inherits from `ConvertAdapter` and implements the `()` operator, where the work of the command is performed. There are many examples of adapters in the `adapters` directory. Some very simple commands, as well as options, stack manipulation, and flow control commands do not have adapters. We will discuss addin [...]
+
+1. To add a command to C3D, you should first create an adapter object. To simplify things, we provide a bash script to create a new adapter in the `adapters/generator` folder. Simply pass in the name of the generator and the parameters to the `operator ()` to the script. For example, if we are adding a command with a single parameter that is a vector, we would call the script as follows:
+
+ cd MYDIR/convert3d/adapters/generator
+ bash runme.sh MyNewAdapter "RealVector x"
+ cat MyNewAdapter.cxx <- Check that everything is OK
+ cat MyNewAdapter.h
+ mv MyNewAdapter.* .. <- Copy to the adapter directory
+
+2. Next, you must write the code for the command. Typically, all you have to do now is to fill out the code for the `operator ()`. Please be sure to use the same convention as in other adapters. For example, every adapter should use the `*c->verbose` stream to report what it's doing and which image it's doing it to. Commands in C3D consume their arguments, so be sure to pop your images off the stack and push your result on the stack. Also be sure to do necessary error checking and fire [...]
+
+3. Next, you must add your new adapter to the `CMakeLists.txt` file. That's easy, just look for all the other adapters and insert yours in alphabetical order.
+
+4. Next, edit `ConvertImageND.cxx`. This is the main application driver and command line processor. At the top of the file, add an `#include` statement for your new adapter, keeping things in alphabetical order.
+
+5. Staying in `ConvertImageND.cxx`, insert your new command in the `usage()` function, again, keeping things in alphabetical order.
+
+6. Still in `ConvertImageND.cxx`, find the section of the code where commands are implemented. Find where your command fits in alphabetical order, and insert a section of code corresponding to your command. Again, use existing commands as examples. Make sure to return a value - how many parameters your command has consumed from the command line. Otherwise, you will get a "fell through" error message.
+
+7. Almost done. Edit the `utilities/bashcomp.sh` script and add a line for your command there.
+
+8. Last step: edit the documentation file (c3d.md) to add your command's description
+
+To summarize, here is a checklist for adding a new command
+
+1. Generate adapter via bash script
+2. Write code for `operator ()`
+3. Add line in `CMakeLists.txt`
+4. Add `#include` line in `ConvertImageND.cxx`
+5. Add `usage()` line in `ConvertImageND.cxx`
+6. Add code to parse command and call adapter in `ConvertImageND.cxx`
+7. Add line to `bashcomp.sh`
+8. Document the command on the wiki
+
+ [1]: http://www.itksnap.org/pmwiki/pmwiki.php?n=Convert3D.Convert3D
+ [2]: http://en.wikipedia.org/wiki/Reverse_Polish_notation
+ [3]: http://c3d.cvs.sourceforge.net/viewvc/*checkout*/c3d/convert3d/utilities/bashcomp.sh
+ [4]: http://www.insight-journal.org/browse/publication/640
+ [5]: http://en.wikipedia.org/wiki/Morphological_image_processing
+ [6]: http://www.fil.ion.ucl.ac.uk/spm/doc/books/hbf2/pdfs/Ch7.pdf
+ [7]: http://www.itk.org/Doxygen/html/classitk_1_1BinaryFillholeImageFilter.html
+ [8]: http://www.cppreference.com/wiki/c/io/printf
+ [9]: http://www.itksnap.org/pmwiki/pmwiki.php?n=Convert3D.Point
+ [10]: #ParameterSpecifications
+ [11]: http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=1309714
+ [12]: http://picsl.upenn.edu/ANTS/index.php
+ [13]: http://www.imagemagick.org/script/command-line-options.php#colorspace
+ [14]: http://www.itk.org/Doxygen/html/classitk_1_1CannyEdgeDetectionImageFilter.html
+ [15]: http://www.itk.org/Doxygen/html/classitk_1_1RecursiveGaussianImageFilter.html
diff --git a/Submodules/c3d/gui/CMake/DeployQt5.cmake b/Submodules/c3d/gui/CMake/DeployQt5.cmake
new file mode 100644
index 0000000..fe2ab1a
--- /dev/null
+++ b/Submodules/c3d/gui/CMake/DeployQt5.cmake
@@ -0,0 +1,337 @@
+#.rst:
+# DeployQt5
+# ---------
+#
+# Functions to help assemble a standalone Qt5 executable.
+#
+# A collection of CMake utility functions useful for deploying Qt5
+# executables.
+#
+# The following functions are provided by this module:
+#
+# ::
+#
+# write_qt5_conf
+# resolve_qt5_paths
+# fixup_qt5_executable
+# install_qt5_plugin_path
+# install_qt5_plugin
+# install_qt5_executable
+#
+# Requires CMake 2.8.9 or greater because Qt 5 does.
+# Also depends on BundleUtilities.cmake.
+#
+# ::
+#
+# WRITE_QT5_CONF(<qt_conf_dir> <qt_conf_contents>)
+#
+# Writes a qt.conf file with the <qt_conf_contents> into <qt_conf_dir>.
+#
+# ::
+#
+# RESOLVE_QT5_PATHS(<paths_var> [<executable_path>])
+#
+# Loop through <paths_var> list and if any don't exist resolve them
+# relative to the <executable_path> (if supplied) or the
+# CMAKE_INSTALL_PREFIX.
+#
+# ::
+#
+# FIXUP_QT5_EXECUTABLE(<executable> [<qtplugins> <libs> <dirs> <plugins_dir> <request_qt_conf>])
+#
+# Copies Qt plugins, writes a Qt configuration file (if needed) and
+# fixes up a Qt5 executable using BundleUtilities so it is standalone
+# and can be drag-and-drop copied to another machine as long as all of
+# the system libraries are compatible.
+#
+# <executable> should point to the executable to be fixed-up.
+#
+# <qtplugins> should contain a list of the names or paths of any Qt
+# plugins to be installed.
+#
+# <libs> will be passed to BundleUtilities and should be a list of any
+# already installed plugins, libraries or executables to also be
+# fixed-up.
+#
+# <dirs> will be passed to BundleUtilities and should contain and
+# directories to be searched to find library dependencies.
+#
+# <plugins_dir> allows an custom plugins directory to be used.
+#
+# <request_qt_conf> will force a qt.conf file to be written even if not
+# needed.
+#
+# ::
+#
+# INSTALL_QT5_PLUGIN_PATH(plugin executable copy installed_plugin_path_var <plugins_dir> <component> <configurations>)
+#
+# Install (or copy) a resolved <plugin> to the default plugins directory
+# (or <plugins_dir>) relative to <executable> and store the result in
+# <installed_plugin_path_var>.
+#
+# If <copy> is set to TRUE then the plugins will be copied rather than
+# installed. This is to allow this module to be used at CMake time
+# rather than install time.
+#
+# If <component> is set then anything installed will use this COMPONENT.
+#
+# ::
+#
+# INSTALL_QT5_PLUGIN(plugin executable copy installed_plugin_path_var <plugins_dir> <component>)
+#
+# Install (or copy) an unresolved <plugin> to the default plugins
+# directory (or <plugins_dir>) relative to <executable> and store the
+# result in <installed_plugin_path_var>. See documentation of
+# INSTALL_QT5_PLUGIN_PATH.
+#
+# ::
+#
+# INSTALL_QT5_EXECUTABLE(<executable> [<qtplugins> <libs> <dirs> <plugins_dir> <request_qt_conf> <component>])
+#
+# Installs Qt plugins, writes a Qt configuration file (if needed) and
+# fixes up a Qt5 executable using BundleUtilities so it is standalone
+# and can be drag-and-drop copied to another machine as long as all of
+# the system libraries are compatible. The executable will be fixed-up
+# at install time. <component> is the COMPONENT used for bundle fixup
+# and plugin installation. See documentation of FIXUP_QT5_BUNDLE.
+
+#=============================================================================
+# Copyright 2011 Mike McQuaid <mike at mikemcquaid.com>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+# The functions defined in this file depend on the fixup_bundle function
+# (and others) found in BundleUtilities.cmake
+
+include(BundleUtilities)
+set(DeployQt5_cmake_dir "${CMAKE_CURRENT_LIST_DIR}")
+set(DeployQt5_apple_plugins_dir "PlugIns")
+
+function(write_qt5_conf qt_conf_dir qt_conf_contents)
+ set(qt_conf_path "${qt_conf_dir}/qt.conf")
+ message(STATUS "Writing ${qt_conf_path}")
+ file(WRITE "${qt_conf_path}" "${qt_conf_contents}")
+endfunction()
+
+function(resolve_qt5_paths paths_var)
+ set(executable_path ${ARGV1})
+
+ set(paths_resolved)
+ foreach(path ${${paths_var}})
+ if(EXISTS "${path}")
+ list(APPEND paths_resolved "${path}")
+ else()
+ if(${executable_path})
+ list(APPEND paths_resolved "${executable_path}/${path}")
+ else()
+ list(APPEND paths_resolved "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${path}")
+ endif()
+ endif()
+ endforeach()
+ set(${paths_var} ${paths_resolved} PARENT_SCOPE)
+endfunction()
+
+function(fixup_qt5_executable executable)
+ set(qtplugins ${ARGV1})
+ set(libs ${ARGV2})
+ set(dirs ${ARGV3})
+ set(plugins_dir ${ARGV4})
+ set(request_qt_conf ${ARGV5})
+
+ message(STATUS "fixup_qt5_executable")
+ message(STATUS " executable='${executable}'")
+ message(STATUS " qtplugins='${qtplugins}'")
+ message(STATUS " libs='${libs}'")
+ message(STATUS " dirs='${dirs}'")
+ message(STATUS " plugins_dir='${plugins_dir}'")
+ message(STATUS " request_qt_conf='${request_qt_conf}'")
+
+ if(QT_LIBRARY_DIR)
+ list(APPEND dirs "${QT_LIBRARY_DIR}")
+ endif()
+ if(QT_BINARY_DIR)
+ list(APPEND dirs "${QT_BINARY_DIR}")
+ endif()
+
+ if(APPLE)
+ set(qt_conf_dir "${executable}/Contents/Resources")
+ set(executable_path "${executable}")
+ set(write_qt_conf TRUE)
+ if(NOT plugins_dir)
+ set(plugins_dir "${DeployQt5_apple_plugins_dir}")
+ endif()
+ else()
+ get_filename_component(executable_path "${executable}" PATH)
+ if(NOT executable_path)
+ set(executable_path ".")
+ endif()
+ set(qt_conf_dir "${executable_path}")
+ set(write_qt_conf ${request_qt_conf})
+ endif()
+
+ foreach(plugin ${qtplugins})
+ set(installed_plugin_path "")
+ install_qt5_plugin("${plugin}" "${executable}" 1 installed_plugin_path)
+ list(APPEND libs ${installed_plugin_path})
+ endforeach()
+
+ foreach(lib ${libs})
+ if(NOT EXISTS "${lib}")
+ message(FATAL_ERROR "Library does not exist: ${lib}")
+ endif()
+ endforeach()
+
+ resolve_qt5_paths(libs "${executable_path}")
+
+ if(write_qt_conf)
+ set(qt_conf_contents "[Paths]\nPlugins = ${plugins_dir}")
+ write_qt5_conf("${qt_conf_dir}" "${qt_conf_contents}")
+ endif()
+
+ fixup_bundle("${executable}" "${libs}" "${dirs}")
+endfunction()
+
+function(install_qt5_plugin_path plugin executable copy installed_plugin_path_var)
+ set(plugins_dir ${ARGV4})
+ set(component ${ARGV5})
+ set(configurations ${ARGV6})
+ if(EXISTS "${plugin}")
+ if(APPLE)
+ if(NOT plugins_dir)
+ set(plugins_dir "${DeployQt5_apple_plugins_dir}")
+ endif()
+ set(plugins_path "${executable}/Contents/${plugins_dir}")
+ else()
+ get_filename_component(plugins_path "${executable}" PATH)
+ if(NOT plugins_path)
+ set(plugins_path ".")
+ endif()
+ if(plugins_dir)
+ set(plugins_path "${plugins_path}/${plugins_dir}")
+ endif()
+ endif()
+
+ set(plugin_group "")
+
+ get_filename_component(plugin_path "${plugin}" PATH)
+ get_filename_component(plugin_parent_path "${plugin_path}" PATH)
+ get_filename_component(plugin_parent_dir_name "${plugin_parent_path}" NAME)
+ get_filename_component(plugin_name "${plugin}" NAME)
+ string(TOLOWER "${plugin_parent_dir_name}" plugin_parent_dir_name)
+
+ if("${plugin_parent_dir_name}" STREQUAL "plugins")
+ get_filename_component(plugin_group "${plugin_path}" NAME)
+ set(${plugin_group_var} "${plugin_group}")
+ endif()
+ set(plugins_path "${plugins_path}/${plugin_group}")
+
+ if(${copy})
+ file(MAKE_DIRECTORY "${plugins_path}")
+ file(COPY "${plugin}" DESTINATION "${plugins_path}")
+ else()
+ if(configurations AND (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE))
+ set(configurations CONFIGURATIONS ${configurations})
+ else()
+ unset(configurations)
+ endif()
+ install(FILES "${plugin}" DESTINATION "${plugins_path}" ${configurations} ${component})
+ endif()
+ set(${installed_plugin_path_var} "${plugins_path}/${plugin_name}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(install_qt5_plugin plugin executable copy installed_plugin_path_var)
+ set(plugins_dir ${ARGV4})
+ set(component ${ARGV5})
+ if(EXISTS "${plugin}")
+ install_qt5_plugin_path("${plugin}" "${executable}" "${copy}" "${installed_plugin_path_var}" "${plugins_dir}" "${component}")
+ else()
+ string(TOUPPER "QT_${plugin}_PLUGIN" plugin_var)
+ set(plugin_release_var "${plugin_var}_RELEASE")
+ set(plugin_debug_var "${plugin_var}_DEBUG")
+ set(plugin_release "${${plugin_release_var}}")
+ set(plugin_debug "${${plugin_debug_var}}")
+ if(DEFINED "${plugin_release_var}" AND DEFINED "${plugin_debug_var}" AND NOT EXISTS "${plugin_release}" AND NOT EXISTS "${plugin_debug}")
+ message(WARNING "Qt plugin \"${plugin}\" not recognized or found.")
+ endif()
+ if(NOT EXISTS "${${plugin_debug_var}}")
+ set(plugin_debug "${plugin_release}")
+ endif()
+
+ if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE)
+ install_qt5_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}_release" "${plugins_dir}" "${component}" "Release|RelWithDebInfo|MinSizeRel")
+ install_qt5_plugin_path("${plugin_debug}" "${executable}" "${copy}" "${installed_plugin_path_var}_debug" "${plugins_dir}" "${component}" "Debug")
+
+ if(CMAKE_BUILD_TYPE MATCHES "^Debug$")
+ set(${installed_plugin_path_var} ${${installed_plugin_path_var}_debug})
+ else()
+ set(${installed_plugin_path_var} ${${installed_plugin_path_var}_release})
+ endif()
+ else()
+ install_qt5_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}" "${plugins_dir}" "${component}")
+ endif()
+ endif()
+ set(${installed_plugin_path_var} ${${installed_plugin_path_var}} PARENT_SCOPE)
+endfunction()
+
+function(install_qt5_executable executable)
+ set(qtplugins ${ARGV1})
+ set(libs ${ARGV2})
+ set(dirs ${ARGV3})
+ set(plugins_dir ${ARGV4})
+ set(request_qt_conf ${ARGV5})
+ set(component ${ARGV6})
+ if(QT_LIBRARY_DIR)
+ list(APPEND dirs "${QT_LIBRARY_DIR}")
+ endif()
+ if(QT_BINARY_DIR)
+ list(APPEND dirs "${QT_BINARY_DIR}")
+ endif()
+ if(component)
+ set(component COMPONENT ${component})
+ else()
+ unset(component)
+ endif()
+
+ get_filename_component(executable_absolute "${executable}" ABSOLUTE)
+ if(EXISTS "${QT_QTCORE_LIBRARY_RELEASE}")
+ gp_file_type("${executable_absolute}" "${QT_QTCORE_LIBRARY_RELEASE}" qtcore_type)
+ elseif(EXISTS "${QT_QTCORE_LIBRARY_DEBUG}")
+ gp_file_type("${executable_absolute}" "${QT_QTCORE_LIBRARY_DEBUG}" qtcore_type)
+ endif()
+ if(qtcore_type STREQUAL "system")
+ set(qt_plugins_dir "")
+ endif()
+
+ if(QT_IS_STATIC)
+ message(WARNING "Qt built statically: not installing plugins.")
+ else()
+ if(APPLE)
+ get_property(loc TARGET Qt5::QCocoaIntegrationPlugin
+ PROPERTY LOCATION_RELEASE)
+ install_qt5_plugin("${loc}" "${executable}" 0 installed_plugin_paths "PlugIns" "${component}")
+ list(APPEND libs ${installed_plugin_paths})
+ endif()
+ foreach(plugin ${qtplugins})
+ set(installed_plugin_paths "")
+ install_qt5_plugin("${plugin}" "${executable}" 0 installed_plugin_paths "${plugins_dir}" "${component}")
+ list(APPEND libs ${installed_plugin_paths})
+ endforeach()
+ endif()
+ resolve_qt5_paths(libs "")
+
+ install(CODE
+ "include(\"${DeployQt5_cmake_dir}/DeployQt5.cmake\")
+ set(BU_CHMOD_BUNDLE_ITEMS TRUE)
+ FIXUP_QT5_EXECUTABLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${executable}\" \"\" \"${libs}\" \"${dirs}\" \"${plugins_dir}\" \"${request_qt_conf}\")"
+ ${component}
+ )
+endfunction()
diff --git a/Submodules/c3d/gui/CommandEditor.cxx b/Submodules/c3d/gui/CommandEditor.cxx
new file mode 100644
index 0000000..1518854
--- /dev/null
+++ b/Submodules/c3d/gui/CommandEditor.cxx
@@ -0,0 +1,503 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CommandEditor.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "CommandEditor.h"
+#include <QCompleter>
+#include <QAbstractItemView>
+#include <QKeyEvent>
+#include <QScrollBar>
+#include <QDir>
+#include <QUrl>
+#include <QSettings>
+#include <QToolTip>
+#include <QDebug>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QDateTime>
+#include <QFontMetrics>
+#include "Documentation.h"
+#include <sstream>
+
+CommandEditor::CommandEditor(QWidget *parent):
+ QTextEdit(parent)
+{
+ m_fileCompleter = NULL;
+ m_commandCompleter = NULL;
+}
+
+CommandEditor::~CommandEditor()
+{
+}
+
+void CommandEditor::setFileCompleter(QCompleter *c)
+{
+ if (m_fileCompleter)
+ QObject::disconnect(m_fileCompleter, 0, this, 0);
+
+ m_fileCompleter = c;
+
+ if (!m_fileCompleter)
+ return;
+
+ m_fileCompleter->setWidget(this);
+ m_fileCompleter->setCompletionMode(QCompleter::PopupCompletion);
+ QObject::connect(m_fileCompleter, SIGNAL(activated(QString)),
+ this, SLOT(insertFileCompletion(QString)));
+}
+
+QCompleter *CommandEditor::fileCompleter() const
+{
+ return m_fileCompleter;
+}
+
+void CommandEditor::setCommandCompleter(QCompleter *c)
+{
+ if (m_commandCompleter)
+ QObject::disconnect(m_commandCompleter, 0, this, 0);
+
+ m_commandCompleter = c;
+
+ if (!m_commandCompleter)
+ return;
+
+ m_commandCompleter->setWidget(this);
+ m_commandCompleter->setCompletionMode(QCompleter::PopupCompletion);
+ QObject::connect(m_commandCompleter, SIGNAL(activated(QString)),
+ this, SLOT(insertCommandCompletion(QString)));
+}
+
+QCompleter *CommandEditor::commandCompleter() const
+{
+ return m_commandCompleter;
+}
+
+void CommandEditor::setDocumentation(Documentation *doc)
+{
+ m_Documentation = doc;
+}
+
+
+QString splitTooltip(QString text, int width)
+{
+ QFontMetrics fm(QToolTip::font());
+ QString result;
+
+ for (;;) {
+ int i = 0;
+ while (i < text.length()) {
+ if (fm.width(text.left(++i + 1)) > width) {
+ int j = text.lastIndexOf(' ', i);
+ if (j > 0)
+ i = j;
+ result += text.left(i);
+ result += '\n';
+ text = text.mid(i+1);
+ break;
+ }
+ else if(text[i] == '\n')
+ {
+ result += text.left(i+1);
+ text = text.mid(i+1);
+ }
+ }
+ if (i >= text.length())
+ break;
+ }
+ return result + text;
+}
+
+#include <iostream>
+bool CommandEditor::event(QEvent *e)
+{
+ if (e->type() == QEvent::ToolTip)
+ {
+ QHelpEvent *helpEvent = static_cast<QHelpEvent *>(e);
+ QTextCursor tc = this->cursorForPosition(helpEvent->pos());
+ QString fn = this->filenameUnderCursor(tc);
+ QString help = QString("future help for %1").arg(fn);
+
+ // Get the c++ string
+ std::string fn_std = fn.toStdString();
+
+ // Special handling for real files
+ QFileInfo fi(QDir(QSettings().value("working_dir").toString()), fn);
+ if(fi.exists() && fi.isReadable())
+ {
+ help=QString(
+ "<html><b>File: </b>%2<br>"
+ "<b>Directory: </b>%1<br>"
+ "<b>Last Modified: </b>%3<br><br><br>"
+ "<i>Double click to open in a 3D viewer</i></html>")
+ .arg(fi.absolutePath())
+ .arg(fi.fileName())
+ .arg(fi.lastModified().toLocalTime().toString());
+ }
+
+ // Special handling for c3d commands
+ else if(m_Documentation->GetAllCommands().find(fn_std)
+ != m_Documentation->GetAllCommands().end())
+ {
+ std::ostringstream oss;
+ std::string nodash = fn_std.substr(1);
+ if(m_Documentation->PrintCommandHelp(oss, nodash.c_str()))
+ {
+ help = splitTooltip(QString::fromStdString(oss.str()), 600);
+ }
+ }
+
+ QToolTip::showText(helpEvent->globalPos(), help);
+ return true;
+ }
+
+ else
+ return QTextEdit::event(e);
+}
+
+bool CommandEditor::canInsertFromMimeData(const QMimeData *source) const
+{
+ if(source->hasUrls())
+ return true;
+ else return QTextEdit::canInsertFromMimeData(source);
+}
+
+void CommandEditor::insertFromMimeData(const QMimeData *source)
+{
+ if(source->hasUrls())
+ {
+ foreach(QUrl url, source->urls())
+ {
+ if(url.isLocalFile())
+ {
+ this->textCursor().insertText(url.toLocalFile());
+ this->textCursor().insertText(" ");
+ }
+ }
+ }
+ else
+ {
+ QTextEdit::insertFromMimeData(source);
+ }
+}
+
+#include <iostream>
+
+void CommandEditor::keyPressEvent(QKeyEvent *e)
+{
+ bool in_file_popup =
+ m_fileCompleter && m_fileCompleter->popup() && m_fileCompleter->popup()->isVisible();
+ bool in_cmd_popup =
+ m_commandCompleter && m_commandCompleter->popup() && m_commandCompleter->popup()->isVisible();
+ bool in_popup = in_file_popup | in_cmd_popup;
+
+ // If a popup is open, we don't want to do anything with these keys
+ if (in_popup)
+ {
+ // The following keys are forwarded by the completer to the widget
+ switch (e->key())
+ {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Escape:
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ e->ignore();
+ return; // let the completer do default behavior
+ default:
+ break;
+ }
+ }
+
+ // Command-enter - execute command
+ if((e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)
+ && (e->modifiers() == Qt::ControlModifier))
+ {
+ emit commandAccepted();
+ e->ignore();
+ return;
+ }
+
+ // Command-backspace - clear contents
+ if((e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete)
+ && (e->modifiers() == Qt::ControlModifier))
+ {
+ emit clearRequested();
+ e->ignore();
+ return;
+ }
+
+ // Indentation. If the user pressed enter, mimic the leading spaces
+ // from the previous line
+ if(e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)
+ {
+ // Go to the beginning of the line and count the blank characters
+ QTextCursor tc = textCursor();
+ tc.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
+
+ // Indent after 'c3d ' or after spaces
+ QRegExp indentme("(c[2-4]d|snap|itksnap|view)\\s*|\\s*", Qt::CaseInsensitive);
+ QString leading = this->document()->find(indentme, tc).selectedText();
+ QTextEdit::keyPressEvent(e);
+ textCursor().insertText(QString(leading.length(),' '));
+ return;
+ }
+
+ // Test for the shortcut
+ bool isShortcut = (!e->modifiers() && e->key() == Qt::Key_Tab);
+ if(!isShortcut && !in_popup)
+ {
+ QTextEdit::keyPressEvent(e);
+ return;
+ }
+
+ // We also want to break out if we are not in popup mode, and the character
+ // to the left of the cursor is a space.
+ QTextCursor tc = textCursor();
+ if(tc.position() == 0)
+ {
+ textCursor().insertText(" ");
+ return;
+ }
+
+ tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor);
+ tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
+ if(tc.selectedText().indexOf(QRegExp("\\s+")) >= 0)
+ {
+ textCursor().insertText(" ");
+ return;
+ }
+
+ // We are here because a shortcut was pressed or because we are in popup
+ // mode and a key was pressed. If the latter, we want to send the key to
+ // the text editor, and keep going
+ if(in_popup)
+ {
+ QTextEdit::keyPressEvent(e);
+ }
+
+ // Check the word under the cursor
+ QString userText = this->filenameUnderCursor();
+
+ // If the text is zero length, treat it as a usual tab
+ if(userText.length() == 0)
+ {
+ QTextEdit::keyPressEvent(e);
+ return;
+ }
+
+ // What completion to do?
+ if(userText.left(1) == "-")
+ {
+ m_commandCompleter->setCompletionPrefix(userText);
+ if(m_commandCompleter->completionCount() == 1)
+ {
+ if(in_cmd_popup)
+ m_commandCompleter->popup()->hide();
+ this->insertCommandCompletion(m_commandCompleter->currentCompletion());
+ }
+ else
+ {
+ m_commandCompleter->setCompletionMode(QCompleter::PopupCompletion);
+ m_commandCompleter->popup()->setCurrentIndex(
+ m_commandCompleter->completionModel()->index(0, 0));
+
+ if(!in_cmd_popup)
+ {
+ m_popupRect = cursorRect();
+ m_popupRect.setWidth(
+ m_commandCompleter->popup()->sizeHintForColumn(0)
+ + m_commandCompleter->popup()->verticalScrollBar()->sizeHint().width());
+ }
+
+ m_commandCompleter->complete(m_popupRect); // popup it up!
+ }
+ }
+
+ else
+ {
+ // Turn the completion prefix into a real filename
+ QString wdir = QSettings().value("working_dir", QDir::currentPath()).toString();
+ QString userPath = QDir::cleanPath(QDir(wdir).absoluteFilePath(userText));
+
+ // If the user specifies an actual directory that exists, we want to search
+ // the contents of that directory
+ if(QFileInfo(userPath).isDir() &&
+ (userText.right(1) == QDir::separator() || userText.right(1) == "/"))
+ {
+ userPath = userPath + "/";
+ }
+
+ // How many completions?
+ m_fileCompleter->setCompletionPrefix(userPath);
+ if(m_fileCompleter->completionCount() == 1)
+ {
+ m_completionFileRelative = userText;
+ if(in_file_popup)
+ m_fileCompleter->popup()->hide();
+ this->insertFileCompletion(m_fileCompleter->currentCompletion());
+ }
+ else
+ {
+ m_completionFileRelative = userText;
+ m_fileCompleter->setCompletionMode(QCompleter::PopupCompletion);
+ m_fileCompleter->popup()->setCurrentIndex(m_fileCompleter->completionModel()->index(0, 0));
+
+ if(!in_file_popup)
+ {
+ m_popupRect = cursorRect();
+ m_popupRect.setWidth(
+ m_fileCompleter->popup()->sizeHintForColumn(0)
+ + m_fileCompleter->popup()->verticalScrollBar()->sizeHint().width());
+ }
+
+ m_fileCompleter->complete(m_popupRect); // popup it up!
+ }
+ }
+
+
+ /*
+ // Popup the completer
+ if(userPath != m_fileCompleter->currentCompletion())
+ {
+ m_completionFileRelative = userText;
+ m_fileCompleter->setCompletionPrefix(userPath);
+ m_fileCompleter->complete();
+ }
+ else
+ {
+ m_fileCompleter->setCurrentRow(m_fileCompleter->currentRow() + 1);
+ m_fileCompleter->complete();
+ }
+ */
+
+ /*
+ static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
+ bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
+
+ if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3
+ || eow.contains(e->text().right(1)))) {
+ m_fileCompleter->popup()->hide();
+ return;
+ }
+
+ if (completionPrefix != m_fileCompleter->completionPrefix()) {
+ m_fileCompleter->setCompletionPrefix(completionPrefix);
+ m_fileCompleter->popup()->setCurrentIndex(m_fileCompleter->completionModel()->index(0, 0));
+ }
+
+ QRect cr = cursorRect();
+ cr.setWidth(m_fileCompleter->popup()->sizeHintForColumn(0)
+ + m_fileCompleter->popup()->verticalScrollBar()->sizeHint().width());
+ m_fileCompleter->complete(cr); // popup it up!
+ */
+}
+
+void CommandEditor::focusInEvent(QFocusEvent *e)
+{
+ if (m_fileCompleter)
+ m_fileCompleter->setWidget(this);
+ QTextEdit::focusInEvent(e);
+}
+
+void CommandEditor::mouseDoubleClickEvent(QMouseEvent *mev)
+{
+ QTextCursor cursor = this->cursorForPosition(mev->pos());
+ QString filename = this->filenameUnderCursor(cursor);
+ QFileInfo fi(QDir(QSettings().value("working_dir").toString()), filename);
+
+ // Does the file exist?
+ if(fi.exists() && fi.isFile() && fi.isReadable())
+ {
+ emit validFilenameClicked(fi.absoluteFilePath());
+ }
+ else
+ QTextEdit::mousePressEvent(mev);
+}
+
+void CommandEditor::insertFileCompletion(const QString &completion)
+{
+ if (m_fileCompleter->widget() != this)
+ return;
+
+
+ // Replace the selection with the new selection
+ QString prefix = m_fileCompleter->completionPrefix();
+ int extra = completion.length() - prefix.length();
+ QString newtext = m_completionFileRelative + completion.right(extra);
+ m_activeCompletion.removeSelectedText();
+ m_activeCompletion.insertText(newtext);
+}
+
+void CommandEditor::insertCommandCompletion(const QString &completion)
+{
+ if (m_commandCompleter->widget() != this)
+ return;
+
+
+ // Replace the selection with the new selection
+ QString prefix = m_commandCompleter->completionPrefix();
+ int extra = completion.length() - prefix.length();
+ QString newtext = prefix + completion.right(extra);
+ m_activeCompletion.removeSelectedText();
+ m_activeCompletion.insertText(newtext);
+}
+
+QString CommandEditor::filenameUnderCursor(QTextCursor tc)
+{
+ // Find the text around the cursor that constitutes a filename
+
+ // Get the cursor
+ if(tc.isNull())
+ tc = textCursor();
+
+ // Find the beginning and end of the current line
+ QTextCursor cline = tc;
+ cline.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
+ cline.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
+
+ // Search backwards to find the first whitespace character
+ QTextCursor prev =
+ this->document()->find(QRegExp("\\s"), tc, QTextDocument::FindBackward);
+
+ // Search forward for next whitespace character
+ QTextCursor next =
+ this->document()->find(QRegExp("\\s"), tc);
+
+ // Set the position
+ int a = (prev.isNull() || prev.anchor() < cline.anchor())
+ ? cline.anchor() : prev.anchor() + 1;
+
+ int p = (next.isNull() || next.position() > cline.position())
+ ? cline.position() : next.position() - 1;
+
+ tc.setPosition(a, QTextCursor::MoveAnchor);
+ tc.setPosition(p, QTextCursor::KeepAnchor);
+
+ // Save the active completion
+ m_activeCompletion = tc;
+
+ return tc.selectedText();
+}
+
+
diff --git a/Submodules/c3d/gui/CommandEditor.h b/Submodules/c3d/gui/CommandEditor.h
new file mode 100644
index 0000000..e896b73
--- /dev/null
+++ b/Submodules/c3d/gui/CommandEditor.h
@@ -0,0 +1,91 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: CommandEditor.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef COMMANDEDITOR_H
+#define COMMANDEDITOR_H
+
+#include <QTextEdit>
+#include <QTextCursor>
+
+class QCompleter;
+class Documentation;
+
+class CommandEditor : public QTextEdit
+{
+ Q_OBJECT
+public:
+ explicit CommandEditor(QWidget *parent = 0);
+
+ ~CommandEditor();
+
+ void setFileCompleter(QCompleter *c);
+ QCompleter *fileCompleter() const;
+
+ void setCommandCompleter(QCompleter *c);
+ QCompleter *commandCompleter() const;
+
+ void setDocumentation(Documentation *doc);
+
+ bool event(QEvent *e);
+
+ bool canInsertFromMimeData(const QMimeData *source) const;
+ void insertFromMimeData(const QMimeData *source);
+
+signals:
+
+ void commandAccepted();
+ void clearRequested();
+ void validFilenameClicked(QString);
+
+protected:
+ void keyPressEvent(QKeyEvent *e);
+ void focusInEvent(QFocusEvent *e);
+ void mouseDoubleClickEvent(QMouseEvent *mev);
+
+private slots:
+ void insertFileCompletion(const QString &completion);
+ void insertCommandCompletion(const QString &completion);
+
+private:
+ QString filenameUnderCursor(QTextCursor tc = QTextCursor());
+
+private:
+ QCompleter *m_fileCompleter, *m_commandCompleter;
+ QTextCursor m_activeCompletion;
+ QString m_completionFileRelative;
+ QRect m_popupRect;
+
+ QStringList m_CommandList;
+
+ /** Documentation system */
+ Documentation *m_Documentation;
+
+signals:
+
+public slots:
+
+};
+
+#endif // COMMANDEDITOR_H
diff --git a/Submodules/c3d/gui/ConvertSyntaxHighlighter.cxx b/Submodules/c3d/gui/ConvertSyntaxHighlighter.cxx
new file mode 100644
index 0000000..855f778
--- /dev/null
+++ b/Submodules/c3d/gui/ConvertSyntaxHighlighter.cxx
@@ -0,0 +1,91 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertSyntaxHighlighter.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "ConvertSyntaxHighlighter.h"
+#include <QFileInfo>
+#include <QSettings>
+#include <QDir>
+
+ConvertSyntaxHighlighter::ConvertSyntaxHighlighter(QTextDocument *parent) :
+ QSyntaxHighlighter(parent)
+{
+}
+
+void ConvertSyntaxHighlighter::setCommandList(const QStringList &cl)
+{
+ m_CommandList = cl;
+}
+
+void ConvertSyntaxHighlighter::highlightBlock(const QString &text)
+{
+ // Format for commands
+ QTextCharFormat fmtCommand;
+ fmtCommand.setFontWeight(QFont::Bold);
+ fmtCommand.setForeground(QColor("green"));
+
+ // Format for filenames
+ QTextCharFormat fmtFile;
+ fmtFile.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+ fmtFile.setForeground(QColor("blue"));
+
+ // Format for filenames
+ QTextCharFormat fmtExe;
+ fmtExe.setFontWeight(QFont::Bold);
+ fmtExe.setForeground(QColor("darkred"));
+
+ // Find things that start with a minus sign
+ // QRegExp reCommand = QRegExp("\\-\\b\\[a-z]+\\b");
+ QRegExp reCommand = QRegExp("(^|\\s)(-[a-z\\-]+)(\\s|$)");
+ int index = text.indexOf(reCommand);
+ while(index >= 0)
+ {
+ QString command = text.mid(reCommand.pos(2), reCommand.cap(2).length());
+ if(m_CommandList.indexOf(command) >= 0)
+ setFormat(reCommand.pos(2), reCommand.cap(2).length(), fmtCommand);
+ index = text.indexOf(reCommand, std::max(reCommand.pos(3), index+1));
+ }
+
+ // Find things that look like filenames
+ reCommand = QRegExp("(^|\\s)(\\S+)(\\s|$)");
+ index = text.indexOf(reCommand);
+ while(index >= 0)
+ {
+ QString dir = QSettings().value("working_dir", QDir::currentPath()).toString();
+ QString testfile = reCommand.cap(2);
+ QFileInfo qfi(dir, testfile);
+ if(qfi.isFile())
+ {
+ setFormat(reCommand.pos(2), testfile.length(), fmtFile);
+ }
+ index = text.indexOf(reCommand, std::max(reCommand.pos(3), index+1));
+ }
+
+ reCommand = QRegExp("(^|\\s)(c[2-4]d|snap|itksnap|view)(\\s|$)", Qt::CaseInsensitive);
+ index = text.indexOf(reCommand, 0);
+ if(index >= 0)
+ {
+ setFormat(reCommand.pos(2), reCommand.cap(2).length(), fmtExe);
+ }
+}
diff --git a/Submodules/c3d/gui/ConvertSyntaxHighlighter.h b/Submodules/c3d/gui/ConvertSyntaxHighlighter.h
new file mode 100644
index 0000000..f2563ef
--- /dev/null
+++ b/Submodules/c3d/gui/ConvertSyntaxHighlighter.h
@@ -0,0 +1,50 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertSyntaxHighlighter.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef CONVERTSYNTAXHIGHLIGHTER_H
+#define CONVERTSYNTAXHIGHLIGHTER_H
+
+#include <QSyntaxHighlighter>
+
+class ConvertSyntaxHighlighter : public QSyntaxHighlighter
+{
+ Q_OBJECT
+public:
+ explicit ConvertSyntaxHighlighter(QTextDocument *parent = 0);
+
+ void setCommandList(const QStringList &cl);
+
+ virtual void highlightBlock(const QString &text);
+
+signals:
+
+public slots:
+
+private:
+ QStringList m_CommandList;
+
+};
+
+#endif // CONVERTSYNTAXHIGHLIGHTER_H
diff --git a/Submodules/c3d/gui/HistoryDialog.cxx b/Submodules/c3d/gui/HistoryDialog.cxx
new file mode 100644
index 0000000..821842b
--- /dev/null
+++ b/Submodules/c3d/gui/HistoryDialog.cxx
@@ -0,0 +1,177 @@
+#include "HistoryDialog.h"
+#include "ui_HistoryDialog.h"
+#include <QSettings>
+#include <QDateTime>
+#include <QDebug>
+#include <QClipboard>
+
+HistoryDialog::HistoryDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::HistoryDialog)
+{
+ ui->setupUi(this);
+ m_HistoryModel = new QStandardItemModel(0, 1);
+ m_HistoryModel->setHeaderData(0, Qt::Horizontal, "Command");
+
+ m_ProxyModel = new QSortFilterProxyModel(this);
+ m_ProxyModel->setSourceModel(m_HistoryModel);
+ ui->tvHistory->setModel(m_ProxyModel);
+
+ m_ProxyModel->setFilterRole(Qt::UserRole+3);
+ m_ProxyModel->setSortRole(Qt::UserRole+2);
+ m_ProxyModel->setDynamicSortFilter(true);
+ m_ProxyModel->sort(0, Qt::DescendingOrder);
+
+ connect(ui->tvHistory, SIGNAL(doubleClicked(QModelIndex)),
+ this, SLOT(onHistoryIndexDoubleClick(QModelIndex)));
+
+ connect(ui->inSearch, SIGNAL(textChanged(QString)),
+ m_ProxyModel, SLOT(setFilterWildcard(QString)));
+
+ // Context menu
+ ui->tvHistory->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui->tvHistory, SIGNAL(customContextMenuRequested(QPoint)),
+ this, SLOT(onHistoryContextRequest(QPoint)));
+}
+
+HistoryDialog::~HistoryDialog()
+{
+ delete ui;
+}
+
+void HistoryDialog::addHistoryEntry(QImage &pixmap, const QString &wd, QString &command,
+ QVariant timestamp)
+{
+ if(timestamp.isNull())
+ timestamp = QDateTime::currentDateTimeUtc();
+
+ QStandardItem *item = new QStandardItem();
+ // item->setData(pixmap, Qt::DecorationRole);
+ item->setData(wd, Qt::UserRole+1);
+ item->setData(timestamp, Qt::UserRole+2);
+ item->setData(command, Qt::UserRole+3);
+ item->setData(command.simplified(), Qt::DisplayRole);
+
+ QDateTime ts = timestamp.toDateTime();
+
+ QString tooltip = QString("Date: %2\nWorking Dir: %1").arg(wd).arg(ts.toLocalTime().toString());
+ item->setData(tooltip, Qt::ToolTipRole);
+
+ m_HistoryModel->appendRow(item);
+}
+
+
+void HistoryDialog::loadHistory()
+{
+ m_HistoryModel->clear();
+
+ QSettings settings;
+ int size = settings.beginReadArray("history");
+ for(int i = 0; i < size; i++)
+ {
+ settings.setArrayIndex(i);
+ QString command = settings.value("command").toString();
+ QString wd = settings.value("workdir").toString();
+ QDateTime ts = settings.value("timestamp").toDateTime();
+ QImage pixmap = settings.value("pixmap").value<QImage>();
+
+ this->addHistoryEntry(pixmap, wd, command, ts);
+ }
+ settings.endArray();
+}
+
+QSize HistoryDialog::sizeHint() const
+{
+ QSize hint = QWidget::sizeHint();
+ hint.setWidth(400);
+ return hint;
+}
+
+void HistoryDialog::saveHistory()
+{
+ QSettings settings;
+ settings.beginWriteArray("history");
+ for(int i = 0; i < m_HistoryModel->rowCount(); i++)
+ {
+ settings.setArrayIndex(i);
+
+ QStandardItem *item = m_HistoryModel->item(i);
+ settings.setValue("command", item->data(Qt::UserRole+3));
+ settings.setValue("workdir", item->data(Qt::UserRole+1));
+ settings.setValue("timestamp", item->data(Qt::UserRole+2));
+ settings.setValue("pixmap", item->data(Qt::DecorationRole));
+ }
+ settings.endArray();
+ settings.sync();
+}
+
+
+void HistoryDialog::on_btnClose_clicked()
+{
+ this->accept();
+}
+
+void HistoryDialog::on_btnCopy_clicked()
+{
+ QModelIndexList slist = ui->tvHistory->selectionModel()->selectedRows();
+ if(slist.size() == 1)
+ {
+ int i = slist.first().row();
+ QStandardItem *item = m_HistoryModel->item(i);
+
+ QString command = item->data(Qt::UserRole+3).toString();
+ emit commandCopyRequested(command);
+ }
+}
+
+void HistoryDialog::onHistoryIndexDoubleClick(QModelIndex mi)
+{
+ int row = m_ProxyModel->mapToSource(mi).row();
+ QStandardItem *item = m_HistoryModel->item(row);
+ QString command = item->data(Qt::UserRole+3).toString();
+ emit commandCopyRequested(command);
+}
+
+#include <QMenu>
+
+void HistoryDialog::onHistoryContextRequest(const QPoint &point)
+{
+ QPoint globalPos = ui->tvHistory->viewport()->mapToGlobal(point);
+ QModelIndex idx = ui->tvHistory->indexAt(point);
+
+
+ if(!idx.isValid())
+ return;
+
+ QMenu myMenu;
+ QAction *a_use = myMenu.addAction("Place in Editor");
+ QAction *a_copy = myMenu.addAction("Copy to Clipboard");
+ QAction *a_chdir = myMenu.addAction("Change to Command's Directory");
+ myMenu.addSeparator();
+ QAction *a_remove = myMenu.addAction("Remove from History");
+
+ QAction* action = myMenu.exec(globalPos);
+ if(!action)
+ return;
+
+ int row = m_ProxyModel->mapToSource(idx).row();
+ QStandardItem *item = m_HistoryModel->item(row);
+
+ if(action == a_chdir)
+ {
+ emit changeDirectoryRequested(item->data(Qt::UserRole+1).toString());
+ }
+ else if(action == a_remove)
+ {
+ m_HistoryModel->removeRow(row);
+ }
+ else if(action == a_use)
+ {
+ emit commandCopyRequested(item->data(Qt::UserRole+3).toString());
+ }
+ else if(action == a_copy)
+ {
+ QApplication::clipboard()->setText(item->data(Qt::UserRole+3).toString());
+ }
+
+}
diff --git a/Submodules/c3d/gui/HistoryDialog.h b/Submodules/c3d/gui/HistoryDialog.h
new file mode 100644
index 0000000..1395e84
--- /dev/null
+++ b/Submodules/c3d/gui/HistoryDialog.h
@@ -0,0 +1,51 @@
+#ifndef HISTORYDIALOG_H
+#define HISTORYDIALOG_H
+
+#include <QDialog>
+#include <QStandardItemModel>
+#include <QSortFilterProxyModel>
+
+namespace Ui {
+class HistoryDialog;
+}
+
+class HistoryDialog : public QDialog
+{
+ Q_OBJECT
+
+signals:
+
+ void commandCopyRequested(QString);
+ void changeDirectoryRequested(QString);
+
+public:
+ explicit HistoryDialog(QWidget *parent = 0);
+ ~HistoryDialog();
+
+ void addHistoryEntry(QImage &pixmap, const QString &wd, QString &command,
+ QVariant timestamp = QVariant());
+
+ void saveHistory();
+ void loadHistory();
+
+ virtual QSize sizeHint() const;
+protected:
+
+ QStandardItemModel *m_HistoryModel;
+ QSortFilterProxyModel *m_ProxyModel;
+
+private slots:
+
+ void on_btnClose_clicked();
+
+ void on_btnCopy_clicked();
+
+ void onHistoryIndexDoubleClick(QModelIndex mi);
+
+ void onHistoryContextRequest(const QPoint &point);
+
+private:
+ Ui::HistoryDialog *ui;
+};
+
+#endif // HISTORYDIALOG_H
diff --git a/Submodules/c3d/gui/HistoryDialog.ui b/Submodules/c3d/gui/HistoryDialog.ui
new file mode 100644
index 0000000..653edd7
--- /dev/null
+++ b/Submodules/c3d/gui/HistoryDialog.ui
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HistoryDialog</class>
+ <widget class="QWidget" name="HistoryDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>467</width>
+ <height>398</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Command History (Convert3DGUI)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QListView" name="tvHistory">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="resizeMode">
+ <enum>QListView::Adjust</enum>
+ </property>
+ <property name="viewMode">
+ <enum>QListView::ListMode</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Search:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="inSearch"/>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignRight">
+ <widget class="QCheckBox" name="chkCurrent">
+ <property name="text">
+ <string>Only in current directory</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Submodules/c3d/gui/MainWindow.cxx b/Submodules/c3d/gui/MainWindow.cxx
new file mode 100644
index 0000000..4489123
--- /dev/null
+++ b/Submodules/c3d/gui/MainWindow.cxx
@@ -0,0 +1,499 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MainWindow.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "MainWindow.h"
+#include "ui_MainWindow.h"
+
+#include "ConvertSyntaxHighlighter.h"
+#include <QDir>
+#include <QFileSystemModel>
+#include <QStringListModel>
+#include <QCompleter>
+#include <QFontMetrics>
+#include <QSettings>
+#include <QFileDialog>
+#include <QProcess>
+#include <QHelpEvent>
+#include <QDockWidget>
+#include <QListView>
+#include <QDebug>
+#include <QMessageBox>
+#include <QShortcut>
+#include <HistoryDialog.h>
+#include <SettingsDialog.h>
+#include "ConvertImageND.h"
+
+#include "Documentation.h"
+
+// Markdown documentation string generated at compile-time
+// this looks a little odd, but works - the include file contains raw bytes
+unsigned char c3d_md[] = {
+ #include "markdown_docs.h"
+ 0x00
+};
+
+class C3DFileSystemModel : public QFileSystemModel
+{
+public:
+ C3DFileSystemModel(QObject *parent) : QFileSystemModel(parent) {}
+
+ virtual Qt::DropActions supportedDragActions() const
+ { return Qt::CopyAction; }
+};
+
+void MainWindow::setupSearchPaths()
+{
+ // Search for the c3d executables
+ QString appPath = QCoreApplication::applicationDirPath();
+
+#ifdef __APPLE__
+ QDir::addSearchPath("c3d", QDir(appPath + "/../bin").absolutePath());
+ QDir::addSearchPath("c3d", appPath);
+ QDir::addSearchPath("c3d", QDir::currentPath());
+#else
+ QDir::addSearchPath("c3d", appPath);
+#endif
+}
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+
+ // Set up the search paths
+ this->setupSearchPaths();
+
+ // Parse the markdown documentation
+ m_Documentation = new Documentation(c3d_md);
+
+ // Get the list of available commands
+ const std::set<std::string> &cmdset = m_Documentation->GetAllCommands();
+ QStringList cmdlist;
+ for(std::set<std::string>::const_iterator it = cmdset.begin(); it != cmdset.end(); ++it)
+ cmdlist.push_back(QString::fromStdString(*it));
+
+ // Pass the documentation to the editor
+ ui->teCommand->setDocumentation(m_Documentation);
+
+ // Set the font in the editors
+ QFont font;
+ font.setFamily("Courier");
+ font.setFixedPitch(true);
+ font.setPixelSize(12);
+
+ ui->teCommand->setFont(font);
+
+ QFontMetrics metrics(font);
+ ui->teCommand->setTabStopWidth(4 * metrics.width(' '));
+
+ // Viewer
+ connect(ui->teCommand, SIGNAL(validFilenameClicked(QString)), this, SLOT(onImageViewRequested(QString)));
+
+ // Enable drag and drop to command editor
+ ui->teCommand->viewport()->setAcceptDrops(true);
+
+ // Disable wrapping in the output
+ ui->teOutput->setFont(font);
+ ui->teOutput->setWordWrapMode(QTextOption::NoWrap);
+
+ // Setup the highlighter
+ ConvertSyntaxHighlighter *shl =
+ new ConvertSyntaxHighlighter(ui->teCommand->document());
+ shl->setCommandList(cmdlist);
+
+ // Highlighted needs to be aware of invalidations
+ connect(this, SIGNAL(highlightingInvalidated()),
+ shl, SLOT(rehighlight()));
+
+ // Setup the filename completer in the editor
+ QCompleter *fileCompleter = new QCompleter(this);
+ m_FileSystemModel = new C3DFileSystemModel(fileCompleter);
+ m_FileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDot);
+ fileCompleter->setModel(m_FileSystemModel);
+ ui->teCommand->setFileCompleter(fileCompleter);
+
+ // Set the command completer
+ QCompleter *commandCompleter = new QCompleter(this);
+ QStringListModel *cmdlistmodel = new QStringListModel(commandCompleter);
+ cmdlistmodel->setStringList(cmdlist);
+ commandCompleter->setModel(cmdlistmodel);
+ ui->teCommand->setCommandCompleter(commandCompleter);
+
+ // Add a dock for the file list
+ QDockWidget *dock = new QDockWidget(this);
+ dock->setAllowedAreas(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea);
+ addDockWidget(Qt::RightDockWidgetArea, dock);
+
+ // Create a list widget with a file model
+ m_CurrentDirView = new QListView(this);
+ m_CurrentDirView->setModel(m_FileSystemModel);
+ m_CurrentDirView->setDragEnabled(true);
+ dock->setWidget(m_CurrentDirView);
+ dock->setWindowTitle("Working Directory");
+
+ connect(m_CurrentDirView, SIGNAL(doubleClicked(QModelIndex)),
+ this, SLOT(onFileListDoubleClick(QModelIndex)));
+
+ // Set the directory info
+ QSettings settings;
+ QString dir = settings.value("working_dir", QDir::currentPath()).toString();
+ this->onWorkingDirectoryChanged(dir);
+
+ // Set up a shortcut for execute command (what a pain!)
+ connect(ui->teCommand, SIGNAL(commandAccepted()), ui->btnRun, SLOT(click()));
+ connect(ui->teCommand, SIGNAL(clearRequested()), ui->btnClear, SLOT(click()));
+
+ // Set up the history dialog
+ m_History = new HistoryDialog(this);
+ connect(m_History, SIGNAL(commandCopyRequested(QString)),
+ this, SLOT(onCommandReceive(QString)));
+ connect(m_History, SIGNAL(changeDirectoryRequested(QString)),
+ this, SLOT(onWorkingDirectoryChanged(QString)));
+
+ // Load the history from settings
+ m_History->loadHistory();
+
+ // Add another dock for the history
+ QDockWidget *dock2 = new QDockWidget(this);
+ dock2->setAllowedAreas(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea);
+ addDockWidget(Qt::RightDockWidgetArea, dock2);
+ dock2->setWidget(m_History);
+ dock2->setWindowTitle("Command History");
+
+ // Settings
+ m_Settings = new SettingsDialog(this);
+
+ // Build the commands list
+ const std::vector<Documentation::Category> &cat = m_Documentation->GetCategories();
+ for(int i = 0; i < cat.size(); i++)
+ {
+ // Create a submenu for these commands
+ QString title = QString("Category: ") + QString::fromStdString(cat[i].Title);
+ QMenu *mCat = new QMenu(title, this);
+
+ // Add all commands to this submenu
+ QList<QAction *> actions;
+
+ for(int j = 0; j < cat[i].Commands.size(); j++)
+ {
+ const Documentation::CommandDoc &cd = cat[i].Commands[j];
+ QString mainAlias = QString::fromStdString(cd.Aliases.front());
+
+ QAction *action = new QAction(this);
+ action->setData(mainAlias);
+ action->setText(QString::fromStdString(cd.Title));
+ action->setToolTip(QString::fromStdString(cd.ShortDesc));
+ connect(action, SIGNAL(triggered()), this, SLOT(onCommandActionTriggered()));
+ actions.push_back(action);
+ }
+
+ mCat->addActions(actions);
+ ui->menuCommands->addMenu(mCat);
+ }
+}
+
+#include <QFileSystemModel>
+
+MainWindow::~MainWindow()
+{
+ m_History->saveHistory();
+ delete ui;
+ delete m_Documentation;
+}
+
+void MainWindow::onCommandReceive(QString command)
+{
+ ui->teCommand->setPlainText(command);
+ ui->teOutput->clear();
+}
+
+
+void MainWindow::on_btnChangeDir_clicked()
+{
+ QString dir =
+ QFileDialog::getExistingDirectory(this, tr("Choose Working Directory"),
+ ui->inWorkDir->text(), 0);
+ if(QFileInfo(dir).isDir())
+ {
+ onWorkingDirectoryChanged(dir);
+ }
+}
+
+void MainWindow::onWorkingDirectoryChanged(const QString &dir)
+{
+ QSettings().setValue("working_dir", dir);
+
+ ui->inWorkDir->setText(dir);
+
+ m_FileSystemModel->setRootPath(dir);
+
+ QModelIndex index = m_FileSystemModel->index(dir);
+ ui->teCommand->fileCompleter()->setCurrentRow(index.row());
+
+ m_CurrentDirView->setRootIndex(index);
+
+ emit highlightingInvalidated();
+
+
+}
+
+void MainWindow::onImageViewRequested(QString filename)
+{
+ // Get the viewer path
+ QString viewerPath = QSettings().value("viewerPath").toString();
+ QFileInfo fiViewer(viewerPath);
+
+ if(!fiViewer.exists() || !fiViewer.isExecutable())
+ {
+ QMessageBox::warning(this, "Cannot Launch Viewer", "Can not launch viewer. Use the Preferences dialog to set viewer program");
+ return;
+ }
+
+ // Create the C3D process
+ QProcess *myProcess = new QProcess(this);
+ myProcess->setWorkingDirectory(ui->inWorkDir->text());
+ QStringList args;
+ args.push_back(filename);
+ myProcess->start(fiViewer.absoluteFilePath(), args);
+}
+
+void MainWindow::onCommandActionTriggered()
+{
+ QAction *action = (QAction *) this->sender();
+ QString command = action->data().toString();
+
+ QTextCursor tc = ui->teCommand->textCursor();
+ tc.insertText(command);
+ if(!ui->teCommand->document()->characterAt(tc.position()+1).isSpace())
+ tc.insertText(" ");
+ ui->teCommand->setFocus();
+}
+
+void MainWindow::on_btnRun_clicked()
+{
+ // Contents of the editor
+ QString contents = ui->teCommand->document()->toPlainText();
+
+ // Split contents into words
+ QStringList words = contents.split(QRegExp("(\\s|^|$)+"), QString::SkipEmptyParts);
+ if(words.size() == 0)
+ return;
+
+ // Devise the command to call
+ QString command = "c3d";
+ if(words[0].toLower() == "c3d" || words[0].toLower() == "c2d" || words[0].toLower() == "c4d")
+ {
+ command = words[0];
+ words.removeFirst();
+ }
+ else if(words[0].toLower() == "snap" || words[0].toLower() == "itksnap"|| words[0].toLower() == "view")
+ {
+ command = "snap";
+ words.removeFirst();
+ }
+
+ // Find the command
+ QFileInfo fiCommand;
+ if(command == "snap")
+ {
+ fiCommand.setFile(QSettings().value("viewerPath").toString());
+ }
+ else
+ {
+#ifdef _WIN32
+ QFile fCommand(QString("c3d:%1.exe").arg(command));
+#else
+ QFile fCommand(QString("c3d:%1").arg(command));
+#endif
+ fiCommand.setFile(fCommand);
+ }
+
+ // Verify that C3D command exists
+ if(!fiCommand.exists())
+ {
+ QMessageBox::warning(this, "Convert3DGUI",
+ QString("Unable to find executable %1").arg(fiCommand.fileName()));
+ return;
+ }
+
+ // Create the C3D process
+ QProcess *myProcess = new QProcess(this);
+ myProcess->setWorkingDirectory(ui->inWorkDir->text());
+ // myProcess->setProcessChannelMode(QProcess::MergedChannels);
+
+ // Take the output from the process
+ connect(myProcess, SIGNAL(error(QProcess::ProcessError)), this , SLOT(onProcessFailed(QProcess::ProcessError)));
+ connect(myProcess, SIGNAL(finished(int)), this, SLOT(onProcessFinished(int)));
+ connect(myProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(onProcessStandardOutput()));
+ connect(myProcess, SIGNAL(readyReadStandardError()), this, SLOT(onProcessStandardError()));
+
+ // Clear the output
+ ui->teOutput->clear();
+
+ // Disable the execute button
+ ui->btnRun->setEnabled(false);
+
+ // Run the process
+ myProcess->start(fiCommand.absoluteFilePath(), words);
+}
+
+#include <QImage>
+#include <QPainter>
+#include <QRgb>
+
+void MainWindow::onProcessFinished(int rc)
+{
+ // Save the command to the command history
+ QString wd = QSettings().value("working_dir").toString();
+ QString command = ui->teCommand->document()->toPlainText();
+
+ // Focus off!
+
+ // Grab a picture of the screen
+ QSize docsize(ui->teCommand->document()->idealWidth() + 2, ui->teCommand->document()->size().height() + 2);
+ QImage image(docsize, QImage::Format_ARGB32);
+
+ if(rc == 0)
+ image.fill(Qt::white);
+ else
+ image.fill(QColor(0xff,0xee,0xee,0xff));
+
+ QPaintDevice *old = ui->teCommand->document()->documentLayout()->paintDevice();
+ ui->teCommand->document()->documentLayout()->setPaintDevice(&image);
+
+ QPainter p(&image);
+ ui->teCommand->document()->drawContents(&p);
+ p.end();
+
+ ui->teCommand->document()->documentLayout()->setPaintDevice(old);
+
+ // QPixmap screen = ui->teCommand->grab(QRect(QPoint(1,1), docsize));
+ /*
+ QRgb whitey = QColor(Qt::white).rgba();
+ for(int i = 0; i < image.width(); i++)
+ for(int j = 0; j < image.height(); j++)
+ if(image.pixel(i,j) == whitey)
+ image.setPixel(i, j, qRgba(0,0,0,0));
+ */
+
+ m_History->addHistoryEntry(image, wd, command);
+
+ // Disable the execute button
+ ui->btnRun->setEnabled(true);
+
+ // Update highlighting
+ emit highlightingInvalidated();
+
+ // A message for the user
+ QTextCursor tc = ui->teOutput->textCursor();
+ QTextCharFormat fmt;
+ QProcess *process = qobject_cast<QProcess *>(this->sender());
+
+ if(rc != 0)
+ {
+ fmt.setForeground(QColor("darkred"));
+ tc.setCharFormat(fmt);
+ tc.insertText(QString("Command %1 returned with error code %2").arg(process->program()).arg(rc));
+ }
+
+
+
+}
+
+void MainWindow::onProcessFailed(QProcess::ProcessError errorCode)
+{
+ QMessageBox::warning(this, "Convert3DGUI",
+ QString("Convert3D process failed with error %1").arg(errorCode));
+
+ ui->btnRun->setEnabled(true);
+}
+
+void MainWindow::onProcessStandardOutput()
+{
+ QProcess *proc = qobject_cast<QProcess *>(sender());
+ if(proc)
+ {
+ QString text(proc->readAllStandardOutput());
+ QTextCursor tc = ui->teOutput->textCursor();
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("black"));
+ tc.setCharFormat(fmt);
+ tc.insertText(text);
+ }
+}
+
+void MainWindow::onProcessStandardError()
+{
+ QProcess *proc = qobject_cast<QProcess *>(sender());
+ if(proc)
+ {
+ QString text(proc->readAllStandardError());
+ QTextCursor tc = ui->teOutput->textCursor();
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("red"));
+ tc.setCharFormat(fmt);
+ tc.insertText(text);
+ }
+}
+
+void MainWindow::onFileListDoubleClick(const QModelIndex &index)
+{
+ if(m_FileSystemModel->isDir(index))
+ {
+ this->onWorkingDirectoryChanged(m_FileSystemModel->fileInfo(index).absoluteFilePath());
+ return;
+ }
+ else
+ {
+ QString filename = m_FileSystemModel->data(index, Qt::DisplayRole).toString();
+ QTextCursor tc = ui->teCommand->textCursor();
+ tc.insertText(filename);
+ if(!ui->teCommand->document()->characterAt(tc.position()+1).isSpace())
+ tc.insertText(" ");
+ m_CurrentDirView->clearFocus();
+ ui->teCommand->setFocus();
+ }
+}
+
+void MainWindow::on_btnClear_clicked()
+{
+ // Clear the document
+ ui->teCommand->document()->clear();
+ ui->teOutput->document()->clear();
+}
+
+void MainWindow::on_actionPreferences_triggered()
+{
+ m_Settings->show();
+ m_Settings->raise();
+}
+
+#include <QDesktopServices>
+
+void MainWindow::on_actionC3D_Manual_triggered()
+{
+ QDesktopServices::openUrl(QUrl("http://www.itksnap.org/pmwiki/pmwiki.php?n=Convert3D.Documentation"));
+}
diff --git a/Submodules/c3d/gui/MainWindow.h b/Submodules/c3d/gui/MainWindow.h
new file mode 100644
index 0000000..2196f58
--- /dev/null
+++ b/Submodules/c3d/gui/MainWindow.h
@@ -0,0 +1,92 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: MainWindow.h
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QProcess>
+#include <QFileSystemModel>
+
+namespace Ui {
+class MainWindow;
+}
+
+class QListView;
+class HistoryDialog;
+class SettingsDialog;
+class Documentation;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+signals:
+
+ void highlightingInvalidated();
+
+public slots:
+
+ void onCommandReceive(QString command);
+ void onImageViewRequested(QString filename);
+ void onCommandActionTriggered();
+
+private slots:
+ void on_btnChangeDir_clicked();
+ void onWorkingDirectoryChanged(const QString &dir);
+
+ void on_btnRun_clicked();
+
+ void onProcessFinished(int rc);
+ void onProcessStandardOutput();
+ void onProcessStandardError();
+ void onProcessFailed(QProcess::ProcessError errorCode);
+
+ void onFileListDoubleClick(const QModelIndex &index);
+
+ void on_btnClear_clicked();
+
+ void on_actionPreferences_triggered();
+
+ void on_actionC3D_Manual_triggered();
+
+protected:
+
+private:
+ Ui::MainWindow *ui;
+ QFileSystemModel *m_FileSystemModel;
+ QListView *m_CurrentDirView;
+ void setupSearchPaths();
+
+ HistoryDialog *m_History;
+ SettingsDialog *m_Settings;
+ Documentation *m_Documentation;
+};
+
+#endif // MAINWINDOW_H
diff --git a/Submodules/c3d/gui/MainWindow.ui b/Submodules/c3d/gui/MainWindow.ui
new file mode 100644
index 0000000..cf3ea64
--- /dev/null
+++ b/Submodules/c3d/gui/MainWindow.ui
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>960</width>
+ <height>548</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Convert3D User Interface (itksnap.org)</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1,0,1,0">
+ <property name="rightMargin">
+ <number>12</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Working Directory:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="inWorkDir">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="btnChangeDir">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="window-new">
+ <normaloff/>
+ </iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>C3D Command</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="CommandEditor" name="teCommand">
+ <property name="font">
+ <font>
+ <family>Courier</family>
+ <pointsize>12</pointsize>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Output:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPlainTextEdit" name="teOutput">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnClear">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Backspace</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnRun">
+ <property name="text">
+ <string>Execute</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Return</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>960</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ <addaction name="actionPreferences"/>
+ </widget>
+ <widget class="QMenu" name="menuHelp">
+ <property name="title">
+ <string>Help</string>
+ </property>
+ <addaction name="actionC3D_Manual"/>
+ </widget>
+ <widget class="QMenu" name="menuCommands">
+ <property name="title">
+ <string>Commands</string>
+ </property>
+ </widget>
+ <addaction name="menuFile"/>
+ <addaction name="menuCommands"/>
+ <addaction name="menuHelp"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <action name="actionPreferences">
+ <property name="text">
+ <string>Preferences ...</string>
+ </property>
+ </action>
+ <action name="actionC3D_Manual">
+ <property name="text">
+ <string>C3D Manual ...</string>
+ </property>
+ <property name="shortcut">
+ <string>F1</string>
+ </property>
+ </action>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>CommandEditor</class>
+ <extends>QTextEdit</extends>
+ <header location="global">CommandEditor.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>teCommand</tabstop>
+ <tabstop>btnRun</tabstop>
+ <tabstop>inWorkDir</tabstop>
+ <tabstop>btnChangeDir</tabstop>
+ <tabstop>teOutput</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Submodules/c3d/gui/SettingsDialog.cxx b/Submodules/c3d/gui/SettingsDialog.cxx
new file mode 100644
index 0000000..496e998
--- /dev/null
+++ b/Submodules/c3d/gui/SettingsDialog.cxx
@@ -0,0 +1,88 @@
+#include "SettingsDialog.h"
+#include "ui_SettingsDialog.h"
+#include <QFileDialog>
+#include <QStandardPaths>
+#include <QSettings>
+
+SettingsDialog::SettingsDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::SettingsDialog)
+{
+ ui->setupUi(this);
+ ui->labelInvalidViewerPath->setVisible(false);
+}
+
+SettingsDialog::~SettingsDialog()
+{
+ delete ui;
+}
+
+void SettingsDialog::show()
+{
+ QDialog::show();
+ ui->inViewerPath->setText(QSettings().value("viewerPath").toString());
+}
+
+void SettingsDialog::on_btnBrowse_clicked()
+{
+
+ QString start =
+ QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).first();
+
+ QString dir =
+ QFileDialog::getOpenFileName(this, "Locate Image Viewer", start);
+
+#ifdef __APPLE__
+
+ // If the user selects an app bundle, we will look for an executable in the bundle
+ if(dir.endsWith(".app", Qt::CaseInsensitive))
+ {
+ dir = QStandardPaths::findExecutable("ITK-SNAP", QStringList(dir + "/Contents/MacOS"));
+ }
+
+#endif
+
+ ui->inViewerPath->setText(dir);
+}
+
+void SettingsDialog::on_inViewerPath_textChanged(const QString &text)
+{
+ // Some automatic replacements
+ QString dir = text;
+
+#ifdef __APPLE__
+
+ // If the user selects an app bundle, we will look for an executable in the bundle
+ if(dir.endsWith(".app", Qt::CaseInsensitive))
+ {
+ dir = QStandardPaths::findExecutable("ITK-SNAP", QStringList(dir + "/Contents/MacOS"));
+ }
+
+#endif
+
+ // Check the path
+ QFileInfo fi(dir);
+ if((fi.exists() && fi.isExecutable()) || dir.length() == 0)
+ ui->labelInvalidViewerPath->setVisible(false);
+ else
+ ui->labelInvalidViewerPath->setVisible(true);
+}
+
+void SettingsDialog::on_SettingsDialog_accepted()
+{
+ // When the user presses OK, we commit the path if it is valid, clear it otherwise
+ if(ui->inViewerPath->text().length() && !ui->labelInvalidViewerPath->isVisible())
+ {
+ QSettings().setValue("viewerPath", ui->inViewerPath->text());
+ }
+ else
+ {
+ QSettings().setValue("viewerPath", QVariant());
+ }
+}
+
+
+
+void SettingsDialog::on_SettingsDialog_rejected()
+{
+}
diff --git a/Submodules/c3d/gui/SettingsDialog.h b/Submodules/c3d/gui/SettingsDialog.h
new file mode 100644
index 0000000..8bc956d
--- /dev/null
+++ b/Submodules/c3d/gui/SettingsDialog.h
@@ -0,0 +1,35 @@
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+ class SettingsDialog;
+}
+
+class SettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsDialog(QWidget *parent = 0);
+ ~SettingsDialog();
+
+public slots:
+
+ virtual void show();
+
+private slots:
+ void on_btnBrowse_clicked();
+
+ void on_inViewerPath_textChanged(const QString &arg1);
+
+ void on_SettingsDialog_accepted();
+
+ void on_SettingsDialog_rejected();
+
+private:
+ Ui::SettingsDialog *ui;
+};
+
+#endif // SETTINGSDIALOG_H
diff --git a/Submodules/c3d/gui/SettingsDialog.ui b/Submodules/c3d/gui/SettingsDialog.ui
new file mode 100644
index 0000000..0948aee
--- /dev/null
+++ b/Submodules/c3d/gui/SettingsDialog.ui
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>597</width>
+ <height>374</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="horizontalSpacing">
+ <number>-1</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="4" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QToolButton" name="btnBrowse">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="window-new">
+ <normaloff/>
+ </iconset>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLineEdit" name="inViewerPath"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Path to 3D Image Viewer:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="0" alignment="Qt::AlignRight">
+ <widget class="QLabel" name="labelInvalidViewerPath">
+ <property name="styleSheet">
+ <string notr="true">color:darkred;</string>
+ </property>
+ <property name="text">
+ <string>The path is not valid</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/Submodules/c3d/gui/Utilities/Forwarding/CMakeLists.txt b/Submodules/c3d/gui/Utilities/Forwarding/CMakeLists.txt
new file mode 100644
index 0000000..8f8094e
--- /dev/null
+++ b/Submodules/c3d/gui/Utilities/Forwarding/CMakeLists.txt
@@ -0,0 +1,19 @@
+PROJECT(C3DGUI_LAUNCHER)
+
+# -- EXPERIMENTAL --
+IF(NOT WIN32 AND NOT APPLE)
+ SET(C3DGUI_EXE_INSTALL ${C3DGUI_MAIN_INSTALL_DIR})
+ SET(C3DGUI_FORWARD_DIR_BUILD "${CMAKE_BINARY_DIR}/gui/bin")
+ SET(C3DGUI_FORWARD_DIR_INSTALL "../${C3DGUI_EXE_INSTALL}")
+ SET(C3DGUI_FORWARD_PATH_BUILD "\"${C3DGUI_FORWARD_DIR_BUILD}\"")
+ SET(C3DGUI_FORWARD_PATH_INSTALL "\"${C3DGUI_FORWARD_DIR_INSTALL}\"")
+ SET(C3DGUI_FORWARD_EXE ${C3DGUI_BUNDLE_NAME})
+ CONFIGURE_FILE(
+ ${CONVERT3D_SOURCE_DIR}/gui/Utilities/Forwarding/SharedForwardExe.c.in
+ ${CMAKE_CURRENT_BINARY_DIR}/shared-forward.c
+ @ONLY IMMEDIATE)
+ ADD_EXECUTABLE(c3d_gui ${CMAKE_CURRENT_BINARY_DIR}/shared-forward.c)
+ ADD_DEPENDENCIES(c3d_gui ${C3DGUI_EXE})
+ INSTALL(TARGETS c3d_gui RUNTIME DESTINATION bin)
+ENDIF(NOT WIN32 AND NOT APPLE)
+
diff --git a/Submodules/c3d/gui/Utilities/Forwarding/SharedForwardExe.c.in b/Submodules/c3d/gui/Utilities/Forwarding/SharedForwardExe.c.in
new file mode 100644
index 0000000..c185925
--- /dev/null
+++ b/Submodules/c3d/gui/Utilities/Forwarding/SharedForwardExe.c.in
@@ -0,0 +1,27 @@
+
+#if defined(CMAKE_INTDIR)
+ #define CONFIG_DIR_PRE CMAKE_INTDIR "/"
+ #define CONFIG_DIR_POST "/" CMAKE_INTDIR
+#else
+ #define CONFIG_DIR_PRE ""
+ #define CONFIG_DIR_POST ""
+#endif
+#define itksys_SHARED_FORWARD_DIR_BUILD "@C3DGUI_FORWARD_DIR_BUILD@"
+#define itksys_SHARED_FORWARD_PATH_BUILD @C3DGUI_FORWARD_PATH_BUILD@
+#define itksys_SHARED_FORWARD_PATH_INSTALL @C3DGUI_FORWARD_PATH_INSTALL@
+#define itksys_SHARED_FORWARD_EXE_BUILD "@C3DGUI_FORWARD_DIR_BUILD@/@C3DGUI_FORWARD_EXE@"
+#define itksys_SHARED_FORWARD_EXE_INSTALL "@C3DGUI_FORWARD_DIR_INSTALL@/@C3DGUI_FORWARD_EXE@"
+#define itksys_SHARED_FORWARD_OPTION_COMMAND "--command"
+#define itksys_SHARED_FORWARD_OPTION_PRINT "--print"
+#define itksys_SHARED_FORWARD_OPTION_LDD "--ldd"
+#if defined(CMAKE_INTDIR)
+ #define itksys_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR
+#endif
+
+#include <itksys/SharedForward.h>
+
+int main(int argc, char** argv)
+{
+ return itksys_shared_forward_to_real(argc, argv);
+}
+
diff --git a/Submodules/c3d/gui/main.cxx b/Submodules/c3d/gui/main.cxx
new file mode 100644
index 0000000..c1ae72f
--- /dev/null
+++ b/Submodules/c3d/gui/main.cxx
@@ -0,0 +1,109 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: main.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include <QApplication>
+#include <QSettings>
+#include <QStandardPaths>
+#include "MainWindow.h"
+
+void findViewer()
+{
+ // Name list to look through
+ QStringList appNameList;
+
+ // Start with the one saved in the system
+ appNameList.append(QSettings().value("viewerPath").toString());
+
+#ifdef __APPLE__
+
+ // This code tries to find ITK-SNAP
+ QStringList appDirList = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
+ foreach (QString path, appDirList)
+ {
+ appNameList.append(QString("%1/ITK-SNAP.app/Contents/MacOS/ITK-SNAP").arg(path));
+ appNameList.append(QString("%1/ITK-SNAP.app/Contents/MacOS/InsightSNAP").arg(path));
+ }
+
+ appNameList.append("/usr/local/bin/itksnap");
+ appNameList.append("/usr/local/bin/snap");
+ appNameList.append("/usr/bin/itksnap");
+ appNameList.append("/usr/bin/snap");
+
+#elif _WIN32
+
+ // Look in Program Files
+ QStringList appDirList;
+ appDirList.append(qgetenv("PROGRAMFILES").constData());
+ appDirList.append(qgetenv("PROGRAMFILES(x86)").constData());
+
+ foreach (QString path, appDirList)
+ {
+ QDir dir(path);
+ if(dir.exists())
+ {
+ dir.setNameFilters(QStringList("ITK-SNAP*"));
+ dir.setSorting(QDir::Time | QDir::Reversed);
+ QStringList matches = dir.entryList();
+ foreach(QString prog, matches)
+ {
+ appNameList.append(QString("%1\\%2\\bin\\ITK-SNAP.exe").arg(path).arg(prog));
+ }
+ }
+ }
+
+#else
+
+ // This
+
+#endif
+
+ // Search through all options
+ foreach (QString prog, appNameList)
+ {
+ QFileInfo fi(prog);
+ if(fi.exists() && fi.isExecutable())
+ {
+ QSettings().setValue("viewerPath", prog);
+ break;
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setOrganizationName("org.itksnap");
+ QCoreApplication::setOrganizationDomain("itksnap.org");
+ QCoreApplication::setApplicationName("Convert3DGUI");
+
+ // Find ITK-SNAP
+ findViewer();
+
+ QApplication app(argc, argv);
+ MainWindow *mwin = new MainWindow();
+ mwin->show();
+ app.exec();
+
+ delete mwin;
+}
diff --git a/Submodules/c3d/gui/resources/macos/Info.plist b/Submodules/c3d/gui/resources/macos/Info.plist
new file mode 100644
index 0000000..a9c9e66
--- /dev/null
+++ b/Submodules/c3d/gui/resources/macos/Info.plist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>Convert3DGUI</string>
+ <key>CFBundleIconFile</key>
+ <string>c3dgui</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.itksnap.c3dgui</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleName</key>
+ <string>Convert3DGUI</string>
+ <key>CFBundleDisplayName</key>
+ <string>C3D GUI Console</string>
+ <key>CFBundleVersion</key>
+ <string>59</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@C3D_VERSION_FULL@</string>
+ <key>CFBundleSignature</key>
+ <string>itksnap</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>NSHighResolutionCapable</key>
+ <string>True</string>
+</dict>
+</plist>
+
diff --git a/Submodules/c3d/gui/resources/macos/c3dgui.icns b/Submodules/c3d/gui/resources/macos/c3dgui.icns
new file mode 100644
index 0000000..567d930
Binary files /dev/null and b/Submodules/c3d/gui/resources/macos/c3dgui.icns differ
diff --git a/Submodules/c3d/gui/resources/macos/icon.graffle b/Submodules/c3d/gui/resources/macos/icon.graffle
new file mode 100644
index 0000000..ad038af
--- /dev/null
+++ b/Submodules/c3d/gui/resources/macos/icon.graffle
@@ -0,0 +1,331 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>ApplicationVersion</key>
+ <array>
+ <string>com.omnigroup.OmniGrafflePro</string>
+ <string>138.33.0.157554</string>
+ </array>
+ <key>AutoAdjust</key>
+ <false/>
+ <key>BackgroundGraphic</key>
+ <dict>
+ <key>Bounds</key>
+ <string>{{0, 0}, {36, 36}}</string>
+ <key>Class</key>
+ <string>SolidGraphic</string>
+ <key>ID</key>
+ <integer>2</integer>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>CanvasSize</key>
+ <string>{36, 36}</string>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2013-08-27 08:18:48 +0000</string>
+ <key>Creator</key>
+ <string>Paul Yushkevich</string>
+ <key>DisplayScale</key>
+ <string>1 pt = 1 pt</string>
+ <key>GraphDocumentVersion</key>
+ <integer>8</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{11, 10}, {26, 15.5}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.883889</string>
+ <key>g</key>
+ <string>0.232426</string>
+ <key>r</key>
+ <string>0.206744</string>
+ </dict>
+ <key>Font</key>
+ <string>HelveticaNeue-Medium</string>
+ <key>Size</key>
+ <real>14</real>
+ </dict>
+ <key>ID</key>
+ <integer>13</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf510
+{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Medium;}
+{\colortbl;\red255\green255\blue255;\red53\green59\blue225;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs32 \cf2 \shad\shadx77\shady-71\shadr200\shado205 \shadc0 3D}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{0, 4}, {32, 29}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>GillSans</string>
+ <key>Size</key>
+ <real>42</real>
+ </dict>
+ <key>ID</key>
+ <integer>14</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf510
+{\fonttbl\f0\fnil\fcharset0 GillSans;}
+{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs84 \cf2 \shad\shadx170\shady-155\shadr199\shado101 \shadc2 C}</string>
+ </dict>
+ </dict>
+ </array>
+ <key>GridInfo</key>
+ <dict>
+ <key>GridSpacing</key>
+ <real>1</real>
+ <key>ShowsGrid</key>
+ <string>YES</string>
+ <key>SnapsToGrid</key>
+ <string>YES</string>
+ </dict>
+ <key>GuidesLocked</key>
+ <string>NO</string>
+ <key>GuidesVisible</key>
+ <string>YES</string>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>ImageCounter</key>
+ <integer>4</integer>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Layer 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict>
+ <key>Animate</key>
+ <string>NO</string>
+ <key>circoMinDist</key>
+ <real>18</real>
+ <key>circoSeparation</key>
+ <real>0.0</real>
+ <key>layoutEngine</key>
+ <string>dot</string>
+ <key>neatoSeparation</key>
+ <real>0.0</real>
+ <key>twopiSeparation</key>
+ <real>0.0</real>
+ </dict>
+ <key>LinksVisible</key>
+ <string>NO</string>
+ <key>MagnetsVisible</key>
+ <string>NO</string>
+ <key>MasterSheets</key>
+ <array/>
+ <key>ModificationDate</key>
+ <string>2013-08-27 08:44:35 +0000</string>
+ <key>Modifier</key>
+ <string>Paul Yushkevich</string>
+ <key>NotesVisible</key>
+ <string>NO</string>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OriginVisible</key>
+ <string>NO</string>
+ <key>PageBreaks</key>
+ <string>YES</string>
+ <key>PrintInfo</key>
+ <dict>
+ <key>NSBottomMargin</key>
+ <array>
+ <string>float</string>
+ <string>41</string>
+ </array>
+ <key>NSHorizonalPagination</key>
+ <array>
+ <string>int</string>
+ <string>0</string>
+ </array>
+ <key>NSLeftMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSPaperSize</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAx7X05TU2l6ZT1mZn2WgWQCgRgDhg==</string>
+ </array>
+ <key>NSPrintReverseOrientation</key>
+ <array>
+ <string>int</string>
+ <string>0</string>
+ </array>
+ <key>NSRightMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSTopMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ </dict>
+ <key>PrintOnePage</key>
+ <false/>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Canvas 1</string>
+ <key>SmartAlignmentGuidesActive</key>
+ <string>NO</string>
+ <key>SmartDistanceGuidesActive</key>
+ <string>NO</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>UseEntirePage</key>
+ <false/>
+ <key>VPages</key>
+ <integer>1</integer>
+ <key>WindowInfo</key>
+ <dict>
+ <key>CurrentSheet</key>
+ <integer>0</integer>
+ <key>ExpandedCanvases</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>Canvas 1</string>
+ </dict>
+ </array>
+ <key>Frame</key>
+ <string>{{501, 99}, {1066, 929}}</string>
+ <key>ListView</key>
+ <true/>
+ <key>OutlineWidth</key>
+ <integer>142</integer>
+ <key>RightSidebar</key>
+ <false/>
+ <key>ShowRuler</key>
+ <true/>
+ <key>Sidebar</key>
+ <true/>
+ <key>SidebarWidth</key>
+ <integer>116</integer>
+ <key>VisibleRegion</key>
+ <string>{{-40, -30}, {116.875, 96.875}}</string>
+ <key>Zoom</key>
+ <real>8</real>
+ <key>ZoomValues</key>
+ <array>
+ <array>
+ <string>Canvas 1</string>
+ <real>8</real>
+ <real>1</real>
+ </array>
+ </array>
+ </dict>
+ <key>saveQuickLookFiles</key>
+ <string>YES</string>
+</dict>
+</plist>
diff --git a/Submodules/c3d/gui/resources/macos/icon_256x256.png b/Submodules/c3d/gui/resources/macos/icon_256x256.png
new file mode 100644
index 0000000..6e6baa7
Binary files /dev/null and b/Submodules/c3d/gui/resources/macos/icon_256x256.png differ
diff --git a/Submodules/c3d/gui/resources/macos/icon_512x512.png b/Submodules/c3d/gui/resources/macos/icon_512x512.png
new file mode 100644
index 0000000..c9b372a
Binary files /dev/null and b/Submodules/c3d/gui/resources/macos/icon_512x512.png differ
diff --git a/Submodules/c3d/itkextras/CMakeLists.txt b/Submodules/c3d/itkextras/CMakeLists.txt
new file mode 100644
index 0000000..052b3a2
--- /dev/null
+++ b/Submodules/c3d/itkextras/CMakeLists.txt
@@ -0,0 +1,3 @@
+SUBDIRS(PovRayIO)
+SUBDIRS(VoxBoIO)
+SUBDIRS(RandomForest)
diff --git a/Logic/Preprocessing/ImageCollectionToImageFilter.h b/Submodules/c3d/itkextras/ImageCollectionToImageFilter.h
similarity index 99%
copy from Logic/Preprocessing/ImageCollectionToImageFilter.h
copy to Submodules/c3d/itkextras/ImageCollectionToImageFilter.h
index 5c5c618..a0399a2 100644
--- a/Logic/Preprocessing/ImageCollectionToImageFilter.h
+++ b/Submodules/c3d/itkextras/ImageCollectionToImageFilter.h
@@ -38,6 +38,7 @@
#include <itkImageSource.h>
#include <itkImageToImageFilter.h>
+#include <itkImageRegionIterator.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkConstNeighborhoodIterator.h>
diff --git a/Submodules/c3d/itkextras/ImageRegionConstIteratorWithIndexOverride.h b/Submodules/c3d/itkextras/ImageRegionConstIteratorWithIndexOverride.h
new file mode 100644
index 0000000..39b2e83
--- /dev/null
+++ b/Submodules/c3d/itkextras/ImageRegionConstIteratorWithIndexOverride.h
@@ -0,0 +1,78 @@
+#ifndef __ImageRegionConstIteratorWithIndexOverride_h_
+#define __ImageRegionConstIteratorWithIndexOverride_h_
+
+#include "itkImageRegionConstIteratorWithIndex.h"
+#include "itkImageLinearIteratorWithIndex.h"
+
+namespace itk {
+ template <typename TPixel, unsigned int VDim> class VectorImage;
+}
+
+template <class TIterator>
+class IteratorExtender : public TIterator
+{
+public:
+ typedef IteratorExtender<TIterator> Self;
+ typedef TIterator Superclass;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+
+
+ IteratorExtender(ImageType *image, const RegionType ®ion)
+ : Superclass(image, region) {}
+
+ IteratorExtender(const ImageType *image, const RegionType ®ion)
+ : Superclass(const_cast<ImageType *>(image), region) {}
+
+ const InternalPixelType *GetPosition() { return this->m_Position; }
+
+ const InternalPixelType *GetBeginPosition() { return this->m_Begin; }
+
+ template <class TPixel, unsigned int VDim>
+ TPixel *GetPixelPointer(itk::Image<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels;
+ }
+
+ template <class TPixel, unsigned int VDim>
+ const TPixel *GetPixelPointer(const itk::Image<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels;
+ }
+
+ template <class TPixel, unsigned int VDim>
+ TPixel *GetPixelPointer(itk::VectorImage<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels * image->GetNumberOfComponentsPerPixel();
+ }
+
+ template <class TPixel, unsigned int VDim>
+ const TPixel *GetPixelPointer(const itk::VectorImage<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels * image->GetNumberOfComponentsPerPixel();
+ }
+};
+
+template <class TIterator>
+class IteratorExtenderWithOffset : public IteratorExtender<TIterator>
+{
+public:
+ typedef IteratorExtender<TIterator> Superclass;
+
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename TIterator::OffsetValueType OffsetValueType;
+
+ IteratorExtenderWithOffset(ImageType *image, const RegionType ®ion)
+ : Superclass(image, region) {}
+
+ const OffsetValueType GetOffset(int direction) { return this->m_OffsetTable[direction]; }
+};
+
+
+#endif
diff --git a/Submodules/c3d/itkextras/OneDimensionalInPlaceAccumulateFilter.h b/Submodules/c3d/itkextras/OneDimensionalInPlaceAccumulateFilter.h
new file mode 100644
index 0000000..19d3bb6
--- /dev/null
+++ b/Submodules/c3d/itkextras/OneDimensionalInPlaceAccumulateFilter.h
@@ -0,0 +1,123 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef ONEDIMENSIONALINPLACEACCUMULATEFILTER_H
+#define ONEDIMENSIONALINPLACEACCUMULATEFILTER_H
+
+#include "itkInPlaceImageFilter.h"
+#include "itkImageRegionSplitterDirection.h"
+
+/**
+ * This is a filter for fast computation of box sums in an image. It is mean to be
+ * used once in each image dimension (i.e., a separable filter). The input to the
+ * filter is assumed to be a VectorImage (the filter is optimized for this)
+ */
+template <class TInputImage>
+class OneDimensionalInPlaceAccumulateFilter : public itk::InPlaceImageFilter<TInputImage, TInputImage>
+{
+public:
+
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> Self;
+ typedef itk::InPlaceImageFilter<TInputImage, TInputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ itkTypeMacro(OneDimensionalInPlaceAccumulateFilter, itk::InPlaceImageFilter)
+
+ itkNewMacro(Self)
+
+ /** Some convenient typedefs. */
+ typedef TInputImage InputImageType;
+ typedef TInputImage OutputImageType;
+ typedef typename OutputImageType::Pointer OutputImagePointer;
+ typedef typename OutputImageType::RegionType OutputImageRegionType;
+ typedef typename OutputImageType::PixelType OutputImagePixelType;
+ typedef typename OutputImageType::InternalPixelType OutputImageComponentType;
+
+ /** We use a custom splitter */
+ typedef itk::ImageRegionSplitterDirection SplitterType;
+
+ /** ImageDimension constant */
+ itkStaticConstMacro(OutputImageDimension, unsigned int, TInputImage::ImageDimension);
+
+ itkGetMacro(Radius, int)
+ itkSetMacro(Radius, int)
+
+ itkGetMacro(Dimension, int)
+ itkSetMacro(Dimension, int)
+
+ /**
+ * Set the range of components in the input image that will be processed by the
+ * accumulation filter. The components out of this range will be ignored. The range
+ * is specified as number of components skipped at the beginning and number of components
+ * skipped at the end. For example, passing (1,2) means accumulation will be applied
+ * only to components 1 ... nc-3 where nc is the number of components in the input.
+ */
+ void SetComponentRange(int num_ignored_at_start, int num_ignored_at_end);
+
+ itkGetMacro(ComponentOffsetFront, int)
+ itkGetMacro(ComponentOffsetBack, int)
+
+protected:
+
+ OneDimensionalInPlaceAccumulateFilter();
+ ~OneDimensionalInPlaceAccumulateFilter() {}
+
+ virtual void ThreadedGenerateData(
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId);
+
+ virtual const itk::ImageRegionSplitterBase *GetImageRegionSplitter(void) const;
+
+ // Dimension of accumulation
+ int m_Dimension;
+
+ // Radius of accumulation
+ int m_Radius;
+
+ // Range of included components
+ int m_ComponentOffsetFront, m_ComponentOffsetBack;
+
+ // Region splitter
+ typename SplitterType::Pointer m_Splitter;
+
+};
+
+/**
+ * This helper function strings N 1-D filters together
+ */
+template <class TInputImage>
+typename TInputImage::Pointer
+AccumulateNeighborhoodSumsInPlace(TInputImage *image, const typename TInputImage::SizeType &radius,
+ int num_ignored_at_start = 0, int num_ignored_at_end = 0);
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "OneDimensionalInPlaceAccumulateFilter.txx"
+#endif
+
+
+#endif // ONEDIMENSIONALINPLACEACCUMULATEFILTER_H
diff --git a/Submodules/c3d/itkextras/OneDimensionalInPlaceAccumulateFilter.txx b/Submodules/c3d/itkextras/OneDimensionalInPlaceAccumulateFilter.txx
new file mode 100644
index 0000000..01d7920
--- /dev/null
+++ b/Submodules/c3d/itkextras/OneDimensionalInPlaceAccumulateFilter.txx
@@ -0,0 +1,565 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include <itkImageLinearIteratorWithIndex.h>
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+
+
+template <class TInputImage>
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::OneDimensionalInPlaceAccumulateFilter()
+{
+ m_Radius = 0;
+ m_Dimension = 0;
+ m_ComponentOffsetFront = m_ComponentOffsetBack = 0;
+ m_Splitter = SplitterType::New();
+ this->InPlaceOn();
+}
+
+template <class TInputImage>
+const itk::ImageRegionSplitterBase *
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::GetImageRegionSplitter(void) const
+{
+ m_Splitter->SetDirection(m_Dimension);
+ return m_Splitter;
+}
+
+
+template <class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::SetComponentRange(int num_ignored_at_start, int num_ignored_at_end)
+{
+ m_ComponentOffsetFront = num_ignored_at_start;
+ m_ComponentOffsetBack = num_ignored_at_end;
+ this->Modified();
+}
+
+/**
+ * This worker class is defined to allow partial specialization of the ThreadedGenerateData
+ * based on the pixel type (float/double)
+ */
+template <class TPixel, class TInputImage>
+class OneDimensionalInPlaceAccumulateFilterWorker
+{
+public:
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> FilterType;
+ typedef typename FilterType::OutputImageRegionType OutputImageRegionType;
+ typedef typename FilterType::InputImageType InputImageType;
+ static void ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId);
+};
+
+
+template <class TPixel, class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilterWorker<TPixel, TInputImage>
+::ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ // Get filter parameters
+ int radius = filter->GetRadius();
+ int dimension = filter->GetDimension();
+
+ // Get the image
+ InputImageType *image = const_cast<InputImageType *>(filter->GetInput());
+
+ // Set up the iterator that will go through all the lines in the
+ // output region. We assume that the lines span the whole length of
+ // the input, i.e., the threading direction does not interfere
+ typedef itk::ImageLinearIteratorWithIndex<TInputImage> IteratorBaseType;
+ typedef IteratorExtenderWithOffset<IteratorBaseType> IteratorType;
+
+ // This is the line iterator, although for even greater speed we operate
+ // directly on pointers, so we only use it's NextLine functionality()
+ IteratorType itLine(image, outputRegionForThread);
+ itLine.SetDirection(dimension);
+
+ // Get the number of components
+ int nc = image->GetNumberOfComponentsPerPixel();
+
+ // Get the first and last component for accumulation - these are optionally
+ // specified by the user
+ int c_first = filter->GetComponentOffsetFront(),
+ c_last = (nc - 1) - filter->GetComponentOffsetBack();
+ int n_skipped = filter->GetComponentOffsetFront() + filter->GetComponentOffsetBack();
+
+ // Get the offset corresponding to a move along the line for this iterator
+ typename IteratorType::OffsetValueType jump = itLine.GetOffset(dimension) * nc;
+
+ // Length of the line being traversed (in whole pixels, then in components)
+ int line_length = outputRegionForThread.GetSize(dimension),
+ line_length_comp = line_length * nc;
+
+ // Width of the kernel (in whole pixels, then in components)
+ int kernel_width = 2 * radius + 1;
+
+ // Allocate an array of the length of the line in components
+ TPixel *line = new TPixel[line_length_comp];
+ // double *line = new double[line_length_comp];
+ // double *sum = new double[nc], *sum_end = sum + nc, *p_sum;
+
+ // Allocate an array to hold the current running sum
+ // OutputImageComponentType *sum = new OutputImageComponentType[nc], *sum_end = sum + nc, *p_sum;
+ TPixel *sum = new TPixel[nc];
+
+ // Pointers into the sum array for the included components
+ TPixel *sum_start = sum + c_first, *sum_end = sum + c_last + 1, *p_sum;
+
+ // Two versions of the code - I thought that maybe the second version (further down) would be
+ // more optimized by the compiler, but if anything, I see an opposite effect (although tiny)
+
+#ifdef _ACCUM_ITER_CODE_
+
+ // Start iterating over lines
+ for(itLine.GoToBegin(); !itLine.IsAtEnd(); itLine.NextLine())
+ {
+ int i;
+
+ // Initialize the sum to zero
+ for(p_sum = sum_start; p_sum < sum_end; p_sum++)
+ *p_sum = itk::NumericTraits<TPixel>::Zero;
+
+ // Pointer to the current position in the line
+ TPixel *p_line = line + c_first, *p_tail = p_line;
+
+ // Pointer to the beginning of the scan line
+ long offset_in_pixels = itLine.GetPosition() - image->GetBufferPointer();
+ long offset_in_comp = offset_in_pixels * nc;
+ const TPixel *p_scan_pixel = image->GetBufferPointer() + offset_in_comp + c_first, *p_scan;
+
+ // Pointer used for writing, it will trail the scan pointer
+ TPixel *p_write_pixel = const_cast<TPixel *>(p_scan_pixel), *p_write;
+
+ // Compute the initial sum
+ for(i = 0; i < radius; i++)
+ {
+ for(p_scan = p_scan_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_line++, p_scan++)
+ {
+ *p_sum += *p_line = *p_scan;
+ }
+
+ p_scan_pixel += jump;
+ p_line += n_skipped;
+ }
+
+ // For the next Radius + 1 values, add to the sum and write
+ for(; i < kernel_width; i++)
+ {
+ for(p_scan = p_scan_pixel, p_write = p_write_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_line++, p_scan++, p_write++)
+ {
+ *p_line = *p_scan;
+ *p_sum += *p_line;
+ *p_write = *p_sum;
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += n_skipped;
+ }
+
+ // Continue until we hit the end of the scanline
+ for(; i < line_length; i++)
+ {
+ for(p_scan = p_scan_pixel, p_write = p_write_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_line++, p_scan++, p_write++, p_tail++)
+ {
+ *p_line = *p_scan;
+ *p_sum += *p_line - *p_tail;
+ *p_write = *p_sum;
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += n_skipped;
+ p_tail += n_skipped;
+ }
+
+ // Fill out the last bit
+ for(; i < line_length + radius; i++)
+ {
+ for(p_write = p_write_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_write++, p_tail++)
+ {
+ *p_sum -= *p_tail;
+ *p_write = *p_sum;
+ }
+
+ p_write_pixel += jump;
+ p_tail += n_skipped;
+ }
+ }
+
+#else
+
+ // Start iterating over lines
+ for(itLine.GoToBegin(); !itLine.IsAtEnd(); itLine.NextLine())
+ {
+
+ int i, k, m;
+
+ // Initialize the sum to zero
+ for(int k = c_first; k <= c_last; k++)
+ sum[k] = itk::NumericTraits<TPixel>::Zero;
+
+ // Pointer to the current position in the line
+ TPixel *p_line = line, *p_tail = p_line;
+
+ // Pointer to the beginning of the scan line
+ long offset_in_pixels = itLine.GetPosition() - image->GetBufferPointer();
+ long offset_in_comp = offset_in_pixels * nc;
+
+ // Where we are scanning from
+ const TPixel *p_scan_pixel = image->GetBufferPointer() + offset_in_comp;
+
+ // Pointer used for writing, it will trail the scan pointer
+ TPixel *p_write_pixel = const_cast<TPixel *>(p_scan_pixel);
+
+ // Compute the initial sum
+ for(i = 0, m = 0; i < radius; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ sum[k] += p_line[k] = p_scan_pixel[k];
+ }
+ p_scan_pixel += jump;
+ p_line += nc;
+ }
+
+ // For the next Radius + 1 values, add to the sum and write
+ for(; i < kernel_width; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ p_write_pixel[k] = (sum[k] += p_line[k] = p_scan_pixel[k]);
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += nc;
+ }
+
+ // Continue until we hit the end of the scanline
+ for(; i < line_length; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ p_write_pixel[k] = (sum[k] += (p_line[k] = p_scan_pixel[k]) - p_tail[k]);
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += nc;
+ p_tail += nc;
+ }
+
+ // Fill out the last bit
+ for(; i < line_length + radius; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ p_write_pixel[k] = (sum[k] -= p_tail[k]);
+ }
+
+ p_write_pixel += jump;
+ p_tail += nc;
+ }
+ }
+#endif
+
+ delete sum;
+ delete line;
+}
+
+
+#define _NCC_SSE_
+#ifdef _NCC_SSE_
+
+#include <xmmintrin.h>
+
+template <class T>
+T* allocate_aligned(int elements)
+{
+ void* pointer;
+ posix_memalign(&pointer, 16, elements * sizeof(T));
+ return static_cast<T *>(pointer) ;
+}
+
+
+/**
+ * A specialization of the threaded generate data method for floating point images that uses
+ * SSE intrinsics for faster computation
+ */
+template <class TInputImage>
+class OneDimensionalInPlaceAccumulateFilterWorker<float, TInputImage>
+{
+public:
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> FilterType;
+ typedef typename FilterType::OutputImageRegionType OutputImageRegionType;
+ typedef typename FilterType::InputImageType InputImageType;
+ static void ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId);
+};
+
+template <class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilterWorker<float, TInputImage>
+::ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ // Get filter parameters
+ int dimension = filter->GetDimension();
+ int radius = filter->GetRadius();
+ int skip_front = filter->GetComponentOffsetFront();
+ int skip_back = filter->GetComponentOffsetBack();
+
+ // Get the image
+ InputImageType *image = const_cast<InputImageType *>(filter->GetInput());
+
+ // Set up the iterator that will go through all the lines in the
+ // output region. We assume that the lines span the whole length of
+ // the input, i.e., the threading direction does not interfere
+ typedef itk::ImageLinearIteratorWithIndex<InputImageType> IteratorBaseType;
+ typedef IteratorExtenderWithOffset<IteratorBaseType> IteratorType;
+
+ // This is the line iterator, although for even greater speed we operate
+ // directly on pointers, so we only use it's NextLine functionality()
+ IteratorType itLine(image, outputRegionForThread);
+ itLine.SetDirection(dimension);
+
+ // Get the number of components
+ int nc = image->GetNumberOfComponentsPerPixel();
+
+ // Get the first and last component for accumulation - these are optionally
+ // specified by the user. The remaining components are left untouched
+ int c_first = skip_front, c_last = (nc - 1) - skip_back;
+ int n_skipped = skip_front + skip_back;
+
+ // Get the offset corresponding to a move along the line for this iterator
+ typename IteratorType::OffsetValueType jump = itLine.GetOffset(dimension) * nc;
+
+ // Length of the line being traversed (in whole pixels, then in components)
+ int line_length = outputRegionForThread.GetSize(dimension);
+
+ // Width of the kernel (in whole pixels, then in components)
+ int kernel_width = 2 * radius + 1;
+
+ // We want some alignment for SIMD purposes. So we need to make a stride be a factor of 16 bytes
+ int nc_used = nc - n_skipped;
+ int bytes_per_pixel = sizeof(float) * (nc_used - n_skipped);
+
+ // Round up, so it works out to 16 bytes
+ int align_stride = 4 * sizeof(float);
+ int padded_bytes_per_pixel = (bytes_per_pixel % align_stride) == 0
+ ? bytes_per_pixel : align_stride * (1 + bytes_per_pixel / align_stride);
+
+ // Number of chunks of four components per pixel
+ int nc_padded = padded_bytes_per_pixel / sizeof(float);
+
+ // This is a byte-aligned copy of the pixel column from the image
+ float *scanline = allocate_aligned<float>(line_length * nc_padded);
+
+ // This is a second aligned copy
+ float *tailline = allocate_aligned<float>(line_length * nc_padded);
+
+ // End of the scanline
+ float *p_scanline_end = scanline + line_length * nc_padded;
+
+ // Aligned sum array - where the sums are computed
+ float *sum_align = allocate_aligned<float>(nc_padded);
+
+ // Start iterating over lines
+ for(itLine.GoToBegin(); !itLine.IsAtEnd(); itLine.NextLine())
+ {
+ int i, k;
+
+ // Pointer to the beginning of the scan line
+ long offset_in_pixels = itLine.GetPosition() - image->GetBufferPointer();
+ long offset_in_comp = offset_in_pixels * nc;
+
+ // Get the pointer to first component in first pixel
+ const float *p_scan_pixel = image->GetBufferPointer() + offset_in_comp + c_first;
+
+ // Registers
+ __m128 m_line, m_tail, m_sum_cur, m_sum_new;
+
+ // Copy the contents of the image into the aligned line
+ float *p_copy = scanline;
+ const float *p_src = p_scan_pixel;
+ for(; p_copy < p_scanline_end; p_copy += nc_padded, p_src += jump)
+ {
+ __builtin_prefetch(p_src + 5 * jump, 0, 0);
+ for(i = 0; i < nc_used; i++)
+ p_copy[i] = p_src[i];
+ }
+
+ // Make a copy of the scan line
+ for(p_src = scanline, p_copy = tailline; p_src < p_scanline_end; p_copy+=4, p_src+=4)
+ {
+ m_line = _mm_load_ps(p_src);
+ _mm_store_ps(p_copy, m_line);
+ }
+
+ // Clear the sum array at the beginning
+ for(k = 0; k < nc_padded; k++)
+ sum_align[k] = 0.0;
+
+ // Pointer to the current position in the line
+ float *p_line = scanline, *p_tail = tailline;
+
+ // Pointer used for writing, it will trail the scan pointer
+ float *p_write_pixel = scanline;
+
+ // Pointer used for writing, it will trail the scan pointer
+ float *p_sum_end = sum_align + nc_padded, *p_sum;
+
+ // Compute the initial sum
+ for(i = 0; i < radius; i++)
+ {
+ #pragma unroll
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_line+=4)
+ {
+ m_line = _mm_load_ps(p_line);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_add_ps(m_sum_cur, m_line);
+ _mm_store_ps(p_sum, m_sum_new);
+ }
+ }
+
+ // For the next Radius + 1 values, add to the sum and write
+ for(; i < kernel_width; i++)
+ {
+ #pragma unroll
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_line+=4, p_write_pixel+=4)
+ {
+ m_line = _mm_load_ps(p_line);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_add_ps(m_sum_cur, m_line);
+ _mm_store_ps(p_sum, m_sum_new);
+ _mm_store_ps(p_write_pixel, m_sum_new);
+ }
+ }
+
+ // Continue until we hit the end of the scanline
+ for(; i < line_length; i++)
+ {
+ #pragma unroll
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_line+=4, p_tail+=4, p_write_pixel+=4)
+ {
+ m_line = _mm_load_ps(p_line);
+ m_tail = _mm_load_ps(p_tail);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_add_ps(m_sum_cur, _mm_sub_ps(m_line, m_tail));
+ _mm_store_ps(p_sum, m_sum_new);
+ _mm_store_ps(p_write_pixel, m_sum_new);
+ }
+ }
+
+ // Fill out the last bit
+ for(; i < line_length + radius; i++)
+ {
+ #pragma unroll
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_tail+=4, p_write_pixel+=4)
+ {
+ m_tail = _mm_load_ps(p_tail);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_sub_ps(m_sum_cur, m_tail);
+ _mm_store_ps(p_sum, m_sum_new);
+ _mm_store_ps(p_write_pixel, m_sum_new);
+ }
+ }
+
+ // Copy the accumulated pixels back into the main image
+ float *p_copy_back = const_cast<float *>(p_scan_pixel);
+ const float *p_src_back = scanline;
+ for(; p_src_back < p_scanline_end; p_src_back += nc_padded, p_copy_back += jump)
+ {
+ __builtin_prefetch(p_copy_back + 5 * jump, 1, 0);
+ for(i = 0; i < nc_used; i++)
+ p_copy_back[i] = p_src_back[i];
+ }
+ }
+
+ // Free allocated memory
+ free(tailline);
+ free(scanline);
+ free(sum_align);
+}
+
+#endif // _NCC_SSE_
+
+
+/**
+ * Default implementaton of the threaded generate data method
+ */
+template <class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::ThreadedGenerateData(
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ typedef OneDimensionalInPlaceAccumulateFilterWorker<OutputImageComponentType, InputImageType> WorkerType;
+ WorkerType::ThreadedGenerateData(this, outputRegionForThread, threadId);
+}
+
+
+
+
+template <class TInputImage>
+typename TInputImage::Pointer
+AccumulateNeighborhoodSumsInPlace(TInputImage *image, const typename TInputImage::SizeType &radius,
+ int num_ignored_at_start, int num_ignored_at_end)
+{
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> AccumFilterType;
+
+ typename itk::ImageSource<TInputImage>::Pointer pipeTail;
+ for(int dir = 0; dir < TInputImage::ImageDimension; dir++)
+ {
+ typename AccumFilterType::Pointer accum = AccumFilterType::New();
+ accum->SetInput(pipeTail.IsNull() ? image : pipeTail->GetOutput());
+ accum->SetDimension(dir);
+ accum->SetRadius(radius[dir]);
+ accum->SetComponentRange(num_ignored_at_start, num_ignored_at_end);
+ pipeTail = accum;
+
+ accum->Update();
+ }
+
+ return pipeTail->GetOutput();
+}
diff --git a/Submodules/c3d/itkextras/PovRayIO/CMakeLists.txt b/Submodules/c3d/itkextras/PovRayIO/CMakeLists.txt
new file mode 100644
index 0000000..d1e0711
--- /dev/null
+++ b/Submodules/c3d/itkextras/PovRayIO/CMakeLists.txt
@@ -0,0 +1,7 @@
+PROJECT(PovRayIO)
+
+INCLUDE_DIRECTORIES(${PovRayIO_SOURCE_DIR})
+
+ADD_LIBRARY(ITKPovRayIO
+ itkPovRayDF3ImageIO.cxx
+ itkPovRayDF3ImageIOFactory.cxx)
diff --git a/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.cxx b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.cxx
new file mode 100644
index 0000000..b61ea17
--- /dev/null
+++ b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.cxx
@@ -0,0 +1,121 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkPovRayDF3ImageIO.cxx,v $
+ Language: C++
+ Date: $Date: 2008/11/08 01:42:03 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#include "itkPovRayDF3ImageIO.h"
+#include "itkIOCommon.h"
+#include "itkExceptionObject.h"
+#include "itkMetaDataObject.h"
+#include "itkByteSwapper.h"
+#include <iostream>
+#include <iomanip>
+#include <list>
+#include <string>
+#include <math.h>
+
+using namespace std;
+
+namespace itk {
+
+bool PovRayDF3ImageIO::CanWriteFile( const char * name )
+{
+ return CheckExtension(name);
+}
+
+void
+PovRayDF3ImageIO
+::WriteImageInformation(void)
+{
+ // Check that the number of dimensions is correct
+ if(GetNumberOfDimensions() != 3)
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("Unsupported number of dimensions");
+ throw exception;
+ }
+
+ // Check that the component type is legal
+ if(m_ComponentType != UINT && m_ComponentType != UCHAR && m_ComponentType != USHORT)
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("Unsupported pixel component type (uchar, ushort, uint are supported)");
+ throw exception;
+ }
+
+ // Write the 3 shorts with image dimensions
+ unsigned short xyz[3];
+ xyz[0] = (unsigned short) m_Dimensions[0];
+ xyz[1] = (unsigned short) m_Dimensions[1];
+ xyz[2] = (unsigned short) m_Dimensions[2];
+
+ // Open a file handle
+ ofstream fout(this->GetFileName(), ios_base::out | ios_base::binary);
+
+ // Write the header
+ itk::ByteSwapper<unsigned short>::SwapWriteRangeFromSystemToBigEndian(
+ xyz, 3, &fout);
+
+ // Write the data to file
+ fout.close();
+}
+
+/** The write function is not implemented */
+void
+PovRayDF3ImageIO
+::Write( const void* buffer)
+{
+ // Write the image information
+ WriteImageInformation();
+
+ // Open a file handle
+ ofstream fout(this->GetFileName(), ios_base::app | ios_base::binary);
+
+ // Write with a byte swap
+ if(m_ComponentType == USHORT)
+ itk::ByteSwapper<unsigned short>::SwapWriteRangeFromSystemToBigEndian(
+ (unsigned short *) buffer, GetImageSizeInBytes() / sizeof(unsigned short), &fout);
+ else if(m_ComponentType == UINT)
+ itk::ByteSwapper<unsigned int>::SwapWriteRangeFromSystemToBigEndian(
+ (unsigned int *) buffer, GetImageSizeInBytes() / sizeof(unsigned int), &fout);
+ else if(m_ComponentType == UCHAR)
+ itk::ByteSwapper<unsigned char>::SwapWriteRangeFromSystemToBigEndian(
+ (unsigned char *) buffer, GetImageSizeInBytes() / sizeof(unsigned char), &fout);
+
+ // Close the stream
+ fout.close();
+}
+
+/** Print Self Method */
+void PovRayDF3ImageIO::PrintSelf(ostream& os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+ os << indent << "PixelType " << m_PixelType << "\n";
+}
+
+
+bool PovRayDF3ImageIO::CheckExtension(const char* filename)
+{
+ string fname = filename;
+ if ( fname == "" )
+ {
+ itkDebugMacro(<< "No filename specified.");
+ return false;
+ }
+
+ string::size_type giplPos = fname.rfind(".df3");
+ return ((giplPos != string::npos) && (giplPos == fname.length() - 4));
+}
+
+} // end namespace itk
diff --git a/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.h b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.h
new file mode 100644
index 0000000..8e60098
--- /dev/null
+++ b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIO.h
@@ -0,0 +1,95 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkPovRayDF3ImageIO.h,v $
+ Language: C++
+ Date: $Date: 2008/11/08 01:42:03 $
+ Version: $1.0$
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkPovRayDF3ImageIO_h
+#define __itkPovRayDF3ImageIO_h
+
+#ifdef _MSC_VER
+#pragma warning ( disable : 4786 )
+#endif
+
+#include <fstream>
+#include <string>
+#include <map>
+#include "itkImageIOBase.h"
+#include "itkSpatialOrientation.h"
+#include <stdio.h>
+
+namespace itk
+{
+
+/** \class PovRayDF3ImageIO
+ *
+ * \brief Read PovRayDF3Image file format.
+ *
+ * \ingroup IOFilters
+ *
+ */
+class ITK_EXPORT PovRayDF3ImageIO : public ImageIOBase
+{
+public:
+ /** Standard class typedefs. */
+ typedef PovRayDF3ImageIO Self;
+ typedef ImageIOBase Superclass;
+ typedef SmartPointer<Self> Pointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(PovRayDF3ImageIO, Superclass);
+
+ /*-------- This part of the interfaces deals with reading data. ----- */
+
+ /** Determine the file type. Returns true if this ImageIO can read the
+ * file specified. */
+ virtual bool CanReadFile(const char*) { return false; }
+
+ /** Set the spacing and dimension information for the set filename. */
+ virtual void ReadImageInformation() { }
+
+ /** Reads the data from disk into the memory buffer provided. */
+ virtual void Read(void* buffer) { }
+
+ /*-------- This part of the interfaces deals with writing data. ----- */
+
+ /** Determine the file type. Returns true if this ImageIO can write the
+ * file specified. */
+ virtual bool CanWriteFile(const char*);
+
+ /** Set the spacing and dimension information for the set filename. */
+ virtual void WriteImageInformation();
+
+ /** Writes the data to disk from the memory buffer provided. Make sure
+ * that the IORegions has been set properly. */
+ virtual void Write(const void* buffer);
+
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+protected:
+ PovRayDF3ImageIO() {}
+ ~PovRayDF3ImageIO() {}
+
+private:
+ PovRayDF3ImageIO(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ bool CheckExtension(const char*);
+};
+
+} // end namespace itk
+
+#endif // __itkPovRayDF3ImageIO_h
diff --git a/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIOFactory.cxx b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIOFactory.cxx
new file mode 100644
index 0000000..2494316
--- /dev/null
+++ b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIOFactory.cxx
@@ -0,0 +1,52 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkPovRayDF3ImageIOFactory.cxx,v $
+ Language: C++
+ Date: $Date: 2008/11/08 01:42:03 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#include "itkPovRayDF3ImageIOFactory.h"
+#include "itkCreateObjectFunction.h"
+#include "itkPovRayDF3ImageIO.h"
+#include "itkVersion.h"
+
+
+namespace itk
+{
+
+PovRayDF3ImageIOFactory::PovRayDF3ImageIOFactory()
+{
+ this->RegisterOverride("itkImageIOBase",
+ "itkPovRayDF3ImageIO",
+ "PovRay DF3 Image IO",
+ 1,
+ CreateObjectFunction<PovRayDF3ImageIO>::New());
+}
+
+PovRayDF3ImageIOFactory::~PovRayDF3ImageIOFactory()
+{
+}
+
+const char*
+PovRayDF3ImageIOFactory::GetITKSourceVersion(void) const
+{
+ return ITK_SOURCE_VERSION;
+}
+
+const char*
+PovRayDF3ImageIOFactory::GetDescription() const
+{
+ return "PovRay DF3 ImageIO Factory, allows the saving of PovRayDF3 images from Insight";
+}
+
+} // end namespace itk
+
diff --git a/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIOFactory.h b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIOFactory.h
new file mode 100644
index 0000000..1914fef
--- /dev/null
+++ b/Submodules/c3d/itkextras/PovRayIO/itkPovRayDF3ImageIOFactory.h
@@ -0,0 +1,67 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkPovRayDF3ImageIOFactory.h,v $
+ Language: C++
+ Date: $Date: 2008/11/08 01:42:03 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkPovRayDF3ImageIOFactory_h
+#define __itkPovRayDF3ImageIOFactory_h
+
+#include "itkObjectFactoryBase.h"
+#include "itkImageIOBase.h"
+
+namespace itk
+{
+/** \class PovRayDF3ImageIOFactory
+ * \brief Create instances of PovRayDF3ImageIO objects using an object factory.
+ */
+class ITK_EXPORT PovRayDF3ImageIOFactory : public ObjectFactoryBase
+{
+public:
+ /** Standard class typedefs. */
+ typedef PovRayDF3ImageIOFactory Self;
+ typedef ObjectFactoryBase Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Class methods used to interface with the registered factories. */
+ virtual const char* GetITKSourceVersion(void) const;
+ virtual const char* GetDescription(void) const;
+
+ /** Method for class instantiation. */
+ itkFactorylessNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(PovRayDF3ImageIOFactory, ObjectFactoryBase);
+
+ /** Register one factory of this type */
+ static void RegisterOneFactory(void)
+ {
+ PovRayDF3ImageIOFactory::Pointer PovRayDF3Factory = PovRayDF3ImageIOFactory::New();
+ ObjectFactoryBase::RegisterFactory(PovRayDF3Factory);
+ }
+
+protected:
+ PovRayDF3ImageIOFactory();
+ ~PovRayDF3ImageIOFactory();
+
+private:
+ PovRayDF3ImageIOFactory(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+};
+
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/c3d/itkextras/RandomForest/CMakeLists.txt b/Submodules/c3d/itkextras/RandomForest/CMakeLists.txt
new file mode 100644
index 0000000..29e9992
--- /dev/null
+++ b/Submodules/c3d/itkextras/RandomForest/CMakeLists.txt
@@ -0,0 +1 @@
+#Empty
diff --git a/Logic/Preprocessing/RandomForest/Library/classification.h b/Submodules/c3d/itkextras/RandomForest/Library/classification.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/classification.h
rename to Submodules/c3d/itkextras/RandomForest/Library/classification.h
diff --git a/Logic/Preprocessing/RandomForest/Library/classifier.h b/Submodules/c3d/itkextras/RandomForest/Library/classifier.h
similarity index 99%
rename from Logic/Preprocessing/RandomForest/Library/classifier.h
rename to Submodules/c3d/itkextras/RandomForest/Library/classifier.h
index f98383f..c293ee9 100755
--- a/Logic/Preprocessing/RandomForest/Library/classifier.h
+++ b/Submodules/c3d/itkextras/RandomForest/Library/classifier.h
@@ -8,6 +8,7 @@
#include <math.h>
#include "data.h"
#include "random.h"
+#include "utility.h"
template<class C, class dataT, class labelT>
class Classifier
diff --git a/Logic/Preprocessing/RandomForest/Library/data.h b/Submodules/c3d/itkextras/RandomForest/Library/data.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/data.h
rename to Submodules/c3d/itkextras/RandomForest/Library/data.h
diff --git a/Logic/Preprocessing/RandomForest/Library/forest.h b/Submodules/c3d/itkextras/RandomForest/Library/forest.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/forest.h
rename to Submodules/c3d/itkextras/RandomForest/Library/forest.h
diff --git a/Logic/Preprocessing/RandomForest/Library/imageio.h b/Submodules/c3d/itkextras/RandomForest/Library/imageio.h
similarity index 98%
rename from Logic/Preprocessing/RandomForest/Library/imageio.h
rename to Submodules/c3d/itkextras/RandomForest/Library/imageio.h
index aae9f74..0ed9512 100755
--- a/Logic/Preprocessing/RandomForest/Library/imageio.h
+++ b/Submodules/c3d/itkextras/RandomForest/Library/imageio.h
@@ -7,8 +7,7 @@
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
-#include "itkImageRegionConstIterator.h"
-#include "itkImageRegionIterator.h"
+#include "RLEImageRegionIterator.h"
#include "data.h"
typedef std::vector<std::string> StringVector;
diff --git a/Logic/Preprocessing/RandomForest/Library/linearalgebra.h b/Submodules/c3d/itkextras/RandomForest/Library/linearalgebra.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/linearalgebra.h
rename to Submodules/c3d/itkextras/RandomForest/Library/linearalgebra.h
diff --git a/Logic/Preprocessing/RandomForest/Library/node.h b/Submodules/c3d/itkextras/RandomForest/Library/node.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/node.h
rename to Submodules/c3d/itkextras/RandomForest/Library/node.h
diff --git a/Logic/Preprocessing/RandomForest/Library/random.h b/Submodules/c3d/itkextras/RandomForest/Library/random.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/random.h
rename to Submodules/c3d/itkextras/RandomForest/Library/random.h
diff --git a/Logic/Preprocessing/RandomForest/Library/statistics.h b/Submodules/c3d/itkextras/RandomForest/Library/statistics.h
similarity index 98%
rename from Logic/Preprocessing/RandomForest/Library/statistics.h
rename to Submodules/c3d/itkextras/RandomForest/Library/statistics.h
index 51caf88..b453f99 100755
--- a/Logic/Preprocessing/RandomForest/Library/statistics.h
+++ b/Submodules/c3d/itkextras/RandomForest/Library/statistics.h
@@ -207,6 +207,7 @@ public:
size_t binSize = 0;
readBasicType(is, binSize);
bins_.resize(binSize);
+ prob_.resize(binSize);
sampleNum_ = 0;
for (size_t i = 0; i < binSize; ++i)
{
@@ -369,7 +370,7 @@ public:
{
CalculateCov();
}
- return (0.5 * log(pow(2*M_PI*M_E, featureDim_)
+ return (0.5 * log(::pow(2*M_PI*M_E, featureDim_)
* spddeterminant(L_, true)));
}
}
@@ -392,7 +393,7 @@ public:
{
X[i] -= mean_[i];
}
- return (sqrt(1.0 / (pow(2 * M_PI, featureDim_) * spddeterminant(L_, true)))
+ return (sqrt(1.0 / (::pow(2 * M_PI, featureDim_) * spddeterminant(L_, true)))
* exp(-0.5 * multiplyXTspdAinvX(X, L_, true)));
}
diff --git a/Logic/Preprocessing/RandomForest/Library/trainer.h b/Submodules/c3d/itkextras/RandomForest/Library/trainer.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/trainer.h
rename to Submodules/c3d/itkextras/RandomForest/Library/trainer.h
diff --git a/Logic/Preprocessing/RandomForest/Library/trainingcontext.h b/Submodules/c3d/itkextras/RandomForest/Library/trainingcontext.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/trainingcontext.h
rename to Submodules/c3d/itkextras/RandomForest/Library/trainingcontext.h
diff --git a/Logic/Preprocessing/RandomForest/Library/tree.h b/Submodules/c3d/itkextras/RandomForest/Library/tree.h
similarity index 99%
rename from Logic/Preprocessing/RandomForest/Library/tree.h
rename to Submodules/c3d/itkextras/RandomForest/Library/tree.h
index 99f5326..251ce0a 100755
--- a/Logic/Preprocessing/RandomForest/Library/tree.h
+++ b/Submodules/c3d/itkextras/RandomForest/Library/tree.h
@@ -245,7 +245,7 @@ public:
void BreathFirstFullTree()
{
- breadthFirstFullTree_.resize(pow(2, depth_)-1);
+ breadthFirstFullTree_.resize(::pow(2, depth_)-1);
for (int i = 0; i < breadthFirstFullTree_.size(); ++i)
{
breadthFirstFullTree_[i] = 0;
@@ -390,13 +390,13 @@ public:
std::ios_base::fmtflags original_flags = std::cout.flags();
int index = 0;
- int interval = pow(2, depth_) - 1;
+ int interval = ::pow(2, depth_) - 1;
int start = (interval - 1) / 2;
int nodeNum = 0;
for (int i = 0; i < depth_; ++i)
{
- nodeNum = pow(2, i);
+ nodeNum = pow(2.0, i);
for (int j = 0; j < start; ++j)
{
for (int k = 0; k < (spaceWidth + numWidth); ++k)
diff --git a/Logic/Preprocessing/RandomForest/Library/type.h b/Submodules/c3d/itkextras/RandomForest/Library/type.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/type.h
rename to Submodules/c3d/itkextras/RandomForest/Library/type.h
diff --git a/Logic/Preprocessing/RandomForest/Library/utility.h b/Submodules/c3d/itkextras/RandomForest/Library/utility.h
similarity index 100%
rename from Logic/Preprocessing/RandomForest/Library/utility.h
rename to Submodules/c3d/itkextras/RandomForest/Library/utility.h
diff --git a/Submodules/c3d/itkextras/RandomForest/RandomForestClassifier.cxx b/Submodules/c3d/itkextras/RandomForest/RandomForestClassifier.cxx
new file mode 100644
index 0000000..e69de29
diff --git a/Submodules/c3d/itkextras/RandomForest/RandomForestClassifier.h b/Submodules/c3d/itkextras/RandomForest/RandomForestClassifier.h
new file mode 100644
index 0000000..f913972
--- /dev/null
+++ b/Submodules/c3d/itkextras/RandomForest/RandomForestClassifier.h
@@ -0,0 +1,197 @@
+#ifndef RANDOMFORESTCLASSIFIER_H
+#define RANDOMFORESTCLASSIFIER_H
+
+#include <itkDataObject.h>
+#include <itkObjectFactory.h>
+#include <itkSize.h>
+#include "Library/classification.h"
+#include <map>
+
+template <class dataT, class labelT> class Histogram;
+template <class dataT, class labelT> class AxisAlignedClassifier;
+template <class HistT, class ClassT, class dataT> class DecisionForest;
+
+/**
+ * This class encapsulates a Random Forest classifier
+ */
+template <class TPixel, class TLabel, int VDim>
+class RandomForestClassifier : public itk::DataObject
+{
+public:
+
+ // Standard ITK stuff
+ typedef RandomForestClassifier Self;
+ typedef itk::DataObject Superclass;
+
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ itkNewMacro(Self)
+
+ // typedefs
+ typedef TPixel GreyType;
+ typedef TLabel LabelType;
+ typedef Histogram<GreyType, LabelType> RFHistogramType;
+ typedef AxisAlignedClassifier<GreyType, LabelType> RFAxisClassifierType;
+ typedef DecisionForest<RFHistogramType, RFAxisClassifierType, GreyType> RandomForestType;
+ typedef std::map<size_t, LabelType> MappingType;
+ typedef itk::Size<VDim> SizeType;
+
+ // A list of weights for each class - used to construct speed image
+ typedef std::vector<double> WeightArray;
+
+ // Reset the classifier
+ void Reset()
+ {
+ if(m_Forest)
+ delete m_Forest;
+
+ m_Forest = new RandomForestType(true);
+ m_ClassToLabelMapping.clear();
+ m_BiasParameter = 0.5;
+ m_PatchRadius.Fill(0);
+ m_UseCoordinateFeatures = false;
+ m_ClassWeights.clear();
+ }
+
+ // Get the random forest
+ itkGetMacro(Forest, RandomForestType *)
+
+ // Get the patch radius
+ itkGetMacro(PatchRadius, const SizeType &)
+ itkSetMacro(PatchRadius, SizeType)
+
+ /** Whether coordinates of the voxels are used as features */
+ itkGetMacro(UseCoordinateFeatures, bool)
+ itkSetMacro(UseCoordinateFeatures, bool)
+
+ // Set the bias parameter (adjusts the mapping of FG probability to speed)
+ itkGetMacro(BiasParameter, double)
+ itkSetMacro(BiasParameter, double)
+
+ // Get a reference to the valid label state
+ itkGetMacro(ValidLabel, bool &)
+
+ // Get a reference to the class index to label mapping
+ itkGetMacro(ClassToLabelMapping, MappingType &)
+
+ // Get a reference to the class weights array
+ itkGetMacro(ClassWeights, WeightArray &)
+
+ // Set the weight for a class
+ void SetClassWeight(size_t class_id, double weight)
+ {
+ m_ClassWeights[class_id] = weight;
+ }
+
+ // Test if the classifier is valid (has 2+ classes)
+ bool IsValidClassifier() const
+ {
+ return m_ClassToLabelMapping.size() >= 2 && m_Forest->GetForestSize() > 0;
+ }
+
+ /** Write classifier to binary file */
+ void Write(std::ostream &os)
+ {
+ // Write the forest
+ for(int d = 0; d < VDim; d++)
+ writeBasicType(os, m_PatchRadius[d]);
+ writeBasicType(os, m_UseCoordinateFeatures);
+ writeBasicType(os, m_BiasParameter);
+
+ writeBasicType(os, (int) m_ClassToLabelMapping.size());
+ for(typename MappingType::iterator it = m_ClassToLabelMapping.begin();
+ it != m_ClassToLabelMapping.end(); ++it)
+ {
+ writeBasicType(os, it->first);
+ writeBasicType(os, it->second);
+ }
+
+ writeBasicType(os, (int) m_ClassWeights.size());
+ for(typename WeightArray::iterator it = m_ClassWeights.begin();
+ it != m_ClassWeights.end(); ++it)
+ {
+ writeBasicType(os, *it);
+ }
+
+ writeBasicType(os, m_ValidLabel);
+
+ m_Forest->Write(os);
+ }
+
+ /** Read classifier from binary file */
+ void Read(std::istream &is)
+ {
+ this->Reset();
+
+ // Read the forest
+ for(int d = 0; d < VDim; d++)
+ readBasicType(is,m_PatchRadius[d]);
+ readBasicType(is,m_UseCoordinateFeatures);
+ readBasicType(is,m_BiasParameter);
+
+ int sz_map, i;
+ readBasicType(is,sz_map);
+ for(i = 0; i < sz_map; i++)
+ {
+ size_t x; LabelType y;
+ readBasicType(is,x);
+ readBasicType(is,y);
+ m_ClassToLabelMapping[x] = y;
+ }
+
+ int sz_wgt, j;
+ readBasicType(is,sz_wgt);
+ for(j = 0; j < sz_wgt; j++)
+ {
+ double x;
+ readBasicType(is,x);
+ m_ClassWeights.push_back(x);
+ }
+
+ readBasicType(is,m_ValidLabel);
+
+ m_Forest->Read(is);
+
+ }
+
+protected:
+
+ RandomForestClassifier()
+ {
+ m_Forest = NULL;
+ this->Reset();
+ }
+
+ ~RandomForestClassifier()
+ {
+ if(m_Forest)
+ delete m_Forest;
+ }
+
+ // The actual decision forest
+ RandomForestType *m_Forest;
+
+ // Whether the labels are valid (?)
+ bool m_ValidLabel;
+
+ // Mapping of index to label (?)
+ MappingType m_ClassToLabelMapping;
+
+ // Weight of each class
+ WeightArray m_ClassWeights;
+
+ // The patch radius
+ SizeType m_PatchRadius;
+
+ // Whether coordinate features are used
+ bool m_UseCoordinateFeatures;
+
+ // Bias parameter
+ double m_BiasParameter;
+
+ // Let the engine handle our data
+ // friend class RFClassificationEngine;
+};
+
+#endif // RANDOMFORESTCLASSIFIER_H
diff --git a/Logic/Preprocessing/RandomForestClassifyImageFilter.h b/Submodules/c3d/itkextras/RandomForest/RandomForestClassifyImageFilter.h
similarity index 77%
rename from Logic/Preprocessing/RandomForestClassifyImageFilter.h
rename to Submodules/c3d/itkextras/RandomForest/RandomForestClassifyImageFilter.h
index fc7fb24..1b44154 100644
--- a/Logic/Preprocessing/RandomForestClassifyImageFilter.h
+++ b/Submodules/c3d/itkextras/RandomForest/RandomForestClassifyImageFilter.h
@@ -3,7 +3,7 @@
#include "itkImageToImageFilter.h"
-class RandomForestClassifier;
+template <class TPixel, class TLabel, int VDim> class RandomForestClassifier;
/**
* @brief A class that takes multiple multi-component images and uses a
@@ -12,7 +12,7 @@ class RandomForestClassifier;
* // TODO: derive this and the GMM filter from a common base class that
* // simplifies working with multiple vector/scalar images
*/
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
class RandomForestClassifyImageFilter :
public itk::ImageToImageFilter<TInputImage, TOutputImage>
{
@@ -46,6 +46,11 @@ public:
itkStaticConstMacro(ImageDimension, unsigned int,
TInputImage::ImageDimension);
+ /** Classifier typedef */
+ typedef TLabel LabelType;
+ typedef RandomForestClassifier<InputPixelType, LabelType,
+ ImageDimension> ClassifierType;
+
/** Add a scalar input image */
void AddScalarImage(InputImageType *image);
@@ -53,10 +58,19 @@ public:
void AddVectorImage(InputVectorImageType *image);
/** Set the mixture model */
- void SetClassifier(RandomForestClassifier *classifier);
+ void SetClassifier(ClassifierType *classifier);
+
+ /**
+ * Turn this flag on to generate class probabilities as outputs, as
+ * opposed to the weighted sum of class probabilities, which is the
+ * default output.
+ */
+ void SetGenerateClassProbabilities(bool flag);
+
+ itkGetMacro(GenerateClassProbabilities, bool)
/** Get the current classifier */
- irisGetMacro(Classifier, RandomForestClassifier *);
+ itkGetMacro(Classifier, ClassifierType *);
/** We need to override this method because of multiple input types */
void GenerateInputRequestedRegion();
@@ -71,7 +85,11 @@ protected:
void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
itk::ThreadIdType threadId);
- RandomForestClassifier *m_Classifier;
+ void UpdateOutputs();
+
+ ClassifierType *m_Classifier;
+
+ bool m_GenerateClassProbabilities;
};
#ifndef ITK_MANUAL_INSTANTIATION
diff --git a/Logic/Preprocessing/RandomForestClassifyImageFilter.txx b/Submodules/c3d/itkextras/RandomForest/RandomForestClassifyImageFilter.txx
similarity index 59%
rename from Logic/Preprocessing/RandomForestClassifyImageFilter.txx
rename to Submodules/c3d/itkextras/RandomForest/RandomForestClassifyImageFilter.txx
index e828051..73c8bf8 100644
--- a/Logic/Preprocessing/RandomForestClassifyImageFilter.txx
+++ b/Submodules/c3d/itkextras/RandomForest/RandomForestClassifyImageFilter.txx
@@ -13,49 +13,81 @@
#include "Library/classifier.h"
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
::RandomForestClassifyImageFilter()
{
// m_MixtureModel = NULL;
+ m_GenerateClassProbabilities = false;
+ m_Classifier = NULL;
}
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
::~RandomForestClassifyImageFilter()
{
}
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
void
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
::AddScalarImage(InputImageType *image)
{
this->AddInput(image);
}
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
void
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
::AddVectorImage(InputVectorImageType *image)
{
this->AddInput(image);
}
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
void
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
-::SetClassifier(RandomForestClassifier *classifier)
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
+::SetClassifier(ClassifierType *classifier)
{
m_Classifier = classifier;
this->Modified();
+ this->UpdateOutputs();
}
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
+void
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
+::SetGenerateClassProbabilities(bool flag)
+{
+ m_GenerateClassProbabilities = flag;
+ this->Modified();
+ this->UpdateOutputs();
+}
+
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
+void
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
+::UpdateOutputs()
+{
+ if(m_Classifier)
+ {
+ if(m_GenerateClassProbabilities)
+ {
+ this->SetNumberOfIndexedOutputs(m_Classifier->GetClassToLabelMapping().size());
+ for(int i = 1; i < m_Classifier->GetClassToLabelMapping().size(); i++)
+ this->SetNthOutput(i, this->MakeOutput(i));
+ }
+ else
+ {
+ this->SetNumberOfIndexedOutputs(1);
+ }
+ }
+}
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
void
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
::GenerateInputRequestedRegion()
{
itk::ImageSource<TOutputImage>::GenerateInputRequestedRegion();
@@ -85,28 +117,36 @@ RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
}
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
void
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
::PrintSelf(std::ostream &os, itk::Indent indent) const
{
os << indent << "RandomForestClassifyImageFilter" << std::endl;
}
-template <class TInputImage, class TInputVectorImage, class TOutputImage>
+template <class TInputImage, class TInputVectorImage, class TOutputImage, class TLabel>
void
-RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
+RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage, TLabel>
::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
itk::ThreadIdType threadId)
{
assert(m_Classifier);
+ // Number of outputs generated
+ int nClass = m_Classifier->GetClassToLabelMapping().size();
+ int nOut = m_GenerateClassProbabilities ? nClass : 1;
+
+ // Primary output
OutputImagePointer outputPtr = this->GetOutput(0);
- // Fill the output region with zeros
- itk::ImageRegionIterator<OutputImageType> zit(outputPtr, outputRegionForThread);
- for(; !zit.IsAtEnd(); ++zit)
- zit.Set((OutputPixelType) 0);
+ // Fill the output region with zeros in all outputs
+ for(int k = 0; k < nOut; k++)
+ {
+ itk::ImageRegionIterator<OutputImageType> zit(this->GetOutput(k), outputRegionForThread);
+ for(; !zit.IsAtEnd(); ++zit)
+ zit.Set((OutputPixelType) 0);
+ }
// Adjust the output region so that we don't touch image boundaries.
OutputImageRegionType crop_region = outputPtr->GetLargestPossibleRegion();
@@ -117,9 +157,11 @@ RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
if(!can_crop)
return;
- // Create an iterator for the output
+ // Create output iterators
typedef itk::ImageRegionIteratorWithIndex<TOutputImage> OutputIter;
- OutputIter it_out(outputPtr, out_region);
+ std::vector<OutputIter> it_out;
+ for(int k = 0; k < nOut; k++)
+ it_out.push_back(OutputIter(this->GetOutput(k), out_region));
// Create a collection iterator for the inputs
typedef ImageCollectionConstRegionIteratorWithIndex<
@@ -142,11 +184,8 @@ RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
if(m_Classifier->GetUseCoordinateFeatures())
nColumns += 3;
- // Get the number of classes
- int nClass = m_Classifier->GetClassToLabelMapping().size();
-
// Get the class weights (as they are assigned to foreground/background)
- const RandomForestClassifier::WeightArray &class_weights = m_Classifier->GetClassWeights();
+ const typename ClassifierType::WeightArray &class_weights = m_Classifier->GetClassWeights();
// Create the MLdata representing each voxel (?)
typedef Histogram<InputPixelType,LabelType> HistogramType;
@@ -166,9 +205,10 @@ RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
// Some vectors that are allocated for speed
std::vector<size_t> vIndex(1);
std::vector<bool> vResult(1);
+ std::vector<double> vClassProb(nClass);
// Iterate through all the voxels
- for(; !it_out.IsAtEnd(); ++it_out, ++cit)
+ while(!it_out[0].IsAtEnd())
{
// Assign the data to the testData vector
int k = 0;
@@ -179,43 +219,72 @@ RandomForestClassifyImageFilter<TInputImage, TInputVectorImage, TOutputImage>
// Add the coordinate features
if(m_Classifier->GetUseCoordinateFeatures())
for(int d = 0; d < 3; d++)
- testData.data[0][k++] = it_out.GetIndex()[d];
+ testData.data[0][k++] = it_out[0].GetIndex()[d];
// Perform classification on this data
m_Classifier->GetForest()->ApplyFast(testData, testResult, vIndex, vResult);
- // New code: compute output map with a bias parameter. The bias parameter q is such
- // that p_fore = q maps to 0 speed value. For the time being we just shift the linear
- // mapping from p_fore to speed and cap speed between -1 and 1
-
- // First we compute p_fore - for some reason not all trees in the forest have probabilities
- // summing up to one (some are zero), so we need to use division
- double p_fore_total = 0, p_total = 0;
- for(int i = 0; i < testResult.Size(); i++)
+ // How we update outputs depends on the m_GenerateClassProbabilities flag
+ if(m_GenerateClassProbabilities)
{
- HistogramType *hist = testResult[i][0];
+ std::fill(vClassProb.begin(), vClassProb.end(), 0.0);
+ double p_total = 0;
+ for(int i = 0; i < testResult.Size(); i++)
+ {
+ HistogramType *hist = testResult[i][0];
+ for(int j = 0; j < nClass; j++)
+ {
+ double p = hist->prob_[j];
+ vClassProb[j] += p;
+ p_total += p;
+ }
+ }
+
for(int j = 0; j < nClass; j++)
{
- double p = hist->prob_[j];
- if(class_weights[j] > 0.0)
- p_fore_total += p;
- p_total += p;
+ if(p_total > 0)
+ it_out[j].Set((OutputPixelType)(vClassProb[j] / p_total));
}
}
-
- // Set output only if the total probability is non-zero
- if(p_total > 0)
+ else
{
- double q = m_Classifier->GetBiasParameter();
- double p_fore = p_fore_total / p_total;
- double speed = 2 * (p_fore - q);
- if(speed < -1.0)
- speed = -1.0;
- else if(speed > 1.0)
- speed = 1.0;
-
- it_out.Set((OutputPixelType)(speed * 0x7fff));
+ // New code: compute output map with a bias parameter. The bias parameter q is such
+ // that p_fore = q maps to 0 speed value. For the time being we just shift the linear
+ // mapping from p_fore to speed and cap speed between -1 and 1
+
+ // First we compute p_fore - for some reason not all trees in the forest have probabilities
+ // summing up to one (some are zero), so we need to use division
+ double p_fore_total = 0, p_total = 0;
+ for(int i = 0; i < testResult.Size(); i++)
+ {
+ HistogramType *hist = testResult[i][0];
+ for(int j = 0; j < nClass; j++)
+ {
+ double p = hist->prob_[j];
+ if(class_weights[j] > 0.0)
+ p_fore_total += p;
+ p_total += p;
+ }
+ }
+
+ // Set output only if the total probability is non-zero
+ if(p_total > 0)
+ {
+ double q = m_Classifier->GetBiasParameter();
+ double p_fore = p_fore_total / p_total;
+ double speed = 2 * (p_fore - q);
+ if(speed < -1.0)
+ speed = -1.0;
+ else if(speed > 1.0)
+ speed = 1.0;
+
+ it_out[0].Set((OutputPixelType)(speed * 0x7fff));
+ }
}
+
+ ++cit;
+ for(int k = 0; k < nOut; k++)
+ ++it_out[k];
}
}
diff --git a/Submodules/c3d/itkextras/VectorImageTools.h b/Submodules/c3d/itkextras/VectorImageTools.h
new file mode 100644
index 0000000..8140d99
--- /dev/null
+++ b/Submodules/c3d/itkextras/VectorImageTools.h
@@ -0,0 +1,59 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: NormalizeLocalWindow.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __VectorImageTools_h_
+#define __VectorImageTools_h_
+
+#include <itkVectorImage.h>
+
+template <class TInputImage>
+typename itk::VectorImage<typename TInputImage::PixelType::ValueType, TInputImage::ImageDimension>::Pointer
+WrapImageOfVectorAsVectorImage(TInputImage *image)
+{
+ typedef typename TInputImage::PixelType VectorType;
+ typedef typename VectorType::ValueType ValueType;
+ typedef itk::VectorImage<ValueType, TInputImage::ImageDimension> VectorImageType;
+
+ // Masquerade as a vector image
+ typename VectorImageType::Pointer vecImage = VectorImageType::New();
+ vecImage->CopyInformation(image);
+ vecImage->SetRegions(image->GetBufferedRegion());
+ vecImage->SetNumberOfComponentsPerPixel(VectorType::Dimension);
+
+ // Transfer the pixel container without copying memory
+ typedef typename VectorImageType::PixelContainer OutPixelContainer;
+ typename OutPixelContainer::Pointer pcon = OutPixelContainer::New();
+ pcon->SetImportPointer(
+ reinterpret_cast<ValueType *>(image->GetPixelContainer()->GetImportPointer()),
+ image->GetPixelContainer()->Size() * VectorType::Dimension);
+ pcon->SetContainerManageMemory(false);
+ vecImage->SetPixelContainer(pcon);
+
+ // Return the image
+ return vecImage;
+}
+
+
+
+#endif
diff --git a/Submodules/c3d/itkextras/VoxBoIO/CMakeLists.txt b/Submodules/c3d/itkextras/VoxBoIO/CMakeLists.txt
new file mode 100644
index 0000000..426be21
--- /dev/null
+++ b/Submodules/c3d/itkextras/VoxBoIO/CMakeLists.txt
@@ -0,0 +1,7 @@
+PROJECT(VoxBoIO)
+
+INCLUDE_DIRECTORIES(${VoxBoIO_SOURCE_DIR})
+
+ADD_LIBRARY(ITKVoxBoIO
+ itkVoxBoCUBImageIO.cxx
+ itkVoxBoCUBImageIOFactory.cxx)
diff --git a/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.cxx b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.cxx
new file mode 100644
index 0000000..a01b422
--- /dev/null
+++ b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.cxx
@@ -0,0 +1,792 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkVoxBoCUBImageIO.cxx,v $
+ Language: C++
+ Date: $Date: 2012/11/01 09:19:14 $
+ Version: $Revision: 1.4 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#include "itkVoxBoCUBImageIO.h"
+#include "itkIOCommon.h"
+#include "itkExceptionObject.h"
+#include "itkMetaDataObject.h"
+#include "itkByteSwapper.h"
+#include <iostream>
+#include <list>
+#include <string>
+#include <math.h>
+
+// Commented out because zlib is not available through Insight Applications
+#ifdef SNAP_GZIP_SUPPORT
+#include <zlib.h>
+#endif
+
+inline double myround(double x)
+ { return (double) ((int) (x + 0.5)); }
+
+namespace itk {
+
+
+/**
+ * A generic reader and writer object for VoxBo files. Basically it
+ * provides uniform access to gzip and normal files
+ */
+class GenericCUBFileAdaptor
+{
+public:
+ virtual unsigned char ReadByte() = 0;
+ virtual void ReadData(void *data, unsigned long bytes) = 0;
+ virtual void WriteData(const void *data, unsigned long bytes) = 0;
+ virtual ~GenericCUBFileAdaptor() {}
+
+ std::string ReadHeader()
+ {
+ // Read everything up to the \f symbol
+ std::ostringstream oss;
+ unsigned char byte = ReadByte();
+ while(byte != '\f')
+ {
+ oss << byte;
+ byte = ReadByte();
+ }
+
+ // Read the next byte
+ unsigned char term = ReadByte();
+ if(term == '\r')
+ term = ReadByte();
+
+ // Throw exception if term is not there
+ if(term != '\n')
+ {
+ ExceptionObject exception;
+ exception.SetDescription("Header is not terminated by newline.");
+ throw exception;
+ }
+
+ // Return the header string
+ return oss.str();
+ }
+};
+
+/**
+ * A reader for gzip files
+ */
+#ifdef SNAP_GZIP_SUPPORT
+
+class CompressedCUBFileAdaptor : public GenericCUBFileAdaptor
+{
+public:
+ CompressedCUBFileAdaptor(const char *file, const char *mode)
+ {
+ m_GzFile = ::gzopen(file, mode);
+ if(m_GzFile == NULL)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("File cannot be accessed");
+ throw exception;
+ }
+ }
+
+ ~CompressedCUBFileAdaptor()
+ {
+ if(m_GzFile)
+ ::gzclose(m_GzFile);
+ }
+
+ unsigned char ReadByte()
+ {
+ int byte = ::gzgetc(m_GzFile);
+ if(byte < 0)
+ {
+ std::ostringstream oss;
+ oss << "Error reading byte from file at position: " << ::gztell(m_GzFile);
+ ExceptionObject exception;
+ exception.SetDescription(oss.str().c_str());
+ throw exception;
+ }
+ return static_cast<unsigned char>(byte);
+ }
+
+ void ReadData(void *data, unsigned long bytes)
+ {
+ if(m_GzFile == NULL)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("File cannot be read");
+ throw exception;
+ }
+
+ int bread = ::gzread(m_GzFile, data, bytes);
+ if(bread != bytes)
+ {
+ std::ostringstream oss;
+ oss << "File size does not match header: "
+ << bytes << " bytes requested but only "
+ << bread << " bytes available!" << std::endl
+ << "At file position " << ::gztell(m_GzFile);
+ ExceptionObject exception;
+ exception.SetDescription(oss.str().c_str());
+ throw exception;
+ }
+ }
+
+ void WriteData(const void *data, unsigned long bytes)
+ {
+ if(m_GzFile == NULL)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("File cannot be written");
+ throw exception;
+ }
+
+ int bwritten = ::gzwrite(m_GzFile, (void *) data, bytes);
+ if(bwritten != bytes)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("Could not write all bytes to file");
+ throw exception;
+ }
+ }
+
+private:
+ ::gzFile m_GzFile;
+};
+
+#endif // SNAP_GZIP_SUPPORT
+
+/**
+ * A reader for non-gzip files
+ */
+class DirectCUBFileAdaptor : public GenericCUBFileAdaptor
+{
+public:
+ DirectCUBFileAdaptor(const char *file, const char *mode)
+ {
+ m_File = fopen(file, mode);
+ if(m_File == NULL)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("File cannot be read");
+ throw exception;
+ }
+ }
+
+ virtual ~DirectCUBFileAdaptor()
+ {
+ if(m_File)
+ fclose(m_File);
+ }
+
+ unsigned char ReadByte()
+ {
+ int byte = fgetc(m_File);
+ if(byte == EOF)
+ {
+ std::ostringstream oss;
+ oss << "Error reading byte from file at position: " << ::ftell(m_File);
+ ExceptionObject exception;
+ exception.SetDescription(oss.str().c_str());
+ throw exception;
+ }
+ return static_cast<unsigned char>(byte);
+ }
+
+ void ReadData(void *data, unsigned long bytes)
+ {
+ if(m_File == NULL)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("File cannot be read");
+ throw exception;
+ }
+
+ unsigned long bread = fread(data, 1, bytes, m_File);
+ if(bread != bytes)
+ {
+ std::ostringstream oss;
+ oss << "File size does not match header: "
+ << bytes << " bytes requested but only "
+ << bread << " bytes available!" << std::endl
+ << "At file position " << ftell(m_File);
+ ExceptionObject exception;
+ exception.SetDescription(oss.str().c_str());
+ throw exception;
+ }
+ }
+
+ void WriteData(const void *data, unsigned long bytes)
+ {
+ if(m_File == NULL)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("File cannot be written");
+ throw exception;
+ }
+
+ unsigned long bwritten = fwrite(data, 1, bytes, m_File);
+ if(bwritten != bytes)
+ {
+ ExceptionObject exception;
+ exception.SetDescription("Could not write all bytes to file");
+ throw exception;
+ }
+ }
+private:
+ FILE *m_File;
+};
+
+
+/**
+ * A swap helper class, used to perform swapping for any input
+ * data type.
+ */
+template<typename TPixel> class VoxBoCUBImageIOSwapHelper
+{
+public:
+ typedef ImageIOBase::ByteOrder ByteOrder;
+ static void SwapIfNecessary(
+ void *buffer, unsigned long numberOfBytes, ByteOrder order)
+ {
+ if ( order == ImageIOBase::LittleEndian )
+ {
+ ByteSwapper<TPixel>::SwapRangeFromSystemToLittleEndian(
+ (TPixel*)buffer, numberOfBytes / sizeof(TPixel) );
+ }
+ else if ( order == ImageIOBase::BigEndian )
+ {
+ ByteSwapper<TPixel>::SwapRangeFromSystemToBigEndian(
+ (TPixel *)buffer, numberOfBytes / sizeof(TPixel) );
+ }
+ }
+};
+
+
+// Strings
+const char *VoxBoCUBImageIO::VB_IDENTIFIER_SYSTEM = "VB98";
+const char *VoxBoCUBImageIO::VB_IDENTIFIER_FILETYPE = "CUB1";
+const char *VoxBoCUBImageIO::VB_DIMENSIONS = "VoxDims(XYZ)";
+const char *VoxBoCUBImageIO::VB_SPACING = "VoxSizes(XYZ)";
+const char *VoxBoCUBImageIO::VB_ORIGIN = "Origin(XYZ)";
+const char *VoxBoCUBImageIO::VB_DATATYPE = "DataType";
+const char *VoxBoCUBImageIO::VB_BYTEORDER = "Byteorder";
+const char *VoxBoCUBImageIO::VB_ORIENTATION = "Orientation";
+const char *VoxBoCUBImageIO::VB_BYTEORDER_MSB = "msbfirst";
+const char *VoxBoCUBImageIO::VB_BYTEORDER_LSB = "lsbfirst";
+const char *VoxBoCUBImageIO::VB_DATATYPE_BYTE = "Byte";
+const char *VoxBoCUBImageIO::VB_DATATYPE_INT = "Integer";
+const char *VoxBoCUBImageIO::VB_DATATYPE_FLOAT = "Float";
+const char *VoxBoCUBImageIO::VB_DATATYPE_DOUBLE = "Double";
+
+/** Constructor */
+VoxBoCUBImageIO::VoxBoCUBImageIO()
+{
+ InitializeOrientationMap();
+ m_ByteOrder = BigEndian;
+ m_Reader = NULL;
+ m_Writer = NULL;
+}
+
+
+/** Destructor */
+VoxBoCUBImageIO::~VoxBoCUBImageIO()
+{
+ if(m_Reader)
+ delete m_Reader;
+ if(m_Writer)
+ delete m_Writer;
+}
+
+GenericCUBFileAdaptor *
+VoxBoCUBImageIO::CreateReader(const char *filename)
+{
+ try
+ {
+ bool compressed;
+ if(CheckExtension(filename, compressed))
+ if(compressed)
+#ifdef SNAP_GZIP_SUPPORT
+ return new CompressedCUBFileAdaptor(filename, "rb");
+#else
+ return NULL;
+#endif
+ else
+ return new DirectCUBFileAdaptor(filename, "rb");
+ else
+ return NULL;
+ }
+ catch(...)
+ {
+ return NULL;
+ }
+}
+
+GenericCUBFileAdaptor *
+VoxBoCUBImageIO::CreateWriter(const char *filename)
+{
+ try
+ {
+ bool compressed;
+ if(CheckExtension(filename, compressed))
+ if(compressed)
+#ifdef SNAP_GZIP_SUPPORT
+ return new CompressedCUBFileAdaptor(filename, "rb");
+#else
+ return NULL;
+#endif
+ else
+ return new DirectCUBFileAdaptor(filename, "wb");
+ else
+ return NULL;
+ }
+ catch(...)
+ {
+ return NULL;
+ }
+}
+
+bool VoxBoCUBImageIO::CanReadFile( const char* filename )
+{
+ // First check if the file can be read
+ GenericCUBFileAdaptor *reader = CreateReader(filename);
+ if(reader == NULL)
+ {
+ itkDebugMacro(<<"The file is not a valid CUB file");
+ return false;
+ }
+
+ // Now check the content
+ bool iscub = true;
+ try
+ {
+ // Get the header
+ std::istringstream iss(reader->ReadHeader());
+
+ // Read the first two words
+ std::string word;
+
+ // Read the first line from the file
+ iss >> word;
+ if(word != VB_IDENTIFIER_SYSTEM)
+ iscub = false;
+
+ // Read the second line
+ iss >> word;
+ if(word != VB_IDENTIFIER_FILETYPE)
+ iscub = false;
+ }
+ catch(...)
+ {
+ iscub = false;
+ }
+
+ delete reader;
+ return iscub;
+}
+
+bool VoxBoCUBImageIO::CanWriteFile( const char * name )
+{
+ bool compressed;
+ return CheckExtension(name, compressed);
+}
+
+void VoxBoCUBImageIO::Read(void* buffer)
+{
+ if(m_Reader == NULL)
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("File cannot be read");
+ throw exception;
+ }
+
+ m_Reader->ReadData(buffer, GetImageSizeInBytes());
+ this->SwapBytesIfNecessary(buffer, GetImageSizeInBytes());
+}
+
+/**
+ * Read Information about the VoxBoCUB file
+ * and put the cursor of the stream just before the first data pixel
+ */
+void VoxBoCUBImageIO::ReadImageInformation()
+{
+ // Make sure there is no other reader
+ if(m_Reader)
+ delete m_Reader;
+
+ // Create a reader
+ m_Reader = CreateReader(m_FileName.c_str());
+ if(m_Reader == NULL)
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("File cannot be read");
+ throw exception;
+ }
+
+ // Set the number of dimensions to three
+ SetNumberOfDimensions(3);
+
+ // Read the file header
+ std::istringstream issHeader(m_Reader->ReadHeader());
+
+ // Read every string in the header. Parse the strings that are special
+ while(issHeader.good())
+ {
+ // Read a line from the stream
+ char linebuffer[512];
+ issHeader.getline(linebuffer, 512);
+
+ // Get the key string
+ std::istringstream iss(linebuffer);
+ std::string key;
+
+ // Read the key and strip the colon from it
+ iss >> key;
+ if(key[key.size() - 1] == ':')
+ {
+ // Strip the colon off the key
+ key = key.substr(0, key.size() - 1);
+
+ // Check if this is a relevant key
+ if(key == VB_DIMENSIONS)
+ {
+ iss >> m_Dimensions[0];
+ iss >> m_Dimensions[1];
+ iss >> m_Dimensions[2];
+ }
+
+ else if(key == VB_SPACING)
+ {
+ iss >> m_Spacing[0];
+ iss >> m_Spacing[1];
+ iss >> m_Spacing[2];
+ }
+
+ else if(key == VB_ORIGIN)
+ {
+ double ox, oy, oz;
+ iss >> ox; iss >> oy; iss >> oz;
+ m_Origin[0] = ox * m_Spacing[0];
+ m_Origin[1] = oy * m_Spacing[1];
+ m_Origin[2] = oz * m_Spacing[2];
+ }
+
+ else if(key == VB_DATATYPE)
+ {
+ std::string type;
+ iss >> type;
+ m_PixelType = SCALAR;
+ if(type == VB_DATATYPE_BYTE)
+ m_ComponentType = UCHAR;
+ else if(type == VB_DATATYPE_INT)
+ m_ComponentType = USHORT;
+ else if(type == VB_DATATYPE_FLOAT)
+ m_ComponentType = FLOAT;
+ else if(type == VB_DATATYPE_DOUBLE)
+ m_ComponentType = DOUBLE;
+ }
+
+ else if(key == VB_BYTEORDER)
+ {
+ std::string type;
+ iss >> type;
+ if(type == VB_BYTEORDER_MSB)
+ SetByteOrderToBigEndian();
+ else if(type == VB_BYTEORDER_LSB)
+ SetByteOrderToLittleEndian();
+ else
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("Unknown byte order constant");
+ throw exception;
+ }
+ }
+
+ /*
+ else if(key == VB_ORIENTATION)
+ {
+ std::string code;
+ iss >> code;
+
+ // Set the orientation code in the data dictionary
+ OrientationMap::const_iterator it = m_OrientationMap.find(code);
+ if(it != m_OrientationMap.end())
+ {
+ itk::MetaDataDictionary &dic =this->GetMetaDataDictionary();
+ EncapsulateMetaData<OrientationFlags>(
+ dic, ITK_CoordinateOrientation, it->second);
+ }
+ }
+ */
+
+ else
+ {
+ // Encode the right hand side of the string in the meta-data dic
+ std::string word;
+ std::ostringstream oss;
+ while(iss >> word)
+ {
+ if(oss.str().size())
+ oss << " ";
+ oss << word;
+ }
+ itk::MetaDataDictionary &dic =this->GetMetaDataDictionary();
+ EncapsulateMetaData<std::string>(dic, key, oss.str());
+ }
+ }
+ }
+}
+
+void
+VoxBoCUBImageIO
+::WriteImageInformation(void)
+{
+ // See if we have a writer already
+ if(m_Writer != NULL)
+ delete m_Writer;
+
+ // First check if the file can be written to
+ m_Writer = CreateWriter(m_FileName.c_str());
+ if(m_Writer == NULL)
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("File cannot be read");
+ throw exception;
+ }
+
+ // Check that the number of dimensions is correct
+ if(GetNumberOfDimensions() != 3)
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("Unsupported number of dimensions");
+ throw exception;
+ }
+
+ // Put together a header
+ std::ostringstream header;
+
+ // Write the identifiers
+ header << VB_IDENTIFIER_SYSTEM << std::endl;
+ header << VB_IDENTIFIER_FILETYPE << std::endl;
+
+ // Write the image dimensions
+ header << VB_DIMENSIONS << ": "
+ << m_Dimensions[0] << " "
+ << m_Dimensions[1] << " "
+ << m_Dimensions[2] << std::endl;
+
+ // Write the spacing
+ header << VB_SPACING << ": "
+ << m_Spacing[0] << " "
+ << m_Spacing[1] << " "
+ << m_Spacing[2] << std::endl;
+
+ // Write the origin (have to convert to bytes)
+ header << VB_ORIGIN << ": "
+ << (int) myround(m_Origin[0] / m_Spacing[0]) << " "
+ << (int) myround(m_Origin[1] / m_Spacing[1]) << " "
+ << (int) myround(m_Origin[2] / m_Spacing[2]) << std::endl;
+
+ // Write the byte order
+ header << VB_BYTEORDER << ": "
+ << (ByteSwapper<char>::SystemIsBigEndian()
+ ? VB_BYTEORDER_MSB : VB_BYTEORDER_LSB) << std::endl;
+
+ // Write the data type
+ switch(m_ComponentType)
+ {
+ case CHAR:
+ case UCHAR:
+ header << VB_DATATYPE << ": " << VB_DATATYPE_BYTE << std::endl;
+ break;
+ case SHORT:
+ case USHORT:
+ header << VB_DATATYPE << ": " << VB_DATATYPE_INT << std::endl;
+ break;
+ case FLOAT:
+ header << VB_DATATYPE << ": " << VB_DATATYPE_FLOAT << std::endl;
+ break;
+ case DOUBLE:
+ header << VB_DATATYPE << ": " << VB_DATATYPE_DOUBLE << std::endl;
+ break;
+ default:
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("Unsupported pixel component type");
+ throw exception;
+ }
+
+ // Write the orientation code
+ /*
+ MetaDataDictionary &dic = GetMetaDataDictionary();
+ OrientationFlags oflag;
+ if(ExposeMetaData<OrientationFlags>(dic, ITK_CoordinateOrientation, oflag))
+ {
+ InverseOrientationMap::const_iterator it =
+ m_InverseOrientationMap.find(oflag);
+ if(it != m_InverseOrientationMap.end())
+ header << VB_ORIENTATION << ": " << it->second << std::endl;
+ }
+ */
+
+ // Write the terminating characters
+ header << "\f\n";
+
+ // Write the header to the file as data
+ m_Writer->WriteData(header.str().c_str(), header.str().size());
+}
+
+/** The write function is not implemented */
+void
+VoxBoCUBImageIO
+::Write( const void* buffer)
+{
+ WriteImageInformation();
+ m_Writer->WriteData(buffer, GetImageSizeInBytes());
+}
+
+/** Print Self Method */
+void VoxBoCUBImageIO::PrintSelf(std::ostream& os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+ os << indent << "PixelType " << m_PixelType << "\n";
+}
+
+
+bool VoxBoCUBImageIO::CheckExtension(const char* filename, bool &isCompressed)
+{
+ std::string fname = filename;
+ if ( fname == "" )
+ {
+ itkDebugMacro(<< "No filename specified.");
+ return false;
+ }
+
+ bool extensionFound = false;
+ isCompressed = false;
+
+ std::string::size_type giplPos = fname.rfind(".cub");
+ if ((giplPos != std::string::npos)
+ && (giplPos == fname.length() - 4))
+ {
+ extensionFound = true;
+ }
+
+ giplPos = fname.rfind(".cub.gz");
+ if ((giplPos != std::string::npos)
+ && (giplPos == fname.length() - 7))
+ {
+ extensionFound = true;
+ isCompressed = true;
+ }
+
+ return extensionFound;
+}
+
+void
+VoxBoCUBImageIO
+::InitializeOrientationMap()
+{
+ m_OrientationMap["RIP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIP;
+ m_OrientationMap["LIP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIP;
+ m_OrientationMap["RSP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP;
+ m_OrientationMap["LSP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSP;
+ m_OrientationMap["RIA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIA;
+ m_OrientationMap["LIA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIA;
+ m_OrientationMap["RSA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSA;
+ m_OrientationMap["LSA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSA;
+ m_OrientationMap["IRP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRP;
+ m_OrientationMap["ILP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILP;
+ m_OrientationMap["SRP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRP;
+ m_OrientationMap["SLP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLP;
+ m_OrientationMap["IRA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRA;
+ m_OrientationMap["ILA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILA;
+ m_OrientationMap["SRA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRA;
+ m_OrientationMap["SLA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLA;
+ m_OrientationMap["RPI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI;
+ m_OrientationMap["LPI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPI;
+ m_OrientationMap["RAI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI;
+ m_OrientationMap["LAI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI;
+ m_OrientationMap["RPS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPS;
+ m_OrientationMap["LPS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS;
+ m_OrientationMap["RAS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS;
+ m_OrientationMap["LAS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAS;
+ m_OrientationMap["PRI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRI;
+ m_OrientationMap["PLI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLI;
+ m_OrientationMap["ARI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARI;
+ m_OrientationMap["ALI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALI;
+ m_OrientationMap["PRS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRS;
+ m_OrientationMap["PLS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLS;
+ m_OrientationMap["ARS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARS;
+ m_OrientationMap["ALS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALS;
+ m_OrientationMap["IPR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPR;
+ m_OrientationMap["SPR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPR;
+ m_OrientationMap["IAR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAR;
+ m_OrientationMap["SAR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAR;
+ m_OrientationMap["IPL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPL;
+ m_OrientationMap["SPL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPL;
+ m_OrientationMap["IAL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAL;
+ m_OrientationMap["SAL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAL;
+ m_OrientationMap["PIR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIR;
+ m_OrientationMap["PSR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSR;
+ m_OrientationMap["AIR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIR;
+ m_OrientationMap["ASR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASR;
+ m_OrientationMap["PIL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIL;
+ m_OrientationMap["PSL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSL;
+ m_OrientationMap["AIL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIL;
+ m_OrientationMap["ASL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASL;
+
+ OrientationMap::const_iterator it;
+ for(it = m_OrientationMap.begin(); it != m_OrientationMap.end(); ++it)
+ m_InverseOrientationMap[it->second] = it->first;
+
+}
+
+void
+VoxBoCUBImageIO
+::SwapBytesIfNecessary(void *buffer, unsigned long numberOfBytes)
+{
+ if(m_ComponentType == CHAR)
+ VoxBoCUBImageIOSwapHelper<char>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == UCHAR)
+ VoxBoCUBImageIOSwapHelper<unsigned char>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == SHORT)
+ VoxBoCUBImageIOSwapHelper<short>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == USHORT)
+ VoxBoCUBImageIOSwapHelper<unsigned short>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == INT)
+ VoxBoCUBImageIOSwapHelper<int>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == UINT)
+ VoxBoCUBImageIOSwapHelper<unsigned int>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == LONG)
+ VoxBoCUBImageIOSwapHelper<long>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == ULONG)
+ VoxBoCUBImageIOSwapHelper<unsigned long>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == FLOAT)
+ VoxBoCUBImageIOSwapHelper<float>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else if(m_ComponentType == DOUBLE)
+ VoxBoCUBImageIOSwapHelper<double>::SwapIfNecessary(
+ buffer, numberOfBytes, m_ByteOrder);
+ else
+ {
+ ExceptionObject exception(__FILE__, __LINE__);
+ exception.SetDescription("Pixel Type Unknown");
+ throw exception;
+ }
+}
+
+} // end namespace itk
diff --git a/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.h b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.h
new file mode 100644
index 0000000..7db236f
--- /dev/null
+++ b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIO.h
@@ -0,0 +1,128 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkVoxBoCUBImageIO.h,v $
+ Language: C++
+ Date: $Date: 2009/11/10 16:19:49 $
+ Version: $1.0$
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkVoxBoCUBImageIO_h
+#define __itkVoxBoCUBImageIO_h
+
+#ifdef _MSC_VER
+#pragma warning ( disable : 4786 )
+#endif
+
+#include <fstream>
+#include <string>
+#include <map>
+#include "itkImageIOBase.h"
+#include "itkSpatialOrientation.h"
+#include <stdio.h>
+
+namespace itk
+{
+ class GenericCUBFileAdaptor;
+
+/** \class VoxBoCUBImageIO
+ *
+ * \brief Read VoxBoCUBImage file format.
+ *
+ * \ingroup IOFilters
+ *
+ */
+class ITK_EXPORT VoxBoCUBImageIO : public ImageIOBase
+{
+public:
+ /** Standard class typedefs. */
+ typedef VoxBoCUBImageIO Self;
+ typedef ImageIOBase Superclass;
+ typedef SmartPointer<Self> Pointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(VoxBoCUBImageIO, Superclass);
+
+ /*-------- This part of the interfaces deals with reading data. ----- */
+
+ /** Determine the file type. Returns true if this ImageIO can read the
+ * file specified. */
+ virtual bool CanReadFile(const char*) ;
+
+ /** Set the spacing and dimension information for the set filename. */
+ virtual void ReadImageInformation();
+
+ /** Reads the data from disk into the memory buffer provided. */
+ virtual void Read(void* buffer);
+
+ /*-------- This part of the interfaces deals with writing data. ----- */
+
+ /** Determine the file type. Returns true if this ImageIO can write the
+ * file specified. */
+ virtual bool CanWriteFile(const char*);
+
+ /** Set the spacing and dimension information for the set filename. */
+ virtual void WriteImageInformation();
+
+ /** Writes the data to disk from the memory buffer provided. Make sure
+ * that the IORegions has been set properly. */
+ virtual void Write(const void* buffer);
+
+
+ VoxBoCUBImageIO();
+ virtual ~VoxBoCUBImageIO();
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+private:
+ VoxBoCUBImageIO(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ bool CheckExtension(const char*, bool &isCompressed);
+ GenericCUBFileAdaptor *CreateReader(const char *filename);
+ GenericCUBFileAdaptor *CreateWriter(const char *filename);
+ GenericCUBFileAdaptor *m_Reader, *m_Writer;
+
+ // Initialize the orientation map (from strings to ITK)
+ void InitializeOrientationMap();
+
+ // Orientation stuff
+ typedef SpatialOrientation::ValidCoordinateOrientationFlags OrientationFlags;
+ typedef std::map<std::string, OrientationFlags> OrientationMap;
+ typedef std::map<OrientationFlags, std::string> InverseOrientationMap;
+
+ OrientationMap m_OrientationMap;
+ InverseOrientationMap m_InverseOrientationMap;
+
+ // Method to swap bytes in read buffer
+ void SwapBytesIfNecessary(void *buffer, unsigned long numberOfBytes);
+
+ // Strings used in VoxBo files
+ static const char *VB_IDENTIFIER_SYSTEM;
+ static const char *VB_IDENTIFIER_FILETYPE;
+ static const char *VB_DIMENSIONS;
+ static const char *VB_SPACING;
+ static const char *VB_ORIGIN;
+ static const char *VB_DATATYPE;
+ static const char *VB_BYTEORDER;
+ static const char *VB_ORIENTATION;
+ static const char *VB_BYTEORDER_MSB;
+ static const char *VB_BYTEORDER_LSB;
+ static const char *VB_DATATYPE_BYTE;
+ static const char *VB_DATATYPE_INT;
+ static const char *VB_DATATYPE_FLOAT;
+ static const char *VB_DATATYPE_DOUBLE;
+};
+
+} // end namespace itk
+
+#endif // __itkVoxBoCUBImageIO_h
diff --git a/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIOFactory.cxx b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIOFactory.cxx
new file mode 100644
index 0000000..c4f9126
--- /dev/null
+++ b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIOFactory.cxx
@@ -0,0 +1,52 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkVoxBoCUBImageIOFactory.cxx,v $
+ Language: C++
+ Date: $Date: 2008/11/08 01:42:03 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#include "itkVoxBoCUBImageIOFactory.h"
+#include "itkCreateObjectFunction.h"
+#include "itkVoxBoCUBImageIO.h"
+#include "itkVersion.h"
+
+
+namespace itk
+{
+
+VoxBoCUBImageIOFactory::VoxBoCUBImageIOFactory()
+{
+ this->RegisterOverride("itkImageIOBase",
+ "itkVoxBoCUBImageIO",
+ "VoxBo CUB Image IO",
+ 1,
+ CreateObjectFunction<VoxBoCUBImageIO>::New());
+}
+
+VoxBoCUBImageIOFactory::~VoxBoCUBImageIOFactory()
+{
+}
+
+const char*
+VoxBoCUBImageIOFactory::GetITKSourceVersion(void) const
+{
+ return ITK_SOURCE_VERSION;
+}
+
+const char*
+VoxBoCUBImageIOFactory::GetDescription() const
+{
+ return "VoxBo CUB ImageIO Factory, allows the loading of VoxBoCUB images into Insight";
+}
+
+} // end namespace itk
+
diff --git a/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIOFactory.h b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIOFactory.h
new file mode 100644
index 0000000..8d03f05
--- /dev/null
+++ b/Submodules/c3d/itkextras/VoxBoIO/itkVoxBoCUBImageIOFactory.h
@@ -0,0 +1,67 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkVoxBoCUBImageIOFactory.h,v $
+ Language: C++
+ Date: $Date: 2008/11/08 01:42:03 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkVoxBoCUBImageIOFactory_h
+#define __itkVoxBoCUBImageIOFactory_h
+
+#include "itkObjectFactoryBase.h"
+#include "itkImageIOBase.h"
+
+namespace itk
+{
+/** \class VoxBoCUBImageIOFactory
+ * \brief Create instances of VoxBoCUBImageIO objects using an object factory.
+ */
+class ITK_EXPORT VoxBoCUBImageIOFactory : public ObjectFactoryBase
+{
+public:
+ /** Standard class typedefs. */
+ typedef VoxBoCUBImageIOFactory Self;
+ typedef ObjectFactoryBase Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Class methods used to interface with the registered factories. */
+ virtual const char* GetITKSourceVersion(void) const;
+ virtual const char* GetDescription(void) const;
+
+ /** Method for class instantiation. */
+ itkFactorylessNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(VoxBoCUBImageIOFactory, ObjectFactoryBase);
+
+ /** Register one factory of this type */
+ static void RegisterOneFactory(void)
+ {
+ VoxBoCUBImageIOFactory::Pointer VoxBoCUBFactory = VoxBoCUBImageIOFactory::New();
+ ObjectFactoryBase::RegisterFactory(VoxBoCUBFactory);
+ }
+
+protected:
+ VoxBoCUBImageIOFactory();
+ ~VoxBoCUBImageIOFactory();
+
+private:
+ VoxBoCUBImageIOFactory(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+};
+
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/c3d/itkextras/gsGSAffine3DTransform.h b/Submodules/c3d/itkextras/gsGSAffine3DTransform.h
new file mode 100644
index 0000000..c2df53b
--- /dev/null
+++ b/Submodules/c3d/itkextras/gsGSAffine3DTransform.h
@@ -0,0 +1,159 @@
+#ifndef __gsGSAffine3DTransform_h
+#define __gsGSAffine3DTransform_h
+
+#include <iostream>
+#include "itkRigid3DTransform.h"
+#include "vnl/vnl_quaternion.h"
+
+namespace itk
+{
+
+/** \brief GSAffine3DTransform of a vector space (e.g. space coordinates).
+ *
+ * This transform applies a rotation and translation to the space
+ *
+ * \ingroup Transforms
+ */
+template < class TScalarType=double > // Data type for scalars (float or double)
+class ITK_EXPORT GSAffine3DTransform :
+ public MatrixOffsetTransformBase< TScalarType, 3, 3>
+ // public Rigid3DTransform< TScalarType >
+{
+public:
+ /** Standard class typedefs. */
+ typedef GSAffine3DTransform Self;
+// typedef Rigid3DTransform< TScalarType > Superclass;
+ typedef MatrixOffsetTransformBase< TScalarType, 3, 3 > Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** New macro for creation of through a Smart Pointer */
+ itkNewMacro( Self );
+
+ /** Run-time type information (and related methods). */
+ // itkTypeMacro( GSAffine3DTransform, Rigid3DTransform );
+ itkTypeMacro( GSAffine3DTransform, MatrixOffsetTransformBase );
+
+ /** Dimension of parameters */
+ itkStaticConstMacro(InputSpaceDimension, unsigned int, 3);
+ itkStaticConstMacro(OutputSpaceDimension, unsigned int, 3);
+ itkStaticConstMacro(SpaceDimension, unsigned int, 3);
+ itkStaticConstMacro(ParametersDimension, unsigned int, 13);
+
+ /** Parameters Type */
+ typedef typename Superclass::ParametersType ParametersType;
+ typedef typename Superclass::JacobianType JacobianType;
+ typedef typename Superclass::ScalarType ScalarType;
+ typedef typename Superclass::InputPointType InputPointType;
+ typedef typename Superclass::OutputPointType OutputPointType;
+ typedef typename Superclass::InputVectorType InputVectorType;
+ typedef typename Superclass::OutputVectorType OutputVectorType;
+ typedef typename Superclass::InputVnlVectorType InputVnlVectorType;
+ typedef typename Superclass::OutputVnlVectorType OutputVnlVectorType;
+ typedef typename Superclass::InputCovariantVectorType
+ InputCovariantVectorType;
+ typedef typename Superclass::OutputCovariantVectorType
+ OutputCovariantVectorType;
+ typedef typename Superclass::MatrixType MatrixType;
+ typedef typename Superclass::InverseMatrixType InverseMatrixType;
+ typedef typename Superclass::CenterType CenterType;
+ typedef typename Superclass::OffsetType OffsetType;
+ typedef typename Superclass::TranslationType TranslationType;
+
+
+ /** VnlQuaternion type. */
+ typedef vnl_quaternion<TScalarType> VnlQuaternionType;
+
+ /** Compute the Jacobian Matrix of the transformation at one point */
+ /** Set the rotation of the rigid transform.
+ * This method sets the rotation of a GSAffine3DTransform to a
+ * value specified by the user. */
+ void SetRotation(const VnlQuaternionType &rotation);
+ void SetS1(const TScalarType S1);
+ void SetS2(const TScalarType S2);
+ void SetS3(const TScalarType S3);
+ void SetK1(const TScalarType K1);
+ void SetK2(const TScalarType K2);
+ void SetK3(const TScalarType K3);
+
+
+ /** Get the rotation from an GSAffine3DTransform.
+ * This method returns the value of the rotation of the
+ * GSAffine3DTransform. **/
+ const VnlQuaternionType & GetRotation(void) const
+ { return m_Rotation; }
+
+ MatrixType ComputeMyRotationMatrix();
+
+ itkGetConstReferenceMacro( S1, TScalarType );
+ itkGetConstReferenceMacro( S2, TScalarType );
+ itkGetConstReferenceMacro( S3, TScalarType );
+ itkGetConstReferenceMacro( K1, TScalarType );
+ itkGetConstReferenceMacro( K2, TScalarType );
+ itkGetConstReferenceMacro( K3, TScalarType );
+
+
+
+ /** Set the parameters to the IdentityTransform */
+ virtual void SetIdentity(void);
+
+ /** Set the transformation from a container of parameters.
+ * This is typically used by optimizers.
+ * There are 7 parameters. The first four represents the
+ * quaternion and the last three represents the
+ * offset. */
+ void SetParameters( const ParametersType & parameters );
+ virtual const ParametersType & GetParameters() const;
+
+ /** Compute the Jacobian of the transformation.
+ * This method computes the Jacobian matrix of the transformation.
+ * given point or vector, returning the transformed point or
+ * vector. The rank of the Jacobian will also indicate if the transform
+ * is invertible at this point. */
+ const JacobianType & GetJacobian(const InputPointType &point ) const;
+
+protected:
+/* GSAffine3DTransform(const MatrixType &matrix, */
+/* const OutputVectorType &offset); */
+ GSAffine3DTransform(unsigned int outputDims,
+ unsigned int paramDims);
+ GSAffine3DTransform();
+ ~GSAffine3DTransform(){};
+
+ void ComputeMatrix();
+
+// void ComputeMatrixParameters();
+
+ void SetVarRotation(const VnlQuaternionType & rotation)
+ { m_Rotation = rotation; };
+
+// const InverseMatrixType & GetInverseMatrix( void ) const;
+
+ void PrintSelf(std::ostream &os, Indent indent) const;
+
+private:
+ GSAffine3DTransform(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ /** Rotation of the transformation. */
+ VnlQuaternionType m_Rotation;
+
+ /** added affine parameters **/
+ TScalarType m_S1;
+ TScalarType m_S2;
+ TScalarType m_S3;
+ TScalarType m_K1;
+ TScalarType m_K2;
+ TScalarType m_K3;
+
+}; //class GSAffine3DTransform
+
+
+} // namespace itk
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "gsGSAffine3DTransform.txx"
+#endif
+
+#endif /* __gsGSAffine3DTransform_h */
diff --git a/Submodules/c3d/itkextras/gsGSAffine3DTransform.txx b/Submodules/c3d/itkextras/gsGSAffine3DTransform.txx
new file mode 100644
index 0000000..cd51300
--- /dev/null
+++ b/Submodules/c3d/itkextras/gsGSAffine3DTransform.txx
@@ -0,0 +1,501 @@
+#ifndef _gsGSAffine3DTransform_txx
+#define _gsGSAffine3DTransform_txx
+
+#include "gsGSAffine3DTransform.h"
+
+
+namespace itk
+{
+
+// Constructor with default arguments
+template <class TScalarType>
+GSAffine3DTransform<TScalarType>
+::GSAffine3DTransform() :
+ Superclass(SpaceDimension, ParametersDimension)
+{
+ m_Rotation = VnlQuaternionType(0,0,0,1); // axis * vcl_sin(t/2), vcl_cos(t/2)
+ m_S1 = NumericTraits< TScalarType >::One;
+ m_S2 = NumericTraits< TScalarType >::One;
+ m_S3 = NumericTraits< TScalarType >::One;
+ m_K1 = NumericTraits< TScalarType >::Zero;
+ m_K2 = NumericTraits< TScalarType >::Zero;
+ m_K3 = NumericTraits< TScalarType >::Zero;
+}
+
+// Constructor with default arguments
+template<class TScalarType>
+GSAffine3DTransform<TScalarType>::
+GSAffine3DTransform( unsigned int outputSpaceDimension,
+ unsigned int parametersDimension ) :
+ Superclass(outputSpaceDimension, parametersDimension)
+{
+ m_Rotation = VnlQuaternionType(0,0,0,1); // axis * vcl_sin(t/2), vcl_cos(t/2)
+ m_S1 = NumericTraits< TScalarType >::One;
+ m_S2 = NumericTraits< TScalarType >::One;
+ m_S3 = NumericTraits< TScalarType >::One;
+ m_K1 = NumericTraits< TScalarType >::Zero;
+ m_K2 = NumericTraits< TScalarType >::Zero;
+ m_K3 = NumericTraits< TScalarType >::Zero;
+}
+
+
+
+// // Constructor with explicit arguments
+// template<class TScalarType>
+// GSAffine3DTransform<TScalarType>::
+// GSAffine3DTransform( const MatrixType & matrix,
+// const OutputVectorType & offset ) :
+// Superclass(matrix, offset)
+// {
+// this->ComputeMatrixParameters();
+// }
+
+
+// Print self
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+PrintSelf(std::ostream &os, Indent indent ) const
+{
+ Superclass::PrintSelf(os,indent);
+ os << indent << "Rotation: " << m_Rotation << std::endl;
+ os << indent << "S1, S2, S3: " << m_S1 <<", "<<m_S2<<", "<<m_S3 << std::endl;
+ os << indent << "K1, K2, K3: " << m_K1 <<", "<<m_K2<<", "<<m_K3 << std::endl;
+}
+
+
+// Set rotation
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetRotation(const VnlQuaternionType &rotation )
+{
+ m_Rotation = rotation;
+
+ this->ComputeMatrix();
+ this->ComputeOffset();
+ this->Modified();
+
+ return;
+}
+
+
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetS1(const TScalarType S1)
+{
+ m_S1 = S1;
+
+ this->ComputeMatrix();
+ this->ComputeOffset();
+ this->Modified();
+
+ return;
+}
+
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetS2(const TScalarType S2)
+{
+ m_S2 = S2;
+
+ this->ComputeMatrix();
+ this->ComputeOffset();
+ this->Modified();
+
+ return;
+}
+
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetS3(const TScalarType S3)
+{
+ m_S3 = S3;
+
+ this->ComputeMatrix();
+ this->ComputeOffset();
+ this->Modified();
+
+ return;
+}
+
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetK1(const TScalarType K1)
+{
+ m_K1 = K1;
+
+ this->ComputeMatrix();
+ this->ComputeOffset();
+ this->Modified();
+
+ return;
+}
+
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetK2(const TScalarType K2)
+{
+ m_K2 = K2;
+
+ this->ComputeMatrix();
+ this->ComputeOffset();
+ this->Modified();
+
+ return;
+}
+
+
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetK3(const TScalarType K3)
+{
+ m_K3 = K3;
+
+ this->ComputeMatrix();
+ this->ComputeOffset();
+ this->Modified();
+
+ return;
+}
+
+
+// Set the parameters in order to fit an Identity transform
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+SetIdentity( void )
+{
+ m_Rotation = VnlQuaternionType(0,0,0,1);
+ m_S1 = NumericTraits< TScalarType >::One;
+ m_S2 = NumericTraits< TScalarType >::One;
+ m_S3 = NumericTraits< TScalarType >::One;
+ m_K1 = NumericTraits< TScalarType >::Zero;
+ m_K2 = NumericTraits< TScalarType >::Zero;
+ m_K3 = NumericTraits< TScalarType >::Zero;
+ this->Superclass::SetIdentity();
+}
+
+
+// Set Parameters
+template <class TScalarType>
+void
+GSAffine3DTransform<TScalarType>
+::SetParameters( const ParametersType & parameters )
+{
+ OutputVectorType translation;
+
+ // Transfer the quaternion part
+ unsigned int par = 0;
+
+ for(unsigned int j=0; j < 4; j++)
+ {
+ m_Rotation[j] = parameters[par];
+ ++par;
+ }
+
+ m_S1 = parameters[par++];
+ m_S2 = parameters[par++];
+ m_S3 = parameters[par++];
+ m_K1 = parameters[par++];
+ m_K2 = parameters[par++];
+ m_K3 = parameters[par++];
+
+ this->ComputeMatrix();
+
+ // Transfer the constant part
+ for(unsigned int i=0; i < SpaceDimension; i++)
+ {
+ translation[i] = parameters[par];
+ ++par;
+ }
+ this->SetVarTranslation( translation );
+
+
+
+ this->ComputeOffset();
+
+ // Modified is always called since we just have a pointer to the
+ // parameters and cannot know if the parameters have changed.
+ this->Modified();
+
+}
+
+
+
+// Set Parameters
+template <class TScalarType>
+const
+typename GSAffine3DTransform<TScalarType>::ParametersType &
+GSAffine3DTransform<TScalarType>
+::GetParameters() const
+{
+ VnlQuaternionType quaternion = this->GetRotation();
+ OutputVectorType translation = this->GetTranslation();
+
+ // Transfer the quaternion part
+ unsigned int par = 0;
+
+ for(unsigned int j=0; j < 4; j++)
+ {
+ this->m_Parameters[par] = quaternion[j];
+ ++par;
+ }
+
+
+ this->m_Parameters[par++] = m_S1;
+ this->m_Parameters[par++] = m_S2;
+ this->m_Parameters[par++] = m_S3;
+ this->m_Parameters[par++] = m_K1;
+ this->m_Parameters[par++] = m_K2;
+ this->m_Parameters[par++] = m_K3;
+
+ // Transfer the constant part
+ for(unsigned int i=0; i < SpaceDimension; i++)
+ {
+ this->m_Parameters[par] = translation[i];
+ ++par;
+ }
+
+ return this->m_Parameters;
+}
+
+
+// Get parameters
+template<class TScalarType>
+const typename GSAffine3DTransform<TScalarType>::JacobianType &
+GSAffine3DTransform<TScalarType>::
+GetJacobian( const InputPointType & p ) const
+{
+
+
+ this->m_Jacobian.Fill(0.0);
+
+
+ TScalarType c1 = this->GetCenter()[0];
+ TScalarType c2 = this->GetCenter()[1];
+ TScalarType c3 = this->GetCenter()[2];
+ TScalarType s1 = this->m_S1;
+ TScalarType s2 = this->m_S2;
+ TScalarType s3 = this->m_S3;
+ TScalarType k1 = this->m_K1;
+ TScalarType k2 = this->m_K2;
+ TScalarType k3 = this->m_K3;
+ TScalarType x1 = p[0];
+ TScalarType x2 = p[1];
+ TScalarType x3 = p[2];
+
+ // z1,z2,z3 is the S*K*point p
+ TScalarType w1 = (x1-c1)+k1*(x2-c2)+k2*(x3-c3);
+ TScalarType w2 = (x2-c2)+k3*(x3-c2);
+ TScalarType w3 = (x3-c3);
+
+ TScalarType z1 = s1*w1;
+ TScalarType z2 = s2*w2;
+ TScalarType z3 = s3*w3;
+
+ // compute Jacobian with respect to quaternion parameters
+ this->m_Jacobian[0][0] = 2.0 * ( m_Rotation.x() * z1 + m_Rotation.y() * z2
+ + m_Rotation.z() * z3 );
+ this->m_Jacobian[0][1] = 2.0 * (- m_Rotation.y() * z1 + m_Rotation.x() * z2
+ + m_Rotation.r() * z3 );
+ this->m_Jacobian[0][2] = 2.0 * (- m_Rotation.z() * z1 - m_Rotation.r() * z2
+ + m_Rotation.x() * z3 );
+ this->m_Jacobian[0][3] = - 2.0 * (- m_Rotation.r() * z1 + m_Rotation.z() * z2
+ - m_Rotation.y() * z3 );
+
+ this->m_Jacobian[1][0] = - this->m_Jacobian[0][1];
+ this->m_Jacobian[1][1] = this->m_Jacobian[0][0];
+ this->m_Jacobian[1][2] = this->m_Jacobian[0][3];
+ this->m_Jacobian[1][3] = - this->m_Jacobian[0][2];
+
+ this->m_Jacobian[2][0] = - this->m_Jacobian[0][2];
+ this->m_Jacobian[2][1] = - this->m_Jacobian[0][3];
+ this->m_Jacobian[2][2] = this->m_Jacobian[0][0];
+ this->m_Jacobian[2][3] = this->m_Jacobian[0][1];
+
+ // get rotation matrix first
+ // this is done to compensate for the transposed representation
+ // between VNL and ITK
+ VnlQuaternionType conjugateRotation = m_Rotation.conjugate();
+ MatrixType newMatrix;
+ newMatrix = conjugateRotation.rotation_matrix_transpose();
+
+ TScalarType r11 = newMatrix[0][0];
+ TScalarType r12 = newMatrix[0][1];
+ TScalarType r13 = newMatrix[0][2];
+ TScalarType r21 = newMatrix[1][0];
+ TScalarType r22 = newMatrix[1][1];
+ TScalarType r23 = newMatrix[1][2];
+ TScalarType r31 = newMatrix[2][0];
+ TScalarType r32 = newMatrix[2][1];
+ TScalarType r33 = newMatrix[2][2];
+
+ // compute Jacobian wrt S1/S2/S3
+ this->m_Jacobian[0][4] = r11 * w1;
+ this->m_Jacobian[0][5] = r12 * w2;
+ this->m_Jacobian[0][6] = r13 * w3;
+ this->m_Jacobian[1][4] = r21 * w1;
+ this->m_Jacobian[1][5] = r22 * w2;
+ this->m_Jacobian[1][6] = r23 * w3;
+ this->m_Jacobian[2][4] = r31 * w1;
+ this->m_Jacobian[2][5] = r32 * w2;
+ this->m_Jacobian[2][6] = r33 * w3;
+
+ // compute Jacobian wrt K1/K2/K3
+ this->m_Jacobian[0][7] = r11 * s1 * (x2-c2);
+ this->m_Jacobian[0][8] = r11 * s1 * (x3-c3);
+ this->m_Jacobian[0][9] = r12 * s2 * (x3-c3);
+ this->m_Jacobian[1][7] = r21 * s1 * (x2-c2);
+ this->m_Jacobian[1][8] = r21 * s1 * (x3-c3);
+ this->m_Jacobian[1][9] = r22 * s2 * (x3-c3);
+ this->m_Jacobian[2][7] = r31 * s1 * (x2-c2);
+ this->m_Jacobian[2][8] = r31 * s1 * (x3-c3);
+ this->m_Jacobian[2][9] = r32 * s2 * (x3-c3);
+
+
+ // for test purpose
+ // FORCE ONLY DO DERIVATIVE ON S
+// for(int ii=0; ii <3; ii++){
+// for(int jj=0; jj<=9;jj++){
+// this->m_Jacobian[ii][jj] = 0.0;
+// }
+// }
+// this->m_Jacobian[0][4] = r11 * w1;
+// this->m_Jacobian[1][4] = r21 * w1;
+// this->m_Jacobian[2][4] = r31 * w1;
+
+
+
+ // compute derivatives for the translation part
+ unsigned int blockOffset = 10;
+ for(unsigned int dim=0; dim < SpaceDimension; dim++ )
+ {
+ this->m_Jacobian[ dim ][ blockOffset + dim ] = 1.0;
+ }
+
+ return this->m_Jacobian;
+
+
+
+// // compute derivatives with respect to rotation
+// this->m_Jacobian.Fill(0.0);
+
+// const TScalarType x = p[0] - this->GetCenter()[0];
+// const TScalarType y = p[1] - this->GetCenter()[1];
+// const TScalarType z = p[2] - this->GetCenter()[2];
+
+// // compute Jacobian with respect to quaternion parameters
+// this->m_Jacobian[0][0] = 2.0 * ( m_Rotation.x() * x + m_Rotation.y() * y
+// + m_Rotation.z() * z );
+// this->m_Jacobian[0][1] = 2.0 * (- m_Rotation.y() * x + m_Rotation.x() * y
+// + m_Rotation.r() * z );
+// this->m_Jacobian[0][2] = 2.0 * (- m_Rotation.z() * x - m_Rotation.r() * y
+// + m_Rotation.x() * z );
+// this->m_Jacobian[0][3] = - 2.0 * (- m_Rotation.r() * x + m_Rotation.z() * y
+// - m_Rotation.y() * z );
+
+// this->m_Jacobian[1][0] = - this->m_Jacobian[0][1];
+// this->m_Jacobian[1][1] = this->m_Jacobian[0][0];
+// this->m_Jacobian[1][2] = this->m_Jacobian[0][3];
+// this->m_Jacobian[1][3] = - this->m_Jacobian[0][2];
+
+// this->m_Jacobian[2][0] = - this->m_Jacobian[0][2];
+// this->m_Jacobian[2][1] = - this->m_Jacobian[0][3];
+// this->m_Jacobian[2][2] = this->m_Jacobian[0][0];
+// this->m_Jacobian[2][3] = this->m_Jacobian[0][1];
+
+
+// // compute derivatives for the translation part
+// unsigned int blockOffset = 4;
+// for(unsigned int dim=0; dim < SpaceDimension; dim++ )
+// {
+// this->m_Jacobian[ dim ][ blockOffset + dim ] = 1.0;
+// }
+
+// return this->m_Jacobian;
+
+}
+
+// template<class TScalarType>
+// const typename GSAffine3DTransform< TScalarType >::InverseMatrixType &
+// GSAffine3DTransform<TScalarType>::
+// GetInverseMatrix() const
+// {
+// // If the transform has been modified we recompute the inverse
+// if(this->InverseMatrixIsOld())
+// {
+// InverseMatrixType newMatrix;
+// VnlQuaternionType conjugateRotation = m_Rotation.conjugate();
+// VnlQuaternionType inverseRotation = conjugateRotation.inverse();
+// newMatrix = inverseRotation.rotation_matrix_transpose();
+// this->SetVarInverseMatrix(newMatrix);
+// }
+// return this->GetVarInverseMatrix();
+// }
+
+template<class TScalarType>
+typename GSAffine3DTransform<TScalarType>::MatrixType GSAffine3DTransform<TScalarType>::
+ComputeMyRotationMatrix()
+{
+ VnlQuaternionType conjugateRotation = m_Rotation.conjugate();
+ // this is done to compensate for the transposed representation
+ // between VNL and ITK
+ MatrixType R;
+ R = conjugateRotation.rotation_matrix_transpose();
+ return R;
+}
+
+template<class TScalarType>
+void
+GSAffine3DTransform<TScalarType>::
+ComputeMatrix()
+{
+ VnlQuaternionType conjugateRotation = m_Rotation.conjugate();
+ // this is done to compensate for the transposed representation
+ // between VNL and ITK
+ MatrixType R;
+ R = conjugateRotation.rotation_matrix_transpose();
+
+ MatrixType S;
+ S.Fill(NumericTraits< TScalarType >::Zero);
+ S[0][0] = this->m_S1;
+ S[1][1] = this->m_S2;
+ S[2][2] = this->m_S3;
+
+ MatrixType K;
+ K.Fill(NumericTraits< TScalarType >::Zero);
+ K[0][0] = NumericTraits< TScalarType >::One;
+ K[0][1] = this->m_K1;
+ K[0][2] = this->m_K2;
+ K[1][1] = NumericTraits< TScalarType >::One;
+ K[1][2] = this->m_K3;
+ K[2][2] = NumericTraits< TScalarType >::One;
+
+ MatrixType newMatrix;
+
+ newMatrix = R * S * K;
+
+
+
+ this->SetVarMatrix(newMatrix);
+}
+
+// template<class TScalarType>
+// void
+// GSAffine3DTransform<TScalarType>::
+// ComputeMatrixParameters()
+// {
+// VnlQuaternionType quat(this->GetMatrix().GetVnlMatrix());
+// m_Rotation = quat;
+// }
+
+} // namespace
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkGaussianInterpolateImageFunction.h b/Submodules/c3d/itkextras/itkGaussianInterpolateImageFunction.h
new file mode 100644
index 0000000..4765dfc
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkGaussianInterpolateImageFunction.h
@@ -0,0 +1,267 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkGaussianInterpolateImageFunction.h,v $
+ Language: C++
+ Date: $Date: 2009/07/01 12:59:34 $
+ Version: $Revision: 1.5 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkGaussianInterpolateImageFunction_h
+#define __itkGaussianInterpolateImageFunction_h
+
+#include "itkInterpolateImageFunction.h"
+#include "vnl/vnl_erf.h"
+
+namespace itk
+{
+
+/** \class GaussianInterpolateImageFunction
+ * \brief Gaussianly interpolate an image at specified positions.
+ *
+ * GaussianInterpolateImageFunction linearly interpolates image intensity at
+ * a non-integer pixel position. This class is templated
+ * over the input image type and the coordinate representation type
+ * (e.g. float or double).
+ *
+ * This function works for N-dimensional images.
+ *
+ * \ingroup ImageFunctions ImageInterpolators
+ */
+template <class TInputImage, class TCoordRep = double>
+class ITK_EXPORT GaussianInterpolateImageFunction :
+ public InterpolateImageFunction<TInputImage,TCoordRep>
+{
+public:
+ /** Standard class typedefs. */
+ typedef GaussianInterpolateImageFunction Self;
+ typedef InterpolateImageFunction<TInputImage,TCoordRep> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(GaussianInterpolateImageFunction, InterpolateImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** OutputType typedef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** InputImageType typedef support. */
+ typedef typename Superclass::InputImageType InputImageType;
+
+ /** RealType typedef support. */
+ typedef typename Superclass::RealType RealType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(VDim, unsigned int,Superclass::ImageDimension);
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Compute internals */
+ virtual void ComputeBoundingBox()
+ {
+ const TInputImage *img = this->GetInputImage();
+ if(img == NULL) return;
+
+ // Set the bounding box
+ for(size_t d = 0; d < VDim; d++)
+ {
+ bb_start[d] = -0.5;
+ bb_end[d] = img->GetBufferedRegion().GetSize()[d] - 0.5;
+ nt[d] = (int)(bb_end[d] - bb_start[d] + 0.5);
+ dx[d].set_size(nt[d]);
+ gx[d].set_size(nt[d]);
+ sf[d] = 1.0 / (sqrt(2.0) * sigma[d] / img->GetSpacing()[d]);
+ cut[d] = sigma[d] * alpha / img->GetSpacing()[d];
+ }
+ }
+
+ /** Set input */
+ virtual void SetInputImage(const TInputImage *img)
+ {
+ // Call parent method
+ Superclass::SetInputImage(img);
+ this->ComputeBoundingBox();
+ }
+
+ void SetParameters(double *sigma, double alpha)
+ {
+ // Set the parameters
+ for(size_t d = 0; d < VDim; d++)
+ this->sigma[d] = sigma[d];
+ this->alpha = alpha;
+
+ // If the image already set, recompute
+ this->ComputeBoundingBox();
+ }
+
+ /** Evaluate the function at a ContinuousIndex position
+ *
+ * Returns the linearly interpolated image intensity at a
+ * specified point position. No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType & index ) const
+ {
+ return EvaluateAtContinuousIndex(index, NULL);
+ }
+
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType &index,
+ OutputType *grad) const
+ {
+ // The bound variables for x, y, z
+ int i0[VDim], i1[VDim];
+
+ // Compute the ERF difference arrays
+ for(size_t d = 0; d < VDim; d++)
+ {
+ double *pdx = const_cast<double *>(dx[d].data_block());
+ double *pgx = grad ? const_cast<double *>(gx[d].data_block()) : NULL;
+ compute_erf_array(pdx, i0[d], i1[d], bb_start[d], nt[d], cut[d], index[d], sf[d], pgx);
+ }
+
+ // Get a pointer to the output value
+ double sum_me = 0.0, sum_m = 0.0;
+ vnl_vector_fixed<double, VDim> dsum_me(0.0), dsum_m(0.0), dw;
+
+ // Loop over the voxels in the region identified
+ ImageRegion<VDim> region;
+ for(size_t d = 0; d < VDim; d++)
+ {
+ region.SetIndex(d, i0[d]);
+ region.SetSize(d, i1[d] - i0[d]);
+ }
+
+ for(
+ ImageRegionConstIteratorWithIndex<InputImageType> it(this->GetInputImage(), region);
+ !it.IsAtEnd(); ++it)
+ {
+ size_t j = it.GetIndex()[0];
+ double w = dx[0][j];
+ if(grad)
+ {
+ dw[0] = gx[0][j];
+ for(size_t d = 1; d < VDim; d++) dw[d] = dx[0][j];
+ }
+ for(size_t d = 1; d < VDim; d++)
+ {
+ j = it.GetIndex()[d];
+ w *= dx[d][j];
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ dw[q] *= (d == q) ? gx[d][j] : dx[d][j];
+ }
+ }
+
+ double V = it.Get();
+ sum_me += V * w;
+ sum_m += w;
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ {
+ dsum_me[q] += V * dw[q];
+ dsum_m[q] += dw[q];
+ }
+ }
+ }
+
+ double rc = sum_me / sum_m;
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ {
+ grad[q] = (dsum_me[q] - rc * dsum_m[q]) / sum_m;
+ grad[q] /= -1.4142135623730951 * sigma[q];
+ }
+ }
+
+ // return sum_me / sum_m;
+ return rc;
+
+ }
+
+protected:
+ GaussianInterpolateImageFunction() {}
+ ~GaussianInterpolateImageFunction(){};
+ void PrintSelf(std::ostream& os, Indent indent) const
+ { this->Superclass::PrintSelf(os,indent); }
+
+private:
+ GaussianInterpolateImageFunction( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ /** Number of neighbors used in the interpolation */
+ static const unsigned long m_Neighbors;
+
+ vnl_vector<double> dx[VDim], gx[VDim];
+ double bb_start[VDim], bb_end[VDim], sf[VDim], cut[VDim];
+ int nt[VDim], stride[VDim];
+ double sigma[VDim], alpha;
+
+ void compute_erf_array (
+ double *dx_erf, // The output array of erf(p+i+1) - erf(p+i)
+ int &k0, int &k1, // The range of integration 0 <= k0 < k1 <= n
+ double b, // Lower bound of the bounding box
+ int n, // Size of the bounding box in steps
+ double cut, // The distance at which to cut off
+ double p, // the value p
+ double sfac, // scaling factor 1 / (Sqrt[2] sigma)
+ double *gx_erf = NULL // Output derivative/erf array (optional)
+ ) const
+ {
+ // Determine the range of voxels along the line where to evaluate erf
+ k0 = (int) floor(p - b - cut);
+ k1 = (int) ceil(p - b + cut);
+ if(k0 < 0) k0 = 0;
+ if(k1 > n) k1 = n;
+
+ // Start at the first voxel
+ double t = (b - p + k0) * sfac;
+ double e_last = vnl_erf(t);
+ double g_last = gx_erf ? 1.128379167095513 * exp(- t * t) : 0.0;
+ for(int i = k0; i < k1; i++)
+ {
+ t += sfac;
+ double e_now = vnl_erf(t);
+ dx_erf[i] = e_now - e_last;
+ if(gx_erf)
+ {
+ double g_now = 1.128379167095513 * exp(- t * t);
+ gx_erf[i] = g_now - g_last;
+ g_last = g_now;
+ }
+ e_last = e_now;
+ }
+ }
+
+};
+
+} // end namespace itk
+
+// Define instantiation macro for this template.
+#define ITK_TEMPLATE_GaussianInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \
+ _(2(class EXPORT GaussianInterpolateImageFunction< ITK_TEMPLATE_2 x >)) \
+ namespace Templates { typedef GaussianInterpolateImageFunction< ITK_TEMPLATE_2 x > \
+ GaussianInterpolateImageFunction##y; } \
+}
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkHessianToObjectnessMeasureImageFilter.h b/Submodules/c3d/itkextras/itkHessianToObjectnessMeasureImageFilter.h
new file mode 100644
index 0000000..d6d228f
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkHessianToObjectnessMeasureImageFilter.h
@@ -0,0 +1,173 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+#ifndef __itkHessianToObjectnessMeasureImageFilter_h
+#define __itkHessianToObjectnessMeasureImageFilter_h
+
+#include "itkSymmetricSecondRankTensor.h"
+#include "itkImageToImageFilter.h"
+
+namespace itk
+{
+/** \class HessianToObjectnessMeasureImageFilter
+ * \brief A filter to enhance M-dimensional objects in N-dimensional images
+ *
+ * The objectness measure is a generalization of Frangi's vesselness measure,
+ * which is based on the analysis of the the Hessian eigen system. The filter
+ * can enhance blob-like structures (M=0), vessel-like structures (M=1), 2D
+ * plate-like structures (M=2), hyper-plate-like structures (M=3) in N-dimensional
+ * images, with M<N.
+ * The filter takes an image of a Hessian pixels ( SymmetricSecondRankTensor pixels
+ * pixels ) and produces an enhanced image. The Hessian input image can be produced
+ * using itk::HessianRecursiveGaussianImageFilter.
+ *
+ *
+ * \par References
+ * Frangi, AF, Niessen, WJ, Vincken, KL, & Viergever, MA (1998). Multiscale Vessel
+ * Enhancement Filtering. In Wells, WM, Colchester, A, & Delp, S, Editors, MICCAI '98
+ * Medical Image Computing and Computer-Assisted Intervention, Lecture Notes in Computer
+ * Science, pages 130-137, Springer Verlag, 1998.
+ *
+ * Additional information can be from in the Insight Journal:
+ * http://hdl.handle.net/1926/576
+ *
+ * \author Luca Antiga Ph.D. Medical Imaging Unit,
+ * Bioengineering Department, Mario Negri Institute, Italy.
+ *
+ * \sa MultiScaleHessianBasedMeasureImageFilter
+ * \sa Hessian3DToVesselnessMeasureImageFilter
+ * \sa HessianSmoothedRecursiveGaussianImageFilter
+ * \sa SymmetricEigenAnalysisImageFilter
+ * \sa SymmetricSecondRankTensor
+ *
+ * \ingroup IntensityImageFilters TensorObjects
+ *
+ * \ingroup ITKReview
+ */
+template< typename TInputImage, typename TOutputImage >
+class ITK_EXPORT HessianToObjectnessMeasureImageFilter:public
+ ImageToImageFilter< TInputImage, TOutputImage >
+{
+public:
+ /** Standard class typedefs. */
+ typedef HessianToObjectnessMeasureImageFilter Self;
+
+ typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass;
+
+ typedef SmartPointer< Self > Pointer;
+ typedef SmartPointer< const Self > ConstPointer;
+
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::OutputImageType OutputImageType;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputImageType::RegionType OutputImageRegionType;
+
+ /** Image dimension */
+ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension);
+
+ typedef double EigenValueType;
+ typedef itk::FixedArray< EigenValueType, itkGetStaticConstMacro(ImageDimension) > EigenValueArrayType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Runtime information support. */
+ itkTypeMacro(HessianToObjectnessMeasureImageFilter, ImageToImageFilter);
+
+ /** Set/Get Alpha, the weight corresponding to R_A
+ * (the ratio of the smallest eigenvalue that has to be large to the larger ones).
+ * Smaller values lead to increased sensitivity to the object dimensionality. */
+ itkSetMacro(Alpha, double);
+ itkGetConstMacro(Alpha, double);
+
+ /** Set/Get Beta, the weight corresponding to R_B
+ * (the ratio of the largest eigenvalue that has to be small to the larger ones).
+ * Smaller values lead to increased sensitivity to the object dimensionality. */
+ itkSetMacro(Beta, double);
+ itkGetConstMacro(Beta, double);
+
+ /** Set/Get Gamma, the weight corresponding to S
+ * (the Frobenius norm of the Hessian matrix, or second-order structureness) */
+ itkSetMacro(Gamma, double);
+ itkGetConstMacro(Gamma, double);
+
+ /** Toggle scaling the objectness measure with the magnitude of the largest
+ absolute eigenvalue */
+ itkSetMacro(ScaleObjectnessMeasure, bool);
+ itkGetConstMacro(ScaleObjectnessMeasure, bool);
+ itkBooleanMacro(ScaleObjectnessMeasure);
+
+ /** Set/Get the dimensionality of the object (0: points (blobs),
+ * 1: lines (vessels), 2: planes (plate-like structures), 3: hyper-planes.
+ * ObjectDimension must be smaller than ImageDimension. */
+ itkSetMacro(ObjectDimension, unsigned int);
+ itkGetConstMacro(ObjectDimension, unsigned int);
+
+ /** Enhance bright structures on a dark background if true, the opposite if
+ false. */
+ itkSetMacro(BrightObject, bool);
+ itkGetConstMacro(BrightObject, bool);
+ itkBooleanMacro(BrightObject);
+
+#ifdef ITK_USE_CONCEPT_CHECKING
+ /** Begin concept checking */
+ itkConceptMacro( DoubleConvertibleToOutputCheck, ( Concept::Convertible< double, OutputPixelType > ) );
+ /** End concept checking */
+#endif
+protected:
+ HessianToObjectnessMeasureImageFilter();
+ ~HessianToObjectnessMeasureImageFilter() {}
+ void PrintSelf(std::ostream & os, Indent indent) const;
+
+ void BeforeThreadedGenerateData(void);
+
+ void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId);
+
+private:
+ HessianToObjectnessMeasureImageFilter(const Self &); //purposely not
+ // implemented
+ void operator=(const Self &); //purposely not
+ // implemented
+
+ // functor used to sort the eigenvalues are to be sorted
+ // |e1|<=|e2|<=...<=|eN|
+ /** \class AbsLessEqualCompare
+ * \brief Returns ( abs(a) <= abs(b) )
+ * \ingroup ITKReview
+ */
+ struct AbsLessEqualCompare {
+ bool operator()(EigenValueType a, EigenValueType b)
+ {
+ return vnl_math_abs(a) <= vnl_math_abs(b);
+ }
+ };
+
+ double m_Alpha;
+ double m_Beta;
+ double m_Gamma;
+ unsigned int m_ObjectDimension;
+ bool m_BrightObject;
+ bool m_ScaleObjectnessMeasure;
+};
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkHessianToObjectnessMeasureImageFilter.hxx"
+#endif
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkHessianToObjectnessMeasureImageFilter.hxx b/Submodules/c3d/itkextras/itkHessianToObjectnessMeasureImageFilter.hxx
new file mode 100644
index 0000000..7e5182a
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkHessianToObjectnessMeasureImageFilter.hxx
@@ -0,0 +1,206 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+#ifndef __itkHessianToObjectnessMeasureImageFilter_hxx
+#define __itkHessianToObjectnessMeasureImageFilter_hxx
+
+#include "itkHessianToObjectnessMeasureImageFilter.h"
+#include "itkImageRegionIterator.h"
+#include "itkSymmetricEigenAnalysis.h"
+#include "itkProgressReporter.h"
+
+#include "vnl/vnl_math.h"
+
+namespace itk
+{
+/**
+ * Constructor
+ */
+template< typename TInputImage, typename TOutputImage >
+HessianToObjectnessMeasureImageFilter< TInputImage, TOutputImage >
+::HessianToObjectnessMeasureImageFilter()
+{
+ m_Alpha = 0.5;
+ m_Beta = 0.5;
+ m_Gamma = 5.0;
+
+ m_ScaleObjectnessMeasure = true;
+
+ // by default extract bright lines (equivalent to vesselness)
+ m_ObjectDimension = 1;
+ m_BrightObject = true;
+}
+
+template< typename TInputImage, typename TOutputImage >
+void
+HessianToObjectnessMeasureImageFilter< TInputImage, TOutputImage >
+::BeforeThreadedGenerateData(void)
+{
+ if ( m_ObjectDimension >= ImageDimension )
+ {
+ itkExceptionMacro("ObjectDimension must be lower than ImageDimension.");
+ }
+}
+
+template< typename TInputImage, typename TOutputImage >
+void
+HessianToObjectnessMeasureImageFilter< TInputImage, TOutputImage >
+::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread,
+ ThreadIdType threadId)
+{
+ typename OutputImageType::Pointer output = this->GetOutput();
+ typename InputImageType::ConstPointer input = this->GetInput();
+
+ // support progress methods/callbacks
+ ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels(), 1000 / this->GetNumberOfThreads() );
+
+ // calculator for computation of the eigen values
+ typedef SymmetricEigenAnalysis< InputPixelType, EigenValueArrayType > CalculatorType;
+ CalculatorType eigenCalculator(ImageDimension);
+
+ // walk the region of eigen values and get the objectness measure
+ ImageRegionConstIterator< InputImageType > it(input, outputRegionForThread);
+ ImageRegionIterator< OutputImageType > oit(output, outputRegionForThread);
+
+ oit.GoToBegin();
+ it.GoToBegin();
+
+ while ( !it.IsAtEnd() )
+ {
+ // compute eigen values
+ EigenValueArrayType eigenValues;
+ eigenCalculator.ComputeEigenValues(it.Get(), eigenValues);
+
+ // Sort the eigenvalues by magnitude but retain their sign.
+ // The eigenvalues are to be sorted |e1|<=|e2|<=...<=|eN|
+ EigenValueArrayType sortedEigenValues = eigenValues;
+ std::sort( sortedEigenValues.Begin(), sortedEigenValues.End(), AbsLessEqualCompare() );
+
+ // check whether eigenvalues have the right sign
+ bool signConstraintsSatisfied = true;
+ for ( unsigned int i = m_ObjectDimension; i < ImageDimension; i++ )
+ {
+ if ( ( m_BrightObject && sortedEigenValues[i] > 0.0 )
+ || ( !m_BrightObject && sortedEigenValues[i] < 0.0 ) )
+ {
+ signConstraintsSatisfied = false;
+ break;
+ }
+ }
+
+ if ( !signConstraintsSatisfied )
+ {
+ oit.Set(NumericTraits< OutputPixelType >::Zero);
+ ++it;
+ ++oit;
+ progress.CompletedPixel();
+ continue;
+ }
+
+ EigenValueArrayType sortedAbsEigenValues;
+ for ( unsigned int i = 0; i < ImageDimension; i++ )
+ {
+ sortedAbsEigenValues[i] = vnl_math_abs(sortedEigenValues[i]);
+ }
+
+ // initialize the objectness measure
+ double objectnessMeasure = 1.0;
+
+ // compute objectness from eigenvalue ratios and second-order structureness
+ if ( m_ObjectDimension < ImageDimension - 1 )
+ {
+ double rA = sortedAbsEigenValues[m_ObjectDimension];
+ double rADenominatorBase = 1.0;
+ for ( unsigned int j = m_ObjectDimension + 1; j < ImageDimension; j++ )
+ {
+ rADenominatorBase *= sortedAbsEigenValues[j];
+ }
+ if ( vcl_fabs(rADenominatorBase) > 0.0 )
+ {
+ if ( vcl_fabs(m_Alpha) > 0.0 )
+ {
+ rA /= vcl_pow( rADenominatorBase, 1.0 / ( ImageDimension - m_ObjectDimension - 1 ) );
+ objectnessMeasure *= 1.0 - vcl_exp( -0.5 * vnl_math_sqr(rA) / vnl_math_sqr(m_Alpha) );
+ }
+ }
+ else
+ {
+ objectnessMeasure = 0.0;
+ }
+ }
+
+ if ( m_ObjectDimension > 0 )
+ {
+ double rB = sortedAbsEigenValues[m_ObjectDimension - 1];
+ double rBDenominatorBase = 1.0;
+ for ( unsigned int j = m_ObjectDimension; j < ImageDimension; j++ )
+ {
+ rBDenominatorBase *= sortedAbsEigenValues[j];
+ }
+ if ( vcl_fabs(rBDenominatorBase) > 0.0 && vcl_fabs(m_Beta) > 0.0 )
+ {
+ rB /= vcl_pow( rBDenominatorBase, 1.0 / ( ImageDimension - m_ObjectDimension ) );
+
+ objectnessMeasure *= vcl_exp( -0.5 * vnl_math_sqr(rB) / vnl_math_sqr(m_Beta) );
+ }
+ else
+ {
+ objectnessMeasure = 0.0;
+ }
+ }
+
+ if ( vcl_fabs(m_Gamma) > 0.0 )
+ {
+ double frobeniusNormSquared = 0.0;
+ for ( unsigned int i = 0; i < ImageDimension; i++ )
+ {
+ frobeniusNormSquared += vnl_math_sqr(sortedAbsEigenValues[i]);
+ }
+ objectnessMeasure *= 1.0 - vcl_exp( -0.5 * frobeniusNormSquared / vnl_math_sqr(m_Gamma) );
+ }
+
+ // in case, scale by largest absolute eigenvalue
+ if ( m_ScaleObjectnessMeasure )
+ {
+ objectnessMeasure *= sortedAbsEigenValues[ImageDimension - 1];
+ }
+
+ oit.Set( static_cast< OutputPixelType >( objectnessMeasure ) );
+
+ ++it;
+ ++oit;
+ progress.CompletedPixel();
+ }
+}
+
+template< typename TInputImage, typename TOutputImage >
+void
+HessianToObjectnessMeasureImageFilter< TInputImage, TOutputImage >
+::PrintSelf(std::ostream & os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "Alpha: " << m_Alpha << std::endl;
+ os << indent << "Beta: " << m_Beta << std::endl;
+ os << indent << "Gamma: " << m_Gamma << std::endl;
+ os << indent << "ScaleObjectnessMeasure: " << m_ScaleObjectnessMeasure << std::endl;
+ os << indent << "ObjectDimension: " << m_ObjectDimension << std::endl;
+ os << indent << "BrightObject: " << m_BrightObject << std::endl;
+}
+} // end namespace itk
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkLabelContourImageFilter.h b/Submodules/c3d/itkextras/itkLabelContourImageFilter.h
new file mode 100644
index 0000000..55f2198
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkLabelContourImageFilter.h
@@ -0,0 +1,226 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkLabelContourImageFilter.h,v $
+ Language: C++
+ Date: $Date: 2009/11/07 13:03:26 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+
+#ifndef __itkLabelContourImageFilter_h
+#define __itkLabelContourImageFilter_h
+
+#include "itkInPlaceImageFilter.h"
+#include "itkImage.h"
+#include "itkConceptChecking.h"
+#include <vector>
+#include <map>
+#include "itkProgressReporter.h"
+#include "itkBarrier.h"
+
+namespace itk
+{
+
+/**
+ * \class LabelContourImageFilter
+ * \brief Labels the pixels on the border of the objects in a labeled image.
+ *
+ * LabelContourImageFilter takes a labeled image as input, where the pixels in the
+ * objects are the pixels with a value different of the BackgroundValue. Only the pixels
+ * on the contours of the objects are kept. The pixels not on the border are changed
+ * to BackgroundValue. The labels of the object are the same in the input and in the
+ * output image.
+ *
+ * The connectivity can be changed to minimum or maximum connectivity with
+ * SetFullyConnected(). Full connectivity produces thicker contours.
+ *
+ * http://hdl.handle.net/1926/1352
+ *
+ * \author Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France.
+ *
+ * \sa BinaryContourImageFilter
+ */
+
+template <class TInputImage, class TOutputImage>
+class ITK_EXPORT LabelContourImageFilter :
+ public InPlaceImageFilter< TInputImage, TOutputImage >
+{
+public:
+ /**
+ * Standard "Self" & Superclass typedef.
+ */
+ typedef LabelContourImageFilter Self;
+ typedef InPlaceImageFilter< TInputImage, TOutputImage > Superclass;
+
+ /**
+ * Types from the Superclass
+ */
+ typedef typename Superclass::InputImagePointer InputImagePointer;
+
+ /**
+ * Extract some information from the image types. Dimensionality
+ * of the two images is assumed to be the same.
+ */
+ typedef typename TOutputImage::PixelType OutputPixelType;
+ typedef typename TOutputImage::InternalPixelType OutputInternalPixelType;
+ typedef typename TInputImage::PixelType InputPixelType;
+ typedef typename TInputImage::InternalPixelType InputInternalPixelType;
+
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TOutputImage::ImageDimension);
+ itkStaticConstMacro(OutputImageDimension, unsigned int,
+ TOutputImage::ImageDimension);
+ itkStaticConstMacro(InputImageDimension, unsigned int,
+ TInputImage::ImageDimension);
+
+ /**
+ * Image typedef support
+ */
+ typedef TInputImage InputImageType;
+ typedef typename TInputImage::IndexType IndexType;
+ typedef typename TInputImage::SizeType SizeType;
+ typedef typename TInputImage::OffsetType OffsetType;
+ typedef typename TInputImage::PixelType InputImagePixelType;
+ typedef typename TInputImage::SizeValueType SizeValueType;
+ typedef typename TInputImage::OffsetValueType OffsetValueType;
+
+ typedef TOutputImage OutputImageType;
+ typedef typename TOutputImage::RegionType RegionType;
+ typedef typename TOutputImage::IndexType OutputIndexType;
+ typedef typename TOutputImage::SizeType OutputSizeType;
+ typedef typename TOutputImage::OffsetType OutputOffsetType;
+ typedef typename TOutputImage::PixelType OutputImagePixelType;
+
+ typedef std::list<IndexType> ListType;
+
+ /**
+ * Smart pointer typedef support
+ */
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /**
+ * Run-time type information (and related methods)
+ */
+ itkTypeMacro(LabelContourImageFilter, ImageToImageFilter);
+
+ /**
+ * Method for creation through the object factory.
+ */
+ itkNewMacro(Self);
+
+ /**
+ * Set/Get whether the connected components are defined strictly by
+ * face connectivity or by face+edge+vertex connectivity. Default is
+ * FullyConnectedOff. For objects that are 1 pixel wide, use
+ * FullyConnectedOn.
+ */
+ itkSetMacro(FullyConnected, bool);
+ itkGetConstReferenceMacro(FullyConnected, bool);
+ itkBooleanMacro(FullyConnected);
+
+ // Concept checking -- input and output dimensions must be the same
+ itkConceptMacro(SameDimension,
+ (Concept::SameDimension<itkGetStaticConstMacro(InputImageDimension),
+ itkGetStaticConstMacro(OutputImageDimension)>));
+
+ /**
+ * Set/Get the background value used to identify the objects and mark the
+ * pixels not on the border of the objects.
+ */
+ itkSetMacro(BackgroundValue, OutputImagePixelType);
+ itkGetConstMacro(BackgroundValue, OutputImagePixelType);
+
+protected:
+ LabelContourImageFilter()
+ {
+ m_FullyConnected = false;
+ m_BackgroundValue = NumericTraits< OutputImagePixelType >::Zero;
+ m_NumberOfThreads = 0;
+ this->SetInPlace( false );
+ }
+ virtual ~LabelContourImageFilter() {}
+ LabelContourImageFilter(const Self&) {}
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+ /**
+ * Standard pipeline methods.
+ */
+ void BeforeThreadedGenerateData ();
+ void AfterThreadedGenerateData ();
+ void ThreadedGenerateData (const RegionType& outputRegionForThread, int threadId);
+
+ /** LabelContourImageFilter needs the entire input. Therefore
+ * it must provide an implementation GenerateInputRequestedRegion().
+ * \sa ProcessObject::GenerateInputRequestedRegion(). */
+ void GenerateInputRequestedRegion();
+
+ /** LabelContourImageFilter will produce all of the output.
+ * Therefore it must provide an implementation of
+ * EnlargeOutputRequestedRegion().
+ * \sa ProcessObject::EnlargeOutputRequestedRegion() */
+ void EnlargeOutputRequestedRegion(DataObject *itkNotUsed(output));
+
+private:
+ OutputImagePixelType m_BackgroundValue;
+ bool m_FullyConnected;
+
+ // some additional types
+ typedef typename TOutputImage::RegionType::SizeType OutSizeType;
+
+ // types to support the run length encoding of lines
+ class runLength
+ {
+ public:
+ // run length information - may be a more type safe way of doing this
+ long int length;
+ typename InputImageType::IndexType where; // Index of the start of the run
+ InputImagePixelType label;
+ };
+
+ typedef std::vector<runLength> lineEncoding;
+
+ // the map storing lines
+ typedef std::vector<lineEncoding> LineMapType;
+
+ typedef std::vector<long> OffsetVec;
+
+ // the types to support union-find operations
+ typedef std::vector<unsigned long int> UnionFindType;
+
+ bool CheckNeighbors(const OutputIndexType &A,
+ const OutputIndexType &B);
+
+ void CompareLines(lineEncoding ¤t, const lineEncoding &Neighbour);
+
+
+ void SetupLineOffsets(OffsetVec &LineOffsets);
+
+ void Wait()
+ {
+ if( m_NumberOfThreads > 1 )
+ {
+ m_Barrier->Wait();
+ }
+ }
+
+ typename Barrier::Pointer m_Barrier;
+ LineMapType m_LineMap;
+ long m_NumberOfThreads;
+};
+
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkLabelContourImageFilter.txx"
+#endif
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkLabelContourImageFilter.txx b/Submodules/c3d/itkextras/itkLabelContourImageFilter.txx
new file mode 100644
index 0000000..c2c15c4
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkLabelContourImageFilter.txx
@@ -0,0 +1,449 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkLabelContourImageFilter.txx,v $
+ Language: C++
+ Date: $Date: 2009/11/07 13:03:26 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkLabelContourImageFilter_txx
+#define __itkLabelContourImageFilter_txx
+
+#include "itkLabelContourImageFilter.h"
+#include "itkNumericTraits.h"
+
+// don't think we need the indexed version as we only compute the
+// index at the start of each run, but there isn't a choice
+#include "itkImageLinearConstIteratorWithIndex.h"
+#include "itkImageLinearIteratorWithIndex.h"
+#include "itkConstShapedNeighborhoodIterator.h"
+#include "itkImageRegionIterator.h"
+#include "itkMaskImageFilter.h"
+#include "itkConnectedComponentAlgorithm.h"
+
+namespace itk
+{
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::GenerateInputRequestedRegion()
+{
+ // call the superclass' implementation of this method
+ Superclass::GenerateInputRequestedRegion();
+
+ // We need all the input.
+ InputImagePointer input = const_cast<InputImageType *>(this->GetInput());
+ if( !input )
+ {
+ return;
+ }
+ input->SetRequestedRegion( input->GetLargestPossibleRegion() );
+
+}
+
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::EnlargeOutputRequestedRegion(DataObject *)
+{
+ this->GetOutput()
+ ->SetRequestedRegion( this->GetOutput()->GetLargestPossibleRegion() );
+}
+
+
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::BeforeThreadedGenerateData()
+{
+ typename TOutputImage::Pointer output = this->GetOutput();
+ typename TInputImage::ConstPointer input = this->GetInput();
+
+ unsigned long nbOfThreads = this->GetNumberOfThreads();
+ if( itk::MultiThreader::GetGlobalMaximumNumberOfThreads() != 0 )
+ {
+ nbOfThreads = vnl_math_min( this->GetNumberOfThreads(), itk::MultiThreader::GetGlobalMaximumNumberOfThreads() );
+ }
+ // number of threads can be constrained by the region size, so call the SplitRequestedRegion
+ // to get the real number of threads which will be used
+ typename TOutputImage::RegionType splitRegion; // dummy region - just to call the following method
+ nbOfThreads = this->SplitRequestedRegion(0, nbOfThreads, splitRegion);
+// std::cout << "nbOfThreads: " << nbOfThreads << std::endl;
+
+ m_Barrier = Barrier::New();
+ m_Barrier->Initialize( nbOfThreads );
+ SizeValueType pixelcount = output->GetRequestedRegion().GetNumberOfPixels();
+ SizeValueType xsize = output->GetRequestedRegion().GetSize()[0];
+ SizeValueType linecount = pixelcount/xsize;
+ m_LineMap.clear();
+ m_LineMap.resize( linecount );
+ m_NumberOfThreads = nbOfThreads;
+}
+
+
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::ThreadedGenerateData(const RegionType& outputRegionForThread,
+ int threadId)
+{
+ typename TOutputImage::Pointer output = this->GetOutput();
+ typename TInputImage::ConstPointer input = this->GetInput();
+
+ // create a line iterator
+ typedef itk::ImageLinearConstIteratorWithIndex<InputImageType>
+ InputLineIteratorType;
+ InputLineIteratorType inLineIt(input, outputRegionForThread);
+ inLineIt.SetDirection(0);
+
+ typedef itk::ImageLinearIteratorWithIndex<OutputImageType>
+ OutputLineIteratorType;
+ OutputLineIteratorType outLineIt(output, outputRegionForThread);
+ outLineIt.SetDirection(0);
+
+ // set the progress reporter to deal with the number of lines
+ SizeValueType pixelcountForThread = outputRegionForThread.GetNumberOfPixels();
+ SizeValueType xsizeForThread = outputRegionForThread.GetSize()[0];
+ SizeValueType linecountForThread = pixelcountForThread/xsizeForThread;
+ ProgressReporter progress(this, threadId, linecountForThread * 2);
+
+ // find the split axis
+ IndexType outputRegionIdx = output->GetRequestedRegion().GetIndex();
+ IndexType outputRegionForThreadIdx = outputRegionForThread.GetIndex();
+ int splitAxis = 0;
+ for( int i=0; i<ImageDimension; i++ )
+ {
+ if( outputRegionIdx[i] != outputRegionForThreadIdx[i] )
+ {
+ splitAxis = i;
+ }
+ }
+
+ // compute the number of pixels before that thread
+ SizeType outputRegionSize = output->GetRequestedRegion().GetSize();
+ outputRegionSize[splitAxis] = outputRegionForThreadIdx[splitAxis] - outputRegionIdx[splitAxis];
+ SizeValueType firstLineIdForThread = RegionType( outputRegionIdx, outputRegionSize ).GetNumberOfPixels() / xsizeForThread;
+ SizeValueType lineId = firstLineIdForThread;
+
+ OffsetVec LineOffsets;
+ SetupLineOffsets(LineOffsets);
+
+ outLineIt.GoToBegin();
+ for( inLineIt.GoToBegin();
+ !inLineIt.IsAtEnd();
+ inLineIt.NextLine(), outLineIt.NextLine() )
+ {
+ inLineIt.GoToBeginOfLine();
+ outLineIt.GoToBeginOfLine();
+ lineEncoding Line;
+ while (! inLineIt.IsAtEndOfLine())
+ {
+ InputPixelType PVal = inLineIt.Get();
+
+ runLength thisRun;
+ SizeValueType length=0;
+ IndexType thisIndex;
+ thisIndex = inLineIt.GetIndex();
+ //std::cout << thisIndex << std::endl;
+ outLineIt.Set( m_BackgroundValue );
+ ++length;
+ ++inLineIt;
+ ++outLineIt;
+ while( !inLineIt.IsAtEndOfLine()
+ && inLineIt.Get() == PVal )
+ {
+ outLineIt.Set( m_BackgroundValue );
+ ++length;
+ ++inLineIt;
+ ++outLineIt;
+ }
+ // create the run length object to go in the vector
+ thisRun.length=length;
+ thisRun.where = thisIndex;
+ thisRun.label = PVal;
+ Line.push_back(thisRun);
+ }
+// std::cout << std::endl;
+ m_LineMap[lineId] = Line;
+ lineId++;
+ progress.CompletedPixel();
+ }
+
+
+ // wait for the other threads to complete that part
+ this->Wait();
+
+ // now process the map and make appropriate entries in an equivalence
+ // table
+ // assert( linecount == m_ForegroundLineMap.size() );
+ SizeValueType pixelcount = output->GetRequestedRegion().GetNumberOfPixels();
+ SizeValueType xsize = output->GetRequestedRegion().GetSize()[0];
+ OffsetValueType linecount = pixelcount/xsize;
+
+ SizeValueType lastLineIdForThread = linecount;
+ if( threadId != m_NumberOfThreads - 1 )
+ {
+ lastLineIdForThread = firstLineIdForThread + RegionType( outputRegionIdx, outputRegionForThread.GetSize() ).GetNumberOfPixels() / xsizeForThread;
+ }
+
+ for(SizeValueType ThisIdx = firstLineIdForThread; ThisIdx < lastLineIdForThread; ++ThisIdx)
+ {
+ if( !m_LineMap[ThisIdx].empty() )
+ {
+ for (OffsetVec::const_iterator I = LineOffsets.begin();
+ I != LineOffsets.end(); ++I)
+ {
+ OffsetValueType NeighIdx = ThisIdx + (*I);
+ // check if the neighbor is in the map
+ if ( NeighIdx >= 0 && NeighIdx < linecount && !m_LineMap[NeighIdx].empty() )
+ {
+ // Now check whether they are really neighbors
+ bool areNeighbors
+ = CheckNeighbors(m_LineMap[ThisIdx][0].where, m_LineMap[NeighIdx][0].where);
+ if (areNeighbors)
+ {
+ // Compare the two lines
+ CompareLines(m_LineMap[ThisIdx], m_LineMap[NeighIdx]);
+ }
+ }
+ }
+ }
+ progress.CompletedPixel();
+ }
+
+}
+
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::AfterThreadedGenerateData()
+{
+ m_Barrier = NULL;
+ m_LineMap.clear();
+}
+
+
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::SetupLineOffsets(OffsetVec &LineOffsets)
+{
+ // Create a neighborhood so that we can generate a table of offsets
+ // to "previous" line indexes
+ // We are going to mis-use the neighborhood iterators to compute the
+ // offset for us. All this messing around produces an array of
+ // offsets that will be used to index the map
+ typename TOutputImage::Pointer output = this->GetOutput();
+ typedef Image<long, TOutputImage::ImageDimension - 1> PretendImageType;
+ typedef typename PretendImageType::RegionType::SizeType PretendSizeType;
+ typedef typename PretendImageType::RegionType::IndexType PretendIndexType;
+ typedef ConstShapedNeighborhoodIterator<PretendImageType> LineNeighborhoodType;
+
+ typename PretendImageType::Pointer fakeImage;
+ fakeImage = PretendImageType::New();
+
+ typename PretendImageType::RegionType LineRegion;
+ //LineRegion = PretendImageType::RegionType::New();
+
+ OutSizeType OutSize = output->GetRequestedRegion().GetSize();
+
+ PretendSizeType PretendSize;
+ // The first dimension has been collapsed
+ for (unsigned int i = 0; i<PretendSize.GetSizeDimension(); i++)
+ {
+ PretendSize[i] = OutSize[i+1];
+ }
+
+ LineRegion.SetSize(PretendSize);
+ fakeImage->SetRegions( LineRegion );
+ PretendSizeType kernelRadius;
+ kernelRadius.Fill(1);
+ LineNeighborhoodType lnit(kernelRadius, fakeImage, LineRegion);
+
+ setConnectivity( &lnit, m_FullyConnected );
+
+ typename LineNeighborhoodType::IndexListType ActiveIndexes;
+ ActiveIndexes = lnit.GetActiveIndexList();
+
+ typename LineNeighborhoodType::IndexListType::const_iterator LI;
+
+ PretendIndexType idx = LineRegion.GetIndex();
+ OffsetValueType offset = fakeImage->ComputeOffset( idx );
+
+ for (LI=ActiveIndexes.begin(); LI != ActiveIndexes.end(); LI++)
+ {
+ LineOffsets.push_back( fakeImage->ComputeOffset( idx + lnit.GetOffset( *LI ) ) - offset );
+ }
+ LineOffsets.push_back( 0 );
+ // LineOffsets is the thing we wanted.
+
+
+}
+
+template< class TInputImage, class TOutputImage>
+bool
+LabelContourImageFilter< TInputImage, TOutputImage>
+::CheckNeighbors(const OutputIndexType &A,
+ const OutputIndexType &B)
+{
+ // this checks whether the line encodings are really neighbors. The
+ // first dimension gets ignored because the encodings are along that
+ // axis
+ OutputOffsetType Off = A - B;
+ for (unsigned i = 1; i < OutputImageDimension; i++)
+ {
+ if (abs(Off[i]) > 1)
+ {
+ return(false);
+ }
+ }
+ return(true);
+}
+
+
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::CompareLines(lineEncoding ¤t, const lineEncoding &Neighbour)
+{
+ bool sameLine = true;
+ OutputOffsetType Off = current[0].where - Neighbour[0].where;
+ for (unsigned i = 1; i < OutputImageDimension; i++)
+ {
+ if (Off[i] != 0)
+ {
+ sameLine = false;
+ }
+ }
+
+ OffsetValueType offset = 0;
+ if (m_FullyConnected || sameLine)
+ {
+ offset = 1;
+ }
+// std::cout << "offset: " << offset << std::endl;
+
+ typename TOutputImage::Pointer output = this->GetOutput();
+
+ typename lineEncoding::const_iterator nIt, mIt;
+ typename lineEncoding::iterator cIt;
+
+ mIt = Neighbour.begin(); // out marker iterator
+
+ for (cIt = current.begin();cIt != current.end();++cIt)
+ {
+ if( cIt->label == m_BackgroundValue )
+ {
+ continue;
+ }
+ //runLength cL = *cIt;
+ OffsetValueType cStart = cIt->where[0]; // the start x position
+ OffsetValueType cLast = cStart + cIt->length - 1;
+ bool lineCompleted = false;
+ for (nIt=mIt; nIt != Neighbour.end() && !lineCompleted; ++nIt)
+ {
+ if( nIt->label == cIt->label )
+ {
+ continue;
+ }
+
+ //runLength nL = *nIt;
+ OffsetValueType nStart = nIt->where[0];
+ OffsetValueType nLast = nStart + nIt->length - 1;
+ // there are a few ways that neighbouring lines might overlap
+ // neighbor S------------------E
+ // current S------------------------E
+ //-------------
+ // neighbor S------------------E
+ // current S----------------E
+ //-------------
+ // neighbor S------------------E
+ // current S------------------E
+ //-------------
+ // neighbor S------------------E
+ // current S-------E
+ //-------------
+ OffsetValueType ss1 = nStart - offset;
+ // OffsetValueType ss2 = nStart + offset;
+ // OffsetValueType ee1 = nLast - offset;
+ OffsetValueType ee2 = nLast + offset;
+ bool eq = false;
+ OffsetValueType oStart = 0;
+ OffsetValueType oLast = 0;
+ // the logic here can probably be improved a lot
+ if ((ss1 >= cStart) && (ee2 <= cLast))
+ {
+ // case 1
+// std::cout << "case 1" << std::endl;
+ eq = true;
+ oStart = ss1;
+ oLast = ee2;
+ }
+ else if ((ss1 <= cStart) && (ee2 >= cLast))
+ {
+ // case 4
+// std::cout << "case 4" << std::endl;
+ eq = true;
+ oStart = cStart;
+ oLast = cLast;
+ }
+ else if ((ss1 <= cLast) && (ee2 >= cLast))
+ {
+ // case 2
+// std::cout << "case 2" << std::endl;
+ eq = true;
+ oStart = ss1;
+ oLast = cLast;
+ }
+ else if ((ss1 <= cStart) && (ee2 >= cStart))
+ {
+ // case 3
+// std::cout << "case 3" << std::endl;
+ eq = true;
+ oStart = cStart;
+ oLast = ee2;
+ }
+
+ if (eq)
+ {
+// std::cout << oStart << " " << oLast << std::endl;
+ assert( oStart <= oLast );
+ IndexType idx = cIt->where;
+ for( int x=oStart; x<=oLast; x++ )
+ {
+// std::cout << idx << std::endl;
+ idx[0] = x;
+ output->SetPixel( idx, cIt->label );
+ }
+ if( oStart == cStart && oLast == cLast )
+ {
+ lineCompleted = true;
+ }
+ }
+
+ }
+ }
+}
+
+template< class TInputImage, class TOutputImage>
+void
+LabelContourImageFilter< TInputImage, TOutputImage>
+::PrintSelf(std::ostream& os, Indent indent) const
+{
+ Superclass::PrintSelf(os,indent);
+
+ os << indent << "FullyConnected: " << m_FullyConnected << std::endl;
+ os << indent << "BackgroundValue: " << static_cast<typename NumericTraits<OutputImagePixelType>::PrintType>(m_BackgroundValue) << std::endl;
+}
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkLabelImageGaussianInterpolateImageFunction.h b/Submodules/c3d/itkextras/itkLabelImageGaussianInterpolateImageFunction.h
new file mode 100644
index 0000000..f26fc03
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkLabelImageGaussianInterpolateImageFunction.h
@@ -0,0 +1,262 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkLabelImageGaussianInterpolateImageFunction.h,v $
+ Language: C++
+ Date: $Date: 2012/11/01 09:19:14 $
+ Version: $Revision: 1.1 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkLabelImageGaussianInterpolateImageFunction_h
+#define __itkLabelImageGaussianInterpolateImageFunction_h
+
+#include "itkInterpolateImageFunction.h"
+#include "vnl/vnl_erf.h"
+
+namespace itk
+{
+
+/** \class LabelImageGaussianInterpolateImageFunction
+ * \brief Interpolation function for multi-label images that implicitly smooths the
+ * binary images corresponding to each label and returns the label with largest vote.
+ *
+ * This filter is an alternative to nearest neighbor interpolation for multi-label
+ * images. Given a multi-label image I with label set L, this function returns a
+ * label at the non-voxel position I(x), based on the following rule
+ *
+ * I(x) = \arg\max_{l \in L} (G_\sigma * I_l)(x)
+ *
+ * Where I_l is the l-th binary component of the multilabel image. In other words,
+ * each label in the multi-label image is convolved with a Gaussian, and the label
+ * for which the response is largest is returned. For sigma=0, this is just nearest
+ * neighbor interpolation.
+ *
+ * This function works for N-dimensional images.
+ *
+ * \ingroup ImageFunctions ImageInterpolators
+ */
+template <class TInputImage, class TCoordRep = double>
+class ITK_EXPORT LabelImageGaussianInterpolateImageFunction :
+ public InterpolateImageFunction<TInputImage,TCoordRep>
+{
+public:
+ /** Standard class typedefs. */
+ typedef LabelImageGaussianInterpolateImageFunction Self;
+ typedef InterpolateImageFunction<TInputImage,TCoordRep> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(LabelImageGaussianInterpolateImageFunction, InterpolateImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** OutputType typedef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** InputImageType typedef support. */
+ typedef typename Superclass::InputImageType InputImageType;
+
+ /** RealType typedef support. */
+ typedef typename Superclass::RealType RealType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(VDim, unsigned int,Superclass::ImageDimension);
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Compute internals */
+ virtual void ComputeBoundingBox()
+ {
+ const TInputImage *img = this->GetInputImage();
+ if(img == NULL) return;
+
+ // Set the bounding box
+ for(size_t d = 0; d < VDim; d++)
+ {
+ bb_start[d] = -0.5;
+ bb_end[d] = img->GetBufferedRegion().GetSize()[d] - 0.5;
+ nt[d] = (int)(bb_end[d] - bb_start[d] + 0.5);
+ dx[d].set_size(nt[d]);
+ gx[d].set_size(nt[d]);
+ sf[d] = 1.0 / (sqrt(2.0) * sigma[d] / img->GetSpacing()[d]);
+ cut[d] = sigma[d] * alpha / img->GetSpacing()[d];
+ }
+ }
+
+ /** Set input */
+ virtual void SetInputImage(const TInputImage *img)
+ {
+ // Call parent method
+ Superclass::SetInputImage(img);
+ this->ComputeBoundingBox();
+ }
+
+ void SetParameters(double *sigma, double alpha)
+ {
+ // Set the parameters
+ for(size_t d = 0; d < VDim; d++)
+ this->sigma[d] = sigma[d];
+ this->alpha = alpha;
+
+ // If the image already set, recompute
+ this->ComputeBoundingBox();
+ }
+
+ /** Evaluate the function at a ContinuousIndex position
+ *
+ * Returns the linearly interpolated image intensity at a
+ * specified point position. No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType & index ) const
+ {
+ // The bound variables for x, y, z
+ int i0[VDim], i1[VDim];
+
+ // Compute the ERF difference arrays
+ for(size_t d = 0; d < VDim; d++)
+ {
+ double *pdx = const_cast<double *>(dx[d].data_block());
+ compute_erf_array(pdx, i0[d], i1[d], bb_start[d], nt[d], cut[d], index[d], sf[d], NULL);
+ }
+
+ // Create a map object to store weights for each label encountered
+ // inside the search region. This is not as efficient as having a
+ // linear list of labels, but probably not a huge deal compared to
+ // having to evaluate the erf function
+ typedef std::map<OutputType, double> WeightMap;
+ typedef typename std::map<OutputType, double>::iterator WeightIter;
+ WeightMap wm;
+
+ // Variables to keep track of the largest current weight
+ double wmax = 0.0;
+ OutputType vmax;
+
+ // Loop over the voxels in the region identified
+ ImageRegion<VDim> region;
+ for(size_t d = 0; d < VDim; d++)
+ {
+ region.SetIndex(d, i0[d]);
+ region.SetSize(d, i1[d] - i0[d]);
+ }
+
+ for(
+ ImageRegionConstIteratorWithIndex<InputImageType> it(this->GetInputImage(), region);
+ !it.IsAtEnd(); ++it)
+ {
+ size_t j = it.GetIndex()[0];
+ double w = dx[0][j];
+ for(size_t d = 1; d < VDim; d++)
+ {
+ j = it.GetIndex()[d];
+ w *= dx[d][j];
+ }
+
+ double wtest;
+ OutputType V = it.Get();
+ WeightIter it = wm.find(V);
+ if(it != wm.end())
+ {
+ it->second += w;
+ wtest = it->second;
+ }
+ else
+ {
+ wm.insert(make_pair(V, w));
+ wtest = w;
+ }
+
+ // Keep track of the max value
+ if(wtest > wmax)
+ {
+ wmax = wtest;
+ vmax = V;
+ }
+ }
+
+ // Return the label with the maximum weight
+ return vmax;
+ }
+
+protected:
+ LabelImageGaussianInterpolateImageFunction() {}
+ ~LabelImageGaussianInterpolateImageFunction(){};
+ void PrintSelf(std::ostream& os, Indent indent) const
+ { this->Superclass::PrintSelf(os,indent); }
+
+private:
+ LabelImageGaussianInterpolateImageFunction( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ /** Number of neighbors used in the interpolation */
+ static const unsigned long m_Neighbors;
+
+ vnl_vector<double> dx[VDim], gx[VDim];
+ double bb_start[VDim], bb_end[VDim], sf[VDim], cut[VDim];
+ int nt[VDim], stride[VDim];
+ double sigma[VDim], alpha;
+
+ void compute_erf_array (
+ double *dx_erf, // The output array of erf(p+i+1) - erf(p+i)
+ int &k0, int &k1, // The range of integration 0 <= k0 < k1 <= n
+ double b, // Lower bound of the bounding box
+ int n, // Size of the bounding box in steps
+ double cut, // The distance at which to cut off
+ double p, // the value p
+ double sfac, // scaling factor 1 / (Sqrt[2] sigma)
+ double *gx_erf = NULL // Output derivative/erf array (optional)
+ ) const
+ {
+ // Determine the range of voxels along the line where to evaluate erf
+ k0 = (int) floor(p - b - cut);
+ k1 = (int) ceil(p - b + cut);
+ if(k0 < 0) k0 = 0;
+ if(k1 > n) k1 = n;
+
+ // Start at the first voxel
+ double t = (b - p + k0) * sfac;
+ double e_last = vnl_erf(t);
+ double g_last = gx_erf ? 1.128379167095513 * exp(- t * t) : 0.0;
+ for(int i = k0; i < k1; i++)
+ {
+ t += sfac;
+ double e_now = vnl_erf(t);
+ dx_erf[i] = e_now - e_last;
+ if(gx_erf)
+ {
+ double g_now = 1.128379167095513 * exp(- t * t);
+ gx_erf[i] = g_now - g_last;
+ g_last = g_now;
+ }
+ e_last = e_now;
+ }
+ }
+
+};
+
+} // end namespace itk
+
+// Define instantiation macro for this template.
+#define ITK_TEMPLATE_LabelImageGaussianInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \
+ _(2(class EXPORT LabelImageGaussianInterpolateImageFunction< ITK_TEMPLATE_2 x >)) \
+ namespace Templates { typedef LabelImageGaussianInterpolateImageFunction< ITK_TEMPLATE_2 x > \
+ LabelImageGaussianInterpolateImageFunction##y; } \
+}
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkLabelOverlapMeasuresImageFilter.h b/Submodules/c3d/itkextras/itkLabelOverlapMeasuresImageFilter.h
new file mode 100644
index 0000000..bb61805
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkLabelOverlapMeasuresImageFilter.h
@@ -0,0 +1,208 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkLabelOverlapMeasuresImageFilter.h,v $
+ Language: C++
+ Date: $Date: 2010/04/24 00:03:17 $
+ Version: $Revision: 1.2 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkLabelOverlapMeasuresImageFilter_h
+#define __itkLabelOverlapMeasuresImageFilter_h
+
+#include "itkImageToImageFilter.h"
+#include "itkFastMutexLock.h"
+#include "itkNumericTraits.h"
+
+#include "itk_hash_map.h"
+
+namespace itk {
+
+/** \class LabelOverlapMeasuresImageFilter
+ * \brief Computes overlap measures between the set same set of labels of
+ * pixels of two images. Background is assumed to be 0.
+ *
+ * \sa LabelOverlapMeasuresImageFilter
+ *
+ * \ingroup MultiThreaded
+ */
+template<class TLabelImage>
+class ITK_EXPORT LabelOverlapMeasuresImageFilter :
+ public ImageToImageFilter<TLabelImage, TLabelImage>
+{
+public:
+ /** Standard Self typedef */
+ typedef LabelOverlapMeasuresImageFilter Self;
+ typedef ImageToImageFilter<TLabelImage,TLabelImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro( Self );
+
+ /** Runtime information support. */
+ itkTypeMacro( LabelOverlapMeasuresImageFilter, ImageToImageFilter );
+
+ /** Image related typedefs. */
+ typedef TLabelImage LabelImageType;
+ typedef typename TLabelImage::Pointer LabelImagePointer;
+ typedef typename TLabelImage::ConstPointer LabelImageConstPointer;
+
+ typedef typename TLabelImage::RegionType RegionType;
+ typedef typename TLabelImage::SizeType SizeType;
+ typedef typename TLabelImage::IndexType IndexType;
+
+ typedef typename TLabelImage::PixelType LabelType;
+
+ /** Type to use form computations. */
+ typedef typename NumericTraits<LabelType>::RealType RealType;
+
+ /** \class LabelLabelOverlapMeasuress
+ * \brief Metrics stored per label */
+ class LabelSetMeasures
+ {
+ public:
+ // default constructor
+ LabelSetMeasures()
+ {
+ m_Source = 0;
+ m_Target = 0;
+ m_Union = 0;
+ m_Intersection = 0;
+ m_SourceComplement = 0;
+ m_TargetComplement = 0;
+ }
+
+ // added for completeness
+ LabelSetMeasures& operator=( const LabelSetMeasures& l )
+ {
+ m_Source = l.m_Source;
+ m_Target = l.m_Target;
+ m_Union = l.m_Union;
+ m_Intersection = l.m_Intersection;
+ m_SourceComplement = l.m_SourceComplement;
+ m_TargetComplement = l.m_TargetComplement;
+ }
+
+ unsigned long m_Source;
+ unsigned long m_Target;
+ unsigned long m_Union;
+ unsigned long m_Intersection;
+ unsigned long m_SourceComplement;
+ unsigned long m_TargetComplement;
+ };
+
+ /** Type of the map used to store data per label */
+ typedef hash_map<LabelType, LabelSetMeasures> MapType;
+ typedef typename MapType::iterator MapIterator;
+ typedef typename MapType::const_iterator MapConstIterator;
+
+ /** Image related typedefs. */
+ itkStaticConstMacro( ImageDimension, unsigned int,
+ TLabelImage::ImageDimension );
+
+ /** Set the source image. */
+ void SetSourceImage( const LabelImageType * image )
+ { this->SetNthInput( 0, const_cast<LabelImageType *>( image ) ); }
+
+ /** Set the target image. */
+ void SetTargetImage( const LabelImageType * image )
+ { this->SetNthInput( 1, const_cast<LabelImageType *>( image ) ); }
+
+ /** Get the source image. */
+ const LabelImageType * GetSourceImage( void )
+ { return this->GetInput( 0 ); }
+
+ /** Get the target image. */
+ const LabelImageType * GetTargetImage( void )
+ { return this->GetInput( 1 ); }
+
+ /** Get the label set measures */
+ MapType GetLabelSetMeasures()
+ { return this->m_LabelSetMeasures; }
+
+ /**
+ * tric overlap measures
+ */
+ /** measures over all labels */
+ RealType GetTotalOverlap();
+ RealType GetUnionOverlap();
+ RealType GetMeanOverlap();
+ RealType GetVolumeSimilarity();
+ RealType GetFalseNegativeError();
+ RealType GetFalsePositiveError();
+ /** measures over individual labels */
+ RealType GetTargetOverlap( LabelType );
+ RealType GetUnionOverlap( LabelType );
+ RealType GetMeanOverlap( LabelType );
+ RealType GetVolumeSimilarity( LabelType );
+ RealType GetFalseNegativeError( LabelType );
+ RealType GetFalsePositiveError( LabelType );
+ /** alternative names */
+ RealType GetJaccardCoefficient()
+ { return this->GetUnionOverlap(); }
+ RealType GetJaccardCoefficient( LabelType label )
+ { return this->GetUnionOverlap( label ); }
+ RealType GetDiceCoefficient()
+ { return this->GetMeanOverlap(); }
+ RealType GetDiceCoefficient( LabelType label )
+ { return this->GetMeanOverlap( label ); }
+
+
+#ifdef ITK_USE_CONCEPT_CHECKING
+ /** Begin concept checking */
+ itkConceptMacro( Input1HasNumericTraitsCheck,
+ ( Concept::HasNumericTraits<LabelType> ) );
+ /** End concept checking */
+#endif
+
+protected:
+ LabelOverlapMeasuresImageFilter();
+ ~LabelOverlapMeasuresImageFilter(){};
+ void PrintSelf( std::ostream& os, Indent indent ) const;
+
+ /**
+ * Pass the input through unmodified. Do this by setting the output to the
+ * source this by setting the output to the source image in the
+ * AllocateOutputs() method.
+ */
+ void AllocateOutputs();
+
+ void BeforeThreadedGenerateData();
+
+ void AfterThreadedGenerateData();
+
+ /** Multi-thread version GenerateData. */
+ void ThreadedGenerateData( const RegionType&, int );
+
+ // Override since the filter needs all the data for the algorithm
+ void GenerateInputRequestedRegion();
+
+ // Override since the filter produces all of its output
+ void EnlargeOutputRequestedRegion( DataObject *data );
+
+private:
+ LabelOverlapMeasuresImageFilter( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ std::vector<MapType> m_LabelSetMeasuresPerThread;
+ MapType m_LabelSetMeasures;
+
+ SimpleFastMutexLock m_Mutex;
+
+}; // end of class
+
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkLabelOverlapMeasuresImageFilter.txx"
+#endif
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkLabelOverlapMeasuresImageFilter.txx b/Submodules/c3d/itkextras/itkLabelOverlapMeasuresImageFilter.txx
new file mode 100644
index 0000000..9c13ce1
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkLabelOverlapMeasuresImageFilter.txx
@@ -0,0 +1,437 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkLabelOverlapMeasuresImageFilter.txx,v $
+ Language: C++
+ Date: $Date: 2010/04/24 00:03:21 $
+ Version: $Revision: 1.2 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef _itkLabelOverlapMeasuresImageFilter_txx
+#define _itkLabelOverlapMeasuresImageFilter_txx
+
+#include "itkLabelOverlapMeasuresImageFilter.h"
+
+#include "itkImageRegionConstIterator.h"
+#include "itkProgressReporter.h"
+
+namespace itk {
+
+#if defined(__GNUC__) && (__GNUC__ <= 2) //NOTE: This class needs a mutex for gnu 2.95
+/** Used for mutex locking */
+#define LOCK_HASHMAP this->m_Mutex.Lock()
+#define UNLOCK_HASHMAP this->m_Mutex.Unlock()
+#else
+#define LOCK_HASHMAP
+#define UNLOCK_HASHMAP
+#endif
+
+template<class TLabelImage>
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::LabelOverlapMeasuresImageFilter()
+{
+ // this filter requires two input images
+ this->SetNumberOfRequiredInputs( 2 );
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GenerateInputRequestedRegion()
+{
+ Superclass::GenerateInputRequestedRegion();
+ if( this->GetSourceImage() )
+ {
+ LabelImagePointer source = const_cast
+ <LabelImageType *>( this->GetSourceImage() );
+ source->SetRequestedRegionToLargestPossibleRegion();
+ }
+ if( this->GetTargetImage() )
+ {
+ LabelImagePointer target = const_cast
+ <LabelImageType *>( this->GetTargetImage() );
+ target->SetRequestedRegionToLargestPossibleRegion();
+ }
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::EnlargeOutputRequestedRegion( DataObject *data )
+{
+ Superclass::EnlargeOutputRequestedRegion( data );
+ data->SetRequestedRegionToLargestPossibleRegion();
+}
+
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::AllocateOutputs()
+{
+ // Pass the source through as the output
+ LabelImagePointer image =
+ const_cast<TLabelImage *>( this->GetSourceImage() );
+ this->SetNthOutput( 0, image );
+
+ // Nothing that needs to be allocated for the remaining outputs
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::BeforeThreadedGenerateData()
+{
+ int numberOfThreads = this->GetNumberOfThreads();
+
+ // Resize the thread temporaries
+ this->m_LabelSetMeasuresPerThread.resize( numberOfThreads );
+
+ // Initialize the temporaries
+ for( int n = 0; n < numberOfThreads; n++ )
+ {
+ this->m_LabelSetMeasuresPerThread[n].clear();
+ }
+
+ // Initialize the final map
+ this->m_LabelSetMeasures.clear();
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::AfterThreadedGenerateData()
+{
+ // Run through the map for each thread and accumulate the set measures.
+ for( int n = 0; n < this->GetNumberOfThreads(); n++ )
+ {
+ // iterate over the map for this thread
+ for( MapConstIterator threadIt = this->m_LabelSetMeasuresPerThread[n].begin();
+ threadIt != this->m_LabelSetMeasuresPerThread[n].end();
+ ++threadIt )
+ {
+ // does this label exist in the cumulative stucture yet?
+ MapIterator mapIt = this->m_LabelSetMeasures.find( ( *threadIt ).first );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ // create a new entry
+ typedef typename MapType::value_type MapValueType;
+ mapIt = this->m_LabelSetMeasures.insert( MapValueType(
+ (*threadIt).first, LabelSetMeasures() ) ).first;
+ }
+
+ // accumulate the information from this thread
+ (*mapIt).second.m_Source += (*threadIt).second.m_Source;
+ (*mapIt).second.m_Target += (*threadIt).second.m_Target;
+ (*mapIt).second.m_Union += (*threadIt).second.m_Union;
+ (*mapIt).second.m_Intersection +=
+ (*threadIt).second.m_Intersection;
+ (*mapIt).second.m_SourceComplement +=
+ (*threadIt).second.m_SourceComplement;
+ (*mapIt).second.m_TargetComplement +=
+ (*threadIt).second.m_TargetComplement;
+ } // end of thread map iterator loop
+ } // end of thread loop
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::ThreadedGenerateData( const RegionType& outputRegionForThread,
+ int threadId )
+{
+ ImageRegionConstIterator<LabelImageType> ItS( this->GetSourceImage(),
+ outputRegionForThread );
+ ImageRegionConstIterator<LabelImageType> ItT( this->GetTargetImage(),
+ outputRegionForThread );
+
+ // support progress methods/callbacks
+ ProgressReporter progress( this, threadId,
+ 2*outputRegionForThread.GetNumberOfPixels() );
+
+ for( ItS.GoToBegin(), ItT.GoToBegin(); !ItS.IsAtEnd(); ++ItS, ++ItT )
+ {
+ LabelType sourceLabel = ItS.Get();
+ LabelType targetLabel = ItT.Get();
+
+ // is the label already in this thread?
+ MapIterator mapItS =
+ this->m_LabelSetMeasuresPerThread[threadId].find( sourceLabel );
+ MapIterator mapItT =
+ this->m_LabelSetMeasuresPerThread[threadId].find( targetLabel );
+
+ if( mapItS == this->m_LabelSetMeasuresPerThread[threadId].end() )
+ {
+ // create a new label set measures object
+ typedef typename MapType::value_type MapValueType;
+ LOCK_HASHMAP;
+ mapItS = this->m_LabelSetMeasuresPerThread[threadId].insert(
+ MapValueType( sourceLabel, LabelSetMeasures() ) ).first;
+ UNLOCK_HASHMAP;
+ }
+
+ if( mapItT == this->m_LabelSetMeasuresPerThread[threadId].end() )
+ {
+ // create a new label set measures object
+ typedef typename MapType::value_type MapValueType;
+ LOCK_HASHMAP;
+ mapItT = this->m_LabelSetMeasuresPerThread[threadId].insert(
+ MapValueType( targetLabel, LabelSetMeasures() ) ).first;
+ UNLOCK_HASHMAP;
+ }
+
+ (*mapItS).second.m_Source++;
+ (*mapItT).second.m_Target++;
+
+ if( sourceLabel == targetLabel )
+ {
+ (*mapItS).second.m_Intersection++;
+ (*mapItS).second.m_Union++;
+ }
+ else
+ {
+ (*mapItS).second.m_Union++;
+ (*mapItT).second.m_Union++;
+
+ (*mapItS).second.m_SourceComplement++;
+ (*mapItT).second.m_TargetComplement++;
+ }
+
+ progress.CompletedPixel();
+ }
+}
+
+/**
+ * measures
+ */
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetTotalOverlap()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_Intersection );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Target );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetTargetOverlap( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_Intersection ) /
+ static_cast<RealType>( (*mapIt).second.m_Target );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetUnionOverlap()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_Intersection );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Union );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetUnionOverlap( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_Intersection ) /
+ static_cast<RealType>( (*mapIt).second.m_Union );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetMeanOverlap()
+{
+ RealType uo = this->GetUnionOverlap();
+ return ( 2.0 * uo / ( 1.0 + uo ) );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetMeanOverlap( LabelType label )
+{
+ RealType uo = this->GetUnionOverlap( label );
+ return ( 2.0 * uo / ( 1.0 + uo ) );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetVolumeSimilarity()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += ( ( static_cast<RealType>( (*mapIt).second.m_Source ) -
+ static_cast<RealType>( (*mapIt).second.m_Target ) ) );
+ denominator += ( ( static_cast<RealType>( (*mapIt).second.m_Source ) +
+ static_cast<RealType>( (*mapIt).second.m_Target ) ) );
+ }
+ return ( 2.0 * numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetVolumeSimilarity( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value = 2.0 *
+ ( static_cast<RealType>( (*mapIt).second.m_Source ) -
+ static_cast<RealType>( (*mapIt).second.m_Target ) ) /
+ ( static_cast<RealType>( (*mapIt).second.m_Source ) +
+ static_cast<RealType>( (*mapIt).second.m_Target ) );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalseNegativeError()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_TargetComplement );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Target );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalseNegativeError( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_TargetComplement ) /
+ static_cast<RealType>( (*mapIt).second.m_Target );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalsePositiveError()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_SourceComplement );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Source );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalsePositiveError( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_SourceComplement ) /
+ static_cast<RealType>( (*mapIt).second.m_Source );
+ return value;
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::PrintSelf( std::ostream& os, Indent indent ) const
+{
+ Superclass::PrintSelf( os, indent );
+
+}
+
+
+}// end namespace itk
+#endif
diff --git a/Submodules/c3d/itkextras/itkMultiScaleHessianBasedMeasureImageFilter.h b/Submodules/c3d/itkextras/itkMultiScaleHessianBasedMeasureImageFilter.h
new file mode 100644
index 0000000..f806c8a
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkMultiScaleHessianBasedMeasureImageFilter.h
@@ -0,0 +1,225 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+#ifndef __itkMultiScaleHessianBasedMeasureImageFilter_h
+#define __itkMultiScaleHessianBasedMeasureImageFilter_h
+
+#include "itkImageToImageFilter.h"
+#include "itkHessianRecursiveGaussianImageFilter.h"
+
+namespace itk
+{
+/**\class MultiScaleHessianBasedMeasureImageFilter
+ * \brief A filter to enhance structures using Hessian eigensystem-based
+ * measures in a multiscale framework
+ *
+ * The filter evaluates a Hessian-based enhancement measure, such as vesselness
+ * or objectness, at different scale levels. The Hessian-based measure is computed
+ * from the Hessian image at each scale level and the best response is selected.
+ *
+ * Minimum and maximum sigma value can be set using SetMinSigma and SetMaxSigma
+ * methods respectively. The number of scale levels is set using
+ * SetNumberOfSigmaSteps method. Exponentially distributed scale levels are
+ * computed within the bound set by the minimum and maximum sigma values
+ *
+ * The filter computes a second output image (accessed by the GetScalesOutput method)
+ * containing the scales at which each pixel gave the best response.
+ *
+ *
+ * This code was contributed in the Insight Journal paper:
+ * "Generalizing vesselness with respect to dimensionality and shape"
+ * by Antiga L.
+ * http://hdl.handle.net/1926/576
+ * http://www.insight-journal.org/browse/publication/175
+ *
+ *
+ * \author Luca Antiga Ph.D. Medical Imaging Unit,
+ * Bioengineering Department, Mario Negri Institute, Italy.
+ *
+ * \sa HessianToObjectnessMeasureImageFilter
+ * \sa Hessian3DToVesselnessMeasureImageFilter
+ * \sa HessianSmoothed3DToVesselnessMeasureImageFilter
+ * \sa HessianRecursiveGaussianImageFilter
+ * \sa SymmetricEigenAnalysisImageFilter
+ * \sa SymmetricSecondRankTensor
+ *
+ * \ingroup IntensityImageFilters TensorObjects
+ *
+ * \ingroup ITKReview
+ */
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage = TInputImage >
+class ITK_EXPORT MultiScaleHessianBasedMeasureImageFilter:
+ public
+ ImageToImageFilter< TInputImage, TOutputImage >
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiScaleHessianBasedMeasureImageFilter Self;
+ typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass;
+
+ typedef SmartPointer< Self > Pointer;
+ typedef SmartPointer< const Self > ConstPointer;
+
+ typedef TInputImage InputImageType;
+ typedef TOutputImage OutputImageType;
+ typedef THessianImage HessianImageType;
+
+ typedef ImageToImageFilter< HessianImageType, OutputImageType > HessianToMeasureFilterType;
+
+ typedef typename TInputImage::PixelType InputPixelType;
+ typedef typename TOutputImage::PixelType OutputPixelType;
+ typedef typename TOutputImage::RegionType OutputRegionType;
+
+ /** Image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension);
+
+ /** Types for Scales image */
+ typedef float ScalesPixelType;
+ typedef Image< ScalesPixelType, itkGetStaticConstMacro(ImageDimension) > ScalesImageType;
+
+ /** Hessian computation filter. */
+ typedef HessianRecursiveGaussianImageFilter< InputImageType, HessianImageType > HessianFilterType;
+
+ /** Update image buffer that holds the best objectness response. This is not redundant from
+ the output image because the latter may not be of float type, which is required for the comparisons
+ between responses at different scales. */
+ typedef Image< double, itkGetStaticConstMacro(ImageDimension) > UpdateBufferType;
+ typedef typename UpdateBufferType::ValueType BufferValueType;
+
+ typedef typename Superclass::DataObjectPointer DataObjectPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Runtime information support. */
+ itkTypeMacro(MultiScaleHessianBasedMeasureImageFilter,
+ ImageToImageFilter);
+
+ /** Set/Get macros for SigmaMin */
+ itkSetMacro(SigmaMinimum, double);
+ itkGetConstMacro(SigmaMinimum, double);
+
+ /** Set/Get macros for SigmaMax */
+ itkSetMacro(SigmaMaximum, double);
+ itkGetConstMacro(SigmaMaximum, double);
+
+ /** Set/Get macros for Number of Scales */
+ itkSetMacro(NumberOfSigmaSteps, unsigned int);
+ itkGetConstMacro(NumberOfSigmaSteps, unsigned int);
+
+ /** Set/Get HessianToMeasureFilter. This will be a filter that takes
+ Hessian input image and produces enhanced output scalar image. The filter must derive from
+ itk::ImageToImage filter */
+ itkSetObjectMacro(HessianToMeasureFilter, HessianToMeasureFilterType);
+ itkGetObjectMacro(HessianToMeasureFilter, HessianToMeasureFilterType);
+
+ /** Methods to turn on/off flag to inform the filter that the Hessian-based measure
+ is non-negative (classical measures like Sato's and Frangi's are), hence it has a minimum
+ at zero. In this case, the update buffer is initialized at zero, and the output scale and Hessian
+ are zero in case the Hessian-based measure returns zero for all scales. Otherwise, the minimum
+ output scale and Hessian are the ones obtained at scale SigmaMinimum. On by default.
+ */
+ itkSetMacro(NonNegativeHessianBasedMeasure, bool);
+ itkGetConstMacro(NonNegativeHessianBasedMeasure, bool);
+ itkBooleanMacro(NonNegativeHessianBasedMeasure);
+
+ typedef enum { EquispacedSigmaSteps = 0,
+ LogarithmicSigmaSteps = 1 } SigmaStepMethodType;
+
+ /** Set/Get the method used to generate scale sequence (Equispaced
+ * or Logarithmic) */
+ itkSetMacro(SigmaStepMethod, SigmaStepMethodType);
+ itkGetConstMacro(SigmaStepMethod, SigmaStepMethodType);
+
+ /**Set equispaced sigma step method */
+ void SetSigmaStepMethodToEquispaced();
+
+ /**Set logartihmic sigma step method */
+ void SetSigmaStepMethodToLogarithmic();
+
+ /** Get the image containing the Hessian computed at the best
+ * response scale */
+ const HessianImageType * GetHessianOutput() const;
+
+ /** Get the image containing the scales at which each pixel gave the
+ * best response */
+ const ScalesImageType * GetScalesOutput() const;
+
+ void EnlargeOutputRequestedRegion(DataObject *);
+
+ /** Methods to turn on/off flag to generate an image with scale values at
+ * each pixel for the best vesselness response */
+ itkSetMacro(GenerateScalesOutput, bool);
+ itkGetConstMacro(GenerateScalesOutput, bool);
+ itkBooleanMacro(GenerateScalesOutput);
+
+ /** Methods to turn on/off flag to generate an image with hessian values at
+ * each pixel for the best vesselness response */
+ itkSetMacro(GenerateHessianOutput, bool);
+ itkGetConstMacro(GenerateHessianOutput, bool);
+ itkBooleanMacro(GenerateHessianOutput);
+
+ /** This is overloaded to create the Scales and Hessian output images */
+ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
+ using Superclass::MakeOutput;
+ virtual DataObjectPointer MakeOutput(DataObjectPointerArraySizeType idx);
+
+protected:
+ MultiScaleHessianBasedMeasureImageFilter();
+ ~MultiScaleHessianBasedMeasureImageFilter() {}
+ void PrintSelf(std::ostream & os, Indent indent) const;
+
+ /** Generate Data */
+ void GenerateData(void);
+
+private:
+ void UpdateMaximumResponse(double sigma);
+
+ double ComputeSigmaValue(int scaleLevel);
+
+ void AllocateUpdateBuffer();
+
+ //purposely not implemented
+ MultiScaleHessianBasedMeasureImageFilter(const Self &);
+ void operator=(const Self &); //purposely not implemented
+
+ bool m_NonNegativeHessianBasedMeasure;
+
+ double m_SigmaMinimum;
+ double m_SigmaMaximum;
+
+ unsigned int m_NumberOfSigmaSteps;
+ SigmaStepMethodType m_SigmaStepMethod;
+
+ typename HessianToMeasureFilterType::Pointer m_HessianToMeasureFilter;
+
+ typename HessianFilterType::Pointer m_HessianFilter;
+
+ typename UpdateBufferType::Pointer m_UpdateBuffer;
+
+ bool m_GenerateScalesOutput;
+ bool m_GenerateHessianOutput;
+};
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkMultiScaleHessianBasedMeasureImageFilter.hxx"
+#endif
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkMultiScaleHessianBasedMeasureImageFilter.hxx b/Submodules/c3d/itkextras/itkMultiScaleHessianBasedMeasureImageFilter.hxx
new file mode 100644
index 0000000..1af5faf
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkMultiScaleHessianBasedMeasureImageFilter.hxx
@@ -0,0 +1,435 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+#ifndef __itkMultiScaleHessianBasedMeasureImageFilter_hxx
+#define __itkMultiScaleHessianBasedMeasureImageFilter_hxx
+
+#include "itkMultiScaleHessianBasedMeasureImageFilter.h"
+#include "itkImageRegionIterator.h"
+#include "vnl/vnl_math.h"
+
+/*
+ *
+ * This code was contributed in the Insight Journal paper:
+ * "Efficient implementation of kernel filtering"
+ * by Beare R., Lehmann G
+ * http://hdl.handle.net/1926/576
+ * http://www.insight-journal.org/browse/publication/175
+ *
+ */
+
+namespace itk
+{
+/**
+ * Constructor
+ */
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::MultiScaleHessianBasedMeasureImageFilter()
+{
+ m_NonNegativeHessianBasedMeasure = true;
+
+ m_SigmaMinimum = 0.2;
+ m_SigmaMaximum = 2.0;
+
+ m_NumberOfSigmaSteps = 10;
+ m_SigmaStepMethod = Self::LogarithmicSigmaSteps;
+
+ m_HessianFilter = HessianFilterType::New();
+ m_HessianToMeasureFilter = NULL;
+
+ //Instantiate Update buffer
+ m_UpdateBuffer = UpdateBufferType::New();
+
+ m_GenerateScalesOutput = false;
+ m_GenerateHessianOutput = false;
+
+ typename ScalesImageType::Pointer scalesImage = ScalesImageType::New();
+ typename HessianImageType::Pointer hessianImage = HessianImageType::New();
+ this->ProcessObject::SetNumberOfRequiredOutputs(3);
+ this->ProcessObject::SetNthOutput( 1, scalesImage.GetPointer() );
+ this->ProcessObject::SetNthOutput( 2, hessianImage.GetPointer() );
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+void
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::EnlargeOutputRequestedRegion(DataObject *output)
+{
+ // currently this filter can not stream so we must set the outputs
+ // requested region to the largest
+ output->SetRequestedRegionToLargestPossibleRegion();
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+typename MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >::DataObjectPointer
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::MakeOutput(DataObjectPointerArraySizeType idx)
+{
+ if ( idx == 1 )
+ {
+ return static_cast< DataObject * >( ScalesImageType::New().GetPointer() );
+ }
+ if ( idx == 2 )
+ {
+ return static_cast< DataObject * >( HessianImageType::New().GetPointer() );
+ }
+ return Superclass::MakeOutput(idx);
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+void
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::AllocateUpdateBuffer()
+{
+ /* The update buffer looks just like the output and holds the best response
+ in the objectness measure */
+
+ typename TOutputImage::Pointer output = this->GetOutput();
+
+ // this copies meta data describing the output such as origin,
+ // spacing and the largest region
+ m_UpdateBuffer->CopyInformation(output);
+
+ m_UpdateBuffer->SetRequestedRegion( output->GetRequestedRegion() );
+ m_UpdateBuffer->SetBufferedRegion( output->GetBufferedRegion() );
+ m_UpdateBuffer->Allocate();
+
+ // Update buffer is used for > comparisons so make it really really small,
+ // just to be sure. Thanks to Hauke Heibel.
+ if ( m_NonNegativeHessianBasedMeasure )
+ {
+ m_UpdateBuffer->FillBuffer(itk::NumericTraits< BufferValueType >::Zero);
+ }
+ else
+ {
+ m_UpdateBuffer->FillBuffer( itk::NumericTraits< BufferValueType >::NonpositiveMin() );
+ }
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+void
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::GenerateData()
+{
+ // TODO: Move the allocation to a derived AllocateOutputs method
+ // Allocate the output
+ this->GetOutput()->SetBufferedRegion( this->GetOutput()->GetRequestedRegion() );
+ this->GetOutput()->Allocate();
+
+ if ( m_HessianToMeasureFilter.IsNull() )
+ {
+ itkExceptionMacro(" HessianToMeasure filter is not set. Use SetHessianToMeasureFilter() ");
+ }
+
+ if ( m_GenerateScalesOutput )
+ {
+ typename ScalesImageType::Pointer scalesImage =
+ dynamic_cast< ScalesImageType * >( this->ProcessObject::GetOutput(1) );
+
+ scalesImage->SetBufferedRegion( scalesImage->GetRequestedRegion() );
+ scalesImage->Allocate();
+ scalesImage->FillBuffer(0);
+ }
+
+ if ( m_GenerateHessianOutput )
+ {
+ typename HessianImageType::Pointer hessianImage =
+ dynamic_cast< HessianImageType * >( this->ProcessObject::GetOutput(2) );
+
+ hessianImage->SetBufferedRegion( hessianImage->GetRequestedRegion() );
+ hessianImage->Allocate();
+ // SymmetricSecondRankTensor is already filled with zero elements at
+ // construction.
+ // No strict need of filling the buffer, but we do it explicitly here to
+ // make sure.
+ typename HessianImageType::PixelType zeroTensor(0.0);
+ hessianImage->FillBuffer(zeroTensor);
+ }
+
+ // Allocate the buffer
+ AllocateUpdateBuffer();
+
+ typename InputImageType::ConstPointer input = this->GetInput();
+
+ this->m_HessianFilter->SetInput(input);
+
+ this->m_HessianFilter->SetNormalizeAcrossScale(true);
+
+ // Create a process accumulator for tracking the progress of this
+ // minipipeline
+ ProgressAccumulator::Pointer progress = ProgressAccumulator::New();
+ progress = ProgressAccumulator::New();
+ progress->SetMiniPipelineFilter(this);
+
+ // prevent a divide by zero
+ if ( m_NumberOfSigmaSteps > 0 )
+ {
+ progress->RegisterInternalFilter(this->m_HessianFilter, .5 / m_NumberOfSigmaSteps);
+ progress->RegisterInternalFilter(this->m_HessianToMeasureFilter, .5 / m_NumberOfSigmaSteps);
+ }
+
+ double sigma = m_SigmaMinimum;
+
+ int scaleLevel = 1;
+
+ while ( sigma <= m_SigmaMaximum )
+ {
+ if ( m_NumberOfSigmaSteps == 0 )
+ {
+ break;
+ }
+
+ itkDebugMacro (<< "Computing measure for scale with sigma = " << sigma);
+
+ m_HessianFilter->SetSigma(sigma);
+
+ m_HessianToMeasureFilter->SetInput ( m_HessianFilter->GetOutput() );
+
+ m_HessianToMeasureFilter->Update();
+
+ this->UpdateMaximumResponse(sigma);
+
+ sigma = this->ComputeSigmaValue(scaleLevel);
+
+ scaleLevel++;
+
+ // reset the progress accumulator after each pass to continue
+ // addition of progress for the next pass
+ progress->ResetFilterProgressAndKeepAccumulatedProgress();
+
+ if ( m_NumberOfSigmaSteps == 1 )
+ {
+ break;
+ }
+ }
+
+ // Write out the best response to the output image
+ // we can assume that the meta-data should match between these two
+ // image, therefore we iterate over the desired output region
+ OutputRegionType outputRegion = this->GetOutput()->GetBufferedRegion();
+ ImageRegionIterator< UpdateBufferType > it(m_UpdateBuffer, outputRegion);
+ it.GoToBegin();
+
+ ImageRegionIterator< TOutputImage > oit(this->GetOutput(), outputRegion);
+ oit.GoToBegin();
+
+ while ( !oit.IsAtEnd() )
+ {
+ oit.Value() = static_cast< OutputPixelType >( it.Get() );
+ ++oit;
+ ++it;
+ }
+
+ // Release data from the update buffer.
+ m_UpdateBuffer->ReleaseData();
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+void
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::UpdateMaximumResponse(double sigma)
+{
+ // the meta-data should match between these images, therefore we
+ // iterate over the desired output region
+ OutputRegionType outputRegion = this->GetOutput()->GetBufferedRegion();
+
+ ImageRegionIterator< UpdateBufferType > oit(m_UpdateBuffer, outputRegion);
+
+ typename ScalesImageType::Pointer scalesImage = static_cast< ScalesImageType * >( this->ProcessObject::GetOutput(1) );
+ ImageRegionIterator< ScalesImageType > osit;
+
+ typename HessianImageType::Pointer hessianImage = static_cast< HessianImageType * >( this->ProcessObject::GetOutput(2) );
+ ImageRegionIterator< HessianImageType > ohit;
+
+ oit.GoToBegin();
+ if ( m_GenerateScalesOutput )
+ {
+ osit = ImageRegionIterator< ScalesImageType >(scalesImage, outputRegion);
+ osit.GoToBegin();
+ }
+ if ( m_GenerateHessianOutput )
+ {
+ ohit = ImageRegionIterator< HessianImageType >(hessianImage, outputRegion);
+ ohit.GoToBegin();
+ }
+
+ typedef typename HessianToMeasureFilterType::OutputImageType HessianToMeasureOutputImageType;
+
+ ImageRegionIterator< HessianToMeasureOutputImageType > it(m_HessianToMeasureFilter->GetOutput(), outputRegion);
+ ImageRegionIterator< HessianImageType > hit(m_HessianFilter->GetOutput(), outputRegion);
+
+ it.GoToBegin();
+ hit.GoToBegin();
+
+ while ( !oit.IsAtEnd() )
+ {
+ if ( oit.Value() < it.Value() )
+ {
+ oit.Value() = it.Value();
+ if ( m_GenerateScalesOutput )
+ {
+ osit.Value() = static_cast< ScalesPixelType >( sigma );
+ }
+ if ( m_GenerateHessianOutput )
+ {
+ ohit.Value() = hit.Value();
+ }
+ }
+ ++oit;
+ ++it;
+ if ( m_GenerateScalesOutput )
+ {
+ ++osit;
+ }
+ if ( m_GenerateHessianOutput )
+ {
+ ++ohit;
+ ++hit;
+ }
+ }
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+double
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::ComputeSigmaValue(int scaleLevel)
+{
+ double sigmaValue;
+
+ if ( m_NumberOfSigmaSteps < 2 )
+ {
+ return m_SigmaMinimum;
+ }
+
+ switch ( m_SigmaStepMethod )
+ {
+ case Self::EquispacedSigmaSteps:
+ {
+ const double stepSize = vnl_math_max( 1e-10, ( m_SigmaMaximum - m_SigmaMinimum ) / ( m_NumberOfSigmaSteps - 1 ) );
+ sigmaValue = m_SigmaMinimum + stepSize * scaleLevel;
+ break;
+ }
+ case Self::LogarithmicSigmaSteps:
+ {
+ const double stepSize =
+ vnl_math_max( 1e-10, ( vcl_log(m_SigmaMaximum) - vcl_log(m_SigmaMinimum) ) / ( m_NumberOfSigmaSteps - 1 ) );
+ sigmaValue = vcl_exp(vcl_log (m_SigmaMinimum) + stepSize * scaleLevel);
+ break;
+ }
+ default:
+ throw ExceptionObject(__FILE__, __LINE__, "Invalid SigmaStepMethod.", ITK_LOCATION);
+ break;
+ }
+
+ return sigmaValue;
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+void
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::SetSigmaStepMethodToEquispaced()
+{
+ this->SetSigmaStepMethod(Self::EquispacedSigmaSteps);
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+void
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::SetSigmaStepMethodToLogarithmic()
+{
+ this->SetSigmaStepMethod(Self::LogarithmicSigmaSteps);
+}
+
+/** Get the image containing the Hessian at which each pixel gave the
+ * best response */
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+const
+typename MultiScaleHessianBasedMeasureImageFilter< TInputImage, THessianImage, TOutputImage >::HessianImageType *
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::GetHessianOutput() const
+{
+ return static_cast< const HessianImageType * >( this->ProcessObject::GetOutput(2) );
+}
+
+/** Get the image containing the scales at which each pixel gave the
+ * best response */
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+const
+typename MultiScaleHessianBasedMeasureImageFilter< TInputImage, THessianImage, TOutputImage >::ScalesImageType *
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::GetScalesOutput() const
+{
+ return static_cast< const ScalesImageType * >( this->ProcessObject::GetOutput(1) );
+}
+
+template< typename TInputImage,
+ typename THessianImage,
+ typename TOutputImage >
+void
+MultiScaleHessianBasedMeasureImageFilter
+< TInputImage, THessianImage, TOutputImage >
+::PrintSelf(std::ostream & os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+
+ os << indent << "SigmaMinimum: " << m_SigmaMinimum << std::endl;
+ os << indent << "SigmaMaximum: " << m_SigmaMaximum << std::endl;
+ os << indent << "NumberOfSigmaSteps: " << m_NumberOfSigmaSteps << std::endl;
+ os << indent << "SigmaStepMethod: " << m_SigmaStepMethod << std::endl;
+ os << indent << "HessianToMeasureFilter: " << m_HessianToMeasureFilter << std::endl;
+ os << indent << "NonNegativeHessianBasedMeasure: " << m_NonNegativeHessianBasedMeasure << std::endl;
+ os << indent << "GenerateScalesOutput: " << m_GenerateScalesOutput << std::endl;
+ os << indent << "GenerateHessianOutput: " << m_GenerateHessianOutput << std::endl;
+}
+} // end namespace itk
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkOrientedRASImage.h b/Submodules/c3d/itkextras/itkOrientedRASImage.h
new file mode 100644
index 0000000..37e969d
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkOrientedRASImage.h
@@ -0,0 +1,271 @@
+#ifndef __itkOrientedRASImage_h_
+#define __itkOrientedRASImage_h_
+
+#include "itkImage.h"
+
+namespace itk {
+
+/**
+ * Oriented image with RAS physical coordinates (as opposed to LPS)
+ */
+template <class TPixel, unsigned int VImageDimension>
+class ITK_EXPORT OrientedRASImage : public Image<TPixel, VImageDimension>
+{
+public:
+ /** Standard class typedefs */
+ typedef OrientedRASImage Self;
+ typedef Image<TPixel, VImageDimension> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+ typedef WeakPointer<const Self> ConstWeakPointer;
+ typedef Matrix<double, VImageDimension+1, VImageDimension+1> TransformMatrixType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(OrientedRASImage, Image);
+
+ /** Index typedef support. An index is used to access pixel values. */
+ typedef typename Superclass::IndexType IndexType;
+
+ /** Direction typedef support. The direction cosines of the image. */
+ typedef typename Superclass::DirectionType DirectionType;
+
+ /** Spacing typedef support. Spacing holds the size of a pixel. The
+ * spacing is the geometric distance between image samples. */
+ typedef typename Superclass::SpacingType SpacingType;
+
+ typedef typename Superclass::AccessorType AccessorType;
+ typedef typename Superclass::AccessorFunctorType AccessorFunctorType;
+ typedef typename Superclass::IOPixelType IOPixelType;
+
+ /** Tyepdef for the functor used to access a neighborhood of pixel pointers.*/
+ typedef NeighborhoodAccessorFunctor< Self >
+ NeighborhoodAccessorFunctorType;
+
+ /** Return the NeighborhoodAccessor functor. This method is called by the
+ * neighborhood iterators. */
+ NeighborhoodAccessorFunctorType GetNeighborhoodAccessor()
+ { return NeighborhoodAccessorFunctorType(); }
+
+ /** Return the NeighborhoodAccessor functor. This method is called by the
+ * neighborhood iterators. */
+ const NeighborhoodAccessorFunctorType GetNeighborhoodAccessor() const
+ { return NeighborhoodAccessorFunctorType(); }
+
+
+ /** \brief Get the continuous index from a physical point
+ *
+ * Returns true if the resulting index is within the image, false otherwise.
+ * \sa Transform */
+ template<class TCoordRep>
+ bool TransformRASPhysicalPointToContinuousIndex(
+ const Point<TCoordRep, VImageDimension>& point,
+ ContinuousIndex<TCoordRep, VImageDimension>& index ) const
+ {
+ Point<TCoordRep, VImageDimension> p_lps = point;
+ p_lps[0] = -point[0]; p_lps[1] = -point[1];
+ return Superclass::TransformPhysicalPointToContinuousIndex(p_lps, index);
+ }
+
+ /** Get the index (discrete) from a physical point.
+ * Floating point index results are truncated to integers.
+ * Returns true if the resulting index is within the image, false otherwise
+ * \sa Transform */
+ template<class TCoordRep>
+ bool TransformRASPhysicalPointToIndex(
+ const Point<TCoordRep, VImageDimension>& point,
+ IndexType & index ) const
+ {
+ Point<TCoordRep, VImageDimension> p_lps = point;
+ p_lps[0] = -point[0]; p_lps[1] = -point[1];
+ return Superclass::TransformPhysicalPointToIndex(p_lps, index);
+ }
+
+ /** Get a physical point (in the space which
+ * the origin and spacing infomation comes from)
+ * from a continuous index (in the index space)
+ * \sa Transform */
+ template<class TCoordRep>
+ void TransformContinuousIndexToRASPhysicalPoint(
+ const ContinuousIndex<TCoordRep, VImageDimension>& index,
+ Point<TCoordRep, VImageDimension>& point ) const
+ {
+ Superclass::TransformContinuousIndexToPhysicalPoint(index, point);
+ point[0] = -point[0];
+ point[1] = -point[1];
+ }
+
+ /** Get a physical point (in the space which
+ * the origin and spacing infomation comes from)
+ * from a discrete index (in the index space)
+ *
+ * \sa Transform */
+ template<class TCoordRep>
+ void TransformIndexToRASPhysicalPoint(
+ const IndexType & index,
+ Point<TCoordRep, VImageDimension>& point ) const
+ {
+ Superclass::TransformIndexToPhysicalPoint(index, point);
+ point[0] = -point[0];
+ point[1] = -point[1];
+ }
+
+ /** Take a vector or covariant vector that has been computed in the
+ * coordinate system parallel to the image grid and rotate it by the
+ * direction cosines in order to get it in terms of the coordinate system of
+ * the image acquisition device. This implementation in the Image
+ * multiply the array (vector or covariant vector) by the matrix of Direction
+ * Cosines. The arguments of the method are of type FixedArray to make
+ * possible to use this method with both Vector and CovariantVector.
+ * The Method is implemented differently in the itk::Image.
+ *
+ * \sa Image
+ */
+ template<class TCoordRep>
+ void TransformLocalVectorToRASPhysicalVector(
+ const FixedArray<TCoordRep, VImageDimension> & inputGradient,
+ FixedArray<TCoordRep, VImageDimension> & outputGradient ) const
+ {
+ Superclass::TransformLocalVectorToPhysicalVector(inputGradient, outputGradient);
+ outputGradient[0] = -outputGradient[0];
+ outputGradient[1] = -outputGradient[1];
+ }
+
+ /**
+ * Get a matrix that maps points voxel coordinates to RAS coordinates
+ */
+ TransformMatrixType GetVoxelSpaceToRASPhysicalSpaceMatrix()
+ {
+ // Generate intermediate terms
+ vnl_matrix<double> m_dir, m_ras_matrix;
+ vnl_diag_matrix<double> m_scale, m_lps_to_ras;
+ vnl_vector<double> v_origin, v_ras_offset;
+
+ // Compute the matrix
+ m_dir = this->GetDirection().GetVnlMatrix();
+ m_scale.set(this->GetSpacing().GetVnlVector());
+ m_lps_to_ras.set(vnl_vector<double>(VImageDimension, 1.0));
+ m_lps_to_ras[0] = -1;
+ m_lps_to_ras[1] = -1;
+ m_ras_matrix = m_lps_to_ras * m_dir * m_scale;
+
+ // Compute the vector
+ v_origin = this->GetOrigin().GetVnlVector();
+ v_ras_offset = m_lps_to_ras * v_origin;
+
+ // Create the larger matrix
+ TransformMatrixType mat;
+ vnl_vector<double> vcol(VImageDimension+1, 1.0);
+ vcol.update(v_ras_offset);
+ mat.SetIdentity();
+ mat.GetVnlMatrix().update(m_ras_matrix);
+ mat.GetVnlMatrix().set_column(VImageDimension, vcol);
+
+ return mat;
+ };
+
+ /**
+ * Set a matrix that maps points voxel coordinates to RAS coordinates
+ */
+ void SetVoxelSpaceToRASPhysicalSpaceMatrix(vnl_matrix<double> mat)
+ {
+ // Generate intermediate terms
+ vnl_matrix<double> m_dir, m_ras_matrix, m_dist;
+ vnl_diag_matrix<double> m_ras_to_lps, m_scale;
+ vnl_vector<double> v_origin ;
+ vnl_vector<double> m_spacing(VImageDimension, 0.0);
+
+ // Get the dim x dim submatrix from mat
+ vnl_matrix<double> smat(VImageDimension,VImageDimension,0.0);
+ for (size_t i=0; i< VImageDimension; i++)
+ for (size_t j=0; j< VImageDimension; j++)
+ smat[i][j] = mat[i][j];
+ //smat = mat.get_n_rows(0, VImageDimension).get_n_columns(0, VImageDimension);
+ // Get the origin
+ m_ras_to_lps.set(vnl_vector<double>(VImageDimension, 1.0));
+ m_ras_to_lps[0] = -1;
+ m_ras_to_lps[1] = -1;
+ vnl_vector<double> v_ras_offset(VImageDimension,0.0);
+ v_ras_offset.fill(0.0);
+ for (size_t i=0; i< VImageDimension; i++)
+ v_ras_offset[i] = mat[i][VImageDimension];
+ v_origin = m_ras_to_lps * v_ras_offset;
+
+ // Get the Spacing
+ // First, create a matrix of the form [1 0 0; 0 1 0; 0 0 1; 0 0 0] to get distances between consecutive voxels
+ // along each axis. When RAS mat will be applied to this matrix, we'll have 3 distance vectors
+ vnl_diag_matrix<double> offsetmat(VImageDimension+1, VImageDimension);
+ offsetmat.fill(0.0);
+ for (size_t i=0; i < VImageDimension+1; i++)
+ offsetmat[i]=1.0;
+ m_dist = mat * offsetmat;
+ // Then compute magnitude of the distance vectors, that's our spacing
+ for (size_t i=0; i< VImageDimension; i++)
+ {
+ vnl_vector<double> distcol(m_dist.get_column(i));
+ m_spacing[i] = distcol.magnitude();
+ }
+ m_scale.set(m_spacing);
+
+ // Get the direction
+ m_scale.invert_in_place();
+ m_dir = m_ras_to_lps * smat * m_scale;
+
+ // Set everything
+ itk::Matrix<double, VImageDimension, VImageDimension> dir;
+ dir.SetIdentity();
+ for (size_t i=0; i< VImageDimension; i++)
+ for (size_t j=0; j< VImageDimension; j++)
+ dir[i][j] = m_dir[i][j];
+ this->SetDirection(dir);
+ double origin[VImageDimension];
+ for (size_t i=0; i< VImageDimension; i++)
+ origin[i] = v_origin[i];
+ this->SetOrigin(origin);
+ double spacing[VImageDimension];
+ for (size_t i=0; i< VImageDimension; i++)
+ spacing[i] = m_spacing[i];
+ this->SetSpacing(spacing);
+
+ };
+
+ /**
+ * Get a matrix that maps points in the x * spacing + origin space to
+ * the RAS space
+ */
+ TransformMatrixType GetSpacingOriginPhysicalSpaceToRASPhysicalSpaceMatrix()
+ {
+ TransformMatrixType mat;
+ mat.SetIdentity();
+
+ for(size_t i = 0; i < VImageDimension; i++)
+ {
+ double ras_flip = (i < 2) ? -1 : 1;
+ mat[i][VImageDimension] = ras_flip * this->GetOrigin()[i];
+ for(size_t j = 0; j < VImageDimension; j++)
+ {
+ mat[i][j] = ras_flip * this->GetDirection()(i,j) * this->GetSpacing()[i];
+ mat[i][VImageDimension] -= ras_flip * this->GetDirection()(i,j) * this->GetOrigin()[i];
+ }
+ }
+
+ return mat;
+ }
+
+
+
+protected:
+ OrientedRASImage() {};
+ virtual ~OrientedRASImage() {};
+
+private:
+ OrientedRASImage(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+};
+
+} //namespace itk
+
+#endif
+
diff --git a/Submodules/c3d/itkextras/itkSLICSuperVoxelImageFilter.h b/Submodules/c3d/itkextras/itkSLICSuperVoxelImageFilter.h
new file mode 100644
index 0000000..f2c310d
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkSLICSuperVoxelImageFilter.h
@@ -0,0 +1,111 @@
+#ifndef __itkSLICSuperVoxelImageFilter_h_
+#define __itkSLICSuperVoxelImageFilter_h_
+
+#include "itkImageToImageFilter.h"
+#include "itkSmartPointer.h"
+#include "itkBarrier.h"
+
+namespace itk {
+
+template <typename TInputImage, typename TLabelImage, typename TRealImage>
+class ITK_EXPORT SLICSuperVoxelImageFilter : public ImageToImageFilter<TInputImage, TLabelImage>
+{
+public:
+ typedef SLICSuperVoxelImageFilter Self;
+ typedef ImageToImageFilter<TInputImage, TLabelImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::OutputImageType OutputImageType;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputImageType::RegionType RegionType;
+ typedef typename InputImageType::IndexType IndexType;
+ typedef typename InputImageType::SizeType SizeType;
+ typedef typename InputImageType::OffsetType OffsetType;
+ typedef typename InputImageType::PointType PointType;
+ typedef typename TRealImage::PixelType RealPixelType;
+
+ /** Image dimension */
+ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Runtime information support. */
+ itkTypeMacro(SLICSuperVoxelImageFilter, ImageToImageFilter);
+
+ /** Set a scalar-valued gradient magnitude image, used to push initial cluster
+ * centers away from the edges */
+ void SetGradientImage(TRealImage *gradImage);
+
+ /** Set the number of supervoxel seeds in each dimension */
+ itkSetMacro(SeedsPerDimension, int);
+ itkGetConstMacro(SeedsPerDimension, int);
+
+ /** Set the weighting between intensity similarity and spatial distance */
+ itkSetMacro(MParameter, double);
+ itkGetConstMacro(MParameter, double);
+
+protected:
+
+ SLICSuperVoxelImageFilter();
+ ~SLICSuperVoxelImageFilter() {}
+
+ void PrintSelf(std::ostream & os, Indent indent) const {};
+
+ void BeforeThreadedGenerateData(void);
+
+ void ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId);
+
+private:
+ SLICSuperVoxelImageFilter(const Self &); //purposely not
+ // implemented
+ void operator=(const Self &); //purposely not
+ // implemented
+
+ typename TRealImage::Pointer m_DistanceImage;
+ typename TRealImage::Pointer m_GradientImage;
+ double m_MParameter;
+ double m_SeedsPerDimension;
+
+ struct Cluster {
+ IndexType Index;
+ InputPixelType Pixel;
+ long Count;
+ InputPixelType MinPixel, MaxPixel;
+ double MaxPixelDist;
+ void Reset()
+ { Index.Fill(0); Pixel = 0; Count = 0;
+ MinPixel = 1e100; MaxPixel = -1e100;
+ MaxPixelDist = 0; }
+ };
+
+ // Cluster center descriptor
+ typedef std::vector<Cluster> ClusterVector;
+ ClusterVector m_Clusters;
+
+ // Per-thread cluster lists
+ std::vector<ClusterVector> m_PerThreadClusters;
+
+ // Barrier for threading
+ typename Barrier::Pointer m_Barrier;
+
+ // Search region size
+ SizeType m_SearchRegionSize;
+ OffsetType m_SearchRegionOffset;
+ double m_SearchRegionMaxWidth;
+
+ // Distance computation
+ double ComputeDistance(const IndexType &index, const InputPixelType &pixel, Cluster &cluster);
+};
+
+// namespace
+}
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkSLICSuperVoxelImageFilter.hxx"
+#endif
+
+#endif
diff --git a/Submodules/c3d/itkextras/itkSLICSuperVoxelImageFilter.hxx b/Submodules/c3d/itkextras/itkSLICSuperVoxelImageFilter.hxx
new file mode 100644
index 0000000..ebb9e99
--- /dev/null
+++ b/Submodules/c3d/itkextras/itkSLICSuperVoxelImageFilter.hxx
@@ -0,0 +1,285 @@
+#ifndef __itkSLICSuperVoxelImageFilter_hxx_
+#define __itkSLICSuperVoxelImageFilter_hxx_
+
+#include "itkSLICSuperVoxelImageFilter.h"
+#include "itkImageToImageFilter.h"
+#include "itkConstNeighborhoodIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkImageRegionIterator.h"
+#include <algorithm>
+
+namespace itk {
+
+
+template <typename TInputImage, typename TLabelImage, typename TRealImage>
+SLICSuperVoxelImageFilter<TInputImage, TLabelImage, TRealImage>
+::SLICSuperVoxelImageFilter()
+{
+ m_MParameter = 20;
+ m_SeedsPerDimension = 20;
+
+ this->SetNumberOfRequiredInputs(2);
+
+ m_Barrier = Barrier::New();
+}
+
+template <typename TInputImage, typename TLabelImage, typename TRealImage>
+void
+SLICSuperVoxelImageFilter<TInputImage, TLabelImage, TRealImage>
+::SetGradientImage(TRealImage *image)
+{
+ m_GradientImage = image;
+ this->SetNthInput(1, image);
+}
+
+template <typename TInputImage, typename TLabelImage, typename TRealImage>
+void
+SLICSuperVoxelImageFilter<TInputImage, TLabelImage, TRealImage>
+::BeforeThreadedGenerateData(void)
+{
+ typename OutputImageType::Pointer output = this->GetOutput();
+ typename InputImageType::ConstPointer input = this->GetInput();
+
+ // Allocate the distance image
+ m_DistanceImage = TRealImage::New();
+ m_DistanceImage->SetRegions(output->GetBufferedRegion());
+ m_DistanceImage->CopyInformation(output);
+ m_DistanceImage->Allocate();
+ m_DistanceImage->FillBuffer(1.0e100);
+
+ // Initialize the label images
+ output->FillBuffer(-1);
+
+ // To sample the initial cluster centers, we create a dummy image of cluster
+ // centers
+ typename TLabelImage::Pointer imgCluster = TLabelImage::New();
+ RegionType rgnOutput;
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ rgnOutput.SetSize(i, m_SeedsPerDimension);
+ rgnOutput.SetIndex(i, 0);
+ }
+
+ imgCluster->SetRegions(rgnOutput);
+ imgCluster->SetOrigin(input->GetOrigin());
+ imgCluster->SetDirection(input->GetDirection());
+
+ Vector<double,ImageDimension> newspc;
+
+ m_SearchRegionMaxWidth = 0;
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ int dim = input->GetBufferedRegion().GetSize(i);
+ double spc = input->GetSpacing()[i];
+ double width = dim * spc;
+ newspc[i] = width / m_SeedsPerDimension;
+
+ int pixWidth = dim / m_SeedsPerDimension;
+ m_SearchRegionOffset[i] = - pixWidth;
+ m_SearchRegionSize[i] = 1 + 2 * pixWidth;
+
+ if(m_SearchRegionMaxWidth < pixWidth)
+ m_SearchRegionMaxWidth = pixWidth;
+ }
+
+ imgCluster->SetSpacing(newspc);
+ imgCluster->SetPixelContainer(output->GetPixelContainer());
+
+ // Create a neighborhood iterator for the gradient image search
+ typedef itk::ConstNeighborhoodIterator<TRealImage> HoodIter;
+ SizeType radius; radius.Fill(1);
+ HoodIter itGrad(radius, m_GradientImage, m_GradientImage->GetBufferedRegion());
+
+ // Create the cluster array
+ m_Clusters.resize(imgCluster->GetBufferedRegion().GetNumberOfPixels());
+ typedef ImageRegionIteratorWithIndex<TLabelImage> OutIter;
+ int k = 0;
+ for(OutIter it(imgCluster, imgCluster->GetBufferedRegion()); !it.IsAtEnd(); ++it, ++k)
+ {
+ // Map the cluster center into a voxel position
+ itk::ContinuousIndex<double, ImageDimension> cidx;
+ for(int i = 0; i < ImageDimension; i++)
+ cidx[i] = it.GetIndex()[i] + 0.5;
+ IndexType idxInput;
+ PointType ptx;
+ imgCluster->TransformContinuousIndexToPhysicalPoint(cidx, ptx);
+ input->TransformPhysicalPointToIndex(ptx, idxInput);
+
+ // Seach the gradient magnitude image for the
+ itGrad.SetLocation(idxInput);
+
+ bool inbounds;
+ int jBest = 0;
+ RealPixelType gBest = 1e100;
+
+ for(int j = 0; j < itGrad.Size(); j++)
+ {
+ RealPixelType gval = itGrad.GetPixel(j, inbounds);
+ if(inbounds && gval < gBest)
+ {
+ gBest = gval; jBest = j;
+ }
+ }
+
+ // Now we have finally an index
+ IndexType idxCenter = itGrad.GetIndex(jBest);
+ InputPixelType pxCenter = input->GetPixel(idxCenter);
+ m_Clusters[k].Index = idxCenter;
+ m_Clusters[k].Pixel = pxCenter;
+ m_Clusters[k].Count = 1;
+ m_Clusters[k].MaxPixelDist = m_MParameter;
+ }
+
+ // Initialize the per-thread cluster data
+ m_PerThreadClusters.resize(this->GetNumberOfThreads(), m_Clusters);
+
+ // Initialize the barrier
+ m_Barrier->Initialize(this->GetNumberOfThreads());
+}
+
+template <typename TInputImage, typename TLabelImage, typename TRealImage>
+void
+SLICSuperVoxelImageFilter<TInputImage, TLabelImage, TRealImage>
+::ThreadedGenerateData(const RegionType & outputRegionForThread,
+ ThreadIdType threadId)
+{
+ typedef ImageRegionConstIteratorWithIndex<InputImageType> InputIter;
+ typedef ImageRegionIterator<OutputImageType> OutputIter;
+ typedef ImageRegionIterator<TRealImage> DistIter;
+
+ // Input and output
+ typename OutputImageType::Pointer output = this->GetOutput();
+ typename InputImageType::ConstPointer input = this->GetInput();
+
+ // A thread-specific copy of the cluster centers
+ ClusterVector &cvLocal = m_PerThreadClusters[threadId];
+
+ // This is the iterative part of the filter
+
+ // Standard size of the search neighborhood
+
+
+ // Here there is a little divergence in behavior. The main thread regulates
+ // the rest of the threads
+ for(int iter = 0; iter < 10; iter++)
+ {
+ // Update voxel memberships
+ for(int i = 0; i < m_Clusters.size(); i++)
+ {
+ // The cluster
+ Cluster &cluster = m_Clusters[i];
+
+ // Get the search region for this cluster
+ RegionType region;
+ region.SetSize(m_SearchRegionSize);
+ region.SetIndex(cluster.Index + m_SearchRegionOffset);
+
+ // Check if the region overlaps with the output region
+ if(region.Crop(outputRegionForThread))
+ {
+ // Create an iterator for the cropped region
+ InputIter itInput(input, region);
+ OutputIter itOutput(output, region);
+ DistIter itDist(m_DistanceImage, region);
+
+ while(!itInput.IsAtEnd())
+ {
+ double dist = this->ComputeDistance(itInput.GetIndex(), itInput.Get(), cluster);
+ if(itDist.Value() > dist)
+ {
+ itDist.Set(dist);
+ itOutput.Set(i);
+ }
+ ++itInput; ++itOutput; ++itDist;
+ }
+ }
+
+ // While we are in this loop, we clear the local cluster information
+ cvLocal[i].Reset();
+ }
+
+ // We have computed the membership of each voxel in our region. Now, we need to update the
+ // cluster means. However, this requires storing a separate copy of the cluster for each
+ // thread
+
+ // Create an iterator for the cropped region
+ InputIter itInput(input, outputRegionForThread);
+ OutputIter itOutput(output, outputRegionForThread);
+
+ while(!itInput.IsAtEnd())
+ {
+ int clid = itOutput.Value();
+ InputPixelType pix = itInput.Get();
+ Cluster &cl = cvLocal[clid];
+ for(int d = 0; d < ImageDimension; d++)
+ cl.Index[d] += itInput.GetIndex()[d];
+
+ if(pix > cl.MaxPixel)
+ cl.MaxPixel = pix;
+ if(pix < cl.MinPixel)
+ cl.MinPixel = pix;
+
+ cl.Pixel += pix;
+ cl.Count++;
+
+ ++itInput; ++itOutput;
+ }
+
+ // Now we need to wait for all the threads to finish
+ m_Barrier->Wait();
+
+ // Now, only the first thread is going to combine the per-thread cluster means into
+ // common cluster mean
+ if(threadId == 0)
+ {
+ m_Clusters = cvLocal;
+ for(int i = 1; i < m_Clusters.size(); i++)
+ {
+ for(int k = 0; k < m_PerThreadClusters.size(); k++)
+ {
+ for(int d = 0; d < ImageDimension; d++)
+ m_Clusters[i].Index[d] += m_PerThreadClusters[k][i].Index[d];
+ m_Clusters[i].Pixel += m_PerThreadClusters[k][i].Pixel;
+ m_Clusters[i].Count += m_PerThreadClusters[k][i].Count;
+ m_Clusters[i].MinPixel =
+ std::min(m_Clusters[i].MinPixel, m_PerThreadClusters[k][i].MinPixel);
+ m_Clusters[i].MaxPixel =
+ std::min(m_Clusters[i].MaxPixel, m_PerThreadClusters[k][i].MaxPixel);
+ }
+ for(int d = 0; d < ImageDimension; d++)
+ m_Clusters[i].Index[d] = m_Clusters[i].Index[d] / m_Clusters[i].Count;
+ m_Clusters[i].Pixel = m_Clusters[i].Pixel / m_Clusters[i].Count;
+ m_Clusters[i].MaxPixelDist = m_Clusters[i].MaxPixel - m_Clusters[i].MinPixel;
+ }
+
+ std::cout << "Cluster center 55: " << m_Clusters[55].Index << " " << m_Clusters[55].Pixel << std::endl;
+ }
+
+ // All threads must wait for thread 0 to get here
+ m_Barrier->Wait();
+ }
+}
+
+template <typename TInputImage, typename TLabelImage, typename TRealImage>
+double
+SLICSuperVoxelImageFilter<TInputImage, TLabelImage, TRealImage>
+::ComputeDistance(const IndexType &index, const InputPixelType &pixel, Cluster &cluster)
+{
+ // Compute the squared spatial distance
+ double ds2 = 0.0;
+ for(int d = 0; d < ImageDimension; d++)
+ {
+ ds2 += (index[d] - cluster.Index[d]) * (index[d] - cluster.Index[d]);
+ }
+
+ double dc2 = (pixel - cluster.Pixel) * (pixel - cluster.Pixel);
+
+ return (dc2 * m_SearchRegionMaxWidth * m_SearchRegionMaxWidth
+ + ds2 * m_MParameter * m_MParameter);
+
+}
+
+}
+
+#endif
+
diff --git a/Submodules/c3d/utilities/AffineTransformTool.cxx b/Submodules/c3d/utilities/AffineTransformTool.cxx
new file mode 100644
index 0000000..35bac09
--- /dev/null
+++ b/Submodules/c3d/utilities/AffineTransformTool.cxx
@@ -0,0 +1,785 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: AffineTransformTool.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "itkOrientedRASImage.h"
+#include "itkImageFileReader.h"
+#include <itkAffineTransform.h>
+#include <itkTransformFileReader.h>
+#include <itkTransformFileWriter.h>
+#include <itkTransformFactory.h>
+#include <itkByteSwapper.h>
+#include "vnl/vnl_matrix_fixed.h"
+#include "vnl/vnl_det.h"
+#include "vnl/vnl_inverse.h"
+#include "vnl/algo/vnl_real_eigensystem.h"
+#include "vnl/algo/vnl_qr.h"
+#include "ConvertException.h"
+#include <iostream>
+
+#define RAS_TO_FSL 0
+#define FSL_TO_RAS 1
+
+using namespace std;
+
+// Typedefs
+typedef itk::OrientedRASImage<double, 3> ImageType;
+typedef vnl_matrix_fixed<double, 4, 4> MatrixType;
+typedef std::vector<MatrixType> MatrixStack;
+
+typedef vnl_matrix_fixed<double, 3, 3> Mat3;
+typedef vnl_vector_fixed<double, 3> Vec3;
+
+
+int usage()
+{
+ cout <<
+ "RAS Affine Transform Tool"
+ "Usage: \n"
+ " c3d_affine_tool [transform_files | options] \n"
+ "Options: \n"
+ " -fsl2ras Convert FSL to RAS\n"
+ " -ras2fsl Convert RAS to FSL\n"
+ " -ref image Set reference (fixed) image - only for -fsl2ras and ras2fsl\n"
+ " -src image Set source (moving) image - only for -fsl2ras and -ras2fsl\n"
+ " -sform image Read matrix from NIfTI sform\n"
+ " -o matfile Write output matrix\n"
+ " -det Print the determinant\n"
+ " -inv Invert matrix\n"
+ " -mult Multiply matrices\n"
+ " -sqrt Matrix square root (i.e., Q s.t. A = Q * Q)\n"
+ " -itk file Import ITK transform\n"
+ " -oitk file Export ITK transform\n"
+ " -irtk file Import IRTK .dof format transform\n"
+ " -oirtk file Export IRTK .dof format transform\n"
+ " -info Print matrix\n"
+ " -info-full Print matrix and more detail about the transform\n"
+ " -rot theta vx vy vz:\n"
+ " Generate rotation matrix corresponding to rotation theta\n"
+ " (in degrees) around vector vx,vy,vz\n"
+ " -trans vx vy vz:\n"
+ " Generate matrix for translation by vx,vy,vz \n"
+ " -scale sx sy sz:\n"
+ " Generate matrix for scaling by sx,sy,sz\n"
+ ;
+ return -1;
+}
+
+void itk_read(MatrixStack &vmat, const char *fname)
+{
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> MOTBType;
+ typedef itk::AffineTransform<double, 3> AffTran;
+ itk::TransformFactory<MOTBType>::RegisterTransform();
+ itk::TransformFactory<AffTran>::RegisterTransform();
+
+ itk::TransformFileReader::Pointer fltReader = itk::TransformFileReader::New();
+ fltReader->SetFileName(fname);
+ fltReader->Update();
+
+ itk::TransformBase *base = fltReader->GetTransformList()->front();
+ typedef itk::MatrixOffsetTransformBase<double, 3, 3> MOTBType;
+ MOTBType *motb = dynamic_cast<MOTBType *>(base);
+
+ MatrixType mat;
+ mat.set_identity();
+ if(motb)
+ {
+ for(size_t r = 0; r < 3; r++)
+ {
+ for(size_t c = 0; c < 3; c++)
+ {
+ mat(r,c) = motb->GetMatrix()(r,c);
+ }
+ mat(r,3) = motb->GetOffset()[r];
+ }
+ mat(2,0) *= -1; mat(2,1) *= -1;
+ mat(0,2) *= -1; mat(1,2) *= -1;
+ mat(0,3) *= -1; mat(1,3) *= -1;
+ vmat.push_back(mat);
+ }
+ else
+ throw ConvertException("Unable to read ITK transform file %s", fname);
+}
+
+void irtk_read(MatrixStack &vmat, const char *fname)
+{
+ // Read the DOF file
+ FILE *fid = fopen(fname, "rb");
+ if(!fid)
+ throw ConvertException("Unable to read file %s", fname);
+
+ // Read the magic number (or whatever that is)
+ unsigned char h[12];
+ if(fread(h, 1, 12, fid) < 12)
+ throw ConvertException("Unable to read header from file %s", fname);
+
+ // Check magic number
+ if(h[1]!=0x0c || h[0]!=0x00 || h[3]!=0x9f || h[2]!=0x6f)
+ throw ConvertException("File %s has wrong magic number for DOF file", fname);
+
+ // Check the type of DOF (only 2/3 are supported)
+ if(h[7]!=0x02 && h[7]!=0x03)
+ throw ConvertException("DOF file %s is not a rigid or affine transform", fname);
+
+ // Allocate parameter array (defaults set below)
+ double p[12] = {0,0,0,0,0,0,100,100,100,0,0,0};
+
+ // Read double data
+ size_t nval = (h[7] == 0x02) ? 6 : 12;
+ if(fread(p, sizeof(double), nval, fid) < nval)
+ throw ConvertException("Unable to read data from file %s", fname);
+
+ // Swap bytes if necessary
+ itk::ByteSwapper<double>::SwapRangeFromSystemToBigEndian(p, nval);
+
+ // Print the transformation parameters
+ printf("DOF parameters: T=(%f, %f, %f); R = (%f, %f, %f); S = (%f, %f, %f); K = (%f, %f, %f)\n",
+ p[0],p[1],p[2],p[3],p[4],p[5],
+ p[6],p[7],p[8],p[9],p[10],p[11]);
+
+ // Assign to variables
+ double *t = p, *r = p+3, *s = p+6, *k = p+9;
+
+ // Close file
+ fclose(fid);
+
+ // Initialize matrices
+ MatrixType T, R[3], K, S;
+
+ // Create rotation matrices for X, Y, Z
+ for(size_t i = 0; i < 3; i++)
+ {
+ size_t i1 = (i + 1) % 3;
+ size_t i2 = (i + 2) % 3;
+ R[i].set_identity();
+ R[i](i1,i1) = R[i](i2,i2) = cos(r[i] * vnl_math::pi / 180.0);
+ R[i](i1,i2) = sin(r[i] * vnl_math::pi / 180.0);
+ R[i](i2,i1) = -R[i](i1,i2);
+ }
+
+ // Create the translation matrix
+ T.set_identity();
+ for(size_t i = 0; i < 3; i++)
+ T(i,3) = t[i];
+
+ // Create the scale matrix
+ S.set_identity();
+ for(size_t i = 0; i < 3; i++)
+ S(i,i) = 0.01 * s[i];
+
+ // Create the skew matrix
+ K.set_identity();
+ K(0,1) = tan(k[0] * vnl_math::pi / 180.0);
+ K(1,2) = tan(k[1] * vnl_math::pi / 180.0);
+ K(0,2) = tan(k[2] * vnl_math::pi / 180.0);
+
+ // Compute the total matrix
+ MatrixType M = T * R[0] * R[1] * R[2] * K * S;
+
+ // Push the matrix on the stack
+ vmat.push_back(M);
+}
+
+void quart_print(MatrixType &mat )
+{
+ const double epsilon = vcl_numeric_limits<double>::epsilon();
+ double m_X, m_Y, m_Z, m_W;
+
+ // Flip the entries that must be flipped to convert to LPS
+ mat(2,0) *= -1; mat(2,1) *= -1;
+ mat(0,2) *= -1; mat(1,2) *= -1;
+ mat(0,3) *= -1; mat(1,3) *= -1;
+
+
+ typedef vnl_matrix_fixed<double, 3, 3> Mat33;
+ Mat33 A = mat.extract(3,3);
+
+
+ // QR decomposition
+ vnl_qr<double> qr(A);
+ vnl_matrix_fixed<double, 3, 3> m = qr.Q(), R = qr.R(), F;
+
+ F.set_identity();
+ for(size_t i = 0; i < 3; i++)
+ if(R(i,i) < 0)
+ F(i,i) = -1;
+
+ // Scale Q and R by the flip matrix
+ m = m * F;
+ R = F * R;
+
+
+ printf("Rotation matrix:\n");
+ for(size_t i = 0; i < 3; i++)
+ printf("%12.5f %12.5f %12.5f\n", m(i,0), m(i,1), m(i,2));
+
+ double r[3], s[3], k[3];
+ // Get the rotation angles
+ r[0] = atan2(m(1,2), m(2,2)) * 180. / vnl_math::pi;
+ r[1] = asin(-m(0,2)) * 180. / vnl_math::pi;
+ r[2] = atan2(m(0,1), m(0,0)) * 180. / vnl_math::pi;
+
+ // Get the scales
+ for(size_t i = 0; i < 3; i++)
+ s[i] = 100. * R(i,i);
+
+ // Get the shears
+ vnl_matrix_fixed<double, 3, 3> Sinv; Sinv.set_identity();
+ for(size_t i = 0; i < 3; i++)
+ Sinv(i,i) = 1.0 / R(i,i);
+ vnl_matrix_fixed<double, 3, 3> K = R * Sinv;
+
+ k[0] = atan(K(0,1)) * 180. / vnl_math::pi;
+ k[1] = atan(K(1,2)) * 180. / vnl_math::pi;
+ k[2] = atan(K(0,2)) * 180. / vnl_math::pi;
+
+ printf("Affine parameters: T=(%f, %f, %f); R = (%f, %f, %f); S = (%f, %f, %f); K = (%f, %f, %f)\n",
+ mat(0,3), mat(1,3), mat(2,3), r[0], r[1], r[2], s[0], s[1], s[2], k[0], k[1], k[2]);
+
+
+
+ double trace = m(0,0) + m(1,1) + m(2,2) + 1.0;
+//std::cout << "trace: " << trace << " epsilon: " << vcl_numeric_limits<T>::epsilon() << std::endl;
+
+ if( trace > epsilon)
+ {
+ const double s = 0.5 / vcl_sqrt(trace);
+ m_W = 0.25 / s;
+ m_X = (m(2,1) - m(1,2)) * s;
+ m_Y = (m(0,2) - m(2,0)) * s;
+ m_Z = (m(1,0) - m(0,1)) * s;
+//std::cout << "opt 1: w " << m_W << " x " << m_X << " y " << m_Y << " z " << m_Z << std::endl;
+ }
+ else
+ {
+ if( m(0,0) > m(1,1) && m(0,0) > m(2,2) )
+ {
+ const double s = 2.0 * vcl_sqrt(1.0 + m(0,0) - m(1,1) - m(2,2));
+ m_X = 0.25 * s;
+ m_Y = (m(0,1) + m(1,0)) / s;
+ m_Z = (m(0,2) + m(2,0)) / s;
+ m_W = (m(1,2) - m(2,1)) / s;
+//std::cout << "opt 2: w " << m_W << " x " << m_X << " y " << m_Y << " z " << m_Z << std::endl;
+ }
+ else
+ {
+ if( m(1,1) > m(2,2) )
+ {
+ const double s = 2.0 * vcl_sqrt(1.0 + m(1,1) - m(0,0) - m(2,2));
+ m_X = (m(0,1) + m(1,0)) / s;
+ m_Y = 0.25 * s;
+ m_Z = (m(1,2) + m(2,1)) / s;
+ m_W = (m(0,2) - m(2,0)) / s;
+//std::cout << "opt 3: w " << m_W << " x " << m_X << " y " << m_Y << " z " << m_Z << std::endl;
+ }
+ else
+ {
+ const double s = 2.0 * vcl_sqrt(1.0 + m(2,2) - m(0,0) - m(1,1));
+ m_X = (m(0,2) + m(2,0)) / s;
+ m_Y = (m(1,2) + m(2,1)) / s;
+ m_Z = 0.25 * s;
+ m_W = (m(0,1) - m(1,0)) / s;
+//std::cout << "opt 4: w " << m_W << " x " << m_X << " y " << m_Y << " z " << m_Z << std::endl;
+ }
+ }
+ }
+ double mag = vcl_sqrt( m_X*m_X + m_Y*m_Y + m_Z*m_Z + m_W*m_W );
+ m_X /= mag;
+ m_Y /= mag;
+ m_Z /= mag;
+ m_W /= mag;
+
+ printf("Quaternion:\n");
+ printf("%12.5f %12.5f %12.5f %12.5f\n", m_X, m_Y, m_Z, m_W);
+
+ double L = vcl_sqrt( m_X*m_X + m_Y*m_Y + m_Z*m_Z );
+ double angle = (180.0/vnl_math::pi) * 2.0 * asin( L );
+ double axis[3];
+ axis[0] = m_X/L;
+ axis[1] = m_Y/L;
+ axis[2] = m_Z/L;
+
+ printf("Rotation angle:\n");
+ printf("%f degrees\n", angle);
+
+ printf("Rotation axis:\n");
+ printf("%12.5f %12.5f %12.5f\n", axis[0], axis[1], axis[2] );
+
+}
+
+void irtk_write(MatrixStack &vmat, const char *fname)
+{
+ // Get the current matrix
+ MatrixType M = vmat.back();
+
+ // Initialize the parameters to save
+ double p[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+ double *t = p, *r = p+3, *s = p+6, *k = p+9;
+
+ // Set the translation parameters
+ for(size_t i = 0; i < 3; i++)
+ t[i] = M(i,3);
+
+ // Extract the 3x3 affine matrix
+ typedef vnl_matrix_fixed<double, 3, 3> Mat33;
+ Mat33 A = M.extract(3,3);
+
+ // QR decomposition
+ vnl_qr<double> qr(A);
+ Mat33 Q = qr.Q(), R = qr.R();
+
+ // Compute the flip matrix so that the scales are positive
+ Mat33 F; F.set_identity();
+ for(size_t i = 0; i < 3; i++)
+ if(R(i,i) < 0)
+ F(i,i) = -1;
+
+ // Scale Q and R by the flip matrix
+ Q = Q * F;
+ R = F * R;
+
+ // Get the rotation angles
+ r[0] = atan2(Q(1,2), Q(2,2)) * 180. / vnl_math::pi;
+ r[1] = asin(-Q(0,2)) * 180. / vnl_math::pi;
+ r[2] = atan2(Q(0,1), Q(0,0)) * 180. / vnl_math::pi;
+
+ // Get the scales
+ for(size_t i = 0; i < 3; i++)
+ s[i] = 100. * R(i,i);
+
+ // Get the shears
+ Mat33 Sinv; Sinv.set_identity();
+ for(size_t i = 0; i < 3; i++)
+ Sinv(i,i) = 1.0 / R(i,i);
+ Mat33 K = R * Sinv;
+
+ k[0] = atan(K(0,1)) * 180. / vnl_math::pi;
+ k[1] = atan(K(1,2)) * 180. / vnl_math::pi;
+ k[2] = atan(K(0,2)) * 180. / vnl_math::pi;
+
+
+ // Print the transformation parameters
+ printf("DOF parameters: T=(%f, %f, %f); R = (%f, %f, %f); S = (%f, %f, %f); K = (%f, %f, %f)\n",
+ p[0],p[1],p[2],p[3],p[4],p[5],
+ p[6],p[7],p[8],p[9],p[10],p[11]);
+
+ // Write to DOF file
+ const char *h = "\x00\x0c\x6f\x9f\x00\x00\x00\x03\x00\x00\x00\x0c";
+ FILE *fid = fopen(fname, "wb");
+ if(!fid)
+ throw ConvertException("Unable to open file %s for writing", fname);
+
+ // Write the header
+ fwrite(h, 1, 12, fid);
+
+ // Write the data
+ itk::ByteSwapper<double>::SwapRangeFromSystemToBigEndian(p, 12);
+ fwrite(p, sizeof(double), 12, fid);
+ fclose(fid);
+}
+
+void itk_write(MatrixStack &vmat, const char *fname)
+{
+ // Get the current matrix
+ MatrixType mat = vmat.back();
+
+ // Flip the entries that must be flipped
+ mat(2,0) *= -1; mat(2,1) *= -1;
+ mat(0,2) *= -1; mat(1,2) *= -1;
+ mat(0,3) *= -1; mat(1,3) *= -1;
+
+ // Create an ITK affine transform
+ typedef itk::MatrixOffsetTransformBase<double, 3> AffTran;
+ AffTran::Pointer atran = AffTran::New();
+
+ // Populate its matrix
+ AffTran::MatrixType amat = atran->GetMatrix();
+ AffTran::OffsetType aoff = atran->GetOffset();
+
+ for(size_t r = 0; r < 3; r++)
+ {
+ for(size_t c = 0; c < 3; c++)
+ {
+ amat(r,c) = mat(r,c);
+ }
+ aoff[r] = mat(r,3);
+ }
+
+ atran->SetMatrix(amat);
+ atran->SetOffset(aoff);
+
+ // Write the transform
+ itk::TransformFileWriter::Pointer wrt = itk::TransformFileWriter::New();
+ wrt->SetInput(atran);
+ wrt->SetFileName(fname);
+ wrt->Update();
+}
+
+void ras_read(MatrixStack &vmat, const char *fname)
+{
+ MatrixType mat;
+
+ ifstream fin(fname);
+ for(size_t i = 0; i < 4; i++)
+ for(size_t j = 0; j < 4; j++)
+ if(fin.good())
+ {
+ fin >> mat[i][j];
+ }
+ else
+ {
+ throw "Unable to read matrix";
+ }
+ fin.close();
+
+ vmat.push_back(mat);
+}
+
+void ras_write(MatrixStack &vmat, const char *fname)
+{
+ MatrixType mat = vmat.back();
+
+ ofstream fout(fname);
+ for(size_t i = 0; i < 4; i++)
+ for(size_t j = 0; j < 4; j++)
+ fout << mat[i][j] << (j < 3 ? " " : "\n");
+
+ fout.close();
+}
+
+void fsl_to_ras(MatrixStack &vmat, ImageType *ref, ImageType *mov, short flag)
+{
+ MatrixType m_fsl, m_spcref, m_spcmov, m_swpref, m_swpmov, m_ref, m_mov, m_out;
+ m_fsl = vmat.back();
+
+ // Set the ref/mov matrices
+ m_ref = ref->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix();
+ m_mov = mov->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix();
+
+ // Set the swap matrices
+ m_swpref.set_identity();
+ if(vnl_det(m_ref) > 0)
+ {
+ m_swpref(0,0) = -1.0;
+ m_swpref(0,3) = (ref->GetBufferedRegion().GetSize(0) - 1) * ref->GetSpacing()[0];
+ }
+
+ m_swpmov.set_identity();
+ if(vnl_det(m_mov) > 0)
+ {
+ m_swpmov(0,0) = -1.0;
+ m_swpmov(0,3) = (mov->GetBufferedRegion().GetSize(0) - 1) * mov->GetSpacing()[0];
+ }
+
+ // Set the spacing matrices
+ m_spcref.set_identity();
+ m_spcmov.set_identity();
+ for(size_t i = 0; i < 3; i++)
+ {
+ m_spcref(i,i) = ref->GetSpacing()[i];
+ m_spcmov(i,i) = mov->GetSpacing()[i];
+ }
+
+ // Compute the output matrix
+ if (flag == FSL_TO_RAS)
+ m_out =
+ m_mov * vnl_inverse(m_spcmov) * m_swpmov *
+ vnl_inverse(m_fsl) *
+ m_swpref * m_spcref * vnl_inverse(m_ref);
+
+ // NOTE: m_fsl is really m_ras here
+ if (flag == RAS_TO_FSL)
+ m_out =
+ vnl_inverse(vnl_inverse(m_swpmov) * m_spcmov* vnl_inverse(m_mov) *
+ m_fsl *
+ m_ref*vnl_inverse(m_spcref)*vnl_inverse(m_swpref));
+
+ // Put it on the stack
+ vmat.pop_back();
+ vmat.push_back(m_out);
+}
+
+void ras_inv(MatrixStack &vmat)
+{
+ MatrixType m = vmat.back();
+ MatrixType minv = vnl_inverse(m);
+ vmat.pop_back();
+ vmat.push_back(minv);
+}
+
+void ras_sqrt(MatrixStack &vmat)
+{
+ MatrixType m = vmat.back();
+
+ // Peform Denman-Beavers iteration
+ MatrixType Z, Y = m;
+ Z.set_identity();
+
+ for(size_t i = 0; i < 16; i++)
+ {
+ MatrixType Ynext = 0.5 * (Y + vnl_inverse(Z));
+ MatrixType Znext = 0.5 * (Z + vnl_inverse(Y));
+ Y = Ynext;
+ Z = Znext;
+ }
+
+ vmat.pop_back();
+ vmat.push_back(Y);
+}
+
+void make_rotation(MatrixStack &vmat, double theta, Vec3 v)
+{
+ // Normalize the vector by magnitude
+ v.normalize();
+
+ // Convert the angle to radians
+ double theta_rad = theta * vnl_math::pi / 180;
+
+ // Compute the skew-symmetric matrix
+ Mat3 S; S.fill(0.0);
+ S(0,1) = -v[2]; S(1,0) = v[2];
+ S(2,0) = -v[1]; S(0,2) = v[1];
+ S(1,2) = -v[0]; S(2,1) = v[0];
+
+ // Apply Rodriguez formula
+ Mat3 R; R.set_identity();
+ R += sin(theta_rad) * S;
+ R += (1 - cos(theta_rad)) * (S * S);
+
+ // Fill out the complete matrix
+ MatrixType A; A.set_identity();
+ for(int i = 0; i < 3; i++)
+ for(int j = 0; j < 3; j++)
+ A(i,j) = R(i,j);
+
+ vmat.push_back(A);
+}
+
+
+void make_translation(MatrixStack &vmat, Vec3 v)
+{
+ // Fill out the complete matrix
+ MatrixType A; A.set_identity();
+ for(int i = 0; i < 3; i++)
+ A(i,3) = v[i];
+
+ vmat.push_back(A);
+}
+
+void make_scaling(MatrixStack &vmat, Vec3 v)
+{
+ // Fill out the complete matrix
+ MatrixType A; A.set_identity();
+ for(int i = 0; i < 3; i++)
+ A(i,i) = v[i];
+
+ vmat.push_back(A);
+}
+
+
+void ras_det(MatrixStack &vmat)
+{
+ MatrixType m = vmat.back();
+ double det = vnl_det(m);
+ cout << "Det: " << det << endl;
+}
+
+void ras_sform(MatrixStack &vmat, const char *fname)
+{
+ typedef itk::OrientedRASImage<float, 3> ImageType;
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(fname);
+ reader->Update();
+
+ ImageType::Pointer img = reader->GetOutput();
+
+ // Set up the directions
+ MatrixType m = img->GetVoxelSpaceToRASPhysicalSpaceMatrix().GetVnlMatrix();
+ vmat.push_back(m);
+}
+
+
+void ras_print(MatrixStack &vmat, bool isfullinfo)
+{
+ MatrixType m = vmat.back();
+ printf("Matrix #%d:\n", (int) vmat.size());
+ for(size_t i = 0; i < 4; i++)
+ printf("%12.5f %12.5f %12.5f %12.5f\n", m(i,0), m(i,1), m(i,2), m(i,3));
+
+ if ( isfullinfo )
+ {
+
+ quart_print(m);
+ }
+
+}
+
+void ras_mult(MatrixStack &vmat)
+{
+ MatrixType A = vmat[vmat.size() - 2];
+ MatrixType B = vmat[vmat.size() - 1];
+ MatrixType AB = A * B;
+ vmat.pop_back();
+ vmat.pop_back();
+ vmat.push_back(AB);
+}
+
+int main(int argc, char *argv[])
+{
+ // Show usage
+ if(argc < 2) return usage();
+
+ // Set up the images that might be loaded
+ ImageType::Pointer ref = NULL, src = NULL;
+
+ // Set up the matrix stack
+ vector<MatrixType> vmat;
+
+ // Parse the command line
+ for(int iarg = 1; iarg < argc; iarg++)
+ {
+ try
+ {
+ string arg = argv[iarg];
+ if(arg == "-ref")
+ {
+ // Read the reference image
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ ReaderType::Pointer read_ref = ReaderType::New();
+ read_ref->SetFileName(argv[++iarg]);
+ read_ref->Update();
+ ref = read_ref->GetOutput();
+ }
+ else if(arg == "-src" || arg == "-mov")
+ {
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ // Read the moving image
+ ReaderType::Pointer read_src = ReaderType::New();
+ read_src->SetFileName(argv[++iarg]);
+ read_src->Update();
+ src = read_src->GetOutput();
+ }
+ else if(arg == "-fsl2ras")
+ {
+ // Convert FSL to RAS
+ fsl_to_ras(vmat, ref, src, FSL_TO_RAS);
+ }
+ else if(arg == "-ras2fsl")
+ {
+ // Convert FSL to RAS
+ fsl_to_ras(vmat, ref, src, RAS_TO_FSL);
+ }
+ else if(arg == "-mult")
+ {
+ ras_mult(vmat);
+ }
+ else if(arg == "-det")
+ {
+ ras_det(vmat);
+ }
+ else if(arg == "-inv")
+ {
+ ras_inv(vmat);
+ }
+ else if(arg == "-sqrt")
+ {
+ ras_sqrt(vmat);
+ }
+ else if(arg == "-info")
+ {
+ ras_print(vmat, false);
+ }
+ else if(arg == "-info-full")
+ {
+ ras_print(vmat, true);
+ }
+ else if(arg == "-sform")
+ {
+ ras_sform(vmat, argv[++iarg]);
+ }
+ else if(arg == "-itk")
+ {
+ itk_read(vmat, argv[++iarg]);
+ }
+ else if(arg == "-irtk")
+ {
+ irtk_read(vmat, argv[++iarg]);
+ }
+ else if(arg == "-o")
+ {
+ ras_write(vmat, argv[++iarg]);
+ }
+ else if(arg == "-oitk")
+ {
+ itk_write(vmat, argv[++iarg]);
+ }
+ else if(arg == "-oirtk")
+ {
+ irtk_write(vmat, argv[++iarg]);
+ }
+ else if(arg == "-rot")
+ {
+ double theta = atof(argv[++iarg]);
+ Vec3 v;
+ v[0] = atof(argv[++iarg]);
+ v[1] = atof(argv[++iarg]);
+ v[2] = atof(argv[++iarg]);
+ make_rotation(vmat, theta, v);
+ }
+ else if(arg == "-tran")
+ {
+ Vec3 v;
+ v[0] = atof(argv[++iarg]);
+ v[1] = atof(argv[++iarg]);
+ v[2] = atof(argv[++iarg]);
+ make_translation(vmat, v);
+ }
+ else if(arg == "-scale")
+ {
+ Vec3 v;
+ v[0] = atof(argv[++iarg]);
+ v[1] = atof(argv[++iarg]);
+ v[2] = atof(argv[++iarg]);
+ make_scaling(vmat, v);
+ }
+ else if(arg[0] != '-')
+ {
+ ras_read(vmat, arg.c_str());
+ }
+ else
+ {
+ cerr << "Unknown option " << arg << endl;
+ return usage();
+ }
+ }
+ catch(std::exception &exc)
+ {
+ cerr << "Exception raised during processing:" << endl;
+ cerr << exc.what() << endl;
+ return -1;
+ }
+ }
+
+}
diff --git a/Submodules/c3d/utilities/CMakeLists.txt b/Submodules/c3d/utilities/CMakeLists.txt
new file mode 100644
index 0000000..c47596d
--- /dev/null
+++ b/Submodules/c3d/utilities/CMakeLists.txt
@@ -0,0 +1,5 @@
+ADD_EXECUTABLE(c3d_affine_tool AffineTransformTool.cxx)
+TARGET_LINK_LIBRARIES(c3d_affine_tool ${ITK_LIBRARIES})
+
+INSTALL(TARGETS c3d_affine_tool RUNTIME DESTINATION bin)
+INSTALL(FILES bashcomp.sh DESTINATION share)
diff --git a/Submodules/c3d/utilities/bashcomp.sh b/Submodules/c3d/utilities/bashcomp.sh
new file mode 100644
index 0000000..0c38521
--- /dev/null
+++ b/Submodules/c3d/utilities/bashcomp.sh
@@ -0,0 +1,120 @@
+function _c3d()
+{
+ local cur prev opts interp
+ opts=""
+ opts="$opts -add"
+ opts="$opts -anisotropic-diffusion -ad"
+ opts="$opts -antialias -alias"
+ opts="$opts -as -set"
+ opts="$opts -background"
+ opts="$opts -biascorr"
+ opts="$opts -binarize"
+ opts="$opts -centroid"
+ opts="$opts -connected-components -connected -comp"
+ opts="$opts -clear"
+ opts="$opts -clip"
+ opts="$opts -copy-transform -ct"
+ opts="$opts -create"
+ opts="$opts -dilate"
+ opts="$opts -divide"
+ opts="$opts -dup -duplicate"
+ opts="$opts -endfor"
+ opts="$opts -erode"
+ opts="$opts -erf"
+ opts="$opts -exp"
+ opts="$opts -fft"
+ opts="$opts -foreach"
+ opts="$opts -glm"
+ opts="$opts -histmatch -histogram-match"
+ opts="$opts -info"
+ opts="$opts -info-full"
+ opts="$opts -insert -ins"
+ opts="$opts -interpolation -interp -int"
+ opts="$opts -iterations"
+ opts="$opts -label-overlap"
+ opts="$opts -label-statistics -lstat"
+ opts="$opts -laplacian -laplace"
+ opts="$opts -levelset"
+ opts="$opts -levelset-curvature"
+ opts="$opts -levelset-advection"
+ opts="$opts -ln -log"
+ opts="$opts -log10"
+ opts="$opts -mcs -multicomponent-split"
+ opts="$opts -mean"
+ opts="$opts -merge"
+ opts="$opts -mi -mutual-info"
+ opts="$opts -mixture -mixture-model"
+ opts="$opts -multiply -times"
+ opts="$opts -ncc -normalized-cross-correlation"
+ opts="$opts -nmi -normalized-mutual-info"
+ opts="$opts -normpdf"
+ opts="$opts -noround"
+ opts="$opts -nospm"
+ opts="$opts -o"
+ opts="$opts -omc -output-multicomponent"
+ opts="$opts -oo -output-multiple"
+ opts="$opts -origin"
+ opts="$opts -overlap"
+ opts="$opts -pad"
+ opts="$opts -pixel"
+ opts="$opts -pop"
+ opts="$opts -popas"
+ opts="$opts -probe"
+ opts="$opts -push -get"
+ opts="$opts -reciprocal"
+ opts="$opts -region"
+ opts="$opts -replace"
+ opts="$opts -resample"
+ opts="$opts -resample-mm"
+ opts="$opts -reslice-itk"
+ opts="$opts -reslice-matrix"
+ opts="$opts -reslice-identity"
+ opts="$opts -rms"
+ opts="$opts -round"
+ opts="$opts -scale"
+ opts="$opts -set-sform"
+ opts="$opts -shift"
+ opts="$opts -signed-distance-transform -sdt"
+ opts="$opts -slice"
+ opts="$opts -smooth"
+ opts="$opts -spacing"
+ opts="$opts -split"
+ opts="$opts -sqrt"
+ opts="$opts -staple"
+ opts="$opts -spm"
+ opts="$opts -stretch"
+ opts="$opts -threshold -thresh"
+ opts="$opts -trim"
+ opts="$opts -trim-to-size"
+ opts="$opts -type"
+ opts="$opts -verbose"
+ opts="$opts -version"
+ opts="$opts -vote"
+ opts="$opts -vote-label"
+ opts="$opts -voxel-sum"
+ opts="$opts -voxel-integral -voxel-int"
+ opts="$opts -voxelwise-regression -voxreg"
+ opts="$opts -warp"
+ opts="$opts -warp-label -warplabel -wl"
+
+ interp="nearest linear cubic sinc gaussian Nearest Linear Cubic Sinc Gaussian"
+
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ if [[ ${cur} == -* ]] ; then
+ COMPREPLY=( `compgen -W "${opts}" -- ${cur}` )
+ return 0
+
+ elif [[ ${prev} == -int* ]] ; then
+ COMPREPLY=( `compgen -W "${interp}" -- ${cur}` )
+ return 0
+
+ else
+ COMPREPLY=( `compgen -f ${cur}` )
+ return 0
+ fi
+}
+complete -F _c3d -o filenames c3d
+
diff --git a/Submodules/c3d/utilities/doc/Documentation.cxx b/Submodules/c3d/utilities/doc/Documentation.cxx
new file mode 100644
index 0000000..5677eca
--- /dev/null
+++ b/Submodules/c3d/utilities/doc/Documentation.cxx
@@ -0,0 +1,194 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertImageND.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "Documentation.h"
+#include <iomanip>
+#include <sstream>
+#include <itksys/RegularExpression.hxx>
+#include <algorithm>
+#include <functional>
+#include <cctype>
+
+// Helper function: trim whitespace
+// from: http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
+//
+// trim from start
+std::string &
+Documentation::ltrim(std::string &s)
+{
+ s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
+ return s;
+}
+
+// trim from end
+std::string &
+Documentation::rtrim(std::string &s)
+{
+ s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
+ return s;
+}
+
+// trim from both ends
+std::string &
+Documentation::trim(std::string &s)
+{
+ return ltrim(rtrim(s));
+}
+
+std::string
+Documentation::tolower(const std::string &src)
+{
+ std::string s = src;
+ std::transform(s.begin(), s.end(), s.begin(), (int(*)(int)) ::tolower);
+ return s;
+}
+
+Documentation::Documentation(unsigned char *rawdoc)
+: m_Text((const char *) rawdoc)
+{
+ // Headings
+ std::string relevant_category_heading = "### Commands:";
+ m_CategoryHeading = "### ";
+ m_CommandHeading = "#### ";
+
+ // Parse the file
+ std::istringstream iss(m_Text);
+
+ // Current category
+ int current_cat = -1;
+ std::string current_command = "";
+
+ // Read lines from the file
+ std::string line;
+ while(std::getline(iss, line))
+ {
+ if(line.find(m_CategoryHeading) == 0)
+ {
+ // This is a category - find out which one
+ current_cat = -1;
+
+ if(line.find(relevant_category_heading) == 0)
+ {
+ std::string title = line.substr(relevant_category_heading.length());
+ m_Categories.push_back(Category(trim(title)));
+ current_cat = ((int) m_Categories.size()) - 1;
+ current_command = "";
+ }
+ }
+
+ else if(line.find(m_CommandHeading) == 0 && current_cat >= 0)
+ {
+ // This is a parseable command. Parse out all of its information
+ std::string rexp1 = m_CommandHeading + " *(.*): *(.*)$";
+ itksys::RegularExpression re1(rexp1.c_str());
+ if(re1.find(line))
+ {
+ CommandDoc cdoc;
+ cdoc.Title = re1.match(1);
+ cdoc.ShortDesc = re1.match(2);
+ current_command = cdoc.Title;
+
+ // Split the title into aliases
+ std::istringstream isstmp(cdoc.Title);
+ std::string cmdline;
+ while(std::getline(isstmp, cmdline, ','))
+ {
+ std::string cmd = trim(cmdline);
+ cdoc.Aliases.push_back(cmd);
+ m_AllCommands.insert(cmd);
+ }
+
+ m_Categories[current_cat].Commands.push_back(cdoc);
+ }
+ else
+ {
+ current_command = "";
+ }
+ }
+
+ else if(current_command.length() > 0 && current_cat >= 0)
+ {
+ // Line from a description
+ m_Categories[current_cat].Commands.back().LongDesc += line;
+ m_Categories[current_cat].Commands.back().LongDesc += "\n";
+ }
+ }
+}
+
+void Documentation::PrintCommandListing(std::ostream &out)
+{
+ for(int i = 0; i < m_Categories.size(); i++)
+ {
+ out << m_Categories[i].Title << ": " << std::endl;
+ for(int j = 0; j < m_Categories[i].Commands.size(); j++)
+ {
+ out << " ";
+ out << std::setw(32) << std::left;
+ out << m_Categories[i].Commands[j].Title;
+ out << ": ";
+ out << m_Categories[i].Commands[j].ShortDesc;
+ out << std::endl;
+ }
+ }
+}
+
+void Documentation::PrintManual(std::ostream &out)
+{
+ out << m_Text << std::endl;
+}
+
+bool Documentation::PrintCommandHelp(std::ostream &out, const std::string &command)
+{
+ // Create a search string
+ if(command.length() == 0)
+ return false;
+
+ std::string req = command[0] == '-' ? command : std::string("-") + command;
+ req = tolower(req);
+
+ for(int i = 0; i < m_Categories.size(); i++)
+ {
+ for(int j = 0; j < m_Categories[i].Commands.size(); j++)
+ {
+ CommandDoc &cmd = m_Categories[i].Commands[j];
+ for(int k = 0; k < cmd.Aliases.size(); k++)
+ {
+ if(tolower(cmd.Aliases[k]) == req)
+ {
+ out << std::setw(32) << std::left;
+ out << m_Categories[i].Commands[j].Title;
+ out << ": ";
+ out << m_Categories[i].Commands[j].ShortDesc;
+ out << std::endl;
+ out << cmd.LongDesc;
+ out << std::endl;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/Submodules/c3d/utilities/doc/Documentation.h b/Submodules/c3d/utilities/doc/Documentation.h
new file mode 100644
index 0000000..ca3f236
--- /dev/null
+++ b/Submodules/c3d/utilities/doc/Documentation.h
@@ -0,0 +1,93 @@
+/*=========================================================================
+
+ Program: C3D: Command-line companion tool to ITK-SNAP
+ Module: ConvertImageND.cxx
+ Language: C++
+ Website: itksnap.org/c3d
+ Copyright (c) 2014 Paul A. Yushkevich
+
+ This file is part of C3D, a command-line companion tool to ITK-SNAP
+
+ C3D is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef __Documentation_h__
+#define __Documentation_h__
+
+#include <string>
+#include <set>
+#include <vector>
+
+/**
+ * Documentation parsing system - takes our markdown format file and uses it
+ * to generate help output
+ */
+class Documentation
+{
+public:
+
+ struct CommandDoc
+ {
+ std::string Title;
+ std::vector<std::string> Aliases;
+ std::string ShortDesc;
+ std::string LongDesc;
+ };
+
+ struct Category
+ {
+ std::string Title;
+ std::vector<CommandDoc> Commands;
+ Category(const std::string name) : Title(name) {}
+ };
+
+ /** Constructor - takes a C string with markdown text */
+ Documentation(unsigned char* rawdoc);
+
+ void PrintCommandListing(std::ostream &out);
+ bool PrintCommandHelp(std::ostream &out, const std::string &command);
+ void PrintManual(std::ostream &out);
+
+ const std::set<std::string> &GetAllCommands() const
+ { return m_AllCommands; }
+
+ const std::vector<Category> &GetCategories() const
+ { return m_Categories; }
+
+ // Text processing routines
+ static std::string <rim(std::string &s);
+ static std::string &rtrim(std::string &s);
+ static std::string &trim(std::string &s);
+ static std::string tolower(const std::string &s);
+
+private:
+ // Complete manual text
+ std::string m_Text;
+
+ // Headings for commands and categories
+ std::string m_CategoryHeading, m_CommandHeading;
+
+ // Grouping of commands
+ std::vector<Category> m_Categories;
+
+ // Complete listing of commands and aliases
+ std::set<std::string> m_AllCommands;
+};
+
+
+
+
+#endif
+
diff --git a/Submodules/c3d/utilities/hexdump.c b/Submodules/c3d/utilities/hexdump.c
new file mode 100644
index 0000000..a34485f
--- /dev/null
+++ b/Submodules/c3d/utilities/hexdump.c
@@ -0,0 +1,2109 @@
+/* ==========================================================================
+ * hexdump.c - hexdump.c
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2013 William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#if __STDC__ && !_XOPEN_SOURCE
+#define _XOPEN_SOURCE 600 /* _setjmp(3), _longjmp(3), getopt(3) */
+#endif
+
+#include <limits.h> /* INT_MAX */
+
+#include <stdint.h> /* int64_t */
+#include <stdio.h> /* FILE fprintf(3) snprintf(3) */
+#include <stdlib.h> /* malloc(3) realloc(3) free(3) abort(3) */
+
+#include <string.h> /* memset(3) memmove(3) */
+
+#include <errno.h> /* ERANGE errno */
+
+#include <setjmp.h> /* _setjmp(3) _longjmp(3) */
+
+#include "hexdump.h"
+
+#ifdef _WIN32
+#define MY_SNPRINTF _snprintf
+#else
+#define MY_SNPRINTF snprintf
+#endif
+
+
+#define SAY_(fmt, ...) fprintf(stderr, fmt "%s", __FILE__, __LINE__, __func__, __VA_ARGS__);
+#define SAY(...) SAY_("@@ %s:%d:%s: " __VA_ARGS__, "\n");
+#define HAI SAY("HAI")
+
+#define OOPS(...) do { \
+ SAY(__VA_ARGS__); \
+ abort(); \
+} while (0)
+
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#define countof(a) (sizeof (a) / sizeof *(a))
+
+#ifndef NOTUSED
+#if __GNUC__
+#define NOTUSED __attribute__((unused))
+#else
+#define NOTUSED
+#endif
+#endif
+
+#ifndef NORETURN
+#if __GNUC__
+#define NORETURN __attribute__((noreturn))
+#else
+#define NORETURN
+#endif
+#endif
+
+#define NARG_(a, b, c, d, e, f, g, h, N,...) N
+#define NARG(...) NARG_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+
+#define PASTE(x, y) x##y
+#define XPASTE(x, y) PASTE(x, y)
+
+
+static unsigned char toprint(unsigned char chr) {
+ return (chr > 0x1f && chr < 0x7f)? chr : '.';
+} /* toprint() */
+
+
+static const char *tooctal(char buf[3], unsigned char chr) {
+ if (chr > 0x1f && chr < 0x7f) {
+ buf[0] = chr;
+ buf[1] = '\0';
+ } else {
+ switch (chr) {
+ case '\0':
+ buf[0] = '\\';
+ buf[1] = '0';
+ buf[2] = '\0';
+
+ break;
+ case '\a':
+ buf[0] = '\\';
+ buf[1] = 'a';
+ buf[2] = '\0';
+
+ break;
+ case '\b':
+ buf[0] = '\\';
+ buf[1] = 'b';
+ buf[2] = '\0';
+
+ break;
+ case '\f':
+ buf[0] = '\\';
+ buf[1] = 'f';
+ buf[2] = '\0';
+
+ break;
+ case '\n':
+ buf[0] = '\\';
+ buf[1] = 'n';
+ buf[2] = '\0';
+
+ break;
+ case '\r':
+ buf[0] = '\\';
+ buf[1] = 'r';
+ buf[2] = '\0';
+
+ break;
+ case '\t':
+ buf[0] = '\\';
+ buf[1] = 't';
+ buf[2] = '\0';
+
+ break;
+ case '\v':
+ buf[0] = '\\';
+ buf[1] = 'v';
+ buf[2] = '\0';
+
+ break;
+ default:
+ buf[0] = "01234567"[0x7 & (chr >> 6)];
+ buf[1] = "01234567"[0x7 & (chr >> 3)];
+ buf[2] = "01234567"[0x7 & (chr >> 0)];
+
+ break;
+ }
+ }
+
+ return buf;
+} /* tooctal() */
+
+
+static const char *toshort(char buf[3], unsigned char chr) {
+ static const char map[][3] = {
+ [0x00] = "nul", [0x01] = "soh", [0x02] = "stx", [0x03] = "etx",
+ [0x04] = "eot", [0x05] = "enq", [0x06] = "ack", [0x07] = "bel",
+ [0x08] = "bs", [0x09] = "ht", [0x0a] = "lf", [0x0b] = "vt",
+ [0x0c] = "ff", [0x0d] = "cr", [0x0e] = "so", [0x0f] = "si",
+ [0x10] = "dle", [0x11] = "dc1", [0x12] = "dc2", [0x13] = "dc3",
+ [0x14] = "dc4", [0x15] = "nak", [0x16] = "syn", [0x17] = "etb",
+ [0x18] = "can", [0x19] = "em", [0x1a] = "sub", [0x1b] = "esc",
+ [0x1c] = "fs", [0x1d] = "gs", [0x1e] = "rs", [0x1f] = "us",
+ [0x7f] = "del",
+ };
+
+ if (chr <= 0x1f || chr == 0x7f) {
+ memcpy(buf, map[chr], 3);
+ } else if (chr < 0x7f) {
+ buf[0] = chr;
+ buf[1] = '\0';
+ } else {
+ buf[0] = "0123456789abcdef"[0x0f & (chr >> 4)];
+ buf[1] = "0123456789abcdef"[0x0f & chr];
+ buf[2] = '\0';
+ }
+
+ return buf;
+} /* toshort() */
+
+
+static _Bool hxd_isspace(unsigned char chr, _Bool nlok) {
+ static const unsigned char space[] = {
+ ['\t'] = 1, ['\n'] = 1, ['\v'] = 1, ['\r'] = 1, ['\f'] = 1, [' '] = 1,
+ };
+
+ return (chr < sizeof space && space[chr] && (nlok || chr != '\n'));
+} /* hxd_isspace() */
+
+
+static unsigned char skipws(const unsigned char **fmt, _Bool nlok) {
+ while (hxd_isspace(**fmt, nlok))
+ ++*fmt;
+
+ return **fmt;
+} /* skipws() */
+
+
+static int getint(const unsigned char **fmt) {
+ static const int limit = ((INT_MAX - (INT_MAX % 10) - 1) / 10);
+ int i = -1;
+
+ if (**fmt >= '0' && **fmt <= '9') {
+ i = 0;
+
+ do {
+ i *= 10;
+ i += **fmt - '0';
+ ++*fmt;
+ } while (**fmt >= '0' && **fmt <= '9' && i <= limit);
+ }
+
+ return i;
+} /* getint() */
+
+
+#define F_HASH 1
+#define F_ZERO 2
+#define F_MINUS 4
+#define F_SPACE 8
+#define F_PLUS 16
+
+#define FC2(x, y) (((0xff & (y)) << 8) | (0xff & (x)))
+#define FC1(x) (0xff & (x))
+#define FC(...) XPASTE(FC, NARG(__VA_ARGS__))(__VA_ARGS__)
+
+static int getcnv(int *flags, int *width, int *prec, int *bytes, const unsigned char **fmt) {
+ int ch;
+
+ *flags = 0;
+
+ for (; (ch = **fmt); ++*fmt) {
+ switch (ch) {
+ case '#':
+ *flags |= F_HASH;
+ break;
+ case '0':
+ *flags |= F_ZERO;
+ break;
+ case '-':
+ *flags |= F_MINUS;
+ break;
+ case ' ':
+ *flags |= F_SPACE;
+ break;
+ case '+':
+ *flags |= F_PLUS;
+ break;
+ default:
+ goto width;
+ } /* switch() */
+ }
+
+width:
+ *width = getint(fmt);
+ *prec = (**fmt == '.')? (++*fmt, getint(fmt)) : -1;
+ *bytes = 0;
+
+ switch ((ch = **fmt)) {
+ case '%':
+ break;
+ case 'c':
+ *bytes = 1;
+ break;
+ case 'd': case 'i': case 'o': case 'u': case 'X': case 'x':
+ *bytes = 4;
+ break;
+ case 's':
+ if (*prec == -1)
+ return 0;
+ *bytes = *prec;
+ break;
+ case '_':
+ switch (*++*fmt) {
+ case 'a':
+ switch (*++*fmt) {
+ case 'd':
+ ch = FC2('_', 'd');
+ break;
+ case 'o':
+ ch = FC2('_', 'o');
+ break;
+ case 'x':
+ ch = FC2('_', 'x');
+ break;
+ default:
+ return 0;
+ }
+ *bytes = 0;
+ break;
+ case 'A':
+ switch (*++*fmt) {
+ case 'd':
+ ch = FC2('_', 'D');
+ break;
+ case 'o':
+ ch = FC2('_', 'O');
+ break;
+ case 'x':
+ ch = FC2('_', 'X');
+ break;
+ default:
+ return 0;
+ }
+ *bytes = 0;
+
+ /* XXX: Not supported yet. */
+ return 0;
+
+ break;
+ case 'c':
+ ch = FC2('_', 'c');
+ *bytes = 1;
+ break;
+ case 'p':
+ ch = FC2('_', 'p');
+ *bytes = 1;
+ break;
+ case 'u':
+ ch = FC2('_', 'u');
+ *bytes = 1;
+ break;
+ default:
+ return 0;
+ }
+
+ break;
+ } /* switch() */
+
+ ++*fmt;
+
+ return ch;
+} /* getcnv() */
+
+
+enum vm_opcode {
+ OP_HALT, /* 0/0 */
+ OP_NOOP, /* 0/0 */
+ OP_TRAP, /* 0/0 */
+ OP_PC, /* 0/1 | push program counter */
+ OP_TRUE, /* 0/1 | push true */
+ OP_FALSE, /* 0/1 | push false */
+ OP_ZERO, /* 0/1 | push 0 */
+ OP_ONE, /* 0/1 | push 1 */
+ OP_TWO, /* 0/1 | push 2 */
+ OP_I8, /* 0/1 | load 8-bit unsigned int from code */
+ OP_I16, /* 0/1 | load 16-bit unsigned int from code */
+ OP_I32, /* 0/1 | load 32-bit unsigned int from code */
+ OP_NEG, /* 1/1 | arithmetic negative */
+ OP_SUB, /* 2/1 | S(-2) - S(-1) */
+ OP_ADD, /* 2/1 | S(-2) + S(-1) */
+ OP_NOT, /* 1/1 | logical not */
+ OP_OR, /* 2/1 | logical or */
+ OP_LT, /* 2/1 | S(-2) < S(-1) */
+ OP_POP, /* 1/0 | pop top of stack */
+ OP_DUP, /* 1/2 | dup top of stack */
+ OP_SWAP, /* 2/2 | swap values at top of stack */
+ OP_READ, /* 1/1 | read bytes from input buffer */
+ OP_COUNT, /* 0/1 | count of bytes in input buffer */
+ OP_PUTC, /* 0/0 | copy char directly to output buffer */
+ OP_CONV, /* 5/0 | write conversion to output buffer */
+ OP_CHOP, /* 1/0 | chop trailing characters from output buffer */
+ OP_PAD, /* 1/0 | emit padding space to output buffer */
+ OP_JMP, /* 2/0 | conditional jump to address */
+ OP_RESET, /* 0/0 | reset input buffer position */
+
+ /*
+ * Optimized conversions with predicates.
+ */
+#define OK_IS0FIXED(F, W, P, N) \
+ (((W) == (N) && ((F) == F_ZERO) && (P) <= 0) || \
+ ((W) == -1 && (P) == (N)) || \
+ ((W) == (P) && (W) == (N)))
+
+#define OK_2XBYTE(F, W, P) OK_IS0FIXED((F), (W), (P), 2)
+ OP_2XBYTE,
+
+#define OK_PBYTE(F, W, P) ((W) <= 1 && (P) <= 1)
+ OP_PBYTE,
+
+#define OK_7XADDR(F, W, P) OK_IS0FIXED((F), (W), (P), 7)
+ OP_7XADDR,
+
+#define OK_8XADDR(F, W, P) OK_IS0FIXED((F), (W), (P), 8)
+ OP_8XADDR,
+}; /* enum vm_opcode */
+
+
+static const char *vm_strop(enum vm_opcode op) {
+ static const char *txt[] = {
+ [OP_HALT] = "HALT",
+ [OP_NOOP] = "NOOP",
+ [OP_TRAP] = "TRAP",
+ [OP_PC] = "PC",
+ [OP_TRUE] = "TRUE",
+ [OP_FALSE] = "FALSE",
+ [OP_ZERO] = "ZERO",
+ [OP_ONE] = "ONE",
+ [OP_TWO] = "TWO",
+ [OP_I8] = "I8",
+ [OP_I16] = "I16",
+ [OP_I32] = "I32",
+ [OP_NEG] = "NEG",
+ [OP_SUB] = "SUB",
+ [OP_ADD] = "ADD",
+ [OP_NOT] = "NOT",
+ [OP_OR] = "OR",
+ [OP_LT] = "LT",
+ [OP_POP] = "POP",
+ [OP_DUP] = "DUP",
+ [OP_SWAP] = "SWAP",
+ [OP_READ] = "READ",
+ [OP_COUNT] = "COUNT",
+ [OP_PUTC] = "PUTC",
+ [OP_CONV] = "CONV",
+ [OP_CHOP] = "CHOP",
+ [OP_PAD] = "PAD",
+ [OP_JMP] = "JMP",
+ [OP_RESET] = "RESET",
+ [OP_2XBYTE] = "2XBYTE",
+ [OP_PBYTE] = "PBYTE",
+ [OP_7XADDR] = "7XADDR",
+ [OP_8XADDR] = "8XADDR",
+ };
+
+ if ((int)op >= 0 && op < (int)countof(txt) && txt[op])
+ return txt[op];
+ else
+ return "-";
+} /* vm_strop() */
+
+
+struct vm_state {
+ jmp_buf trap;
+
+ int flags;
+
+ size_t blocksize;
+
+ int64_t stack[8];
+ int sp;
+
+ unsigned char code[4096];
+ int pc;
+
+ struct {
+ unsigned char *base, *p, *pe;
+ size_t address;
+ _Bool eof;
+ } i;
+
+ struct {
+ unsigned char *base, *p, *pe;
+ } o;
+}; /* struct vm_state */
+
+
+NOTUSED static void op_dump(struct vm_state *M, int *pc, FILE *fp) {
+ enum vm_opcode op = M->code[*pc];
+ unsigned n;
+
+ fprintf(fp, "%d: ", *pc);
+
+ switch (op) {
+ case OP_I8:
+ fprintf(fp, "%s %u\n", vm_strop(op), (unsigned)M->code[++*pc]);
+
+ break;
+ case OP_I16:
+ n = M->code[++*pc] << 8;
+ n |= M->code[++*pc];
+
+ fprintf(fp, "%s %u\n", vm_strop(op), n);
+
+ break;
+ case OP_I32:
+ n = M->code[++*pc] << 24;
+ n |= M->code[++*pc] << 16;
+ n |= M->code[++*pc] << 8;
+ n |= M->code[++*pc] << 0;
+
+ fprintf(fp, "%s %u\n", vm_strop(op), n);
+
+ break;
+ case OP_PUTC: {
+ const char *txt = vm_strop(op);
+ int chr = M->code[++*pc];
+
+ switch (chr) {
+ case '\n':
+ fprintf(fp, "%s \\n (0x0a)\n", txt);
+
+ break;
+ case '\r':
+ fprintf(fp, "%s \\r (0x0d)\n", txt);
+
+ break;
+ case '\t':
+ fprintf(fp, "%s \\t (0x09)\n", txt);
+
+ break;
+ default:
+ if (chr > 31 && chr < 127)
+ fprintf(fp, "%s %c (0x%.2x)\n", txt, chr, chr);
+ else
+ fprintf(fp, "%s . (0x%.2x)\n", txt, chr);
+
+ break;
+ } /* switch() */
+
+ break;
+ }
+ default:
+ fprintf(fp, "%s\n", vm_strop(op));
+
+ break;
+ } /* switch() */
+
+ ++*pc;
+} /* op_dump() */
+
+
+NOTUSED static void vm_dump(struct vm_state *M, FILE *fp) {
+ enum vm_opcode op;
+ int pc = 0;
+
+ fprintf(fp, "-- blocksize: %zu\n", M->blocksize);
+
+ do {
+ op = M->code[pc];
+ op_dump(M, &pc, fp);
+ } while (op != OP_HALT);
+} /* vm_dump() */
+
+
+#ifdef _WIN32
+#define vm_enter(M) setjmp((M)->trap)
+#else
+#define vm_enter(M) _setjmp((M)->trap)
+#endif
+
+NORETURN static void vm_throw(struct vm_state *M, int error) {
+#ifdef _WIN32
+ longjmp(M->trap, error);
+#else
+ _longjmp(M->trap, error);
+#endif
+} /* vm_throw() */
+
+
+static unsigned char vm_getc(struct vm_state *M) {
+ return (M->i.p < M->i.pe)? *M->i.p++ : 0;
+} /* vm_getc() */
+
+
+static void vm_putc(struct vm_state *M, unsigned char ch) {
+ unsigned char *tmp;
+ size_t size, p;
+
+ if (!(M->o.p < M->o.pe)) {
+ size = MAX(M->o.pe - M->o.base, 64);
+ p = M->o.p - M->o.base;
+
+ if (~size < size)
+ vm_throw(M, ENOMEM);
+
+ size *= 2;
+
+ if (!(tmp = realloc(M->o.base, size)))
+ vm_throw(M, errno);
+
+ M->o.base = tmp;
+ M->o.p = &tmp[p];
+ M->o.pe = &tmp[size];
+ }
+
+ *M->o.p++ = ch;
+} /* vm_putc() */
+
+
+static void vm_putx(struct vm_state *M, unsigned char ch) {
+ vm_putc(M, "0123456789abcdef"[0x0f & (ch >> 4)]);
+ vm_putc(M, "0123456789abcdef"[0x0f & (ch >> 0)]);
+} /* vm_putx() */
+
+
+static size_t vm_address(struct vm_state *M) {
+ return M->i.address + (M->i.p - M->i.base);
+} /* vm_address() */
+
+
+static void vm_push(struct vm_state *M, int64_t v) {
+ M->stack[M->sp++] = v;
+} /* vm_push() */
+
+
+static int64_t vm_pop(struct vm_state *M) {
+ return M->stack[--M->sp];
+} /* vm_pop() */
+
+
+NOTUSED static int64_t vm_peek(struct vm_state *M, int i) {
+ return (i < 0)? M->stack[M->sp + i] : M->stack[i];
+} /* vm_peek() */
+
+
+static void vm_conv(struct vm_state *M, int flags, int width, int prec, int fc, int64_t word) {
+ char fmt[32], *fp, buf[256], label[3];
+ const char *s = NULL;
+ int i, len;
+
+ switch (fc) {
+ case FC2('_', 'c'):
+ s = tooctal(label, (unsigned char) word);
+ prec = (prec > 0)? MIN(prec, 3) : 3;
+ fc = 's';
+
+ break;
+ case FC2('_', 'p'):
+ word = toprint((unsigned char) word);
+ fc = 'c';
+
+ break;
+ case FC2('_', 'u'):
+ s = toshort(label, (unsigned char) word);
+ prec = (prec > 0)? MIN(prec, 3) : 3;
+ fc = 's';
+
+ break;
+ case FC2('_', 'd'):
+ word = M->i.address + (M->i.p - M->i.base);
+ fc = 'd';
+
+ break;
+ case FC2('_', 'o'):
+ word = M->i.address + (M->i.p - M->i.base);
+ fc = 'o';
+
+ break;
+ case FC2('_', 'x'):
+ word = M->i.address + (M->i.p - M->i.base);
+ fc = 'x';
+
+ break;
+ case FC2('_', 'D'):
+ /* FALL THROUGH */
+ case FC2('_', 'O'):
+ /* FALL THROUGH */
+ case FC2('_', 'X'):
+ fc = 'x';
+
+ vm_throw(M, HXD_ENOTSUPP);
+
+ break;
+ case FC1('s'):
+ s = (const char *)M->i.p;
+
+ if (prec <= 0 || prec > M->i.pe - M->i.p)
+ prec = (int) (M->i.pe - M->i.p);
+
+ break;
+ case FC1('c'):
+ /* FALL THROUGH */
+ case FC1('d'): case FC1('i'): case FC1('o'):
+ case FC1('u'): case FC1('X'): case FC1('x'):
+ break;
+ default:
+ vm_throw(M, HXD_ENOTSUPP);
+
+ break;
+ } /* switch() */
+
+ fp = fmt;
+
+ *fp++ = '%';
+
+ if (flags & F_HASH)
+ *fp++ = '#';
+ if (flags & F_ZERO)
+ *fp++ = '0';
+ if (flags & F_MINUS)
+ *fp++ = '-';
+ if (flags & F_PLUS)
+ *fp++ = '+';
+
+ *fp++ = '*';
+ *fp++ = '.';
+ *fp++ = '*';
+ *fp++ = fc;
+ *fp = '\0';
+
+ switch (fc) {
+ case 's':
+ len = MY_SNPRINTF(buf, sizeof buf, fmt, MAX(width, 0), MAX(prec, 0), s);
+
+ break;
+ case 'u':
+ len = MY_SNPRINTF(buf, sizeof buf, fmt, MAX(width, 0), prec, (unsigned)word);
+
+ break;
+ default:
+ len = MY_SNPRINTF(buf, sizeof buf, fmt, MAX(width, 0), prec, (int)word);
+
+ break;
+ }
+
+ if (-1 == len)
+ vm_throw(M, errno);
+
+ if (len >= (int)sizeof buf)
+ vm_throw(M, ENOMEM);
+
+ for (i = 0; i < len; i++)
+ vm_putc(M, buf[i]);
+} /* vm_conv() */
+
+
+#define VM_FASTER defined(__GNUC__)
+
+#if VM_FASTER
+#define BEGIN goto *jump[M->code[M->pc]]
+#define END (void)0
+#define CASE(op) XPASTE(OP_, op)
+#define NEXT goto *jump[M->code[++M->pc]]
+#else
+#define BEGIN exec: switch (M->code[M->pc]) {
+#define END } (void)0
+#define CASE(op) case XPASTE(OP_, op)
+#define NEXT ++M->pc; goto exec
+#endif
+
+static void vm_exec(struct vm_state *M) {
+#if VM_FASTER
+#define L(L) (&&XPASTE(OP_, L))
+ static const void *const jump[] = {
+ L(HALT), L(NOOP), L(TRAP), L(PC), L(TRUE), L(FALSE),
+ L(ZERO), L(ONE), L(TWO), L(I8), L(I16), L(I32),
+ L(NEG), L(SUB), L(ADD), L(NOT), L(OR), L(LT),
+ L(POP), L(DUP), L(SWAP), L(READ), L(COUNT), L(PUTC), L(CONV),
+ L(CHOP), L(PAD), L(JMP), L(RESET),
+ L(2XBYTE), L(PBYTE), L(7XADDR), L(8XADDR),
+ };
+#endif
+ int64_t v;
+
+ BEGIN;
+
+ CASE(HALT):
+ return /* void */;
+ CASE(NOOP):
+ NEXT;
+ CASE(TRAP):
+ vm_throw(M, HXD_EOOPS);
+
+ NEXT;
+ CASE(PC):
+ vm_push(M, M->pc);
+
+ NEXT;
+ CASE(TRUE):
+ vm_push(M, 1);
+
+ NEXT;
+ CASE(FALSE):
+ vm_push(M, 0);
+
+ NEXT;
+ CASE(ZERO):
+ vm_push(M, 0);
+
+ NEXT;
+ CASE(ONE):
+ vm_push(M, 1);
+
+ NEXT;
+ CASE(TWO):
+ vm_push(M, 2);
+
+ NEXT;
+ CASE(I8):
+ vm_push(M, M->code[++M->pc]);
+
+ NEXT;
+ CASE(I16):
+ v = M->code[++M->pc] << 8;
+ v |= M->code[++M->pc];
+
+ vm_push(M, v);
+
+ NEXT;
+ CASE(I32):
+ v = M->code[++M->pc] << 24;
+ v = M->code[++M->pc] << 16;
+ v = M->code[++M->pc] << 8;
+ v |= M->code[++M->pc];
+
+ vm_push(M, v);
+
+ NEXT;
+ CASE(NEG):
+ vm_push(M, -vm_pop(M));
+
+ NEXT;
+ CASE(SUB): {
+ int64_t b = vm_pop(M);
+ int64_t a = vm_pop(M);
+
+ vm_push(M, a - b);
+
+ NEXT;
+ }
+ CASE(ADD): {
+ int64_t b = vm_pop(M);
+ int64_t a = vm_pop(M);
+
+ vm_push(M, a + b);
+
+ NEXT;
+ }
+ CASE(NOT):
+ vm_push(M, !vm_pop(M));
+
+ NEXT;
+ CASE(OR): {
+ int64_t b = vm_pop(M);
+ int64_t a = vm_pop(M);
+
+ vm_push(M, a || b);
+
+ NEXT;
+ }
+ CASE(LT): {
+ int64_t b = vm_pop(M);
+ int64_t a = vm_pop(M);
+
+ vm_push(M, a < b);
+
+ NEXT;
+ }
+ CASE(POP):
+ vm_pop(M);
+
+ NEXT;
+ CASE(DUP): {
+ int64_t v = vm_pop(M);
+
+ vm_push(M, v);
+ vm_push(M, v);
+
+ NEXT;
+ }
+ CASE(SWAP): {
+ int64_t x = vm_pop(M);
+ int64_t y = vm_pop(M);
+
+ vm_push(M, x);
+ vm_push(M, y);
+
+ NEXT;
+ }
+ CASE(READ): {
+ int64_t i, n, v;
+
+ n = vm_pop(M);
+ v = 0;
+
+ if (M->flags & HXD_BIG_ENDIAN) {
+ for (i = 0; i < n && M->i.p < M->i.pe; i++) {
+ v <<= 8;
+ v |= *M->i.p++;
+ }
+ } else {
+ for (i = 0; i < n && M->i.p < M->i.pe; i++) {
+ v |= *M->i.p++ << (8 * i);
+ }
+ }
+
+ vm_push(M, v);
+
+ NEXT;
+ }
+ CASE(COUNT):
+ vm_push(M, M->i.pe - M->i.p);
+
+ NEXT;
+ CASE(PUTC): {
+ vm_putc(M, M->code[++M->pc]);
+
+ NEXT;
+ }
+ CASE(CONV): {
+ int fc = (int) vm_pop(M);
+ int prec = (int) vm_pop(M);
+ int width = (int) vm_pop(M);
+ int flags = (int) vm_pop(M);
+ int64_t word = vm_pop(M);
+
+ vm_conv(M, flags, width, prec, fc, word);
+
+ NEXT;
+ }
+ CASE(CHOP):
+ v = vm_pop(M);
+
+ while (v > 0 && M->o.p > M->o.base) {
+ --M->o.p;
+ --v;
+ }
+
+ NEXT;
+ CASE(PAD):
+ v = vm_pop(M);
+
+ while (v-- > 0)
+ vm_putc(M, ' ');
+
+ NEXT;
+ CASE(JMP): {
+ int64_t pc = vm_pop(M);
+
+ if (vm_pop(M)) {
+ M->pc = pc % countof(M->code);
+#if VM_FASTER
+ goto *jump[M->code[pc]];
+#else
+ goto exec;
+#endif
+ }
+
+ NEXT;
+ }
+ CASE(RESET):
+ M->i.p = M->i.base;
+
+ NEXT;
+ CASE(2XBYTE):
+ vm_putx(M, vm_getc(M));
+
+ NEXT;
+ CASE(PBYTE):
+ vm_putc(M, toprint(vm_getc(M)));
+
+ NEXT;
+ CASE(7XADDR): {
+ size_t addr = vm_address(M);
+ vm_putc(M, "0123456789abcdef"[0x0f & (addr >> 24)]);
+ vm_putx(M, (unsigned char) (addr >> 16));
+ vm_putx(M, (unsigned char) (addr >> 8));
+ vm_putx(M, (unsigned char) (addr >> 0));
+
+ NEXT;
+ }
+ CASE(8XADDR): {
+ size_t addr = vm_address(M);
+ vm_putx(M, (unsigned char) (addr >> 24));
+ vm_putx(M, (unsigned char) (addr >> 16));
+ vm_putx(M, (unsigned char) (addr >> 8));
+ vm_putx(M, (unsigned char) (addr >> 0));
+
+ NEXT;
+ }
+ END;
+} /* vm_exec() */
+
+
+static void emit_op(struct vm_state *M, unsigned char code) {
+ if (M->pc >= (int)sizeof M->code - 1)
+ vm_throw(M, ENOMEM);
+ M->code[M->pc++] = code;
+} /* emit_op() */
+
+
+static void emit_int(struct vm_state *M, int64_t i) {
+ _Bool isneg;
+
+ if ((isneg = (i < 0)))
+ i *= -1;
+
+ if (i > ((1LL << 32) - 1)) {
+ vm_throw(M, ERANGE);
+ } else if (i > ((1LL << 16) - 1)) {
+ emit_op(M, OP_I32);
+ emit_op(M, 0xff & (i >> 24));
+ emit_op(M, 0xff & (i >> 16));
+ emit_op(M, 0xff & (i >> 8));
+ emit_op(M, 0xff & (i >> 0));
+ } else if (i > ((1LL << 8) - 1)) {
+ emit_op(M, OP_I16);
+ emit_op(M, 0xff & (i >> 8));
+ emit_op(M, 0xff & (i >> 0));
+ } else {
+ switch (i) {
+ case 0:
+ emit_op(M, OP_ZERO);
+ break;
+ case 1:
+ emit_op(M, OP_ONE);
+ break;
+ case 2:
+ emit_op(M, OP_TWO);
+ break;
+ default:
+ emit_op(M, OP_I8);
+ emit_op(M, 0xff & i);
+ break;
+ }
+ }
+
+ if (isneg) {
+ emit_op(M, OP_NEG);
+ }
+} /* emit_int() */
+
+
+static void emit_putc(struct vm_state *M, unsigned char chr) {
+ emit_op(M, OP_PUTC);
+ emit_op(M, chr);
+} /* emit_putc() */
+
+
+static void emit_jmp(struct vm_state *M, int *from) {
+ *from = M->pc;
+ emit_op(M, OP_TRAP);
+ emit_op(M, OP_TRAP);
+ emit_op(M, OP_TRAP);
+ emit_op(M, OP_TRAP);
+ emit_op(M, OP_TRAP);
+ emit_op(M, OP_TRAP);
+} /* emit_jmp() */
+
+
+static void emit_link(struct vm_state *M, int from, int to) {
+ int pc = M->pc;
+
+ M->pc = from;
+
+ emit_op(M, OP_PC);
+
+ if (to < from) {
+ if (from - to > 65535)
+ vm_throw(M, ERANGE);
+
+ emit_op(M, OP_I16);
+ M->code[M->pc++] = 0xff & ((from - to) >> 8);
+ M->code[M->pc++] = 0xff & ((from - to) >> 0);
+ emit_op(M, OP_SUB);
+ } else {
+ if (to - from > 65535)
+ vm_throw(M, ERANGE);
+
+ emit_op(M, OP_I16);
+ M->code[M->pc++] = 0xff & ((to - from) >> 8);
+ M->code[M->pc++] = 0xff & ((to - from) >> 0);
+ emit_op(M, OP_ADD);
+ }
+
+ emit_op(M, OP_JMP);
+
+ M->pc = pc;
+} /* emit_link() */
+
+
+static void emit_unit(struct vm_state *M, int loop, int limit, int flags, size_t *blocksize, const unsigned char **fmt) {
+ _Bool quoted = 0, escaped = 0;
+ int consumes = 0, chop = 0;
+ int L1, L2, C1 = 0, from, ch;
+
+ loop = (loop < 0)? 1 : loop;
+
+ /* loop counter */
+ emit_int(M, 0);
+
+ /* top of loop */
+ L1 = M->pc;
+ emit_op(M, OP_DUP); /* dup counter */
+ emit_int(M, loop); /* push loop count */
+ emit_op(M, OP_SWAP);
+ emit_op(M, OP_SUB); /* loop - counter */
+ emit_op(M, OP_NOT);
+ if (flags & HXD_NOPADDING) {
+ emit_op(M, OP_COUNT);
+ C1 = M->pc; /* patch destination for unit size */
+ emit_op(M, OP_TRAP);
+ emit_op(M, OP_TRAP);
+ emit_op(M, OP_LT);
+ emit_op(M, OP_OR);
+ }
+ emit_jmp(M, &L2);
+
+ emit_int(M, 1);
+ emit_op(M, OP_ADD);
+
+ while ((ch = **fmt)) {
+ switch (ch) {
+ case '%': {
+ int fc, flags, width, prec, bytes;
+
+ if (escaped)
+ goto copyout;
+
+ ++*fmt;
+
+ if (!(fc = getcnv(&flags, &width, &prec, &bytes, fmt)))
+ vm_throw(M, HXD_EFORMAT);
+
+ --*fmt;
+
+ if (fc == '%') {
+ ch = '%';
+ goto copyout;
+ }
+
+ if (limit >= 0 && bytes > 0) {
+ bytes = MIN(limit - consumes, bytes);
+
+ if (!bytes) /* FIXME: define better error */
+ vm_throw(M, HXD_EDRAINED);
+ }
+
+ consumes += bytes;
+
+ {
+ int J1, J2;
+
+ if (bytes > 0) {
+ if (width > 0) {
+ emit_op(M, OP_COUNT);
+ emit_jmp(M, &J1);
+ emit_int(M, width);
+ emit_op(M, OP_PAD);
+ emit_op(M, OP_TRUE);
+ emit_jmp(M, &J2);
+ emit_link(M, J1, M->pc);
+ } else {
+ emit_op(M, OP_COUNT);
+ emit_op(M, OP_NOT);
+ emit_jmp(M, &J2);
+ }
+ }
+
+ if (fc == 'x' && OK_2XBYTE(flags, width, prec)) {
+ emit_op(M, OP_2XBYTE);
+ } else if (fc == FC2('_', 'p') && OK_PBYTE(flags, width, prec)) {
+ emit_op(M, OP_PBYTE);
+ } else if (fc == FC2('_', 'x') && OK_7XADDR(flags, width, prec)) {
+ emit_op(M, OP_7XADDR);
+ } else if (fc == FC2('_', 'x') && OK_8XADDR(flags, width, prec)) {
+ emit_op(M, OP_8XADDR);
+ } else {
+ emit_int(M, (fc == 's')? 0 : bytes);
+ emit_op(M, OP_READ);
+ emit_int(M, flags);
+ emit_int(M, width);
+ emit_int(M, prec);
+ emit_int(M, fc);
+ emit_op(M, OP_CONV);
+ }
+
+ if (bytes > 0)
+ emit_link(M, J2, M->pc);
+ }
+
+ chop = 0;
+
+ break;
+ }
+ case ' ': case '\t': case '\n':
+ if (quoted || escaped)
+ goto copyout;
+
+ goto epilog;
+ case '"':
+ if (escaped)
+ goto copyout;
+
+ quoted = !quoted;
+
+ break;
+ case '\\':
+ if (escaped)
+ goto copyout;
+
+ escaped = 1;
+
+ break;
+ case '0':
+ if (escaped)
+ ch = '\0';
+
+ goto copyout;
+ case 'a':
+ if (escaped)
+ ch = '\a';
+
+ goto copyout;
+ case 'b':
+ if (escaped)
+ ch = '\b';
+
+ goto copyout;
+ case 'f':
+ if (escaped)
+ ch = '\f';
+
+ goto copyout;
+ case 'n':
+ if (escaped)
+ ch = '\n';
+
+ goto copyout;
+ case 'r':
+ if (escaped)
+ ch = '\r';
+
+ goto copyout;
+ case 't':
+ if (escaped)
+ ch = '\t';
+
+ goto copyout;
+ case 'v':
+ if (escaped)
+ ch = '\v';
+
+ goto copyout;
+ default:
+copyout:
+ emit_putc(M, ch);
+
+ escaped = 0;
+
+ if (hxd_isspace(ch, 0)) {
+ chop++;
+ } else {
+ chop = 0;
+ }
+ }
+
+ ++*fmt;
+ }
+
+epilog:
+ if (loop > 0 && consumes < limit) {
+ emit_int(M, limit - consumes);
+ emit_op(M, OP_READ);
+ emit_op(M, OP_POP);
+
+ consumes = limit;
+ }
+
+ if (flags & HXD_NOPADDING) {
+ if (consumes > 255)
+ vm_throw(M, ERANGE);
+
+ /* patch in our unit size */
+ M->code[C1 + 0] = OP_I8;
+ M->code[C1 + 1] = consumes;
+ }
+
+ emit_op(M, OP_TRUE);
+ emit_jmp(M, &from);
+ emit_link(M, from, L1);
+
+ emit_link(M, L2, M->pc);
+ emit_op(M, OP_POP); /* pop loop counter */
+
+ if (loop > 1 && chop > 0) {
+ emit_int(M, chop);
+ emit_op(M, OP_CHOP);
+ }
+
+ *blocksize += (size_t)(consumes * loop);
+
+ return /* void */;
+} /* emit_unit() */
+
+
+struct hexdump {
+ struct vm_state vm;
+
+ char help[64];
+}; /* struct hexdump */
+
+
+static void hxd_init(struct hexdump *X) {
+ memset(X, 0, sizeof *X);
+} /* hxd_init() */
+
+
+struct hexdump *hxd_open(int *error) {
+ struct hexdump *X;
+
+ if (!(X = malloc(sizeof *X)))
+ goto syerr;
+
+ hxd_init(X);
+
+ return X;
+syerr:
+ *error = errno;
+
+ hxd_close(X);
+
+ return NULL;
+} /* hxd_open() */
+
+
+static void hxd_destroy(struct hexdump *X) {
+ free(X->vm.i.base);
+ free(X->vm.o.base);
+} /* hxd_destroy() */
+
+
+void hxd_close(struct hexdump *X) {
+ if (!X)
+ return /* void */;
+
+ hxd_destroy(X);
+ free(X);
+} /* hxd_close() */
+
+
+void hxd_reset(struct hexdump *X) {
+ X->vm.i.address = 0;
+ X->vm.i.p = X->vm.i.base;
+ X->vm.o.p = X->vm.o.base;
+ X->vm.pc = 0;
+} /* hxd_reset() */
+
+
+int hxd_compile(struct hexdump *X, const char *_fmt, int flags) {
+ const unsigned char *fmt = (const unsigned char *)_fmt;
+ unsigned char *tmp;
+ int error;
+
+ hxd_reset(X);
+
+ if ((error = vm_enter(&X->vm)))
+ goto error;
+
+ X->vm.flags = flags;
+
+ if (!HXD_BYTEORDER(X->vm.flags)) {
+ union { int i; char c; } u = { 0 };
+
+ u.c = 1;
+ X->vm.flags |= (u.i & 0xff)? HXD_LITTLE_ENDIAN : HXD_BIG_ENDIAN;
+ }
+
+ while (skipws(&fmt, 1)) {
+ int lc, loop, limit, flags;
+ size_t blocksize = 0;
+
+ flags = X->vm.flags;
+
+ emit_op(&X->vm, OP_RESET);
+
+ do {
+ loop = getint(&fmt);
+
+ if ('/' == skipws(&fmt, 0)) {
+ fmt++;
+ limit = getint(&fmt);
+
+ if (*fmt == '?') {
+ flags |= HXD_NOPADDING;
+ fmt++;
+ }
+ } else {
+ limit = -1;
+ }
+
+ skipws(&fmt, 0);
+ emit_unit(&X->vm, loop, limit, flags, &blocksize, &fmt);
+ } while ((lc = skipws(&fmt, 0)) && lc != '\n');
+
+ if (blocksize > X->vm.blocksize)
+ X->vm.blocksize = blocksize;
+ }
+
+ emit_op(&X->vm, OP_HALT);
+ memset(&X->vm.code[X->vm.pc], OP_TRAP, sizeof X->vm.code - X->vm.pc);
+
+ if (!(tmp = realloc(X->vm.i.base, X->vm.blocksize)))
+ goto syerr;
+
+ X->vm.i.base = tmp;
+ X->vm.i.p = tmp;
+ X->vm.i.pe = &tmp[X->vm.blocksize];
+
+ return 0;
+syerr:
+ error = errno;
+error:
+ hxd_reset(X);
+ memset(X->vm.code, 0, sizeof X->vm.code);
+
+ return error;
+} /* hxd_compile() */
+
+
+size_t hxd_blocksize(struct hexdump *X) {
+ return X->vm.blocksize;
+} /* hxd_blocksize() */
+
+
+const char *hxd_help(struct hexdump *X) {
+ return "helps";
+} /* hxd_help() */
+
+
+int hxd_write(struct hexdump *X, const void *src, size_t len) {
+ const unsigned char *p, *pe;
+ size_t n;
+ int error;
+
+ if ((error = vm_enter(&X->vm)))
+ goto error;
+
+ if (X->vm.i.pe == X->vm.i.base)
+ vm_throw(&X->vm, HXD_EOOPS);
+
+ p = src;
+ pe = p + len;
+
+ while (p < pe) {
+ n = MIN(pe - p, X->vm.i.pe - X->vm.i.p);
+ memcpy(X->vm.i.p, p, n);
+ X->vm.i.p += n;
+ p += n;
+
+ if (X->vm.i.p < X->vm.i.pe)
+ break;
+
+ X->vm.i.p = X->vm.i.base;
+ X->vm.pc = 0;
+ vm_exec(&X->vm);
+ X->vm.i.p = X->vm.i.base;
+ X->vm.i.address += X->vm.blocksize;
+ }
+
+ return 0;
+error:
+ return error;
+} /* hxd_write() */
+
+
+int hxd_flush(struct hexdump *X) {
+ unsigned char *pe;
+ int error;
+
+ if ((error = vm_enter(&X->vm)))
+ goto error;
+
+ if (X->vm.i.p > X->vm.i.base) {
+ pe = X->vm.i.pe;
+ X->vm.i.pe = X->vm.i.p;
+ X->vm.i.p = X->vm.i.base;
+ X->vm.pc = 0;
+ vm_exec(&X->vm);
+ X->vm.i.p = X->vm.i.base;
+ X->vm.i.pe = pe;
+ }
+
+ return 0;
+error:
+ return error;
+} /* hxd_flush() */
+
+
+size_t hxd_read(struct hexdump *X, void *dst, size_t lim) {
+ unsigned char *p, *pe, *op;
+ size_t n;
+
+ p = dst;
+ pe = p + lim;
+ op = X->vm.o.base;
+
+ while (p < pe && op < X->vm.o.p) {
+ n = MIN(pe - p, X->vm.o.p - op);
+ memcpy(p, op, n);
+ p += n;
+ op += n;
+ }
+
+ n = X->vm.o.p - op;
+ memmove(X->vm.o.base, op, n);
+ X->vm.o.p = &X->vm.o.base[n];
+
+ return p - (unsigned char *)dst;
+} /* hxd_read() */
+
+
+const char *hxd_strerror(int error) {
+ static const char *txt[] = {
+ [HXD_EFORMAT - HXD_EBASE] = "invalid format",
+ [HXD_EDRAINED - HXD_EBASE] = "unit drains buffer",
+ [HXD_ENOTSUPP - HXD_EBASE] = "unsupported conversion sequence",
+ [HXD_EOOPS - HXD_EBASE] = "machine traps",
+ };
+
+ if (error >= 0)
+ return strerror(error);
+
+ if (error >= HXD_EBASE && error < HXD_ELAST) {
+ error -= HXD_EBASE;
+
+ if (error < (int)countof(txt) && txt[error])
+ return txt[error];
+ }
+
+ return "unknown error (hexdump)";
+} /* hxd_strerror() */
+
+
+int hxd_version(void) {
+ return HXD_VERSION;
+} /* hxd_version() */
+
+
+const char *hxd_vendor(void) {
+ return HXD_VENDOR;
+} /* hxd_vendor() */
+
+
+int hxd_v_rel(void) {
+ return HXD_V_REL;
+} /* hxd_v_rel() */
+
+
+int hxd_v_abi(void) {
+ return HXD_V_ABI;
+} /* hxd_v_abi() */
+
+
+int hxd_v_api(void) {
+ return HXD_V_API;
+} /* hxd_v_api() */
+
+
+#if HEXDUMP_LUALIB
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#define HEXDUMP_CLASS "HEXDUMP*"
+
+
+static inline struct hexdump *hxdL_checkudata(lua_State *L, int index) {
+ return *(struct hexdump **)luaL_checkudata(L, index, HEXDUMP_CLASS);
+} /* hxdL_checkudata() */
+
+
+static struct hexdump *hxdL_push(lua_State *L) {
+ struct hexdump **X;
+ int error;
+
+ X = lua_newuserdata(L, sizeof *X);
+ *X = NULL;
+
+ luaL_getmetatable(L, HEXDUMP_CLASS);
+ lua_setmetatable(L, -2);
+
+ if (!(*X = hxd_open(&error)))
+ luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+ return *X;
+} /* hxdL_push() */
+
+
+static int hxdL_apply(lua_State *L) {
+ const char *fmt, *p, *pe;
+ size_t n;
+ struct hexdump *X;
+ luaL_Buffer B;
+ int top = lua_gettop(L), data = 2, flags = 0;
+ int error;
+
+ fmt = luaL_checkstring(L, 1);
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ flags = lua_tointeger(L, 2);
+ data = 3;
+ }
+
+ lua_pushvalue(L, 1);
+ lua_rawget(L, lua_upvalueindex(1));
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+
+ lua_newtable(L);
+
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, lua_upvalueindex(1));
+ }
+
+ lua_rawgeti(L, -1, flags);
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+
+ X = hxdL_push(L);
+
+ if ((error = hxd_compile(X, fmt, flags)))
+ goto error;
+
+ lua_pushvalue(L, -1);
+ lua_rawseti(L, -3, flags);
+ } else {
+ X = hxdL_checkudata(L, -1);
+ }
+
+ hxd_reset(X);
+
+ luaL_buffinit(L, &B);
+
+ for (; data <= top; data++) {
+ p = luaL_checklstring(L, data, &n);
+ pe = p + n;
+
+ while (p < pe) {
+ n = MIN(pe - p, 1024);
+
+ if ((error = hxd_write(X, p, n)))
+ goto error;
+
+ p += n;
+
+ while ((n = hxd_read(X, luaL_prepbuffer(&B), LUAL_BUFFERSIZE)))
+ luaL_addsize(&B, n);
+ }
+ }
+
+ if ((error = hxd_flush(X)))
+ goto error;
+
+ while ((n = hxd_read(X, luaL_prepbuffer(&B), LUAL_BUFFERSIZE)))
+ luaL_addsize(&B, n);
+
+ luaL_pushresult(&B);
+
+ return 1;
+error:
+ return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+} /* hxdL_apply() */
+
+
+static int hxdL__call(lua_State *L) {
+ lua_pushvalue(L, lua_upvalueindex(1));
+ lua_replace(L, 1);
+
+ lua_call(L, lua_gettop(L) - 1, 1);
+
+ return 1;
+} /* hxdL__call() */
+
+
+static int hxdL_new(lua_State *L) {
+ hxdL_push(L);
+
+ return 1;
+} /* hxdL_new() */
+
+
+static int hxdL_compile(lua_State *L) {
+ struct hexdump *X = hxdL_checkudata(L, 1);
+ const char *fmt = luaL_checkstring(L, 2);
+ int flags = luaL_optint(L, 3, 0);
+ int error;
+
+ if ((error = hxd_compile(X, fmt, flags)))
+ return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* hxdL_compile() */
+
+
+static int hxdL_blocksize(lua_State *L) {
+ struct hexdump *X = hxdL_checkudata(L, 1);
+
+ lua_pushnumber(L, hxd_blocksize(X));
+
+ return 1;
+} /* hxdL_blocksize() */
+
+
+static int hxdL_write(lua_State *L) {
+ struct hexdump *X = hxdL_checkudata(L, 1);
+ const char *data;
+ size_t size;
+ int error;
+
+ data = luaL_checklstring(L, 2, &size);
+
+ if ((error = hxd_write(X, data, size)))
+ return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* hxdL_write() */
+
+
+static int hxdL_flush(lua_State *L) {
+ struct hexdump *X = hxdL_checkudata(L, 1);
+ int error;
+
+ if ((error = hxd_flush(X)))
+ return luaL_error(L, "hexdump: %s", hxd_strerror(error));
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* hxdL_flush() */
+
+
+static int hxdL_read(lua_State *L) {
+ struct hexdump *X = hxdL_checkudata(L, 1);
+ luaL_Buffer B;
+ size_t count;
+ int error;
+
+ luaL_buffinit(L, &B);
+
+ while ((count = hxd_read(X, luaL_prepbuffer(&B), LUAL_BUFFERSIZE)))
+ luaL_addsize(&B, count);
+
+ luaL_pushresult(&B);
+
+ return 1;
+} /* hxdL_read() */
+
+
+static int hxdL__gc(lua_State *L) {
+ struct hexdump **X = luaL_checkudata(L, 1, HEXDUMP_CLASS);
+
+ hxd_close(*X);
+ *X = NULL;
+
+ return 0;
+} /* hxdL__gc() */
+
+
+static const luaL_Reg hxdL_methods[] = {
+ { "compile", &hxdL_compile },
+ { "blocksize", &hxdL_blocksize },
+ { "write", &hxdL_write },
+ { "flush", &hxdL_flush },
+ { "read", &hxdL_read },
+ { NULL, NULL },
+}; /* hxdL_methods[] */
+
+
+static const luaL_Reg hxdL_metatable[] = {
+ { "__gc", &hxdL__gc },
+ { NULL, NULL },
+}; /* hxdL_metatable[] */
+
+
+static const luaL_Reg hxdL_globals[] = {
+ { "new", &hxdL_new },
+ { NULL, NULL },
+}; /* hxdL_globals[] */
+
+
+static void hxdL_register(lua_State *L, const luaL_Reg *l) {
+#if LUA_VERSION_NUM >= 502
+ luaL_setfuncs(L, l, 0);
+#else
+ luaL_register(L, NULL, l);
+#endif
+} /* hxdL_register() */
+
+
+int luaopen_hexdump(lua_State *L) {
+ static const struct { const char *k; int v; } macro[] = {
+ { "NATIVE", HXD_NATIVE },
+ { "NETWORK", HXD_NETWORK },
+ { "BIG_ENDIAN", HXD_BIG_ENDIAN },
+ { "LITTLE_ENDIAN", HXD_LITTLE_ENDIAN },
+ { "NOPADDING", HXD_NOPADDING },
+ };
+ static const struct { const char *k; const char *v; } predef[] = {
+ { "b", HEXDUMP_b },
+ { "c", HEXDUMP_c },
+ { "C", HEXDUMP_C },
+ { "d", HEXDUMP_d },
+ { "o", HEXDUMP_o },
+ { "x", HEXDUMP_x },
+ { "i", HEXDUMP_i },
+ };
+ unsigned i;
+
+ if (luaL_newmetatable(L, HEXDUMP_CLASS)) {
+ hxdL_register(L, hxdL_metatable);
+ lua_newtable(L);
+ hxdL_register(L, hxdL_methods);
+ lua_setfield(L, -2, "__index");
+ }
+
+ lua_pop(L, 1);
+
+ lua_newtable(L);
+ hxdL_register(L, hxdL_globals);
+
+ lua_newtable(L); /* cache of compiled formats */
+ lua_pushcclosure(L, &hxdL_apply, 1);
+
+ lua_newtable(L); /* metatable of our global table */
+ lua_pushvalue(L, -2);
+ lua_pushcclosure(L, &hxdL__call, 1);
+ lua_setfield(L, -2, "__call");
+ lua_setmetatable(L, -3);
+
+ lua_setfield(L, -2, "apply"); /* global.apply */
+
+ for (i = 0; i < countof(macro); i++) {
+ lua_pushinteger(L, macro[i].v);
+ lua_setfield(L, -2, macro[i].k);
+ }
+
+ for (i = 0; i < countof(predef); i++) {
+ lua_pushstring(L, predef[i].v);
+ lua_setfield(L, -2, predef[i].k);
+ }
+
+ lua_pushinteger(L, HXD_VERSION);
+ lua_setfield(L, -2, "VERSION");
+
+ lua_pushstring(L, HXD_VENDOR);
+ lua_setfield(L, -2, "VENDOR");
+
+ return 1;
+} /* luaopen_hexdump() */
+
+#else
+
+int luaopen_hexdump() {
+ abort();
+} /* luaopen_hexdump() */
+
+#endif /* HEXDUMP_LUALIB */
+
+
+#if HEXDUMP_MAIN
+
+#include <stdio.h> /* FILE stdout stderr stdin fprintf(3) */
+
+#include <string.h> /* strcmp(3) */
+
+
+#ifndef _WIN32
+#include <unistd.h> /* getopt(3) */
+#include <err.h> /* err(3) errx(3) */
+#else
+#include <stdarg.h>
+
+static void err(int eval, const char *fmt, ...) {
+ int error = errno;
+ va_list ap;
+ va_start(ap, fmt);
+ fputs("hexdump: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(error));
+ va_end(ap);
+ exit(eval);
+}
+
+static void errx(int eval, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ fputs("hexdump: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+ exit(eval);
+}
+#endif
+
+
+static void run(struct hexdump *X, FILE *fp, _Bool flush) {
+ char buf[256];
+ size_t len;
+ int error;
+
+ while ((len = fread(buf, 1, sizeof buf, fp))) {
+ if ((error = hxd_write(X, buf, len)))
+ errx(EXIT_FAILURE, "%s", hxd_strerror(error));
+
+ while ((len = hxd_read(X, buf, sizeof buf)))
+ fwrite(buf, 1, len, stdout);
+ }
+
+ if (flush) {
+ if ((error = hxd_flush(X)))
+ errx(EXIT_FAILURE, "%s", hxd_strerror(error));
+
+ while ((len = hxd_read(X, buf, sizeof buf)))
+ fwrite(buf, 1, len, stdout);
+ }
+} /* run() */
+
+
+#ifdef _WIN32
+
+int main(int argc, char **argv)
+{
+ int flags = 0;
+ struct hexdump *X;
+ char *fmt = HEXDUMP_i;
+ int error;
+
+ if (strcmp(argv[1], "-i") != 0 || argc < 3)
+ {
+ err(EXIT_FAILURE, "Error: must call with parameters -i input_file, you called with %s", argv[1]);
+ }
+
+ if (!(X = hxd_open(&error))){
+ errx(EXIT_FAILURE, "open: %s", hxd_strerror(error));
+ }
+
+ if ((error = hxd_compile(X, fmt, flags))){
+ errx(EXIT_FAILURE, "%s: %s", fmt, hxd_strerror(error));
+ }
+
+ FILE *fp;
+
+ if (!(fp = fopen(argv[2], "rb")))
+ err(EXIT_FAILURE, "%s", argv[2]);
+
+ run(X, fp, 1);
+
+ fclose(fp);
+
+ hxd_close(X);
+
+ return 0;
+}
+
+#else
+
+int main(int argc, char **argv) {
+ extern char *optarg;
+ extern int optind;
+ int opt, flags = 0;
+ _Bool dump = 0;
+ struct hexdump *X;
+ char buf[256], *fmt = HEXDUMP_x, fmtbuf[512];
+ size_t len;
+ int error;
+
+ while (-1 != (opt = getopt(argc, argv, "bcCde:f:oxiBLPDVh"))) {
+ switch (opt) {
+ case 'b':
+ fmt = HEXDUMP_b;
+
+ break;
+ case 'c':
+ fmt = HEXDUMP_c;
+
+ break;
+ case 'C':
+ fmt = HEXDUMP_C;
+ break;
+ case 'd':
+ fmt = HEXDUMP_d;
+
+ break;
+ case 'e':
+ fmt = optarg;
+
+ break;
+ case 'f': {
+ FILE *fp = (!strcmp(optarg, "-"))? stdin : fopen(optarg, "r");
+
+ if (!fp)
+ err(EXIT_FAILURE, "%s", optarg);
+
+ len = fread(fmtbuf, 1, sizeof fmtbuf - 1, fp);
+ fmtbuf[len] = '\0';
+
+ if (fp != stdin)
+ fclose(fp);
+
+ fmt = fmtbuf;
+
+ break;
+ }
+ case 'o':
+ fmt = HEXDUMP_o;
+
+ break;
+ case 'x':
+ fmt = HEXDUMP_x;
+
+ break;
+ case 'i':
+ fmt = HEXDUMP_i;
+
+ break;
+ case 'B':
+ flags |= HXD_BIG_ENDIAN;
+
+ break;
+ case 'L':
+ flags |= HXD_LITTLE_ENDIAN;
+
+ break;
+ case 'P':
+ flags |= HXD_NOPADDING;
+
+ break;
+ case 'D':
+ dump = 1;
+
+ break;
+ case 'V':
+ printf("%s (hexdump.c) %.8X\n", argv[0], hxd_version());
+ printf("built %s %s\n", __DATE__, __TIME__);
+ printf("vendor %s\n", hxd_vendor());
+ printf("release %.8X\n", hxd_v_rel());
+ printf("abi %.8X\n", hxd_v_abi());
+ printf("api %.8X\n", hxd_v_api());
+
+ return 0;
+ default: {
+ FILE *fp = (opt == 'h')? stdout : stderr;
+
+ fprintf(fp,
+ "hexdump [-bcCde:f:oxBLDVh] [file ...]\n" \
+ " -b one-byte octal display\n" \
+ " -c one-byte character display\n" \
+ " -C canonical hex+ASCII display\n" \
+ " -d two-byte decimal display\n" \
+ " -e FMT hexdump string format\n" \
+ " -f PATH path to hexdump format file\n" \
+ " -o two-byte octal display\n" \
+ " -x two-byte hexadecimal display\n" \
+ " -i one-byte hexadecimal like xxd -i\n" \
+ " -B load words big-endian\n" \
+ " -L load words little-endian\n" \
+ " -P disable padding\n" \
+ " -D dump the compiled machine\n" \
+ " -V print version\n" \
+ " -h print usage help\n" \
+ "\n" \
+ "Report bugs to <william at 25thandClement.com>\n"
+ );
+
+ return (opt == 'h')? 0 : EXIT_FAILURE;
+ }
+ } /* switch() */
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!(X = hxd_open(&error)))
+ errx(EXIT_FAILURE, "open: %s", hxd_strerror(error));
+
+ if ((error = hxd_compile(X, fmt, flags)))
+ errx(EXIT_FAILURE, "%s: %s", fmt, hxd_strerror(error));
+
+ if (dump) {
+ vm_dump(&X->vm, stdout);
+
+ goto exit;
+ }
+
+ if (!argc) {
+ run(X, stdin, 1);
+ } else {
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ FILE *fp;
+
+ if (!(fp = fopen(argv[i], "rb")))
+ err(EXIT_FAILURE, "%s", argv[i]);
+
+ run(X, fp, !argv[i + 1]);
+
+ fclose(fp);
+ }
+ }
+exit:
+ hxd_close(X);
+
+ return 0;
+} /* main() */
+#endif //_WIN32
+
+#endif /* HEXDUMP_MAIN */
diff --git a/Submodules/c3d/utilities/hexdump.h b/Submodules/c3d/utilities/hexdump.h
new file mode 100644
index 0000000..7e7decf
--- /dev/null
+++ b/Submodules/c3d/utilities/hexdump.h
@@ -0,0 +1,215 @@
+/* ==========================================================================
+ * hexdump.h - hexdump.c
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2013 William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#ifndef HEXDUMP_H
+#define HEXDUMP_H
+
+
+/*
+ * H E X D U M P V E R S I O N I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define HXD_VERSION HXD_V_REL
+#define HXD_VENDOR "william at 25thandClement.com"
+
+#define HXD_V_REL 0x20130412
+#define HXD_V_ABI 0x20130210
+#define HXD_V_API 0x20130412
+
+int hxd_version(void);
+const char *hxd_vendor(void);
+
+int hxd_v_rel(void);
+int hxd_v_abi(void);
+int hxd_v_api(void);
+
+
+/*
+ * H E X D U M P E R R O R I N T E R F A C E S
+ *
+ * Hexdump internal error conditions are returned using regular int objects.
+ * System errors are loaded from errno as soon as encountered, and the value
+ * returned through the API like internal errors. DO NOT check errno, which
+ * may have been overwritten by subsequent error handling code. Internal
+ * errors are negative and utilize a simple high-order-byte namespacing
+ * protocol. This works because ISO C and POSIX guarantee that all system
+ * error codes are positive.
+ *
+ * hxd_strerror() will forward system errors to strerror(3).
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define HXD_EBASE -(('D' << 24) | ('U' << 16) | ('M' << 8) | 'P')
+#define HXD_ERROR(error) ((error) >= XD_EBASE && (error) < XD_ELAST)
+
+
+enum hxd_errors {
+ HXD_EFORMAT = HXD_EBASE,
+ /* a compile-time error signaling an invalid format string, format
+ unit, or conversion specification syntax */
+
+ HXD_EDRAINED,
+ /* a compile-time error signaling that preceding conversions have
+ already drained the input block */
+
+ HXD_ENOTSUPP,
+ /* a compile-time error returned for valid but unsupported
+ conversion specifications */
+
+ HXD_EOOPS,
+ /* something horrible happened */
+
+ HXD_ELAST
+}; /* enum hxd_errors */
+
+#define hxd_error_t int /* for documentation purposes only */
+
+const char *hxd_strerror(hxd_error_t);
+
+
+/*
+ * H E X D U M P C O R E I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct hexdump;
+
+struct hexdump *hxd_open(hxd_error_t *);
+
+void hxd_close(struct hexdump *);
+
+void hxd_reset(struct hexdump *);
+
+#define HXD_BYTEORDER(x) (0x03 & (x))
+#define HXD_NATIVE 0x00
+#define HXD_NETWORK HXD_BIG_ENDIAN
+#define HXD_BIG_ENDIAN 0x01
+#define HXD_LITTLE_ENDIAN 0x02
+#define HXD_NOPADDING 0x04
+
+hxd_error_t hxd_compile(struct hexdump *, const char *, int);
+
+const char *hxd_help(struct hexdump *);
+
+size_t hxd_blocksize(struct hexdump *);
+
+hxd_error_t hxd_write(struct hexdump *, const void *, size_t);
+
+hxd_error_t hxd_flush(struct hexdump *);
+
+size_t hxd_read(struct hexdump *, void *, size_t);
+
+
+/*
+ * H E X D U M P C O M M O N F O R M A T S
+ *
+ * Predefined formats for hexdump(1) -b, -c, -C, -d, -o, -x, and xxd(1) -i.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define HEXDUMP_b "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\""
+#define HEXDUMP_c "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\""
+#define HEXDUMP_C "\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \"\n" \
+ "\" |\" 16/1 \"%_p\" \"|\\n\""
+#define HEXDUMP_d "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\""
+#define HEXDUMP_o "\"%07.7_ao \" 8/2 \" %06o \" \"\\n\""
+#define HEXDUMP_x "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\""
+
+#define HEXDUMP_i "\" \" 12/1? \"0x%02x, \" \"\\n\""
+
+
+/*
+ * H E X D U M P L U A I N T E R F A C E S
+ *
+ * When built with -DHEXDUMP_LUALIB then luaopen_hexdump() will return a
+ * Lua module table:
+ *
+ * local hexdump = require"hexdump"
+ *
+ * hexdump.NATIVE
+ * hexdump.NETWORK
+ * hexdump.BIG_ENDIAN
+ * hexdump.LITTLE_ENDIAN
+ * Bitwise flags which configure word byte order. The default is the
+ * native byte order.
+ *
+ * hexdump.NOPADDING
+ * Bitwise flag which disables padding; instead, formatting units are
+ * skipped entirely when the block buffer is too short.
+ *
+ * hexdump.b
+ * hexdump.c
+ * hexdump.C
+ * hexdump.d
+ * hexdump.o
+ * hexdump.x
+ * Predefined format strings of the hexdump(1) options -x, -c, -C, -d,
+ * -o, and -x, respectively.
+ *
+ * hexdump.new()
+ * Returns new context, just like hxd_open.
+ *
+ * hexdump.apply(fmt:string, [flags:int,] data:string, ...)
+ * Returns a formatted string, memoizing the context object for later
+ * reuse with the same format.
+ *
+ * The module table also has a __call metamethod, which forwards to .apply.
+ * This allows doing require"hexdump"('/1 "%.2x"', "0123456789").
+ *
+ * Every context is a simple object with methods identical to the C library,
+ * including:
+ *
+ * :reset()
+ * Resets the internal buffers. Returns true.
+ *
+ * :compile(fmt:string)
+ * Parses and compiles the format string according to the rules of BSD
+ * hexdump(1). Returns true on success, or throws an error on failure.
+ *
+ * :blocksize()
+ * Returns the block size of any compiled format string.
+ *
+ * :write(data:string)
+ * Processes the data string. The string DOES NOT have to be the same
+ * length as the block size. It can be any size, although the formatted
+ * output is buffered until drained with :read, so it's better to write
+ * smallish chunks when processing large files. Returns true on success,
+ * or throws an error on failure.
+ *
+ * :flush()
+ * Processes any data (less than the block size) in the input buffer
+ * as-if EOF was received. Returns true on success, or throws an error
+ * on failure.
+ *
+ * :read()
+ * Drains and returns the output buffer as a string.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int luaopen_hexdump(/* pointer to lua_State */);
+
+
+#endif /* HEXDUMP_H */
diff --git a/Submodules/greedy/.CMakeLists.txt.un~ b/Submodules/greedy/.CMakeLists.txt.un~
new file mode 100644
index 0000000..2b607fb
Binary files /dev/null and b/Submodules/greedy/.CMakeLists.txt.un~ differ
diff --git a/Submodules/greedy/.gitignore b/Submodules/greedy/.gitignore
new file mode 100644
index 0000000..d952f82
--- /dev/null
+++ b/Submodules/greedy/.gitignore
@@ -0,0 +1,5 @@
+.*.sw?
+.DS_Store
+CMakeLists.txt.user
+*.autosave
+*~
diff --git a/Submodules/greedy/CMake/FindFFTW.cmake b/Submodules/greedy/CMake/FindFFTW.cmake
new file mode 100644
index 0000000..917d870
--- /dev/null
+++ b/Submodules/greedy/CMake/FindFFTW.cmake
@@ -0,0 +1,68 @@
+## FFTW can be compiled and subsequently linked against
+## various data types.
+## There is a single set of include files, and then muttiple libraries,
+## One for each type. I.e. libfftw.a-->double, libfftwf.a-->float
+
+## The following logic belongs in the individual package
+## mark_as_advanced(ITK_USE_FFTWD)
+## option(ITK_USE_FFTWD "Use double precision FFTW if found" ON)
+## mark_as_advanced(ITK_USE_FFTWF)
+## option(ITK_USE_FFTWF "Use single precision FFTW if found" ON)
+
+if(ITK_USE_FFTWD OR ITK_USE_FFTWF)
+
+ set(FFTW_INC_SEARCHPATH
+ /sw/include
+ /usr/include
+ /usr/local/include
+ /usr/include/fftw
+ /usr/local/include/fftw
+ )
+
+ find_path(FFTW_INCLUDE_PATH fftw3.h ${FFTW_INC_SEARCHPATH})
+
+ if(FFTW_INCLUDE_PATH)
+ set(FFTW_INCLUDE ${FFTW_INCLUDE_PATH})
+ endif()
+
+ if(FFTW_INCLUDE)
+ include_directories( ${FFTW_INCLUDE})
+ endif()
+
+ get_filename_component(FFTW_INSTALL_BASE_PATH ${FFTW_INCLUDE_PATH} PATH)
+
+ set(FFTW_LIB_SEARCHPATH
+ ${FFTW_INSTALL_BASE_PATH}/lib
+ /usr/lib/fftw
+ /usr/local/lib/fftw
+ )
+
+ if(ITK_USE_FFTWD)
+ mark_as_advanced(FFTWD_LIB)
+ find_library(FFTWD_LIB fftw3 ${FFTW_LIB_SEARCHPATH}) #Double Precision Lib
+ find_library(FFTWD_THREADS_LIB fftw3_threads ${FFTW_LIB_SEARCHPATH}) #Double Precision Lib only if compiled with threads support
+
+ if(FFTWD_LIB)
+ set(FFTWD_FOUND 1)
+ get_filename_component(FFTW_LIBDIR ${FFTWD_LIB} PATH)
+ if(FFTWD_THREADS_LIB)
+ set(FFTWD_LIB ${FFTWD_LIB} ${FFTWD_THREADS_LIB} )
+ endif()
+ endif()
+ endif()
+
+ if(ITK_USE_FFTWF)
+ mark_as_advanced(FFTWF_LIB)
+ find_library(FFTWF_LIB fftw3f ${FFTW_LIB_SEARCHPATH}) #Single Precision Lib
+ find_library(FFTWF_THREADS_LIB fftw3f_threads ${FFTW_LIB_SEARCHPATH}) #Single Precision Lib only if compiled with threads support
+
+ if(FFTWF_LIB)
+ set(FFTWF_FOUND 1)
+ get_filename_component(FFTW_LIBDIR ${FFTWF_LIB} PATH)
+ if(FFTWF_THREADS_LIB)
+ set(FFTWF_LIB ${FFTWF_LIB} ${FFTWF_THREADS_LIB} )
+ endif()
+ endif()
+ endif()
+
+endif()
diff --git a/Submodules/greedy/CMake/Package.cmake b/Submodules/greedy/CMake/Package.cmake
new file mode 100644
index 0000000..136168c
--- /dev/null
+++ b/Submodules/greedy/CMake/Package.cmake
@@ -0,0 +1,123 @@
+# ----------------------------------------------------------------
+# INSTALLATION AND PACKAGING with CPack
+# ----------------------------------------------------------------
+
+# On Win32, we must include the redistributable
+IF(MSVC_VERSION GREATER 1399)
+ FIND_PROGRAM(VCREDIST_X86 vcredist_x86.exe)
+ IF(VCREDIST_X86)
+ INSTALL(FILES ${VCREDIST_X86} DESTINATION bin)
+ SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
+ "ExecWait '\\\"$INSTDIR\\\\bin\\\\vcredist_x86.exe\\\" /passive'")
+ ENDIF(VCREDIST_X86)
+ENDIF(MSVC_VERSION GREATER 1399)
+
+# Allow package generation
+SET(CPACK_PACKAGE_NAME "greedy")
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Greedy medical image registration tool")
+SET(CPACK_PACKAGE_VENDOR "itksnap.org")
+SET(CPACK_PACKAGE_VERSION_MAJOR "${GREEDY_VERSION_MAJOR}")
+SET(CPACK_PACKAGE_VERSION_MINOR "${GREEDY_VERSION_MINOR}")
+SET(CPACK_PACKAGE_VERSION_PATCH "${GREEDY_VERSION_PATCH}")
+SET(CPACK_NSIS_MODIFY_PATH ON)
+
+
+# Shamelessly stolen from ParaView_
+SET(CPACK_SOURCE_PACKAGE_FILE_NAME "greedy-${GREEDY_VERSION_FULL}")
+IF (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown")
+ EXEC_PROGRAM(uname ARGS "-m" OUTPUT_VARIABLE CMAKE_SYSTEM_PROCESSOR)
+ENDIF (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown")
+IF(NOT DEFINED CPACK_SYSTEM_NAME)
+ SET(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR})
+ENDIF(NOT DEFINED CPACK_SYSTEM_NAME)
+IF(${CPACK_SYSTEM_NAME} MATCHES Windows)
+ IF(CMAKE_CL_64)
+ SET(CPACK_SYSTEM_NAME win64-${CMAKE_SYSTEM_PROCESSOR})
+ ELSE(CMAKE_CL_64)
+ SET(CPACK_SYSTEM_NAME win32-${CMAKE_SYSTEM_PROCESSOR})
+ ENDIF(CMAKE_CL_64)
+ENDIF(${CPACK_SYSTEM_NAME} MATCHES Windows)
+
+# For Apple, we need to base the filename on the architecture
+IF(CMAKE_SYSTEM_NAME MATCHES Darwin)
+ IF(NOT DEFINED CMAKE_OSX_ARCHITECTURES)
+ MESSAGE(ERROR "CMAKE_OSX_ARCHITECTURES must be defined")
+ ENDIF(NOT DEFINED CMAKE_OSX_ARCHITECTURES)
+ STRING(REPLACE ";" "-" ARCH "${CMAKE_OSX_ARCHITECTURES}")
+ SET(CPACK_SYSTEM_NAME "MacOS-${ARCH}")
+ MESSAGE(STATUS " ARCH ${ARCH}")
+ MESSAGE(STATUS " CMAKE_OSX_ARCHITECTURES ${CMAKE_OSX_ARCHITECTURES}")
+ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin)
+
+MESSAGE(STATUS " CPACK_SYSTEM_NAME ${CPACK_SYSTEM_NAME}")
+
+SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}")
+
+MESSAGE(STATUS " CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}")
+
+# Show GPL license
+SET(CPACK_RESOURCE_FILE_LICENSE "${GREEDY_TOOL_SOURCE_DIR}/COPYING.txt")
+
+IF(WIN32 AND NOT UNIX)
+
+ SET(CPACK_GENERATOR "NSIS")
+ SET(CPACK_EXTENSION "exe")
+
+ELSE(WIN32 AND NOT UNIX)
+
+ # Set the generator to either STGZ or Apple
+ IF(NOT APPLE)
+ SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "pauly2 at mail.med.upenn.edu")
+ SET(CPACK_GENERATOR "TGZ")
+ SET(CPACK_EXTENSION "tar.gz")
+ ELSE(NOT APPLE)
+ SET(CPACK_GENERATOR "DragNDrop")
+ SET(CPACK_EXTENSION "dmg")
+ ENDIF(NOT APPLE)
+
+ENDIF(WIN32 AND NOT UNIX)
+
+#--------------------------------------------------------------------------------
+# Construct the name of the package
+SET(CPACK_PACKAGE_FILE_NAME_WEXT "${CPACK_PACKAGE_FILE_NAME}.${CPACK_EXTENSION}")
+
+
+INCLUDE(CPack)
+
+#--------------------------------------------------------------------------------
+# Uploading code to SourceForge
+#--------------------------------------------------------------------------------
+
+#--------------------------------------------------------------------------------
+# Configure SCP
+
+FIND_PROGRAM(SCP_PROGRAM NAMES scp DOC "Location of the scp program (optional)")
+MARK_AS_ADVANCED(SCP_PROGRAM)
+
+SET(SCP_ARGUMENTS "-v" CACHE STRING "Optional arguments to the scp command for uploads to SourceForge")
+MARK_AS_ADVANCED(SCP_ARGUMENTS)
+
+SET(SCP_USERNAME "" CACHE STRING "SourceForge.net account id for uploads")
+MARK_AS_ADVANCED(SCP_USERNAME)
+
+SET(NIGHTLY_TARGET "greedy-nightly-${CPACK_SYSTEM_NAME}.${CPACK_EXTENSION}")
+
+SET(SCP_ROOT "github.com/pyushkevich/greedy")
+
+#--------------------------------------------------------------------------------
+# Create targets
+
+ADD_CUSTOM_TARGET(upload_nightly
+ VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS}
+ ${CPACK_PACKAGE_FILE_NAME_WEXT} ${SCP_USERNAME},c3d@${SCP_ROOT}/Nightly/${NIGHTLY_TARGET}
+ DEPENDS ${CPACK_TARGET}
+ WORKING_DIRECTORY ${SNAP_BINARY_DIR}
+ COMMENT "Uploading package ${CPACK_PACKAGE_FILE_NAME_WEXT} to SourceForge.net as ${NIGHTLY_TARGET}")
+
+ADD_CUSTOM_TARGET(upload_experimental
+ VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS} ${CPACK_PACKAGE_FILE_NAME_WEXT} ${SCP_USERNAME},c3d@${SCP_ROOT}/Experimental
+ DEPENDS ${CPACK_TARGET}
+ WORKING_DIRECTORY ${SNAP_BINARY_DIR}
+ COMMENT "Uploading package ${CPACK_PACKAGE_FILE_NAME_WEXT} to SourceForge.net to Experimental directory")
+
+
diff --git a/Submodules/greedy/CMakeLists.txt b/Submodules/greedy/CMakeLists.txt
new file mode 100644
index 0000000..d353033
--- /dev/null
+++ b/Submodules/greedy/CMakeLists.txt
@@ -0,0 +1,88 @@
+PROJECT(GREEDY_TOOL)
+
+# If we are building as a sub-project we skip the extra steps
+IF(NOT GREEDY_TOOL_BUILD_AS_SUBPROJECT)
+
+ # Stand-alone build stuff
+ CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+ # ITK
+ FIND_PACKAGE(ITK 4.8.2 REQUIRED)
+ INCLUDE(${ITK_USE_FILE})
+
+ # Advanced option to link with FFT
+ OPTION(USE_FFTW "Use features provided by the FFTW library, only for experimental LDDMM code" OFF)
+
+ # Deal with FFTW - only used by experimental LDDMM code
+ IF(USE_FFTW)
+ SET(ITK_USE_FFTWF ON)
+ INCLUDE(CMake/FindFFTW.cmake)
+ ADD_DEFINITIONS(-D_LDDMM_FFT_)
+ ENDIF(USE_FFTW)
+
+ENDIF(NOT GREEDY_TOOL_BUILD_AS_SUBPROJECT)
+
+# Include the header directories
+INCLUDE_DIRECTORIES(${GREEDY_TOOL_SOURCE_DIR}/src)
+
+# Define header files
+SET(HEADERS
+ src/FastLinearInterpolator.h
+ src/FastWarpCompositeImageFilter.h
+ src/FastWarpCompositeImageFilter.txx
+ src/LinearTransformToWarpFilter.h
+ src/LinearTransformToWarpFilter.txx
+ src/MultiImageAffineMSDMetricFilter.h
+ src/MultiImageAffineMSDMetricFilter.txx
+ src/MultiComponentImageMetricBase.h
+ src/MultiComponentImageMetricBase.txx
+ src/MultiImageOpticalFlowImageFilter.h
+ src/MultiImageOpticalFlowImageFilter.txx
+ src/MultiImageRegistrationHelper.h
+ src/MultiImageRegistrationHelper.txx
+ src/MultiComponentMutualInfoImageMetric.h
+ src/MultiComponentMutualInfoImageMetric.txx
+ src/MultiComponentNCCImageMetric.h
+ src/MultiComponentNCCImageMetric.txx
+ src/OneDimensionalInPlaceAccumulateFilter.h
+ src/OneDimensionalInPlaceAccumulateFilter.txx
+ src/SimpleWarpImageFilter.h
+ src/SimpleWarpImageFilter.txx
+ src/itkGaussianInterpolateImageFunction.h
+ src/itkOptVectorLinearInterpolateImageFunction.h
+ src/itkOptVectorLinearInterpolateImageFunction.txx
+ src/lddmm_common.h
+ src/lddmm_data.h
+ src/GreedyAPI.h
+ src/GreedyException.h
+ src/GreedyParameters.h
+ src/CommandLineHelper.h
+)
+
+# Define greedy library files
+SET(GREEDY_LIB_SRC
+ src/lddmm_data.cxx
+ src/GreedyAPI.cxx
+ src/GreedyParameters.cxx
+)
+
+SET(LDDMM_SRC src/lddmm_main.cxx)
+SET(GREEDY_SRC src/greedy_main.cxx)
+
+ADD_LIBRARY(greedyapi ${GREEDY_LIB_SRC} ${HEADERS})
+
+# The executables are only compiled when the software is built as its own project
+IF(NOT GREEDY_TOOL_BUILD_AS_SUBPROJECT)
+
+ ADD_EXECUTABLE(lddmm ${LDDMM_SRC})
+ TARGET_LINK_LIBRARIES(lddmm greedyapi
+ ${ITK_LIBRARIES} ${FFTWF_LIB}
+ ${FFTWF_THREADS_LIB})
+
+ ADD_EXECUTABLE(greedy ${GREEDY_SRC})
+ TARGET_LINK_LIBRARIES(greedy greedyapi ${ITK_LIBRARIES})
+
+ ADD_EXECUTABLE(test_accum testing/src/TestOneDimensionalInPlaceAccumulateFilter.cxx)
+ TARGET_LINK_LIBRARIES(test_accum ${ITK_LIBRARIES})
+
+ENDIF(NOT GREEDY_TOOL_BUILD_AS_SUBPROJECT)
diff --git a/Submodules/greedy/src/.SimpleWarpImageFilter.h.un~ b/Submodules/greedy/src/.SimpleWarpImageFilter.h.un~
new file mode 100644
index 0000000..151f1f8
Binary files /dev/null and b/Submodules/greedy/src/.SimpleWarpImageFilter.h.un~ differ
diff --git a/Submodules/greedy/src/.SimpleWarpImageFilter.txx.un~ b/Submodules/greedy/src/.SimpleWarpImageFilter.txx.un~
new file mode 100644
index 0000000..da15acc
Binary files /dev/null and b/Submodules/greedy/src/.SimpleWarpImageFilter.txx.un~ differ
diff --git a/Submodules/greedy/src/.greedy_main.cxx.un~ b/Submodules/greedy/src/.greedy_main.cxx.un~
new file mode 100644
index 0000000..fccdadb
Binary files /dev/null and b/Submodules/greedy/src/.greedy_main.cxx.un~ differ
diff --git a/Submodules/greedy/src/.lddmm_data.cxx.un~ b/Submodules/greedy/src/.lddmm_data.cxx.un~
new file mode 100644
index 0000000..21a231a
Binary files /dev/null and b/Submodules/greedy/src/.lddmm_data.cxx.un~ differ
diff --git a/Submodules/greedy/src/.lddmm_data.h.un~ b/Submodules/greedy/src/.lddmm_data.h.un~
new file mode 100644
index 0000000..0430656
Binary files /dev/null and b/Submodules/greedy/src/.lddmm_data.h.un~ differ
diff --git a/Submodules/greedy/src/CommandLineHelper.h b/Submodules/greedy/src/CommandLineHelper.h
new file mode 100644
index 0000000..e4920fc
--- /dev/null
+++ b/Submodules/greedy/src/CommandLineHelper.h
@@ -0,0 +1,316 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef COMMANDLINEHELPER_H
+#define COMMANDLINEHELPER_H
+
+#include <cerrno>
+#include "GreedyException.h"
+#include "GreedyParameters.h"
+#include "GreedyAPI.h"
+#include "itksys/SystemTools.hxx"
+
+/**
+ * @brief A class to facilitate processing of command-line arguments
+ */
+class CommandLineHelper
+{
+public:
+ CommandLineHelper(int argc, char *argv[])
+ {
+ this->argc = argc;
+ this->argv = argv;
+ i = 1;
+ }
+
+ bool is_at_end()
+ {
+ return i >= argc;
+ }
+
+ /**
+ * Just read the next arg (used internally)
+ */
+ const char *read_arg()
+ {
+ if(i >= argc)
+ throw GreedyException("Unexpected end of command line arguments.");
+
+ return argv[i++];
+ }
+
+ /**
+ * Read a command (something that starts with a '-')
+ */
+ std::string read_command()
+ {
+ current_command = read_arg();
+ if(current_command[0] != '-')
+ throw GreedyException("Expected a command at position %d, instead got '%s'.", i, current_command.c_str());
+ return current_command;
+ }
+
+ /**
+ * Read a string that is not a command (may not start with a -)
+ */
+ std::string read_string()
+ {
+ std::string arg = read_arg();
+ if(arg[0] == '-')
+ throw GreedyException("Expected a string argument as parameter to '%s', instead got '%s'.", current_command.c_str(), arg.c_str());
+
+ return arg;
+ }
+
+
+ /**
+ * Get the number of free arguments to the current command. Use only for commands with
+ * a priori unknown number of arguments. Otherwise, just use the get_ commands
+ */
+ int command_arg_count(int min_required = 0)
+ {
+ // Count the number of arguments
+ int n_args = 0;
+ for(int j = i; j < argc; j++, n_args++)
+ if(argv[j][0] == '-')
+ break;
+
+ // Test for minimum required
+ if(n_args < min_required)
+ throw GreedyException(
+ "Expected at least %d arguments to '%s', instead got '%d'",
+ min_required, current_command.c_str(), n_args);
+
+ return n_args;
+ }
+
+ /**
+ * Read an existing filename
+ */
+ std::string read_existing_filename()
+ {
+ std::string file = read_arg();
+ if(!itksys::SystemTools::FileExists(file.c_str()))
+ throw GreedyException("File '%s' does not exist", file.c_str());
+
+ return file;
+ }
+
+ /**
+ * Read a transform specification, format file,number
+ */
+ TransformSpec read_transform_spec()
+ {
+ std::string spec = read_arg();
+ size_t pos = spec.find_first_of(',');
+
+ TransformSpec ts;
+ ts.filename = spec.substr(0, pos);
+ ts.exponent = 1.0;
+
+ if(!itksys::SystemTools::FileExists(ts.filename.c_str()))
+ throw GreedyException("File '%s' does not exist", ts.filename.c_str());
+
+ if(pos != std::string::npos)
+ {
+ errno = 0; char *pend;
+ std::string expstr = spec.substr(pos+1);
+ ts.exponent = std::strtod(expstr.c_str(), &pend);
+
+ if(errno || *pend)
+ throw GreedyException("Expected a floating point number after comma in transform specification '%s', instead got '%s'",
+ current_command.c_str(), spec.substr(pos).c_str());
+
+ }
+
+ return ts;
+ }
+
+ /**
+ * Read an output filename
+ */
+ std::string read_output_filename()
+ {
+ std::string file = read_arg();
+ return file;
+ }
+
+ /**
+ * Read a floating point value
+ */
+ double read_double()
+ {
+ std::string arg = read_arg();
+
+ errno = 0; char *pend;
+ double val = std::strtod(arg.c_str(), &pend);
+
+ if(errno || *pend)
+ throw GreedyException("Expected a floating point number as parameter to '%s', instead got '%s'",
+ current_command.c_str(), arg.c_str());
+
+ return val;
+ }
+
+ /**
+ * Check if a string ends with another string and return the
+ * substring without the suffix
+ */
+ bool check_suffix(const std::string &source, const std::string &suffix, std::string &out_prefix)
+ {
+ int n = source.length(), m = suffix.length();
+ if(n < m)
+ return false;
+
+ if(source.substr(n-m, m) != suffix)
+ return false;
+
+ out_prefix = source.substr(0, n-m);
+ return true;
+ }
+
+ /**
+ * Read a floating point value with units (mm or vox)
+ */
+ double read_scalar_with_units(bool &physical_units)
+ {
+ std::string arg = read_arg();
+ std::string scalar;
+
+ if(check_suffix(arg, "vox", scalar))
+ physical_units = false;
+ else if(check_suffix(arg, "mm", scalar))
+ physical_units = true;
+ else
+ throw GreedyException("Parameter to '%s' should include units, e.g. '3vox' or '3mm', instead got '%s'",
+ current_command.c_str(), arg.c_str());
+
+ errno = 0; char *pend;
+ double val = std::strtod(scalar.c_str(), &pend);
+
+ if(errno || *pend)
+ throw GreedyException("Expected a floating point number as parameter to '%s', instead got '%s'",
+ current_command.c_str(), scalar.c_str());
+
+ return val;
+ }
+
+ /**
+ * Read an integer value
+ */
+ long read_integer()
+ {
+ std::string arg = read_arg();
+
+ errno = 0; char *pend;
+ long val = std::strtol(arg.c_str(), &pend, 10);
+
+ if(errno || *pend)
+ throw GreedyException("Expected an integer as parameter to '%s', instead got '%s'",
+ current_command.c_str(), arg.c_str());
+
+ return val;
+ }
+
+ /**
+ * Read one of a list of strings. The optional parameters to this are in the form
+ * int, string, int, string, int, string. Each string may in turn contain a list
+ * of words (separated by space) that are acceptable. So for example. NULL string
+ * is used to refer to the default option.
+ *
+ * enum Mode { NORMAL, BAD, SILLY }
+ * Mode m = X.read_option(NORMAL, "NORMAL normal", BAD, "bad BAD", SILLY, NULL);
+ */
+ /*
+ template <class TOption>
+ TOption read_option(TOption opt1, const char *str1, ...)
+ {
+ not implemented yet
+ }
+ */
+
+ /**
+ * Read a vector in the format 1.0x0.2x0.6
+ */
+ std::vector<double> read_double_vector()
+ {
+ std::string arg = read_arg();
+ std::istringstream f(arg);
+ std::string s;
+ std::vector<double> vector;
+ while (getline(f, s, 'x'))
+ {
+ errno = 0; char *pend;
+ double val = std::strtod(s.c_str(), &pend);
+
+ if(errno || *pend)
+ throw GreedyException("Expected a floating point vector as parameter to '%s', instead got '%s'",
+ current_command.c_str(), arg.c_str());
+ vector.push_back(val);
+ }
+
+ if(!vector.size())
+ throw GreedyException("Expected a floating point vector as parameter to '%s', instead got '%s'",
+ current_command.c_str(), arg.c_str());
+
+ return vector;
+ }
+
+ std::vector<int> read_int_vector()
+ {
+ std::string arg = read_arg();
+ std::istringstream f(arg);
+ std::string s;
+ std::vector<int> vector;
+ while (getline(f, s, 'x'))
+ {
+ errno = 0; char *pend;
+ long val = std::strtol(s.c_str(), &pend, 10);
+
+ if(errno || *pend)
+ throw GreedyException("Expected an integer vector as parameter to '%s', instead got '%s'",
+ current_command.c_str(), arg.c_str());
+ vector.push_back((int) val);
+ }
+
+ if(!vector.size())
+ throw GreedyException("Expected an integer vector as parameter to '%s', instead got '%s'",
+ current_command.c_str(), arg.c_str());
+
+ return vector;
+ }
+
+
+
+
+
+private:
+ int argc, i;
+ char **argv;
+ std::string current_command;
+};
+
+#endif // COMMANDLINEHELPER_H
diff --git a/Submodules/greedy/src/FastLinearInterpolator.h b/Submodules/greedy/src/FastLinearInterpolator.h
new file mode 100644
index 0000000..7bb4240
--- /dev/null
+++ b/Submodules/greedy/src/FastLinearInterpolator.h
@@ -0,0 +1,893 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __FastLinearInterpolator_h_
+#define __FastLinearInterpolator_h_
+
+#include "itkVectorImage.h"
+#include "itkNumericTraits.h"
+
+template <class TFloat, class TInputComponentType>
+struct FastLinearInterpolatorOutputTraits
+{
+};
+
+template <class TInputComponentType>
+struct FastLinearInterpolatorOutputTraits<float, TInputComponentType>
+{
+ typedef typename itk::NumericTraits<TInputComponentType>::FloatType OutputComponentType;
+};
+
+template <class TInputComponentType>
+struct FastLinearInterpolatorOutputTraits<double, TInputComponentType>
+{
+ typedef typename itk::NumericTraits<TInputComponentType>::RealType OutputComponentType;
+};
+
+template <class TImageType>
+struct FastWarpCompositeImageFilterInputImageTraits
+{
+};
+
+template <class TPixel, unsigned int VDim>
+struct FastWarpCompositeImageFilterInputImageTraits< itk::Image<TPixel, VDim> >
+{
+ static int GetPointerIncrementSize(const itk::Image<TPixel, VDim> *) { return 1; }
+};
+
+template <class TPixel, unsigned int VDim>
+struct FastWarpCompositeImageFilterInputImageTraits< itk::VectorImage<TPixel, VDim> >
+{
+ static int GetPointerIncrementSize(const itk::VectorImage<TPixel, VDim> *image)
+ {
+ return image->GetNumberOfComponentsPerPixel();
+ }
+};
+
+
+/**
+ * Base class for the fast linear interpolators
+ */
+template<class TImage, class TFloat, unsigned int VDim>
+class FastLinearInterpolatorBase
+{
+public:
+ typedef TImage ImageType;
+ typedef TFloat RealType;
+ typedef typename ImageType::InternalPixelType InputComponentType;
+ typedef FastLinearInterpolatorOutputTraits<TFloat, InputComponentType> OutputTraits;
+ typedef typename OutputTraits::OutputComponentType OutputComponentType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, ImageType::ImageDimension );
+
+ enum InOut { INSIDE, OUTSIDE, BORDER };
+
+ /**
+ * Get the number that should be added to the input pointer when parsing the input and
+ * output images. This will be 1 for itk::Image and Ncomp for itk::VectorImage
+ */
+ int GetPointerIncrement() const { return nComp; }
+
+ FastLinearInterpolatorBase(ImageType *image)
+ {
+ buffer = image->GetBufferPointer();
+ nComp = FastWarpCompositeImageFilterInputImageTraits<TImage>::GetPointerIncrementSize(image);
+ def_value_store = new InputComponentType[nComp];
+ for(int i = 0; i < nComp; i++)
+ def_value_store[i] = itk::NumericTraits<InputComponentType>::Zero;
+ def_value = def_value_store;
+ }
+
+ ~FastLinearInterpolatorBase()
+ {
+ delete [] def_value_store;
+ }
+
+protected:
+
+
+ int nComp;
+ const InputComponentType *buffer;
+
+ // Default value - for interpolation outside of the image bounds
+ const InputComponentType *def_value;
+ InputComponentType *def_value_store;
+
+ InOut status;
+
+
+ template <class TInput>
+ inline OutputComponentType lerp(RealType a, const TInput &l, const TInput &h)
+ {
+ return l+((h-l)*a);
+ }
+};
+
+
+/**
+ * Arbitrary dimension fast linear interpolator - meant to be slow
+ */
+template<class TImage, class TFloat, unsigned int VDim>
+class FastLinearInterpolator : public FastLinearInterpolatorBase<TImage, TFloat, VDim>
+{
+public:
+ typedef FastLinearInterpolatorBase<TImage, TFloat, VDim> Superclass;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::OutputComponentType OutputComponentType;
+ typedef typename Superclass::RealType RealType;
+ typedef typename Superclass::InOut InOut;
+
+ FastLinearInterpolator(ImageType *image) : Superclass(image) {}
+
+ InOut InterpolateWithGradient(RealType *cix, OutputComponentType *out, OutputComponentType **grad)
+ { return Superclass::INSIDE; }
+
+ InOut Interpolate(RealType *cix, OutputComponentType *out)
+ { return Superclass::INSIDE; }
+
+ InOut InterpolateNearestNeighbor(RealType *cix, OutputComponentType *out)
+ { return Superclass::INSIDE; }
+
+ TFloat GetMask() { return 0.0; }
+
+ TFloat GetMaskAndGradient(RealType *mask_gradient) { return 0.0; }
+
+ void Splat(RealType *cix, const InputComponentType *value) {}
+
+ template <class THistContainer>
+ void PartialVolumeHistogramSample(RealType *cix, const InputComponentType *fixptr, THistContainer &hist) {}
+
+ template <class THistContainer>
+ void PartialVolumeHistogramGradientSample(RealType *cix, const InputComponentType *fix_ptr, const THistContainer &hist_w, RealType *out_grad) {}
+
+
+protected:
+};
+
+/**
+ * 3D fast linear interpolator - optimized for speed
+ */
+template <class TImage, class TFloat>
+class FastLinearInterpolator<TImage, TFloat, 3>
+ : public FastLinearInterpolatorBase<TImage, TFloat, 3>
+{
+public:
+ typedef TImage ImageType;
+ typedef FastLinearInterpolatorBase<ImageType, TFloat, 3> Superclass;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::OutputComponentType OutputComponentType;
+ typedef typename Superclass::RealType RealType;
+ typedef typename Superclass::InOut InOut;
+
+ FastLinearInterpolator(ImageType *image) : Superclass(image)
+ {
+ xsize = image->GetLargestPossibleRegion().GetSize()[0];
+ ysize = image->GetLargestPossibleRegion().GetSize()[1];
+ zsize = image->GetLargestPossibleRegion().GetSize()[2];
+ }
+
+ /**
+ * Compute the pointers to the eight corners of the interpolating cube
+ */
+ InOut ComputeCorners(RealType *cix)
+ {
+ const InputComponentType *dp;
+
+ x0 = (int) floor(cix[0]); fx = cix[0] - x0;
+ y0 = (int) floor(cix[1]); fy = cix[1] - y0;
+ z0 = (int) floor(cix[2]); fz = cix[2] - z0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+ z1 = z0 + 1;
+
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize &&
+ z0 >= 0 && z1 < zsize)
+ {
+ // The sample point is completely inside
+ dp = dens(x0, y0, z0);
+ d000 = dp;
+ d100 = dp+this->nComp;
+ dp += xsize*this->nComp;
+ d010 = dp;
+ d110 = dp+this->nComp;
+ dp += xsize*ysize*this->nComp;
+ d011 = dp;
+ d111 = dp+this->nComp;
+ dp -= xsize*this->nComp;
+ d001 = dp;
+ d101 = dp+this->nComp;
+
+ // The mask is one
+ this->status = Superclass::INSIDE;
+ }
+ else if (x0 >= -1 && x1 <= xsize &&
+ y0 >= -1 && y1 <= ysize &&
+ z0 >= -1 && z1 <= zsize)
+ {
+ // The sample point is on the border region
+ d000 = border_check(x0, y0, z0, m000);
+ d001 = border_check(x0, y0, z1, m001);
+ d010 = border_check(x0, y1, z0, m010);
+ d011 = border_check(x0, y1, z1, m011);
+ d100 = border_check(x1, y0, z0, m100);
+ d101 = border_check(x1, y0, z1, m101);
+ d110 = border_check(x1, y1, z0, m110);
+ d111 = border_check(x1, y1, z1, m111);
+
+ // The mask is between 0 and 1
+ this->status = Superclass::BORDER;
+ }
+ else
+ {
+ // The mask is zero
+ this->status = Superclass::OUTSIDE;
+ }
+
+ return this->status;
+ }
+
+ /**
+ * Interpolate at position cix, placing the intensity values in out and gradient
+ * values in grad (in strides of VDim)
+ */
+ InOut InterpolateWithGradient(RealType *cix, OutputComponentType *out, OutputComponentType **grad)
+ {
+ RealType dx00, dx01, dx10, dx11, dxy0, dxy1;
+ RealType dx00_x, dx01_x, dx10_x, dx11_x, dxy0_x, dxy1_x;
+ RealType dxy0_y, dxy1_y;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++, grad++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++)
+ {
+ // Interpolate the image intensity
+ dx00 = Superclass::lerp(fx, *d000, *d100);
+ dx01 = Superclass::lerp(fx, *d001, *d101);
+ dx10 = Superclass::lerp(fx, *d010, *d110);
+ dx11 = Superclass::lerp(fx, *d011, *d111);
+ dxy0 = Superclass::lerp(fy, dx00, dx10);
+ dxy1 = Superclass::lerp(fy, dx01, dx11);
+ *(out++) = Superclass::lerp(fz, dxy0, dxy1);
+
+ // Interpolate the gradient in x
+ dx00_x = *d100 - *d000;
+ dx01_x = *d101 - *d001;
+ dx10_x = *d110 - *d010;
+ dx11_x = *d111 - *d011;
+ dxy0_x = this->lerp(fy, dx00_x, dx10_x);
+ dxy1_x = this->lerp(fy, dx01_x, dx11_x);
+ (*grad)[0] = this->lerp(fz, dxy0_x, dxy1_x);
+
+ // Interpolate the gradient in y
+ dxy0_y = dx10 - dx00;
+ dxy1_y = dx11 - dx01;
+ (*grad)[1] = this->lerp(fz, dxy0_y, dxy1_y);
+
+ // Interpolate the gradient in z
+ (*grad)[2] = dxy1 - dxy0;
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut Interpolate(RealType *cix, OutputComponentType *out)
+ {
+ OutputComponentType dx00, dx01, dx10, dx11, dxy0, dxy1;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++)
+ {
+ // Interpolate the image intensity
+ dx00 = Superclass::lerp(fx, *d000, *d100);
+ dx01 = Superclass::lerp(fx, *d001, *d101);
+ dx10 = Superclass::lerp(fx, *d010, *d110);
+ dx11 = Superclass::lerp(fx, *d011, *d111);
+ dxy0 = Superclass::lerp(fy, dx00, dx10);
+ dxy1 = Superclass::lerp(fy, dx01, dx11);
+ *(out++) = Superclass::lerp(fz, dxy0, dxy1);
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut InterpolateNearestNeighbor(RealType *cix, OutputComponentType *out)
+ {
+ x0 = (int) floor(cix[0] + 0.5);
+ y0 = (int) floor(cix[1] + 0.5);
+ z0 = (int) floor(cix[2] + 0.5);
+
+ if (x0 >= 0 && x0 < xsize &&
+ y0 >= 0 && y0 < ysize &&
+ z0 >= 0 && z0 < zsize)
+ {
+ const InputComponentType *dp = dens(x0, y0, z0);
+ for(int iComp = 0; iComp < this->nComp; iComp++)
+ {
+ out[iComp] = dp[iComp];
+ }
+ return Superclass::INSIDE;
+ }
+ else return Superclass::OUTSIDE;
+ }
+
+ void Splat(RealType *cix, const InputComponentType *value)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ // When inside, no checks are required
+ if(this->status == Superclass::INSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy, fyz = fy * fz, fxz = fx * fz, fxyz = fxy * fz;
+
+ RealType w111 = fxyz;
+ RealType w011 = fyz - fxyz;
+ RealType w101 = fxz - fxyz;
+ RealType w110 = fxy - fxyz;
+ RealType w001 = fz - fxz - w011;
+ RealType w010 = fy - fyz - w110;
+ RealType w100 = fx - fxy - w101;
+ RealType w000 = 1.0 - fx - fy + fxy - w001;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++, value++)
+ {
+ // Assign the appropriate weight to each part of the histogram
+ InputComponentType val = *value;
+ *const_cast<InputComponentType *>(d000) += w000 * val;
+ *const_cast<InputComponentType *>(d001) += w001 * val;
+ *const_cast<InputComponentType *>(d010) += w010 * val;
+ *const_cast<InputComponentType *>(d011) += w011 * val;
+ *const_cast<InputComponentType *>(d100) += w100 * val;
+ *const_cast<InputComponentType *>(d101) += w101 * val;
+ *const_cast<InputComponentType *>(d110) += w110 * val;
+ *const_cast<InputComponentType *>(d111) += w111 * val;
+ }
+ }
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramSample(RealType *cix, const InputComponentType *fixptr, THistContainer &hist)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy, fyz = fy * fz, fxz = fx * fz, fxyz = fxy * fz;
+
+ RealType w111 = fxyz;
+ RealType w011 = fyz - fxyz;
+ RealType w101 = fxz - fxyz;
+ RealType w110 = fxy - fxyz;
+ RealType w001 = fz - fxz - w011;
+ RealType w010 = fy - fyz - w110;
+ RealType w100 = fx - fxy - w101;
+ RealType w000 = 1.0 - fx - fy + fxy - w001;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+
+ // Assign the appropriate weight to each part of the histogram
+ hist_line[*d000] += w000;
+ hist_line[*d001] += w001;
+ hist_line[*d010] += w010;
+ hist_line[*d011] += w011;
+ hist_line[*d100] += w100;
+ hist_line[*d101] += w101;
+ hist_line[*d110] += w110;
+ hist_line[*d111] += w111;
+ }
+ }
+ else
+ {
+ for(int iComp = 0; iComp < this->nComp; iComp++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+ hist_line[0] += 1.0;
+ }
+ }
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramGradientSample(RealType *cix, const InputComponentType *fixptr, const THistContainer &hist_w, RealType *out_grad)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ // Outside values do not contribute to the gradient
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy, fyz = fy * fz, fxz = fx * fz;
+
+ // Some horrendous derivatives here! Wow!
+ RealType w111x = fyz, w111y = fxz, w111z = fxy;
+ RealType w011x = -fyz, w011y = fz - fxz, w011z = fy - fxy;
+ RealType w101x = fz - fyz, w101y = -fxz, w101z = fx - fxy;
+ RealType w110x = fy - fyz, w110y = fx - fxz, w110z = -fxy;
+ RealType w001x = -fz - w011x, w001y = -w011y, w001z = 1 - fx - w011z;
+ RealType w010x = -w110x, w010y = 1 - fz - w110y, w010z = -fy - w110z;
+ RealType w100x = 1 - fy - w101x, w100y = -fx - w101y, w100z = -w101z;
+ RealType w000x = -1 + fy - w001x, w000y = -1 + fx - w001y, w000z = -w001z;
+
+ // Initialize gradient to zero
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+ out_grad[2] = 0.0;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d000++, d001++, d010++, d011++,
+ d100++, d101++, d110++, d111++, fixptr++)
+ {
+ // Just this line in the histogram
+ const RealType *f = hist_w[iComp][*fixptr];
+
+ // Take the weighted sum
+ RealType f000 = f[*d000], f001 = f[*d001], f010 = f[*d010], f011 = f[*d011];
+ RealType f100 = f[*d100], f101 = f[*d101], f110 = f[*d110], f111 = f[*d111];
+
+ out_grad[0] += w000x * f000 + w001x * f001 + w010x * f010 + w011x * f011 +
+ w100x * f100 + w101x * f101 + w110x * f110 + w111x * f111;
+
+ out_grad[1] += w000y * f000 + w001y * f001 + w010y * f010 + w011y * f011 +
+ w100y * f100 + w101y * f101 + w110y * f110 + w111y * f111;
+
+ out_grad[2] += w000z * f000 + w001z * f001 + w010z * f010 + w011z * f011 +
+ w100z * f100 + w101z * f101 + w110z * f110 + w111z * f111;
+ }
+ }
+ else
+ {
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+ out_grad[2] = 0.0;
+ }
+ }
+
+ RealType GetMask()
+ {
+ // Interpolate the mask
+ RealType dx00, dx01, dx10, dx11, dxy0, dxy1;
+ dx00 = this->lerp(fx, m000, m100);
+ dx01 = this->lerp(fx, m001, m101);
+ dx10 = this->lerp(fx, m010, m110);
+ dx11 = this->lerp(fx, m011, m111);
+ dxy0 = this->lerp(fy, dx00, dx10);
+ dxy1 = this->lerp(fy, dx01, dx11);
+ return this->lerp(fz, dxy0, dxy1);
+ }
+
+ RealType GetMaskAndGradient(RealType *mask_gradient)
+ {
+ // Interpolate the mask
+ RealType dx00, dx01, dx10, dx11, dxy0, dxy1;
+ dx00 = this->lerp(fx, m000, m100);
+ dx01 = this->lerp(fx, m001, m101);
+ dx10 = this->lerp(fx, m010, m110);
+ dx11 = this->lerp(fx, m011, m111);
+ dxy0 = this->lerp(fy, dx00, dx10);
+ dxy1 = this->lerp(fy, dx01, dx11);
+ RealType mask = this->lerp(fz, dxy0, dxy1);
+
+ // Compute the gradient of the mask
+ RealType dx00_x, dx01_x, dx10_x, dx11_x, dxy0_x, dxy1_x;
+ dx00_x = m100 - m000;
+ dx01_x = m101 - m001;
+ dx10_x = m110 - m010;
+ dx11_x = m111 - m011;
+ dxy0_x = this->lerp(fy, dx00_x, dx10_x);
+ dxy1_x = this->lerp(fy, dx01_x, dx11_x);
+ mask_gradient[0] = this->lerp(fz, dxy0_x, dxy1_x);
+
+ RealType dxy0_y, dxy1_y;
+ dxy0_y = dx10 - dx00;
+ dxy1_y = dx11 - dx01;
+ mask_gradient[1] = this->lerp(fz, dxy0_y, dxy1_y);
+
+ mask_gradient[2] = dxy1 - dxy0;
+
+ return mask;
+ }
+
+protected:
+
+ inline const InputComponentType *border_check(int X, int Y, int Z, RealType &mask)
+ {
+ if(X >= 0 && X < xsize && Y >= 0 && Y < ysize && Z >= 0 && Z < zsize)
+ {
+ mask = 1.0;
+ return dens(X,Y,Z);
+ }
+ else
+ {
+ mask = 0.0;
+ return this->def_value;
+ }
+ }
+
+ inline const InputComponentType *dens(int X, int Y, int Z)
+ {
+ return this->buffer + this->nComp * (X+xsize*(Y+ysize*Z));
+ }
+
+ // Image size
+ int xsize, ysize, zsize;
+
+ // State of current interpolation
+ const InputComponentType *d000, *d001, *d010, *d011, *d100, *d101, *d110, *d111;
+ RealType m000, m001, m010, m011, m100, m101, m110, m111;
+
+ RealType fx, fy, fz;
+ int x0, y0, z0, x1, y1, z1;
+
+};
+
+
+
+/**
+ * 2D fast linear interpolator - optimized for speed
+ */
+template <class TImage, class TFloat>
+class FastLinearInterpolator<TImage, TFloat, 2>
+ : public FastLinearInterpolatorBase<TImage, TFloat, 2>
+{
+public:
+ typedef TImage ImageType;
+ typedef FastLinearInterpolatorBase<ImageType, TFloat, 2> Superclass;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::OutputComponentType OutputComponentType;
+ typedef typename Superclass::RealType RealType;
+ typedef typename Superclass::InOut InOut;
+
+ FastLinearInterpolator(ImageType *image) : Superclass(image)
+ {
+ xsize = image->GetLargestPossibleRegion().GetSize()[0];
+ ysize = image->GetLargestPossibleRegion().GetSize()[1];
+ }
+
+ /**
+ * Compute the pointers to the eight corners of the interpolating cube
+ */
+ InOut ComputeCorners(RealType *cix)
+ {
+ const InputComponentType *dp;
+
+ x0 = (int) floor(cix[0]); fx = cix[0] - x0;
+ y0 = (int) floor(cix[1]); fy = cix[1] - y0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize)
+ {
+ // The sample point is completely inside
+ dp = dens(x0, y0);
+ d00 = dp;
+ d10 = dp+this->nComp;
+ dp += xsize*this->nComp;
+ d01 = dp;
+ d11 = dp+this->nComp;
+
+ // The mask is one
+ this->status = Superclass::INSIDE;
+ }
+ else if (x0 >= -1 && x1 <= xsize &&
+ y0 >= -1 && y1 <= ysize)
+ {
+ // The sample point is on the border region
+ d00 = border_check(x0, y0, m00);
+ d01 = border_check(x0, y1, m01);
+ d10 = border_check(x1, y0, m10);
+ d11 = border_check(x1, y1, m11);
+
+ // The mask is between 0 and 1
+ this->status = Superclass::BORDER;
+ }
+ else
+ {
+ // The mask is zero
+ this->status = Superclass::OUTSIDE;
+ }
+
+ return this->status;
+ }
+
+ /**
+ * Interpolate at position cix, placing the intensity values in out and gradient
+ * values in grad (in strides of VDim)
+ */
+ InOut InterpolateWithGradient(RealType *cix, OutputComponentType *out, OutputComponentType **grad)
+ {
+ RealType dx0, dx1;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++, grad++,
+ d00++, d01++, d10++, d11++)
+ {
+ // Interpolate the image intensity
+ dx0 = Superclass::lerp(fx, *d00, *d10);
+ dx1 = Superclass::lerp(fx, *d01, *d11);
+ *(out++) = Superclass::lerp(fy, dx0, dx1);
+
+ // Interpolate the gradient in x
+ (*grad)[0] = this->lerp(fy, *d10 - *d00, *d11 - *d01);
+
+ // Interpolate the gradient in y
+ (*grad)[1] = dx1 - dx0;
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut Interpolate(RealType *cix, OutputComponentType *out)
+ {
+ OutputComponentType dx0, dx1;
+
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d00++, d01++, d10++, d11++)
+ {
+ // Interpolate the image intensity
+ dx0 = Superclass::lerp(fx, *d00, *d10);
+ dx1 = Superclass::lerp(fx, *d01, *d11);
+ *(out++) = Superclass::lerp(fy, dx0, dx1);
+ }
+ }
+
+ return this->status;
+ }
+
+ InOut InterpolateNearestNeighbor(RealType *cix, OutputComponentType *out)
+ {
+ x0 = (int) floor(cix[0] + 0.5);
+ y0 = (int) floor(cix[1] + 0.5);
+
+ if (x0 >= 0 && x0 < xsize && y0 >= 0 && y0 < ysize)
+ {
+ const InputComponentType *dp = dens(x0, y0);
+ for(int iComp = 0; iComp < this->nComp; iComp++)
+ {
+ out[iComp] = dp[iComp];
+ }
+ return Superclass::INSIDE;
+ }
+ else return Superclass::OUTSIDE;
+ }
+
+ void Splat(RealType *cix, const InputComponentType *value)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ // When inside, no checks are required
+ if(this->status == Superclass::INSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy;
+
+ RealType w11 = fxy;
+ RealType w01 = fy - fxy;
+ RealType w10 = fx - fxy;
+ RealType w00 = 1.0 - fx - fy + fxy;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d00++, d01++, d10++, d11++, value++)
+ {
+ // Assign the appropriate weight to each part of the histogram
+ InputComponentType val = *value;
+
+ *const_cast<InputComponentType *>(d00) += w00 * val;
+ *const_cast<InputComponentType *>(d01) += w01 * val;
+ *const_cast<InputComponentType *>(d10) += w10 * val;
+ *const_cast<InputComponentType *>(d11) += w11 * val;
+ }
+ }
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramSample(RealType *cix, const InputComponentType *fixptr, THistContainer &hist)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Compute the corner weights using 4 multiplications (not 16)
+ RealType fxy = fx * fy;
+
+ RealType w11 = fxy;
+ RealType w01 = fy - fxy;
+ RealType w10 = fx - fxy;
+ RealType w00 = 1.0 - fx - fy + fxy;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d00++, d01++, d10++, d11++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+
+ // Assign the appropriate weight to each part of the histogram
+ hist_line[*d00] += w00;
+ hist_line[*d01] += w01;
+ hist_line[*d10] += w10;
+ hist_line[*d11] += w11;
+ }
+ }
+ else
+ {
+ for(int iComp = 0; iComp < this->nComp; iComp++, fixptr++)
+ {
+ // Just this line in the histogram
+ RealType *hist_line = hist[iComp][*fixptr];
+ hist_line[0] += 1.0;
+ }
+ }
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramGradientSample(RealType *cix, const InputComponentType *fixptr, const THistContainer &hist_w, RealType *out_grad)
+ {
+ // Compute the corners
+ this->ComputeCorners(cix);
+
+ // Outside values do not contribute to the gradient
+ if(this->status != Superclass::OUTSIDE)
+ {
+ // Some horrendous derivatives here! Wow!
+ RealType w11x = fy, w11y = fx;
+ RealType w01x = -fy, w01y = 1 - fx;
+ RealType w10x = 1 - fy, w10y = -fx;
+ RealType w00x = fy - 1, w00y = fx - 1;
+
+ // Initialize gradient to zero
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+
+ // Loop over the components
+ for(int iComp = 0; iComp < this->nComp; iComp++,
+ d00++, d01++, d10++, d11++, fixptr++)
+ {
+ // Just this line in the histogram
+ const RealType *f = hist_w[iComp][*fixptr];
+
+ // Take the weighted sum
+ RealType f00 = f[*d00], f01 = f[*d01];
+ RealType f10 = f[*d10], f11 = f[*d11];
+
+ out_grad[0] += w00x * f00 + w01x * f01 + w10x * f10 + w11x * f11;
+ out_grad[1] += w00y * f00 + w01y * f01 + w10y * f10 + w11y * f11;
+ }
+ }
+ else
+ {
+ out_grad[0] = 0.0;
+ out_grad[1] = 0.0;
+ }
+ }
+
+ RealType GetMask()
+ {
+ // Interpolate the mask
+ RealType dx0, dx1;
+ dx0 = this->lerp(fx, m00, m10);
+ dx1 = this->lerp(fx, m01, m11);
+ return this->lerp(fy, dx0, dx1);
+ }
+
+ RealType GetMaskAndGradient(RealType *mask_gradient)
+ {
+ // Interpolate the mask
+ RealType dx0, dx1;
+ dx0 = this->lerp(fx, m00, m10);
+ dx1 = this->lerp(fx, m01, m11);
+ RealType mask = this->lerp(fy, dx0, dx1);
+
+ // Compute the gradient of the mask
+ mask_gradient[0] = this->lerp(fy, m10 - m00, m11 - m01);
+ mask_gradient[1] = dx1 - dx0;
+
+ return mask;
+ }
+
+protected:
+
+ inline const InputComponentType *border_check(int X, int Y, RealType &mask)
+ {
+ if(X >= 0 && X < xsize && Y >= 0 && Y < ysize)
+ {
+ mask = 1.0;
+ return dens(X,Y);
+ }
+ else
+ {
+ mask = 0.0;
+ return this->def_value;
+ }
+ }
+
+ inline const InputComponentType *dens(int X, int Y)
+ {
+ return this->buffer + this->nComp * (X+xsize*Y);
+ }
+
+ // Image size
+ int xsize, ysize;
+
+ // State of current interpolation
+ const InputComponentType *d00, *d01, *d10, *d11;
+ RealType m00, m01, m10, m11;
+
+ RealType fx, fy;
+ int x0, y0, x1, y1;
+};
+
+
+#endif
diff --git a/Submodules/greedy/src/FastWarpCompositeImageFilter.h b/Submodules/greedy/src/FastWarpCompositeImageFilter.h
new file mode 100644
index 0000000..02c77d4
--- /dev/null
+++ b/Submodules/greedy/src/FastWarpCompositeImageFilter.h
@@ -0,0 +1,142 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef FASTWARPCOMPOSITEIMAGEFILTER_H
+#define FASTWARPCOMPOSITEIMAGEFILTER_H
+
+#include "lddmm_common.h"
+#include "itkImageToImageFilter.h"
+
+/**
+ * This is a warp filter that is fast, supports multi-component (vector) images
+ * and can be performed using either physical space or voxel-space calculations
+ *
+ * This filter supports linear interpolation currently
+ */
+template <class TInputImage, class TOutputImage, class TDeformationField>
+class FastWarpCompositeImageFilter
+ : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ typedef FastWarpCompositeImageFilter<TInputImage,TOutputImage,TDeformationField> Self;
+ typedef itk::ImageToImageFilter<TInputImage, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( FastWarpCompositeImageFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension );
+
+ // Lots of typedefs
+ typedef TInputImage InputImageType;
+ typedef TOutputImage OutputImageType;
+ typedef TDeformationField DeformationFieldType;
+ typedef typename InputImageType::RegionType OutputImageRegionType;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+ typedef typename InputImageType::IndexType IndexType;
+ typedef typename InputImageType::IndexValueType IndexValueType;
+ typedef typename InputImageType::SizeType SizeType;
+ typedef typename InputImageType::SpacingType SpacingType;
+ typedef typename InputImageType::DirectionType DirectionType;
+ typedef typename DeformationFieldType::PixelType DeformationVectorType;
+ typedef typename DeformationVectorType::ValueType DeforamtionScalarType;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ typedef itk::ImageBase<ImageDimension> ImageBaseType;
+
+ /** Set the fixed image */
+ itkNamedInputMacro(DeformationField, DeformationFieldType, "Primary")
+
+ /** Set the moving image */
+ itkNamedInputMacro(MovingImage, InputImageType, "moving")
+
+ /**
+ * Set whether the filter should use physical space calculations, i.e., the
+ * displacement field is between physical coordinates of the voxels in the
+ * fixed image domain and the physical coordinates of the voxels in the
+ * moving image domain. By default, voxel units are used for greater speed.
+ */
+ itkSetMacro(UsePhysicalSpace, bool)
+ itkGetMacro(UsePhysicalSpace, bool)
+
+ /**
+ * Set optional scaling for the deformation field
+ */
+ itkSetMacro(DeformationScaling, DeforamtionScalarType)
+ itkGetMacro(DeformationScaling, DeforamtionScalarType)
+
+ /**
+ * Should nearest neighbor interpolation be used?
+ */
+ itkSetMacro(UseNearestNeighbor, bool)
+ itkGetMacro(UseNearestNeighbor, bool)
+
+ /**
+ * When the warp is slightly outside of the moving image, should we
+ * perform linear interpolation between border pixels and outside value (0)
+ * or just report the outside value
+ */
+ itkSetMacro(ExtrapolateBorders, bool)
+ itkGetMacro(ExtrapolateBorders, bool)
+
+protected:
+
+ FastWarpCompositeImageFilter()
+ : m_UsePhysicalSpace(false), m_DeformationScaling(1.0),
+ m_UseNearestNeighbor(false), m_ExtrapolateBorders(true) { }
+
+ ~FastWarpCompositeImageFilter() {}
+
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+ virtual void VerifyInputInformation() {}
+
+ virtual void GenerateInputRequestedRegion();
+
+ virtual void GenerateOutputInformation();
+
+ bool m_UsePhysicalSpace, m_UseNearestNeighbor, m_ExtrapolateBorders;
+ DeforamtionScalarType m_DeformationScaling;
+
+
+private:
+ FastWarpCompositeImageFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+};
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "FastWarpCompositeImageFilter.txx"
+#endif
+
+#endif // FASTWARPCOMPOSITEIMAGEFILTER_H
diff --git a/Submodules/greedy/src/FastWarpCompositeImageFilter.txx b/Submodules/greedy/src/FastWarpCompositeImageFilter.txx
new file mode 100644
index 0000000..5b251ba
--- /dev/null
+++ b/Submodules/greedy/src/FastWarpCompositeImageFilter.txx
@@ -0,0 +1,147 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef FASTWARPCOMPOSITEIMAGEFILTER_TXX
+#define FASTWARPCOMPOSITEIMAGEFILTER_TXX
+
+#include "FastLinearInterpolator.h"
+#include "FastWarpCompositeImageFilter.h"
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+
+template <class TInputImage, class TOutputImage, class TDeformationField>
+void
+FastWarpCompositeImageFilter<TInputImage,TOutputImage,TDeformationField>
+::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ // Our images
+ const DeformationFieldType *def = this->GetDeformationField();
+ InputImageType *input = this->GetMovingImage();
+
+ int line_len = outputRegionForThread.GetSize(0);
+
+ // Create an iterator over the deformation field
+ typedef itk::ImageLinearIteratorWithIndex<OutputImageType> IterBase;
+ typedef IteratorExtender<IterBase> IterType;
+
+ // Determine the appropriate float/double type for the interpolator.
+ typedef typename itk::NumericTraits<OutputComponentType>::MeasurementVectorType::ValueType FloatType;
+
+ // Create a fast interpolator for the moving image
+ typedef FastLinearInterpolator<TInputImage, FloatType, ImageDimension> FastInterpolator;
+ FastInterpolator fi(input);
+
+ int ncomp = fi.GetPointerIncrement();
+
+ // Loop over the lines in the image
+ for(IterType it(this->GetOutput(), outputRegionForThread); !it.IsAtEnd(); it.NextLine())
+ {
+ // Get the pointer to the displacement vector line and the output vector line
+ const DeformationVectorType *phi = it.GetPixelPointer(def);
+ OutputComponentType *out = it.GetPixelPointer(this->GetOutput());
+
+ // Voxel index
+ IndexType idx = it.GetIndex();
+
+ // The current sample position
+ itk::ContinuousIndex<FloatType, ImageDimension> cix;
+ typename InputImageType::PointType p, pd, p_step;
+
+ if(m_UsePhysicalSpace)
+ {
+ // Compute starting point and point step
+ this->GetDeformationField()->TransformIndexToPhysicalPoint(idx, p);
+ idx[0] += 1;
+ this->GetDeformationField()->TransformIndexToPhysicalPoint(idx, p_step);
+ for(int j = 0; j < ImageDimension; j++)
+ p_step[j] -= p[j];
+ }
+
+ // Loop over the line
+ for(int i = 0; i < line_len; i++)
+ {
+ if(m_UsePhysicalSpace)
+ {
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ pd[j] = p[j] + phi[i][j] * m_DeformationScaling;
+ p[j] += p_step[j];
+ }
+
+ // TODO: this calls IsInside() internally, which limits efficiency
+ input->TransformPhysicalPointToContinuousIndex(pd, cix);
+ }
+ else
+ {
+ for(int j = 0; j < ImageDimension; j++)
+ cix[j] = idx[j] + phi[i][j] * m_DeformationScaling;
+ idx[0]++;
+ }
+
+ // Perform the interpolation
+ typename FastInterpolator::InOut status =
+ m_UseNearestNeighbor
+ ? fi.InterpolateNearestNeighbor(cix.GetDataPointer(), out)
+ : fi.Interpolate(cix.GetDataPointer(), out);
+
+ if(status == FastInterpolator::INSIDE ||
+ (status == FastInterpolator::BORDER && m_ExtrapolateBorders))
+ {
+ out += ncomp;
+ }
+ else
+ {
+ for(int k = 0; k < ncomp; k++)
+ *out++ = itk::NumericTraits<OutputComponentType>::Zero;
+ }
+ }
+ }
+}
+
+template <class TInputImage, class TOutputImage, class TDeformationField>
+void
+FastWarpCompositeImageFilter<TInputImage,TOutputImage,TDeformationField>
+::GenerateInputRequestedRegion()
+{
+ this->GetDeformationField()->SetRequestedRegion(this->GetOutput()->GetRequestedRegion());
+ this->GetMovingImage()->SetRequestedRegionToLargestPossibleRegion();
+
+}
+
+template <class TInputImage, class TOutputImage, class TDeformationField>
+void
+FastWarpCompositeImageFilter<TInputImage,TOutputImage,TDeformationField>
+::GenerateOutputInformation()
+{
+ Superclass::GenerateOutputInformation();
+
+ this->GetOutput()->SetNumberOfComponentsPerPixel(this->GetMovingImage()->GetNumberOfComponentsPerPixel());
+ this->GetOutput()->SetLargestPossibleRegion(this->GetDeformationField()->GetLargestPossibleRegion());
+}
+
+
+
+#endif // FASTWARPCOMPOSITEIMAGEFILTER_TXX
diff --git a/Submodules/greedy/src/GreedyAPI.cxx b/Submodules/greedy/src/GreedyAPI.cxx
new file mode 100644
index 0000000..b8752ca
--- /dev/null
+++ b/Submodules/greedy/src/GreedyAPI.cxx
@@ -0,0 +1,2745 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "GreedyAPI.h"
+
+#include <iostream>
+#include <sstream>
+#include <cstdio>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+#include <itkImageFileReader.h>
+#include <itkAffineTransform.h>
+#include <itkTransformFactory.h>
+#include <itkTimeProbe.h>
+
+#include "MultiImageRegistrationHelper.h"
+#include "FastWarpCompositeImageFilter.h"
+#include <vnl/algo/vnl_powell.h>
+#include <vnl/algo/vnl_svd.h>
+#include <vnl/algo/vnl_symmetric_eigensystem.h>
+#include <vnl/vnl_trace.h>
+#include <vnl/vnl_numeric_traits.h>
+
+// Little helper functions
+template <unsigned int VDim> class array_caster
+{
+public:
+ template <class T> static itk::Size<VDim> to_itkSize(const T &t)
+ {
+ itk::Size<VDim> sz;
+ for(int i = 0; i < VDim; i++)
+ sz[i] = t[i];
+ return sz;
+ }
+};
+
+template <class TITKMatrix, class TVNLMatrix>
+void vnl_matrix_to_itk_matrix(
+ const TVNLMatrix &vmat,
+ TITKMatrix &imat)
+{
+ for(int r = 0; r < TITKMatrix::RowDimensions; r++)
+ for(int c = 0; c < TITKMatrix::ColumnDimensions; c++)
+ imat(r,c) = static_cast<typename TITKMatrix::ValueType>(vmat(r,c));
+}
+
+template <class TITKVector, class TVNLVector>
+void vnl_vector_to_itk_vector(
+ const TVNLVector &vvec,
+ TITKVector &ivec)
+{
+ for(int r = 0; r < TITKVector::Dimension; r++)
+ ivec[r] = static_cast<typename TITKVector::ValueType>(vvec(r));
+}
+
+template <class TITKMatrix, class TVNL>
+void itk_matrix_to_vnl_matrix(
+ const TITKMatrix &imat,
+ vnl_matrix_fixed<TVNL,TITKMatrix::RowDimensions,TITKMatrix::ColumnDimensions> &vmat)
+{
+ for(int r = 0; r < TITKMatrix::RowDimensions; r++)
+ for(int c = 0; c < TITKMatrix::ColumnDimensions; c++)
+ vmat(r,c) = static_cast<TVNL>(imat(r,c));
+}
+
+template <class TITKMatrix, class TVNL>
+void itk_matrix_to_vnl_matrix(
+ const TITKMatrix &imat,
+ vnl_matrix<TVNL> &vmat)
+{
+ vmat.set_size(TITKMatrix::RowDimensions,TITKMatrix::ColumnDimensions);
+ for(int r = 0; r < TITKMatrix::RowDimensions; r++)
+ for(int c = 0; c < TITKMatrix::ColumnDimensions; c++)
+ vmat(r,c) = static_cast<TVNL>(imat(r,c));
+}
+
+template <class TITKVector, class TVNL>
+void itk_vector_to_vnl_vector(
+ const TITKVector &ivec,
+ vnl_vector_fixed<TVNL,TITKVector::Dimension> &vvec)
+{
+ for(int r = 0; r < TITKVector::Dimension; r++)
+ vvec(r) = static_cast<TVNL>(ivec[r]);
+}
+
+template <class TITKVector, class TVNL>
+void itk_vector_to_vnl_vector(
+ const TITKVector &ivec,
+ vnl_vector<TVNL> &vvec)
+{
+ vvec.set_size(TITKVector::Dimension);
+ for(int r = 0; r < TITKVector::Dimension; r++)
+ vvec(r) = static_cast<TVNL>(ivec[r]);
+}
+
+// Helper function to map from ITK coordiante space to RAS space
+template<unsigned int VDim, class TMat, class TVec>
+void
+GetVoxelSpaceToNiftiSpaceTransform(itk::ImageBase<VDim> *image,
+ TMat &A,
+ TVec &b)
+{
+ // Generate intermediate terms
+ typedef typename TMat::element_type TReal;
+ vnl_matrix<double> m_dir, m_ras_matrix;
+ vnl_diag_matrix<double> m_scale, m_lps_to_ras;
+ vnl_vector<double> v_origin, v_ras_offset;
+
+ // Compute the matrix
+ m_dir = image->GetDirection().GetVnlMatrix();
+ m_scale.set(image->GetSpacing().GetVnlVector());
+ m_lps_to_ras.set(vnl_vector<double>(VDim, 1.0));
+ m_lps_to_ras[0] = -1;
+ m_lps_to_ras[1] = -1;
+ A = m_lps_to_ras * m_dir * m_scale;
+
+ // Compute the vector
+ v_origin = image->GetOrigin().GetVnlVector();
+ b = m_lps_to_ras * v_origin;
+}
+
+
+
+
+
+template <unsigned int VDim, typename TReal>
+GreedyApproach<VDim, TReal>::PureAffineCostFunction
+::PureAffineCostFunction(GreedyParameters *param, ParentType *parent, int level, OFHelperType *helper)
+ : AbstractAffineCostFunction(VDim * (VDim + 1))
+{
+ // Store the data
+ m_Param = param;
+ m_OFHelper = helper;
+ m_Level = level;
+ m_Parent = parent;
+
+ // Allocate the working images
+ m_Phi = VectorImageType::New();
+ m_Phi->CopyInformation(helper->GetReferenceSpace(level));
+ m_Phi->SetRegions(helper->GetReferenceSpace(level)->GetBufferedRegion());
+ m_Phi->Allocate();
+
+ m_GradMetric = VectorImageType::New();
+ m_GradMetric->CopyInformation(helper->GetReferenceSpace(level));
+ m_GradMetric->SetRegions(helper->GetReferenceSpace(level)->GetBufferedRegion());
+ m_GradMetric->Allocate();
+
+ m_GradMask = VectorImageType::New();
+ m_GradMask->CopyInformation(helper->GetReferenceSpace(level));
+ m_GradMask->SetRegions(helper->GetReferenceSpace(level)->GetBufferedRegion());
+ m_GradMask->Allocate();
+
+ m_Metric = ImageType::New();
+ m_Metric->CopyInformation(helper->GetReferenceSpace(level));
+ m_Metric->SetRegions(helper->GetReferenceSpace(level)->GetBufferedRegion());
+ m_Metric->Allocate();
+
+ m_Mask = ImageType::New();
+ m_Mask->CopyInformation(helper->GetReferenceSpace(level));
+ m_Mask->SetRegions(helper->GetReferenceSpace(level)->GetBufferedRegion());
+ m_Mask->Allocate();
+}
+
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::PureAffineCostFunction
+::compute(const vnl_vector<double> &x, double *f, vnl_vector<double> *g)
+{
+ // Form a matrix/vector from x
+ typename LinearTransformType::Pointer tran = LinearTransformType::New();
+
+ // Set the components of the transform
+ unflatten_affine_transform(x.data_block(), tran.GetPointer());
+
+ // Compute the gradient
+ double val = 0.0;
+ if(g)
+ {
+ typename LinearTransformType::Pointer grad = LinearTransformType::New();
+
+ if(m_Param->metric == GreedyParameters::SSD)
+ {
+ val = m_OFHelper->ComputeAffineMSDMatchAndGradient(
+ m_Level, tran, m_Metric, m_Mask, m_GradMetric, m_GradMask, m_Phi, grad);
+
+ flatten_affine_transform(grad.GetPointer(), g->data_block());
+ }
+ else if(m_Param->metric == GreedyParameters::NCC)
+ {
+
+ val = m_OFHelper->ComputeAffineNCCMatchAndGradient(
+ m_Level, tran, array_caster<VDim>::to_itkSize(m_Param->metric_radius),
+ m_Metric, m_Mask, m_GradMetric, m_GradMask, m_Phi, grad);
+
+ flatten_affine_transform(grad.GetPointer(), g->data_block());
+
+ // NCC should be maximized
+ (*g) *= -10000.0;
+ val *= -10000.0;
+ }
+ else if(m_Param->metric == GreedyParameters::MI || m_Param->metric == GreedyParameters::NMI)
+ {
+ val = m_OFHelper->ComputeAffineMIMatchAndGradient(
+ m_Level, m_Param->metric == GreedyParameters::NMI,
+ tran, m_Metric, m_Mask, m_GradMetric, m_GradMask, m_Phi, grad);
+
+ flatten_affine_transform(grad.GetPointer(), g->data_block());
+
+ val *= -10000.0;
+ (*g) *= -10000.0;
+
+ }
+ }
+ else
+ {
+ if(m_Param->metric == GreedyParameters::SSD)
+ {
+ val = m_OFHelper->ComputeAffineMSDMatchAndGradient(
+ m_Level, tran, m_Metric, m_Mask, m_GradMetric, m_GradMask, m_Phi, NULL);
+ }
+ else if(m_Param->metric == GreedyParameters::NCC)
+ {
+ val = m_OFHelper->ComputeAffineNCCMatchAndGradient(
+ m_Level, tran, array_caster<VDim>::to_itkSize(m_Param->metric_radius)
+ , m_Metric, m_Mask, m_GradMetric, m_GradMask, m_Phi, NULL);
+
+ // NCC should be maximized
+ val *= -10000.0;
+ }
+ else if(m_Param->metric == GreedyParameters::MI || m_Param->metric == GreedyParameters::NMI)
+ {
+ val = m_OFHelper->ComputeAffineMIMatchAndGradient(
+ m_Level, m_Param->metric == GreedyParameters::NMI,
+ tran, m_Metric, m_Mask, m_GradMetric, m_GradMask, m_Phi, NULL);
+
+ val *= -10000.0;
+ }
+ }
+
+ // Has the metric improved?
+ if(m_Parent->GetMetricLog().size())
+ {
+ const std::vector<double> &log = m_Parent->GetMetricLog().back();
+ if(log.size() == 0 || log.back() > val)
+ {
+ // Record the metric value
+ m_Parent->RecordMetricValue(val);
+
+ // Write out the current iteration transform
+ if(m_Param->output_intermediate.length())
+ {
+ vnl_matrix<double> Q_physical = MapAffineToPhysicalRASSpace(*m_OFHelper, m_Level, tran);
+ m_Parent->WriteAffineMatrixViaCache(m_Param->output_intermediate, Q_physical);
+ }
+ }
+ }
+
+ if(f)
+ *f = val;
+}
+
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::PureAffineCostFunction
+::GetCoefficients(LinearTransformType *tran)
+{
+ vnl_vector<double> x_true(this->get_number_of_unknowns());
+ flatten_affine_transform(tran, x_true.data_block());
+ return x_true;
+}
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::PureAffineCostFunction
+::GetTransform(const vnl_vector<double> &coeff, LinearTransformType *tran)
+{
+ unflatten_affine_transform(coeff.data_block(), tran);
+}
+
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::PureAffineCostFunction
+::GetOptimalParameterScaling(const itk::Size<VDim> &image_dim)
+{
+ // Initialize the scaling vector
+ vnl_vector<double> scaling(this->get_number_of_unknowns());
+
+ // Set the scaling of the parameters based on image dimensions. This makes it
+ // possible to set tolerances in units of voxels. The order of change in the
+ // parameters is comparable to the displacement of any point inside the image
+ typename LinearTransformType::MatrixType matrix;
+ typename LinearTransformType::OffsetType offset;
+
+ for(int i = 0; i < VDim; i++)
+ {
+ offset[i] = 1.0;
+ for(int j = 0; j < VDim; j++)
+ matrix(i, j) = image_dim[j];
+ }
+
+ typename LinearTransformType::Pointer transform = LinearTransformType::New();
+ transform->SetMatrix(matrix);
+ transform->SetOffset(offset);
+ flatten_affine_transform(transform.GetPointer(), scaling.data_block());
+
+ return scaling;
+}
+
+/**
+ * PHYSICAL SPACE COST FUNCTION - WRAPS AROUND AFFINE
+ */
+template <unsigned int VDim, typename TReal>
+GreedyApproach<VDim, TReal>::PhysicalSpaceAffineCostFunction
+::PhysicalSpaceAffineCostFunction(GreedyParameters *param, ParentType *parent, int level, OFHelperType *helper)
+ : AbstractAffineCostFunction(VDim * (VDim + 1)), m_PureFunction(param, parent, level, helper)
+{
+ // The rigid transformation must be rigid in physical space, not in voxel space
+ // So in the constructor, we must compute the mappings from the two spaces
+ GetVoxelSpaceToNiftiSpaceTransform(helper->GetReferenceSpace(level), Q_fix, b_fix);
+ GetVoxelSpaceToNiftiSpaceTransform(helper->GetMovingReferenceSpace(level), Q_mov, b_mov);
+
+ // Compute the inverse transformations
+ Q_fix_inv = vnl_matrix_inverse<double>(Q_fix);
+ b_fix_inv = - Q_fix_inv * b_fix;
+
+ Q_mov_inv = vnl_matrix_inverse<double>(Q_mov);
+ b_mov_inv = - Q_mov_inv * b_mov;
+
+ // Take advantage of the fact that the transformation is linear in A and b to compute
+ // the Jacobian of the transformation ahead of time, and "lazily", using finite differences
+ int n = VDim * (VDim + 1);
+ J_phys_vox.set_size(n, n);
+ vnl_vector<double> x_phys(n, 0), x_vox_0(n), x_vox(n);
+
+ // Voxel parameter vector corresponding to zero transform
+ this->map_phys_to_vox(x_phys, x_vox_0);
+
+ // Compute each column of the jacobian
+ for(int i = 0; i < n; i++)
+ {
+ x_phys.fill(0);
+ x_phys[i] = 1;
+ this->map_phys_to_vox(x_phys, x_vox);
+ J_phys_vox.set_column(i, x_vox - x_vox_0);
+ }
+
+
+}
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::PhysicalSpaceAffineCostFunction
+::map_phys_to_vox(const vnl_vector<double> &x_phys, vnl_vector<double> &x_vox)
+{
+ Mat A_phys;
+ Vec b_phys;
+
+ // unflatten the input parameters into A and b
+ unflatten_affine_transform(x_phys.data_block(), A_phys, b_phys);
+
+ // convert into voxel-space affine transform
+ Mat A_vox = Q_mov_inv * A_phys * Q_fix;
+ Vec b_vox = Q_mov_inv * (A_phys * b_fix + b_phys) + b_mov_inv;
+
+ // Flatten back
+ x_vox.set_size(m_PureFunction.get_number_of_unknowns());
+ flatten_affine_transform(A_vox, b_vox, x_vox.data_block());
+}
+
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::PhysicalSpaceAffineCostFunction
+::compute(const vnl_vector<double> &x, double *f, vnl_vector<double> *g)
+{
+ // Map to voxel space
+ vnl_vector<double> x_vox(m_PureFunction.get_number_of_unknowns());
+ this->map_phys_to_vox(x, x_vox);
+
+ // Do we need the gradient?
+ if(g)
+ {
+ // Compute the function and gradient wrt voxel parameters
+ vnl_vector<double> g_vox(m_PureFunction.get_number_of_unknowns());
+ m_PureFunction.compute(x_vox, f, &g_vox);
+
+ // Transform voxel-space gradient into physical-space gradient
+ *g = J_phys_vox.transpose() * g_vox;
+ }
+ else
+ {
+ // Just compute the function
+ m_PureFunction.compute(x_vox, f, NULL);
+ }
+}
+
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::PhysicalSpaceAffineCostFunction
+::GetOptimalParameterScaling(const itk::Size<VDim> &image_dim)
+{
+ // TODO: work out scaling for this
+ return m_PureFunction.GetOptimalParameterScaling(image_dim);
+}
+
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::PhysicalSpaceAffineCostFunction
+::GetCoefficients(LinearTransformType *tran)
+{
+ // The input transform is in voxel space, we must return parameters in physical space
+ Mat A_vox, A_phys;
+ Vec b_vox, b_phys;
+
+ itk_matrix_to_vnl_matrix(tran->GetMatrix(), A_vox);
+ itk_vector_to_vnl_vector(tran->GetOffset(), b_vox);
+
+ // convert into physical-space affine transform
+ A_phys = Q_mov * A_vox * Q_fix_inv;
+ b_phys = Q_mov * (b_vox - b_mov_inv) - A_phys * b_fix;
+
+ // Flatten
+ vnl_vector<double> x(m_PureFunction.get_number_of_unknowns());
+ flatten_affine_transform(A_phys, b_phys, x.data_block());
+
+ return x;
+}
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::PhysicalSpaceAffineCostFunction
+::GetTransform(const vnl_vector<double> &x, LinearTransformType *tran)
+{
+ // Get voxel-space tranform corresponding to the parameters x
+ vnl_vector<double> x_vox(m_PureFunction.get_number_of_unknowns());
+ this->map_phys_to_vox(x, x_vox);
+
+ // Unflatten into a transform
+ unflatten_affine_transform(x_vox.data_block(), tran);
+}
+
+
+/**
+ * SCALING COST FUNCTION - WRAPS AROUND AFFINE
+ */
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::ScalingCostFunction
+::compute(const vnl_vector<double> &x, double *f, vnl_vector<double> *g)
+{
+ // Scale the parameters so they are in unscaled units
+ vnl_vector<double> x_scaled = element_quotient(x, m_Scaling);
+
+ // Call the wrapped method
+ if(g)
+ {
+ vnl_vector<double> g_scaled(x_scaled.size());
+ m_PureFunction->compute(x_scaled, f, &g_scaled);
+ *g = element_quotient(g_scaled, m_Scaling);
+ }
+ else
+ {
+ m_PureFunction->compute(x_scaled, f, g);
+ }
+}
+
+// Get the parameters for the specified initial transform
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::ScalingCostFunction
+::GetCoefficients(LinearTransformType *tran)
+{
+ vnl_vector<double> x_true = m_PureFunction->GetCoefficients(tran);
+ return element_product(x_true, m_Scaling);
+}
+
+// Get the transform for the specificed coefficients
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::ScalingCostFunction
+::GetTransform(const vnl_vector<double> &coeff, LinearTransformType *tran)
+{
+ vnl_vector<double> x_true = element_quotient(coeff, m_Scaling);
+ m_PureFunction->GetTransform(x_true, tran);
+}
+
+
+
+/**
+ * RIGID COST FUNCTION - WRAPS AROUND AFFINE
+ */
+template <unsigned int VDim, typename TReal>
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::RigidCostFunction(GreedyParameters *param, ParentType *parent, int level, OFHelperType *helper)
+ : AbstractAffineCostFunction(VDim * 2), m_AffineFn(param, parent, level, helper)
+{
+}
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::compute(const vnl_vector<double> &x, double *f, vnl_vector<double> *g)
+{
+ // Place parameters into q and b
+ Vec3 q, b;
+ q[0] = x[0]; q[1] = x[1]; q[2] = x[2];
+ b[0] = x[3]; b[1] = x[4]; b[2] = x[5];
+
+ // Compute theta
+ double theta = q.magnitude();
+
+ // Predefine the rotation matrix
+ Mat3 R; R.set_identity();
+
+ // Create the Q matrix
+ Mat3 Qmat; Qmat.fill(0.0);
+ Qmat(0,1) = -q[2]; Qmat(1,0) = q[2];
+ Qmat(0,2) = q[1]; Qmat(2,0) = -q[1];
+ Qmat(1,2) = -q[0]; Qmat(2,1) = q[0];
+
+ // Compute the square of the matrix
+ Mat3 QQ = vnl_matrix_fixed_mat_mat_mult(Qmat, Qmat);
+
+ // A small epsilon for which a better approximation is R = I + Q
+ double eps = 1.0e-4;
+ double a1, a2;
+
+ // When theta = 0, rotation is identity
+ if(theta > eps)
+ {
+ // Compute the constant terms in the Rodriguez formula
+ a1 = sin(theta) / theta;
+ a2 = (1 - cos(theta)) / (theta * theta);
+
+ // Compute the rotation matrix
+ R += a1 * Qmat + a2 * QQ;
+ }
+ else
+ {
+ R += Qmat;
+ }
+
+ // Now we have a rotation and a translation, convert to parameters for the affine function
+ vnl_vector<double> x_affine(m_AffineFn.get_number_of_unknowns());
+ flatten_affine_transform(R, b, x_affine.data_block());
+
+ // Split depending on whether there is gradient to compute
+ if(g)
+ {
+ // Create a vector to store the affine gradient
+ vnl_vector<double> g_affine(m_AffineFn.get_number_of_unknowns());
+ m_AffineFn.compute(x_affine, f, &g_affine);
+
+ // Compute the matrices d_Qmat
+ Mat3 d_Qmat[3], d_R[3];
+ d_Qmat[0].fill(0); d_Qmat[0](1,2) = -1; d_Qmat[0](2,1) = 1;
+ d_Qmat[1].fill(0); d_Qmat[1](0,2) = 1; d_Qmat[1](2,0) = -1;
+ d_Qmat[2].fill(0); d_Qmat[2](0,1) = -1; d_Qmat[2](1,0) = 1;
+
+ // Compute partial derivatives of R wrt q
+ if(theta > eps)
+ {
+ // Compute the scaling factors in the Rodriguez formula
+ double d_a1 = (theta * cos(theta) - sin(theta)) / (theta * theta * theta);
+ double d_a2 = (theta * sin(theta) + 2 * cos(theta) - 2) /
+ (theta * theta * theta * theta);
+
+ // Loop over the coordinate and compute the derivative of the rotation matrix wrt x
+ for(int p = 0; p < 3; p++)
+ {
+ // Compute the gradient of the rotation with respect to q[p]
+ d_R[p] = d_a1 * q[p] * Qmat +
+ a1 * d_Qmat[p] +
+ d_a2 * q[p] * vnl_matrix_fixed_mat_mat_mult(Qmat, Qmat)
+ + a2 * (vnl_matrix_fixed_mat_mat_mult(d_Qmat[p], Qmat) +
+ vnl_matrix_fixed_mat_mat_mult(Qmat, d_Qmat[p]));
+ }
+ }
+ else
+ {
+ for(int p = 0; p < 3; p++)
+ d_R[p] = d_Qmat[p];
+ }
+
+ // Create a matrix to hold the jacobian
+ vnl_matrix<double> jac(m_AffineFn.get_number_of_unknowns(), 6);
+ jac.fill(0.0);
+
+ // Zero vector
+ Vec3 zero_vec; zero_vec.fill(0.0);
+ Mat3 zero_mat; zero_mat.fill(0.0);
+
+ // Fill out the jacobian
+ for(int p = 0; p < 3; p++)
+ {
+ // Fill the corresponding column
+ vnl_vector<double> jac_col_q(m_AffineFn.get_number_of_unknowns());
+ flatten_affine_transform(d_R[p], zero_vec, jac_col_q.data_block());
+ jac.set_column(p, jac_col_q);
+
+ // Also set column on the right (wrt translation)
+ vnl_vector<double> jac_col_b(m_AffineFn.get_number_of_unknowns());
+ Vec3 ep; ep.fill(0.0); ep[p] = 1;
+ flatten_affine_transform(zero_mat, ep, jac_col_b.data_block());
+ jac.set_column(p+3, jac_col_b);
+ }
+
+ // Multiply the gradient by the jacobian
+ *g = jac.transpose() * g_affine;
+ }
+ else
+ {
+ m_AffineFn.compute(x_affine, f, NULL);
+ }
+}
+
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::GetOptimalParameterScaling(const itk::Size<VDim> &image_dim)
+{
+ // Initialize the scaling vector
+ vnl_vector<double> scaling(this->get_number_of_unknowns());
+
+ // Scaling is harder for rotations. The rotation parameters are in units of
+ // radians. We must figure out how many radians are equivalent to a point in
+ // the image moving by a single voxel. That actually works out to be 1/dim.
+
+ // So we take the average of the image dimensions and use that as scaling
+ double mean_dim = 0;
+ for(int i = 0; i < VDim; i++)
+ mean_dim += image_dim[i] / VDim;
+ scaling[0] = scaling[1] = scaling[2] = mean_dim;
+ scaling[3] = scaling[4] = scaling[5] = 1.0;
+
+ return scaling;
+}
+
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::GetCoefficients(LinearTransformType *tran)
+{
+ // This affine transform is in voxel space. We must first map it into physical
+ vnl_vector<double> x_aff_phys = m_AffineFn.GetCoefficients(tran);
+ Mat3 A; Vec3 b;
+ unflatten_affine_transform(x_aff_phys.data_block(), A, b);
+
+ // Compute polar decomposition of the affine matrix
+ vnl_svd<double> svd(A);
+ Mat3 R = svd.U() * svd.V().transpose();
+ Vec3 q = this->GetAxisAngle(R);
+
+ // Make result
+ vnl_vector<double> x(6);
+ x[0] = q[0]; x[1] = q[1]; x[2] = q[2];
+ x[3] = b[0]; x[4] = b[1]; x[5] = b[2];
+
+ return x;
+}
+
+template <unsigned int VDim, typename TReal>
+typename GreedyApproach<VDim, TReal>::RigidCostFunction::Vec3
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::GetAxisAngle(const Mat3 &R)
+{
+ double eps = 1e-4;
+ double f_thresh = cos(eps);
+
+ // Compute the matrix logarithm of R
+ double f = (vnl_trace(R) - 1) / 2;
+ Vec3 q;
+ if(f >= f_thresh)
+ {
+ q[0] = R(2,1) - R(1,2);
+ q[1] = R(0,2) - R(2,0);
+ q[2] = R(1,0) - R(0,1);
+ q *= 0.5;
+ }
+ else
+ {
+ double theta = acos(f);
+ double sin_theta = sqrt(1 - f * f);
+ q[0] = R(2,1) - R(1,2);
+ q[1] = R(0,2) - R(2,0);
+ q[2] = R(1,0) - R(0,1);
+ q *= theta / (2 * sin_theta);
+ }
+
+ return q;
+}
+
+
+
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::GetRandomCoeff(const vnl_vector<double> &xInit, vnl_random &randy, double sigma_angle, double sigma_xyz,
+ const Vec3 &C_fixed, const Vec3 &C_moving)
+{
+ // Generate a random rotation using given angles
+ Vec3 q;
+ for(int d = 0; d < 3; d++)
+ q[d] = randy.normal() * sigma_angle;
+ Mat3 R = this->GetRotationMatrix(q);
+
+ // Generate a rotation matrix for the initial parameters
+ Vec3 qInit;
+ for(int d = 0; d < 3; d++)
+ qInit[d] = xInit[d];
+ Mat3 R_init = this->GetRotationMatrix(qInit);
+
+ // Combined rotation
+ Mat3 R_comb = R * R_init;
+
+ // Take the log map
+ Vec3 q_comb = this->GetAxisAngle(R_comb);
+
+ // Generate the offset
+ Vec3 b = C_moving - R_comb * C_fixed;
+
+ // Apply random offset
+ for(int d = 0; d < 3; d++)
+ b[d] += randy.normal() * sigma_xyz;
+
+ // Generate output vector
+ vnl_vector<double> x(6);
+ x[0] = q_comb[0];
+ x[1] = q_comb[1];
+ x[2] = q_comb[2];
+ x[3] = b[0];
+ x[4] = b[1];
+ x[5] = b[2];
+
+ return x;
+}
+
+template <unsigned int VDim, typename TReal>
+typename GreedyApproach<VDim, TReal>::RigidCostFunction::Mat3
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::GetRotationMatrix(const Vec3 &q)
+{
+ // Compute theta
+ double theta = q.magnitude();
+
+ // Predefine the rotation matrix
+ Mat3 R; R.set_identity();
+
+ // Create the Q matrix
+ Mat3 Qmat; Qmat.fill(0.0);
+ Qmat(0,1) = -q[2]; Qmat(1,0) = q[2];
+ Qmat(0,2) = q[1]; Qmat(2,0) = -q[1];
+ Qmat(1,2) = -q[0]; Qmat(2,1) = q[0];
+
+ // Compute the square of the matrix
+ Mat3 QQ = vnl_matrix_fixed_mat_mat_mult(Qmat, Qmat);
+
+ // When theta = 0, rotation is identity
+ double eps = 1e-4;
+
+ if(theta > eps)
+ {
+ // Compute the constant terms in the Rodriguez formula
+ double a1 = sin(theta) / theta;
+ double a2 = (1 - cos(theta)) / (theta * theta);
+
+ // Compute the rotation matrix
+ R += a1 * Qmat + a2 * QQ;
+ }
+ else
+ {
+ R += Qmat;
+ }
+
+ return R;
+}
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>::RigidCostFunction
+::GetTransform(const vnl_vector<double> &x, LinearTransformType *tran)
+{
+ // Place parameters into q and b
+ Vec3 q, b;
+ q[0] = x[0]; q[1] = x[1]; q[2] = x[2];
+ b[0] = x[3]; b[1] = x[4]; b[2] = x[5];
+
+ // Get the rotation matrix
+ Mat3 R = this->GetRotationMatrix(q);
+
+ // This gives us the physical space affine matrices. Flatten and map to voxel space
+ vnl_vector<double> x_aff_phys(m_AffineFn.get_number_of_unknowns());
+ flatten_affine_transform(R, b, x_aff_phys.data_block());
+ m_AffineFn.GetTransform(x_aff_phys, tran);
+}
+
+
+/*
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, double>::AffineCostFunction
+::compute(const vnl_vector<double> &x, double *f, vnl_vector<double> *g)
+{
+ // Form a matrix/vector from x
+ typename TransformType::Pointer tran = TransformType::New();
+
+ // Set the components of the transform
+ unflatten_affine_transform(x.data_block(), tran.GetPointer());
+
+ // Compute the gradient
+ double val = 0.0;
+ if(g)
+ {
+ typename TransformType::Pointer grad = TransformType::New();
+ val = m_OFHelper->ComputeAffineMatchAndGradient(m_Level, tran, grad);
+ flatten_affine_transform(grad.GetPointer(), g->data_block());
+ }
+ else
+ {
+ val = m_OFHelper->ComputeAffineMatchAndGradient(m_Level, tran, NULL);
+ }
+
+ if(f)
+ *f = val;
+}
+*
+*
+*/
+
+#include "itkTransformFileReader.h"
+
+template <unsigned int VDim, typename TReal>
+vnl_matrix<double>
+GreedyApproach<VDim, TReal>
+::ReadAffineMatrixViaCache(const TransformSpec &ts)
+{
+ // Physical (RAS) space transform matrix
+ vnl_matrix<double> Qp(VDim+1, VDim+1); Qp.set_identity();
+
+ // An ITK-style transform - forced to floating point here
+ typedef itk::MatrixOffsetTransformBase<double, VDim, VDim> TransformType;
+ typename TransformType::Pointer itk_tran;
+
+ // See if a transform is already stored in the cache
+ typename ImageCache::const_iterator itCache = m_ImageCache.find(ts.filename);
+ if(itCache != m_ImageCache.end())
+ {
+ TransformType *cached = dynamic_cast<TransformType *>(itCache->second);
+ if(!cached)
+ throw GreedyException("Cached transform %s is of the wrong type (%s, but should be %s)",
+ ts.filename.c_str(), typeid(*itCache->second).name(),
+ typeid(TransformType).name());
+
+ itk_tran = cached;
+ }
+ else
+ {
+ // Open the file and read the first line
+ std::ifstream fin(ts.filename.c_str());
+ std::string header_line, itk_header = "#Insight Transform File";
+ std::getline(fin, header_line);
+
+ if(header_line.substr(0, itk_header.size()) == itk_header)
+ {
+ fin.close();
+ try
+ {
+ // First we try to load the transform using ITK code
+ // This code is from c3d_affine_tool
+ typedef itk::AffineTransform<double, VDim> AffTran;
+ itk::TransformFactory<TransformType>::RegisterTransform();
+ itk::TransformFactory<AffTran>::RegisterTransform();
+
+ itk::TransformFileReader::Pointer fltReader = itk::TransformFileReader::New();
+ fltReader->SetFileName(ts.filename.c_str());
+ fltReader->Update();
+
+ itk::TransformBase *base = fltReader->GetTransformList()->front();
+ itk_tran = dynamic_cast<TransformType *>(base);
+ }
+ catch(...)
+ {
+ throw GreedyException("Unable to read ITK transform file %s", ts.filename.c_str());
+ }
+ }
+ else
+ {
+ // Try reading C3D matrix format
+ fin.seekg(0);
+ for(size_t i = 0; i < VDim+1; i++)
+ for(size_t j = 0; j < VDim+1; j++)
+ if(fin.good())
+ {
+ fin >> Qp[i][j];
+ }
+ fin.close();
+ }
+ }
+
+ // At this point we might have read the RAS matrix directly, or an ITK transform
+ // if the latter, extract the RAS matrix
+ if(itk_tran.IsNotNull())
+ {
+ for(size_t r = 0; r < VDim; r++)
+ {
+ for(size_t c = 0; c < VDim; c++)
+ {
+ Qp(r,c) = itk_tran->GetMatrix()(r,c);
+ }
+ Qp(r,VDim) = itk_tran->GetOffset()[r];
+ }
+
+ // RAS - LPI nonsense
+ if(VDim == 3)
+ {
+ Qp(2,0) *= -1; Qp(2,1) *= -1;
+ Qp(0,2) *= -1; Qp(1,2) *= -1;
+ Qp(0,3) *= -1; Qp(1,3) *= -1;
+ }
+ }
+
+ // Compute the exponent
+ if(ts.exponent == 1.0)
+ {
+ return Qp;
+ }
+ else if(ts.exponent == -1.0)
+ {
+ return vnl_matrix_inverse<double>(Qp);
+ }
+ else
+ {
+ throw GreedyException("Transform exponent values of +1 and -1 are the only ones currently supported");
+ }
+
+ return Qp;
+}
+
+
+
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>
+::WriteAffineMatrixViaCache(
+ const std::string &filename, const vnl_matrix<double> &Qp)
+{
+ // An ITK-style transform - forced to double point here
+ typedef itk::MatrixOffsetTransformBase<double, VDim, VDim> TransformType;
+
+ // See if a transform is already stored in the cache
+ typename ImageCache::const_iterator itCache = m_ImageCache.find(filename);
+ if(itCache != m_ImageCache.end())
+ {
+ TransformType *cached = dynamic_cast<TransformType *>(itCache->second);
+ if(!cached)
+ throw GreedyException("Cached transform %s is of the wrong type (%s, but should be %s)",
+ filename.c_str(), typeid(*itCache->second).name(),
+ typeid(TransformType).name());
+
+ // RAS - LPI nonsense
+ vnl_matrix<double> Q = Qp;
+ if(VDim == 3)
+ {
+ Q(2,0) *= -1; Q(2,1) *= -1;
+ Q(0,2) *= -1; Q(1,2) *= -1;
+ Q(0,3) *= -1; Q(1,3) *= -1;
+ }
+
+ typename TransformType::MatrixType matrix;
+ typename TransformType::OffsetType offset;
+
+ // We have found the output transform and can use it for assignment
+ for(size_t r = 0; r < VDim; r++)
+ {
+ for(size_t c = 0; c < VDim; c++)
+ {
+ matrix(r, c) = Q(r, c);
+ }
+ offset[r] = Q(r, VDim);
+ }
+
+ cached->SetMatrix(matrix);
+ cached->SetOffset(offset);
+ }
+ else
+ {
+ std::ofstream matrixFile;
+ matrixFile.open(filename.c_str());
+ matrixFile << Qp;
+ matrixFile.close();
+ }
+}
+
+
+
+template <unsigned int VDim, typename TReal>
+template <class TImage>
+itk::SmartPointer<TImage>
+GreedyApproach<VDim, TReal>
+::ReadImageViaCache(const std::string &filename)
+{
+ // Check the cache for the presence of the image
+ typename ImageCache::const_iterator it = m_ImageCache.find(filename);
+ if(it != m_ImageCache.end())
+ {
+ TImage *image = dynamic_cast<TImage *>(it->second);
+ if(!image)
+ throw GreedyException("Cached image %s is of the wrong type (%s, but should be %s)",
+ filename.c_str(), typeid(*it->second).name(), typeid(TImage).name());
+ itk::SmartPointer<TImage> pointer = image;
+
+ return pointer;
+ }
+
+ // Read the image using ITK reader
+ typedef itk::ImageFileReader<TImage> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(filename.c_str());
+ reader->Update();
+
+ itk::SmartPointer<TImage> pointer = reader->GetOutput();
+ return pointer;
+}
+
+
+template <unsigned int VDim, typename TReal>
+void GreedyApproach<VDim, TReal>
+::ReadImages(GreedyParameters ¶m, OFHelperType &ofhelper)
+{
+ // If the parameters include a sequence of transforms, apply it first
+ VectorImagePointer moving_pre_warp;
+
+ // Read the input images and stick them into an image array
+ for(int i = 0; i < param.inputs.size(); i++)
+ {
+ // Read fixed and moving images
+ CompositeImagePointer imgFix = ReadImageViaCache<CompositeImageType>(param.inputs[i].fixed);
+ CompositeImagePointer imgMov = ReadImageViaCache<CompositeImageType>(param.inputs[i].moving);
+
+ // Read the pre-warps (only once)
+ if(param.moving_pre_transforms.size() && moving_pre_warp.IsNull())
+ {
+ ReadTransformChain(param.moving_pre_transforms, imgFix, moving_pre_warp);
+ }
+
+ if(moving_pre_warp.IsNotNull())
+ {
+ // Create an image to store the warp
+ CompositeImagePointer warped_moving;
+ LDDMMType::alloc_cimg(warped_moving, imgFix,
+ imgMov->GetNumberOfComponentsPerPixel());
+
+ // Interpolate the moving image using the transform chain
+ LDDMMType::interp_cimg(imgMov, moving_pre_warp, warped_moving, false, true);
+
+ // Add the image pair to the helper
+ ofhelper.AddImagePair(imgFix, warped_moving, param.inputs[i].weight);
+ }
+ else
+ {
+ // Add to the helper object
+ ofhelper.AddImagePair(imgFix, imgMov, param.inputs[i].weight);
+ }
+ }
+
+ // Read the fixed-space mask
+ if(param.gradient_mask.size())
+ {
+ typedef typename OFHelperType::FloatImageType MaskType;
+ typename MaskType::Pointer imgMask =
+ ReadImageViaCache<MaskType>(param.gradient_mask);
+
+ ofhelper.SetGradientMask(imgMask);
+ }
+
+ // Read the moving-space mask
+ if(param.moving_mask.size())
+ {
+ typedef typename OFHelperType::FloatImageType MaskType;
+ typename MaskType::Pointer imgMovMask =
+ ReadImageViaCache<MaskType>(param.moving_mask);
+
+ if(moving_pre_warp.IsNotNull())
+ {
+ // Create an image to store the warp
+ typename MaskType::Pointer warped_moving_mask;
+ LDDMMType::alloc_img(warped_moving_mask, moving_pre_warp);
+
+ // Interpolate the moving image using the transform chain
+ LDDMMType::interp_img(imgMovMask, moving_pre_warp, warped_moving_mask, false, true);
+
+ // Add the warped mask to the helper
+ ofhelper.SetMovingMask(warped_moving_mask);
+ }
+ else
+ {
+ // Add the mask to the helper object
+ ofhelper.SetMovingMask(imgMovMask);
+ }
+ }
+
+ // Generate the optimized composite images. For the NCC metric, we add random noise to
+ // the composite images, specified in units of the interquartile intensity range.
+ double noise = (param.metric == GreedyParameters::NCC) ? param.ncc_noise_factor : 0.0;
+
+ // Build the composite images
+ ofhelper.BuildCompositeImages(noise);
+
+ // If the metric is NCC, then also apply special processing to the gradient masks
+ if(param.metric == GreedyParameters::NCC)
+ ofhelper.DilateCompositeGradientMasksForNCC(array_caster<VDim>::to_itkSize(param.metric_radius));
+}
+
+#include <vnl/algo/vnl_lbfgs.h>
+
+template <unsigned int VDim, typename TReal>
+vnl_matrix<double>
+GreedyApproach<VDim, TReal>
+::MapAffineToPhysicalRASSpace(
+ OFHelperType &of_helper, int level,
+ LinearTransformType *tran)
+{
+ // Map the transform to NIFTI units
+ vnl_matrix<double> T_fix, T_mov, Q, A;
+ vnl_vector<double> s_fix, s_mov, p, b;
+
+ GetVoxelSpaceToNiftiSpaceTransform(of_helper.GetReferenceSpace(level), T_fix, s_fix);
+ GetVoxelSpaceToNiftiSpaceTransform(of_helper.GetMovingReferenceSpace(level), T_mov, s_mov);
+
+ itk_matrix_to_vnl_matrix(tran->GetMatrix(), A);
+ itk_vector_to_vnl_vector(tran->GetOffset(), b);
+
+ Q = T_mov * A * vnl_matrix_inverse<double>(T_fix);
+ p = T_mov * b + s_mov - Q * s_fix;
+
+ vnl_matrix<double> Qp(VDim+1, VDim+1);
+ Qp.set_identity();
+ for(int i = 0; i < VDim; i++)
+ {
+ Qp(i, VDim) = p(i);
+ for(int j = 0; j < VDim; j++)
+ Qp(i,j) = Q(i,j);
+ }
+
+ return Qp;
+}
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>
+::MapPhysicalRASSpaceToAffine(
+ OFHelperType &of_helper, int level,
+ vnl_matrix<double> &Qp,
+ LinearTransformType *tran)
+{
+ // Map the transform to NIFTI units
+ vnl_matrix<double> T_fix, T_mov, Q(VDim, VDim), A;
+ vnl_vector<double> s_fix, s_mov, p(VDim), b;
+
+ GetVoxelSpaceToNiftiSpaceTransform(of_helper.GetReferenceSpace(level), T_fix, s_fix);
+ GetVoxelSpaceToNiftiSpaceTransform(of_helper.GetMovingReferenceSpace(level), T_mov, s_mov);
+
+ for(int i = 0; i < VDim; i++)
+ {
+ p(i) = Qp(i, VDim);
+ for(int j = 0; j < VDim; j++)
+ Q(i,j) = Qp(i,j);
+ }
+
+ // A = vnl_matrix_inverse<double>(T_mov) * (Q * T_fix);
+ // b = vnl_matrix_inverse<double>(T_mov) * (p - s_mov + Q * s_fix);
+ A=vnl_svd<double>(T_mov).solve(Q * T_fix);
+ b=vnl_svd<double>(T_mov).solve(p - s_mov + Q * s_fix);
+
+ typename LinearTransformType::MatrixType tran_A;
+ typename LinearTransformType::OffsetType tran_b;
+
+ vnl_matrix_to_itk_matrix(A, tran_A);
+ vnl_vector_to_itk_vector(b, tran_b);
+
+ tran->SetMatrix(tran_A);
+ tran->SetOffset(tran_b);
+}
+
+template <unsigned int VDim, typename TReal>
+void
+GreedyApproach<VDim, TReal>
+::RecordMetricValue(double val)
+{
+ if(m_MetricLog.size())
+ m_MetricLog.back().push_back(val);
+}
+
+/**
+ * Find a plane of symmetry in an image
+ */
+/*
+template <unsigned int VDim, typename TReal>
+vnl_vector<double>
+GreedyApproach<VDim, TReal>
+::FindSymmetryPlane(ImageType *image, int N, int n_search_pts)
+{
+ typedef vnl_vector_fixed<double, 3> Vec3;
+ typedef vnl_matrix_fixed<double, 3, 3> Mat3;
+
+ // Loop over direction on a sphere, using the Saff & Kuijlaars algorithm
+ // https://perswww.kuleuven.be/~u0017946/publications/Papers97/art97a-Saff-Kuijlaars-MI/Saff-Kuijlaars-MathIntel97.pdf
+ double phi = 0.0;
+ double spiral_const = 3.6 / sqrt(N);
+ for(int k = 0; k < n_sphere_pts; k++)
+ {
+ // Height of the k-th point
+ double cos_theta = -1 * (2 * k) / (N - 1);
+ double sin_theta = sqrt(1 - cos_theta * cos_theta);
+
+ // Phase of the k-th point
+ if(k > 0 && k < N-1)
+ phi = fmod(phi_last + spiral_const / sin_theta, vnl_math::pi * 2);
+ else
+ phi = 0.0;
+
+ // We now have the polar coordinates of the points, get cartesian coordinates
+ Vec3 q;
+ q[0] = sin_theta * cos(phi);
+ q[1] = sin_theta * sin(phi);
+ q[2] = cos_theta;
+
+ // Now q * (x,y,z) = 0 defines a plane through the origin. We will test whether the image
+ // is symmetric across this plane. We first construct the reflection matrix
+ Mat3 R;
+ R(0,0) = 1 - q[0] * q[0]; R(0,1) = -2 * q[1] * q[0]; R(0,2) = -2 * q[2] * q[0];
+ R(1,0) = -2 * q[0] * q[1]; R(1,1) = 1 - q[1] * q[1]; R(1,2) = -2 * q[2] * q[1];
+ R(2,0) = -2 * q[0] * q[2]; R(2,1) = -2 * q[1] * q[2]; R(2,2) = 1 - q[2] * q[2];
+
+ // We must find the reasonable range of intercepts to search for. An intercept is reasonable
+ // if the plane cuts the image volume in at least a 80/20 ratio (let's say)
+
+
+ // This is a test axis of rotation. We will now measure the symmetry of the image across this axis
+ // To do so, we will construct a series of flips across this direction
+
+ }
+}
+*/
+
+/**
+ * This method performs initial alignment by first searching for a plane of symmetry
+ * in each image, and then finding the transform between the planes of symmetry.
+ *
+ * The goal is to have an almost sure-fire method for brain alignment, yet generic
+ * enough to work for other data as well.
+ */
+/*
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::SymmetrySearch(GreedyParameters ¶m, int level, OFHelperType *of_helper)
+{
+
+}
+*/
+
+
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::RunAffine(GreedyParameters ¶m)
+{
+ // Create an optical flow helper object
+ OFHelperType of_helper;
+
+ // Set the scaling factors for multi-resolution
+ of_helper.SetDefaultPyramidFactors(param.iter_per_level.size());
+
+ // Add random sampling jitter for affine stability at voxel edges
+ of_helper.SetJitterSigma(param.affine_jitter);
+
+ // Read the image pairs to register - this will also build the composite pyramids
+ ReadImages(param, of_helper);
+
+ // Matrix describing current transform in physical space
+ vnl_matrix<double> Q_physical;
+
+ // The number of resolution levels
+ unsigned nlevels = param.iter_per_level.size();
+
+ // Clear the metric log
+ m_MetricLog.clear();
+
+ // Iterate over the resolution levels
+ for(unsigned int level = 0; level < nlevels; ++level)
+ {
+ // Add stage to metric log
+ m_MetricLog.push_back(std::vector<double>());
+
+ // Define the affine cost function
+ AbstractAffineCostFunction *pure_acf, *acf;
+ if(param.affine_dof == GreedyParameters::DOF_RIGID)
+ {
+ RigidCostFunction *rigid_acf = new RigidCostFunction(¶m, this, level, &of_helper);
+ acf = new ScalingCostFunction(
+ rigid_acf,
+ rigid_acf->GetOptimalParameterScaling(
+ of_helper.GetReferenceSpace(level)->GetBufferedRegion().GetSize()));
+ pure_acf = rigid_acf;
+ }
+ else
+ {
+ // PureAffineCostFunction *affine_acf = new PureAffineCostFunction(¶m, level, &of_helper);
+ PhysicalSpaceAffineCostFunction *affine_acf = new PhysicalSpaceAffineCostFunction(¶m, this, level, &of_helper);
+ acf = new ScalingCostFunction(
+ affine_acf,
+ affine_acf->GetOptimalParameterScaling(
+ of_helper.GetReferenceSpace(level)->GetBufferedRegion().GetSize()));
+ pure_acf = affine_acf;
+ }
+
+ // Current transform
+ typename LinearTransformType::Pointer tLevel = LinearTransformType::New();
+
+ // Set up the initial transform
+ if(level == 0)
+ {
+ // Get the coefficients corresponding to the identity transform in voxel space
+ tLevel->SetIdentity();
+ vnl_vector<double> xIdent = acf->GetCoefficients(tLevel);
+
+ // Use the provided initial affine as the starting point
+ if(param.affine_init_mode == RAS_FILENAME)
+ {
+ // Read the initial affine transform from a file
+ vnl_matrix<double> Qp = this->ReadAffineMatrixViaCache(param.affine_init_transform);
+
+ // Map this to voxel space
+ MapPhysicalRASSpaceToAffine(of_helper, level, Qp, tLevel);
+ }
+ else if(param.affine_init_mode == RAS_IDENTITY)
+ {
+ // Physical space transform
+ vnl_matrix<double> Qp(VDim+1, VDim+1); Qp.set_identity();
+
+ // Map this to voxel space
+ MapPhysicalRASSpaceToAffine(of_helper, level, Qp, tLevel);
+ }
+
+ // Get the new coefficients
+ vnl_vector<double> xInit = acf->GetCoefficients(tLevel);
+
+ // If the voxel-space transform is identity, apply a little bit of jitter
+ if((xIdent - xInit).inf_norm() < 1e-4)
+ {
+ // Apply jitter
+ vnl_random rndy(12345);
+ for (unsigned i = 0; i < xInit.size(); i++)
+ xInit[i] += rndy.drand32(-0.4, 0.4);
+
+ // Map back into transform format
+ acf->GetTransform(xInit, tLevel);
+ }
+
+ // If the uses asks for rigid search, do it!
+ if(param.rigid_search.iterations > 0)
+ {
+ // Create a pure rigid acf
+ RigidCostFunction search_fun(¶m, this, level, &of_helper);
+
+ // Get the parameters corresponding to the current transform
+ vnl_vector<double> xRigidInit = search_fun.GetCoefficients(tLevel);
+
+ // Get center of fixed and moving images in physical space
+ itk::Point<double, VDim> ctr_Fixed, ctr_Moving;
+ itk::Index<VDim> idx_Fixed = of_helper.GetReferenceSpace(level)->GetBufferedRegion().GetIndex();
+ for(int d = 0; d < 3; d++)
+ idx_Fixed[d] += of_helper.GetReferenceSpace(level)->GetBufferedRegion().GetSize()[d] / 2;
+ of_helper.GetReferenceSpace(level)->TransformIndexToPhysicalPoint(idx_Fixed, ctr_Fixed);
+
+ itk::Index<VDim> idx_Moving = of_helper.GetMovingReferenceSpace(level)->GetBufferedRegion().GetIndex();
+ for(int d = 0; d < 3; d++)
+ idx_Moving[d] += of_helper.GetMovingReferenceSpace(level)->GetBufferedRegion().GetSize()[d] / 2;
+ of_helper.GetMovingReferenceSpace(level)->TransformIndexToPhysicalPoint(idx_Moving, ctr_Moving);
+
+ // At random, try a whole bunch of transforms, around 5 degrees
+ vnl_random randy(12345);
+
+ // TODO: make a heap of k best tries
+ double fBest;
+ vnl_vector<double> xBest = xRigidInit;
+ search_fun.compute(xBest, &fBest, NULL);
+
+ // Report the initial best
+ std::cout << "Rigid search -> Initial best: " << fBest << " " << xBest << std::endl;
+
+ for(int i = 0; i < param.rigid_search.iterations; i++)
+ {
+ // Get random coefficient
+ // Compute a random rotation
+ vnl_vector<double> xTry = search_fun.GetRandomCoeff(xRigidInit, randy,
+ param.rigid_search.sigma_angle,
+ param.rigid_search.sigma_xyz,
+ ctr_Fixed.GetVnlVector(), ctr_Moving.GetVnlVector());
+
+ // Evaluate this transform
+ double f;
+ search_fun.compute(xTry, &f, NULL);
+
+ if(f < fBest)
+ {
+ fBest = f;
+ xBest = xTry;
+ std::cout << "New best: " << fBest << " " << xBest << std::endl;
+ }
+ }
+
+ xInit = xBest;
+ search_fun.GetTransform(xInit, tLevel);
+ }
+ }
+ else
+ {
+ // Update the transform from the last level
+ MapPhysicalRASSpaceToAffine(of_helper, level, Q_physical, tLevel);
+ }
+
+ // Test derivatives
+ // Convert to a parameter vector
+ vnl_vector<double> xLevel = acf->GetCoefficients(tLevel.GetPointer());
+
+ if(param.flag_debug_deriv)
+ {
+ // Test the gradient computation
+ vnl_vector<double> xGrad(acf->get_number_of_unknowns(), 0.0);
+ double f0;
+ acf->compute(xLevel, &f0, &xGrad);
+
+ // Propagate the jitter to the transform
+ Q_physical = MapAffineToPhysicalRASSpace(of_helper, level, tLevel);
+ std::cout << "Initial RAS Transform: " << std::endl << Q_physical << std::endl;
+
+ printf("ANL gradient: ");
+ for (unsigned i = 0; i < xGrad.size(); i++)
+ printf("%11.4f ", xGrad[i]);
+ printf("\n");
+
+ vnl_vector<double> xGradN(acf->get_number_of_unknowns(), 0.0);
+ for(int i = 0; i < acf->get_number_of_unknowns(); i++)
+ {
+ // double eps = (i % VDim == 0) ? 1.0e-2 : 1.0e-5;
+ double eps = param.deriv_epsilon;
+ double f1, f2, f3, f4;
+ vnl_vector<double> x1 = xLevel, x2 = xLevel, x3 = xLevel, x4 = xLevel;
+ x1[i] -= 2 * eps; x2[i] -= eps; x3[i] += eps; x4[i] += 2 * eps;
+
+ // Four-point derivative computation
+ acf->compute(x1, &f1, NULL);
+ acf->compute(x2, &f2, NULL);
+ acf->compute(x3, &f3, NULL);
+ acf->compute(x4, &f4, NULL);
+
+ xGradN[i] = (f1 - 8 * f2 + 8 * f3 - f4) / (12 * eps);
+ }
+
+ printf("NUM gradient: ");
+ for (unsigned i = 0; i < xGradN.size(); i++)
+ printf("%11.4f ", xGradN[i]);
+ printf("\n");
+
+ std::cout << "f = " << f0 << std::endl;
+
+ acf->GetTransform(xGrad, tLevel.GetPointer());
+ std::cout << "A: " << std::endl
+ << tLevel->GetMatrix() << std::endl
+ << tLevel->GetOffset() << std::endl;
+
+ acf->GetTransform(xGradN, tLevel.GetPointer());
+ std::cout << "N: " << std::endl
+ << tLevel->GetMatrix() << std::endl
+ << tLevel->GetOffset() << std::endl;
+ }
+
+ if(param.flag_debug_aff_obj)
+ {
+ for(int k = -50; k < 50; k++)
+ {
+ printf("Obj\t%d\t", k);
+ for(int i = 0; i < acf->get_number_of_unknowns(); i++)
+ {
+ vnl_vector<double> xTest = xLevel;
+ xTest[i] = xLevel[i] + k * param.deriv_epsilon;
+ double f; acf->compute(xTest, &f, NULL);
+ printf("%12.8f\t", f);
+ }
+ printf("\n");
+ }
+ {
+ vnl_vector<double> xTest = xLevel;
+ {
+ }
+ printf("\n");
+ }
+ }
+
+ // Run the minimization
+ if(param.iter_per_level[level] > 0)
+ {
+ if(param.flag_powell)
+ {
+ // Set up the optimizer
+ vnl_powell *optimizer = new vnl_powell(acf);
+ optimizer->set_f_tolerance(1e-9);
+ optimizer->set_x_tolerance(1e-4);
+ optimizer->set_g_tolerance(1e-6);
+ optimizer->set_trace(true);
+ optimizer->set_verbose(true);
+ optimizer->set_max_function_evals(param.iter_per_level[level]);
+
+ optimizer->minimize(xLevel);
+ delete optimizer;
+
+ }
+ else
+ {
+ // Set up the optimizer
+ vnl_lbfgs *optimizer = new vnl_lbfgs(*acf);
+ optimizer->set_f_tolerance(1e-9);
+ optimizer->set_x_tolerance(1e-4);
+ optimizer->set_g_tolerance(1e-6);
+ optimizer->set_trace(true);
+ optimizer->set_max_function_evals(param.iter_per_level[level]);
+
+ optimizer->minimize(xLevel);
+ delete optimizer;
+ }
+
+ // Did the registration succeed?
+ if(xLevel.size() > 0)
+ {
+ // Get the final transform
+ typename LinearTransformType::Pointer tFinal = LinearTransformType::New();
+ acf->GetTransform(xLevel, tFinal.GetPointer());
+ Q_physical = MapAffineToPhysicalRASSpace(of_helper, level, tFinal);
+ }
+ else
+ {
+ // Use the pre-initialization transform parameters
+ Q_physical = MapAffineToPhysicalRASSpace(of_helper, level, tLevel);
+ }
+ }
+
+ std::cout << "Final RAS Transform: " << std::endl << Q_physical << std::endl;
+
+ delete acf;
+ delete pure_acf;
+ }
+
+ // Write the final affine transform
+ this->WriteAffineMatrixViaCache(param.output, Q_physical);
+ return 0;
+}
+
+#include "itkStatisticsImageFilter.h"
+
+
+/**
+ * This is the main function of the GreedyApproach algorithm
+ */
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::RunDeformable(GreedyParameters ¶m)
+{
+ // Create an optical flow helper object
+ OFHelperType of_helper;
+
+ // Set the scaling factors for multi-resolution
+ of_helper.SetDefaultPyramidFactors(param.iter_per_level.size());
+
+ // Read the image pairs to register
+ ReadImages(param, of_helper);
+
+ // An image pointer desribing the current estimate of the deformation
+ VectorImagePointer uLevel = NULL;
+
+ // The number of resolution levels
+ unsigned nlevels = param.iter_per_level.size();
+
+ // Iterate over the resolution levels
+ for(unsigned int level = 0; level < nlevels; ++level)
+ {
+ // Reference space
+ ImageBaseType *refspace = of_helper.GetReferenceSpace(level);
+
+ // Smoothing factors for this level, in physical units
+ typename LDDMMType::Vec sigma_pre_phys =
+ of_helper.GetSmoothingSigmasInPhysicalUnits(level, param.sigma_pre.sigma,
+ param.sigma_pre.physical_units);
+
+ typename LDDMMType::Vec sigma_post_phys =
+ of_helper.GetSmoothingSigmasInPhysicalUnits(level, param.sigma_post.sigma,
+ param.sigma_post.physical_units);
+
+ // Report the smoothing factors used
+ std::cout << "LEVEL " << level+1 << " of " << nlevels << std::endl;
+ std::cout << " Smoothing sigmas: " << sigma_pre_phys << ", " << sigma_post_phys << std::endl;
+
+ // Set up timers for different critical components of the optimization
+ itk::TimeProbe tm_Gradient, tm_Gaussian1, tm_Gaussian2, tm_Iteration;
+
+ // Intermediate images
+ ImagePointer iTemp = ImageType::New();
+ VectorImagePointer viTemp = VectorImageType::New();
+ VectorImagePointer uk = VectorImageType::New();
+ VectorImagePointer uk1 = VectorImageType::New();
+
+ // Allocate the intermediate data
+ LDDMMType::alloc_vimg(uk, refspace);
+ LDDMMType::alloc_img(iTemp, refspace);
+ LDDMMType::alloc_vimg(viTemp, refspace);
+ LDDMMType::alloc_vimg(uk1, refspace);
+
+ // Initialize the deformation field from last iteration
+ if(uLevel.IsNotNull())
+ {
+ LDDMMType::vimg_resample_identity(uLevel, refspace, uk);
+ LDDMMType::vimg_scale_in_place(uk, 2.0);
+ uLevel = uk;
+ }
+ else if(param.affine_init_mode != VOX_IDENTITY)
+ {
+ typename LinearTransformType::Pointer tran = LinearTransformType::New();
+
+ if(param.affine_init_mode == RAS_FILENAME)
+ {
+ // Read the initial affine transform from a file
+ vnl_matrix<double> Qp = ReadAffineMatrixViaCache(param.affine_init_transform);
+
+ // Map this to voxel space
+ MapPhysicalRASSpaceToAffine(of_helper, level, Qp, tran);
+ }
+ else if(param.affine_init_mode == RAS_IDENTITY)
+ {
+ // Physical space transform
+ vnl_matrix<double> Qp(VDim+1, VDim+1); Qp.set_identity();
+
+ // Map this to voxel space
+ MapPhysicalRASSpaceToAffine(of_helper, level, Qp, tran);
+ }
+
+ // Create an initial warp
+ OFHelperType::AffineToField(tran, uk);
+ uLevel = uk;
+
+ itk::Index<VDim> test; test.Fill(24);
+ std::cout << "Index 24x24x24 maps to " << uk->GetPixel(test) << std::endl;
+ }
+
+ // Iterate for this level
+ for(unsigned int iter = 0; iter < param.iter_per_level[level]; iter++)
+ {
+ // Start the iteration timer
+ tm_Iteration.Start();
+
+ // The epsilon for this level
+ double eps= param.epsilon_per_level[level];
+
+ // Compute the gradient of objective
+ double total_energy;
+
+ if(param.metric == GreedyParameters::SSD)
+ {
+ // Begin gradient computation
+ tm_Gradient.Start();
+
+ vnl_vector<double> all_metrics =
+ of_helper.ComputeOpticalFlowField(level, uk, iTemp, uk1, eps) / eps;
+
+ // If there is a mask, multiply the gradient by the mask
+ if(param.gradient_mask.size())
+ LDDMMType::vimg_multiply_in_place(uk1, of_helper.GetGradientMask(level));
+
+ // End gradient computation
+ tm_Gradient.Stop();
+
+ printf("Lev:%2d Itr:%5d Met:[", level, iter);
+ total_energy = 0.0;
+ for (unsigned i = 0; i < all_metrics.size(); i++)
+ {
+ printf(" %8.6f", all_metrics[i]);
+ total_energy += all_metrics[i];
+ }
+ printf("] Tot: %8.6f\n", total_energy);
+ }
+
+ else if(param.metric == GreedyParameters::MI || param.metric == GreedyParameters::NMI)
+ {
+ // Begin gradient computation
+ tm_Gradient.Start();
+
+ vnl_vector<double> all_metrics =
+ of_helper.ComputeMIFlowField(level, param.metric == GreedyParameters::NMI, uk, iTemp, uk1, eps);
+
+ // If there is a mask, multiply the gradient by the mask
+ if(param.gradient_mask.size())
+ LDDMMType::vimg_multiply_in_place(uk1, of_helper.GetGradientMask(level));
+
+ // End gradient computation
+ tm_Gradient.Stop();
+
+ printf("Lev:%2d Itr:%5d Met:[", level, iter);
+ total_energy = 0.0;
+ for (unsigned i = 0; i < all_metrics.size(); i++)
+ {
+ printf(" %8.6f", all_metrics[i]);
+ total_energy += all_metrics[i];
+ }
+ printf("] Tot: %8.6f\n", total_energy);
+ }
+
+ else
+ {
+ itk::Size<VDim> radius = array_caster<VDim>::to_itkSize(param.metric_radius);
+
+ // Test derivative
+ // total_energy = of_helper.ComputeNCCMetricAndGradient(level, uk, uk1, radius, param.epsilon);
+
+ /*
+ if(iter == 0)
+ {
+
+ // Perform a derivative check!
+
+ itk::Index<VDim> test; test.Fill(24);
+ typename VectorImageType::PixelType vtest = uk->GetPixel(test), vv;
+
+ itk::ImageRegion<VDim> region = uk1->GetBufferedRegion();
+ // region.ShrinkByRadius(1);
+
+ double eps = param.epsilon;
+ for(int d = 0; d < VDim; d++)
+ {
+ vv.Fill(0.5); vv[d] -= eps; uk->FillBuffer(vv);
+ of_helper.ComputeNCCMetricImage(level, uk, radius, iTemp, uk1, 1.0);
+
+ double a1 = 0.0;
+ typedef itk::ImageRegionConstIterator<ImageType> Iter;
+ for(Iter it(iTemp, region); !it.IsAtEnd(); ++it)
+ {
+ a1 += it.Get();
+ }
+
+
+ vv.Fill(0.5); vv[d] += eps; uk->FillBuffer(vv);
+ of_helper.ComputeNCCMetricImage(level, uk, radius, iTemp, uk1, 1.0);
+
+ double a2 = 0.0;
+ typedef itk::ImageRegionConstIterator<ImageType> Iter;
+ for(Iter it(iTemp, region); !it.IsAtEnd(); ++it)
+ {
+ a2 += it.Get();
+ }
+
+ std::cout << "NUM:" << (a2 - a1) / (2*eps) << std::endl;
+
+ }
+
+ vv.Fill(0.5); uk->FillBuffer(vv);
+ total_energy = of_helper.ComputeNCCMetricImage(level, uk, radius, iTemp, uk1, 1.0);
+ for(int d = 0; d < VDim; d++)
+ {
+
+ double ader = 0.0;
+ typedef itk::ImageRegionConstIterator<VectorImageType> Iter;
+ for(Iter it(uk1, region); !it.IsAtEnd(); ++it)
+ {
+ ader += it.Get()[d];
+ }
+
+ // itk::Index<VDim> test; test.Fill(24);
+ // std::cout << "ANA:" << uk1->GetPixel(test) << std::endl;
+
+ std::cout << "ANA:" << ader << std::endl;
+ }
+ }
+ */
+
+ // Begin gradient computation
+ tm_Gradient.Start();
+
+ // Compute the metric - no need to multiply by the mask, this happens already in the NCC metric code
+ total_energy = of_helper.ComputeNCCMetricImage(level, uk, radius, iTemp, uk1, eps) / eps;
+
+ // End gradient computation
+ tm_Gradient.Stop();
+
+ printf("Level %5d, Iter %5d: Energy = %8.4f\n", level, iter, total_energy);
+ fflush(stdout);
+ }
+
+ // Dump the gradient image if requested
+ if(param.flag_dump_moving && 0 == iter % param.dump_frequency)
+ {
+ char fname[256];
+ sprintf(fname, "dump_gradient_lev%02d_iter%04d.nii.gz", level, iter);
+ LDDMMType::vimg_write(uk1, fname);
+ }
+
+ // We have now computed the gradient vector field. Next, we smooth it
+ tm_Gaussian1.Start();
+ LDDMMType::vimg_smooth_withborder(uk1, viTemp, sigma_pre_phys, 1);
+ tm_Gaussian1.Stop();
+
+ // After smoothing, compute the maximum vector norm and use it as a normalizing
+ // factor for the displacement field
+ if(param.time_step_mode == GreedyParameters::SCALE)
+ LDDMMType::vimg_normalize_to_fixed_max_length(viTemp, iTemp, eps, false);
+ else if (param.time_step_mode == GreedyParameters::SCALEDOWN)
+ LDDMMType::vimg_normalize_to_fixed_max_length(viTemp, iTemp, eps, true);
+
+ // Dump the smoothed gradient image if requested
+ if(param.flag_dump_moving && 0 == iter % param.dump_frequency)
+ {
+ char fname[256];
+ sprintf(fname, "dump_optflow_lev%02d_iter%04d.nii.gz", level, iter);
+ LDDMMType::vimg_write(viTemp, fname);
+ }
+
+ // Compute the updated deformation field - in uk1
+ LDDMMType::interp_vimg(uk, viTemp, 1.0, uk1);
+ LDDMMType::vimg_add_in_place(uk1, viTemp);
+
+ // Dump if requested
+ if(param.flag_dump_moving && 0 == iter % param.dump_frequency)
+ {
+ char fname[256];
+ sprintf(fname, "dump_uk1_lev%02d_iter%04d.nii.gz", level, iter);
+ LDDMMType::vimg_write(uk1, fname);
+ }
+
+ // Another layer of smoothing
+ tm_Gaussian2.Start();
+ LDDMMType::vimg_smooth_withborder(uk1, uk, sigma_post_phys, 1);
+ tm_Gaussian2.Stop();
+
+ tm_Iteration.Stop();
+ }
+
+ // Store the end result
+ uLevel = uk;
+
+ // Compute the jacobian of the deformation field
+ LDDMMType::field_jacobian_det(uk, iTemp);
+ TReal jac_min, jac_max;
+ LDDMMType::img_min_max(iTemp, jac_min, jac_max);
+ printf("END OF LEVEL %5d DetJac Range: %8.4f to %8.4f \n", level, jac_min, jac_max);
+
+ // Print timing information
+ printf(" Avg. Gradient Time : %6.4fs %5.2f%% \n", tm_Gradient.GetMean(), tm_Gradient.GetMean() * 100.0 / tm_Iteration.GetMean());
+ printf(" Avg. Gaussian Time : %6.4fs %5.2f%% \n", tm_Gaussian1.GetMean() + tm_Gaussian2.GetMean(),
+ (tm_Gaussian1.GetMean() + tm_Gaussian2.GetMean()) * 100.0 / tm_Iteration.GetMean());
+ printf(" Avg. Iteration Time : %6.4fs \n", tm_Iteration.GetMean());
+
+ }
+
+ // The transformation field is in voxel units. To work with ANTS, it must be mapped
+ // into physical offset units - just scaled by the spacing?
+
+ // Write the resulting transformation field
+ of_helper.WriteCompressedWarpInPhysicalSpace(nlevels - 1, uLevel, param.output.c_str(), param.warp_precision);
+
+ // If an inverse is requested, compute the inverse using the Chen 2008 fixed method.
+ // A modification of this method is that if convergence is slow, we take the square
+ // root of the forward transform.
+ //
+ // TODO: it would be more efficient to check the Lipschitz condition rather than
+ // the brute force approach below
+ //
+ // TODO: the maximum checks should only be done over the region where the warp is
+ // not going outside of the image. Right now, they are meaningless and we are doing
+ // extra work when computing the inverse.
+ if(param.inverse_warp.size())
+ {
+ // Compute the inverse
+ VectorImagePointer uInverse = VectorImageType::New();
+ LDDMMType::alloc_vimg(uInverse, uLevel);
+ of_helper.ComputeDeformationFieldInverse(uLevel, uInverse, param.inverse_exponent);
+
+ // Write the warp using compressed format
+ of_helper.WriteCompressedWarpInPhysicalSpace(nlevels - 1, uInverse, param.inverse_warp.c_str(), param.warp_precision);
+ }
+ return 0;
+}
+
+/**
+ * This function performs brute force search for similar patches. It generates a discrete displacement
+ * field where every pixel in the fixed image is matched to the most similar pixel in the moving image
+ * within a certain radius
+ */
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::RunBrute(GreedyParameters ¶m)
+{
+ // Check for valid parameters
+ if(param.metric != GreedyParameters::NCC)
+ {
+ std::cerr << "Brute force search requires NCC metric only" << std::endl;
+ return -1;
+ }
+
+ if(param.brute_search_radius.size() != VDim)
+ {
+ std::cerr << "Brute force search radius must be same dimension as the images" << std::endl;
+ return -1;
+ }
+
+ // Create an optical flow helper object
+ OFHelperType of_helper;
+
+ // No multi-resolution
+ of_helper.SetDefaultPyramidFactors(1);
+
+ // Read the image pairs to register
+ ReadImages(param, of_helper);
+
+ // Reference space
+ ImageBaseType *refspace = of_helper.GetReferenceSpace(0);
+
+ // Intermediate images
+ VectorImagePointer u_best = VectorImageType::New();
+ VectorImagePointer u_curr = VectorImageType::New();
+ ImagePointer m_curr = ImageType::New();
+ ImagePointer m_best = ImageType::New();
+
+ // Allocate the intermediate data
+ LDDMMType::alloc_vimg(u_best, refspace);
+ LDDMMType::alloc_vimg(u_curr, refspace);
+ LDDMMType::alloc_img(m_best, refspace);
+ LDDMMType::alloc_img(m_curr, refspace);
+
+ // Allocate m_best to a negative value
+ m_best->FillBuffer(-100.0);
+
+ // Create a neighborhood for computing offsets
+ itk::Neighborhood<float, VDim> dummy_nbr;
+ itk::Size<VDim> search_rad = array_caster<VDim>::to_itkSize(param.brute_search_radius);
+ itk::Size<VDim> metric_rad = array_caster<VDim>::to_itkSize(param.metric_radius);
+ dummy_nbr.SetRadius(search_rad);
+
+ // Iterate over all offsets
+ for(int k = 0; k < dummy_nbr.Size(); k++)
+ {
+ // Get the offset corresponding to this iteration
+ itk::Offset<VDim> offset = dummy_nbr.GetOffset(k);
+
+ // Fill the deformation field with this offset
+ typename LDDMMType::Vec vec_offset;
+ for(int i = 0; i < VDim; i++)
+ vec_offset[i] = offset[i];
+ u_curr->FillBuffer(vec_offset);
+
+ // Perform interpolation and metric computation
+ of_helper.ComputeNCCMetricImage(0, u_curr, metric_rad, m_curr);
+
+ // Temp: keep track of number of updates
+ unsigned long n_updates = 0;
+
+ // Out of laziness, just take a quick pass over the images
+ typename VectorImageType::RegionType rgn = refspace->GetBufferedRegion();
+ itk::ImageRegionIterator<VectorImageType> it_u(u_best, rgn);
+ itk::ImageRegionConstIterator<ImageType> it_m_curr(m_curr, rgn);
+ itk::ImageRegionIterator<ImageType> it_m_best(m_best, rgn);
+ for(; !it_m_best.IsAtEnd(); ++it_m_best, ++it_m_curr, ++it_u)
+ {
+ float v_curr = it_m_curr.Value();
+ if(v_curr > it_m_best.Value())
+ {
+ it_m_best.Set(v_curr);
+ it_u.Set(vec_offset);
+ ++n_updates;
+ }
+ }
+
+ std::cout << "offset: " << offset << " updates: " << n_updates << std::endl;
+ }
+
+ LDDMMType::vimg_write(u_best, param.output.c_str());
+ LDDMMType::img_write(m_best, "mbest.nii.gz");
+
+ return 0;
+}
+
+
+#include "itkWarpVectorImageFilter.h"
+#include "itkWarpImageFilter.h"
+#include "itkNearestNeighborInterpolateImageFunction.h"
+
+
+template <unsigned int VDim, typename TReal>
+void GreedyApproach<VDim, TReal>
+::ReadTransformChain(const std::vector<TransformSpec> &tran_chain,
+ ImageBaseType *ref_space,
+ VectorImagePointer &out_warp)
+{
+ // Create the initial transform and set it to zero
+ out_warp = VectorImageType::New();
+ LDDMMType::alloc_vimg(out_warp, ref_space);
+
+ // Read the sequence of transforms
+ for(int i = 0; i < tran_chain.size(); i++)
+ {
+ // Read the next parameter
+ std::string tran = tran_chain[i].filename;
+
+ // Determine if it's an affine transform
+ if(itk::ImageIOFactory::CreateImageIO(tran.c_str(), itk::ImageIOFactory::ReadMode))
+ {
+ // Create a temporary warp
+ VectorImagePointer warp_tmp = VectorImageType::New();
+ LDDMMType::alloc_vimg(warp_tmp, ref_space);
+
+ // Read the next warp
+ VectorImagePointer warp_i = VectorImageType::New();
+ LDDMMType::vimg_read(tran.c_str(), warp_i);
+
+ // Now we need to compose the current transform and the overall warp.
+ LDDMMType::interp_vimg(warp_i, out_warp, 1.0, warp_tmp, false, true);
+ LDDMMType::vimg_add_in_place(out_warp, warp_tmp);
+ }
+ else
+ {
+ // Read the transform as a matrix
+ vnl_matrix<double> mat = ReadAffineMatrixViaCache(tran_chain[i]);
+ vnl_matrix<double> A = mat.extract(VDim, VDim);
+ vnl_vector<double> b = mat.get_column(VDim).extract(VDim), q;
+
+ // TODO: stick this in a filter to take advantage of threading!
+ typedef itk::ImageRegionIteratorWithIndex<VectorImageType> IterType;
+ for(IterType it(out_warp, out_warp->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ itk::Point<double, VDim> pt, pt2;
+ typename VectorImageType::IndexType idx = it.GetIndex();
+
+ // Get the physical position
+ // TODO: this calls IsInside() internally, which limits efficiency
+ out_warp->TransformIndexToPhysicalPoint(idx, pt);
+
+ // Add the displacement (in DICOM coordinates) and
+ for(int i = 0; i < VDim; i++)
+ pt2[i] = pt[i] + it.Value()[i];
+
+ // Switch to NIFTI coordinates
+ pt2[0] = -pt2[0]; pt2[1] = -pt2[1];
+
+ // Apply the matrix - get the transformed coordinate in DICOM space
+ q = A * pt2.GetVnlVector() + b;
+ q[0] = -q[0]; q[1] = -q[1];
+
+ // Compute the difference in DICOM space
+ for(int i = 0; i < VDim; i++)
+ it.Value()[i] = q[i] - pt[i];
+ }
+ }
+ }
+}
+
+#include "itkBinaryThresholdImageFilter.h"
+//#include "itkRecursiveGaussianImageFilter.h"
+#include "itkSmoothingRecursiveGaussianImageFilter.h"
+#include "itkNaryFunctorImageFilter.h"
+
+template <class TInputImage, class TOutputImage>
+class NaryLabelVotingFunctor
+{
+public:
+ typedef NaryLabelVotingFunctor<TInputImage,TOutputImage> Self;
+ typedef typename TInputImage::PixelType InputPixelType;
+ typedef typename TOutputImage::PixelType OutputPixelType;
+ typedef std::vector<OutputPixelType> LabelArray;
+
+ NaryLabelVotingFunctor(const LabelArray &labels)
+ : m_LabelArray(labels), m_Size(labels.size()) {}
+
+ NaryLabelVotingFunctor() : m_Size(0) {}
+
+
+ OutputPixelType operator() (const std::vector<InputPixelType> &pix)
+ {
+ InputPixelType best_val = pix[0];
+ int best_index = 0;
+ for(int i = 1; i < m_Size; i++)
+ if(pix[i] > best_val)
+ {
+ best_val = pix[i];
+ best_index = i;
+ }
+
+ return m_LabelArray[best_index];
+ }
+
+ bool operator != (const Self &other)
+ { return other.m_LabelArray != m_LabelArray; }
+
+protected:
+ LabelArray m_LabelArray;
+ int m_Size;
+};
+
+#include "itkMeshFileReader.h"
+#include "itkMeshFileWriter.h"
+#include "itkMesh.h"
+#include "itkTransformMeshFilter.h"
+
+template <unsigned int VDim, typename TArray>
+class PhysicalCoordinateTransform
+{
+ static void ras_to_lps(const TArray &src, TArray &trg) {}
+ static void lps_to_ras(const TArray &src, TArray &trg) {}
+};
+
+template <typename TArray>
+class PhysicalCoordinateTransform<2, TArray>
+{
+public:
+ static void ras_to_lps(const TArray &src, TArray &trg)
+ {
+ trg[0] = -src[0];
+ trg[1] = -src[1];
+ }
+
+ static void lps_to_ras(const TArray &src, TArray &trg)
+ {
+ trg[0] = -src[0];
+ trg[1] = -src[1];
+ }
+};
+
+template <typename TArray>
+class PhysicalCoordinateTransform<3, TArray>
+{
+public:
+ static void ras_to_lps(const TArray &src, TArray &trg)
+ {
+ trg[0] = -src[0];
+ trg[1] = -src[1];
+ trg[2] = src[2];
+ }
+
+ static void lps_to_ras(const TArray &src, TArray &trg)
+ {
+ trg[0] = -src[0];
+ trg[1] = -src[1];
+ trg[2] = src[2];
+ }
+};
+
+template <typename TArray>
+class PhysicalCoordinateTransform<4, TArray>
+{
+public:
+ static void ras_to_lps(const TArray &src, TArray &trg)
+ {
+ trg[0] = -src[0];
+ trg[1] = -src[1];
+ trg[2] = src[2];
+ trg[3] = src[3];
+ }
+
+ static void lps_to_ras(const TArray &src, TArray &trg)
+ {
+ trg[0] = -src[0];
+ trg[1] = -src[1];
+ trg[2] = src[2];
+ trg[3] = src[3];
+ }
+};
+
+
+
+template <unsigned int VDim, typename TReal>
+class WarpMeshTransformFunctor : public itk::DataObject
+{
+public:
+ typedef WarpMeshTransformFunctor<VDim, TReal> Self;
+ typedef itk::DataObject Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ itkTypeMacro(WarpMeshTransformFunctor, itk::DataObject)
+ itkNewMacro(Self)
+
+ typedef GreedyApproach<VDim, TReal> GreedyAPI;
+ typedef typename GreedyAPI::VectorImageType VectorImageType;
+ typedef typename GreedyAPI::ImageBaseType ImageBaseType;
+ typedef FastLinearInterpolator<VectorImageType, TReal, VDim> FastInterpolator;
+ typedef itk::ContinuousIndex<TReal, VDim> CIndexType;
+ typedef itk::Point<TReal, VDim> PointType;
+
+ void SetWarp(VectorImageType *warp)
+ {
+ if(m_Interpolator) delete m_Interpolator;
+ m_Interpolator = new FastInterpolator(warp);
+ }
+
+ void SetReferenceSpace(ImageBaseType *ref)
+ {
+ m_ReferenceSpace = ref;
+ }
+
+ PointType TransformPoint(const PointType &x)
+ {
+ // Our convention is to use NIFTI/RAS coordinates for meshes, whereas ITK
+ // uses the DICOM/LPS convention. We transform point to LPS first
+ PointType x_lps, phi_x;
+
+ PhysicalCoordinateTransform<VDim, PointType>::ras_to_lps(x, x_lps);
+
+ CIndexType cix;
+ typename VectorImageType::PixelType vec;
+ vec.Fill(0.0);
+ m_ReferenceSpace->TransformPhysicalPointToContinuousIndex(x_lps, cix);
+ m_Interpolator->Interpolate(cix.GetDataPointer(), &vec);
+
+ for(int d = 0; d < VDim; d++)
+ {
+ phi_x[d] = vec[d] + x_lps[d];
+ }
+
+
+ PhysicalCoordinateTransform<VDim, PointType>::lps_to_ras(phi_x, phi_x);
+
+ return phi_x;
+ }
+
+protected:
+
+ WarpMeshTransformFunctor() { m_Interpolator = NULL; }
+ ~WarpMeshTransformFunctor()
+ {
+ if(m_Interpolator)
+ delete m_Interpolator;
+ }
+
+private:
+
+ typename ImageBaseType::Pointer m_ReferenceSpace;
+ FastInterpolator *m_Interpolator;
+
+};
+
+/**
+ * Run the reslice code - simply apply a warp or set of warps to images
+ */
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::RunReslice(GreedyParameters ¶m)
+{
+ GreedyResliceParameters r_param = param.reslice_param;
+
+ // Check the parameters
+ if(!r_param.ref_image.size())
+ throw GreedyException("A reference image (-rf) option is required for reslice commands");
+
+ if(r_param.images.size() + r_param.meshes.size() == 0
+ && !r_param.out_composed_warp.size())
+ throw GreedyException("No operation specified for reslice mode. "
+ "Use one of -rm, -rs or -rc commands.");
+
+ // Read the fixed as a plain image (we don't care if it's composite)
+ ImagePointer ref = ImageType::New();
+ LDDMMType::img_read(r_param.ref_image.c_str(), ref);
+ itk::ImageBase<VDim> *ref_space = ref;
+
+ // Read the transform chain
+ VectorImagePointer warp;
+ ReadTransformChain(param.reslice_param.transforms, ref_space, warp);
+
+ // Write the composite warp if requested
+ if(r_param.out_composed_warp.size())
+ {
+ LDDMMType::vimg_write(warp.GetPointer(), r_param.out_composed_warp.c_str(),
+ itk::ImageIOBase::FLOAT);
+ }
+
+ // Process image pairs
+ for(int i = 0; i < r_param.images.size(); i++)
+ {
+ const char *filename = r_param.images[i].moving.c_str();
+
+ // Handle the special case of multi-label images
+ if(r_param.images[i].interp.mode == InterpSpec::LABELWISE)
+ {
+ // The label image assumed to be an image of shorts
+ typedef itk::Image<short, VDim> LabelImageType;
+ typedef itk::ImageFileReader<LabelImageType> LabelReaderType;
+
+ // Create a reader
+ typename LabelReaderType::Pointer reader = LabelReaderType::New();
+ reader->SetFileName(filename);
+ reader->Update();
+ typename LabelImageType::Pointer moving = reader->GetOutput();
+
+ // Scan the unique labels in the image
+ std::set<short> label_set;
+ short *labels = moving->GetBufferPointer();
+ int n_pixels = moving->GetPixelContainer()->Size();
+
+ // Get the list of unique pixels
+ short last_pixel = 0;
+ for(int j = 0; j < n_pixels; j++)
+ {
+ short pixel = labels[j];
+ if(last_pixel != pixel || i == 0)
+ {
+ label_set.insert(pixel);
+ last_pixel = pixel;
+ if(label_set.size() > 1000)
+ throw GreedyException("Label wise interpolation not supported for image %s "
+ "which has over 1000 distinct labels", filename);
+ }
+ }
+
+ // Turn this set into an array
+ std::vector<short> label_array(label_set.begin(), label_set.end());
+
+ // Create a N-way voting filter
+ typedef NaryLabelVotingFunctor<ImageType, LabelImageType> VotingFunctor;
+ VotingFunctor vf(label_array);
+
+ typedef itk::NaryFunctorImageFilter<ImageType, LabelImageType, VotingFunctor> VotingFilter;
+ typename VotingFilter::Pointer fltVoting = VotingFilter::New();
+ fltVoting->SetFunctor(vf);
+
+ // Create a mini-pipeline of streaming filters
+ for(int j = 0; j < label_array.size(); j++)
+ {
+ // Set up a threshold filter for this label
+ typedef itk::BinaryThresholdImageFilter<LabelImageType, ImageType> ThresholdFilterType;
+ typename ThresholdFilterType::Pointer fltThreshold = ThresholdFilterType::New();
+ fltThreshold->SetInput(moving);
+ fltThreshold->SetLowerThreshold(label_array[j]);
+ fltThreshold->SetUpperThreshold(label_array[j]);
+ fltThreshold->SetInsideValue(1.0);
+ fltThreshold->SetOutsideValue(0.0);
+
+ // Set up a smoothing filter for this label
+ typedef itk::SmoothingRecursiveGaussianImageFilter<ImageType, ImageType> SmootherType;
+ typename SmootherType::Pointer fltSmooth = SmootherType::New();
+ fltSmooth->SetInput(fltThreshold->GetOutput());
+
+ // Work out the sigmas for the filter
+ if(r_param.images[i].interp.sigma.physical_units)
+ {
+ fltSmooth->SetSigma(r_param.images[i].interp.sigma.sigma);
+ }
+ else
+ {
+ typename SmootherType::SigmaArrayType sigma_array;
+ for(int d = 0; d < VDim; d++)
+ sigma_array[d] = r_param.images[i].interp.sigma.sigma * moving->GetSpacing()[d];
+ fltSmooth->SetSigmaArray(sigma_array);
+ }
+
+ // TODO: we should really be coercing the output into a vector image to speed up interpolation!
+ typedef FastWarpCompositeImageFilter<ImageType, ImageType, VectorImageType> InterpFilter;
+ typename InterpFilter::Pointer fltInterp = InterpFilter::New();
+ fltInterp->SetMovingImage(fltSmooth->GetOutput());
+ fltInterp->SetDeformationField(warp);
+ fltInterp->SetUsePhysicalSpace(true);
+
+ fltInterp->Update();
+
+ // Add to the voting filter
+ fltVoting->SetInput(j, fltInterp->GetOutput());
+ }
+
+ // TODO: test out streaming!
+ // Run this big pipeline
+ fltVoting->Update();
+
+ // Save
+ typedef itk::ImageFileWriter<LabelImageType> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetFileName(r_param.images[i].output.c_str());
+ writer->SetUseCompression(true);
+ writer->SetInput(fltVoting->GetOutput());
+ writer->Update();
+ }
+ else
+ {
+ // Read the input image
+ CompositeImagePointer moving, warped;
+ itk::ImageIOBase::IOComponentType comp =
+ LDDMMType::cimg_read(filename, moving);
+
+ // Allocate the warped image
+ LDDMMType::alloc_cimg(warped, ref_space, moving->GetNumberOfComponentsPerPixel());
+
+ // Perform the warp
+ LDDMMType::interp_cimg(moving, warp, warped,
+ r_param.images[i].interp.mode == InterpSpec::NEAREST,
+ true);
+
+ // Write, casting to the input component type
+ LDDMMType::cimg_write(warped, r_param.images[i].output.c_str(), comp);
+ }
+ }
+
+ // Process meshes
+ for(int i = 0; i < r_param.meshes.size(); i++)
+ {
+ typedef itk::Mesh<TReal, VDim> MeshType;
+ typedef itk::MeshFileReader<MeshType> MeshReader;
+ typename MeshReader::Pointer reader = MeshReader::New();
+ reader->SetFileName(r_param.meshes[i].fixed.c_str());
+
+ typedef WarpMeshTransformFunctor<VDim, TReal> TransformType;
+ typename TransformType::Pointer transform = TransformType::New();
+ transform->SetWarp(warp);
+ transform->SetReferenceSpace(ref);
+
+ typedef itk::TransformMeshFilter<MeshType, MeshType, TransformType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetTransform(transform);
+ filter->SetInput(reader->GetOutput());
+
+ typedef itk::MeshFileWriter<MeshType> MeshWriter;
+ typename MeshWriter::Pointer writer = MeshWriter::New();
+ writer->SetInput(filter->GetOutput());
+ writer->SetFileName(r_param.meshes[i].output.c_str());
+ writer->Update();
+ }
+
+
+
+ return 0;
+}
+
+template <unsigned int VDim, typename TReal>
+void GreedyApproach<VDim, TReal>
+::ComputeImageMoments(CompositeImageType *image,
+ const std::vector<double> &weights,
+ VecFx &m1, MatFx &m2)
+{
+ int n = image->GetNumberOfComponentsPerPixel();
+ TReal sum_I = 0.0;
+ m1.fill(0.0); m2.fill(0.0);
+
+ typedef itk::ImageRegionConstIteratorWithIndex<CompositeImageType> Iterator;
+ for(Iterator it(image, image->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ typedef itk::Point<TReal, VDim> PointType;
+ PointType p_lps, p_ras;
+ image->TransformIndexToPhysicalPoint(it.GetIndex(), p_lps);
+ PhysicalCoordinateTransform<VDim, PointType>::lps_to_ras(p_lps, p_ras);
+ VecFx X(p_ras.GetDataPointer());
+ MatFx X2 = outer_product(X, X);
+
+ typename CompositeImageType::PixelType pix = it.Get();
+
+ // Just weight the components of intensity by weight vector - this sort of makes sense?
+ TReal val = 0.0;
+ for(int k = 0; k < n; k++)
+ val += weights[k] * pix[k];
+
+ sum_I += val;
+ m1 += X * val;
+ m2 += X2 * val;
+ }
+
+ // Compute the mean and covariance from the sum of squares
+ m1 = m1 / sum_I;
+ m2 = (m2 - sum_I * outer_product(m1, m1)) / sum_I;
+}
+
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::RunAlignMoments(GreedyParameters ¶m)
+{
+ // Create an optical flow helper object
+ OFHelperType of_helper;
+
+ // No multi-resolution
+ of_helper.SetDefaultPyramidFactors(1);
+
+ // Read the image pairs to register
+ ReadImages(param, of_helper);
+
+ // Compute the moments of intertia for the fixed and moving images. For now
+ // this is done in an iterator loop, out of laziness. Should be converted to
+ // a filter if this whole moments business proves useful
+ VecFx m1f, m1m;
+ MatFx m2f, m2m;
+
+
+ std::cout << "--- MATCHING BY MOMENTS OF ORDER " << param.moments_order << " ---" << std::endl;
+
+ ComputeImageMoments(of_helper.GetFixedComposite(0), of_helper.GetWeights(), m1f, m2f);
+
+ std::cout << "Fixed Mean : " << m1f << std::endl;
+ std::cout << "Fixed Covariance : " << std::endl << m2f << std::endl;
+
+ ComputeImageMoments(of_helper.GetMovingComposite(0), of_helper.GetWeights(), m1m, m2m);
+
+ std::cout << "Moving Mean : " << m1m << std::endl;
+ std::cout << "Moving Covariance : " << std::endl << m2m << std::endl;
+
+ // This flag forces no rotation, only flip
+ if(param.moments_order == 1 || param.flag_moments_id_covariance)
+ {
+ m2f.set_identity();
+ m2m.set_identity();
+ }
+
+ // Decompose covariance matrices into eigenvectors and eigenvalues
+ vnl_vector<TReal> Df, Dm;
+ vnl_matrix<TReal> Vf, Vm;
+ vnl_symmetric_eigensystem_compute<TReal>(m2f, Vf, Df);
+ vnl_symmetric_eigensystem_compute<TReal>(m2m, Vm, Dm);
+
+ // Create a rigid registration problem
+ PhysicalSpaceAffineCostFunction cost_fn(¶m, this, 0, &of_helper);
+
+ // The best set of coefficients and the associated match value
+ vnl_vector<double> xBest;
+ TReal xBestMatch = vnl_numeric_traits<TReal>::maxval;
+
+ // Generate all possible flip matrices
+ int n_flip = 1 << VDim;
+ for(int k_flip = 0; k_flip < n_flip; k_flip++)
+ {
+ // If using first moments, ignore all flips, only allow identity
+ if(param.moments_order == 1 && k_flip != n_flip - 1)
+ continue;
+
+ // Generate the flip matrix
+ MatFx F(0.0);
+ for(int d = 0; d < VDim; d++)
+ F(d,d) = (k_flip & (1 << d)) ? 1 : -1;;
+
+ // Compute the rotation matrix - takes fixed coordinates into moving space
+ MatFx R = Vm * F * Vf.transpose();
+ VecFx b = m1m - R * m1f;
+
+ vnl_matrix<TReal> A(VDim+1, VDim+1, 0.0);
+ A.set_identity();
+ A.update(R, 0, 0);
+ for(int d= 0 ;d< VDim;d++)
+ A(d,VDim) = b[d];
+
+ // Ignore flips with the wrong determinant
+ double det_R = vnl_determinant(R);
+ if((param.moments_order == 2 && param.moments_flip_determinant == 1 && det_R < 0) ||
+ (param.moments_order == 2 && param.moments_flip_determinant == -1 && det_R > 0))
+ {
+ continue;
+ }
+
+ // Generate affine coefficients from the rotation and shift
+ vnl_vector<double> x(cost_fn.get_number_of_unknowns());
+ flatten_affine_transform(R, b, x.data_block());
+
+ // Compute similarity
+ double f = 0.0;
+ cost_fn.compute(x, &f, NULL);
+
+ std::cout << "Metric for flip " << F.get_diagonal() << " : " << f << std::endl;
+
+ // Compare
+ if(xBestMatch > f || xBest.size() == 0)
+ {
+ xBestMatch = f;
+ xBest = x;
+ }
+ }
+
+ // Save the best transform
+ typename LinearTransformType::Pointer tran = LinearTransformType::New();
+ cost_fn.GetTransform(xBest, tran);
+ vnl_matrix<double> Q_physical = MapAffineToPhysicalRASSpace(of_helper, 0, tran);
+ this->WriteAffineMatrixViaCache(param.output, Q_physical);
+
+ return 0;
+}
+
+/**
+ * Post-hoc warp inversion - the Achilles heel of non-symmetric registration :(
+ */
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::RunInvertWarp(GreedyParameters ¶m)
+{
+ // Read the warp as a transform chain
+ VectorImagePointer warp;
+
+ // Read the warp file
+ LDDMMType::vimg_read(param.invwarp_param.in_warp.c_str(), warp);
+
+ // Convert the warp file into voxel units from physical units
+ OFHelperType::PhysicalWarpToVoxelWarp(warp, warp, warp);
+
+
+ // Compute the inverse of the warp
+ VectorImagePointer uInverse = VectorImageType::New();
+ LDDMMType::alloc_vimg(uInverse, warp);
+ OFHelperType::ComputeDeformationFieldInverse(warp, uInverse, param.inverse_exponent, true);
+
+ // Write the warp using compressed format
+ OFHelperType::WriteCompressedWarpInPhysicalSpace(uInverse, warp, param.invwarp_param.out_warp.c_str(), param.warp_precision);
+
+ return 0;
+}
+
+/**
+ * Post-hoc warp root
+ */
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::RunRootWarp(GreedyParameters ¶m)
+{
+ // Read the warp as a transform chain
+ VectorImagePointer warp;
+
+ // Read the warp file
+ LDDMMType::vimg_read(param.warproot_param.in_warp.c_str(), warp);
+
+ // Convert the warp file into voxel units from physical units
+ OFHelperType::PhysicalWarpToVoxelWarp(warp, warp, warp);
+
+ // Compute the inverse of the warp
+ VectorImagePointer uInverse = VectorImageType::New();
+ LDDMMType::alloc_vimg(uInverse, warp);
+
+ // Do the root computation
+
+ // Create a copy of the forward warp
+ VectorImagePointer uForward = VectorImageType::New();
+ LDDMMType::alloc_vimg(uForward, warp);
+ LDDMMType::vimg_copy(warp, uForward);
+
+ // Create a working image for the square root computation
+ VectorImagePointer uWork = VectorImageType::New();
+ LDDMMType::alloc_vimg(uWork, warp);
+
+ // Compute the square root
+ for(int k = 0; k < param.warproot_param.exponent; k++)
+ {
+ for(int i = 0; i < 20; i++)
+ {
+ LDDMMType::interp_vimg(uInverse, uInverse, 1.0, uWork);
+ LDDMMType::vimg_scale_in_place(uWork, -1.0);
+ LDDMMType::vimg_add_scaled_in_place(uWork, uInverse, -1.0);
+ LDDMMType::vimg_add_in_place(uWork, uForward);
+
+ // Check the maximum delta
+ std::cout << "." << std::flush;
+
+ LDDMMType::vimg_add_scaled_in_place(uInverse, uWork, 0.5);
+ }
+
+ std::cout << "." << std::endl;
+ LDDMMType::vimg_copy(uInverse, uForward);
+ uInverse->FillBuffer(itk::NumericTraits<typename VectorImageType::PixelType>::Zero);
+ }
+
+ // Write the warp using compressed format
+ OFHelperType::WriteCompressedWarpInPhysicalSpace(uForward, warp, param.warproot_param.out_warp.c_str(), param.warp_precision);
+
+ return 0;
+}
+
+
+template <unsigned int VDim, typename TReal>
+void GreedyApproach<VDim, TReal>
+::AddCachedInputObject(std::string &string, itk::Object *object)
+{
+ m_ImageCache[string] = object;
+}
+
+template <unsigned int VDim, typename TReal>
+const typename GreedyApproach<VDim,TReal>::MetricLogType &
+GreedyApproach<VDim,TReal>
+::GetMetricLog() const
+{
+ return m_MetricLog;
+}
+
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::Run(GreedyParameters ¶m)
+{
+ switch(param.mode)
+ {
+ case GreedyParameters::GREEDY:
+ return Self::RunDeformable(param);
+ case GreedyParameters::AFFINE:
+ return Self::RunAffine(param);
+ case GreedyParameters::BRUTE:
+ return Self::RunBrute(param);
+ case GreedyParameters::MOMENTS:
+ return Self::RunAlignMoments(param);
+ case GreedyParameters::RESLICE:
+ return Self::RunReslice(param);
+ case GreedyParameters::INVERT_WARP:
+ return Self::RunInvertWarp(param);
+ case GreedyParameters::ROOT_WARP:
+ return Self::RunRootWarp(param);
+ }
+
+ return -1;
+}
+
+
+
+
+template class GreedyApproach<2, float>;
+template class GreedyApproach<3, float>;
+template class GreedyApproach<4, float>;
+template class GreedyApproach<2, double>;
+template class GreedyApproach<3, double>;
+template class GreedyApproach<4, double>;
diff --git a/Submodules/greedy/src/GreedyAPI.h b/Submodules/greedy/src/GreedyAPI.h
new file mode 100644
index 0000000..9a1a58d
--- /dev/null
+++ b/Submodules/greedy/src/GreedyAPI.h
@@ -0,0 +1,318 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef GREEDYAPI_H
+#define GREEDYAPI_H
+
+#include "GreedyParameters.h"
+#include "GreedyException.h"
+#include "lddmm_data.h"
+#include <vnl/vnl_cost_function.h>
+#include <vnl/vnl_random.h>
+#include <map>
+
+#include "itkCommand.h"
+
+template <typename T, unsigned int V> class MultiImageOpticalFlowHelper;
+
+namespace itk {
+ template <typename T, unsigned int D1, unsigned int D2> class MatrixOffsetTransformBase;
+
+}
+
+/**
+ * This is the top level class for the greedy software. It contains methods
+ * for deformable and affine registration.
+ */
+template <unsigned int VDim, typename TReal = double>
+class GreedyApproach
+{
+public:
+
+ typedef GreedyApproach<VDim, TReal> Self;
+
+ typedef LDDMMData<TReal, VDim> LDDMMType;
+ typedef typename LDDMMType::ImageBaseType ImageBaseType;
+ typedef typename LDDMMType::ImageType ImageType;
+ typedef typename LDDMMType::ImagePointer ImagePointer;
+ typedef typename LDDMMType::VectorImageType VectorImageType;
+ typedef typename LDDMMType::VectorImagePointer VectorImagePointer;
+ typedef typename LDDMMType::CompositeImageType CompositeImageType;
+ typedef typename LDDMMType::CompositeImagePointer CompositeImagePointer;
+
+ typedef vnl_vector_fixed<TReal, VDim> VecFx;
+ typedef vnl_matrix_fixed<TReal, VDim, VDim> MatFx;
+
+ typedef std::vector<std::vector<double> > MetricLogType;
+
+ typedef MultiImageOpticalFlowHelper<TReal, VDim> OFHelperType;
+
+ typedef itk::MatrixOffsetTransformBase<TReal, VDim, VDim> LinearTransformType;
+
+ struct ImagePair {
+ ImagePointer fixed, moving;
+ VectorImagePointer grad_moving;
+ double weight;
+ };
+
+
+ int Run(GreedyParameters ¶m);
+
+ int RunDeformable(GreedyParameters ¶m);
+
+ int RunAffine(GreedyParameters ¶m);
+
+ int RunBrute(GreedyParameters ¶m);
+
+ int RunReslice(GreedyParameters ¶m);
+
+ int RunInvertWarp(GreedyParameters ¶m);
+
+ int RunRootWarp(GreedyParameters ¶m);
+
+ int RunAlignMoments(GreedyParameters ¶m);
+
+ /**
+ * Add an image that is already in memory to the internal cache, and
+ * associate it with a filename. This provides a way for images already
+ * loaded in memory to be passed in to the Greedy API while using the
+ * standard parameter structures.
+ *
+ * Normally, images such as the fixed image are passed as part of the
+ * GreedyParameters object as filenames. For example, we might set
+ *
+ * param.inputs[0].fixed = "/tmp/goo.nii.gz";
+ *
+ * However, if we are linking to the greedy API from another program and
+ * already have the fixed image in memory, we can use the cache mechanism
+ * instead.
+ *
+ * greedyapi.AddCachedInputObject("FIXED-0", myimage);
+ * param.inputs[0].fixed = "FIXED-0";
+ *
+ * The API will check the cache before loading the image. The type of the
+ * object in the cache must match the type of the object expected internally,
+ * which is VectorImage for most images. If not, an exception will be
+ * thrown.
+ *
+ * Note that the cache does not use smart pointers to refer to the objects
+ * so it's the caller's responsibility to keep the object pointed to while
+ * the API is being used.
+ */
+ void AddCachedInputObject(std::string &string, itk::Object *object);
+
+ /**
+ * Get the metric log - values of metric per level. Can be called from
+ * callback functions and observers
+ */
+ const MetricLogType &GetMetricLog() const;
+
+
+
+protected:
+
+ typedef std::map<std::string, itk::Object *> ImageCache;
+ ImageCache m_ImageCache;
+
+ // A log of metric values used during registration - so metric can be looked up
+ // in the callbacks to RunAffine, etc.
+ std::vector< std::vector<double> > m_MetricLog;
+
+ // This function reads the image from disk, or from a memory location mapped to a
+ // string. The first approach is used by the command-line interface, and the second
+ // approach is used by the API, allowing images to be passed from other software
+ template <class TImage>
+ itk::SmartPointer<TImage> ReadImageViaCache(const std::string &filename);
+
+ vnl_matrix<double> ReadAffineMatrixViaCache(const TransformSpec &ts);
+
+ void WriteAffineMatrixViaCache(const std::string &filename, const vnl_matrix<double> &Qp);
+
+ void ReadImages(GreedyParameters ¶m, OFHelperType &ofhelper);
+
+ void ReadTransformChain(const std::vector<TransformSpec> &tran_chain,
+ ImageBaseType *ref_space,
+ VectorImagePointer &out_warp);
+
+ static vnl_matrix<double> MapAffineToPhysicalRASSpace(
+ OFHelperType &of_helper, int level,
+ LinearTransformType *tran);
+
+ static void MapPhysicalRASSpaceToAffine(
+ OFHelperType &of_helper, int level,
+ vnl_matrix<double> &Qp,
+ LinearTransformType *tran);
+
+ void RecordMetricValue(double val);
+
+ // Compute the moments of a composite image (mean and covariance matrix of coordinate weighted by intensity)
+ void ComputeImageMoments(CompositeImageType *image, const std::vector<double> &weights, VecFx &m1, MatFx &m2);
+
+ class AbstractAffineCostFunction : public vnl_cost_function
+ {
+ public:
+
+ AbstractAffineCostFunction(int n_unknowns) : vnl_cost_function(n_unknowns) {}
+ virtual vnl_vector<double> GetCoefficients(LinearTransformType *tran) = 0;
+ virtual void GetTransform(const vnl_vector<double> &coeff, LinearTransformType *tran) = 0;
+ virtual void compute(vnl_vector<double> const& x, double *f, vnl_vector<double>* g) = 0;
+ };
+
+ /**
+ * Pure affine cost function - parameters are elements of N x N matrix M.
+ * Transformation takes place in voxel coordinates - not physical coordinates (for speed)
+ */
+ class PureAffineCostFunction : public AbstractAffineCostFunction
+ {
+ public:
+
+ typedef GreedyApproach<VDim, TReal> ParentType;
+
+ // Construct the function
+ PureAffineCostFunction(GreedyParameters *param, ParentType *parent, int level, OFHelperType *helper);
+
+ // Get the parameters for the specified initial transform
+ vnl_vector<double> GetCoefficients(LinearTransformType *tran);
+
+ // Get the transform for the specificed coefficients
+ void GetTransform(const vnl_vector<double> &coeff, LinearTransformType *tran);
+
+ // Get the preferred scaling for this function given image dimensions
+ virtual vnl_vector<double> GetOptimalParameterScaling(const itk::Size<VDim> &image_dim);
+
+ // Cost function computation
+ virtual void compute(vnl_vector<double> const& x, double *f, vnl_vector<double>* g);
+
+ protected:
+
+ // Data needed to compute the cost function
+ GreedyParameters *m_Param;
+ OFHelperType *m_OFHelper;
+ GreedyApproach<VDim, TReal> *m_Parent;
+ int m_Level;
+
+ // Storage for the gradient of the similarity map
+ VectorImagePointer m_Phi, m_GradMetric, m_GradMask;
+ ImagePointer m_Metric, m_Mask;
+
+ // Last set of coefficients evaluated
+ vnl_vector<double> last_coeff;
+ };
+
+ /**
+ * Physical space affine cost function - parameters are elements of affine transform in
+ * physical RAS space.
+ */
+ class PhysicalSpaceAffineCostFunction : public AbstractAffineCostFunction
+ {
+ public:
+ typedef GreedyApproach<VDim, TReal> ParentType;
+
+ PhysicalSpaceAffineCostFunction(GreedyParameters *param, ParentType *parent, int level, OFHelperType *helper);
+ virtual vnl_vector<double> GetCoefficients(LinearTransformType *tran);
+ virtual void GetTransform(const vnl_vector<double> &coeff, LinearTransformType *tran);
+ virtual void compute(vnl_vector<double> const& x, double *f, vnl_vector<double>* g);
+ virtual vnl_vector<double> GetOptimalParameterScaling(const itk::Size<VDim> &image_dim);
+
+ void map_phys_to_vox(const vnl_vector<double> &x_phys, vnl_vector<double> &x_vox);
+
+ protected:
+ PureAffineCostFunction m_PureFunction;
+
+ // Voxel to physical transforms for fixed, moving image
+ typedef vnl_matrix_fixed<double, VDim, VDim> Mat;
+ typedef vnl_vector_fixed<double, VDim> Vec;
+
+ Mat Q_fix, Q_mov, Q_fix_inv, Q_mov_inv;
+ Vec b_fix, b_mov, b_fix_inv, b_mov_inv;
+
+ vnl_matrix<double> J_phys_vox;
+ };
+
+ /** Abstract scaling cost function - wraps around another cost function and provides scaling */
+ class ScalingCostFunction : public AbstractAffineCostFunction
+ {
+ public:
+
+ // Construct the function
+ ScalingCostFunction(AbstractAffineCostFunction *pure_function, const vnl_vector<double> &scaling)
+ : AbstractAffineCostFunction(pure_function->get_number_of_unknowns()),
+ m_PureFunction(pure_function), m_Scaling(scaling) {}
+
+ // Get the parameters for the specified initial transform
+ vnl_vector<double> GetCoefficients(LinearTransformType *tran);
+
+ // Get the transform for the specificed coefficients
+ void GetTransform(const vnl_vector<double> &coeff, LinearTransformType *tran);
+
+ // Cost function computation
+ virtual void compute(vnl_vector<double> const& x, double *f, vnl_vector<double>* g);
+
+ const vnl_vector<double> &GetScaling() { return m_Scaling; }
+
+ protected:
+
+ // Data needed to compute the cost function
+ AbstractAffineCostFunction *m_PureFunction;
+ vnl_vector<double> m_Scaling;
+ };
+
+ /** Cost function for rigid registration */
+ class RigidCostFunction : public AbstractAffineCostFunction
+ {
+ public:
+ typedef vnl_vector_fixed<double, VDim> Vec3;
+ typedef vnl_matrix_fixed<double, VDim, VDim> Mat3;
+ typedef GreedyApproach<VDim, TReal> ParentType;
+
+ RigidCostFunction(GreedyParameters *param, ParentType *parent, int level, OFHelperType *helper);
+ vnl_vector<double> GetCoefficients(LinearTransformType *tran);
+ void GetTransform(const vnl_vector<double> &coeff, LinearTransformType *tran);
+ virtual void compute(vnl_vector<double> const& x, double *f, vnl_vector<double>* g);
+
+ // Get the preferred scaling for this function given image dimensions
+ virtual vnl_vector<double> GetOptimalParameterScaling(const itk::Size<VDim> &image_dim);
+
+ // Create a random set of parameters, such that on average point C_fixed maps to point C_mov
+ vnl_vector<double> GetRandomCoeff(const vnl_vector<double> &xInit, vnl_random &randy, double sigma_angle, double sigma_xyz,
+ const Vec3 &C_fixed, const Vec3 &C_moving);
+
+ protected:
+
+ Mat3 GetRotationMatrix(const Vec3 &q);
+ Vec3 GetAxisAngle(const Mat3 &R);
+
+ // We wrap around a physical space affine function, since rigid in physical space is not
+ // the same as rigid in voxel space
+ PhysicalSpaceAffineCostFunction m_AffineFn;
+
+ };
+
+ friend class GreedyApproach<VDim, TReal>::PureAffineCostFunction;
+
+};
+
+#endif // GREEDYAPI_H
diff --git a/Submodules/greedy/src/GreedyException.h b/Submodules/greedy/src/GreedyException.h
new file mode 100644
index 0000000..1a3ef95
--- /dev/null
+++ b/Submodules/greedy/src/GreedyException.h
@@ -0,0 +1,60 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __GreedyException_h_
+#define __GreedyException_h_
+
+#include <cstdio>
+#include <cstdarg>
+
+/**
+ * A simple exception class with string formatting
+ */
+class GreedyException : public std::exception
+{
+public:
+
+ GreedyException(const char *format, ...)
+ {
+ buffer = new char[4096];
+ va_list args;
+ va_start (args, format);
+ vsprintf (buffer,format, args);
+ va_end (args);
+ }
+
+ virtual const char* what() const throw() { return buffer; }
+
+ virtual ~GreedyException() throw() { delete buffer; }
+
+private:
+
+ char *buffer;
+
+};
+
+
+#endif
diff --git a/Submodules/greedy/src/GreedyParameters.cxx b/Submodules/greedy/src/GreedyParameters.cxx
new file mode 100644
index 0000000..b04d4e3
--- /dev/null
+++ b/Submodules/greedy/src/GreedyParameters.cxx
@@ -0,0 +1,69 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "GreedyParameters.h"
+
+
+
+void
+GreedyParameters
+::SetToDefaults(GreedyParameters ¶m)
+{
+ param.dim = 2;
+ param.mode = GreedyParameters::GREEDY;
+ param.flag_dump_moving = false;
+ param.flag_debug_deriv = false;
+ param.flag_debug_aff_obj = false;
+ param.dump_frequency = 1;
+ param.epsilon_per_level = std::vector<double>(1, 1.0);
+ param.sigma_pre.sigma = sqrt(3.0);
+ param.sigma_pre.physical_units = false;
+ param.sigma_post.sigma = sqrt(0.5);
+ param.sigma_post.physical_units = false;
+ param.threads = 0;
+ param.metric = GreedyParameters::SSD;
+ param.time_step_mode = GreedyParameters::SCALE;
+ param.deriv_epsilon = 1e-4;
+ param.flag_powell = false;
+ param.inverse_exponent = 2;
+ param.warp_precision = 0.1;
+ param.ncc_noise_factor = 0.001;
+ param.affine_init_mode = VOX_IDENTITY;
+ param.affine_dof = GreedyParameters::DOF_AFFINE;
+ param.affine_jitter = 0.5;
+ param.flag_float_math = false;
+
+ // reslice mode parameters
+ InterpSpec interp_current;
+
+ param.iter_per_level.push_back(100);
+ param.iter_per_level.push_back(100);
+
+ // Moments of inertia parameters
+ param.moments_flip_determinant = 0;
+ param.flag_moments_id_covariance = false;
+ param.moments_order = 1;
+}
diff --git a/Submodules/greedy/src/GreedyParameters.h b/Submodules/greedy/src/GreedyParameters.h
new file mode 100644
index 0000000..913cabb
--- /dev/null
+++ b/Submodules/greedy/src/GreedyParameters.h
@@ -0,0 +1,221 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef GREEDYPARAMETERS_H
+#define GREEDYPARAMETERS_H
+
+#include <string>
+#include <vector>
+#include <vnl/vnl_matrix.h>
+#include <vnl/vnl_vector.h>
+
+struct ImagePairSpec
+{
+ std::string fixed;
+ std::string moving;
+ double weight;
+};
+
+struct SmoothingParameters
+{
+ double sigma;
+ bool physical_units;
+ SmoothingParameters(double s, bool pu) : sigma(s), physical_units(pu) {}
+ SmoothingParameters() : sigma(0.0), physical_units(true) {}
+};
+
+struct RigidSearchSpec
+{
+ int iterations;
+ double sigma_xyz;
+ double sigma_angle;
+
+ RigidSearchSpec() : iterations(0), sigma_xyz(0.0), sigma_angle(0.0) {}
+};
+
+struct InterpSpec
+{
+ enum InterpMode { LINEAR, NEAREST, LABELWISE };
+
+ InterpMode mode;
+ SmoothingParameters sigma;
+
+ InterpSpec() : mode(LINEAR), sigma(0.5, false) {}
+};
+
+struct ResliceSpec
+{
+ std::string moving;
+ std::string output;
+ InterpSpec interp;
+};
+
+struct ResliceMeshSpec
+{
+ std::string fixed;
+ std::string output;
+};
+
+struct TransformSpec
+{
+ // Transform file
+ std::string filename;
+
+ // Optional exponent (-1 for inverse, 0.5 for square root)
+ double exponent;
+};
+
+enum AffineInitMode
+{
+ VOX_IDENTITY = 0, // Identity mapping in voxel space
+ RAS_IDENTITY, // Identity mapping in physical space (i.e., use headers)
+ RAS_FILENAME, // User-specified matrix in physical space
+ IMG_CENTERS // Match image centers, identity rotation in voxel space
+};
+
+struct GreedyResliceParameters
+{
+ // For reslice mode
+ std::vector<ResliceSpec> images;
+ std::vector<ResliceMeshSpec> meshes;
+
+ // Reference image
+ std::string ref_image;
+
+ // Chain of transforms
+ std::vector<TransformSpec> transforms;
+
+ // Output warp
+ std::string out_composed_warp;
+};
+
+// Parameters for inverse warp command
+struct GreedyInvertWarpParameters
+{
+ std::string in_warp, out_warp;
+};
+
+
+// Parameters for inverse warp command
+struct GreedyWarpRootParameters
+{
+ std::string in_warp, out_warp;
+ int exponent;
+};
+
+
+struct GreedyParameters
+{
+ enum MetricType { SSD = 0, NCC, MI, NMI };
+ enum TimeStepMode { CONSTANT=0, SCALE, SCALEDOWN };
+ enum Mode { GREEDY=0, AFFINE, BRUTE, RESLICE, INVERT_WARP, ROOT_WARP, MOMENTS };
+ enum AffineDOF { DOF_RIGID=6, DOF_SIMILARITY=7, DOF_AFFINE=12 };
+
+ std::vector<ImagePairSpec> inputs;
+ std::string output;
+ unsigned int dim;
+
+ // Output for each iteration. This can be in the format "blah_%04d_%04d.mat" for
+ // saving intermediate results into separate files. Or it can point to an object
+ // in the GreedyAPI cache
+ std::string output_intermediate;
+
+ // Reslice parameters
+ GreedyResliceParameters reslice_param;
+
+ // Inversion parameters
+ GreedyInvertWarpParameters invwarp_param;
+
+ // Root warp parameters
+ GreedyWarpRootParameters warproot_param;
+
+ // Registration mode
+ Mode mode;
+
+ bool flag_dump_moving, flag_debug_deriv, flag_powell;
+ int dump_frequency, threads;
+ double deriv_epsilon;
+
+ double affine_jitter;
+
+ // Smoothing parameters
+ SmoothingParameters sigma_pre, sigma_post;
+
+ MetricType metric;
+ TimeStepMode time_step_mode;
+
+ // Iterations per level (i.e., 40x40x100)
+ std::vector<double> epsilon_per_level;
+
+ std::vector<int> iter_per_level;
+
+ std::vector<int> metric_radius;
+
+ std::vector<int> brute_search_radius;
+
+ // List of transforms to apply to the moving image before registration
+ std::vector<TransformSpec> moving_pre_transforms;
+
+ // Initial affine transform
+ AffineInitMode affine_init_mode;
+ AffineDOF affine_dof;
+ TransformSpec affine_init_transform;
+
+ // Mask for gradient computation (fixed mask)
+ std::string gradient_mask;
+
+ // Mask for the moving image
+ std::string moving_mask;
+
+ // Inverse warp
+ std::string inverse_warp;
+ int inverse_exponent;
+
+ // Precision for output warps
+ double warp_precision;
+
+ // Noise for NCC
+ double ncc_noise_factor;
+
+ // Debugging matrices
+ bool flag_debug_aff_obj;
+
+ // Rigid search
+ RigidSearchSpec rigid_search;
+
+ // Moments of inertia specification
+ int moments_flip_determinant;
+ int moments_order;
+ bool flag_moments_id_covariance;
+
+ // Floating point precision?
+ bool flag_float_math;
+
+ static void SetToDefaults(GreedyParameters ¶m);
+};
+
+
+#endif // GREEDYPARAMETERS_H
diff --git a/Submodules/greedy/src/ImageRegionConstIteratorWithIndexOverride.h b/Submodules/greedy/src/ImageRegionConstIteratorWithIndexOverride.h
new file mode 100644
index 0000000..39b2e83
--- /dev/null
+++ b/Submodules/greedy/src/ImageRegionConstIteratorWithIndexOverride.h
@@ -0,0 +1,78 @@
+#ifndef __ImageRegionConstIteratorWithIndexOverride_h_
+#define __ImageRegionConstIteratorWithIndexOverride_h_
+
+#include "itkImageRegionConstIteratorWithIndex.h"
+#include "itkImageLinearIteratorWithIndex.h"
+
+namespace itk {
+ template <typename TPixel, unsigned int VDim> class VectorImage;
+}
+
+template <class TIterator>
+class IteratorExtender : public TIterator
+{
+public:
+ typedef IteratorExtender<TIterator> Self;
+ typedef TIterator Superclass;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+
+
+ IteratorExtender(ImageType *image, const RegionType ®ion)
+ : Superclass(image, region) {}
+
+ IteratorExtender(const ImageType *image, const RegionType ®ion)
+ : Superclass(const_cast<ImageType *>(image), region) {}
+
+ const InternalPixelType *GetPosition() { return this->m_Position; }
+
+ const InternalPixelType *GetBeginPosition() { return this->m_Begin; }
+
+ template <class TPixel, unsigned int VDim>
+ TPixel *GetPixelPointer(itk::Image<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels;
+ }
+
+ template <class TPixel, unsigned int VDim>
+ const TPixel *GetPixelPointer(const itk::Image<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels;
+ }
+
+ template <class TPixel, unsigned int VDim>
+ TPixel *GetPixelPointer(itk::VectorImage<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels * image->GetNumberOfComponentsPerPixel();
+ }
+
+ template <class TPixel, unsigned int VDim>
+ const TPixel *GetPixelPointer(const itk::VectorImage<TPixel, VDim> *image)
+ {
+ long offset_in_pixels = this->m_Position - this->m_Image->GetBufferPointer();
+ return image->GetBufferPointer() + offset_in_pixels * image->GetNumberOfComponentsPerPixel();
+ }
+};
+
+template <class TIterator>
+class IteratorExtenderWithOffset : public IteratorExtender<TIterator>
+{
+public:
+ typedef IteratorExtender<TIterator> Superclass;
+
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::ImageType ImageType;
+ typedef typename TIterator::OffsetValueType OffsetValueType;
+
+ IteratorExtenderWithOffset(ImageType *image, const RegionType ®ion)
+ : Superclass(image, region) {}
+
+ const OffsetValueType GetOffset(int direction) { return this->m_OffsetTable[direction]; }
+};
+
+
+#endif
diff --git a/Submodules/greedy/src/LinearTransformToWarpFilter.h b/Submodules/greedy/src/LinearTransformToWarpFilter.h
new file mode 100644
index 0000000..738717a
--- /dev/null
+++ b/Submodules/greedy/src/LinearTransformToWarpFilter.h
@@ -0,0 +1,106 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef LINEARTRANSFORMTOWARPFILTER_H
+#define LINEARTRANSFORMTOWARPFILTER_H
+
+#include <itkImageToImageFilter.h>
+#include "lddmm_common.h"
+
+/**
+ * This class transforms a linear transform into a deformation field
+ */
+template <class TInputImage, class TDeformationField, class TTransform>
+class LinearTransformToWarpFilter
+ : public itk::ImageToImageFilter<TInputImage,TDeformationField>
+{
+public:
+
+ /** Standard class typedefs. */
+ typedef LinearTransformToWarpFilter<TInputImage,TDeformationField,TTransform> Self;
+ typedef itk::ImageToImageFilter<TInputImage,TDeformationField> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( LinearTransformToWarpFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension );
+
+ // Lots of typedefs
+ typedef TInputImage InputImageType;
+ typedef TDeformationField DeformationFieldType;
+ typedef typename InputImageType::RegionType OutputImageRegionType;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+ typedef typename InputImageType::IndexType IndexType;
+ typedef typename InputImageType::IndexValueType IndexValueType;
+ typedef typename InputImageType::SizeType SizeType;
+ typedef typename InputImageType::SpacingType SpacingType;
+ typedef typename InputImageType::DirectionType DirectionType;
+ typedef typename DeformationFieldType::PixelType DeformationVectorType;
+ typedef itk::ImageBase<ImageDimension> ImageBaseType;
+
+ typedef TTransform TransformType;
+
+ /** Set the fixed image */
+ itkNamedInputMacro(FixedImage, InputImageType, "Primary")
+
+ /** Set the moving image */
+ itkNamedInputMacro(MovingImage, InputImageType, "moving")
+
+ /** Set the transform */
+ itkSetObjectMacro(Transform, TransformType)
+
+ /** Get the transform */
+ itkGetObjectMacro(Transform, TransformType)
+
+protected:
+
+ LinearTransformToWarpFilter() {}
+ virtual ~LinearTransformToWarpFilter() {}
+
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+ typename TransformType::Pointer m_Transform;
+
+private:
+ LinearTransformToWarpFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+};
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "LinearTransformToWarpFilter.txx"
+#endif
+
+#endif // LINEARTRANSFORMTOWARPFILTER_H
+
diff --git a/Submodules/greedy/src/LinearTransformToWarpFilter.txx b/Submodules/greedy/src/LinearTransformToWarpFilter.txx
new file mode 100644
index 0000000..cb64ed5
--- /dev/null
+++ b/Submodules/greedy/src/LinearTransformToWarpFilter.txx
@@ -0,0 +1,104 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef LINEARTRANSFORMTOWARPFILTER_TXX
+#define LINEARTRANSFORMTOWARPFILTER_TXX
+
+#include "LinearTransformToWarpFilter.h"
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+
+
+template <class TInputImage, class TDeformationField, class TTransform>
+void
+LinearTransformToWarpFilter<TInputImage,TDeformationField,TTransform>
+::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+
+
+
+ // Get a pointer to the output deformation field
+ DeformationVectorType *b_phi = this->GetOutput()->GetBufferPointer();
+
+ // Affine transform matrix and vector
+ vnl_matrix_fixed<double, ImageDimension, ImageDimension> M =
+ this->GetTransform()->GetMatrix().GetVnlMatrix();
+ vnl_vector_fixed<double, ImageDimension> off =
+ this->GetTransform()->GetOffset().GetVnlVector();
+
+ // Create an iterator over the deformation field
+ typedef itk::ImageLinearIteratorWithIndex<DeformationFieldType> IterBase;
+ typedef IteratorExtender<IterBase> IterType;
+
+ // Loop over the lines in the image
+ for(IterType it(this->GetOutput(), outputRegionForThread); !it.IsAtEnd(); it.NextLine())
+ {
+ // Get the index at the current location. For the rest of the line, the index will
+ // increment by one
+ IndexType idx = it.GetIndex();
+
+ // Displacement vector for the first position in the line and a delta corresponding to a
+ // step along the line
+ DeformationVectorType disp, delta_disp;
+
+ // Map to a position at which to interpolate
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ disp[i] = off[i] - idx[i];
+ delta_disp[i] = M(i, 0);
+ for(int j = 0; j < ImageDimension; j++)
+ disp[i] += M(i,j) * idx[j];
+ }
+ delta_disp[0] -= 1.0;
+
+ // Pointer to the start and end of the line
+ DeformationVectorType *p_phi = const_cast<DeformationVectorType *>(it.GetPosition());
+ DeformationVectorType *p_phi_end = p_phi + outputRegionForThread.GetSize(0);
+
+ // Run a loop filling out the displacement field
+ for( ; p_phi < p_phi_end; ++p_phi, disp += delta_disp)
+ {
+ *p_phi = disp;
+ }
+
+ /*
+ for( ; p_phi < p_phi_end; ++p_phi, ++idx[0])
+ {
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ (*p_phi)[i] = off[i] - idx[i];
+ for(int j = 0; j < ImageDimension; j++)
+ (*p_phi)[i] += M(i,j) * idx[j];
+ }
+ }
+
+*/
+ }
+}
+
+
+#endif // LINEARTRANSFORMTOWARPFILTER_TXX
+
diff --git a/Submodules/greedy/src/MultiComponentApproximateNCCImageMetric.h b/Submodules/greedy/src/MultiComponentApproximateNCCImageMetric.h
new file mode 100644
index 0000000..10659a8
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentApproximateNCCImageMetric.h
@@ -0,0 +1,242 @@
+/*=========================================================================
+
+ Program: ALFABIS fast image registration
+ Language: C++
+
+ Copyright (c) Paul Yushkevich. All rights reserved.
+
+ This program is part of ALFABIS
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef MULTICOMPONENTAPPROXIMATENCCIMAGEMETRIC_H
+#define MULTICOMPONENTAPPROXIMATENCCIMAGEMETRIC_H
+
+#include "MultiComponentImageMetricBase.h"
+#include "itkBarrier.h"
+
+/**
+ * Normalized cross-correlation metric similar to the one used in ANTS. The gradient
+ * is an approximation, but it seems to work very well.
+ */
+template <class TMetricTraits>
+class ITK_EXPORT MultiComponentApproximateNCCImageMetric :
+ public MultiComponentImageMetricBase<TMetricTraits>
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiComponentApproximateNCCImageMetric<TMetricTraits> Self;
+ typedef MultiComponentImageMetricBase<TMetricTraits> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiComponentApproximateNCCImageMetric, MultiComponentImageMetricBase )
+
+ /** Typedef to describe the output image region type. */
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::InputPixelType InputPixelType;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::MetricImageType MetricImageType;
+ typedef typename Superclass::MetricPixelType MetricPixelType;
+ typedef typename Superclass::GradientImageType GradientImageType;
+ typedef typename Superclass::GradientPixelType GradientPixelType;
+ typedef typename Superclass::MaskImageType MaskImageType;
+
+
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::SpacingType SpacingType;
+ typedef typename Superclass::DirectionType DirectionType;
+ typedef typename Superclass::ImageBaseType ImageBaseType;
+ typedef typename Superclass::RealType RealType;
+
+ /** Information from the deformation field class */
+ typedef typename Superclass::DeformationFieldType DeformationFieldType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension );
+
+ /** Set the radius of the cross-correlation */
+ itkSetMacro(Radius, SizeType)
+
+ /** Get the radius of the cross-correlation */
+ itkGetMacro(Radius, SizeType)
+
+ /**
+ * Set the working memory image for this filter. This function should be used to prevent
+ * repeated allocation of memory when the metric is created/destructed in a loop. The
+ * user can just pass in a pointer to a blank image, the filter will take care of allocating
+ * the image as necessary
+ */
+ itkSetObjectMacro(WorkingImage, InputImageType)
+
+ /**
+ * Get the gradient scaling factor. To get the actual gradient of the metric, multiply the
+ * gradient output of this filter by the scaling factor. Explanation: for efficiency, the
+ * metrics return an arbitrarily scaled vector, such that adding the gradient to the
+ * deformation field would INCREASE SIMILARITY. For metrics that are meant to be minimized,
+ * this is the opposite of the gradient direction. For metrics that are meant to be maximized,
+ * it is the gradient direction.
+ */
+ virtual double GetGradientScalingFactor() const { return 1.0; }
+
+
+protected:
+ MultiComponentApproximateNCCImageMetric()
+ { m_Radius.Fill(1); }
+
+ ~MultiComponentApproximateNCCImageMetric() {}
+
+ // TODO: set up for proper streaming
+ // virtual void GenerateInputRequestedRegion();
+
+ virtual void BeforeThreadedGenerateData();
+ virtual void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
+ itk::ThreadIdType threadId);
+
+
+private:
+ MultiComponentApproximateNCCImageMetric(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // A pointer to the working image. The user should supply this image in order to prevent
+ // unnecessary memory allocation
+ typename InputImageType::Pointer m_WorkingImage;
+
+ // Radius of the cross-correlation
+ SizeType m_Radius;
+
+ // Whether to compute the approximate gradient of the metric, as in Avants et al., 2008.
+ // When not set, the exact gradient is computed
+ bool m_ApproximateGradient;
+};
+
+
+
+
+
+
+/**
+ * \class MultiImageApproximateNCCPrecomputeFilter
+ * \brief Helps compute the NCC metric
+ *
+ * This filter takes a pair of images plus a warp and computes the components that
+ * are used to calculate the cross-correlation metric between them and
+ * the gradient. These components are in the form I, I*J, and
+ * so on. These components must then be mean-filtered and combined to get the
+ * metric and the gradient.
+ *
+ * The output of this filter must be a vector image. The input may be a vector image.
+ *
+ */
+template <class TMetricTraits, class TOutputImage>
+class ITK_EXPORT MultiImageApproximateNCCPrecomputeFilter :
+ public itk::ImageToImageFilter<typename TMetricTraits::InputImageType, TOutputImage>
+{
+public:
+
+ /** The parent/owner class */
+ typedef MultiComponentApproximateNCCImageMetric<TMetricTraits> ParentType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename ParentType::InputImageType InputImageType;
+ typedef typename ParentType::InputPixelType InputPixelType;
+ typedef typename ParentType::InputComponentType InputComponentType;
+ typedef typename ParentType::DeformationVectorType DeformationVectorType;
+ typedef typename ParentType::MetricPixelType MetricPixelType;
+ typedef typename ParentType::GradientPixelType GradientPixelType;
+ typedef typename ParentType::RealType RealType;
+ typedef TOutputImage OutputImageType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ /** Standard class typedefs. */
+ typedef MultiImageApproximateNCCPrecomputeFilter Self;
+ typedef itk::ImageToImageFilter<InputImageType, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Typedef to describe the output image region type. */
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ typedef typename Superclass::DataObjectIdentifierType DataObjectIdentifierType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageApproximateNCCPrecomputeFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension );
+
+ /** Set the parent class */
+ void SetParent(ParentType *parent)
+ {
+ m_Parent = parent;
+ }
+
+ enum Stage {FIRST, SECOND};
+
+ /** Set the stage of the computation */
+ itkSetMacro(Stage, Stage)
+
+ /** Get the number of components in the output */
+ int GetNumberOfOutputComponents();
+
+
+protected:
+ MultiImageApproximateNCCPrecomputeFilter();
+ ~MultiImageApproximateNCCPrecomputeFilter() {}
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ virtual void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+ /** Set up the output information */
+ virtual void GenerateOutputInformation();
+
+ /** Override input checks to allow fixed and moving to be in different space */
+ virtual void VerifyInputInformation() {}
+
+private:
+ MultiImageApproximateNCCPrecomputeFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ ParentType *m_Parent;
+
+ Stage m_Stage;
+};
+
+
+
+
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiComponentApproximateNCCImageMetric.txx"
+#endif
+
+
+#endif // MULTICOMPONENTAPPROXIMATENCCIMAGEMETRIC_H
diff --git a/Submodules/greedy/src/MultiComponentApproximateNCCImageMetric.txx b/Submodules/greedy/src/MultiComponentApproximateNCCImageMetric.txx
new file mode 100644
index 0000000..0de6b55
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentApproximateNCCImageMetric.txx
@@ -0,0 +1,496 @@
+/*=========================================================================
+
+ Program: ALFABIS fast image registration
+ Language: C++
+
+ Copyright (c) Paul Yushkevich. All rights reserved.
+
+ This program is part of ALFABIS
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiComponentApproximateNCCImageMetric_txx
+#define __MultiComponentApproximateNCCImageMetric_txx
+
+#include "MultiComponentApproximateNCCImageMetric.h"
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include "itkImageFileWriter.h"
+
+
+
+/* ==========================================================================
+ *
+ * PRECOMPUTE filter implementation
+ *
+ * ========================================================================== */
+
+template <class TMetricTraits, class TOutputImage>
+MultiImageApproximateNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::MultiImageApproximateNCCPrecomputeFilter()
+{
+ m_Parent = NULL;
+ m_Stage = FIRST;
+}
+
+/**
+ * Generate output information, which will be different from the default
+ */
+template <class TMetricTraits, class TOutputImage>
+void
+MultiImageApproximateNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::GenerateOutputInformation()
+{
+ // Call the parent method to set up all the outputs
+ Superclass::GenerateOutputInformation();
+
+ // Set the number of components in the primary output
+ int ncomp = this->GetNumberOfOutputComponents();
+ this->GetOutput()->SetNumberOfComponentsPerPixel(ncomp);
+}
+
+template <class TMetricTraits, class TOutputImage>
+int
+MultiImageApproximateNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::GetNumberOfOutputComponents()
+{
+ // This is complex! The number of components depends on what we are computing.
+ int nc = m_Parent->GetFixedImage()->GetNumberOfComponentsPerPixel();
+
+ // If there are no gradients computed, we just need 7 output pixels per input comp.
+ return 1 + nc * 7;
+}
+
+/**
+ * Compute the output for the region specified by outputRegionForThread.
+ *
+ * Coding:
+ * 1
+ * f
+ * m
+ * f
+ * m
+ * f*f
+ * m*m
+ * f*m
+ * ...
+ */
+template <class TMetricTraits, class TOutputImage>
+void
+MultiImageApproximateNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId )
+{
+ typedef FastLinearInterpolator<InputImageType, RealType, ImageDimension> FastInterpolator;
+
+ // Get the number of input and output components
+ int ncomp_in = m_Parent->GetFixedImage()->GetNumberOfComponentsPerPixel();
+
+ // Create an iterator specialized for going through metrics
+ typedef MultiComponentMetricWorker<TMetricTraits, TOutputImage> InterpType;
+ InterpType iter(m_Parent, this->GetOutput(), outputRegionForThread);
+
+ // Iterate over the lines
+ for(; !iter.IsAtEnd(); iter.NextLine())
+ {
+ // Iterate over the pixels in the line
+ for(; !iter.IsAtEndOfLine(); ++iter)
+ {
+ // Get the output pointer for this voxel
+ OutputComponentType *out = iter.GetOutputLine();
+
+ // Interpolate the moving image at the current position. The worker knows
+ // whether to interpolate the gradient or not
+ typename FastInterpolator::InOut status = iter.Interpolate();
+
+ // Border should be ok here because we are not sampling the gradient
+ if(status == FastInterpolator::OUTSIDE)
+ {
+ // Place a zero in the output
+ *out++ = 1.0;
+
+ // Iterate over the components
+ for(int k = 0; k < ncomp_in; k++)
+ {
+ InputComponentType x_fix = iter.GetFixedLine()[k];
+ *out++ = x_fix;
+ *out++ = 0.0;
+ *out++ = x_fix;
+ *out++ = 0.0;
+ *out++ = x_fix * x_fix;
+ *out++ = 0.0;
+ *out++ = 0.0;
+ }
+ }
+
+ else
+ {
+ if(m_Stage == FIRST)
+ {
+ // Place a zero in the output
+ *out++ = 1.0;
+
+ // Iterate over the components
+ for(int k = 0; k < ncomp_in; k++)
+ {
+ InputComponentType x_mov = iter.GetMovingSample()[k];
+ InputComponentType x_fix = iter.GetFixedLine()[k];
+
+ // Write the five components
+ *out++ = x_fix;
+ *out++ = x_mov;
+ *out++ = 0.0;
+ *out++ = 0.0;
+ *out++ = 0.0;
+ *out++ = 0.0;
+ *out++ = 0.0;
+ }
+ }
+ else
+ {
+ // Get the number of neighbors from last round
+ InputComponentType n_nbr = *out;
+
+ // Place a one in the output
+ out++;
+
+ // Iterate over the components
+ for(int k = 0; k < ncomp_in; k++)
+ {
+ InputComponentType x_mov_raw = iter.GetMovingSample()[k];
+ InputComponentType x_fix_raw = iter.GetFixedLine()[k];
+
+ // Write the five components
+ InputComponentType x_fix = x_fix_raw - *out / n_nbr;
+ *out++ = x_fix;
+ InputComponentType x_mov = x_mov_raw - *out / n_nbr;
+ *out++ = x_mov;
+ *out++ = x_fix;
+ *out++ = x_mov;
+ *out++ = x_fix * x_fix;
+ *out++ = x_mov * x_mov;
+ *out++ = x_fix * x_mov;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+/* ==========================================================================
+ *
+ * main filter implementation
+ *
+ * ========================================================================== */
+
+
+
+/**
+ * This is a similar function to above, but uses the approximate algorithm described by Avants et al.
+ * in the 2008 NeuroImage paper. This is not the exact gradient of the metric. This is useful for
+ * comparing Greedy with ANTS
+ */
+template <class TPixel, class TWeight, class TMetric, class TGradient>
+TPixel *
+MultiImageApproximateNNCPostComputeFunction(
+ TPixel *ptr, TPixel *ptr_end, TWeight *weights, TMetric *ptr_metric, TGradient *ptr_gradient, int ImageDimension,
+ const TPixel *grad_fix, bool debug)
+{
+ // Get the size of the mean filter kernel
+ TPixel n = *ptr++, one_over_n = 1.0 / n;
+
+ // Loop over components
+ int i_wgt = 0;
+
+ // Initialize metric to zero
+ *ptr_metric = 0;
+
+ for(; ptr < ptr_end; ++i_wgt)
+ {
+ TPixel I_bar = *ptr++;
+ TPixel J_bar = *ptr++;
+ TPixel I_2bar = *ptr++;
+ TPixel J_2bar = *ptr++;
+ TPixel x_fix_sq = *ptr++;
+ TPixel x_mov_sq = *ptr++;
+ TPixel x_fix_mov = *ptr++;
+
+ TPixel sff = x_fix_sq - I_2bar * I_2bar * one_over_n;
+ TPixel smm = x_mov_sq - J_2bar * J_2bar * one_over_n;
+ TPixel smf = x_fix_mov - I_2bar * J_2bar * one_over_n;
+
+
+ if(sff == 0 || smm == 0)
+ {
+ if(ptr_gradient)
+ ptr += 3 * ImageDimension;
+ continue;
+ }
+
+ TWeight w = weights[i_wgt];
+ TPixel zoop = w * (smf / (sff * smm));
+
+ if(ptr_gradient)
+ {
+
+ // Term to multiply the gradient by...
+ TPixel factor = -2.0 * zoop * (J_bar - (smf / sff) * I_bar);
+
+ // 2.0 * sfm / (sff * smm) * ( Ji - sfm / sff * Ii )
+
+ if(debug)
+ {
+ printf("Ii = %f, Ji = %f, sff = %f, sfm = %f, smm = %f\n", I_bar, J_bar, sff, smf, smm);
+ printf("Metric = %f\n", zoop * smf);
+ printf("GradF = %f, %f, %f\n", grad_fix[0], grad_fix[1], grad_fix[2]);
+ }
+
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ (*ptr_gradient)[i] += factor * (*grad_fix++);
+ }
+
+ if(debug)
+ {
+ printf("Deriv = %g, %g, %g\n", (*ptr_gradient)[0], (*ptr_gradient)[1], (*ptr_gradient)[2]);
+ }
+ }
+
+
+ // Accumulate the metric
+ *ptr_metric += zoop * smf;
+ }
+
+ return ptr;
+}
+
+
+// #define DUMP_NCC 1
+
+template <class TMetricTraits>
+void
+MultiComponentApproximateNCCImageMetric<TMetricTraits>
+::BeforeThreadedGenerateData()
+{
+ // Call the parent method
+ Superclass::BeforeThreadedGenerateData();
+
+ // Pre-compute filter 1
+ typedef MultiImageApproximateNCCPrecomputeFilter<TMetricTraits, InputImageType> PreFilterType;
+ typename PreFilterType::Pointer preFilter1 = PreFilterType::New();
+
+ // Configure the precompute filter
+ preFilter1->SetParent(this);
+ preFilter1->SetInput(this->GetFixedImage());
+
+ // Number of components in the working image
+ int ncomp = preFilter1->GetNumberOfOutputComponents();
+
+ // If the user supplied a working image, configure it and graft it as output
+ if(m_WorkingImage)
+ {
+ // Configure the working image
+ m_WorkingImage->CopyInformation(this->GetFixedImage());
+ m_WorkingImage->SetNumberOfComponentsPerPixel(ncomp);
+ m_WorkingImage->SetRegions(this->GetFixedImage()->GetBufferedRegion());
+ m_WorkingImage->Allocate();
+
+ // Graft the working image onto the filter's output
+ preFilter1->GraftOutput(m_WorkingImage);
+ }
+
+ // Execute the filter
+ preFilter1->SetStage(PreFilterType::FIRST);
+ preFilter1->Update();
+
+#ifdef DUMP_NCC
+ typename itk::ImageFileWriter<InputImageType>::Pointer pwriter = itk::ImageFileWriter<InputImageType>::New();
+ pwriter->SetInput(preFilter1->GetOutput());
+ pwriter->SetFileName("nccpre1.nii.gz");
+ pwriter->Update();
+#endif
+
+ // First round of accumulation
+ typedef OneDimensionalInPlaceAccumulateFilter<InputImageType> AccumFilterType;
+
+ // Create a chain of separable 1-D filters
+ typename itk::ImageSource<InputImageType>::Pointer pipeTail;
+ for(int dir = 0; dir < ImageDimension; dir++)
+ {
+ typename AccumFilterType::Pointer accum = AccumFilterType::New();
+ if(pipeTail.IsNull())
+ accum->SetInput(preFilter1->GetOutput());
+ else
+ accum->SetInput(pipeTail->GetOutput());
+ accum->SetComponentRange(0, 5);
+ accum->SetDimension(dir);
+ accum->SetRadius(m_Radius[dir]);
+ pipeTail = accum;
+
+ accum->Update();
+ }
+
+ // At this point, we want to pull out components 1-3 from the working image
+
+ // Perform the second round of accumulation
+ typename PreFilterType::Pointer preFilter2 = PreFilterType::New();
+
+ // Configure the precompute filter
+ preFilter2->SetParent(this);
+ preFilter2->SetInput(this->GetFixedImage());
+ preFilter2->GraftOutput(m_WorkingImage);
+
+ // Execute the filter
+ preFilter2->SetStage(PreFilterType::SECOND);
+ preFilter2->Update();
+
+#ifdef DUMP_NCC
+ typename itk::ImageFileWriter<InputImageType>::Pointer pwriter1 = itk::ImageFileWriter<InputImageType>::New();
+ pwriter1->SetInput(preFilter2->GetOutput());
+ pwriter1->SetFileName("nccpre2.nii.gz");
+ pwriter1->Update();
+#endif
+
+ // Create a chain of separable 1-D filters
+ pipeTail = NULL;
+ for(int dir = 0; dir < ImageDimension; dir++)
+ {
+ typename AccumFilterType::Pointer accum = AccumFilterType::New();
+ if(pipeTail.IsNull())
+ accum->SetInput(preFilter2->GetOutput());
+ else
+ accum->SetInput(pipeTail->GetOutput());
+ accum->SetComponentRange(3, 0);
+ accum->SetDimension(dir);
+ accum->SetRadius(m_Radius[dir]);
+ pipeTail = accum;
+
+ accum->Update();
+ }
+
+#ifdef DUMP_NCC
+ typename itk::ImageFileWriter<InputImageType>::Pointer pwriter2 = itk::ImageFileWriter<InputImageType>::New();
+ pwriter2->SetInput(pipeTail->GetOutput());
+ pwriter2->SetFileName("nccaccum.nii.gz");
+ pwriter2->Update();
+#endif
+}
+
+// TODO: this only needs to be computed once per resolution level
+#include <itkVectorImageCentralDifferenceImageFunction.h>
+
+template <class TMetricTraits>
+void
+MultiComponentApproximateNCCImageMetric<TMetricTraits>
+::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId)
+{
+ int nc = m_WorkingImage->GetNumberOfComponentsPerPixel();
+ int line_len = outputRegionForThread.GetSize()[0];
+
+ // Our thread data
+ typename Superclass::ThreadData &td = this->m_ThreadData[threadId];
+
+ // Set up an iterator for the working image
+ typedef itk::ImageLinearConstIteratorWithIndex<InputImageType> InputIteratorTypeBase;
+ typedef IteratorExtender<InputIteratorTypeBase> InputIteratorType;
+ InputIteratorType it(m_WorkingImage, outputRegionForThread);
+
+ // TODO: this can be pre-computed!
+ /* BEGIN: fixed gradient comp */
+ // Set up the fixed gradient calculator
+
+ typedef itk::VectorImageCentralDifferenceImageFunction<InputImageType, RealType> CDType;
+ typename CDType::Pointer cdiff = CDType::New();
+ cdiff->SetInputImage(this->GetFixedImage());
+ IndexType index;
+ int grad_fixed_length = ImageDimension * this->GetFixedImage()->GetNumberOfComponentsPerPixel();
+ RealType *grad_fixed = new RealType[grad_fixed_length];
+ itk::VariableLengthVector<RealType> grad_fixed_vec(grad_fixed, grad_fixed_length);
+
+ // Loop over the lines
+ for (; !it.IsAtEnd(); it.NextLine())
+ {
+ // Get the pointer to the input line
+ long offset_in_pixels = it.GetPosition() - m_WorkingImage->GetBufferPointer();
+
+ // Pointer to the input pixel data for this line
+ InputComponentType *p_input = m_WorkingImage->GetBufferPointer() + nc * offset_in_pixels;
+
+ // Pointer to the metric data for this line
+ MetricPixelType *p_metric = this->GetMetricOutput()->GetBufferPointer() + offset_in_pixels;
+
+ // The gradient output is optional
+ GradientPixelType *p_grad_metric = (this->m_ComputeGradient && !this->m_ComputeAffine)
+ ? this->GetDeformationGradientOutput()->GetBufferPointer() + offset_in_pixels
+ : NULL;
+
+ // Index: for gradient computation (remove later)
+ index = it.GetIndex();
+
+ // Case 1 - dense gradient field requested
+ if(!this->m_ComputeAffine)
+ {
+ if(this->m_ComputeGradient)
+ {
+ // Loop over the pixels in the line
+ for(int i = 0; i < line_len; ++i)
+ {
+ // Clear the metric and the gradient
+ *p_metric = itk::NumericTraits<MetricPixelType>::Zero;
+
+ // Gradient of the fixed image is passed in through the p_grad_metric!
+ *p_grad_metric = itk::NumericTraits<GradientPixelType>::Zero;
+
+ // Compute the gradient
+ index[0] = i;
+ cdiff->EvaluateAtIndex(index, grad_fixed_vec);
+
+ bool debugff = (it.GetIndex()[1] == 33 && it.GetIndex()[2] == 22 && i == 44);
+ p_input = MultiImageApproximateNNCPostComputeFunction(p_input, p_input + nc, this->m_Weights.data_block(),
+ p_metric, p_grad_metric++, ImageDimension, grad_fixed, debugff);
+ // Accumulate the total metric
+ td.metric += *p_metric;
+ td.mask += 1.0;
+ }
+ }
+ else
+ {
+ // Loop over the pixels in the line
+ for(int i = 0; i < line_len; ++i)
+ {
+ // Clear the metric and the gradient
+ *p_metric = itk::NumericTraits<MetricPixelType>::Zero;
+
+ // Apply the post computation
+ p_input = MultiImageApproximateNNCPostComputeFunction(p_input, p_input + nc, this->m_Weights.data_block(),
+ p_metric, (GradientPixelType *)(NULL), ImageDimension,
+ (const RealType *) NULL, false);
+
+ // Accumulate the total metric
+ td.metric += *p_metric;
+ td.mask += 1.0;
+ }
+ }
+ }
+ }
+
+ delete grad_fixed;
+}
+
+
+#endif // __MultiComponentApproximateNCCImageMetric_txx
+
diff --git a/Submodules/greedy/src/MultiComponentImageMetricBase.h b/Submodules/greedy/src/MultiComponentImageMetricBase.h
new file mode 100644
index 0000000..50a042b
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentImageMetricBase.h
@@ -0,0 +1,378 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef MULTICOMPONENTIMAGEMETRICBASE_H
+#define MULTICOMPONENTIMAGEMETRICBASE_H
+
+#include "itkImageBase.h"
+#include "itkImageToImageFilter.h"
+#include "itkPoint.h"
+#include "itkFixedArray.h"
+#include "itkVectorImage.h"
+#include "itkMatrixOffsetTransformBase.h"
+#include "lddmm_common.h"
+
+/**
+ * Default traits for parameterizing the metric filters below
+ */
+template <class TReal, unsigned int VDim> struct DefaultMultiComponentImageMetricTraits
+{
+ typedef itk::VectorImage<TReal, VDim> InputImageType;
+ typedef itk::Image<TReal, VDim> ScalarImageType;
+ typedef itk::Image<itk::CovariantVector<TReal, VDim>, VDim> VectorImageType;
+
+ typedef ScalarImageType MaskImageType;
+ typedef VectorImageType DeformationFieldType;
+ typedef VectorImageType GradientImageType;
+ typedef ScalarImageType MetricImageType;
+ typedef itk::MatrixOffsetTransformBase<TReal, VDim, VDim> TransformType;
+
+ typedef TReal RealType;
+};
+
+
+/**
+ * \class MultiComponentImageMetricBase
+ *
+ * Base class for metrics that compute similarity between two multi-component
+ * images based on a deformation field. This filter is extended to support
+ * normalized cross-correlation and least squares metrics.
+ *
+ * The metric takes the following inputs:
+ * Fixed multicomponent image (type TMetricTraits::InputImageType)
+ * Moving multicomponent image (type TMetricTraits::InputImageType)
+ * A mask for the fixed image (type TMetricTraits::MaskImageType) [optional]
+ * A deformation field (type TMetricTraits::DeformationFieldType)
+ *
+ * It produces the following outputs
+ * Metric image (type TMetricTraits::MetricImageType)
+ * Metric image gradient (type TMetricTraits::GradientImageType)
+ * Moving image domain mask (type TMetricTraits::MetricImageType)
+ * Moving image domain mask gradient (type TMetricTraits::GradientImageType)
+ */
+template <class TMetricTraits>
+class ITK_EXPORT MultiComponentImageMetricBase :
+ public itk::ImageToImageFilter<typename TMetricTraits::InputImageType,
+ typename TMetricTraits::MetricImageType>
+{
+public:
+ /** Type definitions from the traits class */
+ typedef typename TMetricTraits::InputImageType InputImageType;
+ typedef typename TMetricTraits::MaskImageType MaskImageType;
+ typedef typename TMetricTraits::DeformationFieldType DeformationFieldType;
+ typedef typename TMetricTraits::MetricImageType MetricImageType;
+ typedef typename TMetricTraits::GradientImageType GradientImageType;
+ typedef typename TMetricTraits::TransformType TransformType;
+ typedef typename TMetricTraits::RealType RealType;
+
+ /** Standard class typedefs. */
+ typedef MultiComponentImageMetricBase Self;
+ typedef itk::ImageToImageFilter<InputImageType,MetricImageType> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiComponentImageMetricBase, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ MetricImageType::ImageDimension );
+
+ /** Typedef to describe the output image region type. */
+ typedef typename InputImageType::RegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+ typedef typename MetricImageType::PixelType MetricPixelType;
+ typedef typename MetricImageType::IndexType IndexType;
+ typedef typename MetricImageType::IndexValueType IndexValueType;
+ typedef typename MetricImageType::SizeType SizeType;
+ typedef typename MetricImageType::SpacingType SpacingType;
+ typedef typename MetricImageType::DirectionType DirectionType;
+ typedef typename GradientImageType::PixelType GradientPixelType;
+ typedef itk::ImageBase<ImageDimension> ImageBaseType;
+
+ typedef typename Superclass::DataObjectIdentifierType DataObjectIdentifierType;
+
+ /** Information from the deformation field class */
+ typedef typename DeformationFieldType::Pointer DeformationFieldPointer;
+ typedef typename DeformationFieldType::PixelType DeformationVectorType;
+
+ /** Weight vector */
+ typedef vnl_vector<float> WeightVectorType;
+
+ /** Set the fixed image(s) */
+ itkNamedInputMacro(FixedImage, InputImageType, "Primary")
+
+ /** Set the moving image(s) and their gradients */
+ itkNamedInputMacro(MovingImage, InputImageType, "moving")
+
+ /** Set the optional mask input */
+ itkNamedInputMacro(FixedMaskImage, MaskImageType, "fixed_mask")
+
+ /** Set the optional jitter input - for affine images*/
+ itkNamedInputMacro(JitterImage, DeformationFieldType, "jitter")
+
+ /**
+ * Set the deformation field. If the deformation field is set, the affine
+ * transform gradients will not be computed.
+ */
+ void SetDeformationField(DeformationFieldType *phi)
+ {
+ this->itk::ProcessObject::SetInput("phi", phi);
+ this->UpdateOutputs();
+ }
+
+ itkNamedInputGetMacro(DeformationField, DeformationFieldType, "phi")
+
+ /**
+ * Set the affine transform. If the affine transform is set, affine transform
+ * gradients will be computed, but not the deformation field gradients
+ */
+ void SetAffineTransform(TransformType *transform)
+ {
+ this->m_AffineTransform = transform;
+ this->m_ComputeAffine = true;
+ this->UpdateOutputs();
+ }
+
+ itkGetMacro(AffineTransform, TransformType *)
+
+ /** Set the weight vector - for different components in the input image */
+ itkSetMacro(Weights, WeightVectorType)
+ itkGetConstMacro(Weights, WeightVectorType)
+
+ /** Whether or not the gradient is required */
+ itkGetMacro(ComputeGradient, bool)
+
+ /** Whether the transformation is affine */
+ itkGetMacro(ComputeAffine, bool)
+
+ /** Specify whether the filter should compute gradients (whether affine or deformable) */
+ void SetComputeGradient(bool flag)
+ {
+ this->m_ComputeGradient = flag;
+ this->UpdateOutputs();
+ }
+
+ itkBooleanMacro(ComputeMovingDomainMask)
+
+ /** Specify whether the metric should be normalized by the moving image domain */
+ void SetComputeMovingDomainMask(bool flag)
+ {
+ this->m_ComputeMovingDomainMask = flag;
+ this->UpdateOutputs();
+ }
+
+ /** Get the metric image output - this is the main output */
+ itkNamedOutputMacro(MetricOutput, MetricImageType, "Primary")
+
+ /** Get the metric dense gradient output. The gradient may be arbitrarily scaled. */
+ itkNamedOutputMacro(DeformationGradientOutput, GradientImageType, "phi_gradient")
+
+ /** Get the gradient of the affine transform */
+ itkGetMacro(AffineTransformGradient, TransformType *)
+
+ /**
+ * Get the gradient scaling factor. To get the actual gradient of the metric, multiply the
+ * gradient output of this filter by the scaling factor. Explanation: for efficiency, the
+ * metrics return an arbitrarily scaled vector, such that adding the gradient to the
+ * deformation field would INCREASE SIMILARITY. For metrics that are meant to be minimized,
+ * this is the opposite of the gradient direction. For metrics that are meant to be maximized,
+ * it is the gradient direction.
+ */
+ virtual double GetGradientScalingFactor() const = 0;
+
+ /** Summary results after running the filter */
+ itkGetConstMacro(MetricValue, double)
+
+ /** Get the metric values per component (each component weighted) */
+ vnl_vector<double> GetAllMetricValues() const;
+
+protected:
+ MultiComponentImageMetricBase();
+ ~MultiComponentImageMetricBase() {}
+
+ virtual void VerifyInputInformation() {}
+
+ /** It is difficult to compute in advance the input image region
+ * required to compute the requested output region. Thus the safest
+ * thing to do is to request for the whole input image.
+ *
+ * For the deformation field, the input requested region
+ * set to be the same as that of the output requested region. */
+ virtual void GenerateInputRequestedRegion();
+
+ virtual typename itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &);
+
+ void UpdateOutputs();
+ void ToggleOutput(bool flag, const DataObjectIdentifierType &key);
+
+ virtual void BeforeThreadedGenerateData();
+ virtual void AfterThreadedGenerateData();
+
+
+
+ // Weight vector
+ WeightVectorType m_Weights;
+
+ bool m_ComputeMovingDomainMask;
+ bool m_ComputeGradient;
+ bool m_ComputeAffine;
+
+ // Data accumulated for each thread
+ struct ThreadData {
+ double metric, mask;
+
+ // Component-wise metric values - for reporting
+ vnl_vector<double> comp_metric;
+
+ vnl_vector<double> gradient, grad_mask;
+ ThreadData() : metric(0.0), mask(0.0),
+ gradient(ImageDimension * (ImageDimension+1), 0.0),
+ grad_mask(ImageDimension * (ImageDimension+1), 0.0) {}
+ };
+
+ // Per-thread data
+ std::vector<ThreadData> m_ThreadData;
+
+ // Total accumulated data
+ ThreadData m_AccumulatedData;
+
+ // Accumulated metric value
+ double m_MetricValue;
+
+ // Affine transform
+ typename TransformType::Pointer m_AffineTransform, m_AffineTransformGradient;
+
+private:
+ MultiComponentImageMetricBase(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+};
+
+
+/**
+ * Flatten an affine transform to a flat array
+ */
+template<class TFloat, class TFloatArr, unsigned int VDim>
+static void flatten_affine_transform(
+ const itk::MatrixOffsetTransformBase<TFloat, VDim, VDim> *transform,
+ TFloatArr *flat_array)
+{
+ int pos = 0;
+ for(int i = 0; i < VDim; i++)
+ {
+ flat_array[pos++] = transform->GetOffset()[i];
+ for(int j = 0; j < VDim; j++)
+ flat_array[pos++] = transform->GetMatrix()(i,j);
+ }
+}
+
+template<class TFloat, class TFloatArr, unsigned int VDim>
+static void flatten_affine_transform(
+ const vnl_matrix_fixed<TFloat, VDim, VDim> &matrix,
+ const vnl_vector_fixed<TFloat, VDim> &offset,
+ TFloatArr *flat_array)
+{
+ int pos = 0;
+ for(int i = 0; i < VDim; i++)
+ {
+ flat_array[pos++] = offset[i];
+ for(int j = 0; j < VDim; j++)
+ flat_array[pos++] = matrix(i,j);
+ }
+}
+
+/**
+ * Unflatten a flat array to an affine transform
+ */
+template<class TFloat, class TFloatArr, unsigned int VDim>
+static void unflatten_affine_transform(
+ const TFloatArr *flat_array,
+ itk::MatrixOffsetTransformBase<TFloat, VDim, VDim> *transform,
+ double scaling = 1.0)
+{
+ typename itk::MatrixOffsetTransformBase<TFloat, VDim, VDim>::MatrixType matrix;
+ typename itk::MatrixOffsetTransformBase<TFloat, VDim, VDim>::OffsetType offset;
+
+ int pos = 0;
+ for(int i = 0; i < VDim; i++)
+ {
+ offset[i] = flat_array[pos++] * scaling;
+ for(int j = 0; j < VDim; j++)
+ matrix(i, j) = flat_array[pos++] * scaling;
+ }
+
+ transform->SetMatrix(matrix);
+ transform->SetOffset(offset);
+}
+
+template<class TFloat, class TFloatArr, unsigned int VDim>
+static void unflatten_affine_transform(
+ const TFloatArr *flat_array,
+ vnl_matrix_fixed<TFloat, VDim, VDim> &matrix,
+ vnl_vector_fixed<TFloat, VDim> &offset,
+ double scaling = 1.0)
+{
+ int pos = 0;
+ for(int i = 0; i < VDim; i++)
+ {
+ offset[i] = flat_array[pos++] * scaling;
+ for(int j = 0; j < VDim; j++)
+ matrix(i, j) = flat_array[pos++] * scaling;
+ }
+}
+
+
+
+template <class TFloat, unsigned int VDim>
+static void set_affine_transform(
+ const vnl_matrix_fixed<TFloat, VDim, VDim> &matrix,
+ const vnl_vector_fixed<TFloat, VDim> &offset,
+ itk::MatrixOffsetTransformBase<TFloat, VDim, VDim> *transform)
+{
+ typename itk::MatrixOffsetTransformBase<TFloat, VDim, VDim>::MatrixType tmatrix;
+ typename itk::MatrixOffsetTransformBase<TFloat, VDim, VDim>::OffsetType toffset;
+
+ for(int i = 0; i < VDim; i++)
+ {
+ toffset[i] = offset[i];
+ for(int j = 0; j < VDim; j++)
+ tmatrix(i, j) = matrix(i,j);
+ }
+
+ transform->SetMatrix(tmatrix);
+ transform->SetOffset(toffset);
+}
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiComponentImageMetricBase.txx"
+#endif
+
+
+#endif // MULTICOMPONENTIMAGEMETRICBASE_H
diff --git a/Submodules/greedy/src/MultiComponentImageMetricBase.txx b/Submodules/greedy/src/MultiComponentImageMetricBase.txx
new file mode 100644
index 0000000..6a011a9
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentImageMetricBase.txx
@@ -0,0 +1,512 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiComponentImageMetricBase_txx_
+#define __MultiComponentImageMetricBase_txx_
+
+#include "MultiComponentImageMetricBase.h"
+
+template <class TMetricTraits>
+MultiComponentImageMetricBase<TMetricTraits>
+::MultiComponentImageMetricBase()
+{
+ // Create the outputs of this filter
+ this->SetPrimaryOutput(this->MakeOutput("Primary"));
+ this->m_ComputeGradient = false;
+ this->m_ComputeMovingDomainMask = false;
+ this->m_ComputeAffine = false;
+}
+
+
+template <class TMetricTraits>
+typename itk::DataObject::Pointer
+MultiComponentImageMetricBase<TMetricTraits>
+::MakeOutput(const DataObjectIdentifierType &key)
+{
+ if(key == "Primary")
+ {
+ return (MetricImageType::New()).GetPointer();
+ }
+ else if(key == "phi_gradient")
+ {
+ return (GradientImageType::New()).GetPointer();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+template <class TMetricTraits>
+void
+MultiComponentImageMetricBase<TMetricTraits>
+::ToggleOutput(bool flag, const DataObjectIdentifierType &key)
+{
+ if(flag && !this->HasOutput(key))
+ this->SetOutput(key, this->MakeOutput(key));
+ if(!flag && this->HasOutput(key))
+ this->RemoveOutput(key);
+}
+
+template <class TMetricTraits>
+void
+MultiComponentImageMetricBase<TMetricTraits>
+::UpdateOutputs()
+{
+ this->ToggleOutput(m_ComputeGradient && !m_ComputeAffine, "phi_gradient");
+ this->ToggleOutput(m_ComputeGradient && m_ComputeAffine, "tran_gradient");
+
+ if(m_ComputeAffine)
+ m_AffineTransformGradient = TransformType::New();
+ else
+ m_AffineTransformGradient = NULL;
+}
+
+template <class TMetricTraits>
+void
+MultiComponentImageMetricBase<TMetricTraits>
+::GenerateInputRequestedRegion()
+{
+ // Call the superclass's implementation, which proparages output RR to input RR
+ Superclass::GenerateInputRequestedRegion();
+
+ // Set the moving image RR to maximum
+ this->GetMovingImage()->SetRequestedRegionToLargestPossibleRegion();
+}
+
+
+template <class TMetricTraits>
+void
+MultiComponentImageMetricBase<TMetricTraits>
+::BeforeThreadedGenerateData()
+{
+ // Create the statistics accumulators
+ const InputImageType *fixed = this->GetFixedImage();
+
+ // Create the prototype results vector
+ m_ThreadData.clear();
+ for (unsigned i = 0; i < this->GetNumberOfThreads(); i++)
+ {
+ ThreadData td;
+ td.comp_metric = vnl_vector<double>(fixed->GetNumberOfComponentsPerPixel(), 0.0);
+ m_ThreadData.push_back(td);
+ }
+}
+
+template <class TMetricTraits>
+void
+MultiComponentImageMetricBase<TMetricTraits>
+::AfterThreadedGenerateData()
+{
+ // Compute summary stats
+ const InputImageType *fixed = this->GetFixedImage();
+
+ // Allocate the final result vector
+ m_AccumulatedData = ThreadData();
+ m_AccumulatedData.comp_metric = vnl_vector<double>(fixed->GetNumberOfComponentsPerPixel(), 0.0);
+
+ for(int i = 0; i < m_ThreadData.size(); i++)
+ {
+ m_AccumulatedData.metric += m_ThreadData[i].metric;
+ m_AccumulatedData.mask += m_ThreadData[i].mask;
+ m_AccumulatedData.gradient += m_ThreadData[i].gradient;
+ m_AccumulatedData.grad_mask += m_ThreadData[i].grad_mask;
+ m_AccumulatedData.comp_metric += m_ThreadData[i].comp_metric;
+ }
+
+ /*
+ printf("acc metric: %f\n", m_AccumulatedData.metric);
+ printf("acc mask: %f\n", m_AccumulatedData.mask);
+ */
+
+ // Report the normalized value
+ m_MetricValue = m_AccumulatedData.metric / m_AccumulatedData.mask;
+
+ // Compute the affine gradient
+ if(m_ComputeAffine)
+ {
+ m_AffineTransformGradient = TransformType::New();
+
+
+ vnl_vector<double> grad_metric(m_AccumulatedData.gradient.size());
+ for (unsigned j = 0; j < m_AccumulatedData.gradient.size(); j++)
+ {
+ grad_metric[j] =
+ (this->GetGradientScalingFactor() * m_AccumulatedData.gradient[j]
+ -m_MetricValue * m_AccumulatedData.grad_mask[j])
+ / m_AccumulatedData.mask;
+ }
+
+ // Pack into the output
+ unflatten_affine_transform(grad_metric.data_block(), m_AffineTransformGradient.GetPointer());
+ }
+}
+
+template <class TMetricTraits>
+vnl_vector<double>
+MultiComponentImageMetricBase<TMetricTraits>
+::GetAllMetricValues() const
+{
+ vnl_vector<double> result;
+ result = m_AccumulatedData.comp_metric / m_AccumulatedData.mask;
+ return result;
+
+ /*
+ vnl_vector<double> result;
+ result = m_MetricStats.metric_values / m_MetricStats.num_voxels;
+ return result;
+ */
+}
+
+#include "itkImageLinearIteratorWithIndex.h"
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+#include "FastLinearInterpolator.h"
+
+
+// #define _FAKE_FUNC_
+
+
+
+/**
+ * \class MultiComponentMetricWorker
+ *
+ * This class is an iterator/interpolator that supports metric classes. It supports
+ * linear traversal through the output region, like a ImageLinearIteratorWithIndex.
+ *
+ * It also supports interpolating the moving image at the current location.
+ */
+template <class TMetricTraits, class TOutputImage>
+class MultiComponentMetricWorker
+{
+public:
+ /** Type definitions from the traits class */
+ typedef typename TMetricTraits::InputImageType InputImageType;
+ typedef typename TMetricTraits::MaskImageType MaskImageType;
+ typedef typename TMetricTraits::DeformationFieldType DeformationFieldType;
+ typedef typename TMetricTraits::MetricImageType MetricImageType;
+ typedef typename TMetricTraits::GradientImageType GradientImageType;
+ typedef typename TMetricTraits::TransformType TransformType;
+ typedef typename TMetricTraits::RealType RealType;
+ typedef TOutputImage OutputImageType;
+
+ typedef MultiComponentImageMetricBase<TMetricTraits> MetricType;
+ typedef typename MetricType::OutputImageRegionType RegionType;
+
+ typedef MultiComponentMetricWorker<TMetricTraits,TOutputImage> Self;
+
+ typedef itk::ImageLinearIteratorWithIndex<TOutputImage> IterBase;
+ typedef IteratorExtender<IterBase> IterType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension );
+
+ typedef FastLinearInterpolator<InputImageType, RealType, ImageDimension> InterpType;
+
+ typedef typename InputImageType::IndexType IndexType;
+ typedef itk::ContinuousIndex<double, ImageDimension> ContIndexType;
+
+ typedef typename GradientImageType::PixelType GradientPixelType;
+
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ MultiComponentMetricWorker(MetricType *metric, TOutputImage * image, const RegionType ®ion)
+ : m_WrappedIter(image, region),
+ m_Interpolator(metric->GetMovingImage()),
+ m_OutputImage(image)
+ {
+ m_Metric = metric;
+ m_Affine = (m_Metric->GetDeformationField() == NULL);
+ m_Gradient = m_Metric->GetComputeGradient();
+ m_LineLength = region.GetSize(0);
+ m_FixedStep = m_Metric->GetFixedImage()->GetNumberOfComponentsPerPixel();
+ m_OutputStep = image->GetNumberOfComponentsPerPixel();
+
+ m_MovingSample = new RealType[m_FixedStep];
+ m_MovingSampleGradient = new RealType * [m_FixedStep];
+ for(int i = 0; i < m_FixedStep; i++)
+ m_MovingSampleGradient[i] = new RealType[ImageDimension];
+ m_MaskGradient = new RealType[ImageDimension];
+
+ m_SamplePos = vnl_vector<RealType>(ImageDimension, 0.0);
+ m_SampleStep = vnl_vector<RealType>(ImageDimension, 0.0);
+
+ this->SetupLine();
+ }
+
+ ~MultiComponentMetricWorker()
+ {
+ for(int i = 0; i < m_FixedStep; i++)
+ delete m_MovingSampleGradient[i];
+ delete m_MovingSampleGradient;
+ delete m_MovingSample;
+ delete m_MaskGradient;
+ }
+
+ bool IsAtEnd()
+ {
+ return m_WrappedIter.IsAtEnd();
+ }
+
+ void NextLine()
+ {
+ // Go to the next line
+ m_WrappedIter.NextLine();
+
+ if(!m_WrappedIter.IsAtEnd())
+ this->SetupLine();
+ }
+
+ void SetupLine()
+ {
+ // Get the offset of this line in pixels (not components)
+ m_OffsetInPixels = m_WrappedIter.GetPosition() - m_OutputImage->GetBufferPointer();
+
+ // Set up the arrays for this line
+ m_FixedLine = m_Metric->GetFixedImage()->GetBufferPointer()
+ + m_OffsetInPixels * m_FixedStep;
+
+ // Mask line
+ m_FixedMaskLine = (m_Metric->GetFixedMaskImage())
+ ? m_Metric->GetFixedMaskImage()->GetBufferPointer() + m_OffsetInPixels
+ : NULL;
+
+ // Get the phi line
+ m_PhiLine = m_Affine ? NULL
+ : m_Metric->GetDeformationField()->GetBufferPointer() + m_OffsetInPixels;
+
+ // Get the jitter line
+ m_JitterLine = m_Metric->GetJitterImage()
+ ? m_Metric->GetJitterImage()->GetBufferPointer() + m_OffsetInPixels
+ : NULL;
+
+ // Get the output line
+ m_OutputLine = m_OutputImage->GetBufferPointer() + m_OffsetInPixels * m_OutputStep;
+
+ // Set the current sample position
+ m_Index = m_WrappedIter.GetIndex();
+ if(m_Affine)
+ {
+ for(int d = 0; d < ImageDimension; d++)
+ {
+ m_SamplePos[d] = m_Metric->GetAffineTransform()->GetOffset()[d];
+ m_SampleStep[d] = m_Metric->GetAffineTransform()->GetMatrix()(d, 0);
+ for(int j = 0; j < ImageDimension; j++)
+ m_SamplePos[d] += m_Metric->GetAffineTransform()->GetMatrix()(d,j) * m_Index[j];
+
+ if(m_JitterLine)
+ m_SamplePos[d] += (*m_JitterLine)[d];
+ }
+ }
+ else
+ {
+ for(int d = 0; d < ImageDimension; d++)
+ {
+ m_SamplePos[d] = m_Index[d] + (*m_PhiLine)[d];
+ }
+ }
+ }
+
+ Self &operator ++()
+ {
+ m_Index[0]++;
+ if(m_Index[0] < m_LineLength)
+ {
+ m_FixedLine += m_FixedStep;
+ m_OutputLine += m_OutputStep;
+
+ if(m_FixedMaskLine)
+ m_FixedMaskLine++;
+
+ if(m_Affine)
+ {
+ if(m_JitterLine)
+ {
+ typename TMetricTraits::DeformationFieldType::PixelType *jnext = m_JitterLine + 1;
+ for(int d = 0; d < ImageDimension; d++)
+ m_SamplePos[d] += m_SampleStep[d] - (*m_JitterLine)[d] + (*jnext)[d];
+ m_JitterLine = jnext;
+ }
+ else
+ {
+ for(int d = 0; d < ImageDimension; d++)
+ m_SamplePos[d] += m_SampleStep[d];
+ }
+ }
+ else
+ {
+ m_PhiLine++;
+ for(int d = 0; d < ImageDimension; d++)
+ m_SamplePos[d] = m_Index[d] + (*m_PhiLine)[d];
+ }
+ }
+
+ return *this;
+ }
+
+ bool IsAtEndOfLine()
+ {
+ return m_Index[0] >= m_LineLength;
+ }
+
+ int GetLineLength() { return m_LineLength; }
+
+ int GetLinePos() { return m_Index[0]; }
+
+ typename InterpType::InOut Interpolate()
+ {
+ typename InterpType::InOut status;
+
+ // Clear the moving sample
+ for(int i = 0; i < m_FixedStep; i++)
+ m_MovingSample[i] = 0.0;
+
+#ifdef _FAKE_FUNC_
+
+ for(int i = 0; i < m_FixedStep; i++)
+ {
+ // Fake a function
+ double x = m_SamplePos[0], y = m_SamplePos[1], z = m_SamplePos[2];
+ double a = 0.01, b = 0.005, c = 0.008, d = 0.004;
+ m_MovingSample[i] = sin(a * x * y + b * z) + cos(c * x + d * y * z);
+ m_MovingSampleGradient[i][0] = cos(a * x * y + b * z) * a * y - sin(c * x + d * y * z) * c;
+ m_MovingSampleGradient[i][1] = cos(a * x * y + b * z) * a * x - sin(c * x + d * y * z) * d * z;
+ m_MovingSampleGradient[i][2] = cos(a * x * y + b * z) * b - sin(c * x + d * y * z) * d * y;
+ status = InterpType::INSIDE;
+ }
+
+#else
+
+ // Interpolate the moving image
+ if(m_Gradient)
+ {
+ // Read out the status
+ status = m_Interpolator.InterpolateWithGradient(
+ m_SamplePos.data_block(), m_MovingSample, m_MovingSampleGradient);
+
+ // If the status is 'border', scale the values and the gradient by the mask
+ if(status == InterpType::BORDER)
+ {
+ // Compute the mask
+ m_Mask = m_Interpolator.GetMaskAndGradient(m_MaskGradient);
+ }
+ }
+ else
+ {
+ status = m_Interpolator.Interpolate(
+ m_SamplePos.data_block(), m_MovingSample);
+
+ // If the status is 'border', scale the values and the gradient by the mask
+ if(status == InterpType::BORDER)
+ {
+ // Compute the mask
+ m_Mask = m_Interpolator.GetMask();
+ }
+ }
+
+#endif
+
+ return status;
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramSample(THistContainer &hist)
+ {
+ m_Interpolator.PartialVolumeHistogramSample(m_SamplePos.data_block(), m_FixedLine, hist);
+ }
+
+ template <class THistContainer>
+ void PartialVolumeHistogramGradientSample(const THistContainer &weights, RealType *out_ptr)
+ {
+ m_Interpolator.PartialVolumeHistogramGradientSample(m_SamplePos.data_block(), m_FixedLine, weights, out_ptr);
+ }
+
+
+ long GetOffsetInPixels() { return m_OffsetInPixels; }
+
+ InputComponentType *GetFixedLine() { return m_FixedLine; }
+
+ /**
+ * Returns true if the voxel is not masked out, i.e., either the mask is NULL or
+ * the mask value is > threshold
+ */
+ bool CheckFixedMask(double threshold = 0.0) const { return !m_FixedMaskLine || *m_FixedMaskLine > threshold; }
+
+ OutputComponentType *GetOutputLine() { return m_OutputLine; }
+
+ template <class TImage>
+ typename TImage::InternalPixelType *GetImageLine(TImage *image)
+ { return m_WrappedIter.GetPixelPointer(image); }
+
+ template <class TImage>
+ const typename TImage::InternalPixelType *GetImageLine(const TImage *image)
+ { return m_WrappedIter.GetPixelPointer(image); }
+
+
+ const RealType *GetMovingSample() { return m_MovingSample; }
+
+ const RealType *GetMovingSampleGradient(int k) { return m_MovingSampleGradient[k]; }
+
+ const RealType *GetMaskGradient() { return m_MaskGradient; }
+
+
+ RealType GetMask() { return m_Mask; }
+
+ const IndexType &GetIndex() { return m_Index; }
+
+ const vnl_vector<float> &GetSamplePos() { return m_SamplePos; }
+
+protected:
+
+ MetricType *m_Metric;
+ OutputImageType *m_OutputImage;
+ IterType m_WrappedIter;
+
+
+ InputComponentType *m_FixedLine;
+ typename MaskImageType::PixelType *m_FixedMaskLine;
+ typename TMetricTraits::DeformationFieldType::PixelType *m_PhiLine;
+ typename TMetricTraits::DeformationFieldType::PixelType *m_JitterLine;
+ typename TOutputImage::InternalPixelType *m_OutputLine;
+
+ int m_LineLength;
+ int m_FixedStep, m_OutputStep;
+ long m_OffsetInPixels;
+
+ IndexType m_Index;
+ vnl_vector<RealType> m_SamplePos, m_SampleStep;
+
+ InterpType m_Interpolator;
+
+ RealType *m_MovingSample, **m_MovingSampleGradient, *m_MaskGradient;
+ RealType m_Mask;
+
+ bool m_Affine, m_Gradient;
+
+};
+
+
+#endif // __MultiComponentImageMetricBase_txx_
diff --git a/Submodules/greedy/src/MultiComponentMutualInfoImageMetric.h b/Submodules/greedy/src/MultiComponentMutualInfoImageMetric.h
new file mode 100644
index 0000000..2a96969
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentMutualInfoImageMetric.h
@@ -0,0 +1,367 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#ifndef MULTICOMPONENTMUTUALINFOIMAGEMETRIC_H
+#define MULTICOMPONENTMUTUALINFOIMAGEMETRIC_H
+
+#include "MultiComponentImageMetricBase.h"
+#include "itkBarrier.h"
+#include <queue>
+#include <vector>
+#include <functional>
+
+/**
+ * Default traits for parameterizing the metric filters below.
+ */
+template <class TReal, class TIndex, unsigned int VDim>
+struct DefaultMultiComponentMutualInfoImageMetricTraits
+{
+ typedef itk::VectorImage<TIndex, VDim> InputImageType;
+ typedef itk::Image<TReal, VDim> ScalarImageType;
+ typedef itk::Image<itk::CovariantVector<TReal, VDim>, VDim> VectorImageType;
+
+ typedef ScalarImageType MaskImageType;
+ typedef VectorImageType DeformationFieldType;
+ typedef VectorImageType GradientImageType;
+ typedef ScalarImageType MetricImageType;
+ typedef itk::MatrixOffsetTransformBase<TReal, VDim, VDim> TransformType;
+
+ typedef TReal RealType;
+};
+
+/**
+ * Normalized mutual information metric function
+ */
+template <class TReal>
+class NormalizedMutualInformationMetricFunction
+{
+public:
+ static TReal compute(int n_bins,
+ const vnl_matrix<TReal> &Pfm,
+ const vnl_vector<TReal> &Pf,
+ const vnl_vector<TReal> &Pm,
+ vnl_matrix<TReal> *gradWeights);
+};
+
+/**
+ * Plain vanilla mutual information metric function
+ */
+template <class TReal>
+class StandardMutualInformationMetricFunction
+{
+public:
+ static TReal compute(int n_bins,
+ const vnl_matrix<TReal> &Pfm,
+ const vnl_vector<TReal> &Pf,
+ const vnl_vector<TReal> &Pm,
+ vnl_matrix<TReal> *gradWeights);
+};
+
+
+template <class TMetricTraits>
+class ITK_EXPORT MultiComponentMutualInfoImageMetric :
+ public MultiComponentImageMetricBase<TMetricTraits>
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiComponentMutualInfoImageMetric<TMetricTraits> Self;
+ typedef MultiComponentImageMetricBase<TMetricTraits> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageOpticalFlowImageFilter, MultiComponentImageMetricBase )
+
+ /** Typedef to describe the output image region type. */
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::InputPixelType InputPixelType;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::MetricImageType MetricImageType;
+ typedef typename Superclass::GradientImageType GradientImageType;
+ typedef typename Superclass::MetricPixelType MetricPixelType;
+ typedef typename Superclass::GradientPixelType GradientPixelType;
+ typedef typename Superclass::RealType RealType;
+
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::SpacingType SpacingType;
+ typedef typename Superclass::DirectionType DirectionType;
+ typedef typename Superclass::ImageBaseType ImageBaseType;
+
+ /** Information from the deformation field class */
+ typedef typename Superclass::DeformationFieldType DeformationFieldType;
+ typedef typename Superclass::DeformationFieldPointer DeformationFieldPointer;
+ typedef typename Superclass::DeformationVectorType DeformationVectorType;
+ typedef typename Superclass::TransformType TransformType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension );
+
+ /** Working image - used for intermediate storage of information */
+ // typedef std::pair<InputPixelType, RealType> IndexWeightPair;
+ // typedef itk::Image<IndexWeightPair, ImageDimension> WorkingImageType;
+
+ /**
+ * Number of bins. Important - the intensity values in the fixed and moving images must
+ * be integers between 0 and nBins - 1.
+ */
+ itkSetMacro(Bins, unsigned int)
+
+ /** Get the number of bins */
+ itkGetMacro(Bins, unsigned int)
+
+ /**
+ * Get the gradient scaling factor. To get the actual gradient of the metric, multiply the
+ * gradient output of this filter by the scaling factor. Explanation: for efficiency, the
+ * metrics return an arbitrarily scaled vector, such that adding the gradient to the
+ * deformation field would INCREASE SIMILARITY. For metrics that are meant to be minimized,
+ * this is the opposite of the gradient direction. For metrics that are meant to be maximized,
+ * it is the gradient direction.
+ */
+ virtual double GetGradientScalingFactor() const { return 1.0; }
+
+ /**
+ * When this flag is On, the metric will use the Normalized Mutual Information formulation
+ * proposed by Studholme et al., Pattern Recognition, 1999.
+ */
+ itkSetMacro(ComputeNormalizedMutualInformation, bool)
+ itkGetMacro(ComputeNormalizedMutualInformation, bool)
+
+protected:
+
+ virtual void BeforeThreadedGenerateData();
+ virtual void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+protected:
+ MultiComponentMutualInfoImageMetric()
+ : m_Bins(32), m_ComputeNormalizedMutualInformation(false) { }
+
+ ~MultiComponentMutualInfoImageMetric() {}
+
+private:
+ MultiComponentMutualInfoImageMetric(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // Barrier - for thread management
+ typename itk::Barrier::Pointer m_Barrier;
+
+ // Number of bins
+ unsigned int m_Bins;
+
+ // What flavor of mutual information will we use
+ bool m_ComputeNormalizedMutualInformation;
+
+ // Combined histogram representation
+ struct Histogram
+ {
+ vnl_matrix<RealType> Pfm, Wfm;
+ vnl_vector<RealType> Pf, Pm;
+ Histogram(int bins) : Pfm(bins, bins, 0.0), Pf(bins, 0.0), Pm(bins, 0.0), Wfm(bins, bins, 0.0) {}
+ };
+
+ // Histogram accumulator - array over the components in the image
+ typedef std::vector< vnl_matrix<RealType> > HistogramAccumType;
+
+ // Weights for gradient computation - derived from the histogram. For each component, this is a
+ // matrix holding derivatives of the metric with respect to the height of the i,j-th bin in the
+ // histogram.
+ HistogramAccumType m_GradWeights;
+
+ // Histogram accumulator for each thread
+ std::vector<HistogramAccumType> m_MIThreadData;
+
+ // Joint probability and marginals for each component
+ std::vector<Histogram> m_Histograms;
+
+};
+
+
+
+/**
+ * A helper filter to remap intensities for mutual information. It can double
+ * as a quick way to compute per-component quantiles of a multi-component image
+ */
+template <class TInputImage, class TOutputImage>
+class MutualInformationPreprocessingFilter
+ : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ typedef MutualInformationPreprocessingFilter<TInputImage, TOutputImage> Self;
+ typedef itk::ImageToImageFilter<TInputImage, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MutualInformationPreprocessingFilter, ImageToImageFilter )
+
+ /** Typedef to describe the output image region type. */
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename InputImageType::InternalPixelType InputComponentType;
+ typedef typename Superclass::OutputImageType OutputImageType;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ typedef typename InputImageType::IndexType IndexType;
+ typedef typename InputImageType::SizeType SizeType;
+
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension );
+
+ /** Set the desired number of bins into which to partition the image */
+ itkSetMacro(Bins, unsigned int)
+
+ /**
+ * Set the lower quantile (default 0), below which all values are treated as equal
+ * to the minimum value.
+ */
+ itkSetMacro(LowerQuantile, double)
+
+ /**
+ * Set the upper quantile (default 0.99), above which all values are treated as equal
+ * to the maximum value.
+ */
+ itkSetMacro(UpperQuantile, double)
+
+ /**
+ * When this flag is set, the quantiles are computed and nothing else is done, i.e., the
+ * input image is passed on as is. Set the filter to be in place.
+ */
+ itkSetMacro(NoRemapping, bool)
+
+ /**
+ * When this flag is set, the lowest intensity in the image is mapped to bin 1, rather than
+ * bin 0. This leaves bin 0 in the histogram empty. Subsequently, it can be used to represent
+ * outside values. In other words, when this is set, the remapped image will have intensities
+ * between 1 and n_Bins-1, when it is not set, this will be between 0 and n_Bins-1
+ *
+ * By default this flag is False.
+ */
+ itkSetMacro(StartAtBinOne, bool)
+
+ /** After the filter ran, get the value of the lower quantile */
+ InputComponentType GetLowerQuantileValue(int component) const
+ { return m_LowerQuantileValues[component]; }
+
+ /** After the filter ran, get the value of the upper quantile */
+ InputComponentType GetUpperQuantileValue(int component) const
+ { return m_UpperQuantileValues[component]; }
+
+protected:
+ MutualInformationPreprocessingFilter();
+ ~MutualInformationPreprocessingFilter() {}
+
+ virtual void GenerateOutputInformation();
+
+ virtual void BeforeThreadedGenerateData();
+ virtual void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+private:
+ MutualInformationPreprocessingFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // Number of bins
+ unsigned int m_Bins;
+
+ // The quantile to map to the upper and lower bins.
+ double m_LowerQuantile, m_UpperQuantile;
+
+ // Heap data types
+ typedef std::priority_queue<
+ InputComponentType, std::vector<InputComponentType>, std::less<InputComponentType> > LowerHeap;
+
+ typedef std::priority_queue<
+ InputComponentType, std::vector<InputComponentType>, std::greater<InputComponentType> > UpperHeap;
+
+ void heap_lower_push(LowerHeap &heap, int max_size, const InputComponentType &v)
+ {
+ if(heap.size() < max_size)
+ {
+ heap.push(v);
+ }
+ else if(heap.top() > v)
+ {
+ heap.pop();
+ heap.push(v);
+ }
+ }
+
+ void heap_upper_push(UpperHeap &heap, int max_size, const InputComponentType &v)
+ {
+ if(heap.size() < max_size)
+ {
+ heap.push(v);
+ }
+ else if(heap.top() < v)
+ {
+ heap.pop();
+ heap.push(v);
+ }
+ }
+
+ // Per thread data
+ struct ThreadData
+ {
+ // Heaps for minimum and maximum intensities
+ LowerHeap heap_lower;
+ UpperHeap heap_upper;
+ };
+
+ std::vector<ThreadData> m_ThreadData;
+
+ typename itk::Barrier::Pointer m_Barrier;
+
+ std::vector<InputComponentType> m_LowerQuantileValues, m_UpperQuantileValues;
+
+ bool m_NoRemapping;
+
+ bool m_StartAtBinOne;
+};
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiComponentMutualInfoImageMetric.txx"
+#endif
+
+
+#endif // MULTICOMPONENTMUTUALINFOIMAGEMETRIC_H
diff --git a/Submodules/greedy/src/MultiComponentMutualInfoImageMetric.txx b/Submodules/greedy/src/MultiComponentMutualInfoImageMetric.txx
new file mode 100644
index 0000000..08baddd
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentMutualInfoImageMetric.txx
@@ -0,0 +1,592 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef MULTICOMPONENTMUTUALINFOIMAGEMETRIC_TXX
+#define MULTICOMPONENTMUTUALINFOIMAGEMETRIC_TXX
+
+#include "MultiComponentMutualInfoImageMetric.h"
+
+
+/**
+ * Implementation of the Normalized Mutual Information (Studholme) method
+ *
+ * Metric = (H(M) + H(F)) / H(M,F)
+ */
+template <class TReal>
+TReal
+NormalizedMutualInformationMetricFunction<TReal>
+::compute(int n_bins,
+ const vnl_matrix<TReal> &mat_Pfm,
+ const vnl_vector<TReal> &mat_Pf,
+ const vnl_vector<TReal> &mat_Pm,
+ vnl_matrix<TReal> *gradWeights)
+{
+ // We need the joint and marginal entropies for the calculation
+ TReal Hfm = 0, Hf = 0, Hm = 0;
+
+ // Simple case - no gradient
+ if(!gradWeights)
+ {
+ for(int i = 1; i < n_bins; i++)
+ {
+ TReal Pf = mat_Pf(i);
+ TReal Pm = mat_Pm(i);
+
+ if(Pf > 0)
+ Hf += Pf * log(Pf);
+
+ if(Pm > 0)
+ Hm += Pm * log(Pm);
+
+ for(int j = 1; j < n_bins; j++)
+ {
+ TReal Pfm = mat_Pfm(i, j);
+ if(Pfm > 0)
+ Hfm += Pfm * log(Pfm);
+ }
+ }
+
+ return (Hf + Hm) / Hfm;
+ }
+
+ else
+ {
+ // Allocate vectors to hold log(Pf), log(Pm)
+ vnl_vector<TReal> log_Pf(n_bins, 0.0), log_Pm(n_bins, 0.0);
+
+ for(int i = 1; i < n_bins; i++)
+ {
+ TReal Pf = mat_Pf(i);
+ TReal Pm = mat_Pm(i);
+
+ if(Pf > 0)
+ {
+ log_Pf(i) = log(Pf);
+ Hf += Pf * log_Pf(i);
+ }
+
+ if(Pm > 0)
+ {
+ log_Pm(i) = log(Pm);
+ Hm += Pm * log_Pm(i);
+ }
+
+ for(int j = 1; j < n_bins; j++)
+ {
+ TReal Pfm = mat_Pfm(i, j);
+ if(Pfm > 0)
+ {
+ TReal log_Pfm = log(Pfm);
+ Hfm += Pfm * log_Pfm;
+ (*gradWeights)(i, j) = log_Pfm; // store for future use
+ }
+ }
+ }
+
+ // Compute the metric
+ TReal metric = (Hf + Hm) / Hfm;
+
+ // Compute the gradient
+ for(int i = 1; i < n_bins; i++)
+ {
+ for(int j = 1; j < n_bins; j++)
+ {
+ TReal Pfm = mat_Pfm(i, j);
+ if(Pfm > 0)
+ {
+ // Reuse the log
+ TReal log_Pfm = (*gradWeights)(i, j);
+ (*gradWeights)(i,j) = (2 + log_Pf(i) + log_Pm(j) - metric * (log_Pfm + 1)) / Hfm;
+ }
+ else
+ {
+ (*gradWeights)(i,j) = 0.0;
+ }
+ }
+ }
+
+ // Return the metric
+ return metric;
+ }
+}
+
+/**
+ * Implementation of the standard Mutual Information method
+ *
+ * Metric = H(M,F) - H(M) - H(F)
+ */
+template <class TReal>
+TReal
+StandardMutualInformationMetricFunction<TReal>
+::compute(int n_bins,
+ const vnl_matrix<TReal> &mat_Pfm,
+ const vnl_vector<TReal> &mat_Pf,
+ const vnl_vector<TReal> &mat_Pm,
+ vnl_matrix<TReal> *gradWeights)
+{
+ TReal metric = 0;
+ for(int bf = 1; bf < n_bins; bf++)
+ {
+ for(int bm = 1; bm < n_bins; bm++)
+ {
+ TReal Pfm = mat_Pfm(bf, bm);
+ TReal Pf = mat_Pf(bf);
+ TReal Pm = mat_Pm(bm);
+
+ if(Pfm > 0)
+ {
+ // This expression is actually correct for computing H(I,J) - (H(I) + H(J))
+ double q = log(Pfm / (Pf * Pm));
+ double v = Pfm * q;
+ metric += v;
+
+ // If computing the gradient, also compute the additional weight information
+ if(gradWeights)
+ {
+ (*gradWeights)[bf][bm] = q - 1;
+ }
+ }
+ }
+ }
+
+ return metric;
+}
+
+
+template <class TMetricTraits>
+void
+MultiComponentMutualInfoImageMetric<TMetricTraits>
+::BeforeThreadedGenerateData()
+{
+ Superclass::BeforeThreadedGenerateData();
+
+ int ncomp = this->GetFixedImage()->GetNumberOfComponentsPerPixel();
+
+ // Clear the per-thread histograms
+ m_MIThreadData.clear();
+
+ // Create the per-thread histograms
+ m_MIThreadData.resize(this->GetNumberOfThreads(),
+ HistogramAccumType(ncomp, vnl_matrix<RealType>(m_Bins, m_Bins, 0.0)));
+
+ // Initialize the gradient matrices
+ if(this->m_ComputeGradient)
+ m_GradWeights.resize(ncomp, vnl_matrix<RealType>(m_Bins, m_Bins, 0.0));
+
+ // Code to determine the actual number of threads used below
+ itk::ThreadIdType nbOfThreads = this->GetNumberOfThreads();
+ if ( itk::MultiThreader::GetGlobalMaximumNumberOfThreads() != 0 )
+ {
+ nbOfThreads = vnl_math_min( this->GetNumberOfThreads(), itk::MultiThreader::GetGlobalMaximumNumberOfThreads() );
+ }
+
+ itk::ImageRegion<ImageDimension> splitRegion; // dummy region - just to call
+ // the following method
+ nbOfThreads = this->SplitRequestedRegion(0, nbOfThreads, splitRegion);
+
+ // Initialize the barrier
+ m_Barrier = itk::Barrier::New();
+ m_Barrier->Initialize(nbOfThreads);
+}
+
+
+template <class TMetricTraits>
+void
+MultiComponentMutualInfoImageMetric<TMetricTraits>
+::ThreadedGenerateData(
+ const OutputImageRegionType &outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ // Get the number of components
+ int ncomp = this->GetFixedImage()->GetNumberOfComponentsPerPixel();
+
+ // Create an iterator specialized for going through metrics
+ typedef MultiComponentMetricWorker<TMetricTraits, MetricImageType> InterpType;
+ InterpType iter(this, this->GetMetricOutput(), outputRegionForThread);
+
+ // Initially, I am implementing this as a two-pass filter. On the first pass, the joint
+ // histogram is computed without the gradient. On the second pass, the gradient is computed.
+ // The inefficiency of this implementation is that the interpolation code is being called
+ // twice. The only way I see to avoid this is to store the results of each interpolation in
+ // an intermediate working image, but I am not sure how much one would save from that!
+
+ // Importantly, the input images are rescaled to the range 1..nBins. This means that the
+ // 0 row and 0 column of the histogram are reserved for outside values, or in other words
+ // outside values are treated differently from zero. This is important for the computation
+ // of the overlap-invariant metrics.
+
+ // First pass - compute the histograms
+ HistogramAccumType &thread_histogram = m_MIThreadData[threadId];
+
+ // Iterate over the lines
+ for(; !iter.IsAtEnd(); iter.NextLine())
+ {
+ // Iterate over the pixels in the line
+ for(; !iter.IsAtEndOfLine(); ++iter)
+ {
+ // Get the current histogram corners
+ if(iter.CheckFixedMask())
+ iter.PartialVolumeHistogramSample(thread_histogram);
+ }
+ }
+
+ // Wait for all the threads to finish this computation
+ m_Barrier->Wait();
+
+ // Add up and process the histograms - use the first one as the target for storage
+ if(threadId == 0)
+ {
+ // Initialize the histograms per component
+ m_Histograms.clear();
+
+ // All procesing is separate for each component
+ for(int c = 0; c < ncomp; c++)
+ {
+ // The histogram for this component
+ m_Histograms.push_back(Histogram(m_Bins));
+ Histogram &hc = m_Histograms.back();
+
+ // When computing the empirical joint probability, we will ignore outside values.
+ // We need multiple passes through the histogram to calculate the emprirical prob.
+
+ // First pass, add thread data and compute the sum of all non-outside histogram bin balues
+ double hist_sum = 0.0;
+ for (unsigned bf = 1; bf < m_Bins; bf++)
+ {
+ for(unsigned bm = 1; bm < m_Bins; bm++)
+ {
+ // Reference to the joint probability entry
+ RealType &Pfm = hc.Pfm(bf,bm);
+
+ // Add the entries from all threads
+ for (unsigned q = 0; q < this->GetNumberOfThreads(); q++)
+ Pfm += m_MIThreadData[q][c][bf][bm];
+
+ // Accumulate the sum of all entries
+ hist_sum += hc.Pfm(bf,bm);
+ }
+ }
+
+ // Second pass, normalize the entries and compute marginals
+ for (unsigned bf = 1; bf < m_Bins; bf++)
+ {
+ for(unsigned bm = 1; bm < m_Bins; bm++)
+ {
+ // Reference to the joint probability entry
+ RealType &Pfm = hc.Pfm(bf,bm);
+
+ // Normalize to make a probability
+ Pfm /= hist_sum;
+
+ // Add up the marginals
+ hc.Pf[bf] += Pfm;
+ hc.Pm[bm] += Pfm;
+ }
+ }
+
+ // Third pass: compute the mutual information for this component and overall
+ double &m_comp = this->m_ThreadData[0].comp_metric[c];
+ double &m_total = this->m_ThreadData[0].metric;
+
+ // Compute the metric and gradient for this component using the emprical probabilities
+ if(this->m_ComputeNormalizedMutualInformation)
+ m_comp = NormalizedMutualInformationMetricFunction<RealType>::compute(
+ m_Bins, hc.Pfm, hc.Pf, hc.Pm, this->m_ComputeGradient ? &this->m_GradWeights[c] : NULL);
+ else
+ m_comp = StandardMutualInformationMetricFunction<RealType>::compute(
+ m_Bins, hc.Pfm, hc.Pf, hc.Pm, this->m_ComputeGradient ? &this->m_GradWeights[c] : NULL);
+
+ m_total += m_comp;
+
+ if(this->m_ComputeGradient)
+ {
+ // The gradient is currently relative to emprical probabilities. Convert it to gradient
+ // in terms of the bin counts
+ double grad_weights_dot_Pfm = 0.0;
+
+ for (unsigned bf = 1; bf < m_Bins; bf++)
+ {
+ for(unsigned bm = 1; bm < m_Bins; bm++)
+ {
+ double Pfm = hc.Pfm(bf, bm);
+ if(Pfm > 0)
+ grad_weights_dot_Pfm += m_GradWeights[c][bf][bm] * Pfm;
+ }
+ }
+
+ for (unsigned bf = 1; bf < m_Bins; bf++)
+ {
+ for(unsigned bm = 1; bm < m_Bins; bm++)
+ {
+ m_GradWeights[c][bf][bm] = (m_GradWeights[c][bf][bm] - grad_weights_dot_Pfm) / hist_sum;
+ }
+ }
+ }
+
+ } // loop over components
+
+ // The last thing is to set the normalizing constant to 1
+ this->m_ThreadData[0].mask = 1.0;
+
+ } // If thread_id = 0
+
+ // Wait for all threads
+ m_Barrier->Wait();
+
+ // At this point, we should be computing the gradient using the probability values computed above
+ if(this->m_ComputeGradient && !this->m_ComputeAffine)
+ {
+ GradientPixelType *grad_buffer = this->GetDeformationGradientOutput()->GetBufferPointer();
+
+ // Iterate one more time through the voxels
+ InterpType iter_g(this, this->GetMetricOutput(), outputRegionForThread);
+ for(; !iter_g.IsAtEnd(); iter_g.NextLine())
+ {
+ // Get the output gradient pointer at the beginning of this line
+ GradientPixelType *grad_line = iter_g.GetOffsetInPixels() + grad_buffer;
+
+ // Iterate over the pixels in the line
+ for(; !iter_g.IsAtEndOfLine(); ++iter_g, grad_line++)
+ {
+ if(iter_g.CheckFixedMask())
+ {
+ // Reference to the gradient pointer
+ GradientPixelType &grad_x = *grad_line;
+
+ // Get the current histogram corners
+ iter_g.PartialVolumeHistogramGradientSample(m_GradWeights, grad_x.GetDataPointer());
+ }
+ }
+ }
+ }
+
+ else if(this->m_ComputeGradient && this->m_ComputeAffine)
+ {
+ GradientPixelType grad_x;
+ typename Superclass::ThreadData &tds = this->m_ThreadData[threadId];
+
+ int nvox = this->GetInput()->GetBufferedRegion().GetNumberOfPixels();
+
+ // Iterate one more time through the voxels
+ InterpType iter_g(this, this->GetMetricOutput(), outputRegionForThread);
+ for(; !iter_g.IsAtEnd(); iter_g.NextLine())
+ {
+ // Iterate over the pixels in the line
+ for(; !iter_g.IsAtEndOfLine(); ++iter_g)
+ {
+ if(iter_g.CheckFixedMask())
+ {
+ // Get the current histogram corners
+ iter_g.PartialVolumeHistogramGradientSample(m_GradWeights, grad_x.GetDataPointer());
+
+ // Add the gradient
+ for(int i = 0, q = 0; i < ImageDimension; i++)
+ {
+ // double v = grad_x[i] / nvox;
+ double v = grad_x[i];
+ tds.gradient[q++] += v;
+ for(int j = 0; j < ImageDimension; j++)
+ tds.gradient[q++] += v * iter_g.GetIndex()[j];
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+template <class TInputImage, class TOutputImage>
+MutualInformationPreprocessingFilter<TInputImage, TOutputImage>
+::MutualInformationPreprocessingFilter()
+{
+ m_Barrier = itk::Barrier::New();
+
+ m_LowerQuantile = 0.0;
+ m_UpperQuantile = 0.99;
+
+ m_NoRemapping = false;
+ m_StartAtBinOne = false;
+}
+
+template <class TInputImage, class TOutputImage>
+void
+MutualInformationPreprocessingFilter<TInputImage, TOutputImage>
+::GenerateOutputInformation()
+{
+ Superclass::GenerateOutputInformation();
+ this->GetOutput()->SetNumberOfComponentsPerPixel(this->GetInput()->GetNumberOfComponentsPerPixel());
+}
+
+template <class TInputImage, class TOutputImage>
+void
+MutualInformationPreprocessingFilter<TInputImage, TOutputImage>
+::BeforeThreadedGenerateData()
+{
+ m_ThreadData.clear();
+ m_ThreadData.resize(this->GetNumberOfThreads());
+
+ // Code to determine the actual number of threads used below
+ itk::ThreadIdType nbOfThreads = this->GetNumberOfThreads();
+ if ( itk::MultiThreader::GetGlobalMaximumNumberOfThreads() != 0 )
+ {
+ nbOfThreads = vnl_math_min( this->GetNumberOfThreads(), itk::MultiThreader::GetGlobalMaximumNumberOfThreads() );
+ }
+
+ typename TOutputImage::RegionType splitRegion; // dummy region - just to call
+ // the following method
+ nbOfThreads = this->SplitRequestedRegion(0, nbOfThreads, splitRegion);
+
+
+ m_Barrier = itk::Barrier::New();
+ m_Barrier->Initialize(nbOfThreads);
+
+ m_LowerQuantileValues.resize(this->GetInput()->GetNumberOfComponentsPerPixel());
+ m_UpperQuantileValues.resize(this->GetInput()->GetNumberOfComponentsPerPixel());
+}
+
+template <class TInputImage, class TOutputImage>
+void
+MutualInformationPreprocessingFilter<TInputImage, TOutputImage>
+::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId)
+{
+ // Determine the size of the heap
+ int total_pixels = this->GetInput()->GetBufferedRegion().GetNumberOfPixels();
+ int heap_size_upper = 1 + (int)((1.0 - m_UpperQuantile) * total_pixels);
+ int heap_size_lower = 1 + (int)(m_LowerQuantile * total_pixels);
+ int line_length = outputRegionForThread.GetSize(0);
+
+ // Thread data for this thread
+ ThreadData &td = m_ThreadData[threadId];
+
+ typedef itk::ImageLinearConstIteratorWithIndex<InputImageType> IterBase;
+ typedef IteratorExtender<IterBase> Iterator;
+
+ // Iterate over each component
+ int ncomp = this->GetInput()->GetNumberOfComponentsPerPixel();
+ for(int k = 0; k < ncomp; k++)
+ {
+ // Initialize the two heaps
+ td.heap_lower = LowerHeap();
+ td.heap_upper = UpperHeap();
+
+ // Build up the heaps
+ for(Iterator it(this->GetInput(), outputRegionForThread); !it.IsAtEnd(); it.NextLine())
+ {
+ // Get a pointer to the start of the line
+ const InputComponentType *line = it.GetPixelPointer(this->GetInput()) + k;
+
+ // Iterate over the line
+ for(int p = 0; p < line_length; p++, line+=ncomp)
+ {
+ InputComponentType v = *line;
+ heap_lower_push(td.heap_lower, heap_size_lower, v);
+ heap_upper_push(td.heap_upper, heap_size_upper, v);
+ }
+ }
+
+ // Wait for the threads to synchronize
+ m_Barrier->Wait();
+
+ // The main thread combines all the priority queues
+ if(threadId == 0)
+ {
+ // Combine the priority queues
+ for(unsigned q = 1; q < this->GetNumberOfThreads(); q++)
+ {
+ ThreadData &tdq = m_ThreadData[q];
+ while(!tdq.heap_lower.empty())
+ {
+ InputComponentType v = tdq.heap_lower.top();
+ heap_lower_push(td.heap_lower, heap_size_lower, v);
+ tdq.heap_lower.pop();
+ }
+
+ while(!tdq.heap_upper.empty())
+ {
+ InputComponentType v = tdq.heap_upper.top();
+ heap_upper_push(td.heap_upper, heap_size_upper, v);
+ tdq.heap_upper.pop();
+ }
+ }
+
+ // Get the quantile values
+ m_UpperQuantileValues[k] = td.heap_upper.top();
+ m_LowerQuantileValues[k] = td.heap_lower.top();
+ }
+
+ // Wait for all threads to catch up
+ m_Barrier->Wait();
+
+ // Continue if no remapping requested
+ if(m_NoRemapping)
+ continue;
+
+ // Which bin do we start at
+ unsigned start_bin = m_StartAtBinOne ? 1 : 0;
+
+ // Compute the scale and shift
+ double scale = (m_Bins - start_bin) * 1.0 / (m_UpperQuantileValues[k] - m_LowerQuantileValues[k]);
+ double shift = m_LowerQuantileValues[k] * scale - start_bin;
+
+ // Now each thread remaps the intensities into the quantile range
+ for(Iterator it(this->GetInput(), outputRegionForThread); !it.IsAtEnd(); it.NextLine())
+ {
+ // Get a pointer to the start of the line
+ const InputComponentType *line = it.GetPixelPointer(this->GetInput()) + k;
+ OutputComponentType *out_line = it.GetPixelPointer(this->GetOutput()) + k;
+
+ // Iterate over the line
+ for(int p = 0; p < line_length; p++, line+=ncomp, out_line+=ncomp)
+ {
+ unsigned bin = (int) (*line * scale - shift);
+ if(bin < start_bin)
+ *out_line = start_bin;
+ else if(bin >= m_Bins)
+ *out_line = m_Bins - 1;
+ else
+ *out_line = bin;
+ }
+ }
+ } // loop over components
+}
+
+
+
+
+#endif // MULTICOMPONENTMUTUALINFOIMAGEMETRIC_TXX
diff --git a/Submodules/greedy/src/MultiComponentNCCImageMetric.h b/Submodules/greedy/src/MultiComponentNCCImageMetric.h
new file mode 100644
index 0000000..2852e90
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentNCCImageMetric.h
@@ -0,0 +1,271 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef MULTICOMPONENTNCCIMAGEMETRIC_H
+#define MULTICOMPONENTNCCIMAGEMETRIC_H
+
+#include "MultiComponentImageMetricBase.h"
+#include "itkBarrier.h"
+
+/**
+ * Normalized cross-correlation metric. This filter sets up a mini-pipeline with
+ * a pre-compute filter that interpolates the moving image, N one-dimensional
+ * mean filters, and a post-compute filter that generates the metric and the
+ * gradient.
+ */
+template <class TMetricTraits>
+class ITK_EXPORT MultiComponentNCCImageMetric :
+ public MultiComponentImageMetricBase<TMetricTraits>
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiComponentNCCImageMetric<TMetricTraits> Self;
+ typedef MultiComponentImageMetricBase<TMetricTraits> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiComponentNCCImageMetric, MultiComponentImageMetricBase )
+
+ /** Typedef to describe the output image region type. */
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::InputPixelType InputPixelType;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::MetricImageType MetricImageType;
+ typedef typename Superclass::MetricPixelType MetricPixelType;
+ typedef typename Superclass::GradientImageType GradientImageType;
+ typedef typename Superclass::GradientPixelType GradientPixelType;
+ typedef typename Superclass::MaskImageType MaskImageType;
+
+
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::SpacingType SpacingType;
+ typedef typename Superclass::DirectionType DirectionType;
+ typedef typename Superclass::ImageBaseType ImageBaseType;
+
+ /** Information from the deformation field class */
+ typedef typename Superclass::DeformationFieldType DeformationFieldType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension );
+
+ /** Set the radius of the cross-correlation */
+ itkSetMacro(Radius, SizeType)
+
+ /** Get the radius of the cross-correlation */
+ itkGetMacro(Radius, SizeType)
+
+ /**
+ * Set this flag to true to compute the approximate gradient of the metric, in the
+ * direction of the (fixed - why?) image gradient.
+ */
+ itkSetMacro(ApproximateGradient, bool)
+
+ /** Get the approximate gradient flag */
+ itkGetMacro(ApproximateGradient, bool)
+
+ /**
+ * Set the working memory image for this filter. This function should be used to prevent
+ * repeated allocation of memory when the metric is created/destructed in a loop. The
+ * user can just pass in a pointer to a blank image, the filter will take care of allocating
+ * the image as necessary
+ */
+ itkSetObjectMacro(WorkingImage, InputImageType)
+
+ /**
+ * Whether we should reuse the fixed components in the working image. This is true
+ * if the filter is being run repeatedly on the same image
+ */
+ itkSetMacro(ReuseWorkingImageFixedComponents, bool)
+
+ /**
+ * Get the gradient scaling factor. To get the actual gradient of the metric, multiply the
+ * gradient output of this filter by the scaling factor. Explanation: for efficiency, the
+ * metrics return an arbitrarily scaled vector, such that adding the gradient to the
+ * deformation field would INCREASE SIMILARITY. For metrics that are meant to be minimized,
+ * this is the opposite of the gradient direction. For metrics that are meant to be maximized,
+ * it is the gradient direction.
+ */
+ virtual double GetGradientScalingFactor() const { return 1.0; }
+
+
+protected:
+ MultiComponentNCCImageMetric()
+ : m_ApproximateGradient(false), m_ReuseWorkingImageFixedComponents(false)
+ { m_Radius.Fill(1); }
+
+ ~MultiComponentNCCImageMetric() {}
+
+ // TODO: set up for proper streaming
+ // virtual void GenerateInputRequestedRegion();
+
+ virtual void BeforeThreadedGenerateData();
+ virtual void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread,
+ itk::ThreadIdType threadId);
+
+private:
+ MultiComponentNCCImageMetric(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // A pointer to the working image. The user should supply this image in order to prevent
+ // unnecessary memory allocation
+ typename InputImageType::Pointer m_WorkingImage;
+
+ // Whether we should reuse the fixed components in the working image. This is true
+ // if the filter is being run repeatedly on the same image
+ bool m_ReuseWorkingImageFixedComponents;
+
+ // Radius of the cross-correlation
+ SizeType m_Radius;
+
+ // Whether to compute the approximate gradient of the metric, as in Avants et al., 2008.
+ // When not set, the exact gradient is computed
+ bool m_ApproximateGradient;
+};
+
+
+
+
+
+
+/**
+ * \class MultiImageNCCPrecomputeFilter
+ * \brief Helps compute the NCC metric
+ *
+ * This filter takes a pair of images plus a warp and computes the components that
+ * are used to calculate the cross-correlation metric between them and
+ * the gradient. These components are in the form I, I*J, I * gradJ, and
+ * so on. These components must then be mean-filtered and combined to get the
+ * metric and the gradient.
+ *
+ * The output of this filter must be a vector image. The input may be a vector image.
+ *
+ */
+template <class TMetricTraits, class TOutputImage>
+class ITK_EXPORT MultiImageNCCPrecomputeFilter :
+ public itk::ImageToImageFilter<typename TMetricTraits::InputImageType, TOutputImage>
+{
+public:
+
+ /** The parent/owner class */
+ typedef MultiComponentNCCImageMetric<TMetricTraits> ParentType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename ParentType::InputImageType InputImageType;
+ typedef typename ParentType::InputPixelType InputPixelType;
+ typedef typename ParentType::InputComponentType InputComponentType;
+ typedef typename ParentType::DeformationVectorType DeformationVectorType;
+ typedef typename ParentType::MetricPixelType MetricPixelType;
+ typedef typename ParentType::GradientPixelType GradientPixelType;
+ typedef typename ParentType::RealType RealType;
+ typedef TOutputImage OutputImageType;
+ typedef typename OutputImageType::InternalPixelType OutputComponentType;
+
+ /** Standard class typedefs. */
+ typedef MultiImageNCCPrecomputeFilter Self;
+ typedef itk::ImageToImageFilter<InputImageType, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Typedef to describe the output image region type. */
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+
+ typedef typename Superclass::DataObjectIdentifierType DataObjectIdentifierType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageNCCPrecomputeFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension );
+
+ /** Set the parent class */
+ void SetParent(ParentType *parent)
+ {
+ m_Parent = parent;
+ }
+
+ /** Get the number of components in the output */
+ int GetNumberOfOutputComponents();
+
+ /** Get the number of components in the output */
+ int GetNumberOfFixedOnlyOutputComponents();
+
+ /**
+ * Set whether the filter is being run for the first time. When run for the first
+ * time, fixed components and products will be stored in the output. Otherwise,
+ * these are skipped over in memory
+ */
+ itkSetMacro(FlagGenerateFixedComponents, bool)
+
+
+protected:
+ MultiImageNCCPrecomputeFilter();
+ ~MultiImageNCCPrecomputeFilter() {}
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ virtual void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+ /** Set up the output information */
+ virtual void GenerateOutputInformation();
+
+ /** Override input checks to allow fixed and moving to be in different space */
+ virtual void VerifyInputInformation() {}
+
+private:
+ MultiImageNCCPrecomputeFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ ParentType *m_Parent;
+
+ bool m_FlagGenerateFixedComponents;
+};
+
+
+
+
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiComponentNCCImageMetric.txx"
+#endif
+
+
+#endif // MULTICOMPONENTNCCIMAGEMETRIC_H
diff --git a/Submodules/greedy/src/MultiComponentNCCImageMetric.txx b/Submodules/greedy/src/MultiComponentNCCImageMetric.txx
new file mode 100644
index 0000000..f07ec9f
--- /dev/null
+++ b/Submodules/greedy/src/MultiComponentNCCImageMetric.txx
@@ -0,0 +1,641 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiComponentNCCImageMetric_txx
+#define __MultiComponentNCCImageMetric_txx
+
+#include "MultiComponentNCCImageMetric.h"
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include "itkImageFileWriter.h"
+
+
+
+/* ==========================================================================
+ *
+ * PRECOMPUTE filter implementation
+ *
+ * ========================================================================== */
+
+template <class TMetricTraits, class TOutputImage>
+MultiImageNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::MultiImageNCCPrecomputeFilter()
+{
+ m_Parent = NULL;
+ m_FlagGenerateFixedComponents = true;
+}
+
+/**
+ * Generate output information, which will be different from the default
+ */
+template <class TMetricTraits, class TOutputImage>
+void
+MultiImageNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::GenerateOutputInformation()
+{
+ // Call the parent method to set up all the outputs
+ Superclass::GenerateOutputInformation();
+
+ // Set the number of components in the primary output
+ int ncomp = this->GetNumberOfOutputComponents();
+ this->GetOutput()->SetNumberOfComponentsPerPixel(ncomp);
+}
+
+template <class TMetricTraits, class TOutputImage>
+int
+MultiImageNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::GetNumberOfOutputComponents()
+{
+ // This is complex! The number of components depends on what we are computing.
+ int nc = m_Parent->GetFixedImage()->GetNumberOfComponentsPerPixel();
+
+ // If there are no gradients computed, we just need 5 output pixels per input comp.
+ if(!m_Parent->GetComputeGradient())
+ return 1 + nc * 5;
+
+ // If we are not computing affine transform, then the gradient requires 3 comps per dimension
+ if(!m_Parent->GetComputeAffine())
+ return 1 + nc * (5 + 3 * ImageDimension);
+
+ // Otherwise we need a ton of components, because for each gradient component we need it also
+ // scaled by x, by y and by z.
+ return 1 + nc * (5 + 3 * ImageDimension * (1 + ImageDimension));
+}
+
+template <class TMetricTraits, class TOutputImage>
+int
+MultiImageNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::GetNumberOfFixedOnlyOutputComponents()
+{
+ // This is complex! The number of components depends on what we are computing.
+ int nc = m_Parent->GetFixedImage()->GetNumberOfComponentsPerPixel();
+ return 1 + nc * 2;
+}
+
+/**
+ * Compute the output for the region specified by outputRegionForThread.
+ */
+template <class TMetricTraits, class TOutputImage>
+void
+MultiImageNCCPrecomputeFilter<TMetricTraits,TOutputImage>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId )
+{
+ typedef FastLinearInterpolator<InputImageType, RealType, ImageDimension> FastInterpolator;
+
+ // Get the number of input and output components
+ int ncomp_in = m_Parent->GetFixedImage()->GetNumberOfComponentsPerPixel();
+ int ncomp_out = this->GetNumberOfOutputComponents();
+
+ // Number of invariant components (fixed-only)
+ int ncomp_fixed = ncomp_in * 2 + 1;
+
+ // Get the pointer to the output image
+ OutputImageType *out = this->GetOutput();
+
+ // Create an iterator specialized for going through metrics
+ typedef MultiComponentMetricWorker<TMetricTraits, TOutputImage> InterpType;
+ InterpType iter(m_Parent, this->GetOutput(), outputRegionForThread);
+
+ // Iterate over the lines
+ for(; !iter.IsAtEnd(); iter.NextLine())
+ {
+ // Iterate over the pixels in the line
+ for(; !iter.IsAtEndOfLine(); ++iter)
+ {
+ // The mask is 0.5 for points in range of the user mask, and 1.0 for the user mask
+ // We can safely ignore the points outside of range - they have no impact on the
+ // region that we are going to measure
+ if(iter.CheckFixedMask(0.0))
+ {
+ // Get the output pointer for this voxel
+ OutputComponentType *out;
+
+ // Is this the first time the filter is being run for this output working image. If
+ // so, we store the fixed components for future accumulation.
+ if(this->m_FlagGenerateFixedComponents)
+ {
+ out = iter.GetOutputLine();
+ // Store the components that are invariant of the moving image at the beginning. This
+ // helps avoid having to do repeated accumulation on these components
+ *out++ = 1.0;
+ for(int k = 0; k < ncomp_in; k++)
+ {
+ InputComponentType x_fix = iter.GetFixedLine()[k];
+ *out++ = x_fix;
+ *out++ = x_fix * x_fix;
+ }
+ }
+ else
+ {
+ out = iter.GetOutputLine() + ncomp_fixed;
+ }
+
+ // Interpolate the moving image at the current position. The worker knows
+ // whether to interpolate the gradient or not
+ typename FastInterpolator::InOut status = iter.Interpolate();
+
+ // TODO: debugging border issues: setting mask=0 on the border
+ // TODO: i added this check for affine/non-affine. Seems like there were some problems previously
+ // with the NCC metric at the border in deformable mode, but in affine mode you need the border
+ // values to be included.
+ // Outside interpolations are ignored
+ if((m_Parent->GetComputeAffine() && status == FastInterpolator::OUTSIDE) ||
+ (!m_Parent->GetComputeAffine() && (status == FastInterpolator::OUTSIDE || status == FastInterpolator::BORDER)))
+ {
+ // Iterate over the components
+ for(int k = 0; k < ncomp_in; k++)
+ {
+ InputComponentType x_fix = iter.GetFixedLine()[k];
+ *out++ = 0.0;
+ *out++ = 0.0;
+ *out++ = 0.0;
+
+ int n = m_Parent->GetComputeGradient()
+ ? ( m_Parent->GetComputeAffine()
+ ? 3 * ImageDimension * (1 + ImageDimension)
+ : 3 * ImageDimension)
+ : 0;
+
+ for(int j = 0; j < n; j++)
+ *out++ = 0.0;
+ }
+ }
+ else
+ {
+ // Iterate over the components
+ for(int k = 0; k < ncomp_in; k++)
+ {
+ InputComponentType x_mov = iter.GetMovingSample()[k];
+ InputComponentType x_fix = iter.GetFixedLine()[k];
+
+ // Write the five components
+ *out++ = x_mov;
+ *out++ = x_mov * x_mov;
+ *out++ = x_fix * x_mov;
+
+ // If gradient, do more
+ if(m_Parent->GetComputeGradient())
+ {
+ const InputComponentType *mov_grad = iter.GetMovingSampleGradient(k);
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ InputComponentType mov_grad_i = mov_grad[i];
+ *out++ = mov_grad_i;
+ *out++ = x_fix * mov_grad_i;
+ *out++ = x_mov * mov_grad_i;
+
+ // If affine, tack on additional components
+ if(m_Parent->GetComputeAffine())
+ {
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ double x = iter.GetIndex()[j];
+ *out++ = mov_grad_i * x;
+ *out++ = x_fix * mov_grad_i * x;
+ *out++ = x_mov * mov_grad_i * x;
+ }
+ }
+ }
+
+
+ }
+ }
+ }
+ }
+ else
+ {
+ // TODO: do we need this? Zero out the images
+ OutputComponentType *out = iter.GetOutputLine();
+ for(int q = 0; q < ncomp_out; q++)
+ *out++ = 0;
+ }
+ }
+ }
+}
+
+
+
+
+/* ==========================================================================
+ *
+ * main filter implementation
+ *
+ * ========================================================================== */
+
+
+template <class TPixel, class TWeight, class TMetric, class TGradient>
+TPixel *
+MultiImageNNCPostComputeFunction(
+ TPixel *ptr, TPixel *ptr_end, int n_comp, TWeight *weights, TMetric *ptr_metric, TGradient *ptr_gradient, int ImageDimension)
+{
+ // IMPORTANT: this code uses double precision because single precision float seems
+ // to mess up and lead to unstable computations
+
+ // Get the size of the mean filter kernel
+ double n = *ptr++, one_over_n = 1.0 / n;
+
+ // Get the pointer for the fixed only components, and for the moving components
+ TPixel *ptr_fix = ptr; ptr += n_comp * 2;
+
+ // Loop over components
+ int i_wgt = 0;
+ const double eps = 1e-8;
+
+ // Initialize metric to zero
+ *ptr_metric = 0;
+
+ for(; ptr < ptr_end; ++i_wgt)
+ {
+ double x_fix = *ptr_fix++;
+ double x_mov = *ptr++;
+ double x_fix_sq = *ptr_fix++;
+ double x_mov_sq = *ptr++;
+ double x_fix_mov = *ptr++;
+
+ double x_fix_over_n = x_fix * one_over_n;
+ double x_mov_over_n = x_mov * one_over_n;
+
+ double var_fix = x_fix_sq - x_fix * x_fix_over_n;
+ double var_mov = x_mov_sq - x_mov * x_mov_over_n;
+
+ if(var_fix < eps || var_mov < eps)
+ {
+ if(ptr_gradient)
+ ptr += 3 * ImageDimension;
+ continue;
+ }
+
+ double cov_fix_mov = x_fix_mov - x_fix * x_mov_over_n;
+ double one_over_denom = 1.0 / (var_fix * var_mov);
+ double cov_fix_mov_over_denom = cov_fix_mov * one_over_denom;
+ double ncc_fix_mov = cov_fix_mov * cov_fix_mov_over_denom;
+
+ // Weight - includes scaling of squared covariance by direction
+ TWeight w = (cov_fix_mov < 0) ? -weights[i_wgt] : weights[i_wgt];
+
+ if(ptr_gradient)
+ {
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ double x_grad_mov_i = *ptr++;
+ double x_fix_grad_mov_i = *ptr++;
+ double x_mov_grad_mov_i = *ptr++;
+
+ // Derivative of cov_fix_mov
+ double grad_cov_fix_mov_i = x_fix_grad_mov_i - x_fix_over_n * x_grad_mov_i;
+
+ // One half derivative of var_mov
+ double half_grad_var_mov_i = x_mov_grad_mov_i - x_mov_over_n * x_grad_mov_i;
+
+ double grad_ncc_fix_mov_i =
+ 2 * cov_fix_mov_over_denom * (grad_cov_fix_mov_i - var_fix * half_grad_var_mov_i * cov_fix_mov_over_denom);
+
+ if(fabs(grad_ncc_fix_mov_i) > 1000)
+ {
+ printf("big value: %f\n", grad_ncc_fix_mov_i);
+ }
+
+ (*ptr_gradient)[i] += (TPixel) (w * grad_ncc_fix_mov_i);
+ }
+ }
+
+ // Accumulate the metric
+ *ptr_metric += (TPixel) (w * ncc_fix_mov);
+ }
+
+ return ptr;
+}
+
+
+
+template <class TPixel, class TWeight, class TMetric, class TGradient>
+TPixel *
+MultiImageNNCPostComputeAffineGradientFunction(
+ TPixel *ptr, TPixel *ptr_end, int n_comp, TWeight *weights, TMetric *ptr_metric, TGradient *ptr_affine_gradient, int ImageDimension)
+{
+ // Get the size of the mean filter kernel
+ TPixel n = *ptr++, one_over_n = 1.0 / n;
+
+ // Get the pointer for the fixed only components, and for the moving components
+ TPixel *ptr_fix = ptr; ptr += n_comp * 2;
+
+ // Loop over components
+ int i_wgt = 0;
+ const TPixel eps = 1e-2;
+
+ // Initialize metric to zero
+ *ptr_metric = 0;
+
+ for(; ptr < ptr_end; ++i_wgt)
+ {
+ TPixel x_fix = *ptr_fix++;
+ TPixel x_mov = *ptr++;
+ TPixel x_fix_sq = *ptr_fix++;
+ TPixel x_mov_sq = *ptr++;
+ TPixel x_fix_mov = *ptr++;
+
+ TPixel x_fix_over_n = x_fix * one_over_n;
+ TPixel x_mov_over_n = x_mov * one_over_n;
+
+ TPixel var_fix = x_fix_sq - x_fix * x_fix_over_n + eps;
+ TPixel var_mov = x_mov_sq - x_mov * x_mov_over_n + eps;
+
+ TPixel cov_fix_mov = x_fix_mov - x_fix * x_mov_over_n + eps;
+ TPixel one_over_denom = 1.0 / (var_fix * var_mov);
+ TPixel cov_fix_mov_over_denom = cov_fix_mov * one_over_denom;
+ TPixel ncc_fix_mov = cov_fix_mov * cov_fix_mov_over_denom;
+
+ // Weight - includes scaling of squared covariance by direction
+ TWeight w = (cov_fix_mov < 0) ? -weights[i_wgt] : weights[i_wgt];
+
+ if(ptr_affine_gradient)
+ {
+ // There are 12 components to compute
+ TGradient *paff = ptr_affine_gradient;
+
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ for(int j = 0; j <= ImageDimension; j++)
+ {
+ TPixel x_grad_mov_i = *ptr++;
+ TPixel x_fix_grad_mov_i = *ptr++;
+ TPixel x_mov_grad_mov_i = *ptr++;
+
+ // Derivative of cov_fix_mov
+ TPixel grad_cov_fix_mov_i = x_fix_grad_mov_i - x_fix_over_n * x_grad_mov_i;
+
+ // One half derivative of var_mov
+ TPixel half_grad_var_mov_i = x_mov_grad_mov_i - x_mov_over_n * x_grad_mov_i;
+
+ TPixel grad_ncc_fix_mov_i =
+ 2 * cov_fix_mov_over_denom * (grad_cov_fix_mov_i - var_fix * half_grad_var_mov_i * cov_fix_mov_over_denom);
+
+ *paff++ += w * grad_ncc_fix_mov_i;
+ }
+ }
+ }
+
+ // Accumulate the metric
+ *ptr_metric += w * ncc_fix_mov;
+ }
+
+ return ptr;
+}
+
+
+
+
+template <class TMetricTraits>
+void
+MultiComponentNCCImageMetric<TMetricTraits>
+::BeforeThreadedGenerateData()
+{
+ // Call the parent method
+ Superclass::BeforeThreadedGenerateData();
+
+ // Pre-compute filter
+ typedef MultiImageNCCPrecomputeFilter<TMetricTraits, InputImageType> PreFilterType;
+ typename PreFilterType::Pointer preFilter = PreFilterType::New();
+
+ // Configure the precompute filter
+ preFilter->SetParent(this);
+ preFilter->SetInput(this->GetFixedImage());
+
+ // Number of components in the working image
+ unsigned ncomp = preFilter->GetNumberOfOutputComponents();
+ unsigned ncomp_ignore = 0;
+
+ // If the working image is supplied, make sure that it has sufficient size
+ bool flag_reuse = m_ReuseWorkingImageFixedComponents;
+
+ if(m_WorkingImage)
+ {
+ // Check if the working image needs to be allocated
+ if(m_WorkingImage->GetBufferedRegion() != this->GetFixedImage()->GetBufferedRegion()
+ || m_WorkingImage->GetNumberOfComponentsPerPixel() < ncomp)
+ {
+ // Configure the working image
+ m_WorkingImage->CopyInformation(this->GetFixedImage());
+ m_WorkingImage->SetNumberOfComponentsPerPixel(ncomp);
+ m_WorkingImage->SetRegions(this->GetFixedImage()->GetBufferedRegion());
+ m_WorkingImage->Allocate();
+
+ // Can't reuse the fixed components
+ flag_reuse = false;
+ }
+
+ // Graft the working image onto the filter's output
+ preFilter->GraftOutput(m_WorkingImage);
+ }
+ else
+ {
+ // No working image, can't reuse anything
+ flag_reuse = false;
+ }
+
+ // Set reuse information
+ if(flag_reuse)
+ {
+ // Reuse the fixed components
+ preFilter->SetFlagGenerateFixedComponents(false);
+ ncomp_ignore = preFilter->GetNumberOfFixedOnlyOutputComponents();
+ }
+ else
+ {
+ // Tell the filter to compute the fixed components
+ preFilter->SetFlagGenerateFixedComponents(true);
+ }
+
+ // Execute the filter
+ preFilter->Update();
+
+ // Get the output image
+ InputImageType *img_pre = preFilter->GetOutput();
+
+#ifdef DUMP_NCC
+ typename itk::ImageFileWriter<InputImageType>::Pointer pwriter = itk::ImageFileWriter<InputImageType>::New();
+ pwriter->SetInput(img_pre);
+ pwriter->SetFileName("nccpre.nii.gz");
+ pwriter->Update();
+#endif
+
+ // It is feasible that the working image has been allocated for more components that
+ // are currently used. In this case, we skip those components at the end
+ int n_overalloc_comp = m_WorkingImage->GetNumberOfComponentsPerPixel() - ncomp;
+
+ // Currently, we have all the stuff we need to compute the metric in the working
+ // image. Next, we run the fast sum computation to give us the local average of
+ // intensities, products, gradients in the working image
+ typename InputImageType::Pointer img_accum =
+ AccumulateNeighborhoodSumsInPlace(img_pre, m_Radius, ncomp_ignore, n_overalloc_comp);
+
+#ifdef DUMP_NCC
+ typename itk::ImageFileWriter<InputImageType>::Pointer pwriter = itk::ImageFileWriter<InputImageType>::New();
+ pwriter->SetInput(img_accum);
+ pwriter->SetFileName("nccaccum.nii.gz");
+ pwriter->Update();
+#endif
+
+ // At this point, the working image will hold the proper neighborhood sums (I, J, I^2, J^2, IJ, etc).
+ // The last step is to use this information to compute the gradients. This is done by the threaded
+ // portion of the filter.
+}
+
+
+template <class TMetricTraits>
+void
+MultiComponentNCCImageMetric<TMetricTraits>
+::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId)
+{
+ int nc_img = this->GetFixedImage()->GetNumberOfComponentsPerPixel();
+ int nc = m_WorkingImage->GetNumberOfComponentsPerPixel();
+ int line_len = outputRegionForThread.GetSize()[0];
+
+ // Our thread data
+ typename Superclass::ThreadData &td = this->m_ThreadData[threadId];
+
+ // Set up an iterator for the working image
+ typedef itk::ImageLinearConstIteratorWithIndex<InputImageType> InputIteratorTypeBase;
+ typedef IteratorExtender<InputIteratorTypeBase> InputIteratorType;
+ InputIteratorType it(m_WorkingImage, outputRegionForThread);
+
+ // Loop over the lines
+ for (; !it.IsAtEnd(); it.NextLine())
+ {
+ // Get the pointer to the input line
+ long offset_in_pixels = it.GetPosition() - m_WorkingImage->GetBufferPointer();
+
+ // Pointer to the input pixel data for this line
+ const InputComponentType *p_input = m_WorkingImage->GetBufferPointer() + nc * offset_in_pixels;
+
+ // Pointer to the metric data for this line
+ MetricPixelType *p_metric = this->GetMetricOutput()->GetBufferPointer() + offset_in_pixels;
+
+ // The gradient output is optional
+ GradientPixelType *p_grad_metric = (this->m_ComputeGradient && !this->m_ComputeAffine)
+ ? this->GetDeformationGradientOutput()->GetBufferPointer() + offset_in_pixels
+ : NULL;
+
+ // Get the fixed mask like
+ typename MaskImageType::PixelType *fixed_mask_line =
+ this->GetFixedMaskImage()
+ ? this->GetFixedMaskImage()->GetBufferPointer() + offset_in_pixels
+ : NULL;
+
+ // Case 1 - dense gradient field requested
+ if(!this->m_ComputeAffine)
+ {
+ if(this->m_ComputeGradient)
+ {
+ // Loop over the pixels in the line
+ for(int i = 0; i < line_len; ++i)
+ {
+ // Clear the metric and the gradient
+ *p_metric = itk::NumericTraits<MetricPixelType>::Zero;
+ *p_grad_metric = itk::NumericTraits<GradientPixelType>::Zero;
+
+ if(!fixed_mask_line || fixed_mask_line[i] > 0.5)
+ {
+ p_input = MultiImageNNCPostComputeFunction(p_input, p_input + nc, nc_img, this->m_Weights.data_block(),
+ p_metric, p_grad_metric++, ImageDimension);
+ }
+ else
+ {
+ p_grad_metric++;
+ p_input+=nc;
+ }
+
+ // Accumulate the total metric
+ td.metric += *p_metric;
+ td.mask += 1.0;
+ }
+ }
+ else
+ {
+ // Loop over the pixels in the line
+ for(int i = 0; i < line_len; ++i)
+ {
+ // Clear the metric and the gradient
+ *p_metric = itk::NumericTraits<MetricPixelType>::Zero;
+
+ // Apply the post computation
+ if(!fixed_mask_line || fixed_mask_line[i] > 0.5)
+ {
+ p_input = MultiImageNNCPostComputeFunction(p_input, p_input + nc, nc_img, this->m_Weights.data_block(),
+ p_metric, (GradientPixelType *)(NULL), ImageDimension);
+ }
+ else
+ {
+ p_input+=nc;
+ }
+
+ // Accumulate the total metric
+ td.metric += *p_metric;
+ td.mask += 1.0;
+ }
+ }
+ }
+ // Computing affine
+ else
+ {
+ double *p_grad = this->m_ComputeGradient ? td.gradient.data_block() : NULL;
+
+ // Is there a mask?
+ typename MaskImageType::PixelType *mask_line = NULL;
+ if(this->GetFixedMaskImage())
+ mask_line = this->GetFixedMaskImage()->GetBufferPointer() + offset_in_pixels;
+
+ // Loop over the pixels in the line
+ for(int i = 0; i < line_len; ++i)
+ {
+ // Use mask
+ if(!fixed_mask_line || fixed_mask_line[i] > 0.5)
+ {
+ // Clear the metric and the gradient
+ *p_metric = itk::NumericTraits<MetricPixelType>::Zero;
+
+ // Apply the post computation
+ p_input = MultiImageNNCPostComputeAffineGradientFunction(
+ p_input, p_input + nc, nc_img, this->m_Weights.data_block(),
+ p_metric, p_grad, ImageDimension);
+
+ // Accumulate the total metric
+ td.metric += *p_metric;
+ td.mask += 1.0;
+ }
+ else
+ {
+ p_input+=nc;
+ }
+ }
+ }
+ }
+}
+
+
+#endif // __MultiComponentNCCImageMetric_txx
+
diff --git a/Submodules/greedy/src/MultiImageAffineMSDMetricFilter.h b/Submodules/greedy/src/MultiImageAffineMSDMetricFilter.h
new file mode 100644
index 0000000..6eb29e1
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageAffineMSDMetricFilter.h
@@ -0,0 +1,220 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiImageAffineMSDMetricFilter_h
+#define __MultiImageAffineMSDMetricFilter_h
+
+#include "itkImageBase.h"
+#include "itkImageToImageFilter.h"
+#include "itkPoint.h"
+#include "itkVectorImage.h"
+#include "itkMatrixOffsetTransformBase.h"
+
+
+
+
+/**
+ * This filter computes the gradient of the affine transform. The inputs to this filter
+ * should come from a metric
+ */
+template <class TMetricTraits>
+class ITK_EXPORT MultiImageAffineMetricFilter :
+ public itk::ImageToImageFilter<typename TMetricTraits::MetricImageType,
+ typename TMetricTraits::MetricImageType>
+{
+public:
+
+ /** Type definitions from the traits class */
+ typedef typename TMetricTraits::MetricImageType InputImageType;
+ typedef typename TMetricTraits::GradientImageType GradientImageType;
+
+ /** Standard class typedefs. */
+ typedef MultiImageAffineMetricFilter Self;
+ typedef itk::ImageToImageFilter<InputImageType,InputImageType> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageAffineMetricFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension );
+
+ /** Typedef to describe the output image region type. */
+
+ /** Inherit some types from the superclass. */
+ typedef itk::ImageBase<ImageDimension> ImageBaseType;
+ typedef typename InputImageType::PixelType InputPixelType;
+ typedef typename GradientImageType::PixelType GradientPixelType;
+ typedef typename InputImageType::IndexType IndexType;
+ typedef typename InputImageType::IndexValueType IndexValueType;
+ typedef typename InputImageType::SizeType SizeType;
+ typedef typename InputImageType::SpacingType SpacingType;
+ typedef typename InputImageType::DirectionType DirectionType;
+ typedef typename InputImageType::RegionType OutputImageRegionType;
+
+ /** Information from the parent class */
+ typedef itk::MatrixOffsetTransformBase<double, ImageDimension, ImageDimension> TransformType;
+ typedef typename TransformType::Pointer TransformPointer;
+
+ /** Set the metric image, which is passed through as the output */
+ void SetMetricImage(InputImageType *metric)
+ { this->itk::ProcessObject::SetInput("Primary", metric); }
+
+ InputImageType *GetMetricImage()
+ { return dynamic_cast<InputImageType *>(this->itk::ProcessObject::GetInput("Primary")); }
+
+ /** Set the metric image, which is passed through as the output */
+ void SetGradientImage(GradientImageType *metric)
+ { this->itk::ProcessObject::SetInput("gradient", metric); }
+
+ GradientImageType *GetGradientImage()
+ { return dynamic_cast<GradientImageType *>(this->itk::ProcessObject::GetInput("gradient")); }
+
+ /** Set the metric image, which is passed through as the output */
+ void SetMovingDomainMaskImage(InputImageType *metric)
+ { this->itk::ProcessObject::SetInput("moving_mask", metric); }
+
+ InputImageType *GetMovingDomainMaskImage()
+ { return dynamic_cast<InputImageType *>(this->itk::ProcessObject::GetInput("moving_mask")); }
+
+ /** Set the metric image, which is passed through as the output */
+ void SetMovingDomainMaskGradientImage(GradientImageType *metric)
+ { this->itk::ProcessObject::SetInput("moving_mask_gradient", metric); }
+
+ GradientImageType *GetMovingDomainMaskGradientImage()
+ { return dynamic_cast<GradientImageType *>(this->itk::ProcessObject::GetInput("moving_mask_gradient")); }
+
+ /** Whether the gradient is computed */
+ itkSetMacro(ComputeGradient, bool)
+ itkGetMacro(ComputeGradient, bool)
+
+ /** Set the transform field. */
+ void SetTransform(TransformType *transform)
+ { m_Transform = transform; }
+
+ itkGetConstMacro(Transform, TransformType *)
+
+ /**
+ * Set the scaling factor for the metric gradient input. For efficiency reasons, metric filters
+ * may produce gradient outputs that are arbitrarily scaled (e.g., by -0.5). This value is used
+ * to scale the gradient so it is the actual gradient of the sum of all voxels in the metric
+ * input
+ */
+ itkSetMacro(GradientScalingFactor, double)
+ itkGetMacro(GradientScalingFactor, double)
+
+ /** The gradient (in the form of a transform) after running the filter */
+ itkGetConstMacro(MetricGradient, TransformType *)
+
+ /** Get the computed metric value */
+ itkGetMacro(MetricValue, double)
+
+protected:
+ MultiImageAffineMetricFilter() : m_ComputeGradient(false) {}
+ ~MultiImageAffineMetricFilter() {}
+
+ void PrintSelf(std::ostream& os, itk::Indent indent) const
+ { this->PrintSelf(os, indent); }
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+ /** It is difficult to compute in advance the input image region
+ * required to compute the requested output region. Thus the safest
+ * thing to do is to request for the whole input image.
+ *
+ * For the deformation field, the input requested region
+ * set to be the same as that of the output requested region. */
+ virtual void GenerateInputRequestedRegion();
+
+ /** Override since input passed to output */
+ virtual void EnlargeOutputRequestedRegion(itk::DataObject *data);
+
+ /** This method is used to set the state of the filter before
+ * multi-threading. */
+ virtual void BeforeThreadedGenerateData();
+
+ /** This method is used to set the state of the filter after
+ * multi-threading. */
+ virtual void AfterThreadedGenerateData();
+
+ /** Allocate outputs - just pass through the input */
+ virtual void AllocateOutputs();
+
+ void VerifyInputInformation() {}
+
+ // Object to assist specializaiton
+ struct DispatchBase {};
+ template <unsigned int VDim> struct Dispatch : public DispatchBase {};
+
+private:
+ MultiImageAffineMetricFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // Transform pointer
+ TransformPointer m_Transform;
+
+ // Data accumulated for each thread
+ struct ThreadData {
+ double metric, mask;
+ vnl_vector<double> gradient, grad_mask;
+ ThreadData() : metric(0.0), mask(0.0),
+ gradient(ImageDimension * (ImageDimension+1), 0.0),
+ grad_mask(ImageDimension * (ImageDimension+1), 0.0) {}
+ };
+
+ std::vector<ThreadData> m_ThreadData;
+
+ // Gradient
+ TransformPointer m_MetricGradient;
+
+ // Metric scaled by the mask
+ double m_MetricValue;
+
+ // Whether the gradient is computed
+ bool m_ComputeGradient;
+
+ // Gradient scaling factor
+ double m_GradientScalingFactor;
+};
+
+
+
+
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiImageAffineMSDMetricFilter.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/MultiImageAffineMSDMetricFilter.txx b/Submodules/greedy/src/MultiImageAffineMSDMetricFilter.txx
new file mode 100644
index 0000000..06a8773
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageAffineMSDMetricFilter.txx
@@ -0,0 +1,258 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiImageAffineMSDMetricFilter_txx
+#define __MultiImageAffineMSDMetricFilter_txx
+#include "MultiImageAffineMSDMetricFilter.h"
+
+#define _USING_MASK_ 1
+
+
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkNumericTraits.h"
+#include "itkContinuousIndex.h"
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+#include "vnl/vnl_math.h"
+#include "FastLinearInterpolator.h"
+
+/**
+ * Setup state of filter before multi-threading.
+ * InterpolatorType::SetInputImage is not thread-safe and hence
+ * has to be setup before ThreadedGenerateData
+ */
+template <class TMetricTraits>
+void
+MultiImageAffineMetricFilter<TMetricTraits>
+::BeforeThreadedGenerateData()
+{
+ // Initialize the per thread data
+ m_ThreadData.resize(this->GetNumberOfThreads(), ThreadData());
+}
+
+/**
+ * Setup state of filter after multi-threading.
+ */
+template <class TMetricTraits>
+void
+MultiImageAffineMetricFilter<TMetricTraits>
+::AfterThreadedGenerateData()
+{
+ // Add up all the thread data
+ ThreadData summary;
+ for(int i = 0; i < m_ThreadData.size(); i++)
+ {
+ summary.metric += m_ThreadData[i].metric;
+ summary.mask += m_ThreadData[i].mask;
+ summary.gradient += m_ThreadData[i].gradient;
+ summary.grad_mask += m_ThreadData[i].grad_mask;
+ }
+
+ // Initialize the output metric gradient
+ m_MetricGradient = TransformType::New();
+
+ // Compute the objective value
+#ifdef _USING_MASK_
+ m_MetricValue = summary.metric / summary.mask;
+#else
+ m_MetricValue = summary.metric;
+#endif
+
+ // Compute the gradient
+ vnl_vector<double> grad_metric(summary.gradient.size());
+ for(int j = 0; j < summary.gradient.size(); j++)
+ {
+#ifdef _USING_MASK_
+ grad_metric[j] =
+ (m_GradientScalingFactor * summary.gradient[j] - m_MetricValue * summary.grad_mask[j]) / summary.mask;
+#else
+ grad_metric[j] = m_GradientScalingFactor * summary.gradient[j];
+#endif
+ }
+
+ // Pack into the output
+ unflatten_affine_transform(grad_metric.data_block(), m_MetricGradient.GetPointer());
+}
+
+template <class TMetricTraits>
+void
+MultiImageAffineMetricFilter<TMetricTraits>
+::GenerateInputRequestedRegion()
+{
+ // Call the superclass's implementation
+ Superclass::GenerateInputRequestedRegion();
+
+ // Set all regions to max
+ this->GetMetricImage()->SetRequestedRegionToLargestPossibleRegion();
+ this->GetMovingDomainMaskImage()->SetRequestedRegionToLargestPossibleRegion();
+
+ if(m_ComputeGradient)
+ {
+ this->GetGradientImage()->SetRequestedRegionToLargestPossibleRegion();
+ this->GetMovingDomainMaskGradientImage()->SetRequestedRegionToLargestPossibleRegion();
+ }
+}
+
+template <class TMetricTraits>
+void
+MultiImageAffineMetricFilter<TMetricTraits>
+::EnlargeOutputRequestedRegion(itk::DataObject *data)
+{
+ Superclass::EnlargeOutputRequestedRegion(data);
+ data->SetRequestedRegionToLargestPossibleRegion();
+}
+
+
+template <class TMetricTraits>
+void
+MultiImageAffineMetricFilter<TMetricTraits>
+::AllocateOutputs()
+{
+ // Propagate input
+ this->GraftOutput(this->GetMetricImage());
+}
+
+
+template <class TMetricTraits>
+void
+MultiImageAffineMetricFilter<TMetricTraits>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId )
+{
+ // Get the pointers to the input and output images
+ InputImageType *metric = this->GetMetricImage();
+ InputImageType *mask = this->GetMovingDomainMaskImage();
+
+ // Get the pointers to the buffers of these four images
+ const InputPixelType *b_metric = metric->GetBufferPointer();
+ const InputPixelType *b_mask = mask->GetBufferPointer();
+
+ const GradientPixelType *b_gradient =
+ m_ComputeGradient ? this->GetGradientImage()->GetBufferPointer() : NULL;
+
+ const GradientPixelType *b_mask_gradient =
+ m_ComputeGradient ? this->GetMovingDomainMaskGradientImage()->GetBufferPointer() : NULL;
+
+ // Iterate over the deformation field and the output image. In reality, we don't
+ // need to waste so much time on iteration, so we use a specialized iterator here
+ typedef itk::ImageLinearConstIteratorWithIndex<InputImageType> IterBase;
+ typedef IteratorExtender<IterBase> Iter;
+
+ // The thread data to accumulate
+ ThreadData &td = m_ThreadData[threadId];
+
+ // Gradient accumulator
+ vnl_vector_fixed<double, ImageDimension> grad, gradM;
+
+ itk::ImageRegion<ImageDimension> workRegion = outputRegionForThread;
+ // workRegion.ShrinkByRadius(3);
+
+ // Iterate over the fixed space region
+ for(Iter it(metric, workRegion); !it.IsAtEnd(); it.NextLine())
+ {
+ // Process the whole line using pointer arithmetic. We have to deal with messy behavior
+ // of iterators on vector images. Trying to avoid using accessors and Set/Get
+ long offset_in_pixels = it.GetPosition() - b_metric;
+
+ // Get pointers to the start of the line
+ const InputPixelType *p_metric = b_metric + offset_in_pixels;
+ const InputPixelType *p_mask = b_mask + offset_in_pixels;
+
+ // Loop over the line
+ const InputPixelType *p_metric_end = p_metric + outputRegionForThread.GetSize(0);
+
+ // Loop if we are computing gradient
+ if(m_ComputeGradient)
+ {
+ const GradientPixelType *p_gradient = b_gradient + offset_in_pixels;
+ const GradientPixelType *p_mask_gradient = b_mask_gradient + offset_in_pixels;
+
+ // Get the index at the current location
+ IndexType idx = it.GetIndex();
+
+ // Do we need the gradient?
+ for(; p_metric < p_metric_end; ++p_metric, ++p_mask, ++p_gradient, ++p_mask_gradient, ++idx[0])
+ {
+ // Accumulators for the gradients
+ double *out_grad = td.gradient.data_block();
+ double *out_grad_mask = td.grad_mask.data_block();
+
+ // For border regions, we need to explicitly deal with the mask
+ const InputPixelType &metric = *p_metric;
+ const InputPixelType &mask = *p_mask;
+ const GradientPixelType &grad = *p_gradient;
+ const GradientPixelType &gradM = *p_mask_gradient;
+
+ // Compute the mask and metric gradient contributions
+#ifdef _USING_MASK_
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ double v = grad[i] * mask - 0.5 * gradM[i] * metric;
+ *(out_grad++) += v;
+ *(out_grad_mask++) += gradM[i];
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ *(out_grad++) += v * idx[j];
+ *(out_grad_mask++) += gradM[i] * idx[j];
+ }
+ }
+
+ td.metric += metric * mask;
+ td.mask += mask;
+#else
+ // NOT USING MASK
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ double v = grad[i];
+ *(out_grad++) += v;
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ *(out_grad++) += v * idx[j];
+ }
+ }
+
+ td.metric += metric;
+#endif
+ }
+ }
+ else
+ {
+ // Do we need the gradient?
+ for(; p_metric < p_metric_end; ++p_metric, ++p_mask)
+ {
+ // For border regions, we need to explicitly deal with the mask
+ const InputPixelType &metric = *p_metric;
+ const InputPixelType &mask = *p_mask;
+ td.metric += metric * mask;
+ td.mask += mask;
+ }
+ }
+ }
+}
+
+
+#endif
diff --git a/Submodules/greedy/src/MultiImageOpticalFlowImageFilter.h b/Submodules/greedy/src/MultiImageOpticalFlowImageFilter.h
new file mode 100644
index 0000000..8feeb23
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageOpticalFlowImageFilter.h
@@ -0,0 +1,142 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiImageOpticalFlowImageFilter_h
+#define __MultiImageOpticalFlowImageFilter_h
+
+#include "MultiComponentImageMetricBase.h"
+
+
+/**
+ * \class MultiImageOpticalFlowImageFilter
+ * \brief Warps an image using an input deformation field (for LDDMM)
+ *
+ * This filter efficiently computes the optical flow field between a
+ * set of image pairs, given a transformation phi. This filter is the
+ * workhorse of deformable and affine rigid registration algorithms that
+ * use the mean squared difference metric. Given a set of fixed images F_i
+ * and moving images M_i, it computes
+ *
+ * v(x) = Sum_i w_i \[ F_i(x) - M_i(Phi(x)) ] \Grad M_i (Phi(x))
+ *
+ * The efficiency of this filter comes from combining the interpolation of
+ * all the M and GradM terms in one loop, so that all possible computations
+ * are reused
+ *
+ * The fixed and moving images must be passed in to the filter in the form
+ * of VectorImages of size K and (VDim+K), respectively - i.e., the moving
+ * images and their gradients are packed together.
+ *
+ * The output should be an image of CovariantVector type
+ *
+ * \warning This filter assumes that the input type, output type
+ * and deformation field type all have the same number of dimensions.
+ *
+ */
+template <class TMetricTraits>
+class ITK_EXPORT MultiImageOpticalFlowImageFilter :
+ public MultiComponentImageMetricBase<TMetricTraits>
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiImageOpticalFlowImageFilter<TMetricTraits> Self;
+ typedef MultiComponentImageMetricBase<TMetricTraits> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageOpticalFlowImageFilter, MultiComponentImageMetricBase )
+
+ /** Typedef to describe the output image region type. */
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::InputPixelType InputPixelType;
+ typedef typename Superclass::InputComponentType InputComponentType;
+ typedef typename Superclass::MetricImageType MetricImageType;
+ typedef typename Superclass::GradientImageType GradientImageType;
+ typedef typename Superclass::MetricPixelType MetricPixelType;
+ typedef typename Superclass::GradientPixelType GradientPixelType;
+ typedef typename Superclass::RealType RealType;
+
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+ typedef typename Superclass::SizeType SizeType;
+ typedef typename Superclass::SpacingType SpacingType;
+ typedef typename Superclass::DirectionType DirectionType;
+ typedef typename Superclass::ImageBaseType ImageBaseType;
+
+ /** Information from the deformation field class */
+ typedef typename Superclass::DeformationFieldType DeformationFieldType;
+ typedef typename Superclass::DeformationFieldPointer DeformationFieldPointer;
+ typedef typename Superclass::DeformationVectorType DeformationVectorType;
+ typedef typename Superclass::TransformType TransformType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension );
+
+ /**
+ * Get the gradient scaling factor. To get the actual gradient of the metric, multiply the
+ * gradient output of this filter by the scaling factor. Explanation: for efficiency, the
+ * metrics return an arbitrarily scaled vector, such that adding the gradient to the
+ * deformation field would INCREASE SIMILARITY. For metrics that are meant to be minimized,
+ * this is the opposite of the gradient direction. For metrics that are meant to be maximized,
+ * it is the gradient direction.
+ */
+ virtual double GetGradientScalingFactor() const { return -2.0; }
+
+protected:
+ MultiImageOpticalFlowImageFilter() {}
+ ~MultiImageOpticalFlowImageFilter() {}
+
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId );
+
+private:
+ MultiImageOpticalFlowImageFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+};
+
+
+
+
+
+
+
+
+
+
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiImageOpticalFlowImageFilter.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/MultiImageOpticalFlowImageFilter.txx b/Submodules/greedy/src/MultiImageOpticalFlowImageFilter.txx
new file mode 100644
index 0000000..2144def
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageOpticalFlowImageFilter.txx
@@ -0,0 +1,174 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiImageOpticalFlowImageFilter_txx
+#define __MultiImageOpticalFlowImageFilter_txx
+
+#include "MultiImageOpticalFlowImageFilter.h"
+
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkNumericTraits.h"
+#include "itkContinuousIndex.h"
+#include "vnl/vnl_math.h"
+#include "FastLinearInterpolator.h"
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+
+
+
+/**
+ * Compute the output for the region specified by outputRegionForThread.
+ */
+template <class TMetricTraits>
+void
+MultiImageOpticalFlowImageFilter<TMetricTraits>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId )
+{
+ // Get the number of components
+ int ncomp = this->GetFixedImage()->GetNumberOfComponentsPerPixel();
+
+ // Create an iterator specialized for going through metrics
+ typedef MultiComponentMetricWorker<TMetricTraits, MetricImageType> InterpType;
+ InterpType iter(this, this->GetMetricOutput(), outputRegionForThread);
+
+ // Get the per-component metric array
+ typename Superclass::ThreadData &td = this->m_ThreadData[threadId];
+
+ // Iterate over the lines
+ for(; !iter.IsAtEnd(); iter.NextLine())
+ {
+ // If we are computing deforamble gradient, get a pointer to the gradient line
+ GradientPixelType *grad_line =
+ (this->m_ComputeGradient && !this->m_ComputeAffine)
+ ? this->GetDeformationGradientOutput()->GetBufferPointer() + iter.GetOffsetInPixels()
+ : NULL;
+
+ // Temporary gradient pixel
+ GradientPixelType grad_metric;
+
+ // Iterate over the pixels in the line
+ for(; !iter.IsAtEndOfLine(); ++iter)
+ {
+ // Metric value at this pixel
+ double metric = 0.0;
+
+ // Gradient vector at this pixel.
+ if(this->m_ComputeGradient)
+ grad_metric.Fill(0.0);
+
+ // Check the fixed mask at this location
+ if(iter.CheckFixedMask())
+ {
+ // Interpolate the moving image at the current position. The worker knows
+ // whether to interpolate the gradient or not
+ typedef FastLinearInterpolator<InputImageType, RealType, ImageDimension> FastInterpolator;
+ typename FastInterpolator::InOut status = iter.Interpolate();
+
+ // Outside interpolations are ignored
+ if(status != FastInterpolator::OUTSIDE)
+ {
+ // Iterate over the components
+ for(int k = 0; k < ncomp; k++)
+ {
+ // Compute the weighted squared difference
+ double del = iter.GetFixedLine()[k] - iter.GetMovingSample()[k];
+ double delw = this->m_Weights[k] * del;
+ double del2w = delw * del;
+
+ // Add the value to the metric
+ metric += del2w;
+
+ // Add the value to each component
+ td.comp_metric[k] += del2w;
+
+ // This is currently computing negative half of the gradient
+ if(this->m_ComputeGradient)
+ {
+ const RealType *grad_mov_k = iter.GetMovingSampleGradient(k);
+ for(int i = 0; i < ImageDimension; i++)
+ grad_metric[i] += delw * grad_mov_k[i];
+ }
+ }
+
+ // If on the border, apply the mask
+ if(status == FastInterpolator::BORDER && this->m_ComputeMovingDomainMask)
+ {
+ double mask = iter.GetMask();
+
+ // Update the metric and the gradient vector to reflect multiplication with mask
+ for(int i = 0; i < ImageDimension; i++)
+ grad_metric[i] = grad_metric[i] * mask - 0.5 * iter.GetMaskGradient()[i] * metric;
+ metric = metric * mask;
+
+ td.mask += mask;
+ }
+ else
+ {
+ td.mask += 1.0;
+ }
+
+ // Accumulate the metric
+ td.metric += metric;
+
+ // Do the gradient computation
+ if(this->m_ComputeGradient)
+ {
+ // If affine, use the gradient to accumulte the affine partial derivs
+ if(this->m_ComputeAffine)
+ {
+ for(int i = 0, q = 0; i < ImageDimension; i++)
+ {
+ td.gradient[q++] += grad_metric[i];
+ for(int j = 0; j < ImageDimension; j++)
+ td.gradient[q++] += grad_metric[i] * iter.GetIndex()[j];
+ }
+
+ if(status == FastInterpolator::BORDER)
+ {
+ for(int i = 0, q = 0; i < ImageDimension; i++)
+ {
+ td.grad_mask[q++] += iter.GetMaskGradient()[i];
+ for(int j = 0; j < ImageDimension; j++)
+ td.grad_mask[q++] += iter.GetMaskGradient()[i] * iter.GetIndex()[j];
+ }
+ }
+ }
+ }
+ } // not outside
+ } // check fixed mask
+
+ // Last thing - update the output voxels
+ *iter.GetOutputLine() = metric;
+ if(grad_line)
+ grad_line[iter.GetLinePos()] = grad_metric;
+ }
+ }
+}
+
+
+#endif
diff --git a/Submodules/greedy/src/MultiImageRegistrationHelper.h b/Submodules/greedy/src/MultiImageRegistrationHelper.h
new file mode 100644
index 0000000..e95a1c5
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageRegistrationHelper.h
@@ -0,0 +1,238 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiImageRegistrationHelper_h
+#define __MultiImageRegistrationHelper_h
+
+#include "itkImageBase.h"
+#include "itkVectorImage.h"
+#include "itkMatrixOffsetTransformBase.h"
+
+
+
+/**
+ * This class is used to perform mean square intensity difference type
+ * registration with multiple images. The filter is designed for speed
+ * of interpolation.
+ */
+template <class TFloat, unsigned int VDim>
+class MultiImageOpticalFlowHelper
+{
+public:
+
+ typedef itk::VectorImage<TFloat, VDim> MultiComponentImageType;
+ typedef itk::Image<TFloat, VDim> FloatImageType;
+ typedef itk::CovariantVector<TFloat, VDim> VectorType;
+ typedef itk::Image<VectorType, VDim> VectorImageType;
+ typedef itk::ImageBase<VDim> ImageBaseType;
+
+ typedef typename MultiComponentImageType::Pointer MultiComponentImagePointer;
+ typedef typename FloatImageType::Pointer FloatImagePointer;
+ typedef typename VectorImageType::Pointer VectorImagePointer;
+
+ typedef std::vector<int> PyramidFactorsType;
+ typedef itk::Size<VDim> SizeType;
+ typedef itk::CovariantVector<TFloat, VDim> Vec;
+
+ typedef itk::MatrixOffsetTransformBase<TFloat, VDim, VDim> LinearTransformType;
+
+ /** Set default (power of two) pyramid factors */
+ void SetDefaultPyramidFactors(int n_levels);
+
+ /** Set the pyramid factors - for multi-resolution (e.g., 8,4,2) */
+ void SetPyramidFactors(const PyramidFactorsType &factors);
+
+ /** Add a pair of multi-component images to the class - same weight for each component */
+ void AddImagePair(MultiComponentImageType *fixed, MultiComponentImageType *moving, double weight);
+
+ /** Set the gradient image mask */
+ void SetGradientMask(FloatImageType *maskImage) { m_GradientMaskImage = maskImage; }
+
+ /** Set the moving image mask */
+ void SetMovingMask(FloatImageType *maskImage) { m_MovingMaskImage = maskImage; }
+
+ /** Set jitter sigma - for jittering image samples in affine mode */
+ void SetJitterSigma(double sigma);
+
+ /** Compute the composite image - must be run before any sampling is done */
+ void BuildCompositeImages(double noise_sigma_relative = 0.0);
+
+ /**
+ * Apply a dilation to the fixed gradient masks - this is used with the NCC metric. The initial
+ * user-specified mask is transformed into a dilated mask, with values as follows:
+ * 1.0 : voxel is inside the user-specified mask
+ * 0.5 : voxel is within radius of the user-specified mask
+ * 0.0 : voxel is outside of the user-specified mask
+ *
+ * NCC metrics exploit this mask format for faster processing - region where the mask is zero are
+ * excluded from NCC computation and accumulation
+ */
+ void DilateCompositeGradientMasksForNCC(SizeType radius);
+
+ /** Get the reference image for level k */
+ ImageBaseType *GetReferenceSpace(int level);
+
+ /** Get the reference image for level k */
+ ImageBaseType *GetMovingReferenceSpace(int level);
+
+ /** Get the gradient mask at a pyramid level */
+ FloatImageType *GetGradientMask(int level) { return m_GradientMaskComposite[level]; }
+
+ /** Get the moving mask at a pyramid level */
+ FloatImageType *GetMovingMask(int level) { return m_MovingMaskComposite[level]; }
+
+ /** Get the fixed image at a pyramid level */
+ MultiComponentImageType *GetFixedComposite(int level) { return m_FixedComposite[level]; }
+
+ /** Get the moving image at a pyramid level */
+ MultiComponentImageType *GetMovingComposite(int level) { return m_MovingComposite[level]; }
+
+ /** Get the smoothing factor for given level based on parameters */
+ Vec GetSmoothingSigmasInPhysicalUnits(int level, double sigma, bool in_physical_units);
+
+ /** Get the component weights in the composite */
+ const std::vector<double> &GetWeights() const { return m_Weights; }
+
+ /** Perform interpolation - compute [(I - J(Tx)) GradJ(Tx)] */
+ vnl_vector<double> ComputeOpticalFlowField(
+ int level, VectorImageType *def, FloatImageType *out_metric,
+ VectorImageType *out_gradient, double result_scaling = 1.0);
+
+ /** Perform interpolation - compute mutual information metric */
+ vnl_vector<double> ComputeMIFlowField(
+ int level, bool normalized_mutual_information,
+ VectorImageType *def, FloatImageType *out_metric,
+ VectorImageType *out_gradient, double result_scaling = 1.0);
+
+ /** Compute the NCC metric without gradient */
+ double ComputeNCCMetricImage(int level, VectorImageType *def, const SizeType &radius,
+ FloatImageType *out_metric, VectorImageType *out_gradient = NULL,
+ double result_scaling = 1.0);
+
+
+
+ /** Compute affine similarity and gradient */
+ double ComputeAffineMSDMatchAndGradient(int level, LinearTransformType *tran,
+ FloatImageType *wrkMetric,
+ FloatImageType *wrkMask,
+ VectorImageType *wrkGradMetric,
+ VectorImageType *wrkGradMask,
+ VectorImageType *wrkPhi,
+ LinearTransformType *grad = NULL);
+
+
+ double ComputeAffineMIMatchAndGradient(int level, bool normalized_mutual_info,
+ LinearTransformType *tran,
+ FloatImageType *wrkMetric,
+ FloatImageType *wrkMask,
+ VectorImageType *wrkGradMetric,
+ VectorImageType *wrkGradMask,
+ VectorImageType *wrkPhi,
+ LinearTransformType *grad = NULL);
+
+ double ComputeAffineNCCMatchAndGradient(int level, LinearTransformType *tran,
+ const SizeType &radius,
+ FloatImageType *wrkMetric,
+ FloatImageType *wrkMask,
+ VectorImageType *wrkGradMetric,
+ VectorImageType *wrkGradMask,
+ VectorImageType *wrkPhi,
+ LinearTransformType *grad = NULL);
+
+ static void AffineToField(LinearTransformType *tran, VectorImageType *def);
+
+ /** Convert a warp to physical space */
+ static void VoxelWarpToPhysicalWarp(VectorImageType *warp, ImageBaseType *moving_space, VectorImageType *result);
+ static void PhysicalWarpToVoxelWarp(VectorImageType *warp, ImageBaseType *moving_space, VectorImageType *result);
+
+ /*
+ * Write a warp to a file. The warp must be in voxel space, not physical space
+ * this is the static version of this method
+ */
+ static void WriteCompressedWarpInPhysicalSpace(
+ VectorImageType *warp, ImageBaseType *moving_ref_space, const char *filename, double precision);
+
+ /** Write a warp to a file. The warp must be in voxel space, not physical space */
+ void WriteCompressedWarpInPhysicalSpace(int level, VectorImageType *warp, const char *filename, double precision);
+
+ /**
+ * Invert a deformation field by first dividing it into small transformations using the
+ * square root command, and then inverting the small transformations
+ */
+ static void ComputeDeformationFieldInverse(VectorImageType *warp, VectorImageType *result, int n_sqrt, bool verbose = false);
+
+ MultiImageOpticalFlowHelper() : m_JitterSigma(0.0) {}
+
+protected:
+
+ // Pyramid factors
+ PyramidFactorsType m_PyramidFactors;
+
+ // Weights
+ std::vector<double> m_Weights;
+
+ // Vector of images
+ typedef std::vector<typename MultiComponentImageType::Pointer> MultiCompImageSet;
+ typedef std::vector<typename FloatImageType::Pointer> FloatImageSet;
+ typedef std::vector<typename VectorImageType::Pointer> VectorImageSet;
+
+ // Fixed and moving images
+ MultiCompImageSet m_Fixed, m_Moving;
+
+ // Composite image at each resolution level
+ MultiCompImageSet m_FixedComposite, m_MovingComposite;
+
+ // Working memory image for NCC computation
+ typename MultiComponentImageType::Pointer m_NCCWorkingImage;
+
+ // Gradient mask image - used to multiply the gradient
+ typename FloatImageType::Pointer m_GradientMaskImage;
+
+ // Moving mask image - used to reduce region where metric is computed
+ typename FloatImageType::Pointer m_MovingMaskImage;
+
+ // Mask composites
+ FloatImageSet m_GradientMaskComposite, m_MovingMaskComposite;
+
+ // Amount of jitter - for affine only
+ double m_JitterSigma;
+
+ // Jitter composite
+ VectorImageSet m_JitterComposite;
+
+ void PlaceIntoComposite(FloatImageType *src, MultiComponentImageType *target, int offset);
+ void PlaceIntoComposite(VectorImageType *src, MultiComponentImageType *target, int offset);
+
+ // Adjust NCC radius to be smaller than half image size
+ SizeType AdjustNCCRadius(int level, const SizeType &radius, bool report_on_adjust);
+};
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiImageRegistrationHelper.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/MultiImageRegistrationHelper.txx b/Submodules/greedy/src/MultiImageRegistrationHelper.txx
new file mode 100644
index 0000000..9faacad
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageRegistrationHelper.txx
@@ -0,0 +1,1210 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __MultiImageRegistrationHelper_txx
+#define __MultiImageRegistrationHelper_txx
+#include "MultiImageRegistrationHelper.h"
+
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkNumericTraits.h"
+#include "itkContinuousIndex.h"
+#include "vnl/vnl_math.h"
+#include "lddmm_data.h"
+#include "MultiImageOpticalFlowImageFilter.h"
+#include "MultiComponentNCCImageMetric.h"
+#include "MultiComponentApproximateNCCImageMetric.h"
+#include "MultiComponentMutualInfoImageMetric.h"
+#include "itkVectorIndexSelectionCastImageFilter.h"
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include "itkUnaryFunctorImageFilter.h"
+
+#include "LinearTransformToWarpFilter.h"
+
+#include "itkImageFileWriter.h"
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::SetDefaultPyramidFactors(int n_levels)
+{
+ for(int i = n_levels-1; i>=0; --i)
+ m_PyramidFactors.push_back(1 << i);
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::SetPyramidFactors(const PyramidFactorsType &factors)
+{
+ m_PyramidFactors = factors;
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::AddImagePair(MultiComponentImageType *fixed, MultiComponentImageType *moving, double weight)
+{
+ // Collect the weights
+ for(unsigned i = 0; i < fixed->GetNumberOfComponentsPerPixel(); i++)
+ m_Weights.push_back(weight);
+
+ // Store the images
+ m_Fixed.push_back(fixed);
+ m_Moving.push_back(moving);
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::SetJitterSigma(double sigma)
+{
+ m_JitterSigma = sigma;
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::PlaceIntoComposite(FloatImageType *source, MultiComponentImageType *target, int offset)
+{
+ // We do this using a loop - no threading
+ TFloat *src_ptr = source->GetPixelContainer()->GetBufferPointer();
+ TFloat *trg_ptr = target->GetPixelContainer()->GetBufferPointer() + offset;
+
+ int trg_comp = target->GetNumberOfComponentsPerPixel();
+
+ int n_voxels = source->GetPixelContainer()->Size();
+ TFloat *trg_end = trg_ptr + n_voxels * target->GetNumberOfComponentsPerPixel();
+
+ while(trg_ptr < trg_end)
+ {
+ *trg_ptr = *src_ptr++;
+ trg_ptr += trg_comp;
+ }
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::PlaceIntoComposite(VectorImageType *source, MultiComponentImageType *target, int offset)
+{
+ // We do this using a loop - no threading
+ VectorType *src_ptr = source->GetPixelContainer()->GetBufferPointer();
+ TFloat *trg_ptr = target->GetPixelContainer()->GetBufferPointer() + offset;
+
+ int trg_skip = target->GetNumberOfComponentsPerPixel() - VDim;
+
+ int n_voxels = source->GetPixelContainer()->Size();
+ TFloat *trg_end = trg_ptr + n_voxels * target->GetNumberOfComponentsPerPixel();
+
+ while(trg_ptr < trg_end)
+ {
+ const VectorType &vsrc = *src_ptr++;
+ for(int k = 0; k < VDim; k++)
+ *trg_ptr++ = vsrc[k];
+ trg_ptr += trg_skip;
+ }
+}
+
+#include <vnl/vnl_random.h>
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::DilateCompositeGradientMasksForNCC(SizeType radius)
+{
+ typedef LDDMMData<TFloat, VDim> LDDMMType;
+
+ for(int level = 0; level < m_PyramidFactors.size(); level++)
+ {
+ if(m_GradientMaskComposite[level])
+ {
+ // Threshold the mask itself
+ LDDMMType::img_threshold_in_place(m_GradientMaskComposite[level], 0.5, 1e100, 0.5, 0);
+
+ // Make a copy of the mask
+ typename FloatImageType::Pointer mask_copy;
+ LDDMMType::alloc_img(mask_copy, m_GradientMaskComposite[level]);
+ LDDMMType::img_copy(m_GradientMaskComposite[level], mask_copy);
+
+ // Run the accumulation filter on the mask
+ typename FloatImageType::Pointer mask_accum =
+ AccumulateNeighborhoodSumsInPlace(mask_copy.GetPointer(), radius);
+
+ // Threshold the mask copy
+ LDDMMType::img_threshold_in_place(mask_accum, 0.25, 1e100, 0.5, 0);
+
+ // Add the two images - the result has 1 for the initial mask, 0.5 for the 'outer' mask
+ LDDMMType::img_add_in_place(m_GradientMaskComposite[level], mask_accum);
+ }
+ }
+}
+
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::BuildCompositeImages(double noise_sigma_relative)
+{
+ typedef LDDMMData<TFloat, VDim> LDDMMType;
+
+ // Offsets into the composite images
+ int off_fixed = 0, off_moving = 0;
+
+ // Set up the composite images
+ m_FixedComposite.resize(m_PyramidFactors.size());
+ m_MovingComposite.resize(m_PyramidFactors.size());
+
+ // Repeat for each of the input images
+ for(int j = 0; j < m_Fixed.size(); j++)
+ {
+ // Repeat for each component
+ for(unsigned k = 0; k < m_Fixed[j]->GetNumberOfComponentsPerPixel(); k++)
+ {
+ // Extract the k-th image component from fixed and moving images
+ typedef itk::VectorIndexSelectionCastImageFilter<MultiComponentImageType, FloatImageType> ExtractType;
+ typename ExtractType::Pointer fltExtractFixed, fltExtractMoving;
+
+ fltExtractFixed = ExtractType::New();
+ fltExtractFixed->SetInput(m_Fixed[j]);
+ fltExtractFixed->SetIndex(k);
+ fltExtractFixed->Update();
+
+ fltExtractMoving = ExtractType::New();
+ fltExtractMoving->SetInput(m_Moving[j]);
+ fltExtractMoving->SetIndex(k);
+ fltExtractMoving->Update();
+
+ // Deal with additive noise
+ double noise_sigma_fixed = 0.0, noise_sigma_moving = 0.0;
+
+ if(noise_sigma_relative > 0.0)
+ {
+ // Figure out the quartiles of the fixed image
+ typedef MutualInformationPreprocessingFilter<FloatImageType, FloatImageType> QuantileFilter;
+ typename QuantileFilter::Pointer fltQuantileFixed = QuantileFilter::New();
+ fltQuantileFixed->SetLowerQuantile(0.01);
+ fltQuantileFixed->SetUpperQuantile(0.99);
+ fltQuantileFixed->SetInput(fltExtractFixed->GetOutput());
+ fltQuantileFixed->Update();
+ double range_fixed = fltQuantileFixed->GetUpperQuantileValue(0) - fltQuantileFixed->GetLowerQuantileValue(0);
+ noise_sigma_fixed = noise_sigma_relative * range_fixed;
+
+ // Figure out the quartiles of the moving image
+ typename QuantileFilter::Pointer fltQuantileMoving = QuantileFilter::New();
+ fltQuantileMoving->SetLowerQuantile(0.01);
+ fltQuantileMoving->SetUpperQuantile(0.99);
+ fltQuantileMoving->SetInput(fltExtractMoving->GetOutput());
+ fltQuantileMoving->Update();
+ double range_moving = fltQuantileMoving->GetUpperQuantileValue(0) - fltQuantileMoving->GetLowerQuantileValue(0);
+ noise_sigma_moving = noise_sigma_relative * range_moving;
+
+ // Report noise levels
+ printf("Noise on image %d component %d: fixed = %g, moving = %g\n", j, k, noise_sigma_fixed, noise_sigma_moving);
+ }
+
+ // Compute the pyramid for this component
+ for(int i = 0; i < m_PyramidFactors.size(); i++)
+ {
+ // Downsample the image to the right pyramid level
+ typename FloatImageType::Pointer lFixed, lMoving;
+ if (m_PyramidFactors[i] == 1)
+ {
+ lFixed = fltExtractFixed->GetOutput();
+ lMoving = fltExtractMoving->GetOutput();
+ }
+ else
+ {
+ lFixed = FloatImageType::New();
+ lMoving = FloatImageType::New();
+ LDDMMType::img_downsample(fltExtractFixed->GetOutput(), lFixed, m_PyramidFactors[i]);
+ LDDMMType::img_downsample(fltExtractMoving->GetOutput(), lMoving, m_PyramidFactors[i]);
+ }
+
+ // Add some noise to the images
+ if(noise_sigma_relative > 0.0)
+ {
+ vnl_random randy(12345);
+ for(long i = 0; i < lFixed->GetPixelContainer()->Size(); i++)
+ lFixed->GetBufferPointer()[i] += randy.normal() * noise_sigma_fixed;
+ for(long i = 0; i < lMoving->GetPixelContainer()->Size(); i++)
+ lMoving->GetBufferPointer()[i] += randy.normal() * noise_sigma_moving;
+ }
+
+ // Compute the gradient of the moving image
+ //typename VectorImageType::Pointer gradMoving = VectorImageType::New();
+ //LDDMMType::alloc_vimg(gradMoving, lMoving);
+ //LDDMMType::image_gradient(lMoving, gradMoving);
+
+ // Allocate the composite images if they have not been allocated
+ if(j == 0 && k == 0)
+ {
+ m_FixedComposite[i] = MultiComponentImageType::New();
+ m_FixedComposite[i]->CopyInformation(lFixed);
+ m_FixedComposite[i]->SetNumberOfComponentsPerPixel(m_Weights.size());
+ m_FixedComposite[i]->SetRegions(lFixed->GetBufferedRegion());
+ m_FixedComposite[i]->Allocate();
+
+ m_MovingComposite[i] = MultiComponentImageType::New();
+ m_MovingComposite[i]->CopyInformation(lMoving);
+ m_MovingComposite[i]->SetNumberOfComponentsPerPixel(m_Weights.size());
+ m_MovingComposite[i]->SetRegions(lMoving->GetBufferedRegion());
+ m_MovingComposite[i]->Allocate();
+ }
+
+ // Pack the data into the fixed and moving composite images
+ this->PlaceIntoComposite(lFixed, m_FixedComposite[i], off_fixed);
+ this->PlaceIntoComposite(lMoving, m_MovingComposite[i], off_moving);
+ }
+
+ // Update the offsets
+ off_fixed++;
+ off_moving++;
+ }
+ }
+
+ // Set up the mask pyramid
+ m_GradientMaskComposite.resize(m_PyramidFactors.size(), NULL);
+ if(m_GradientMaskImage)
+ {
+ for(int i = 0; i < m_PyramidFactors.size(); i++)
+ {
+ // Downsample the image to the right pyramid level
+ if (m_PyramidFactors[i] == 1)
+ {
+ m_GradientMaskComposite[i] = m_GradientMaskImage;
+ }
+ else
+ {
+ m_GradientMaskComposite[i] = FloatImageType::New();
+
+ // Downsampling the mask involves smoothing, so the mask will no longer be binary
+ LDDMMType::img_downsample(m_GradientMaskImage, m_GradientMaskComposite[i], m_PyramidFactors[i]);
+ LDDMMType::img_threshold_in_place(m_GradientMaskComposite[i], 0.5, 1e100, 1.0, 0.0);
+ }
+ }
+ }
+
+ // Set up the moving mask pyramid
+ m_MovingMaskComposite.resize(m_PyramidFactors.size(), NULL);
+ if(m_MovingMaskImage)
+ {
+ for(int i = 0; i < m_PyramidFactors.size(); i++)
+ {
+ // Downsample the image to the right pyramid level
+ if (m_PyramidFactors[i] == 1)
+ {
+ m_MovingMaskComposite[i] = m_MovingMaskImage;
+ }
+ else
+ {
+ m_MovingMaskComposite[i] = FloatImageType::New();
+
+ // Downsampling the mask involves smoothing, so the mask will no longer be binary
+ LDDMMType::img_downsample(m_MovingMaskImage, m_MovingMaskComposite[i], m_PyramidFactors[i]);
+
+ // We don't need the moving mask to be binary, we can leave it be floating point...
+ // LDDMMType::img_threshold_in_place(m_MovingMaskComposite[i], 0.5, 1e100, 1.0, 0.0);
+ }
+ }
+ }
+
+ // Set up the jitter images
+ m_JitterComposite.resize(m_PyramidFactors.size(), NULL);
+ if(m_JitterSigma > 0)
+ {
+ for(int i = 0; i < m_PyramidFactors.size(); i++)
+ {
+ // Get the reference space
+ ImageBaseType *base = this->GetReferenceSpace(i);
+ VectorImagePointer iJitter = VectorImageType::New();
+ iJitter->CopyInformation(base);
+ iJitter->SetRegions(base->GetBufferedRegion());
+ iJitter->Allocate();
+
+ vnl_random randy(12345);
+ typedef itk::ImageRegionIterator<VectorImageType> IterType;
+ for(IterType iter(iJitter, iJitter->GetBufferedRegion()); !iter.IsAtEnd(); ++iter)
+ {
+ for(int k = 0; k < VDim; k++)
+ {
+ iter.Value()[k] = randy.normal() * m_JitterSigma;
+ }
+ }
+
+ m_JitterComposite[i] = iJitter;
+ }
+ }
+}
+
+template <class TFloat, unsigned int VDim>
+typename MultiImageOpticalFlowHelper<TFloat, VDim>::ImageBaseType *
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::GetMovingReferenceSpace(int level)
+{
+ return m_MovingComposite[level];
+}
+
+template <class TFloat, unsigned int VDim>
+typename MultiImageOpticalFlowHelper<TFloat, VDim>::ImageBaseType *
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::GetReferenceSpace(int level)
+{
+ return m_FixedComposite[level];
+}
+
+template <class TFloat, unsigned int VDim>
+typename MultiImageOpticalFlowHelper<TFloat, VDim>::Vec
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::GetSmoothingSigmasInPhysicalUnits(int level, double sigma, bool in_physical_units)
+{
+ Vec sigmas;
+ if(in_physical_units)
+ {
+ sigmas.Fill(sigma * m_PyramidFactors[level]);
+ }
+ else
+ {
+ for(int k = 0; k < VDim; k++)
+ sigmas[k] = this->GetReferenceSpace(level)->GetSpacing()[k] * sigma;
+ }
+ return sigmas;
+}
+
+template <class TFloat, unsigned int VDim>
+vnl_vector<double>
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeOpticalFlowField(int level,
+ VectorImageType *def,
+ FloatImageType *out_metric,
+ VectorImageType *out_gradient,
+ double result_scaling)
+{
+ typedef DefaultMultiComponentImageMetricTraits<TFloat, VDim> TraitsType;
+ typedef MultiImageOpticalFlowImageFilter<TraitsType> FilterType;
+
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for (unsigned i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i] * result_scaling;
+
+ // Run the filter
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImage(m_MovingComposite[level]);
+ filter->SetDeformationField(def);
+ filter->SetWeights(wscaled);
+ filter->SetComputeGradient(true);
+ filter->GetMetricOutput()->Graft(out_metric);
+ filter->GetDeformationGradientOutput()->Graft(out_gradient);
+ filter->Update();
+
+ // Get the vector of the normalized metrics
+ return filter->GetAllMetricValues();
+}
+
+template <class TFloat, unsigned int VDim>
+vnl_vector<double>
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeMIFlowField(int level,
+ bool normalized_mutual_information,
+ VectorImageType *def,
+ FloatImageType *out_metric,
+ VectorImageType *out_gradient,
+ double result_scaling)
+{
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for (unsigned i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i] * result_scaling;
+
+ // Set up the mutual information metric
+ typedef DefaultMultiComponentMutualInfoImageMetricTraits<TFloat, unsigned char, VDim> TraitsType;
+ typedef MultiComponentMutualInfoImageMetric<TraitsType> MetricType;
+
+ typedef itk::VectorImage<unsigned char, VDim> BinnedImageType;
+ typedef MutualInformationPreprocessingFilter<MultiComponentImageType, BinnedImageType> BinnerType;
+
+ // TODO: this is utter laziness, get rid of this garbage!
+ static typename BinnerType::Pointer binner_fixed;
+ static typename BinnerType::Pointer binner_moving;
+
+ if(binner_fixed.IsNull()
+ || binner_fixed->GetOutput()->GetBufferedRegion()
+ != m_FixedComposite[level]->GetBufferedRegion())
+ {
+ binner_fixed = BinnerType::New();
+ binner_fixed->SetInput(m_FixedComposite[level]);
+ binner_fixed->SetBins(128);
+ binner_fixed->SetLowerQuantile(0.01);
+ binner_fixed->SetUpperQuantile(0.99);
+ binner_fixed->SetStartAtBinOne(true);
+ binner_fixed->Update();
+
+ binner_moving = BinnerType::New();
+ binner_moving->SetInput(m_MovingComposite[level]);
+ binner_moving->SetBins(128);
+ binner_moving->SetLowerQuantile(0.01);
+ binner_moving->SetUpperQuantile(0.99);
+ binner_moving->SetStartAtBinOne(true);
+ binner_moving->Update();
+ }
+
+
+ typename MetricType::Pointer metric = MetricType::New();
+
+ metric->SetComputeNormalizedMutualInformation(normalized_mutual_information);
+ metric->SetFixedImage(binner_fixed->GetOutput());
+ metric->SetMovingImage(binner_moving->GetOutput());
+ metric->SetDeformationField(def);
+ metric->SetWeights(wscaled);
+ metric->SetComputeGradient(true);
+ metric->GetMetricOutput()->Graft(out_metric);
+ metric->GetDeformationGradientOutput()->Graft(out_gradient);
+ metric->GetMetricOutput()->Graft(out_metric);
+ metric->SetBins(128);
+ metric->Update();
+
+ // Process the results
+ return metric->GetAllMetricValues();
+}
+
+// #undef DUMP_NCC
+#define DUMP_NCC 1
+
+
+template <class TFloat, unsigned int VDim>
+typename MultiImageOpticalFlowHelper<TFloat, VDim>::SizeType
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::AdjustNCCRadius(int level, const SizeType &radius, bool report_on_adjust)
+{
+ SizeType radius_fix = radius;
+ for(int d = 0; d < VDim; d++)
+ {
+ int sz_d = (int) m_FixedComposite[level]->GetBufferedRegion().GetSize()[d];
+ if(radius_fix[d] * 2 + 1 >= sz_d)
+ radius_fix[d] = (sz_d - 1) / 2;
+ }
+
+ if(report_on_adjust && radius != radius_fix)
+ {
+ std::cout << " *** NCC radius adjusted to " << radius_fix
+ << " because image too small at level " << level
+ << " (" << m_FixedComposite[level]->GetBufferedRegion().GetSize() << ")" << std::endl;
+ }
+
+ return radius_fix;
+}
+
+template <class TFloat, unsigned int VDim>
+double
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeNCCMetricImage(int level,
+ VectorImageType *def,
+ const SizeType &radius,
+ FloatImageType *out_metric,
+ VectorImageType *out_gradient,
+ double result_scaling)
+{
+ typedef DefaultMultiComponentImageMetricTraits<TFloat, VDim> TraitsType;
+ typedef MultiComponentNCCImageMetric<TraitsType> FilterType;
+ // typedef MultiComponentApproximateNCCImageMetric<TraitsType> FilterType;
+
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for (unsigned i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i] * result_scaling;
+
+ // Allocate a working image
+ if(m_NCCWorkingImage.IsNull())
+ m_NCCWorkingImage = MultiComponentImageType::New();
+
+ // Is this the first time that this function is being called with this image?
+ bool first_run =
+ m_NCCWorkingImage->GetBufferedRegion() != m_FixedComposite[level]->GetBufferedRegion();
+
+ // Check the radius against the size of the image
+ SizeType radius_fix = AdjustNCCRadius(level, radius, first_run);
+
+ // Run the filter
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImage(m_MovingComposite[level]);
+ filter->SetDeformationField(def);
+ filter->SetWeights(wscaled);
+ filter->SetComputeGradient(true);
+ filter->GetMetricOutput()->Graft(out_metric);
+ filter->GetDeformationGradientOutput()->Graft(out_gradient);
+ filter->SetRadius(radius_fix);
+ filter->SetWorkingImage(m_NCCWorkingImage);
+ filter->SetReuseWorkingImageFixedComponents(!first_run);
+ filter->SetFixedMaskImage(m_GradientMaskComposite[level]);
+
+ // TODO: support moving masks...
+ // filter->SetMovingMaskImage(m_MovingMaskComposite[level]);
+ filter->Update();
+
+ // Get the vector of the normalized metrics
+ return filter->GetMetricValue();
+}
+
+
+
+template <class TFloat, unsigned int VDim>
+double
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeAffineMSDMatchAndGradient(int level,
+ LinearTransformType *tran,
+ FloatImageType *wrkMetric,
+ FloatImageType *wrkMask,
+ VectorImageType *wrkGradMetric,
+ VectorImageType *wrkGradMask,
+ VectorImageType *wrkPhi,
+ LinearTransformType *grad)
+{
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for (unsigned i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i];
+
+ // Set up the optical flow computation
+ typedef DefaultMultiComponentImageMetricTraits<TFloat, VDim> TraitsType;
+ typedef MultiImageOpticalFlowImageFilter<TraitsType> MetricType;
+ typename MetricType::Pointer metric = MetricType::New();
+
+ metric->SetFixedImage(m_FixedComposite[level]);
+ metric->SetMovingImage(m_MovingComposite[level]);
+ metric->SetWeights(wscaled);
+ metric->SetAffineTransform(tran);
+ metric->SetComputeMovingDomainMask(true);
+ metric->GetMetricOutput()->Graft(wrkMetric);
+ metric->SetComputeGradient(grad != NULL);
+ metric->SetFixedMaskImage(m_GradientMaskComposite[level]);
+ metric->SetJitterImage(m_JitterComposite[level]);
+ metric->Update();
+
+ // TODO: erase this
+ /*
+ std::cout << "SAVING METRIC, TRAN = " << tran->GetMatrix() << std::endl;
+ static int iter = 0;
+ std::ostringstream oss; oss << "metric_" << iter << ".nii.gz";
+ LDDMMData<TFloat, VDim>::img_write(wrkMetric, oss.str().c_str());
+ std::stringstream oss2; oss2 << "metric_mask_" << iter << ".nii.gz";
+ LDDMMData<TFloat, VDim>::img_write(wrkMask, oss2.str().c_str());
+ ++iter;
+ */
+
+ // Process the results
+ if(grad)
+ {
+ grad->SetMatrix(metric->GetAffineTransformGradient()->GetMatrix());
+ grad->SetOffset(metric->GetAffineTransformGradient()->GetOffset());
+ }
+
+ return metric->GetMetricValue();
+}
+
+#include "itkRescaleIntensityImageFilter.h"
+
+template <class TFloat, unsigned int VDim>
+double
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeAffineMIMatchAndGradient(int level,
+ bool normalized_mutual_info,
+ LinearTransformType *tran,
+ FloatImageType *wrkMetric,
+ FloatImageType *wrkMask,
+ VectorImageType *wrkGradMetric,
+ VectorImageType *wrkGradMask,
+ VectorImageType *wrkPhi,
+ LinearTransformType *grad)
+{
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for (unsigned i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i];
+
+ // Set up the mutual information metric
+ typedef DefaultMultiComponentMutualInfoImageMetricTraits<TFloat, unsigned char, VDim> TraitsType;
+ typedef MultiComponentMutualInfoImageMetric<TraitsType> MetricType;
+
+ typedef itk::VectorImage<unsigned char, VDim> BinnedImageType;
+ typedef MutualInformationPreprocessingFilter<MultiComponentImageType, BinnedImageType> BinnerType;
+
+ // TODO: this is utter laziness, get rid of this garbage!
+ static typename BinnerType::Pointer binner_fixed;
+ static typename BinnerType::Pointer binner_moving;
+
+ if(binner_fixed.IsNull()
+ || binner_fixed->GetOutput()->GetBufferedRegion()
+ != m_FixedComposite[level]->GetBufferedRegion())
+ {
+ binner_fixed = BinnerType::New();
+ binner_fixed->SetInput(m_FixedComposite[level]);
+ binner_fixed->SetBins(128);
+ binner_fixed->SetLowerQuantile(0.01);
+ binner_fixed->SetUpperQuantile(0.99);
+ binner_fixed->SetStartAtBinOne(true);
+ binner_fixed->Update();
+
+ binner_moving = BinnerType::New();
+ binner_moving->SetInput(m_MovingComposite[level]);
+ binner_moving->SetBins(128);
+ binner_moving->SetLowerQuantile(0.01);
+ binner_moving->SetUpperQuantile(0.99);
+ binner_moving->SetStartAtBinOne(true);
+ binner_moving->Update();
+ }
+
+ typename MetricType::Pointer metric = MetricType::New();
+
+ metric->SetComputeNormalizedMutualInformation(normalized_mutual_info);
+ metric->SetFixedImage(binner_fixed->GetOutput());
+ metric->SetMovingImage(binner_moving->GetOutput());
+ metric->SetWeights(wscaled);
+ metric->SetAffineTransform(tran);
+ metric->SetComputeMovingDomainMask(true);
+ metric->GetMetricOutput()->Graft(wrkMetric);
+ metric->SetComputeGradient(grad != NULL);
+ metric->SetFixedMaskImage(m_GradientMaskComposite[level]);
+ metric->SetBins(128);
+ metric->SetJitterImage(m_JitterComposite[level]);
+ metric->Update();
+
+ // Process the results
+ if(grad)
+ {
+ grad->SetMatrix(metric->GetAffineTransformGradient()->GetMatrix());
+ grad->SetOffset(metric->GetAffineTransformGradient()->GetOffset());
+ }
+
+ return metric->GetMetricValue();
+}
+
+
+
+// TODO: there is a lot of code duplication here!
+template <class TFloat, unsigned int VDim>
+double
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeAffineNCCMatchAndGradient(int level,
+ LinearTransformType *tran,
+ const SizeType &radius,
+ FloatImageType *wrkMetric,
+ FloatImageType *wrkMask,
+ VectorImageType *wrkGradMetric,
+ VectorImageType *wrkGradMask,
+ VectorImageType *wrkPhi,
+ LinearTransformType *grad)
+{
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for (unsigned i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i];
+
+ // Allocate a working image
+ if(m_NCCWorkingImage.IsNull())
+ m_NCCWorkingImage = MultiComponentImageType::New();
+
+ // Set up the optical flow computation
+ typedef DefaultMultiComponentImageMetricTraits<TFloat, VDim> TraitsType;
+ typedef MultiComponentNCCImageMetric<TraitsType> MetricType;
+ typename MetricType::Pointer metric = MetricType::New();
+
+ // Is this the first time that this function is being called with this image?
+ bool first_run =
+ m_NCCWorkingImage->GetBufferedRegion() != m_FixedComposite[level]->GetBufferedRegion();
+
+ // Check the radius against the size of the image
+ SizeType radius_fix = AdjustNCCRadius(level, radius, first_run);
+
+ metric->SetFixedImage(m_FixedComposite[level]);
+ metric->SetMovingImage(m_MovingComposite[level]);
+ metric->SetWeights(wscaled);
+ metric->SetAffineTransform(tran);
+ metric->SetComputeMovingDomainMask(false);
+ metric->GetMetricOutput()->Graft(wrkMetric);
+ metric->SetComputeGradient(grad != NULL);
+ metric->SetRadius(radius_fix);
+ metric->SetWorkingImage(m_NCCWorkingImage);
+ metric->SetReuseWorkingImageFixedComponents(!first_run);
+ metric->SetFixedMaskImage(m_GradientMaskComposite[level]);
+ metric->SetJitterImage(m_JitterComposite[level]);
+ metric->Update();
+
+ // Process the results
+ if(grad)
+ {
+ grad->SetMatrix(metric->GetAffineTransformGradient()->GetMatrix());
+ grad->SetOffset(metric->GetAffineTransformGradient()->GetOffset());
+ }
+
+ return metric->GetMetricValue();
+
+
+ /*
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for(int i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i];
+
+ // Create a deformation field from the affine transform
+ typedef LinearTransformToWarpFilter<
+ MultiComponentImageType, VectorImageType, LinearTransformType> WarpFilter;
+ typename WarpFilter::Pointer warp_source = WarpFilter::New();
+ warp_source->SetInput(m_FixedComposite[level]);
+ warp_source->SetTransform(tran);
+ warp_source->GraftOutput(wrkPhi);
+
+ // Allocate a working image
+ if(m_NCCWorkingImage.IsNull())
+ m_NCCWorkingImage = MultiComponentImageType::New();
+
+ // Set up the optical flow computation
+ typedef DefaultMultiComponentImageMetricTraits<TFloat, VDim> TraitsType;
+ typedef MultiComponentNCCImageMetric<TraitsType> MetricType;
+ typename MetricType::Pointer metric = MetricType::New();
+
+ metric->SetWorkingImage(m_NCCWorkingImage);
+ metric->SetRadius(radius);
+
+ metric->SetFixedImage(m_FixedComposite[level]);
+ metric->SetMovingImage(m_MovingComposite[level]);
+ metric->SetWeights(wscaled);
+ metric->SetDeformationField(warp_source->GetOutput());
+
+ metric->GetMetricOutput()->Graft(wrkMetric);
+
+ metric->SetComputeMovingDomainMask(true);
+ metric->GetMovingDomainMaskOutput()->Graft(wrkMask);
+
+ if(grad)
+ {
+ metric->SetComputeGradient(true);
+ metric->GetGradientOutput()->Graft(wrkGradMetric);
+ metric->GetMovingDomainMaskGradientOutput()->Graft(wrkGradMask);
+ }
+
+ // Use finite differences
+ typedef MultiImageAffineMetricFilter<TraitsType> AffineMetricType;
+ typename AffineMetricType::Pointer affine_metric = AffineMetricType::New();
+
+ // Run the filter
+ affine_metric->SetMetricImage(metric->GetMetricOutput());
+ affine_metric->SetMovingDomainMaskImage(metric->GetMovingDomainMaskOutput());
+
+ // TODO: only if gradient!
+ if(grad)
+ {
+ affine_metric->SetComputeGradient(true);
+ affine_metric->SetGradientImage(metric->GetGradientOutput());
+ affine_metric->SetMovingDomainMaskGradientImage(metric->GetMovingDomainMaskGradientOutput());
+ affine_metric->SetGradientScalingFactor(metric->GetGradientScalingFactor());
+ }
+
+ affine_metric->Update();
+
+ // Process the results
+ if(grad)
+ {
+ grad->SetMatrix(affine_metric->GetMetricGradient()->GetMatrix());
+ grad->SetOffset(affine_metric->GetMetricGradient()->GetOffset());
+ }
+
+ // / *
+ // LDDMMData<TFloat, VDim>::img_write(wrkMetric, "dump_metric.nii.gz");
+ // LDDMMData<TFloat, VDim>::img_write(wrkMask, "dump_mask.nii.gz");
+ // LDDMMData<TFloat, VDim>::vimg_write(wrkGradMetric, "dump_grad_metric.nii.gz");
+ // LDDMMData<TFloat, VDim>::vimg_write(wrkGradMask, "dump_grad_mask.nii.gz");
+ // exit(-1);
+ // * /
+
+ return affine_metric->GetMetricValue();
+*/
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::AffineToField(LinearTransformType *tran, VectorImageType *def)
+{
+ // TODO: convert this to a filter
+ typedef itk::ImageLinearIteratorWithIndex<VectorImageType> IterBase;
+ typedef IteratorExtender<IterBase> Iter;
+ Iter it(def, def->GetBufferedRegion());
+ it.SetDirection(0);
+
+ for(; !it.IsAtEnd(); it.NextLine())
+ {
+ // Get the pointer to the begin of line
+ VectorType *ptr = const_cast<VectorType *>(it.GetPosition());
+ VectorType *ptr_end = ptr + def->GetBufferedRegion().GetSize(0);
+
+ // Get the initial index
+ typename LinearTransformType::InputPointType pt;
+ for(int k = 0; k < VDim; k++)
+ pt[k] = it.GetIndex()[k];
+
+ for(; ptr < ptr_end; ++ptr, ++pt[0])
+ {
+ // Apply transform to the index. TODO: this is stupid, just use an offset
+ typename LinearTransformType::OutputPointType pp = tran->TransformPoint(pt);
+ for(int k = 0; k < VDim; k++)
+ (*ptr)[k] = pp[k] - pt[k];
+ }
+ }
+}
+
+
+template <class TInputImage, class TOutputImage, class TFunctor>
+class UnaryPositionBasedFunctorImageFilter : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+
+ typedef UnaryPositionBasedFunctorImageFilter<TInputImage,TOutputImage,TFunctor> Self;
+ typedef itk::ImageToImageFilter<TInputImage, TOutputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+ typedef typename Superclass::OutputImageRegionType OutputImageRegionType;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( UnaryPositionBasedFunctorImageFilter, itk::ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension );
+
+ void SetFunctor(const TFunctor &f) { this->m_Functor = f; }
+
+protected:
+ UnaryPositionBasedFunctorImageFilter() {}
+ ~UnaryPositionBasedFunctorImageFilter() {}
+
+ virtual void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ itk::ThreadIdType threadId)
+ {
+ typedef itk::ImageRegionConstIteratorWithIndex<TInputImage> InputIter;
+ InputIter it_in(this->GetInput(), outputRegionForThread);
+
+ typedef itk::ImageRegionIterator<TOutputImage> OutputIter;
+ OutputIter it_out(this->GetOutput(), outputRegionForThread);
+
+ for(; !it_out.IsAtEnd(); ++it_out, ++it_in)
+ {
+ it_out.Set(m_Functor(it_in.Get(), it_in.GetIndex()));
+ }
+ }
+
+ TFunctor m_Functor;
+};
+
+template <class TWarpImage>
+class VoxelToPhysicalWarpFunctor
+{
+public:
+ typedef itk::ImageBase<TWarpImage::ImageDimension> ImageBaseType;
+ typedef typename TWarpImage::PixelType VectorType;
+ typedef itk::Index<TWarpImage::ImageDimension> IndexType;
+
+ VectorType operator()(const VectorType &v, const IndexType &pos)
+ {
+ // Get the physical point for the tail of the arrow
+ typedef itk::ContinuousIndex<double, TWarpImage::ImageDimension> CIType;
+ typedef typename TWarpImage::PointType PtType;
+
+ CIType ia, ib;
+ PtType pa, pb;
+ for(int i = 0; i < TWarpImage::ImageDimension; i++)
+ {
+ ia[i] = pos[i];
+ ib[i] = pos[i] + v[i];
+ }
+
+ m_Warp->TransformContinuousIndexToPhysicalPoint(ia, pa);
+ m_MovingSpace->TransformContinuousIndexToPhysicalPoint(ib, pb);
+
+ VectorType y;
+ for(int i = 0; i < TWarpImage::ImageDimension; i++)
+ y[i] = pb[i] - pa[i];
+
+ return y;
+ }
+
+ VoxelToPhysicalWarpFunctor(TWarpImage *warp, ImageBaseType *moving)
+ : m_Warp(warp), m_MovingSpace(moving) {}
+
+ VoxelToPhysicalWarpFunctor() {}
+
+protected:
+
+ TWarpImage *m_Warp;
+ ImageBaseType *m_MovingSpace;
+};
+
+
+template <class TWarpImage>
+class PhysicalToVoxelWarpFunctor
+{
+public:
+ typedef itk::ImageBase<TWarpImage::ImageDimension> ImageBaseType;
+ typedef typename TWarpImage::PixelType VectorType;
+ typedef itk::Index<TWarpImage::ImageDimension> IndexType;
+
+ VectorType operator()(const VectorType &v, const IndexType &pos)
+ {
+ // Get the voxel offset between the tip of the arrow and the input position
+ // Get the physical point for the tail of the arrow
+ typedef itk::ContinuousIndex<double, TWarpImage::ImageDimension> CIType;
+ typedef typename TWarpImage::PointType PtType;
+
+ CIType ia, ib;
+ PtType pa, pb;
+
+ // Get the base physical position
+ for(int i = 0; i < TWarpImage::ImageDimension; i++)
+ ia[i] = pos[i];
+ m_Warp->TransformContinuousIndexToPhysicalPoint(ia, pa);
+
+ // Compute the tip physical position
+ for(int i = 0; i < TWarpImage::ImageDimension; i++)
+ pb[i] = pa[i] + v[i];
+
+ // Map the tip into continuous index
+ m_MovingSpace->TransformPhysicalPointToContinuousIndex(pb, ib);
+
+ VectorType y;
+ for(int i = 0; i < TWarpImage::ImageDimension; i++)
+ y[i] = ib[i] - ia[i];
+
+ return y;
+ }
+
+ PhysicalToVoxelWarpFunctor(TWarpImage *warp, ImageBaseType *moving)
+ : m_Warp(warp), m_MovingSpace(moving) {}
+
+ PhysicalToVoxelWarpFunctor() {}
+
+protected:
+
+ TWarpImage *m_Warp;
+ ImageBaseType *m_MovingSpace;
+};
+
+
+
+/**
+ * This functor is used to compress a warp before saving it. The input
+ * to this functor is a voxel-space warp, and the output is a physical
+ * space warp, with the precision of the voxel-space warp reduced to a
+ * prescribed value. The functor will also cast the warp to desired
+ * output type
+ */
+template <class TInputWarp, class TOutputWarp>
+class CompressWarpFunctor
+{
+public:
+ typedef VoxelToPhysicalWarpFunctor<TInputWarp> PhysFunctor;
+ typedef typename PhysFunctor::ImageBaseType ImageBaseType;
+
+ typedef typename TInputWarp::IndexType IndexType;
+ typedef typename TInputWarp::PixelType InputVectorType;
+ typedef typename TOutputWarp::PixelType OutputVectorType;
+
+ CompressWarpFunctor() {}
+
+ CompressWarpFunctor(TInputWarp *input, ImageBaseType *mov_space, double precision)
+ : m_InputWarp(input), m_Precision(precision), m_ScaleFactor(1.0 / m_Precision),
+ m_PhysFunctor(input, mov_space) {}
+
+ OutputVectorType operator()(const InputVectorType &v, const IndexType &pos)
+ {
+ InputVectorType w;
+
+ // Round to precision
+ if(m_Precision > 0)
+ {
+ for(int i = 0; i < TInputWarp::ImageDimension; i++)
+ w[i] = std::floor(v[i] * m_ScaleFactor + 0.5) * m_Precision;
+ }
+
+ // Map to physical space
+ w = m_PhysFunctor(w, pos);
+
+ // Cast to output type
+ InputVectorType y;
+ for(int i = 0; i < TInputWarp::ImageDimension; i++)
+ y[i] = static_cast<typename OutputVectorType::ValueType>(w[i]);
+
+ return y;
+ }
+
+protected:
+ TInputWarp *m_InputWarp;
+ double m_Precision, m_ScaleFactor;
+ PhysFunctor m_PhysFunctor;
+};
+
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::VoxelWarpToPhysicalWarp(VectorImageType *warp, ImageBaseType *moving_space, VectorImageType *result)
+{
+ typedef VoxelToPhysicalWarpFunctor<VectorImageType> Functor;
+ typedef UnaryPositionBasedFunctorImageFilter<VectorImageType,VectorImageType,Functor> Filter;
+ Functor functor(warp, moving_space);
+
+ typename Filter::Pointer filter = Filter::New();
+ filter->SetFunctor(functor);
+ filter->SetInput(warp);
+ filter->GraftOutput(result);
+ filter->Update();
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::PhysicalWarpToVoxelWarp(VectorImageType *warp, ImageBaseType *moving_space, VectorImageType *result)
+{
+ typedef PhysicalToVoxelWarpFunctor<VectorImageType> Functor;
+ typedef UnaryPositionBasedFunctorImageFilter<VectorImageType,VectorImageType,Functor> Filter;
+ Functor functor(warp, moving_space);
+
+ typename Filter::Pointer filter = Filter::New();
+ filter->SetFunctor(functor);
+ filter->SetInput(warp);
+ filter->GraftOutput(result);
+ filter->Update();
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::WriteCompressedWarpInPhysicalSpace(int level, VectorImageType *warp, const char *filename, double precision)
+{
+ WriteCompressedWarpInPhysicalSpace(warp, this->GetMovingReferenceSpace(level), filename, precision);
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::WriteCompressedWarpInPhysicalSpace(VectorImageType *warp, ImageBaseType *moving_ref_space, const char *filename, double precision)
+{
+ // Define a _float_ output type, even if working with double precision (less space on disk)
+ typedef itk::CovariantVector<float, VDim> OutputVectorType;
+ typedef itk::Image<OutputVectorType, VDim> OutputWarpType;
+ typedef CompressWarpFunctor<VectorImageType, OutputWarpType> Functor;
+
+ typedef UnaryPositionBasedFunctorImageFilter<VectorImageType,OutputWarpType,Functor> Filter;
+ Functor functor(warp, moving_ref_space, precision);
+
+ typename Filter::Pointer filter = Filter::New();
+ filter->SetFunctor(functor);
+ filter->SetInput(warp);
+ filter->Update();
+
+ LDDMMData<float, VDim>::vimg_write(filter->GetOutput(), filename);
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeDeformationFieldInverse(
+ VectorImageType *warp, VectorImageType *uInverse, int n_sqrt, bool verbose)
+{
+ typedef LDDMMData<TFloat, VDim> LDDMMType;
+
+ // Create a copy of the forward warp
+ VectorImagePointer uForward = VectorImageType::New();
+ LDDMMType::alloc_vimg(uForward, warp);
+ LDDMMType::vimg_copy(warp, uForward);
+
+ // Create a working image for the square root computation
+ VectorImagePointer uWork = VectorImageType::New();
+ LDDMMType::alloc_vimg(uWork, warp);
+
+ // Compute the square root
+ for(int k = 0; k < n_sqrt; k++)
+ {
+ for(int i = 0; i < 20; i++)
+ {
+ LDDMMType::interp_vimg(uInverse, uInverse, 1.0, uWork);
+ LDDMMType::vimg_scale_in_place(uWork, -1.0);
+ LDDMMType::vimg_add_scaled_in_place(uWork, uInverse, -1.0);
+ LDDMMType::vimg_add_in_place(uWork, uForward);
+
+ // Check the maximum delta
+ // LDDMMType::vimg_norm_min_max(uDelta, iTemp, norm_min, norm_max);
+ // std::cout << "sqrt iter " << i << " max_delta " << norm_max << std::endl;
+
+ LDDMMType::vimg_add_scaled_in_place(uInverse, uWork, 0.5);
+ }
+
+ LDDMMType::vimg_copy(uInverse, uForward);
+ uInverse->FillBuffer(itk::NumericTraits<typename VectorImageType::PixelType>::Zero);
+ }
+
+ // At this point, uForward holds the small deformation
+ // Try to compute the inverse of the current forward transformation
+ for(int i = 0; i < 20; i++)
+ {
+ // We are using uPhys as temporary storage
+ LDDMMType::interp_vimg(uForward, uInverse, 1.0, uWork);
+ LDDMMType::vimg_scale_in_place(uWork, -1.0);
+
+ // Compute the maximum change from last iteration
+ LDDMMType::vimg_subtract_in_place(uInverse, uWork);
+
+ // std::cout << "inverse iter " << i << " change " << norm_max << std::endl;
+ LDDMMType::vimg_copy(uWork, uInverse);
+ }
+
+ // Compose the inverses
+ for(int i = 0; i < n_sqrt; i++)
+ {
+ LDDMMType::interp_vimg(uInverse, uInverse, 1.0, uWork);
+ LDDMMType::vimg_add_in_place(uInverse, uWork);
+ }
+
+ // If verbose, compute the maximum error
+ if(verbose)
+ {
+ FloatImagePointer iNorm = FloatImageType::New();
+ LDDMMType::alloc_img(iNorm, uWork);
+ LDDMMType::interp_vimg(uInverse, uForward, 1.0, uWork);
+ LDDMMType::vimg_add_in_place(uWork, uForward);
+ TFloat norm_min, norm_max;
+ LDDMMType::vimg_norm_min_max(uWork, iNorm, norm_min, norm_max);
+ std::cout << "Warp inverse max residual: " << norm_max << std::endl;
+ }
+}
+
+
+#endif
diff --git a/Submodules/greedy/src/MultiImageSimpleWarpImageFilter.h b/Submodules/greedy/src/MultiImageSimpleWarpImageFilter.h
new file mode 100644
index 0000000..e86a5ae
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageSimpleWarpImageFilter.h
@@ -0,0 +1,903 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: SimpleWarpImageFilter.h,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:00 $
+ Version: $Revision: 1.31 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __MultiImageSimpleWarpImageFilter_h
+#define __MultiImageSimpleWarpImageFilter_h
+#include "itkImageBase.h"
+#include "itkImageFunction.h"
+#include "itkImageToImageFilter.h"
+#include "itkPoint.h"
+#include "itkFixedArray.h"
+#include "itkVectorImage.h"
+#include "itkMatrixOffsetTransformBase.h"
+#include "itkInPlaceImageFilter.h"
+
+/**
+ * This class is used to perform mean square intensity difference type
+ * registration with multiple images. The filter is designed for speed
+ * of interpolation.
+ */
+template <class TFloat, unsigned int VDim>
+class MultiImageOpticalFlowHelper
+{
+public:
+
+ typedef itk::VectorImage<TFloat, VDim> MultiComponentImageType;
+ typedef itk::Image<TFloat, VDim> FloatImageType;
+ typedef itk::CovariantVector<TFloat, VDim> VectorType;
+ typedef itk::Image<VectorType, VDim> VectorImageType;
+ typedef itk::ImageBase<VDim> ImageBaseType;
+
+ typedef std::vector<int> PyramidFactorsType;
+
+ typedef itk::MatrixOffsetTransformBase<double, VDim, VDim> LinearTransformType;
+
+ /** Set default (power of two) pyramid factors */
+ void SetDefaultPyramidFactors(int n_levels);
+
+ /** Set the pyramid factors - for multi-resolution (e.g., 8,4,2) */
+ void SetPyramidFactors(const PyramidFactorsType &factors);
+
+ /** Add a pair of multi-component images to the class - same weight for each component */
+ void AddImagePair(MultiComponentImageType *fixed, MultiComponentImageType *moving, double weight);
+
+ /** Compute the composite image - must be run before any sampling is done */
+ void BuildCompositeImages();
+
+ /** Get the reference image for level k */
+ ImageBaseType *GetReferenceSpace(int level);
+
+ /** Get the reference image for level k */
+ ImageBaseType *GetMovingReferenceSpace(int level);
+
+ /** Perform interpolation - compute [(I - J(Tx)) GradJ(Tx)] */
+ double ComputeOpticalFlowField(int level, VectorImageType *def, VectorImageType *result,
+ double result_scaling = 1.0);
+
+ double ComputeAffineMatchAndGradient(int level, LinearTransformType *tran,
+ LinearTransformType *grad = NULL);
+
+
+protected:
+
+ // Pyramid factors
+ PyramidFactorsType m_PyramidFactors;
+
+ // Weights
+ std::vector<double> m_Weights;
+
+ // Vector of images
+ typedef std::vector<typename MultiComponentImageType::Pointer> MultiCompImageSet;
+
+ // Fixed and moving images
+ MultiCompImageSet m_Fixed, m_Moving;
+
+ // Composite image at each resolution level
+ MultiCompImageSet m_FixedComposite, m_MovingComposite;
+
+ void PlaceIntoComposite(FloatImageType *src, MultiComponentImageType *target, int offset);
+ void PlaceIntoComposite(VectorImageType *src, MultiComponentImageType *target, int offset);
+};
+
+namespace itk
+{
+
+template<class TFloat, class TFloatArr, unsigned int VDim>
+static void flatten_affine_transform(
+ const MatrixOffsetTransformBase<TFloat, VDim, VDim> *transform,
+ TFloatArr *flat_array)
+{
+ int pos = 0;
+ for(int i = 0; i < VDim; i++)
+ {
+ flat_array[pos++] = transform->GetOffset()[i];
+ for(int j = 0; j < VDim; j++)
+ flat_array[pos++] = transform->GetMatrix()(i,j);
+ }
+}
+
+template<class TFloat, class TFloatArr, unsigned int VDim>
+static void unflatten_affine_transform(
+ const TFloatArr *flat_array,
+ MatrixOffsetTransformBase<TFloat, VDim, VDim> *transform,
+ double scaling = 1.0)
+{
+ typename MatrixOffsetTransformBase<TFloat, VDim, VDim>::MatrixType matrix;
+ typename MatrixOffsetTransformBase<TFloat, VDim, VDim>::OffsetType offset;
+
+ int pos = 0;
+ for(int i = 0; i < VDim; i++)
+ {
+ offset[i] = flat_array[pos++] * scaling;
+ for(int j = 0; j < VDim; j++)
+ matrix(i, j) = flat_array[pos++] * scaling;
+ }
+
+ transform->SetMatrix(matrix);
+ transform->SetOffset(offset);
+}
+
+
+template<class TInputImage, class TOutputImage, class TDeformationImage>
+class MultiImageOpticalFlowWarpTraits
+{
+public:
+ typedef TDeformationImage TransformType;
+
+ typedef typename TInputImage::InternalPixelType InputPixelType;
+ typedef typename TOutputImage::InternalPixelType OutputPixelType;
+
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TDeformationImage::ImageDimension );
+
+ static DataObject *AsDataObject(TransformType *t) { return t; }
+ static ImageBase<ImageDimension> *AsImageBase(TransformType *t) { return t; }
+
+ static int GetResultAccumSize(int) { return 1; }
+
+ static int GetStride(int) { return 1; }
+
+ static bool InterpolateOutsideOverlapRegion() { return true; }
+
+ static void TransformIndex(const itk::Index<ImageDimension> &pos,
+ TransformType *transform, long offset,
+ float *ptran)
+ {
+ typename TDeformationImage::InternalPixelType &def = transform->GetBufferPointer()[offset];
+ for(int i = 0; i < ImageDimension; i++)
+ ptran[i] = pos[i] + def[i];
+ }
+
+ static void PostInterpolate(
+ const itk::Index<ImageDimension> &pos,
+ const InputPixelType *pFix, const InputPixelType *pMov, int nComp,
+ float *weight, float mask, double *summary, OutputPixelType &vOut)
+ {
+ for(int i = 0; i < ImageDimension; i++)
+ vOut[i] = 0;
+
+ const InputPixelType *pMovEnd = pMov + nComp;
+ while(pMov < pMovEnd)
+ {
+ double del = (*pFix++) - *(pMov++);
+ double delw = (*weight++) * del;
+ for(int i = 0; i < ImageDimension; i++)
+ vOut[i] += delw * *(pMov++);
+ *summary += delw * del;
+ }
+ }
+};
+
+template<class TInputImage, class TOutputImage>
+class MultiImageOpticalFlowAffineGradientTraits
+{
+public:
+
+ typedef typename TInputImage::InternalPixelType InputPixelType;
+ typedef typename TOutputImage::InternalPixelType OutputPixelType;
+
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TInputImage::ImageDimension );
+
+ typedef MatrixOffsetTransformBase<double, ImageDimension, ImageDimension> TransformType;
+
+
+ static DataObject *AsDataObject(TransformType *t) { return NULL; }
+ static ImageBase<ImageDimension> *AsImageBase(TransformType *t) { return NULL; }
+
+ static int GetResultAccumSize(int nComp) { return 1 + ImageDimension * (1 + ImageDimension); }
+
+ static int GetStride(int) { return 1; }
+
+ static bool InterpolateOutsideOverlapRegion() { return true; }
+
+ static void TransformIndex(const itk::Index<ImageDimension> &pos,
+ TransformType *transform, long offset,
+ float *ptran)
+ {
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ ptran[i] = transform->GetOffset()[i];
+ for(int j = 0; j < ImageDimension; j++)
+ ptran[i] += transform->GetMatrix()(i,j) * pos[j];
+ }
+ }
+
+ static void PostInterpolate(
+ const itk::Index<ImageDimension> &pos,
+ const InputPixelType *pFix, const InputPixelType *pMov, int nComp,
+ float *weight, float mask, double *summary, OutputPixelType &vOut)
+ {
+ const InputPixelType *pMovEnd = pMov + nComp;
+ for(int i = 0; i < ImageDimension; i++)
+ vOut[i] = 0;
+
+ if(mask == 1.0)
+ {
+ while(pMov < pMovEnd)
+ {
+ double del = (*pFix++) - *(pMov++);
+ double delw = (*weight++) * del;
+ for(int i = 0; i < ImageDimension; i++)
+ vOut[i] += delw * *(pMov++);
+ *summary += delw * del;
+ }
+
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ *(++summary) += vOut[i];
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ *(++summary) += vOut[i] * pos[j];
+ }
+ }
+ }
+ else if(mask > 0.0)
+ {
+ while(pMov < pMovEnd)
+ {
+ double del = (*pFix++) - *(pMov++);
+ double delw = (*weight++) * del;
+ //for(int i = 0; i < ImageDimension; i++)
+ // vOut[i] += delw * ( *(pMov++) * mask + del *
+ //*summary += delw * del;
+ }
+
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ *(++summary) += vOut[i];
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ *(++summary) += vOut[i] * pos[j];
+ }
+ }
+ }
+
+
+ /*
+ */
+
+
+ }
+};
+
+
+
+
+template<class TInputImage, class TOutputImage>
+class MultiImageOpticalFlowAffineObjectiveTraits
+{
+public:
+ typedef MultiImageOpticalFlowAffineGradientTraits<TInputImage,TOutputImage> SourceTraits;
+ typedef typename SourceTraits::TransformType TransformType;
+
+ typedef typename TInputImage::InternalPixelType InputPixelType;
+ typedef typename TOutputImage::InternalPixelType OutputPixelType;
+
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TInputImage::ImageDimension );
+
+ static DataObject *AsDataObject(TransformType *t) { return NULL; }
+ static ImageBase<ImageDimension> *AsImageBase(TransformType *t) { return NULL; }
+
+ // We keep track of the average difference between fixed and interpolating moving
+ // images as well as the size of the overlap region (i.e., number of voxels where
+ // the measurement was obtained
+ static int GetResultAccumSize(int) { return 2; }
+
+ static int GetStride(int) { return 1 + ImageDimension; }
+
+ static bool InterpolateOutsideOverlapRegion() { return true; }
+
+ static void TransformIndex(const itk::Index<ImageDimension> &pos,
+ TransformType *transform, long offset,
+ float *ptran)
+ {
+ return SourceTraits::TransformIndex(pos, transform, offset, ptran);
+ }
+
+ static void PostInterpolate(
+ const itk::Index<ImageDimension> &pos,
+ const InputPixelType *pFix, const InputPixelType *pMov, int nComp,
+ float *weight, float mask, double *summary, OutputPixelType &vOut)
+ {
+<<<<<<< HEAD
+ double wdiff = 0.0;
+
+ if(mask > 0.0)
+ {
+ for(int i = 0; i < nComp; i+=(1+ImageDimension))
+ {
+ double del = (*pFix++) - *(pMov++);
+ double delw = (*weight++) * del;
+ wdiff += delw * del;
+ }
+
+ summary[0] += wdiff * mask;
+ summary[1] += mask;
+=======
+ double avgsqdiff;
+ for(int i = 0; i < nComp; i+=(1+ImageDimension))
+ {
+ double del = (*pFix++) - *(pMov++);
+ double delw = (*weight++) * del;
+ avgsqdiff += delw * del;
+>>>>>>> 43ef7496f075d647d8e516d0c8c81fc86f04a1ae
+ }
+ summary[0] += avgsqdiff;
+ summary[1] += 1.0;
+ }
+};
+
+
+
+/** \class MultiImageOpticalFlowImageFilter
+ * \brief Warps an image using an input deformation field (for LDDMM)
+ *
+ * This filter efficiently computes the optical flow field between a
+ * set of image pairs, given a transformation phi. This filter is the
+ * workhorse of deformable and affine rigid registration algorithms that
+ * use the mean squared difference metric. Given a set of fixed images F_i
+ * and moving images M_i, it computes
+ *
+ * v(x) = Sum_i w_i \[ F_i(x) - M_i(Phi(x)) ] \Grad M_i (Phi(x))
+ *
+ * The efficiency of this filter comes from combining the interpolation of
+ * all the M and GradM terms in one loop, so that all possible computations
+ * are reused
+ *
+ * The fixed and moving images must be passed in to the filter in the form
+ * of VectorImages of size K and (VDim+K), respectively - i.e., the moving
+ * images and their gradients are packed together.
+ *
+ * The output should be an image of CovariantVector type
+ *
+ * \warning This filter assumes that the input type, output type
+ * and deformation field type all have the same number of dimensions.
+ *
+ */
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+class ITK_EXPORT MultiImageOpticalFlowImageFilter :
+ public ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiImageOpticalFlowImageFilter Self;
+ typedef ImageToImageFilter<TInputImage,TOutputImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageOpticalFlowImageFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TOutputImage::ImageDimension );
+
+ /** Typedef to describe the output image region type. */
+ typedef typename TInputImage::RegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef TInputImage InputImageType;
+ typedef typename TInputImage::PixelType InputPixelType;
+ typedef typename TInputImage::InternalPixelType InputComponentType;
+ typedef TOutputImage OutputImageType;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputPixelType::ComponentType OutputComponentType;
+ typedef typename OutputImageType::IndexType IndexType;
+ typedef typename OutputImageType::IndexValueType IndexValueType;
+ typedef typename OutputImageType::SizeType SizeType;
+ typedef typename OutputImageType::SpacingType SpacingType;
+ typedef typename OutputImageType::DirectionType DirectionType;
+
+ /** Information from the parent class */
+ typedef typename TTransformTraits::TransformType TransformType;
+ typedef typename TransformType::Pointer TransformPointer;
+
+ /** Weight vector */
+ typedef vnl_vector<float> WeightVectorType;
+ typedef vnl_vector<double> SummaryType;
+
+
+ /** typedef for base image type at the current ImageDimension */
+ typedef ImageBase<itkGetStaticConstMacro(ImageDimension)> ImageBaseType;
+
+ /** Set the fixed image(s) */
+ void SetFixedImage(InputImageType *fixed)
+ { this->ProcessObject::SetInput("Primary", fixed); }
+
+ /** Set the moving image(s) and their gradients */
+ void SetMovingImageAndGradient(InputImageType *moving)
+ { this->ProcessObject::SetInput("moving", moving); }
+
+ /** Set the weight vector */
+ itkSetMacro(Weights, WeightVectorType)
+ itkGetConstMacro(Weights, WeightVectorType)
+
+ /** Set the transform field. */
+ void SetTransform(TransformType *transform)
+ {
+ m_Transform = transform;
+ if(TTransformTraits::AsDataObject(transform))
+ this->ProcessObject::SetInput("transform", TTransformTraits::AsDataObject(transform));
+ }
+
+ /** Summary results after running the filter */
+ itkGetConstMacro(SummaryResult, SummaryType)
+
+ /** This filter produces an image which is a different
+ * size than its input image. As such, it needs to provide an
+ * implemenation for GenerateOutputInformation() which set
+ * the output information according the OutputSpacing, OutputOrigin
+ * and the deformation field's LargestPossibleRegion. */
+ virtual void GenerateOutputInformation();
+
+ /** It is difficult to compute in advance the input image region
+ * required to compute the requested output region. Thus the safest
+ * thing to do is to request for the whole input image.
+ *
+ * For the deformation field, the input requested region
+ * set to be the same as that of the output requested region. */
+ virtual void GenerateInputRequestedRegion();
+
+ /** This method is used to set the state of the filter before
+ * multi-threading. */
+ virtual void BeforeThreadedGenerateData();
+
+ /** This method is used to set the state of the filter after
+ * multi-threading. */
+ virtual void AfterThreadedGenerateData();
+
+protected:
+ MultiImageOpticalFlowImageFilter();
+ ~MultiImageOpticalFlowImageFilter() {}
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId );
+
+ void VerifyInputInformation() {}
+
+ // Object to assist specializaiton
+ struct DispatchBase {};
+ template <unsigned int VDim> struct Dispatch : public DispatchBase {};
+
+ /** Fast interpolation method */
+ /*
+ double OpticalFlowFastInterpolate(const Dispatch<3> &dispatch,
+ float *cix,
+ const InputComponentType *fixed_ptr,
+ const InputComponentType *moving_ptr,
+ OutputPixelType &outVector,
+ int *movSize,
+ int nComp,
+ const InputComponentType *def_value);
+
+ // Dummy implementation
+ double OpticalFlowFastInterpolate(const DispatchBase &base,
+ float *cix,
+ const InputComponentType *fixed_ptr,
+ const InputComponentType *moving_ptr,
+ OutputPixelType &outVector,
+ int *movSize,
+ int nComp,
+ const InputComponentType *def_value)
+ { return 0.0; }
+ */
+
+ bool OpticalFlowFastInterpolate(const Dispatch<3> &dispatch,
+ const InputComponentType *moving_ptr,
+ int nComp, int stride, int *movSize,
+ const InputComponentType *def_value,
+ float *cix,
+ InputComponentType *out);
+
+ bool OpticalFlowFastInterpolate(const DispatchBase &base,
+ const InputComponentType *moving_ptr,
+ int nComp, int stride, int *movSize,
+ const InputComponentType *def_value,
+ float *cix,
+ InputComponentType *out) { return true; }
+
+ void OpticalFlowFastInterpolateWithMask(const Dispatch<3> &dispatch,
+ const InputComponentType *moving_ptr,
+ int nComp, int stride, int *movSize,
+ const InputComponentType *def_value,
+ float *cix,
+ InputComponentType *out, float &outMask);
+
+ void OpticalFlowFastInterpolateWithMask(const DispatchBase &base,
+ const InputComponentType *moving_ptr,
+ int nComp, int stride, int *movSize,
+ const InputComponentType *def_value,
+ float *cix,
+ InputComponentType *out, float &outMask) { }
+
+private:
+ MultiImageOpticalFlowImageFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // Weight vector
+ WeightVectorType m_Weights;
+
+ // Transform pointer
+ TransformPointer m_Transform;
+
+ // Vector of accumulated data (difference, gradient of affine transform, etc)
+ SummaryType m_SummaryResult;
+ std::vector<SummaryType> m_SummaryResultPerThread;
+};
+
+
+
+
+
+
+/**
+ * This filter computes the similarity between a set of moving images and a
+ * set of fixed images in a highly optimized way
+ */
+template <class TInputImage>
+class ITK_EXPORT MultiImageAffineMSDMetricFilter :
+ public ImageToImageFilter<TInputImage, TInputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiImageAffineMSDMetricFilter Self;
+ typedef InPlaceImageFilter<TInputImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageAffineMSDMetricFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TInputImage::ImageDimension );
+
+ /** Typedef to describe the output image region type. */
+ typedef typename TInputImage::RegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef TInputImage InputImageType;
+ typedef ImageBase<ImageDimension> ImageBaseType;
+ typedef typename TInputImage::PixelType InputPixelType;
+ typedef typename TInputImage::InternalPixelType InputComponentType;
+ typedef typename InputImageType::IndexType IndexType;
+ typedef typename InputImageType::IndexValueType IndexValueType;
+ typedef typename InputImageType::SizeType SizeType;
+ typedef typename InputImageType::SpacingType SpacingType;
+ typedef typename InputImageType::DirectionType DirectionType;
+
+ /** Information from the parent class */
+ typedef MatrixOffsetTransformBase<double, ImageDimension, ImageDimension> TransformType;
+ typedef typename TransformType::Pointer TransformPointer;
+
+ /** Weight vector */
+ typedef vnl_vector<float> WeightVectorType;
+
+ /** Set the fixed image(s) */
+ void SetFixedImage(InputImageType *fixed)
+ { this->ProcessObject::SetInput("Primary", fixed); }
+
+ /** Set the moving image(s) and their gradients */
+ void SetMovingImageAndGradient(InputImageType *moving)
+ { this->ProcessObject::SetInput("moving", moving); }
+
+ /** Set the weight vector */
+ itkSetMacro(Weights, WeightVectorType)
+ itkGetConstMacro(Weights, WeightVectorType)
+
+ /** Whether to compute gradient */
+ itkSetMacro(ComputeGradient, bool)
+ itkGetConstMacro(ComputeGradient, bool)
+
+ /** Set the transform field. */
+ void SetTransform(TransformType *transform)
+ { m_Transform = transform; }
+
+ itkGetConstMacro(Transform, TransformType *)
+
+ /** Value of the similarity objective after running the filter */
+ itkGetConstMacro(MetricValue, double)
+
+ /** The gradient (in the form of a transform) after running the filter */
+ itkGetConstMacro(MetricGradient, TransformType *)
+
+
+
+protected:
+ MultiImageAffineMSDMetricFilter() : m_ComputeGradient(false) {}
+ ~MultiImageAffineMSDMetricFilter() {}
+
+ void PrintSelf(std::ostream& os, Indent indent) const
+ { this->PrintSelf(os, indent); }
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId );
+
+ /** It is difficult to compute in advance the input image region
+ * required to compute the requested output region. Thus the safest
+ * thing to do is to request for the whole input image.
+ *
+ * For the deformation field, the input requested region
+ * set to be the same as that of the output requested region. */
+ virtual void GenerateInputRequestedRegion();
+
+ /** Override since input passed to output */
+ virtual void EnlargeOutputRequestedRegion(DataObject *data);
+
+ /** This method is used to set the state of the filter before
+ * multi-threading. */
+ virtual void BeforeThreadedGenerateData();
+
+ /** This method is used to set the state of the filter after
+ * multi-threading. */
+ virtual void AfterThreadedGenerateData();
+
+ /** Allocate outputs - just pass through the input */
+ virtual void AllocateOutputs();
+
+ void VerifyInputInformation() {}
+
+ // Object to assist specializaiton
+ struct DispatchBase {};
+ template <unsigned int VDim> struct Dispatch : public DispatchBase {};
+
+private:
+ MultiImageAffineMSDMetricFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // Weight vector
+ WeightVectorType m_Weights;
+
+ // Transform pointer
+ TransformPointer m_Transform;
+
+ // Whether the gradient is computed
+ bool m_ComputeGradient;
+
+ // Data accumulated for each thread
+ struct ThreadData {
+ double metric, mask;
+ vnl_vector<double> gradient, grad_mask;
+ ThreadData() : metric(0.0), mask(0.0),
+ gradient(ImageDimension * (ImageDimension+1), 0.0),
+ grad_mask(ImageDimension * (ImageDimension+1), 0.0) {}
+ };
+
+ std::vector<ThreadData> m_ThreadData;
+
+ // Vector of accumulated data (difference, gradient of affine transform, etc)
+ double m_MetricValue;
+
+ // Gradient
+ TransformPointer m_MetricGradient;
+};
+
+
+
+
+
+
+
+
+
+/** \class MultiImageOpticalFlowImageFilter
+ * \brief Warps an image using an input deformation field (for LDDMM)
+ *
+ * This filter efficiently computes the optical flow field between a
+ * set of image pairs, given a transformation phi. This filter is the
+ * workhorse of deformable and affine rigid registration algorithms that
+ * use the mean squared difference metric. Given a set of fixed images F_i
+ * and moving images M_i, it computes
+ *
+ * v(x) = Sum_i w_i \[ F_i(x) - M_i(Phi(x)) ] \Grad M_i (Phi(x))
+ *
+ * The efficiency of this filter comes from combining the interpolation of
+ * all the M and GradM terms in one loop, so that all possible computations
+ * are reused
+ *
+ * The fixed and moving images must be passed in to the filter in the form
+ * of VectorImages of size K and (VDim+K), respectively - i.e., the moving
+ * images and their gradients are packed together.
+ *
+ * The output should be an image of CovariantVector type
+ *
+ * \warning This filter assumes that the input type, output type
+ * and deformation field type all have the same number of dimensions.
+ *
+ */
+#ifdef SHAHAHA
+template <class TInputImage, class TOutputImage, class TDeformationField = TOutputImage>
+class ITK_EXPORT MultiImageOpticalFlowImageFilter :
+ public ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef MultiImageOpticalFlowImageFilter Self;
+ typedef ImageToImageFilter<TInputImage,TOutputImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self)
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( MultiImageOpticalFlowImageFilter, ImageToImageFilter )
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TOutputImage::ImageDimension );
+
+ /** Typedef to describe the output image region type. */
+ typedef typename TInputImage::RegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef TInputImage InputImageType;
+ typedef typename TInputImage::PixelType InputPixelType;
+ typedef typename TInputImage::InternalPixelType InputComponentType;
+ typedef TOutputImage OutputImageType;
+ typedef typename OutputImageType::PixelType OutputPixelType;
+ typedef typename OutputPixelType::ComponentType OutputComponentType;
+ typedef typename OutputImageType::IndexType IndexType;
+ typedef typename OutputImageType::IndexValueType IndexValueType;
+ typedef typename OutputImageType::SizeType SizeType;
+ typedef typename OutputImageType::SpacingType SpacingType;
+ typedef typename OutputImageType::DirectionType DirectionType;
+
+ typedef itk::MatrixOffsetTransformBase<double, ImageDimension, ImageDimension> TransformType;
+
+ /** Weight vector */
+ typedef vnl_vector<float> WeightVectorType;
+
+ /** typedef for base image type at the current ImageDimension */
+ typedef ImageBase<itkGetStaticConstMacro(ImageDimension)> ImageBaseType;
+
+ /** Deformation field typedef support. */
+ typedef TDeformationField DeformationFieldType;
+ typedef typename DeformationFieldType::Pointer DeformationFieldPointer;
+ typedef typename DeformationFieldType::PixelType DisplacementType;
+
+ /** Set the fixed image(s) */
+ void SetFixedImage(InputImageType *fixed)
+ { this->ProcessObject::SetInput("Primary", fixed); }
+
+ /** Set the moving image(s) and their gradients */
+ void SetMovingImageAndGradient(InputImageType *moving)
+ { this->ProcessObject::SetInput("moving", moving); }
+
+ /** Set the weight vector */
+ itkSetMacro(Weights, WeightVectorType)
+ itkGetConstMacro(Weights, WeightVectorType)
+
+ /** Set the deformation field. */
+ void SetDeformationField(DeformationFieldType *field)
+ { this->ProcessObject::SetInput("deformation", field); }
+
+ /** Set the affine transform - currently mutually exclusive with the deformation */
+ void SetLinearTransform(TransformType *transform);
+
+ /** Set constant scaling factor for the deformation field */
+ itkSetMacro(DeformationScaling, float)
+ itkGetConstMacro(DeformationScaling, float)
+
+ /** Get the total energy of optical flow - only after Update has been called */
+ itkGetConstMacro(TotalEnergy, double)
+
+ /** This filter produces an image which is a different
+ * size than its input image. As such, it needs to provide an
+ * implemenation for GenerateOutputInformation() which set
+ * the output information according the OutputSpacing, OutputOrigin
+ * and the deformation field's LargestPossibleRegion. */
+ virtual void GenerateOutputInformation();
+
+ /** It is difficult to compute in advance the input image region
+ * required to compute the requested output region. Thus the safest
+ * thing to do is to request for the whole input image.
+ *
+ * For the deformation field, the input requested region
+ * set to be the same as that of the output requested region. */
+ virtual void GenerateInputRequestedRegion();
+
+ /** This method is used to set the state of the filter before
+ * multi-threading. */
+ virtual void BeforeThreadedGenerateData();
+
+ /** This method is used to set the state of the filter after
+ * multi-threading. */
+ virtual void AfterThreadedGenerateData();
+
+protected:
+ MultiImageOpticalFlowImageFilter();
+ ~MultiImageOpticalFlowImageFilter() {}
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId );
+
+ void VerifyInputInformation() {}
+
+ // Object to assist specializaiton
+ struct DispatchBase {};
+ template <unsigned int VDim> struct Dispatch : public DispatchBase {};
+
+ /** Fast interpolation method */
+ double OpticalFlowFastInterpolate(const Dispatch<3> &dispatch,
+ float *cix,
+ const InputComponentType *fixed_ptr,
+ const InputComponentType *moving_ptr,
+ OutputPixelType &outVector,
+ int *movSize,
+ int nComp,
+ const InputComponentType *def_value);
+
+ // Dummy implementation
+ double OpticalFlowFastInterpolate(const DispatchBase &base,
+ float *cix,
+ const InputComponentType *fixed_ptr,
+ const InputComponentType *moving_ptr,
+ OutputPixelType &outVector,
+ int *movSize,
+ int nComp,
+ const InputComponentType *def_value)
+ { return 0.0; }
+
+private:
+ MultiImageOpticalFlowImageFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ // Scaling for the deformation field
+ float m_DeformationScaling;
+
+ // Weight vector
+ WeightVectorType m_Weights;
+
+ // Linear transform
+ typename TransformType::Pointer m_Transform;
+
+ // Total energy - Sum |I_k - J_k|^2
+ double m_TotalEnergy;
+ std::vector<double> m_TotalEnergyPerThread;
+
+ // Gradient of the affine transform - computed when the deformation is null
+ typename TransformType::Pointer m_GradTransform;
+ std::vector<typename TransformType::Pointer> m_GradTransformPerThread;
+};
+#endif
+
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "MultiImageSimpleWarpImageFilter.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/MultiImageSimpleWarpImageFilter.txx b/Submodules/greedy/src/MultiImageSimpleWarpImageFilter.txx
new file mode 100644
index 0000000..f386835
--- /dev/null
+++ b/Submodules/greedy/src/MultiImageSimpleWarpImageFilter.txx
@@ -0,0 +1,1429 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: SimpleWarpImageFilter.txx,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:10 $
+ Version: $Revision: 1.34 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __MultiImageSimpleWarpImageFilter_txx
+#define __MultiImageSimpleWarpImageFilter_txx
+#include "MultiImageSimpleWarpImageFilter.h"
+
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkNumericTraits.h"
+#include "itkProgressReporter.h"
+#include "itkContinuousIndex.h"
+#include "vnl/vnl_math.h"
+#include "lddmm_data.h"
+#include "FastLinearInterpolator.h"
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::SetDefaultPyramidFactors(int n_levels)
+{
+ for(int i = n_levels-1; i>=0; --i)
+ m_PyramidFactors.push_back(1 << i);
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::SetPyramidFactors(const PyramidFactorsType &factors)
+{
+ m_PyramidFactors = factors;
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::AddImagePair(MultiComponentImageType *fixed, MultiComponentImageType *moving, double weight)
+{
+ // Collect the weights
+ for(int i = 0; i < fixed->GetNumberOfComponentsPerPixel(); i++)
+ m_Weights.push_back(weight);
+
+ // Store the images
+ m_Fixed.push_back(fixed);
+ m_Moving.push_back(moving);
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::PlaceIntoComposite(FloatImageType *source, MultiComponentImageType *target, int offset)
+{
+ // We do this using a loop - no threading
+ TFloat *src_ptr = source->GetPixelContainer()->GetBufferPointer();
+ TFloat *trg_ptr = target->GetPixelContainer()->GetBufferPointer() + offset;
+
+ int trg_comp = target->GetNumberOfComponentsPerPixel();
+
+ int n_voxels = source->GetPixelContainer()->Size();
+ TFloat *trg_end = trg_ptr + n_voxels * target->GetNumberOfComponentsPerPixel();
+
+ while(trg_ptr < trg_end)
+ {
+ *trg_ptr = *src_ptr++;
+ trg_ptr += trg_comp;
+ }
+}
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::PlaceIntoComposite(VectorImageType *source, MultiComponentImageType *target, int offset)
+{
+ // We do this using a loop - no threading
+ VectorType *src_ptr = source->GetPixelContainer()->GetBufferPointer();
+ TFloat *trg_ptr = target->GetPixelContainer()->GetBufferPointer() + offset;
+
+ int trg_skip = target->GetNumberOfComponentsPerPixel() - VDim;
+
+ int n_voxels = source->GetPixelContainer()->Size();
+ TFloat *trg_end = trg_ptr + n_voxels * target->GetNumberOfComponentsPerPixel();
+
+ while(trg_ptr < trg_end)
+ {
+ const VectorType &vsrc = *src_ptr++;
+ for(int k = 0; k < VDim; k++)
+ *trg_ptr++ = vsrc[k];
+ trg_ptr += trg_skip;
+ }
+}
+
+
+#include "itkVectorIndexSelectionCastImageFilter.h"
+#include "itkImageFileWriter.h"
+
+template <class TFloat, unsigned int VDim>
+void
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::BuildCompositeImages()
+{
+ typedef LDDMMData<TFloat, VDim> LDDMMType;
+
+ // Offsets into the composite images
+ int off_fixed = 0, off_moving = 0;
+
+ // Set up the composite images
+ m_FixedComposite.resize(m_PyramidFactors.size());
+ m_MovingComposite.resize(m_PyramidFactors.size());
+
+ // Repeat for each of the input images
+ for(int j = 0; j < m_Fixed.size(); j++)
+ {
+ // Repeat for each component
+ for(int k = 0; k < m_Fixed[j]->GetNumberOfComponentsPerPixel(); k++)
+ {
+ // Extract the k-th image component from fixed and moving images
+ typedef itk::VectorIndexSelectionCastImageFilter<MultiComponentImageType, FloatImageType> ExtractType;
+ typename ExtractType::Pointer fltExtractFixed, fltExtractMoving;
+
+ fltExtractFixed = ExtractType::New();
+ fltExtractFixed->SetInput(m_Fixed[j]);
+ fltExtractFixed->SetIndex(k);
+ fltExtractFixed->Update();
+
+ fltExtractMoving = ExtractType::New();
+ fltExtractMoving->SetInput(m_Moving[j]);
+ fltExtractMoving->SetIndex(k);
+ fltExtractMoving->Update();
+
+ // Compute the pyramid for this component
+ for(int i = 0; i < m_PyramidFactors.size(); i++)
+ {
+ // Downsample the image to the right pyramid level
+ typename FloatImageType::Pointer lFixed, lMoving;
+ if (m_PyramidFactors[i] == 1)
+ {
+ lFixed = fltExtractFixed->GetOutput();
+ lMoving = fltExtractMoving->GetOutput();
+ }
+ else
+ {
+ lFixed = FloatImageType::New();
+ lMoving = FloatImageType::New();
+ LDDMMType::img_downsample(fltExtractFixed->GetOutput(), lFixed, m_PyramidFactors[i]);
+ LDDMMType::img_downsample(fltExtractMoving->GetOutput(), lMoving, m_PyramidFactors[i]);
+ }
+
+ // Compute the gradient of the moving image
+ typename VectorImageType::Pointer gradMoving = VectorImageType::New();
+ LDDMMType::alloc_vimg(gradMoving, lMoving);
+ LDDMMType::image_gradient(lMoving, gradMoving);
+
+ // Allocate the composite images if they have not been allocated
+ if(j == 0 && k == 0)
+ {
+ m_FixedComposite[i] = MultiComponentImageType::New();
+ m_FixedComposite[i]->CopyInformation(lFixed);
+ m_FixedComposite[i]->SetNumberOfComponentsPerPixel(m_Weights.size());
+ m_FixedComposite[i]->SetRegions(lFixed->GetBufferedRegion());
+ m_FixedComposite[i]->Allocate();
+
+ m_MovingComposite[i] = MultiComponentImageType::New();
+ m_MovingComposite[i]->CopyInformation(lMoving);
+ m_MovingComposite[i]->SetNumberOfComponentsPerPixel(m_Weights.size() * (1 + VDim));
+ m_MovingComposite[i]->SetRegions(lMoving->GetBufferedRegion());
+ m_MovingComposite[i]->Allocate();
+ }
+
+ // Pack the data into the fixed and moving composite images
+ this->PlaceIntoComposite(lFixed, m_FixedComposite[i], off_fixed);
+ this->PlaceIntoComposite(lMoving, m_MovingComposite[i], off_moving);
+ this->PlaceIntoComposite(gradMoving, m_MovingComposite[i], off_moving + 1);
+ }
+
+ // Update the offsets
+ off_fixed++;
+ off_moving += (1 + VDim);
+ }
+ }
+}
+
+template <class TFloat, unsigned int VDim>
+typename MultiImageOpticalFlowHelper<TFloat, VDim>::ImageBaseType *
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::GetMovingReferenceSpace(int level)
+{
+ return m_MovingComposite[level];
+}
+
+template <class TFloat, unsigned int VDim>
+typename MultiImageOpticalFlowHelper<TFloat, VDim>::ImageBaseType *
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::GetReferenceSpace(int level)
+{
+ return m_FixedComposite[level];
+}
+
+template <class TFloat, unsigned int VDim>
+double
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeOpticalFlowField(int level, VectorImageType *def, VectorImageType *result, double result_scaling)
+{
+ typedef itk::MultiImageOpticalFlowWarpTraits<
+ MultiComponentImageType, VectorImageType, VectorImageType> TraitsType;
+ typedef itk::MultiImageOpticalFlowImageFilter<
+ MultiComponentImageType, VectorImageType, TraitsType> FilterType;
+
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for(int i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i] * result_scaling;
+
+ // Run the filter
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImageAndGradient(m_MovingComposite[level]);
+ // filter->SetDeformationField(def);
+ filter->SetTransform(def);
+ filter->SetWeights(wscaled);
+ filter->GraftOutput(result);
+ filter->Update();
+
+ // Get the total energy
+ return filter->GetSummaryResult()[0];
+}
+
+template <class TFloat, unsigned int VDim>
+double
+MultiImageOpticalFlowHelper<TFloat, VDim>
+::ComputeAffineMatchAndGradient(
+ int level, LinearTransformType *tran,
+ LinearTransformType *grad)
+{
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for(int i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i];
+
+ // Use finite differences
+ typedef itk::MultiImageAffineMSDMetricFilter<MultiComponentImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Run the filter
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImageAndGradient(m_MovingComposite[level]);
+ filter->SetTransform(tran);
+ filter->SetWeights(wscaled);
+ filter->SetComputeGradient(grad != NULL);
+ filter->Update();
+
+ // Process the results
+ if(grad)
+ {
+ grad->SetMatrix(filter->GetMetricGradient()->GetMatrix());
+ grad->SetOffset(filter->GetMetricGradient()->GetOffset());
+ }
+
+ return filter->GetMetricValue();
+
+ /*
+ // Scale the weights by epsilon
+ vnl_vector<float> wscaled(m_Weights.size());
+ for(int i = 0; i < wscaled.size(); i++)
+ wscaled[i] = m_Weights[i];
+
+ // Use finite differences
+ typedef itk::MultiImageAffineMSDMetricFilter<MultiComponentImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Run the filter
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImageAndGradient(m_MovingComposite[level]);
+ filter->SetTransform(tran);
+ filter->SetWeights(wscaled);
+ filter->SetComputeGradient(false);
+ filter->Update();
+
+ double f0 = filter->GetMetricValue();
+
+ // Compute finite differences
+ if(grad)
+ {
+ vnl_vector<float> x(12), gradf(12);
+ itk::flatten_affine_transform(tran, x.data_block());
+ for(int k = 0; k < 12; k++)
+ {
+ double fk[2], eps = 1.0e-3;
+ for(int q = 0; q < 2; q++)
+ {
+ typename LinearTransformType::Pointer tranq = LinearTransformType::New();
+ vnl_vector<float> xq = x;
+ xq[k] += (q == 0 ? -1 : 1) * eps;
+ itk::unflatten_affine_transform(xq.data_block(), tranq.GetPointer());
+
+ filter = FilterType::New();
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImageAndGradient(m_MovingComposite[level]);
+ filter->SetTransform(tranq);
+ filter->SetWeights(wscaled);
+ filter->SetComputeGradient(false);
+ filter->Update();
+
+ fk[q] = filter->GetMetricValue();
+ }
+ gradf[k] = (fk[1]-fk[0]) / (2.0 * eps);
+ }
+ itk::unflatten_affine_transform(gradf.data_block(), grad);
+ }
+
+ return f0;
+
+ */
+
+ /*
+ if(grad)
+ {
+ typedef itk::MultiImageOpticalFlowAffineGradientTraits<
+ MultiComponentImageType, VectorImageType> TraitsType;
+ typedef itk::MultiImageOpticalFlowImageFilter<
+ MultiComponentImageType, VectorImageType, TraitsType> FilterType;
+
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Run the filter
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImageAndGradient(m_MovingComposite[level]);
+ filter->SetTransform(tran);
+ filter->SetWeights(wscaled);
+
+ // TODO: stop the filter from allocating a image pointlessly!
+ // filter->GraftOutput(result);
+ filter->Update();
+
+ // Process the results, scaling by -2
+ itk::unflatten_affine_transform(filter->GetSummaryResult().data_block()+1, grad, -2.0);
+
+ // Get the total energy
+ return filter->GetSummaryResult()[0];
+ }
+ else
+ {
+ typedef itk::MultiImageOpticalFlowAffineObjectiveTraits<
+ MultiComponentImageType, VectorImageType> TraitsType;
+ typedef itk::MultiImageOpticalFlowImageFilter<
+ MultiComponentImageType, VectorImageType, TraitsType> FilterType;
+
+ typename FilterType::Pointer filter = FilterType::New();
+
+ // Run the filter
+ filter->SetFixedImage(m_FixedComposite[level]);
+ filter->SetMovingImageAndGradient(m_MovingComposite[level]);
+ filter->SetTransform(tran);
+ filter->SetWeights(wscaled);
+ // TODO: stop the filter from allocating a image pointlessly!
+
+ // filter->GraftOutput(result);
+ filter->Update();
+
+ // Process the results
+ return filter->GetSummaryResult()[0]; // / filter->GetSummaryResult()[1];
+ }
+ */
+}
+
+
+
+
+
+namespace itk
+{
+
+/**
+ * Default constructor.
+ */
+
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::MultiImageOpticalFlowImageFilter()
+{
+ // Setup default values
+ // m_DeformationScaling = 1.0;
+}
+
+/**
+ * Standard PrintSelf method.
+ */
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+void
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::PrintSelf(std::ostream& os, Indent indent) const
+{
+ Superclass::PrintSelf(os, indent);
+}
+
+/**
+ * Setup state of filter before multi-threading.
+ * InterpolatorType::SetInputImage is not thread-safe and hence
+ * has to be setup before ThreadedGenerateData
+ */
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+void
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::BeforeThreadedGenerateData()
+{
+ // Create the prototype results vector
+ InputImageType *moving = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("moving"));
+ int kMoving = moving->GetNumberOfComponentsPerPixel();
+ int nResult = TTransformTraits::GetResultAccumSize(kMoving);
+ m_SummaryResult = SummaryType(nResult, 0.0);
+
+ // Clear the energy per thread array
+ m_SummaryResultPerThread =
+ std::vector<SummaryType>(this->GetNumberOfThreads(), m_SummaryResult);
+}
+
+/**
+ * Setup state of filter after multi-threading.
+ */
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+void
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::AfterThreadedGenerateData()
+{
+ for(int i = 0; i < m_SummaryResultPerThread.size(); i++)
+ m_SummaryResult += m_SummaryResultPerThread[i];
+}
+
+
+/**
+ Trilinear interpolation - code borrowed from http://tog.acm.org/resources/GraphicsGems/gemsiv/trilerp.c
+ */
+#define INRANGE(X, Y, Z) ((X) >= 0 && (X) < xsize && (Y) >= 0 && (Y) < ysize && (Z) >= 0 && (Z) < zsize)
+#define DENS(X, Y, Z, ptr, comp) (ptr + comp * ((X)+xsize*((Y)+ysize*(Z))))
+
+template <class TFloat>
+inline TFloat LERP(TFloat a, TFloat l, TFloat h)
+{
+ return l+((h-l)*a);
+}
+
+/*
+template <class TInputImage, class TOutputImage, class TDeformationField>
+double
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TDeformationField>
+::OpticalFlowFastInterpolate(const Dispatch<3> &,
+ float *cix,
+ const InputComponentType *fixed_ptr,
+ const InputComponentType *moving_ptr,
+ OutputPixelType &outVector,
+ int *movSize,
+ int nComp,
+ const InputComponentType *def_value)
+{
+ int x0, y0, z0, x1, y1, z1;
+ const InputComponentType *dp, *d000, *d001, *d010, *d011, *d100, *d101, *d110, *d111;
+
+ double fx, fy, fz;
+ double dx00, dx01, dx10, dx11, dxy0, dxy1, dxyz;
+
+ int xsize = movSize[0];
+ int ysize = movSize[1];
+ int zsize = movSize[2];
+
+ x0 = floor(cix[0]); fx = cix[0] - x0;
+ y0 = floor(cix[1]); fy = cix[1] - y0;
+ z0 = floor(cix[2]); fz = cix[2] - z0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+ z1 = z0 + 1;
+
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize &&
+ z0 >= 0 && z1 < zsize)
+ {
+ dp = DENS(x0, y0, z0, moving_ptr, nComp);
+ d000 = dp;
+ d100 = dp+nComp;
+ dp += xsize*nComp;
+ d010 = dp;
+ d110 = dp+nComp;
+ dp += xsize*ysize*nComp;
+ d011 = dp;
+ d111 = dp+nComp;
+ dp -= xsize*nComp;
+ d001 = dp;
+ d101 = dp+nComp;
+ }
+ else
+ {
+ d000 = INRANGE(x0, y0, z0) ? DENS(x0, y0, z0, moving_ptr, nComp) : def_value;
+ d001 = INRANGE(x0, y0, z1) ? DENS(x0, y0, z1, moving_ptr, nComp) : def_value;
+ d010 = INRANGE(x0, y1, z0) ? DENS(x0, y1, z0, moving_ptr, nComp) : def_value;
+ d011 = INRANGE(x0, y1, z1) ? DENS(x0, y1, z1, moving_ptr, nComp) : def_value;
+ d100 = INRANGE(x1, y0, z0) ? DENS(x1, y0, z0, moving_ptr, nComp) : def_value;
+ d101 = INRANGE(x1, y0, z1) ? DENS(x1, y0, z1, moving_ptr, nComp) : def_value;
+ d110 = INRANGE(x1, y1, z0) ? DENS(x1, y1, z0, moving_ptr, nComp) : def_value;
+ d111 = INRANGE(x1, y1, z1) ? DENS(x1, y1, z1, moving_ptr, nComp) : def_value;
+ }
+
+ // Output vector
+ double Vx = 0.0, Vy = 0.0, Vz = 0.0;
+
+ // Output value
+ double Tval = 0.0;
+
+ // Weight array
+ float *weight = m_Weights.data_block();
+
+ // Interpolate each component
+ for(int iComp = 0; iComp < nComp; iComp+=4)
+ {
+ double M, Mx, My, Mz;
+
+ // TODO: parallelize this using SSD
+
+ // Interpolate first component
+ dx00 = LERP(fx, *d000++, *d100++);
+ dx01 = LERP(fx, *d001++, *d101++);
+ dx10 = LERP(fx, *d010++, *d110++);
+ dx11 = LERP(fx, *d011++, *d111++);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ M = LERP(fz, dxy0, dxy1);
+
+ dx00 = LERP(fx, *d000++, *d100++);
+ dx01 = LERP(fx, *d001++, *d101++);
+ dx10 = LERP(fx, *d010++, *d110++);
+ dx11 = LERP(fx, *d011++, *d111++);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ Mx = LERP(fz, dxy0, dxy1);
+
+ dx00 = LERP(fx, *d000++, *d100++);
+ dx01 = LERP(fx, *d001++, *d101++);
+ dx10 = LERP(fx, *d010++, *d110++);
+ dx11 = LERP(fx, *d011++, *d111++);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ My = LERP(fz, dxy0, dxy1);
+
+ dx00 = LERP(fx, *d000++, *d100++);
+ dx01 = LERP(fx, *d001++, *d101++);
+ dx10 = LERP(fx, *d010++, *d110++);
+ dx11 = LERP(fx, *d011++, *d111++);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ Mz = LERP(fz, dxy0, dxy1);
+
+ // Compute the difference
+ double del = (*fixed_ptr++) - M;
+ double delw = (*weight++) * del;
+ Vx += delw * Mx;
+ Vy += delw * My;
+ Vz += delw * Mz;
+ Tval += delw * del;
+ }
+
+ // Store the output
+ outVector[0] = Vx;
+ outVector[1] = Vy;
+ outVector[2] = Vz;
+
+ // What to return?
+ return Tval;
+}
+*/
+
+/*
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+bool
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::OpticalFlowFastInterpolateWithMask(
+ const Dispatch<3> &,
+ const InputComponentType *moving_ptr,
+ int nComp, int stride, int *movSize,
+ const InputComponentType *def_value,
+ float *cix,
+ InputComponentType *out, float &outMask)
+{
+ int x0, y0, z0, x1, y1, z1;
+ const InputComponentType *dp, *d000, *d001, *d010, *d011, *d100, *d101, *d110, *d111;
+
+ double fx, fy, fz;
+ double dx00, dx01, dx10, dx11, dxy0, dxy1, dxyz;
+
+ int xsize = movSize[0];
+ int ysize = movSize[1];
+ int zsize = movSize[2];
+
+ x0 = floor(cix[0]); fx = cix[0] - x0;
+ y0 = floor(cix[1]); fy = cix[1] - y0;
+ z0 = floor(cix[2]); fz = cix[2] - z0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+ z1 = z0 + 1;
+
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize &&
+ z0 >= 0 && z1 < zsize)
+ {
+ // The sample point is completely inside
+ dp = DENS(x0, y0, z0, moving_ptr, nComp);
+ d000 = dp;
+ d100 = dp+nComp;
+ dp += xsize*nComp;
+ d010 = dp;
+ d110 = dp+nComp;
+ dp += xsize*ysize*nComp;
+ d011 = dp;
+ d111 = dp+nComp;
+ dp -= xsize*nComp;
+ d001 = dp;
+ d101 = dp+nComp;
+
+ // The mask is one
+ outMask = 1.0;
+ }
+ else if (x0 >= -1 && x1 <= xsize &&
+ y0 >= -1 && y1 <= ysize &&
+ z0 >= -1 && z1 <= zsize)
+ {
+ // The sample point is on the border region
+ d000 = INRANGE(x0, y0, z0) ? DENS(x0, y0, z0, moving_ptr, nComp) : def_value;
+ d001 = INRANGE(x0, y0, z1) ? DENS(x0, y0, z1, moving_ptr, nComp) : def_value;
+ d010 = INRANGE(x0, y1, z0) ? DENS(x0, y1, z0, moving_ptr, nComp) : def_value;
+ d011 = INRANGE(x0, y1, z1) ? DENS(x0, y1, z1, moving_ptr, nComp) : def_value;
+ d100 = INRANGE(x1, y0, z0) ? DENS(x1, y0, z0, moving_ptr, nComp) : def_value;
+ d101 = INRANGE(x1, y0, z1) ? DENS(x1, y0, z1, moving_ptr, nComp) : def_value;
+ d110 = INRANGE(x1, y1, z0) ? DENS(x1, y1, z0, moving_ptr, nComp) : def_value;
+ d111 = INRANGE(x1, y1, z1) ? DENS(x1, y1, z1, moving_ptr, nComp) : def_value;
+
+ // Compute the mask value - TODO rewrite better
+ dx00 = LERP(fx, d000 == def_value ? 0.0 : 1.0, d100 == def_value ? 0.0 : 1.0);
+ dx01 = LERP(fx, d001 == def_value ? 0.0 : 1.0, d101 == def_value ? 0.0 : 1.0);
+ dx10 = LERP(fx, d010 == def_value ? 0.0 : 1.0, d110 == def_value ? 0.0 : 1.0);
+ dx11 = LERP(fx, d011 == def_value ? 0.0 : 1.0, d111 == def_value ? 0.0 : 1.0);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ outMask = LERP(fz, dxy0, dxy1);
+ }
+ else
+ {
+ // The sample point is outside
+ for(int iComp = 0; iComp < nComp; iComp+=stride)
+ *(out++) = def_value[iComp];
+
+ // The mask is zero
+ outMask = 0.0;
+ return;
+ }
+
+ // Interpolate each component
+ for(int iComp = 0; iComp < nComp; iComp+=stride,
+ d000+=stride, d001+=stride, d010+=stride, d011+=stride,
+ d100+=stride, d101+=stride, d110+=stride, d111+=stride)
+ {
+ // Interpolate first component
+ dx00 = LERP(fx, *d000, *d100);
+ dx01 = LERP(fx, *d001, *d101);
+ dx10 = LERP(fx, *d010, *d110);
+ dx11 = LERP(fx, *d011, *d111);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ *(out++) = LERP(fz, dxy0, dxy1);
+ }
+}
+
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+void
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::OpticalFlowFastInterpolate(const Dispatch<3> &,
+ const InputComponentType *moving_ptr,
+ int nComp, int stride, int *movSize,
+ const InputComponentType *def_value,
+ float *cix,
+ InputComponentType *out, float &mask_val)
+{
+ int x0, y0, z0, x1, y1, z1;
+ const InputComponentType *dp, *d000, *d001, *d010, *d011, *d100, *d101, *d110, *d111;
+
+ double fx, fy, fz;
+ double dx00, dx01, dx10, dx11, dxy0, dxy1, dxyz;
+
+ int xsize = movSize[0];
+ int ysize = movSize[1];
+ int zsize = movSize[2];
+
+ x0 = floor(cix[0]); fx = cix[0] - x0;
+ y0 = floor(cix[1]); fy = cix[1] - y0;
+ z0 = floor(cix[2]); fz = cix[2] - z0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+ z1 = z0 + 1;
+
+ // Fully inside border region?
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize &&
+ z0 >= 0 && z1 < zsize)
+ {
+ dp = DENS(x0, y0, z0, moving_ptr, nComp);
+ d000 = dp;
+ d100 = dp+nComp;
+ dp += xsize*nComp;
+ d010 = dp;
+ d110 = dp+nComp;
+ dp += xsize*ysize*nComp;
+ d011 = dp;
+ d111 = dp+nComp;
+ dp -= xsize*nComp;
+ d001 = dp;
+ d101 = dp+nComp;
+ }
+
+ // Partially inside border region
+ else if(x0 >= -1 && x1 <= xsize &&
+ y0 >= -1 && y1 <= ysize &&
+ z0 >= -1 && z1 <= zsize)
+ {
+ d000 = INRANGE(x0, y0, z0) ? DENS(x0, y0, z0, moving_ptr, nComp) : def_value;
+ d001 = INRANGE(x0, y0, z1) ? DENS(x0, y0, z1, moving_ptr, nComp) : def_value;
+ d010 = INRANGE(x0, y1, z0) ? DENS(x0, y1, z0, moving_ptr, nComp) : def_value;
+ d011 = INRANGE(x0, y1, z1) ? DENS(x0, y1, z1, moving_ptr, nComp) : def_value;
+ d100 = INRANGE(x1, y0, z0) ? DENS(x1, y0, z0, moving_ptr, nComp) : def_value;
+ d101 = INRANGE(x1, y0, z1) ? DENS(x1, y0, z1, moving_ptr, nComp) : def_value;
+ d110 = INRANGE(x1, y1, z0) ? DENS(x1, y1, z0, moving_ptr, nComp) : def_value;
+ d111 = INRANGE(x1, y1, z1) ? DENS(x1, y1, z1, moving_ptr, nComp) : def_value;
+
+ }
+
+ // Outside border region
+ else
+ {
+ for(int iComp = 0; iComp < nComp; iComp+=stride)
+ *(out++) = def_value[iComp]
+ }
+
+ // Interpolate each component
+ for(int iComp = 0; iComp < nComp; iComp+=stride,
+ d000+=stride, d001+=stride, d010+=stride, d011+=stride,
+ d100+=stride, d101+=stride, d110+=stride, d111+=stride)
+ {
+ // Interpolate first component
+ dx00 = LERP(fx, *d000, *d100);
+ dx01 = LERP(fx, *d001, *d101);
+ dx10 = LERP(fx, *d010, *d110);
+ dx11 = LERP(fx, *d011, *d111);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ *(out++) = LERP(fz, dxy0, dxy1);
+ }
+
+ return true;
+}
+*/
+
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+bool
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::OpticalFlowFastInterpolate(const Dispatch<3> &,
+ const InputComponentType *moving_ptr,
+ int nComp, int stride, int *movSize,
+ const InputComponentType *def_value,
+ float *cix,
+ InputComponentType *out)
+{
+ int x0, y0, z0, x1, y1, z1;
+ const InputComponentType *dp, *d000, *d001, *d010, *d011, *d100, *d101, *d110, *d111;
+
+ double fx, fy, fz;
+ double dx00, dx01, dx10, dx11, dxy0, dxy1, dxyz;
+
+ int xsize = movSize[0];
+ int ysize = movSize[1];
+ int zsize = movSize[2];
+
+ x0 = floor(cix[0]); fx = cix[0] - x0;
+ y0 = floor(cix[1]); fy = cix[1] - y0;
+ z0 = floor(cix[2]); fz = cix[2] - z0;
+
+ x1 = x0 + 1;
+ y1 = y0 + 1;
+ z1 = z0 + 1;
+
+ if (x0 >= 0 && x1 < xsize &&
+ y0 >= 0 && y1 < ysize &&
+ z0 >= 0 && z1 < zsize)
+ {
+ dp = DENS(x0, y0, z0, moving_ptr, nComp);
+ d000 = dp;
+ d100 = dp+nComp;
+ dp += xsize*nComp;
+ d010 = dp;
+ d110 = dp+nComp;
+ dp += xsize*ysize*nComp;
+ d011 = dp;
+ d111 = dp+nComp;
+ dp -= xsize*nComp;
+ d001 = dp;
+ d101 = dp+nComp;
+ }
+ else if(def_value)
+ {
+ d000 = INRANGE(x0, y0, z0) ? DENS(x0, y0, z0, moving_ptr, nComp) : def_value;
+ d001 = INRANGE(x0, y0, z1) ? DENS(x0, y0, z1, moving_ptr, nComp) : def_value;
+ d010 = INRANGE(x0, y1, z0) ? DENS(x0, y1, z0, moving_ptr, nComp) : def_value;
+ d011 = INRANGE(x0, y1, z1) ? DENS(x0, y1, z1, moving_ptr, nComp) : def_value;
+ d100 = INRANGE(x1, y0, z0) ? DENS(x1, y0, z0, moving_ptr, nComp) : def_value;
+ d101 = INRANGE(x1, y0, z1) ? DENS(x1, y0, z1, moving_ptr, nComp) : def_value;
+ d110 = INRANGE(x1, y1, z0) ? DENS(x1, y1, z0, moving_ptr, nComp) : def_value;
+ d111 = INRANGE(x1, y1, z1) ? DENS(x1, y1, z1, moving_ptr, nComp) : def_value;
+ }
+ else
+ {
+ return false;
+ }
+
+ // Interpolate each component
+ for(int iComp = 0; iComp < nComp; iComp+=stride)
+ {
+ // Interpolate first component
+ dx00 = LERP(fx, *d000, *d100);
+ dx01 = LERP(fx, *d001, *d101);
+ dx10 = LERP(fx, *d010, *d110);
+ dx11 = LERP(fx, *d011, *d111);
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+ *(out++) = LERP(fz, dxy0, dxy1);
+
+ // TODO: unnecessary on last pass!
+ d000 += stride; d001 += stride; d010 += stride; d011 += stride;
+ d100 += stride; d101 += stride; d110 += stride; d111 += stride;
+ }
+
+ return true;
+}
+
+template <typename TImage>
+class ImageRegionConstIteratorWithIndexOverride
+ : public itk::ImageRegionConstIteratorWithIndex<TImage>
+{
+public:
+ typedef ImageRegionConstIteratorWithIndexOverride<TImage> Self;
+ typedef itk::ImageRegionConstIteratorWithIndex<TImage> Superclass;
+ typedef typename Superclass::RegionType RegionType;
+ typedef typename Superclass::InternalPixelType InternalPixelType;
+
+ ImageRegionConstIteratorWithIndexOverride(TImage *im, const RegionType ®ion)
+ : Superclass(im, region) {}
+
+ const InternalPixelType *GetPosition() { return this->m_Position; }
+ const InternalPixelType *GetBeginPosition() { return this->m_Begin; }
+};
+
+
+/**
+ * Compute the output for the region specified by outputRegionForThread.
+ */
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+void
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId )
+{
+ // Get the pointers to the input and output images
+ InputImageType *fixed = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("Primary"));
+ InputImageType *moving = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("moving"));
+ OutputImageType *out = this->GetOutput();
+
+ // Get the number of components
+ int kFixed = fixed->GetNumberOfComponentsPerPixel();
+ int kMoving = moving->GetNumberOfComponentsPerPixel();
+
+ // Iterate over the deformation field and the output image. In reality, we don't
+ // need to waste so much time on iteration, so we use a specialized iterator here
+ typedef ImageRegionConstIteratorWithIndexOverride<OutputImageType> OutputIter;
+
+ // Location of the lookup
+ vnl_vector_fixed<float, ImageDimension> cix;
+
+ // Pointer to the fixed image data
+ const typename InputImageType::InternalPixelType *bFix = fixed->GetBufferPointer();
+ const typename InputImageType::InternalPixelType *bMov = moving->GetBufferPointer();
+ typename OutputImageType::InternalPixelType *bOut = out->GetBufferPointer();
+
+ // Pointer to store interpolated moving data
+ vnl_vector<typename InputImageType::InternalPixelType> interp_mov(kMoving);
+ SummaryType &sum_res = m_SummaryResultPerThread[threadId];
+
+ // Get the stride for interpolation (how many moving pixels to skip)
+ int stride = TTransformTraits::GetStride(kMoving);
+
+ // Array of moving image size
+ vnl_vector<int> mov_size(ImageDimension);
+ for(unsigned int j = 0; j < ImageDimension; j++ )
+ mov_size[j] = moving->GetBufferedRegion().GetSize()[j];
+
+ // Array of zeros - default value, if used
+ vnl_vector<InputComponentType> zeros(kMoving, 0.0);
+ InputComponentType *zeroPtr =
+ TTransformTraits::InterpolateOutsideOverlapRegion() ? zeros.data_block() : NULL;
+
+ // Iterate over the fixed space region
+ for(OutputIter it(out, outputRegionForThread); !it.IsAtEnd(); ++it)
+ {
+ // Get the index at the current location
+ const IndexType &idx = it.GetIndex();
+
+ // Get the output pointer at this location
+ typename OutputImageType::InternalPixelType *ptrOut =
+ const_cast<typename OutputImageType::InternalPixelType *>(it.GetPosition());
+
+ // Get the offset into the fixed pointer
+ long offset = ptrOut - bOut;
+
+ // Map to a position at which to interpolate
+ TTransformTraits::TransformIndex(idx, m_Transform, offset, cix.data_block());
+
+ // Perform the interpolation, put the results into interp_mov
+ float mask;
+ this->OpticalFlowFastInterpolateWithMask(
+ Dispatch<ImageDimension>(),
+//<<<<<<< HEAD
+ bMov, kMoving, stride, mov_size.data_block(), zeros.data_block(),
+ cix.data_block(), interp_mov.data_block(), mask);
+//=======
+// bMov, kMoving, stride, mov_size.data_block(), zeroPtr,
+// cix.data_block(), interp_mov.data_block());
+//>>>>>>> 43ef7496f075d647d8e516d0c8c81fc86f04a1ae
+
+ // Perform the calculation of interest on the interpolated data
+ TTransformTraits::PostInterpolate(
+ idx, bFix + offset * kFixed,
+ interp_mov.data_block(), kMoving, m_Weights.data_block(),
+ mask, sum_res.data_block(), *ptrOut);
+ }
+
+}
+
+
+
+
+/**
+ * Compute the output for the region specified by outputRegionForThread.
+ */
+/*
+template <class TImage, class TVectorImage, class TFloat>
+void
+MultiImageOpticalFlowImageFilter<TImage,TVectorImage,TFloat>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId )
+{
+ // Get the pointers to the four images
+ InputImageType *fixed = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("Primary"));
+ InputImageType *moving = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("moving"));
+ DeformationFieldType *def = dynamic_cast<DeformationFieldType *>(this->ProcessObject::GetInput("deformation"));
+ OutputImageType *out = this->GetOutput();
+
+ // Get the number of components
+ int kFixed = fixed->GetNumberOfComponentsPerPixel();
+ int kMoving = moving->GetNumberOfComponentsPerPixel();
+
+ // Iterate over the deformation field and the output image. In reality, we don't
+ // need to waste so much time on iteration, so we use a specialized iterator here
+ typedef ImageRegionConstIteratorWithIndexOverride<OutputImageType> OutputIter;
+
+ // Get the transform parameters if using affine transform
+ vnl_matrix_fixed<float, ImageDimension, ImageDimension> t_M;
+ vnl_vector_fixed<float, ImageDimension> t_b;
+ if(m_Transform)
+ {
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ t_M(i,j) = m_Transform->GetMatrix().GetVnlMatrix()(i,j);
+ }
+ t_b(i) = m_Transform->GetOffset()(i);
+ }
+ }
+ else
+ {
+ t_M.set_identity();
+ t_b.fill(0.0f);
+ }
+
+ // Location of the lookup
+ vnl_vector_fixed<float, ImageDimension> cix;
+
+ // Pointer to the fixed image data
+ const typename InputImageType::InternalPixelType *bFix = fixed->GetBufferPointer();
+ const typename DeformationFieldType::InternalPixelType *bDef = def->GetBufferPointer();
+ typename OutputImageType::InternalPixelType *bOut = out->GetBufferPointer();
+
+ // Array of moving image size
+ vnl_vector<int> mov_size(ImageDimension);
+ for(unsigned int j = 0; j < ImageDimension; j++ )
+ mov_size[j] = moving->GetBufferedRegion().GetSize()[j];
+
+ // Array of zeros - default value
+ vnl_vector<InputComponentType> zeros(kMoving, 0.0);
+
+ // Iterate over the fixed space region
+ for(OutputIter it(out, outputRegionForThread); !it.IsAtEnd(); ++it)
+ {
+ // Get the index at the current location
+ const IndexType &idx = it.GetIndex();
+
+ // Get the output pointer at this location
+ const typename OutputImageType::InternalPixelType *ptrOut = it.GetPosition();
+
+ // Get the offset into the fixed pointer
+ long offset = ptrOut - bOut;
+
+ // Map to a position at which to interpolate
+ if(def)
+ {
+ const typename DeformationFieldType::InternalPixelType &def_x = bDef[offset];
+
+ // Use deformation field
+ for(unsigned int j = 0; j < ImageDimension; j++ )
+ {
+ cix[j] = idx[j] + m_DeformationScaling * def_x[j];
+ }
+ }
+ else
+ {
+ // Compute the affine transform - directly in index space
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ cix[i] = t_b(i);
+ for(int j = 0; j < ImageDimension; j++)
+ cix[i] += t_M(i,j) * idx[j];
+ }
+ }
+
+ // Call the interpolation code
+ m_TotalEnergyPerThread[threadId] +=
+ this->OpticalFlowFastInterpolate(
+ Dispatch<ImageDimension>(),
+ cix.data_block(),
+ bFix + offset * kFixed,
+ moving->GetBufferPointer(),
+ *ptrOut,
+ mov_size.data_block(),
+ kMoving,
+ zeros.data_block());
+ }
+
+}
+*/
+
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+void
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::GenerateInputRequestedRegion()
+{
+ // call the superclass's implementation
+ Superclass::GenerateInputRequestedRegion();
+
+ // Different behavior for fixed and moving images
+ InputImageType *fixed = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("Primary"));
+ InputImageType *moving = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("moving"));
+ ImageBase<ImageDimension> *transform = TTransformTraits::AsImageBase(m_Transform);
+ // DeformationFieldType *def = dynamic_cast<DeformationFieldType *>(this->ProcessObject::GetInput("deformation"));
+
+ if(moving)
+ moving->SetRequestedRegionToLargestPossibleRegion();
+
+ if(fixed)
+ {
+ fixed->SetRequestedRegion( this->GetOutput()->GetRequestedRegion() );
+ if(!fixed->VerifyRequestedRegion())
+ fixed->SetRequestedRegionToLargestPossibleRegion();
+ }
+
+ if(transform)
+ {
+ transform->SetRequestedRegion( this->GetOutput()->GetRequestedRegion() );
+ if(!transform->VerifyRequestedRegion())
+ transform->SetRequestedRegionToLargestPossibleRegion();
+ }
+}
+
+
+template <class TInputImage, class TOutputImage, class TTransformTraits>
+void
+MultiImageOpticalFlowImageFilter<TInputImage,TOutputImage,TTransformTraits>
+::GenerateOutputInformation()
+{
+ // call the superclass's implementation of this method
+ Superclass::GenerateOutputInformation();
+
+ OutputImageType *outputPtr = this->GetOutput();
+ InputImageType *fixed = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("Primary"));
+ outputPtr->SetSpacing( fixed->GetSpacing() );
+ outputPtr->SetOrigin( fixed->GetOrigin() );
+ outputPtr->SetDirection( fixed->GetDirection() );
+ outputPtr->SetLargestPossibleRegion( fixed->GetLargestPossibleRegion() );
+}
+
+
+
+
+
+/* ================= AFFINE =================== */
+
+/**
+ * Setup state of filter before multi-threading.
+ * InterpolatorType::SetInputImage is not thread-safe and hence
+ * has to be setup before ThreadedGenerateData
+ */
+template <class TInputImage>
+void
+MultiImageAffineMSDMetricFilter<TInputImage>
+::BeforeThreadedGenerateData()
+{
+ // Initialize the per thread data
+ m_ThreadData.resize(this->GetNumberOfThreads(), ThreadData());
+}
+
+/**
+ * Setup state of filter after multi-threading.
+ */
+template <class TInputImage>
+void
+MultiImageAffineMSDMetricFilter<TInputImage>
+::AfterThreadedGenerateData()
+{
+ // Add up all the thread data
+ ThreadData summary;
+ for(int i = 0; i < m_ThreadData.size(); i++)
+ {
+ summary.metric += m_ThreadData[i].metric;
+ summary.mask += m_ThreadData[i].mask;
+ summary.gradient += m_ThreadData[i].gradient;
+ summary.grad_mask += m_ThreadData[i].grad_mask;
+ }
+
+ // Compute the objective value
+ m_MetricValue = summary.metric / summary.mask;
+
+ // Compute the gradient
+ vnl_vector<double> grad_metric(summary.gradient.size());
+ for(int j = 0; j < summary.gradient.size(); j++)
+ {
+ grad_metric[j] =
+ (-2.0 * summary.gradient[j] - m_MetricValue * summary.grad_mask[j]) / summary.mask;
+ }
+
+ // Pack into the output
+ m_MetricGradient = TransformType::New();
+ itk::unflatten_affine_transform(grad_metric.data_block(), m_MetricGradient.GetPointer());
+
+ /*
+ m_MetricValue = summary.mask;
+ m_MetricGradient = TransformType::New();
+ itk::unflatten_affine_transform(summary.grad_mask.data_block(), m_MetricGradient.GetPointer());
+ */
+
+ /*
+ m_MetricValue = summary.metric;
+ m_MetricGradient = TransformType::New();
+ vnl_vector<double> grad_metric = -2.0 * summary.gradient;
+ itk::unflatten_affine_transform(grad_metric.data_block(), m_MetricGradient.GetPointer());
+ */
+}
+
+template <class TInputImage>
+void
+MultiImageAffineMSDMetricFilter<TInputImage>
+::GenerateInputRequestedRegion()
+{
+ // Call the superclass's implementation
+ Superclass::GenerateInputRequestedRegion();
+
+ // Set regions to max
+ InputImageType *fixed = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("Primary"));
+ InputImageType *moving = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("moving"));
+
+ if(moving)
+ moving->SetRequestedRegionToLargestPossibleRegion();
+
+ if(fixed)
+ fixed->SetRequestedRegionToLargestPossibleRegion();
+}
+
+template <class TInputImage>
+void
+MultiImageAffineMSDMetricFilter<TInputImage>
+::EnlargeOutputRequestedRegion(DataObject *data)
+{
+ Superclass::EnlargeOutputRequestedRegion(data);
+ data->SetRequestedRegionToLargestPossibleRegion();
+}
+
+
+template <class TInputImage>
+void
+MultiImageAffineMSDMetricFilter<TInputImage>
+::AllocateOutputs()
+{
+ InputImageType *fixed = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("Primary"));
+ this->GraftOutput(fixed);
+}
+
+
+template <class TInputImage>
+void
+MultiImageAffineMSDMetricFilter<TInputImage>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId )
+{
+ // Get the pointers to the input and output images
+ InputImageType *fixed = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("Primary"));
+ InputImageType *moving = dynamic_cast<InputImageType *>(this->ProcessObject::GetInput("moving"));
+
+ // Get the pointer to the start of the fixed data
+ const InputComponentType *fix_buffer = fixed->GetBufferPointer();
+
+ // Get the number of components
+ int kFixed = fixed->GetNumberOfComponentsPerPixel();
+ int kMoving = moving->GetNumberOfComponentsPerPixel();
+
+ // Create an interpolator for the moving image
+ typedef FastLinearInterpolator<InputComponentType, ImageDimension> FastInterpolator;
+ FastInterpolator flint(moving);
+
+ // Iterate over the deformation field and the output image. In reality, we don't
+ // need to waste so much time on iteration, so we use a specialized iterator here
+ typedef ImageRegionConstIteratorWithIndexOverride<InputImageType> FixedIter;
+
+ // Location of the lookup
+ vnl_vector_fixed<float, ImageDimension> cix;
+
+ // Pointer to store interpolated moving data
+ vnl_vector<typename InputImageType::InternalPixelType> interp_mov(kFixed);
+
+ // Pointer to store the gradient of the moving images
+ vnl_vector<typename InputImageType::InternalPixelType> interp_mov_grad(kFixed * ImageDimension);
+
+ // The thread data to accumulate
+ ThreadData &td = m_ThreadData[threadId];
+
+ // Get the stride for interpolation (how many moving pixels to skip)
+ int stride = ImageDimension + 1;
+
+ // Affine transform matrix and vector
+ vnl_matrix_fixed<double, ImageDimension, ImageDimension> M =
+ m_Transform->GetMatrix().GetVnlMatrix();
+ vnl_vector_fixed<double, ImageDimension> off =
+ m_Transform->GetOffset().GetVnlVector();
+
+ // Gradient accumulator
+ vnl_vector_fixed<double, ImageDimension> grad, gradM;
+
+ // Iterate over the fixed space region
+ for(FixedIter it(fixed, outputRegionForThread); !it.IsAtEnd(); ++it)
+ {
+ // Get the index at the current location
+ const IndexType &idx = it.GetIndex();
+
+ // Get the pointer to the fixed pixel
+ // TODO: WHY IS THIS RETURNING NONSENSE?
+ const InputComponentType *fix_ptr =
+ fix_buffer + (it.GetPosition() - fix_buffer) * kFixed;
+
+ // Map to a position at which to interpolate
+ // TODO: all this can be done more efficiently!
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ cix[i] = off[i];
+ for(int j = 0; j < ImageDimension; j++)
+ cix[i] += M(i,j) * idx[j];
+ }
+
+ // Do we need the gradient?
+ if(m_ComputeGradient)
+ {
+ // Interpolate moving image with gradient
+ typename FastInterpolator::InOut status =
+ flint.InterpolateWithGradient(cix.data_block(), stride,
+ interp_mov.data_block(),
+ interp_mov_grad.data_block());
+
+ // Stop if the sample is outside
+ if(status == FastInterpolator::OUTSIDE)
+ continue;
+
+ // Initialize the gradient to zeros
+ grad.fill(0.0);
+
+ // Iterate over the components
+ const InputComponentType *mov_ptr = interp_mov.data_block(), *mov_ptr_end = mov_ptr + kFixed;
+ const InputComponentType *mov_grad_ptr = interp_mov_grad.data_block();
+ float *wgt_ptr = m_Weights.data_block();
+ double w_sq_diff = 0.0;
+
+ // Compute the gradient of the term contribution for this voxel
+ for( ;mov_ptr < mov_ptr_end; ++mov_ptr, ++fix_ptr, ++wgt_ptr)
+ {
+ // Intensity difference for k-th component
+ double del = (*fix_ptr) - *(mov_ptr);
+
+ // Weighted intensity difference for k-th component
+ double delw = (*wgt_ptr) * del;
+
+ // Accumulate the weighted sum of squared differences
+ w_sq_diff += delw * del;
+
+ // Accumulate the weighted gradient term
+ for(int i = 0; i < ImageDimension; i++)
+ grad[i] += delw * *(mov_grad_ptr++);
+ }
+
+ // Accumulators for the gradients
+ double *out_grad = td.gradient.data_block();
+ double *out_grad_mask = td.grad_mask.data_block();
+
+ // For border regions, we need to explicitly deal with the mask
+ if(status == FastInterpolator::BORDER)
+ {
+ // Border - compute the mask and its gradient
+ double mask = flint.GetMaskAndGradient(gradM.data_block());
+
+ // Compute the mask and metric gradient contributions
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ double v = grad[i] * mask - 0.5 * gradM[i] * w_sq_diff;
+ *(out_grad++) += v;
+ *(out_grad_mask++) += gradM[i];
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ *(out_grad++) += v * idx[j];
+ *(out_grad_mask++) += gradM[i] * idx[j];
+ }
+ }
+
+ td.metric += w_sq_diff * mask;
+ td.mask += mask;
+ }
+ else
+ {
+ // No border - means no dealing with the mask!
+ for(int i = 0; i < ImageDimension; i++)
+ {
+ *(out_grad++) += grad[i];
+ for(int j = 0; j < ImageDimension; j++)
+ {
+ *(out_grad++) += grad[i] * idx[j];
+ }
+ }
+
+ td.metric += w_sq_diff;
+ td.mask += 1.0;
+ }
+ }
+
+ // No gradient requested
+ else
+ {
+ // Interpolate moving image with gradient
+ typename FastInterpolator::InOut status =
+ flint.Interpolate(cix.data_block(), stride, interp_mov.data_block());
+
+ // Stop if the sample is outside
+ if(status == FastInterpolator::OUTSIDE)
+ continue;
+
+ // Iterate over the components
+ const InputComponentType *mov_ptr = interp_mov.data_block(), *mov_ptr_end = mov_ptr + kFixed;
+ float *wgt_ptr = m_Weights.data_block();
+ double w_sq_diff = 0.0;
+
+ // Compute the gradient of the term contribution for this voxel
+ for( ;mov_ptr < mov_ptr_end; ++mov_ptr, ++fix_ptr, ++wgt_ptr)
+ {
+ // Intensity difference for k-th component
+ double del = (*fix_ptr) - *(mov_ptr);
+
+ // Weighted intensity difference for k-th component
+ double delw = (*wgt_ptr) * del;
+
+ // Accumulate the weighted sum of squared differences
+ w_sq_diff += delw * del;
+ }
+
+ // For border regions, we need to explicitly deal with the mask
+ if(status == FastInterpolator::BORDER)
+ {
+ // Border - compute the mask and its gradient
+ double mask = flint.GetMaskAndGradient(gradM.data_block());
+ td.metric += w_sq_diff * mask;
+ td.mask += mask;
+ }
+ else
+ {
+ td.metric += w_sq_diff;
+ td.mask += 1.0;
+ }
+ }
+ }
+}
+
+
+
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/greedy/src/OneDimensionalInPlaceAccumulateFilter.h b/Submodules/greedy/src/OneDimensionalInPlaceAccumulateFilter.h
new file mode 100644
index 0000000..19d3bb6
--- /dev/null
+++ b/Submodules/greedy/src/OneDimensionalInPlaceAccumulateFilter.h
@@ -0,0 +1,123 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef ONEDIMENSIONALINPLACEACCUMULATEFILTER_H
+#define ONEDIMENSIONALINPLACEACCUMULATEFILTER_H
+
+#include "itkInPlaceImageFilter.h"
+#include "itkImageRegionSplitterDirection.h"
+
+/**
+ * This is a filter for fast computation of box sums in an image. It is mean to be
+ * used once in each image dimension (i.e., a separable filter). The input to the
+ * filter is assumed to be a VectorImage (the filter is optimized for this)
+ */
+template <class TInputImage>
+class OneDimensionalInPlaceAccumulateFilter : public itk::InPlaceImageFilter<TInputImage, TInputImage>
+{
+public:
+
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> Self;
+ typedef itk::InPlaceImageFilter<TInputImage, TInputImage> Superclass;
+ typedef itk::SmartPointer<Self> Pointer;
+ typedef itk::SmartPointer<const Self> ConstPointer;
+
+ itkTypeMacro(OneDimensionalInPlaceAccumulateFilter, itk::InPlaceImageFilter)
+
+ itkNewMacro(Self)
+
+ /** Some convenient typedefs. */
+ typedef TInputImage InputImageType;
+ typedef TInputImage OutputImageType;
+ typedef typename OutputImageType::Pointer OutputImagePointer;
+ typedef typename OutputImageType::RegionType OutputImageRegionType;
+ typedef typename OutputImageType::PixelType OutputImagePixelType;
+ typedef typename OutputImageType::InternalPixelType OutputImageComponentType;
+
+ /** We use a custom splitter */
+ typedef itk::ImageRegionSplitterDirection SplitterType;
+
+ /** ImageDimension constant */
+ itkStaticConstMacro(OutputImageDimension, unsigned int, TInputImage::ImageDimension);
+
+ itkGetMacro(Radius, int)
+ itkSetMacro(Radius, int)
+
+ itkGetMacro(Dimension, int)
+ itkSetMacro(Dimension, int)
+
+ /**
+ * Set the range of components in the input image that will be processed by the
+ * accumulation filter. The components out of this range will be ignored. The range
+ * is specified as number of components skipped at the beginning and number of components
+ * skipped at the end. For example, passing (1,2) means accumulation will be applied
+ * only to components 1 ... nc-3 where nc is the number of components in the input.
+ */
+ void SetComponentRange(int num_ignored_at_start, int num_ignored_at_end);
+
+ itkGetMacro(ComponentOffsetFront, int)
+ itkGetMacro(ComponentOffsetBack, int)
+
+protected:
+
+ OneDimensionalInPlaceAccumulateFilter();
+ ~OneDimensionalInPlaceAccumulateFilter() {}
+
+ virtual void ThreadedGenerateData(
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId);
+
+ virtual const itk::ImageRegionSplitterBase *GetImageRegionSplitter(void) const;
+
+ // Dimension of accumulation
+ int m_Dimension;
+
+ // Radius of accumulation
+ int m_Radius;
+
+ // Range of included components
+ int m_ComponentOffsetFront, m_ComponentOffsetBack;
+
+ // Region splitter
+ typename SplitterType::Pointer m_Splitter;
+
+};
+
+/**
+ * This helper function strings N 1-D filters together
+ */
+template <class TInputImage>
+typename TInputImage::Pointer
+AccumulateNeighborhoodSumsInPlace(TInputImage *image, const typename TInputImage::SizeType &radius,
+ int num_ignored_at_start = 0, int num_ignored_at_end = 0);
+
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "OneDimensionalInPlaceAccumulateFilter.txx"
+#endif
+
+
+#endif // ONEDIMENSIONALINPLACEACCUMULATEFILTER_H
diff --git a/Submodules/greedy/src/OneDimensionalInPlaceAccumulateFilter.txx b/Submodules/greedy/src/OneDimensionalInPlaceAccumulateFilter.txx
new file mode 100644
index 0000000..26f7412
--- /dev/null
+++ b/Submodules/greedy/src/OneDimensionalInPlaceAccumulateFilter.txx
@@ -0,0 +1,585 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include <itkImageLinearIteratorWithIndex.h>
+#include "ImageRegionConstIteratorWithIndexOverride.h"
+
+
+template <class TInputImage>
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::OneDimensionalInPlaceAccumulateFilter()
+{
+ m_Radius = 0;
+ m_Dimension = 0;
+ m_ComponentOffsetFront = m_ComponentOffsetBack = 0;
+ m_Splitter = SplitterType::New();
+ this->InPlaceOn();
+}
+
+template <class TInputImage>
+const itk::ImageRegionSplitterBase *
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::GetImageRegionSplitter(void) const
+{
+ m_Splitter->SetDirection(m_Dimension);
+ return m_Splitter;
+}
+
+
+template <class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::SetComponentRange(int num_ignored_at_start, int num_ignored_at_end)
+{
+ m_ComponentOffsetFront = num_ignored_at_start;
+ m_ComponentOffsetBack = num_ignored_at_end;
+ this->Modified();
+}
+
+/**
+ * This worker class is defined to allow partial specialization of the ThreadedGenerateData
+ * based on the pixel type (float/double)
+ */
+template <class TPixel, class TInputImage>
+class OneDimensionalInPlaceAccumulateFilterWorker
+{
+public:
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> FilterType;
+ typedef typename FilterType::OutputImageRegionType OutputImageRegionType;
+ typedef typename FilterType::InputImageType InputImageType;
+ static void ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId);
+};
+
+
+template <class TPixel, class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilterWorker<TPixel, TInputImage>
+::ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ // Get filter parameters
+ int radius = filter->GetRadius();
+ int dimension = filter->GetDimension();
+
+ // Get the image
+ InputImageType *image = const_cast<InputImageType *>(filter->GetInput());
+
+ // Set up the iterator that will go through all the lines in the
+ // output region. We assume that the lines span the whole length of
+ // the input, i.e., the threading direction does not interfere
+ typedef itk::ImageLinearIteratorWithIndex<TInputImage> IteratorBaseType;
+ typedef IteratorExtenderWithOffset<IteratorBaseType> IteratorType;
+
+ // This is the line iterator, although for even greater speed we operate
+ // directly on pointers, so we only use it's NextLine functionality()
+ IteratorType itLine(image, outputRegionForThread);
+ itLine.SetDirection(dimension);
+
+ // Get the number of components
+ int nc = image->GetNumberOfComponentsPerPixel();
+
+ // Get the first and last component for accumulation - these are optionally
+ // specified by the user
+ int c_first = filter->GetComponentOffsetFront(),
+ c_last = (nc - 1) - filter->GetComponentOffsetBack();
+ int n_skipped = filter->GetComponentOffsetFront() + filter->GetComponentOffsetBack();
+
+ // Get the offset corresponding to a move along the line for this iterator
+ typename IteratorType::OffsetValueType jump = itLine.GetOffset(dimension) * nc;
+
+ // Length of the line being traversed (in whole pixels, then in components)
+ int line_length = outputRegionForThread.GetSize(dimension),
+ line_length_comp = line_length * nc;
+
+ // Width of the kernel (in whole pixels, then in components)
+ int kernel_width = 2 * radius + 1;
+
+ // Allocate an array of the length of the line in components
+ TPixel *line = new TPixel[line_length_comp];
+ // double *line = new double[line_length_comp];
+ // double *sum = new double[nc], *sum_end = sum + nc, *p_sum;
+
+ // Allocate an array to hold the current running sum
+ // OutputImageComponentType *sum = new OutputImageComponentType[nc], *sum_end = sum + nc, *p_sum;
+ TPixel *sum = new TPixel[nc];
+
+ // Pointers into the sum array for the included components
+ TPixel *sum_start = sum + c_first, *sum_end = sum + c_last + 1;
+
+ // Two versions of the code - I thought that maybe the second version (further down) would be
+ // more optimized by the compiler, but if anything, I see an opposite effect (although tiny)
+
+#ifdef _ACCUM_ITER_CODE_
+
+ TPixel *p_sum;
+
+ // Start iterating over lines
+ for(itLine.GoToBegin(); !itLine.IsAtEnd(); itLine.NextLine())
+ {
+ int i;
+
+ // Initialize the sum to zero
+ for(p_sum = sum_start; p_sum < sum_end; p_sum++)
+ *p_sum = itk::NumericTraits<TPixel>::Zero;
+
+ // Pointer to the current position in the line
+ TPixel *p_line = line + c_first, *p_tail = p_line;
+
+ // Pointer to the beginning of the scan line
+ long offset_in_pixels = itLine.GetPosition() - image->GetBufferPointer();
+ long offset_in_comp = offset_in_pixels * nc;
+ const TPixel *p_scan_pixel = image->GetBufferPointer() + offset_in_comp + c_first, *p_scan;
+
+ // Pointer used for writing, it will trail the scan pointer
+ TPixel *p_write_pixel = const_cast<TPixel *>(p_scan_pixel), *p_write;
+
+ // Compute the initial sum
+ for(i = 0; i < radius; i++)
+ {
+ for(p_scan = p_scan_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_line++, p_scan++)
+ {
+ *p_sum += *p_line = *p_scan;
+ }
+
+ p_scan_pixel += jump;
+ p_line += n_skipped;
+ }
+
+ // For the next Radius + 1 values, add to the sum and write
+ for(; i < kernel_width; i++)
+ {
+ for(p_scan = p_scan_pixel, p_write = p_write_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_line++, p_scan++, p_write++)
+ {
+ *p_line = *p_scan;
+ *p_sum += *p_line;
+ *p_write = *p_sum;
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += n_skipped;
+ }
+
+ // Continue until we hit the end of the scanline
+ for(; i < line_length; i++)
+ {
+ for(p_scan = p_scan_pixel, p_write = p_write_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_line++, p_scan++, p_write++, p_tail++)
+ {
+ *p_line = *p_scan;
+ *p_sum += *p_line - *p_tail;
+ *p_write = *p_sum;
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += n_skipped;
+ p_tail += n_skipped;
+ }
+
+ // Fill out the last bit
+ for(; i < line_length + radius; i++)
+ {
+ for(p_write = p_write_pixel, p_sum = sum_start;
+ p_sum < sum_end;
+ p_sum++, p_write++, p_tail++)
+ {
+ *p_sum -= *p_tail;
+ *p_write = *p_sum;
+ }
+
+ p_write_pixel += jump;
+ p_tail += n_skipped;
+ }
+ }
+
+#else
+
+ // Start iterating over lines
+ for(itLine.GoToBegin(); !itLine.IsAtEnd(); itLine.NextLine())
+ {
+
+ int i, k, m;
+
+ // Initialize the sum to zero
+ for(int k = c_first; k <= c_last; k++)
+ sum[k] = itk::NumericTraits<TPixel>::Zero;
+
+ // Pointer to the current position in the line
+ TPixel *p_line = line, *p_tail = p_line;
+
+ // Pointer to the beginning of the scan line
+ long offset_in_pixels = itLine.GetPosition() - image->GetBufferPointer();
+ long offset_in_comp = offset_in_pixels * nc;
+
+ // Where we are scanning from
+ const TPixel *p_scan_pixel = image->GetBufferPointer() + offset_in_comp;
+
+ // Pointer used for writing, it will trail the scan pointer
+ TPixel *p_write_pixel = const_cast<TPixel *>(p_scan_pixel);
+
+ // Compute the initial sum
+ for(i = 0, m = 0; i < radius; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ sum[k] += p_line[k] = p_scan_pixel[k];
+ }
+ p_scan_pixel += jump;
+ p_line += nc;
+ }
+
+ // For the next Radius + 1 values, add to the sum and write
+ for(; i < kernel_width; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ p_write_pixel[k] = (sum[k] += p_line[k] = p_scan_pixel[k]);
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += nc;
+ }
+
+ // Continue until we hit the end of the scanline
+ for(; i < line_length; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ p_write_pixel[k] = (sum[k] += (p_line[k] = p_scan_pixel[k]) - p_tail[k]);
+ }
+
+ p_scan_pixel += jump;
+ p_write_pixel += jump;
+ p_line += nc;
+ p_tail += nc;
+ }
+
+ // Fill out the last bit
+ for(; i < line_length + radius; i++)
+ {
+ for(k = c_first; k <= c_last; k++)
+ {
+ p_write_pixel[k] = (sum[k] -= p_tail[k]);
+ }
+
+ p_write_pixel += jump;
+ p_tail += nc;
+ }
+ }
+#endif
+
+ delete sum;
+ delete line;
+}
+
+// ### PY, 05/18/2016: checked again, this SSE code is not causing any differences in float/double
+// processing, safe to keep as is!
+#define _NCC_SSE_
+#ifdef _NCC_SSE_
+
+#include <xmmintrin.h>
+
+#ifdef WIN32
+inline void allocate_aligned(int elements, float ** pointer)
+{
+ *pointer = (float *)_aligned_malloc(elements * sizeof(float), 16);
+ if (*pointer == NULL)
+ {
+ std::cerr << "_aligned_malloc returned NULL input " << elements * sizeof(float) << std::endl;
+ throw std::string("_aligned_malloc allocation error");
+ }
+}
+#else
+inline void allocate_aligned(int elements, float ** pointer)
+{
+ int rc = posix_memalign( (void **) pointer, 16, elements * sizeof(float));
+ if(rc != 0)
+ {
+ std::cerr << "posix_memalign return value " << rc << " input " << elements * sizeof(float) << std::endl;
+ throw std::string("posix_memalign allocation error");
+ }
+}
+#endif
+
+/**
+ * A specialization of the threaded generate data method for floating point images that uses
+ * SSE intrinsics for faster computation
+ */
+template <class TInputImage>
+class OneDimensionalInPlaceAccumulateFilterWorker<float, TInputImage>
+{
+public:
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> FilterType;
+ typedef typename FilterType::OutputImageRegionType OutputImageRegionType;
+ typedef typename FilterType::InputImageType InputImageType;
+ static void ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId);
+};
+
+template <class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilterWorker<float, TInputImage>
+::ThreadedGenerateData(FilterType *filter,
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ // Get filter parameters
+ int dimension = filter->GetDimension();
+ int radius = filter->GetRadius();
+ int skip_front = filter->GetComponentOffsetFront();
+ int skip_back = filter->GetComponentOffsetBack();
+
+ // Get the image
+ InputImageType *image = const_cast<InputImageType *>(filter->GetInput());
+
+ // Set up the iterator that will go through all the lines in the
+ // output region. We assume that the lines span the whole length of
+ // the input, i.e., the threading direction does not interfere
+ typedef itk::ImageLinearIteratorWithIndex<InputImageType> IteratorBaseType;
+ typedef IteratorExtenderWithOffset<IteratorBaseType> IteratorType;
+
+ // This is the line iterator, although for even greater speed we operate
+ // directly on pointers, so we only use it's NextLine functionality()
+ IteratorType itLine(image, outputRegionForThread);
+ itLine.SetDirection(dimension);
+
+ // Get the number of components
+ int nc = image->GetNumberOfComponentsPerPixel();
+
+ // Get the first and last component for accumulation - these are optionally
+ // specified by the user. The remaining components are left untouched
+ int c_first = skip_front, c_last = (nc - 1) - skip_back;
+ int n_skipped = skip_front + skip_back;
+
+ // Get the offset corresponding to a move along the line for this iterator
+ typename IteratorType::OffsetValueType jump = itLine.GetOffset(dimension) * nc;
+
+ // Length of the line being traversed (in whole pixels, then in components)
+ int line_length = outputRegionForThread.GetSize(dimension);
+
+ // Width of the kernel (in whole pixels, then in components)
+ int kernel_width = 2 * radius + 1;
+
+ // We want some alignment for SIMD purposes. So we need to make a stride be a factor of 16 bytes
+ int nc_used = nc - n_skipped;
+ int bytes_per_pixel = sizeof(float) * nc_used;
+
+ // Round up, so it works out to 16 bytes
+ int align_stride = 4 * sizeof(float);
+ int padded_bytes_per_pixel = (bytes_per_pixel % align_stride) == 0
+ ? bytes_per_pixel : align_stride * (1 + bytes_per_pixel / align_stride);
+
+ // Number of chunks of four components per pixel
+ int nc_padded = padded_bytes_per_pixel / sizeof(float);
+
+ // The following arrays are allocated temporarily
+ float *scanline, *tailline, *sum_align;
+
+ // This is a byte-aligned copy of the pixel column from the image
+ allocate_aligned(line_length * nc_padded, &scanline);
+
+ // This is a second aligned copy
+ allocate_aligned(line_length * nc_padded, &tailline);
+
+ // End of the scanline
+ float *p_scanline_end = scanline + line_length * nc_padded;
+
+ // Aligned sum array - where the sums are computed
+ allocate_aligned(nc_padded, &sum_align);
+
+ // Start iterating over lines
+ for(itLine.GoToBegin(); !itLine.IsAtEnd(); itLine.NextLine())
+ {
+ int i, k;
+
+ // Pointer to the beginning of the scan line
+ long offset_in_pixels = itLine.GetPosition() - image->GetBufferPointer();
+ long offset_in_comp = offset_in_pixels * nc;
+
+ // Get the pointer to first component in first pixel
+ const float *p_scan_pixel = image->GetBufferPointer() + offset_in_comp + c_first;
+
+ // Registers
+ __m128 m_line, m_tail, m_sum_cur, m_sum_new;
+
+ // Copy the contents of the image into the aligned line
+ float *p_copy = scanline;
+ const float *p_src = p_scan_pixel;
+ for(; p_copy < p_scanline_end; p_copy += nc_padded, p_src += jump)
+ {
+#ifndef WIN32
+ __builtin_prefetch(p_src + 5 * jump, 0, 0);
+#endif
+
+ for (i = 0; i < nc_used; i++)
+ p_copy[i] = p_src[i];
+ }
+
+ // Make a copy of the scan line
+ for(p_src = scanline, p_copy = tailline; p_src < p_scanline_end; p_copy+=4, p_src+=4)
+ {
+ m_line = _mm_load_ps(p_src);
+ _mm_store_ps(p_copy, m_line);
+ }
+
+ // Clear the sum array at the beginning
+ for(k = 0; k < nc_padded; k++)
+ sum_align[k] = 0.0;
+
+ // Pointer to the current position in the line
+ float *p_line = scanline, *p_tail = tailline;
+
+ // Pointer used for writing, it will trail the scan pointer
+ float *p_write_pixel = scanline;
+
+ // Pointer used for writing, it will trail the scan pointer
+ float *p_sum_end = sum_align + nc_padded, *p_sum;
+
+ // Compute the initial sum
+ for(i = 0; i < radius; i++)
+ {
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_line+=4)
+ {
+ m_line = _mm_load_ps(p_line);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_add_ps(m_sum_cur, m_line);
+ _mm_store_ps(p_sum, m_sum_new);
+ }
+ }
+
+ // For the next Radius + 1 values, add to the sum and write
+ for(; i < kernel_width; i++)
+ {
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_line+=4, p_write_pixel+=4)
+ {
+ m_line = _mm_load_ps(p_line);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_add_ps(m_sum_cur, m_line);
+ _mm_store_ps(p_sum, m_sum_new);
+ _mm_store_ps(p_write_pixel, m_sum_new);
+ }
+ }
+
+ // Continue until we hit the end of the scanline
+ for(; i < line_length; i++)
+ {
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_line+=4, p_tail+=4, p_write_pixel+=4)
+ {
+ m_line = _mm_load_ps(p_line);
+ m_tail = _mm_load_ps(p_tail);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_add_ps(m_sum_cur, _mm_sub_ps(m_line, m_tail));
+ _mm_store_ps(p_sum, m_sum_new);
+ _mm_store_ps(p_write_pixel, m_sum_new);
+ }
+ }
+
+ // Fill out the last bit
+ for(; i < line_length + radius; i++)
+ {
+ for(p_sum = sum_align; p_sum < p_sum_end; p_sum+=4, p_tail+=4, p_write_pixel+=4)
+ {
+ m_tail = _mm_load_ps(p_tail);
+ m_sum_cur = _mm_load_ps(p_sum);
+ m_sum_new = _mm_sub_ps(m_sum_cur, m_tail);
+ _mm_store_ps(p_sum, m_sum_new);
+ _mm_store_ps(p_write_pixel, m_sum_new);
+ }
+ }
+
+ // Copy the accumulated pixels back into the main image
+ float *p_copy_back = const_cast<float *>(p_scan_pixel);
+ const float *p_src_back = scanline;
+ for(; p_src_back < p_scanline_end; p_src_back += nc_padded, p_copy_back += jump)
+ {
+#ifndef WIN32
+ __builtin_prefetch(p_copy_back + 5 * jump, 1, 0);
+#endif
+ for(i = 0; i < nc_used; i++)
+ p_copy_back[i] = p_src_back[i];
+ }
+ }
+
+ // Free allocated memory
+ free(tailline);
+ free(scanline);
+ free(sum_align);
+}
+
+#endif // _NCC_SSE_
+
+
+/**
+ * Default implementaton of the threaded generate data method
+ */
+template <class TInputImage>
+void
+OneDimensionalInPlaceAccumulateFilter<TInputImage>
+::ThreadedGenerateData(
+ const OutputImageRegionType & outputRegionForThread,
+ itk::ThreadIdType threadId)
+{
+ typedef OneDimensionalInPlaceAccumulateFilterWorker<OutputImageComponentType, InputImageType> WorkerType;
+ WorkerType::ThreadedGenerateData(this, outputRegionForThread, threadId);
+}
+
+
+
+
+template <class TInputImage>
+typename TInputImage::Pointer
+AccumulateNeighborhoodSumsInPlace(TInputImage *image, const typename TInputImage::SizeType &radius,
+ int num_ignored_at_start, int num_ignored_at_end)
+{
+ typedef OneDimensionalInPlaceAccumulateFilter<TInputImage> AccumFilterType;
+
+ typename itk::ImageSource<TInputImage>::Pointer pipeTail;
+ for(int dir = 0; dir < TInputImage::ImageDimension; dir++)
+ {
+ typename AccumFilterType::Pointer accum = AccumFilterType::New();
+ accum->SetInput(pipeTail.IsNull() ? image : pipeTail->GetOutput());
+ accum->SetDimension(dir);
+ accum->SetRadius(radius[dir]);
+ accum->SetComponentRange(num_ignored_at_start, num_ignored_at_end);
+ pipeTail = accum;
+
+ accum->Update();
+ }
+
+ return pipeTail->GetOutput();
+}
diff --git a/Submodules/greedy/src/SimpleWarpImageFilter.h b/Submodules/greedy/src/SimpleWarpImageFilter.h
new file mode 100644
index 0000000..ddc1960
--- /dev/null
+++ b/Submodules/greedy/src/SimpleWarpImageFilter.h
@@ -0,0 +1,208 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __SimpleWarpImageFilter_h
+#define __SimpleWarpImageFilter_h
+#include "itkImageBase.h"
+#include "itkImageFunction.h"
+#include "itkImageToImageFilter.h"
+#include "itkPoint.h"
+#include "itkFixedArray.h"
+
+namespace itk
+{
+
+/** \class SimpleWarpImageFilter
+ * \brief Warps an image using an input deformation field (for LDDMM)
+ *
+ * SimpleWarpImageFilter warps an existing image with respect to
+ * a given deformation field.
+ *
+ * A deformation field is represented as a image whose pixel type is some
+ * vector type with at least N elements, where N is the dimension of
+ * the input image. The vector type must support element access via operator
+ * [].
+ *
+ * The output image is produced by inverse mapping: the output pixels
+ * are mapped back onto the input image. This scheme avoids the creation of
+ * any holes and overlaps in the output image.
+ *
+ * Each vector in the deformation field represent the distance between
+ * a geometric point in the input space and a point in the output space
+ * in VOXEL COORDINATES (why? because it's faster!)
+ *
+ * \f[ p_{in} = p_{out} + d \f]
+ *
+ * Linear interpolation is used
+ *
+ * Position mapped to outside of the input image buffer are assigned
+ * a edge padding value.
+ *
+ * This class is templated over the type of the input image, the
+ * type of the output image and the type of the deformation field.
+ *
+ * The input image is set via SetInput. The input deformation field
+ * is set via SetDeformationField.
+ *
+ * This filter is implemented as a multithreaded filter.
+ *
+ * \warning This filter assumes that the input type, output type
+ * and deformation field type all have the same number of dimensions.
+ *
+ */
+template <
+ class TInputImage,
+ class TOutputImage,
+ class TDeformationField,
+ class TFloat
+ >
+class ITK_EXPORT SimpleWarpImageFilter :
+ public ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef SimpleWarpImageFilter Self;
+ typedef ImageToImageFilter<TInputImage,TOutputImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( SimpleWarpImageFilter, ImageToImageFilter );
+
+ /** Typedef to describe the output image region type. */
+ typedef typename TOutputImage::RegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::InputImagePointer InputImagePointer;
+ typedef typename Superclass::OutputImageType OutputImageType;
+ typedef typename Superclass::OutputImagePointer OutputImagePointer;
+ typedef typename Superclass::InputImageConstPointer InputImageConstPointer;
+ typedef typename OutputImageType::IndexType IndexType;
+ typedef typename OutputImageType::IndexValueType IndexValueType;
+ typedef typename OutputImageType::SizeType SizeType;
+ typedef typename OutputImageType::PixelType PixelType;
+ typedef typename OutputImageType::SpacingType SpacingType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TOutputImage::ImageDimension );
+ itkStaticConstMacro(InputImageDimension, unsigned int,
+ TInputImage::ImageDimension );
+ itkStaticConstMacro(DeformationFieldDimension, unsigned int,
+ TDeformationField::ImageDimension );
+ /** typedef for base image type at the current ImageDimension */
+ typedef ImageBase<itkGetStaticConstMacro(ImageDimension)> ImageBaseType;
+
+ /** Deformation field typedef support. */
+ typedef TDeformationField DeformationFieldType;
+ typedef typename DeformationFieldType::Pointer DeformationFieldPointer;
+ typedef typename DeformationFieldType::PixelType DisplacementType;
+
+ /** Type for representing the direction of the output image */
+ typedef typename TOutputImage::DirectionType DirectionType;
+
+ /** Set the deformation field. */
+ void SetDeformationField( const DeformationFieldType * field );
+
+ /** Get a pointer the deformation field. */
+ DeformationFieldType * GetDeformationField(void);
+
+ /** Set the edge padding value */
+ itkSetMacro( EdgePaddingValue, PixelType );
+
+ /** Get the edge padding value */
+ itkGetConstMacro( EdgePaddingValue, PixelType );
+
+ /** Set scaling factor for the deformation field */
+ itkSetMacro(DeformationScaling, TFloat);
+ itkGetMacro(DeformationScaling, TFloat);
+
+ /** SimpleWarpImageFilter produces an image which is a different
+ * size than its input image. As such, it needs to provide an
+ * implemenation for GenerateOutputInformation() which set
+ * the output information according the OutputSpacing, OutputOrigin
+ * and the deformation field's LargestPossibleRegion. */
+ virtual void GenerateOutputInformation();
+
+ /** It is difficult to compute in advance the input image region
+ * required to compute the requested output region. Thus the safest
+ * thing to do is to request for the whole input image.
+ *
+ * For the deformation field, the input requested region
+ * set to be the same as that of the output requested region. */
+ virtual void GenerateInputRequestedRegion();
+
+ /** This method is used to set the state of the filter before
+ * multi-threading. */
+ virtual void BeforeThreadedGenerateData();
+
+ /** This method is used to set the state of the filter after
+ * multi-threading. */
+ virtual void AfterThreadedGenerateData();
+
+ typedef itk::ImageFunction<
+ TInputImage,
+ typename itk::NumericTraits< typename TInputImage::PixelType >::RealType,
+ TFloat> InterpolatorType;
+
+ itkSetObjectMacro(Interpolator, InterpolatorType);
+ itkGetObjectMacro(Interpolator, InterpolatorType);
+
+protected:
+ SimpleWarpImageFilter();
+ ~SimpleWarpImageFilter() {};
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId );
+
+private:
+ SimpleWarpImageFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ PixelType m_EdgePaddingValue;
+
+ // Scaling for the deformation field
+ TFloat m_DeformationScaling;
+
+ // Interpolator
+ typename InterpolatorType::Pointer m_Interpolator;
+};
+
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "SimpleWarpImageFilter.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/SimpleWarpImageFilter.txx b/Submodules/greedy/src/SimpleWarpImageFilter.txx
new file mode 100644
index 0000000..080b09d
--- /dev/null
+++ b/Submodules/greedy/src/SimpleWarpImageFilter.txx
@@ -0,0 +1,246 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef __SimpleWarpImageFilter_txx
+#define __SimpleWarpImageFilter_txx
+#include "SimpleWarpImageFilter.h"
+
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkNumericTraits.h"
+#include "itkProgressReporter.h"
+#include "itkContinuousIndex.h"
+#include "vnl/vnl_math.h"
+namespace itk
+{
+
+/**
+ * Default constructor.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::SimpleWarpImageFilter()
+{
+ // Setup the number of required inputs
+ this->SetNumberOfRequiredInputs( 2 );
+
+ // Setup default values
+ m_Interpolator = NULL;
+ m_EdgePaddingValue = NumericTraits<PixelType>::Zero;
+}
+
+/**
+ * Standard PrintSelf method.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::PrintSelf(std::ostream& os, Indent indent) const
+{
+
+ Superclass::PrintSelf(os, indent);
+ os << indent << "EdgePaddingValue: "
+ << static_cast<typename NumericTraits<PixelType>::PrintType>(m_EdgePaddingValue)
+ << std::endl;
+}
+
+/**
+ * Set deformation field as Inputs[1] for this ProcessObject.
+ *
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::SetDeformationField(
+ const DeformationFieldType * field )
+{
+ // const cast is needed because the pipeline is not const-correct.
+ DeformationFieldType * input =
+ const_cast< DeformationFieldType * >( field );
+ this->ProcessObject::SetNthInput( 1, input );
+}
+
+
+/**
+ * Return a pointer to the deformation field.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+typename SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::DeformationFieldType *
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::GetDeformationField(void)
+{
+ return static_cast<DeformationFieldType *>
+ ( this->ProcessObject::GetInput( 1 ));
+}
+
+
+/**
+ * Setup state of filter before multi-threading.
+ * InterpolatorType::SetInputImage is not thread-safe and hence
+ * has to be setup before ThreadedGenerateData
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::BeforeThreadedGenerateData()
+{
+ if( !m_Interpolator )
+ {
+ itkExceptionMacro(<< "Interpolator not set");
+ }
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+
+ // Connect input image to interpolator
+ m_Interpolator->SetInputImage( this->GetInput() );
+ typename DeformationFieldType::RegionType defRegion =
+ fieldPtr->GetLargestPossibleRegion();
+ typename OutputImageType::RegionType outRegion =
+ this->GetOutput()->GetLargestPossibleRegion();
+}
+
+/**
+ * Setup state of filter after multi-threading.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::AfterThreadedGenerateData()
+{
+ // Disconnect input image from interpolator
+ m_Interpolator->SetInputImage( NULL );
+}
+
+/**
+ * Compute the output for the region specified by outputRegionForThread.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId )
+{
+ InputImageConstPointer inputPtr = this->GetInput();
+ OutputImagePointer outputPtr = this->GetOutput();
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+
+ // iterator for the output image
+ ImageRegionIteratorWithIndex<OutputImageType> outputIt(
+ outputPtr, outputRegionForThread );
+ IndexType index;
+ itk::ContinuousIndex<TFloat,ImageDimension> cix;
+ DisplacementType displacement;
+
+ // iterator for the deformation field
+ ImageRegionIterator<DeformationFieldType>
+ fieldIt(fieldPtr, outputRegionForThread );
+
+ while( !outputIt.IsAtEnd() )
+ {
+ // get the output image index
+ index = outputIt.GetIndex();
+
+ // get the required displacement
+ displacement = fieldIt.Get();
+
+ // compute the required input image point
+ for(unsigned int j = 0; j < ImageDimension; j++ )
+ {
+ cix[j] = index[j] + m_DeformationScaling * displacement[j];
+ }
+
+ // get the interpolated value
+ if( m_Interpolator->IsInsideBuffer( cix ) )
+ {
+ PixelType value =
+ static_cast<PixelType>(m_Interpolator->EvaluateAtContinuousIndex( cix ) );
+ outputIt.Set( value );
+ }
+ else
+ {
+ outputIt.Set( m_EdgePaddingValue );
+ }
+ ++outputIt;
+ ++fieldIt;
+ }
+}
+
+
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::GenerateInputRequestedRegion()
+{
+
+ // call the superclass's implementation
+ Superclass::GenerateInputRequestedRegion();
+
+ // request the largest possible region for the input image
+ InputImagePointer inputPtr =
+ const_cast< InputImageType * >( this->GetInput() );
+
+ if( inputPtr )
+ {
+ inputPtr->SetRequestedRegionToLargestPossibleRegion();
+ }
+
+ // just propagate up the output requested region for the
+ // deformation field.
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+ OutputImagePointer outputPtr = this->GetOutput();
+ if(fieldPtr.IsNotNull() )
+ {
+ fieldPtr->SetRequestedRegion( outputPtr->GetRequestedRegion() );
+ if(!fieldPtr->VerifyRequestedRegion())
+ {
+ fieldPtr->SetRequestedRegion(fieldPtr->GetLargestPossibleRegion());
+ }
+ }
+}
+
+
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::GenerateOutputInformation()
+{
+ // call the superclass's implementation of this method
+ Superclass::GenerateOutputInformation();
+
+ OutputImagePointer outputPtr = this->GetOutput();
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+ outputPtr->SetSpacing( fieldPtr->GetSpacing() );
+ outputPtr->SetOrigin( fieldPtr->GetOrigin() );
+ outputPtr->SetDirection( fieldPtr->GetDirection() );
+ outputPtr->SetLargestPossibleRegion( fieldPtr->
+ GetLargestPossibleRegion() );
+}
+
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/greedy/src/bk/SimpleWarpImageFilter.h b/Submodules/greedy/src/bk/SimpleWarpImageFilter.h
new file mode 100644
index 0000000..54e99b2
--- /dev/null
+++ b/Submodules/greedy/src/bk/SimpleWarpImageFilter.h
@@ -0,0 +1,195 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: SimpleWarpImageFilter.h,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:00 $
+ Version: $Revision: 1.31 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __SimpleWarpImageFilter_h
+#define __SimpleWarpImageFilter_h
+#include "itkImageBase.h"
+#include "itkImageFunction.h"
+#include "itkImageToImageFilter.h"
+#include "itkPoint.h"
+#include "itkFixedArray.h"
+
+namespace itk
+{
+
+/** \class SimpleWarpImageFilter
+ * \brief Warps an image using an input deformation field (for LDDMM)
+ *
+ * SimpleWarpImageFilter warps an existing image with respect to
+ * a given deformation field.
+ *
+ * A deformation field is represented as a image whose pixel type is some
+ * vector type with at least N elements, where N is the dimension of
+ * the input image. The vector type must support element access via operator
+ * [].
+ *
+ * The output image is produced by inverse mapping: the output pixels
+ * are mapped back onto the input image. This scheme avoids the creation of
+ * any holes and overlaps in the output image.
+ *
+ * Each vector in the deformation field represent the distance between
+ * a geometric point in the input space and a point in the output space
+ * in VOXEL COORDINATES (why? because it's faster!)
+ *
+ * \f[ p_{in} = p_{out} + d \f]
+ *
+ * Linear interpolation is used
+ *
+ * Position mapped to outside of the input image buffer are assigned
+ * a edge padding value.
+ *
+ * This class is templated over the type of the input image, the
+ * type of the output image and the type of the deformation field.
+ *
+ * The input image is set via SetInput. The input deformation field
+ * is set via SetDeformationField.
+ *
+ * This filter is implemented as a multithreaded filter.
+ *
+ * \warning This filter assumes that the input type, output type
+ * and deformation field type all have the same number of dimensions.
+ *
+ */
+template <
+ class TInputImage,
+ class TOutputImage,
+ class TDeformationField,
+ class TFloat
+ >
+class ITK_EXPORT SimpleWarpImageFilter :
+ public ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+ /** Standard class typedefs. */
+ typedef SimpleWarpImageFilter Self;
+ typedef ImageToImageFilter<TInputImage,TOutputImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** Run-time type information (and related methods) */
+ itkTypeMacro( SimpleWarpImageFilter, ImageToImageFilter );
+
+ /** Typedef to describe the output image region type. */
+ typedef typename TOutputImage::RegionType OutputImageRegionType;
+
+ /** Inherit some types from the superclass. */
+ typedef typename Superclass::InputImageType InputImageType;
+ typedef typename Superclass::InputImagePointer InputImagePointer;
+ typedef typename Superclass::OutputImageType OutputImageType;
+ typedef typename Superclass::OutputImagePointer OutputImagePointer;
+ typedef typename Superclass::InputImageConstPointer InputImageConstPointer;
+ typedef typename OutputImageType::IndexType IndexType;
+ typedef typename OutputImageType::IndexValueType IndexValueType;
+ typedef typename OutputImageType::SizeType SizeType;
+ typedef typename OutputImageType::PixelType PixelType;
+ typedef typename OutputImageType::SpacingType SpacingType;
+
+ /** Determine the image dimension. */
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TOutputImage::ImageDimension );
+ itkStaticConstMacro(InputImageDimension, unsigned int,
+ TInputImage::ImageDimension );
+ itkStaticConstMacro(DeformationFieldDimension, unsigned int,
+ TDeformationField::ImageDimension );
+ /** typedef for base image type at the current ImageDimension */
+ typedef ImageBase<itkGetStaticConstMacro(ImageDimension)> ImageBaseType;
+
+ /** Deformation field typedef support. */
+ typedef TDeformationField DeformationFieldType;
+ typedef typename DeformationFieldType::Pointer DeformationFieldPointer;
+ typedef typename DeformationFieldType::PixelType DisplacementType;
+
+ /** Type for representing the direction of the output image */
+ typedef typename TOutputImage::DirectionType DirectionType;
+
+ /** Set the deformation field. */
+ void SetDeformationField( const DeformationFieldType * field );
+
+ /** Get a pointer the deformation field. */
+ DeformationFieldType * GetDeformationField(void);
+
+ /** Set the edge padding value */
+ itkSetMacro( EdgePaddingValue, PixelType );
+
+ /** Get the edge padding value */
+ itkGetConstMacro( EdgePaddingValue, PixelType );
+
+ /** Set scaling factor for the deformation field */
+ itkSetMacro(DeformationScaling, TFloat);
+ itkGetMacro(DeformationScaling, TFloat);
+
+ /** SimpleWarpImageFilter produces an image which is a different
+ * size than its input image. As such, it needs to provide an
+ * implemenation for GenerateOutputInformation() which set
+ * the output information according the OutputSpacing, OutputOrigin
+ * and the deformation field's LargestPossibleRegion. */
+ virtual void GenerateOutputInformation();
+
+ /** It is difficult to compute in advance the input image region
+ * required to compute the requested output region. Thus the safest
+ * thing to do is to request for the whole input image.
+ *
+ * For the deformation field, the input requested region
+ * set to be the same as that of the output requested region. */
+ virtual void GenerateInputRequestedRegion();
+
+ /** This method is used to set the state of the filter before
+ * multi-threading. */
+ virtual void BeforeThreadedGenerateData();
+
+ /** This method is used to set the state of the filter after
+ * multi-threading. */
+ virtual void AfterThreadedGenerateData();
+
+ typedef itk::ImageFunction<
+ TInputImage, typename TOutputImage::PixelType, TFloat> InterpolatorType;
+ itkSetObjectMacro(Interpolator, InterpolatorType);
+ itkGetObjectMacro(Interpolator, InterpolatorType);
+
+protected:
+ SimpleWarpImageFilter();
+ ~SimpleWarpImageFilter() {};
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+ /** SimpleWarpImageFilter is implemented as a multi-threaded filter.
+ * As such, it needs to provide and implementation for
+ * ThreadedGenerateData(). */
+ void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId );
+
+private:
+ SimpleWarpImageFilter(const Self&); //purposely not implemented
+ void operator=(const Self&); //purposely not implemented
+
+ PixelType m_EdgePaddingValue;
+
+ // Scaling for the deformation field
+ TFloat m_DeformationScaling;
+
+ // Interpolator
+ typename InterpolatorType::Pointer m_Interpolator;
+};
+
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "SimpleWarpImageFilter.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/bk/SimpleWarpImageFilter.txx b/Submodules/greedy/src/bk/SimpleWarpImageFilter.txx
new file mode 100644
index 0000000..5210f10
--- /dev/null
+++ b/Submodules/greedy/src/bk/SimpleWarpImageFilter.txx
@@ -0,0 +1,236 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: SimpleWarpImageFilter.txx,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:10 $
+ Version: $Revision: 1.34 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __SimpleWarpImageFilter_txx
+#define __SimpleWarpImageFilter_txx
+#include "SimpleWarpImageFilter.h"
+
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionIteratorWithIndex.h"
+#include "itkNumericTraits.h"
+#include "itkProgressReporter.h"
+#include "itkContinuousIndex.h"
+#include "vnl/vnl_math.h"
+namespace itk
+{
+
+/**
+ * Default constructor.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::SimpleWarpImageFilter()
+{
+ // Setup the number of required inputs
+ this->SetNumberOfRequiredInputs( 2 );
+
+ // Setup default values
+ m_Interpolator = NULL;
+ m_EdgePaddingValue = NumericTraits<PixelType>::Zero;
+}
+
+/**
+ * Standard PrintSelf method.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::PrintSelf(std::ostream& os, Indent indent) const
+{
+
+ Superclass::PrintSelf(os, indent);
+ os << indent << "EdgePaddingValue: "
+ << static_cast<typename NumericTraits<PixelType>::PrintType>(m_EdgePaddingValue)
+ << std::endl;
+}
+
+/**
+ * Set deformation field as Inputs[1] for this ProcessObject.
+ *
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::SetDeformationField(
+ const DeformationFieldType * field )
+{
+ // const cast is needed because the pipeline is not const-correct.
+ DeformationFieldType * input =
+ const_cast< DeformationFieldType * >( field );
+ this->ProcessObject::SetNthInput( 1, input );
+}
+
+
+/**
+ * Return a pointer to the deformation field.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+typename SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::DeformationFieldType *
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::GetDeformationField(void)
+{
+ return static_cast<DeformationFieldType *>
+ ( this->ProcessObject::GetInput( 1 ));
+}
+
+
+/**
+ * Setup state of filter before multi-threading.
+ * InterpolatorType::SetInputImage is not thread-safe and hence
+ * has to be setup before ThreadedGenerateData
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::BeforeThreadedGenerateData()
+{
+ if( !m_Interpolator )
+ {
+ itkExceptionMacro(<< "Interpolator not set");
+ }
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+
+ // Connect input image to interpolator
+ m_Interpolator->SetInputImage( this->GetInput() );
+ typename DeformationFieldType::RegionType defRegion =
+ fieldPtr->GetLargestPossibleRegion();
+ typename OutputImageType::RegionType outRegion =
+ this->GetOutput()->GetLargestPossibleRegion();
+}
+
+/**
+ * Setup state of filter after multi-threading.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::AfterThreadedGenerateData()
+{
+ // Disconnect input image from interpolator
+ m_Interpolator->SetInputImage( NULL );
+}
+
+/**
+ * Compute the output for the region specified by outputRegionForThread.
+ */
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::ThreadedGenerateData(
+ const OutputImageRegionType& outputRegionForThread,
+ ThreadIdType threadId )
+{
+ InputImageConstPointer inputPtr = this->GetInput();
+ OutputImagePointer outputPtr = this->GetOutput();
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+
+ // iterator for the output image
+ ImageRegionIteratorWithIndex<OutputImageType> outputIt(
+ outputPtr, outputRegionForThread );
+ IndexType index;
+ itk::ContinuousIndex<TFloat,ImageDimension> cix;
+ DisplacementType displacement;
+
+ // iterator for the deformation field
+ ImageRegionIterator<DeformationFieldType>
+ fieldIt(fieldPtr, outputRegionForThread );
+
+ while( !outputIt.IsAtEnd() )
+ {
+ // get the output image index
+ index = outputIt.GetIndex();
+
+ // get the required displacement
+ displacement = fieldIt.Get();
+
+ // compute the required input image point
+ for(unsigned int j = 0; j < ImageDimension; j++ )
+ {
+ cix[j] = index[j] + m_DeformationScaling * displacement[j];
+ }
+
+ // get the interpolated value
+ if( m_Interpolator->IsInsideBuffer( cix ) )
+ {
+ PixelType value =
+ static_cast<PixelType>(m_Interpolator->EvaluateAtContinuousIndex( cix ) );
+ outputIt.Set( value );
+ }
+ else
+ {
+ outputIt.Set( m_EdgePaddingValue );
+ }
+ ++outputIt;
+ ++fieldIt;
+ }
+}
+
+
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::GenerateInputRequestedRegion()
+{
+
+ // call the superclass's implementation
+ Superclass::GenerateInputRequestedRegion();
+
+ // request the largest possible region for the input image
+ InputImagePointer inputPtr =
+ const_cast< InputImageType * >( this->GetInput() );
+
+ if( inputPtr )
+ {
+ inputPtr->SetRequestedRegionToLargestPossibleRegion();
+ }
+
+ // just propagate up the output requested region for the
+ // deformation field.
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+ OutputImagePointer outputPtr = this->GetOutput();
+ if(fieldPtr.IsNotNull() )
+ {
+ fieldPtr->SetRequestedRegion( outputPtr->GetRequestedRegion() );
+ if(!fieldPtr->VerifyRequestedRegion())
+ {
+ fieldPtr->SetRequestedRegion(fieldPtr->GetLargestPossibleRegion());
+ }
+ }
+}
+
+
+template <class TInputImage,class TOutputImage,class TDeformationField, class TFloat>
+void
+SimpleWarpImageFilter<TInputImage,TOutputImage,TDeformationField,TFloat>
+::GenerateOutputInformation()
+{
+ // call the superclass's implementation of this method
+ Superclass::GenerateOutputInformation();
+
+ OutputImagePointer outputPtr = this->GetOutput();
+ DeformationFieldPointer fieldPtr = this->GetDeformationField();
+ outputPtr->SetSpacing( fieldPtr->GetSpacing() );
+ outputPtr->SetOrigin( fieldPtr->GetOrigin() );
+ outputPtr->SetDirection( fieldPtr->GetDirection() );
+ outputPtr->SetLargestPossibleRegion( fieldPtr->
+ GetLargestPossibleRegion() );
+}
+
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/greedy/src/bk/greedy_main.cxx b/Submodules/greedy/src/bk/greedy_main.cxx
new file mode 100644
index 0000000..d2a309b
--- /dev/null
+++ b/Submodules/greedy/src/bk/greedy_main.cxx
@@ -0,0 +1,295 @@
+#include <iostream>
+#include <cstdio>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+#include <itkImageFileReader.h>
+#include <itkGaussianInterpolateImageFunction.h>
+#include <itkResampleImageFilter.h>
+#include <itkIdentityTransform.h>
+#include <itkShrinkImageFilter.h>
+
+
+
+int usage()
+{
+ printf("greedy: Paul's greedy diffeomorphic registration implementation\n");
+ printf("Usage: \n");
+ printf(" greedy [options]\n");
+ printf("Required options: \n");
+ printf(" -d DIM : Number of image dimensions\n");
+ printf(" -i fixed.nii moving.nii : Image pair (may be repeated)\n");
+ printf(" -o output.nii : Output warp file\n");
+ printf("Optional: \n");
+ printf(" -w weight : weight of the next -i pair\n");
+ printf(" -e epsilon : step size (default = 1.0)\n");
+ printf(" -s sigma : smoothing for the greedy update step (3.0)\n");
+ printf(" -n number : number of iterations (200) \n");
+ printf(" -dump-moving : dump moving image at each iter\n");
+ printf(" -dump-freq N : dump frequency\n");
+ return -1;
+}
+
+struct ImagePairSpec
+{
+ std::string fixed;
+ std::string moving;
+ double weight;
+};
+
+struct GreedyParameters
+{
+ std::vector<ImagePairSpec> inputs;
+ std::string output;
+ unsigned int dim;
+
+ bool flag_dump_moving;
+ int dump_frequency;
+ double epsilon, sigma;
+ int niter;
+};
+
+
+template <unsigned int VDim, typename TReal = double>
+class GreedyApproach
+{
+public:
+
+ typedef LDDMMData<TReal, VDim> LDDMMType;
+ typedef typename LDDMMType::ImageType ImageType;
+ typedef typename LDDMMType::ImagePointer ImagePointer;
+ typedef typename LDDMMType::VectorImageType VectorImageType;
+ typedef typename LDDMMType::VectorImagePointer VectorImagePointer;
+
+ struct ImagePair {
+ ImagePointer fixed, moving;
+ VectorImagePointer grad_moving;
+ double weight;
+ };
+
+ static int Run(GreedyParameters ¶m);
+
+protected:
+};
+
+/**
+ * This is the main function of the GreedyApproach algorithm
+ */
+template <unsigned int VDim, typename TReal>
+int GreedyApproach<VDim, TReal>
+::Run(GreedyParameters ¶m)
+{
+ // Image pairs to register
+ std::vector<ImagePair> img;
+
+ // Read the input images
+ for(int i = 0; i < param.inputs.size(); i++)
+ {
+ ImagePair ip;
+
+ // Read fixed
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typename ReaderType::Pointer readfix = ReaderType::New();
+ readfix->SetFileName(param.inputs[i].fixed);
+ readfix->Update();
+ ip.fixed = readfix->GetOutput();
+
+ // Read moving
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typename ReaderType::Pointer readmov = ReaderType::New();
+ readmov->SetFileName(param.inputs[i].moving);
+ readmov->Update();
+ ip.moving = readmov->GetOutput();
+
+ // Allocate the gradient
+ LDDMMType::alloc_vimg(ip.grad_moving, ip.moving);
+
+ // Precompute the gradient of the moving images. There should be some
+ // smoothing of the input images before applying this computation!
+ LDDMMType::image_gradient(ip.moving, ip.grad_moving);
+
+ // Set weight
+ ip.weight = param.inputs[i].weight;
+
+ // Append
+ img.push_back(ip);
+ }
+
+ // Reference space
+ ImagePointer refspace = img.front().fixed;
+
+ // Initialize the displacement to identity
+ VectorImagePointer uk = VectorImageType::New();
+ LDDMMType::alloc_vimg(uk, refspace);
+
+ // Initialize the intermediate data
+ ImagePointer iTemp = ImageType::New();
+ LDDMMType::alloc_img(iTemp, refspace);
+
+ VectorImagePointer viTemp = VectorImageType::New();
+ LDDMMType::alloc_vimg(viTemp, refspace);
+
+ VectorImagePointer uk1 = VectorImageType::New();
+ LDDMMType::alloc_vimg(uk1, refspace);
+
+ // Iterate
+ for(unsigned int iter = 0; iter < param.niter; iter++)
+ {
+ // Initialize u(k+1) to zero
+ uk1->FillBuffer(typename LDDMMType::Vec(0.0));
+
+ // Initialize the energy computation
+ double total_energy = 0.0;
+
+ // Add all the derivative terms
+ for(int j = 0; j < img.size(); j++)
+ {
+ // Interpolate each moving image
+ LDDMMType::interp_img(img[j].moving, uk, iTemp);
+
+ // Dump the moving image?
+ if(param.flag_dump_moving && 0 == iter % param.dump_frequency)
+ {
+ char fname[256];
+ sprintf(fname, "dump_moving_%02d_iter%04d.nii.gz", j, iter);
+ LDDMMType::img_write(iTemp, fname);
+ }
+
+ // Subtract the fixed image
+ LDDMMType::img_subtract_in_place(iTemp, img[j].fixed);
+
+ // Record the norm of the difference image
+ total_energy += img[j].weight * LDDMMType::img_euclidean_norm_sq(iTemp);
+
+ // Interpolate the gradient of the moving image
+ LDDMMType::interp_vimg(img[j].grad_moving, uk, 1.0, viTemp);
+ LDDMMType::vimg_multiply_in_place(viTemp, iTemp);
+
+ // Accumulate to the force
+ LDDMMType::vimg_add_scaled_in_place(uk1, viTemp, -img[j].weight * param.epsilon);
+ }
+
+ // We have now computed the gradient vector field. Next, we smooth it
+ LDDMMType::vimg_smooth(uk1, viTemp, param.sigma);
+
+ // Write Uk1
+ if(param.flag_dump_moving && 0 == iter % param.dump_frequency)
+ {
+ char fname[256];
+ sprintf(fname, "dump_graduent_iter%04d.nii.gz", iter);
+ LDDMMType::vimg_write(uk1, fname);
+ sprintf(fname, "dump_optflow_iter%04d.nii.gz", iter);
+ LDDMMType::vimg_write(viTemp, fname);
+ }
+
+ // Compute the updated deformation field - in uk1
+ LDDMMType::interp_vimg(uk, viTemp, 1.0, uk1);
+ LDDMMType::vimg_add_in_place(uk1, viTemp);
+
+ if(param.flag_dump_moving && 0 == iter % param.dump_frequency)
+ {
+ char fname[256];
+ sprintf(fname, "dump_uk1_iter%04d.nii.gz", iter);
+ LDDMMType::vimg_write(uk1, fname);
+ }
+
+ // Swap uk and uk1 pointers
+ VectorImagePointer tmpptr = uk1; uk1 = uk; uk = tmpptr;
+ // LDDMMType::vimg_smooth(uk1, uk, 1.0);
+
+ // Compute the Jacobian determinant of the updated field (temporary)
+ LDDMMType::field_jacobian_det(uk, iTemp);
+ TReal jac_min, jac_max;
+ LDDMMType::img_min_max(iTemp, jac_min, jac_max);
+
+ // Report the energy
+ printf("Iter %5d: Energy = %8.4f DetJac Range: %8.4f to %8.4f \n", iter, total_energy, jac_min, jac_max);
+ }
+
+ // Write the resulting transformation field
+ LDDMMType::vimg_write(uk, param.output.c_str());
+
+ return 0;
+}
+
+
+
+int main(int argc, char *argv[])
+{
+ GreedyParameters param;
+ double current_weight = 1.0;
+
+ param.dim = 2;
+ param.flag_dump_moving = false;
+ param.dump_frequency = 1;
+ param.epsilon = 1.0;
+ param.sigma = 3.0;
+ param.niter = 200;
+
+ if(argc < 3)
+ return usage();
+
+ for(int i = 1; i < argc; ++i)
+ {
+ std::string arg = argv[i];
+ if(arg == "-d")
+ {
+ param.dim = atoi(argv[++i]);
+ }
+ else if(arg == "-n")
+ {
+ param.niter = atoi(argv[++i]);
+ }
+ else if(arg == "-w")
+ {
+ current_weight = atof(argv[++i]);
+ }
+ else if(arg == "-e")
+ {
+ param.epsilon = atof(argv[++i]);
+ }
+ else if(arg == "-s")
+ {
+ param.sigma = atof(argv[++i]);
+ }
+ else if(arg == "-i")
+ {
+ ImagePairSpec ip;
+ ip.weight = current_weight;
+ ip.fixed = argv[++i];
+ ip.moving = argv[++i];
+ param.inputs.push_back(ip);
+ }
+ else if(arg == "-o")
+ {
+ param.output = argv[++i];
+ }
+ else if(arg == "-dump-moving")
+ {
+ param.flag_dump_moving = true;
+ }
+ else if(arg == "-dump-frequency")
+ {
+ param.dump_frequency = atoi(argv[++i]);
+ }
+ else
+ {
+ std::cerr << "Unknown parameter " << arg << std::endl;
+ return -1;
+ }
+ }
+
+ switch(param.dim)
+ {
+ case 2: return GreedyApproach<2>::Run(param); break;
+ case 3: return GreedyApproach<3>::Run(param); break;
+ case 4: return GreedyApproach<4>::Run(param); break;
+ default:
+ std::cerr << "Wrong dimensionality" << std::endl;
+ return -1;
+ }
+}
diff --git a/Submodules/greedy/src/bk/itkGaussianInterpolateImageFunction.h b/Submodules/greedy/src/bk/itkGaussianInterpolateImageFunction.h
new file mode 100644
index 0000000..4765dfc
--- /dev/null
+++ b/Submodules/greedy/src/bk/itkGaussianInterpolateImageFunction.h
@@ -0,0 +1,267 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkGaussianInterpolateImageFunction.h,v $
+ Language: C++
+ Date: $Date: 2009/07/01 12:59:34 $
+ Version: $Revision: 1.5 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkGaussianInterpolateImageFunction_h
+#define __itkGaussianInterpolateImageFunction_h
+
+#include "itkInterpolateImageFunction.h"
+#include "vnl/vnl_erf.h"
+
+namespace itk
+{
+
+/** \class GaussianInterpolateImageFunction
+ * \brief Gaussianly interpolate an image at specified positions.
+ *
+ * GaussianInterpolateImageFunction linearly interpolates image intensity at
+ * a non-integer pixel position. This class is templated
+ * over the input image type and the coordinate representation type
+ * (e.g. float or double).
+ *
+ * This function works for N-dimensional images.
+ *
+ * \ingroup ImageFunctions ImageInterpolators
+ */
+template <class TInputImage, class TCoordRep = double>
+class ITK_EXPORT GaussianInterpolateImageFunction :
+ public InterpolateImageFunction<TInputImage,TCoordRep>
+{
+public:
+ /** Standard class typedefs. */
+ typedef GaussianInterpolateImageFunction Self;
+ typedef InterpolateImageFunction<TInputImage,TCoordRep> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(GaussianInterpolateImageFunction, InterpolateImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** OutputType typedef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** InputImageType typedef support. */
+ typedef typename Superclass::InputImageType InputImageType;
+
+ /** RealType typedef support. */
+ typedef typename Superclass::RealType RealType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(VDim, unsigned int,Superclass::ImageDimension);
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Compute internals */
+ virtual void ComputeBoundingBox()
+ {
+ const TInputImage *img = this->GetInputImage();
+ if(img == NULL) return;
+
+ // Set the bounding box
+ for(size_t d = 0; d < VDim; d++)
+ {
+ bb_start[d] = -0.5;
+ bb_end[d] = img->GetBufferedRegion().GetSize()[d] - 0.5;
+ nt[d] = (int)(bb_end[d] - bb_start[d] + 0.5);
+ dx[d].set_size(nt[d]);
+ gx[d].set_size(nt[d]);
+ sf[d] = 1.0 / (sqrt(2.0) * sigma[d] / img->GetSpacing()[d]);
+ cut[d] = sigma[d] * alpha / img->GetSpacing()[d];
+ }
+ }
+
+ /** Set input */
+ virtual void SetInputImage(const TInputImage *img)
+ {
+ // Call parent method
+ Superclass::SetInputImage(img);
+ this->ComputeBoundingBox();
+ }
+
+ void SetParameters(double *sigma, double alpha)
+ {
+ // Set the parameters
+ for(size_t d = 0; d < VDim; d++)
+ this->sigma[d] = sigma[d];
+ this->alpha = alpha;
+
+ // If the image already set, recompute
+ this->ComputeBoundingBox();
+ }
+
+ /** Evaluate the function at a ContinuousIndex position
+ *
+ * Returns the linearly interpolated image intensity at a
+ * specified point position. No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType & index ) const
+ {
+ return EvaluateAtContinuousIndex(index, NULL);
+ }
+
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType &index,
+ OutputType *grad) const
+ {
+ // The bound variables for x, y, z
+ int i0[VDim], i1[VDim];
+
+ // Compute the ERF difference arrays
+ for(size_t d = 0; d < VDim; d++)
+ {
+ double *pdx = const_cast<double *>(dx[d].data_block());
+ double *pgx = grad ? const_cast<double *>(gx[d].data_block()) : NULL;
+ compute_erf_array(pdx, i0[d], i1[d], bb_start[d], nt[d], cut[d], index[d], sf[d], pgx);
+ }
+
+ // Get a pointer to the output value
+ double sum_me = 0.0, sum_m = 0.0;
+ vnl_vector_fixed<double, VDim> dsum_me(0.0), dsum_m(0.0), dw;
+
+ // Loop over the voxels in the region identified
+ ImageRegion<VDim> region;
+ for(size_t d = 0; d < VDim; d++)
+ {
+ region.SetIndex(d, i0[d]);
+ region.SetSize(d, i1[d] - i0[d]);
+ }
+
+ for(
+ ImageRegionConstIteratorWithIndex<InputImageType> it(this->GetInputImage(), region);
+ !it.IsAtEnd(); ++it)
+ {
+ size_t j = it.GetIndex()[0];
+ double w = dx[0][j];
+ if(grad)
+ {
+ dw[0] = gx[0][j];
+ for(size_t d = 1; d < VDim; d++) dw[d] = dx[0][j];
+ }
+ for(size_t d = 1; d < VDim; d++)
+ {
+ j = it.GetIndex()[d];
+ w *= dx[d][j];
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ dw[q] *= (d == q) ? gx[d][j] : dx[d][j];
+ }
+ }
+
+ double V = it.Get();
+ sum_me += V * w;
+ sum_m += w;
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ {
+ dsum_me[q] += V * dw[q];
+ dsum_m[q] += dw[q];
+ }
+ }
+ }
+
+ double rc = sum_me / sum_m;
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ {
+ grad[q] = (dsum_me[q] - rc * dsum_m[q]) / sum_m;
+ grad[q] /= -1.4142135623730951 * sigma[q];
+ }
+ }
+
+ // return sum_me / sum_m;
+ return rc;
+
+ }
+
+protected:
+ GaussianInterpolateImageFunction() {}
+ ~GaussianInterpolateImageFunction(){};
+ void PrintSelf(std::ostream& os, Indent indent) const
+ { this->Superclass::PrintSelf(os,indent); }
+
+private:
+ GaussianInterpolateImageFunction( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ /** Number of neighbors used in the interpolation */
+ static const unsigned long m_Neighbors;
+
+ vnl_vector<double> dx[VDim], gx[VDim];
+ double bb_start[VDim], bb_end[VDim], sf[VDim], cut[VDim];
+ int nt[VDim], stride[VDim];
+ double sigma[VDim], alpha;
+
+ void compute_erf_array (
+ double *dx_erf, // The output array of erf(p+i+1) - erf(p+i)
+ int &k0, int &k1, // The range of integration 0 <= k0 < k1 <= n
+ double b, // Lower bound of the bounding box
+ int n, // Size of the bounding box in steps
+ double cut, // The distance at which to cut off
+ double p, // the value p
+ double sfac, // scaling factor 1 / (Sqrt[2] sigma)
+ double *gx_erf = NULL // Output derivative/erf array (optional)
+ ) const
+ {
+ // Determine the range of voxels along the line where to evaluate erf
+ k0 = (int) floor(p - b - cut);
+ k1 = (int) ceil(p - b + cut);
+ if(k0 < 0) k0 = 0;
+ if(k1 > n) k1 = n;
+
+ // Start at the first voxel
+ double t = (b - p + k0) * sfac;
+ double e_last = vnl_erf(t);
+ double g_last = gx_erf ? 1.128379167095513 * exp(- t * t) : 0.0;
+ for(int i = k0; i < k1; i++)
+ {
+ t += sfac;
+ double e_now = vnl_erf(t);
+ dx_erf[i] = e_now - e_last;
+ if(gx_erf)
+ {
+ double g_now = 1.128379167095513 * exp(- t * t);
+ gx_erf[i] = g_now - g_last;
+ g_last = g_now;
+ }
+ e_last = e_now;
+ }
+ }
+
+};
+
+} // end namespace itk
+
+// Define instantiation macro for this template.
+#define ITK_TEMPLATE_GaussianInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \
+ _(2(class EXPORT GaussianInterpolateImageFunction< ITK_TEMPLATE_2 x >)) \
+ namespace Templates { typedef GaussianInterpolateImageFunction< ITK_TEMPLATE_2 x > \
+ GaussianInterpolateImageFunction##y; } \
+}
+
+#endif
diff --git a/Submodules/greedy/src/bk/itkOptLinearInterpolateImageFunction.h b/Submodules/greedy/src/bk/itkOptLinearInterpolateImageFunction.h
new file mode 100755
index 0000000..039535a
--- /dev/null
+++ b/Submodules/greedy/src/bk/itkOptLinearInterpolateImageFunction.h
@@ -0,0 +1,520 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkOptLinearInterpolateImageFunction.h,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:19 $
+ Version: $Revision: 1.11 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkOptLinearInterpolateImageFunction_h
+#define __itkOptLinearInterpolateImageFunction_h
+
+#include "itkInterpolateImageFunction.h"
+
+namespace itk
+{
+
+/** \class LinearInterpolateImageFunction
+ * \brief Linearly interpolate an image at specified positions.
+ *
+ * LinearInterpolateImageFunction linearly interpolates image intensity at
+ * a non-integer pixel position. This class is templated
+ * over the input image type and the coordinate representation type
+ * (e.g. float or double).
+ *
+ * This function works for N-dimensional images.
+ *
+ * \warning This function work only for images with scalar pixel
+ * types. For vector images use VectorLinearInterpolateImageFunction.
+ *
+ * \sa VectorLinearInterpolateImageFunction
+ *
+ * \ingroup ImageFunctions ImageInterpolators
+ */
+template <class TInputImage, class TCoordRep = double>
+class ITK_EXPORT LinearInterpolateImageFunction :
+ public InterpolateImageFunction<TInputImage,TCoordRep>
+{
+public:
+ /** Standard class typedefs. */
+ typedef LinearInterpolateImageFunction Self;
+ typedef InterpolateImageFunction<TInputImage,TCoordRep> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(LinearInterpolateImageFunction, InterpolateImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** OutputType typedef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** InputImageType typedef support. */
+ typedef typename Superclass::InputImageType InputImageType;
+
+ /** InputPixelType typedef support. */
+ typedef typename Superclass::InputPixelType InputPixelType;
+
+ /** RealType typedef support. */
+ typedef typename Superclass::RealType RealType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(ImageDimension, unsigned int,Superclass::ImageDimension);
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Evaluate the function at a ContinuousIndex position
+ *
+ * Returns the linearly interpolated image intensity at a
+ * specified point position. No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual inline OutputType EvaluateAtContinuousIndex( const
+ ContinuousIndexType &
+ index ) const
+ {
+ return this->EvaluateOptimized( Dispatch< ImageDimension >(), index );
+ }
+
+protected:
+ LinearInterpolateImageFunction();
+ ~LinearInterpolateImageFunction();
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+private:
+ LinearInterpolateImageFunction( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ /** Number of neighbors used in the interpolation */
+ static const unsigned long m_Neighbors;
+
+ struct DispatchBase {};
+ template< unsigned int > struct Dispatch : DispatchBase {};
+
+ inline OutputType EvaluateOptimized( const Dispatch<0> &,
+ const ContinuousIndexType & index) const
+ {
+ return 0;
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<1>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+
+ const double distance = index[0] - static_cast<double>(basei[0]);
+
+ const RealType val0 = this->GetInputImage()->GetPixel( basei );
+
+ if(distance <= 0.)
+ {
+ return( static_cast<OutputType>( val0 ) );
+ }
+
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val0 ) );
+ }
+ const RealType val1 = this->GetInputImage()->GetPixel( basei );
+
+ return( static_cast<OutputType>( val0 + ( val1 - val0 ) * distance ) );
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<2>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+ const double distance0 = index[0] - static_cast<double>(basei[0]);
+
+ basei[1] = Math::Floor<IndexValueType>(index[1]);
+ if( basei[1] < this->m_StartIndex[1] )
+ {
+ basei[1] = this->m_StartIndex[1];
+ }
+ const double distance1 = index[1] - static_cast<double>(basei[1]);
+
+
+ const RealType val00 = this->GetInputImage()->GetPixel( basei );
+ if(distance0 <= 0. && distance1 <= 0.)
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ else if(distance1 <= 0.) // if they have the same "y"
+ {
+ ++basei[0]; // then interpolate across "x"
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const RealType val10 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val10 - val00) * distance0) );
+ }
+ else if(distance0 <= 0.) // if they have the same "x"
+ {
+ ++basei[1]; // then interpolate across "y"
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const RealType val01 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val01 - val00) * distance1) );
+ }
+ else // interpolate across "xy"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "y"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const RealType val01 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val01 - val00) * distance1) );
+ }
+ const RealType val10 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx0 = val00 + (val10 - val00) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx0 ) );
+ }
+ const RealType val11 = this->GetInputImage()->GetPixel( basei );
+ --basei[0];
+ const RealType val01 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx1 = val01 + (val11 - val01) * distance0;
+
+ return( static_cast<OutputType>( valx0 + (valx1-valx0) * distance1 ) );
+ }
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<3>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+ const double distance0 = index[0] - static_cast<double>(basei[0]);
+
+ basei[1] = Math::Floor<IndexValueType>(index[1]);
+ if( basei[1] < this->m_StartIndex[1] )
+ {
+ basei[1] = this->m_StartIndex[1];
+ }
+ const double distance1 = index[1] - static_cast<double>(basei[1]);
+
+ basei[2] = Math::Floor<IndexValueType>(index[2]);
+ if( basei[2] < this->m_StartIndex[2] )
+ {
+ basei[2] = this->m_StartIndex[2];
+ }
+ const double distance2 = index[2] - static_cast<double>(basei[2]);
+
+ if(distance0<=0. && distance1<=0. && distance2<=0.)
+ {
+ return( static_cast<OutputType>( this->GetInputImage()->GetPixel( basei ) ) );
+ }
+
+
+ const RealType val000 = this->GetInputImage()->GetPixel( basei );
+
+ if(distance2 <= 0.)
+ {
+ if(distance1 <= 0.) // interpolate across "x"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const RealType val100 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val100-val000) * distance0 );
+ }
+ else if(distance0 <= 0.) // interpolate across "y"
+ {
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const RealType val010 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val010-val000) * distance1 );
+ }
+ else // interpolate across "xy"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "y"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const RealType val010 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val010-val000) * distance1 );
+ }
+ const RealType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const RealType val110 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const RealType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx10 = val010 + (val110-val010) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx10-valx00) * distance1 );
+ }
+ }
+ else
+ {
+ if(distance1 <= 0.)
+ {
+ if(distance0 <= 0.) // interpolate across "z"
+ {
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ else // interpolate across "xz"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "z"
+ {
+ --basei[0];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const RealType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const RealType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx01 = val001 + (val101-val001) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx01-valx00) * distance2 );
+ }
+ }
+ else if(distance0 <= 0.) // interpolate across "yz"
+ {
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "z"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const RealType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType val0x0 = val000 + (val010-val000) * distance1;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "y"
+ {
+ return( static_cast<OutputType>( val0x0 ) );
+ }
+ const RealType val011 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType val0x1 = val001 + (val011-val001) * distance1;
+
+ return static_cast<OutputType>( val0x0 + (val0x1-val0x0) * distance2 );
+ }
+ else // interpolate across "xyz"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "yz"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "z"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const RealType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType val0x0 = val000 + (val010-val000) * distance1;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "y"
+ {
+ return( static_cast<OutputType>( val0x0 ) );
+ }
+ const RealType val011 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType val0x1 = val001 + (val011-val001) * distance1;
+
+ return static_cast<OutputType>( val0x0 + (val0x1-val0x0) * distance2 );
+ }
+ const RealType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "xz"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const RealType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx01 = val001 + (val101-val001) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx01-valx00) * distance2 );
+ }
+ const RealType val110 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const RealType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx10 = val010 + (val110-val010) * distance0;
+
+ const RealType valxx0 = valx00 + (valx10-valx00) * distance1;
+
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "xy"
+ {
+ return( static_cast<OutputType>( valxx0 ) );
+ }
+ const RealType val011 = this->GetInputImage()->GetPixel( basei );
+
+ ++basei[0];
+ const RealType val111 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const RealType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const RealType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const RealType valx01 = val001 + (val101-val001) * distance0;
+ const RealType valx11 = val011 + (val111-val011) * distance0;
+ const RealType valxx1 = valx01 + (valx11-valx01) * distance1;
+
+ return( static_cast<OutputType>( valxx0 + (valxx1-valxx0) * distance2 ) );
+ }
+ }
+ }
+
+ inline OutputType EvaluateOptimized( const DispatchBase &,
+ const ContinuousIndexType & index) const
+ {
+ return this->EvaluateUnoptimized( index );
+ }
+
+ virtual inline OutputType EvaluateUnoptimized(
+ const ContinuousIndexType & index) const;
+};
+
+} // end namespace itk
+
+// Define instantiation macro for this template.
+#define ITK_TEMPLATE_LinearInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \
+ _(2(class EXPORT LinearInterpolateImageFunction< ITK_TEMPLATE_2 x >)) \
+ namespace Templates { typedef LinearInterpolateImageFunction< ITK_TEMPLATE_2 x > \
+ LinearInterpolateImageFunction##y; } \
+ }
+
+#if ITK_TEMPLATE_EXPLICIT
+# include "Templates/itkLinearInterpolateImageFunction+-.h"
+#endif
+
+#if ITK_TEMPLATE_TXX
+# include "itkOptLinearInterpolateImageFunction.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/bk/itkOptLinearInterpolateImageFunction.txx b/Submodules/greedy/src/bk/itkOptLinearInterpolateImageFunction.txx
new file mode 100755
index 0000000..91d9e50
--- /dev/null
+++ b/Submodules/greedy/src/bk/itkOptLinearInterpolateImageFunction.txx
@@ -0,0 +1,160 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkOptLinearInterpolateImageFunction.txx,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:19 $
+ Version: $Revision: 1.9 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkOptLinearInterpolateImageFunction_txx
+#define __itkOptLinearInterpolateImageFunction_txx
+
+#include "itkOptLinearInterpolateImageFunction.h"
+
+#include "vnl/vnl_math.h"
+
+namespace itk
+{
+
+/**
+ * Define the number of neighbors
+ */
+template<class TInputImage, class TCoordRep>
+const unsigned long
+LinearInterpolateImageFunction< TInputImage, TCoordRep >
+::m_Neighbors = 1 << TInputImage::ImageDimension;
+
+
+/**
+ * Constructor
+ */
+template<class TInputImage, class TCoordRep>
+LinearInterpolateImageFunction< TInputImage, TCoordRep >
+::LinearInterpolateImageFunction()
+{
+}
+
+template<class TInputImage, class TCoordRep>
+LinearInterpolateImageFunction< TInputImage, TCoordRep >
+::~LinearInterpolateImageFunction()
+{
+}
+
+/**
+ * PrintSelf
+ */
+template<class TInputImage, class TCoordRep>
+void
+LinearInterpolateImageFunction< TInputImage, TCoordRep >
+::PrintSelf(std::ostream& os, Indent indent) const
+{
+ this->Superclass::PrintSelf(os,indent);
+}
+
+
+/**
+ * Evaluate at image index position
+ */
+template<class TInputImage, class TCoordRep>
+typename LinearInterpolateImageFunction< TInputImage, TCoordRep >
+::OutputType
+LinearInterpolateImageFunction< TInputImage, TCoordRep >
+::EvaluateUnoptimized( const ContinuousIndexType& index) const
+{
+ unsigned int dim; // index over dimension
+
+ /**
+ * Compute base index = closet index below point
+ * Compute distance from point to base index
+ */
+ IndexType baseIndex;
+ double distance[ImageDimension];
+
+ for( dim = 0; dim < ImageDimension; dim++ )
+ {
+ baseIndex[dim] = Math::Floor<IndexValueType>( index[dim] );
+ distance[dim] = index[dim] - static_cast< double >( baseIndex[dim] );
+ }
+
+ /**
+ * Interpolated value is the weighted sum of each of the surrounding
+ * neighbors. The weight for each neighbor is the fraction overlap
+ * of the neighbor pixel with respect to a pixel centered on point.
+ */
+ InputPixelType value;
+ value.Fill(NumericTraits<RealType>::Zero);
+
+ typedef typename NumericTraits<InputPixelType>::ScalarRealType ScalarRealType;
+ ScalarRealType totalOverlap = NumericTraits<ScalarRealType>::Zero;
+
+ for( unsigned int counter = 0; counter < m_Neighbors; counter++ )
+ {
+
+ double overlap = 1.0; // fraction overlap
+ unsigned int upper = counter; // each bit indicates upper/lower neighbour
+ IndexType neighIndex;
+
+ // get neighbor index and overlap fraction
+ for( dim = 0; dim < ImageDimension; dim++ )
+ {
+
+ if ( upper & 1 )
+ {
+ neighIndex[dim] = baseIndex[dim] + 1;
+#ifdef ITK_USE_CENTERED_PIXEL_COORDINATES_CONSISTENTLY
+ // Take care of the case where the pixel is just
+ // in the outer upper boundary of the image grid.
+ if( neighIndex[dim] > this->m_EndIndex[dim] )
+ {
+ neighIndex[dim] = this->m_EndIndex[dim];
+ }
+#endif
+ overlap *= distance[dim];
+ }
+ else
+ {
+ neighIndex[dim] = baseIndex[dim];
+#ifdef ITK_USE_CENTERED_PIXEL_COORDINATES_CONSISTENTLY
+ // Take care of the case where the pixel is just
+ // in the outer lower boundary of the image grid.
+ if( neighIndex[dim] < this->m_StartIndex[dim] )
+ {
+ neighIndex[dim] = this->m_StartIndex[dim];
+ }
+#endif
+ overlap *= 1.0 - distance[dim];
+ }
+
+ upper >>= 1;
+
+ }
+
+ // get neighbor value only if overlap is not zero
+ if( overlap )
+ {
+ value += static_cast<InputPixelType>( this->GetInputImage()->GetPixel( neighIndex ) ) * overlap;
+ totalOverlap += overlap;
+ }
+
+ if( totalOverlap == 1.0 )
+ {
+ // finished
+ break;
+ }
+
+ }
+
+ return ( static_cast<OutputType>( value ) );
+}
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/greedy/src/bk/itkOptVectorLinearInterpolateImageFunction.h b/Submodules/greedy/src/bk/itkOptVectorLinearInterpolateImageFunction.h
new file mode 100755
index 0000000..d68fef8
--- /dev/null
+++ b/Submodules/greedy/src/bk/itkOptVectorLinearInterpolateImageFunction.h
@@ -0,0 +1,520 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkOptVectorLinearInterpolateImageFunction.h,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:19 $
+ Version: $Revision: 1.11 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkOptVectorLinearInterpolateImageFunction_h
+#define __itkOptVectorLinearInterpolateImageFunction_h
+
+#include "itkVectorInterpolateImageFunction.h"
+
+namespace itk
+{
+
+/** \class LinearInterpolateImageFunction
+ * \brief Linearly interpolate an image at specified positions.
+ *
+ * LinearInterpolateImageFunction linearly interpolates image intensity at
+ * a non-integer pixel position. This class is templated
+ * over the input image type and the coordinate representation type
+ * (e.g. float or double).
+ *
+ * This function works for N-dimensional images.
+ *
+ * \warning This function work only for images with scalar pixel
+ * types. For vector images use VectorLinearInterpolateImageFunction.
+ *
+ * \sa VectorLinearInterpolateImageFunction
+ *
+ * \ingroup ImageFunctions ImageInterpolators
+ */
+template <class TInputImage, class TCoordRep = double>
+class ITK_EXPORT OptVectorLinearInterpolateImageFunction :
+ public VectorInterpolateImageFunction<TInputImage,TCoordRep>
+{
+public:
+ /** Standard class typedefs. */
+ typedef OptVectorLinearInterpolateImageFunction Self;
+ typedef VectorInterpolateImageFunction<TInputImage,TCoordRep> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(OptVectorLinearInterpolateImageFunction, VectorInterpolateImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** OutputType typedef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** InputImageType typedef support. */
+ typedef typename Superclass::InputImageType InputImageType;
+
+ /** InputPixelType typedef support. */
+ typedef typename Superclass::InputPixelType InputPixelType;
+
+ /** RealType typedef support. */
+ typedef typename Superclass::RealType RealType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(ImageDimension, unsigned int,Superclass::ImageDimension);
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Evaluate the function at a ContinuousIndex position
+ *
+ * Returns the linearly interpolated image intensity at a
+ * specified point position. No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual inline OutputType EvaluateAtContinuousIndex( const
+ ContinuousIndexType &
+ index ) const
+ {
+ return this->EvaluateOptimized( Dispatch< ImageDimension >(), index );
+ }
+
+protected:
+ OptVectorLinearInterpolateImageFunction();
+ ~OptVectorLinearInterpolateImageFunction();
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+private:
+ OptVectorLinearInterpolateImageFunction( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ /** Number of neighbors used in the interpolation */
+ static const unsigned long m_Neighbors;
+
+ struct DispatchBase {};
+ template< unsigned int > struct Dispatch : DispatchBase {};
+
+ inline OutputType EvaluateOptimized( const Dispatch<0> &,
+ const ContinuousIndexType & index) const
+ {
+ return 0;
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<1>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+
+ const double distance = index[0] - static_cast<double>(basei[0]);
+
+ const InputPixelType val0 = this->GetInputImage()->GetPixel( basei );
+
+ if(distance <= 0.)
+ {
+ return( static_cast<OutputType>( val0 ) );
+ }
+
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val0 ) );
+ }
+ const InputPixelType val1 = this->GetInputImage()->GetPixel( basei );
+
+ return( static_cast<OutputType>( val0 + ( val1 - val0 ) * distance ) );
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<2>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+ const double distance0 = index[0] - static_cast<double>(basei[0]);
+
+ basei[1] = Math::Floor<IndexValueType>(index[1]);
+ if( basei[1] < this->m_StartIndex[1] )
+ {
+ basei[1] = this->m_StartIndex[1];
+ }
+ const double distance1 = index[1] - static_cast<double>(basei[1]);
+
+
+ const InputPixelType val00 = this->GetInputImage()->GetPixel( basei );
+ if(distance0 <= 0. && distance1 <= 0.)
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ else if(distance1 <= 0.) // if they have the same "y"
+ {
+ ++basei[0]; // then interpolate across "x"
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const InputPixelType val10 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val10 - val00) * distance0) );
+ }
+ else if(distance0 <= 0.) // if they have the same "x"
+ {
+ ++basei[1]; // then interpolate across "y"
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const InputPixelType val01 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val01 - val00) * distance1) );
+ }
+ else // interpolate across "xy"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "y"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const InputPixelType val01 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val01 - val00) * distance1) );
+ }
+ const InputPixelType val10 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx0 = val00 + (val10 - val00) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx0 ) );
+ }
+ const InputPixelType val11 = this->GetInputImage()->GetPixel( basei );
+ --basei[0];
+ const InputPixelType val01 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx1 = val01 + (val11 - val01) * distance0;
+
+ return( static_cast<OutputType>( valx0 + (valx1-valx0) * distance1 ) );
+ }
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<3>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+ const double distance0 = index[0] - static_cast<double>(basei[0]);
+
+ basei[1] = Math::Floor<IndexValueType>(index[1]);
+ if( basei[1] < this->m_StartIndex[1] )
+ {
+ basei[1] = this->m_StartIndex[1];
+ }
+ const double distance1 = index[1] - static_cast<double>(basei[1]);
+
+ basei[2] = Math::Floor<IndexValueType>(index[2]);
+ if( basei[2] < this->m_StartIndex[2] )
+ {
+ basei[2] = this->m_StartIndex[2];
+ }
+ const double distance2 = index[2] - static_cast<double>(basei[2]);
+
+ if(distance0<=0. && distance1<=0. && distance2<=0.)
+ {
+ return( static_cast<OutputType>( this->GetInputImage()->GetPixel( basei ) ) );
+ }
+
+
+ const InputPixelType val000 = this->GetInputImage()->GetPixel( basei );
+
+ if(distance2 <= 0.)
+ {
+ if(distance1 <= 0.) // interpolate across "x"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val100-val000) * distance0 );
+ }
+ else if(distance0 <= 0.) // interpolate across "y"
+ {
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val010-val000) * distance1 );
+ }
+ else // interpolate across "xy"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "y"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val010-val000) * distance1 );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const InputPixelType val110 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx10 = val010 + (val110-val010) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx10-valx00) * distance1 );
+ }
+ }
+ else
+ {
+ if(distance1 <= 0.)
+ {
+ if(distance0 <= 0.) // interpolate across "z"
+ {
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ else // interpolate across "xz"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "z"
+ {
+ --basei[0];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const InputPixelType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx01 = val001 + (val101-val001) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx01-valx00) * distance2 );
+ }
+ }
+ else if(distance0 <= 0.) // interpolate across "yz"
+ {
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "z"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x0 = val000 + (val010-val000) * distance1;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "y"
+ {
+ return( static_cast<OutputType>( val0x0 ) );
+ }
+ const InputPixelType val011 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x1 = val001 + (val011-val001) * distance1;
+
+ return static_cast<OutputType>( val0x0 + (val0x1-val0x0) * distance2 );
+ }
+ else // interpolate across "xyz"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "yz"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "z"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x0 = val000 + (val010-val000) * distance1;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "y"
+ {
+ return( static_cast<OutputType>( val0x0 ) );
+ }
+ const InputPixelType val011 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x1 = val001 + (val011-val001) * distance1;
+
+ return static_cast<OutputType>( val0x0 + (val0x1-val0x0) * distance2 );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "xz"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const InputPixelType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx01 = val001 + (val101-val001) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx01-valx00) * distance2 );
+ }
+ const InputPixelType val110 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx10 = val010 + (val110-val010) * distance0;
+
+ const InputPixelType valxx0 = valx00 + (valx10-valx00) * distance1;
+
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "xy"
+ {
+ return( static_cast<OutputType>( valxx0 ) );
+ }
+ const InputPixelType val011 = this->GetInputImage()->GetPixel( basei );
+
+ ++basei[0];
+ const InputPixelType val111 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const InputPixelType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx01 = val001 + (val101-val001) * distance0;
+ const InputPixelType valx11 = val011 + (val111-val011) * distance0;
+ const InputPixelType valxx1 = valx01 + (valx11-valx01) * distance1;
+
+ return( static_cast<OutputType>( valxx0 + (valxx1-valxx0) * distance2 ) );
+ }
+ }
+ }
+
+ inline OutputType EvaluateOptimized( const DispatchBase &,
+ const ContinuousIndexType & index) const
+ {
+ return this->EvaluateUnoptimized( index );
+ }
+
+ virtual inline OutputType EvaluateUnoptimized(
+ const ContinuousIndexType & index) const;
+};
+
+} // end namespace itk
+
+// Define instantiation macro for this template.
+#define ITK_TEMPLATE_OptVectorLinearInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \
+ _(2(class EXPORT OptVectorLinearInterpolateImageFunction< ITK_TEMPLATE_2 x >)) \
+ namespace Templates { typedef OptVectorLinearInterpolateImageFunction< ITK_TEMPLATE_2 x > \
+ OptVectorLinearInterpolateImageFunction##y; } \
+ }
+
+#if ITK_TEMPLATE_EXPLICIT
+# include "Templates/itkOptVectorLinearInterpolateImageFunction+-.h"
+#endif
+
+#if ITK_TEMPLATE_TXX
+# include "itkOptVectorLinearInterpolateImageFunction.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/bk/itkOptVectorLinearInterpolateImageFunction.txx b/Submodules/greedy/src/bk/itkOptVectorLinearInterpolateImageFunction.txx
new file mode 100755
index 0000000..337ce31
--- /dev/null
+++ b/Submodules/greedy/src/bk/itkOptVectorLinearInterpolateImageFunction.txx
@@ -0,0 +1,160 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkOptVectorLinearInterpolateImageFunction.txx,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:19 $
+ Version: $Revision: 1.9 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkOptVectorLinearInterpolateImageFunction_txx
+#define __itkOptVectorLinearInterpolateImageFunction_txx
+
+#include "itkOptVectorLinearInterpolateImageFunction.h"
+
+#include "vnl/vnl_math.h"
+
+namespace itk
+{
+
+/**
+ * Define the number of neighbors
+ */
+template<class TInputImage, class TCoordRep>
+const unsigned long
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::m_Neighbors = 1 << TInputImage::ImageDimension;
+
+
+/**
+ * Constructor
+ */
+template<class TInputImage, class TCoordRep>
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::OptVectorLinearInterpolateImageFunction()
+{
+}
+
+template<class TInputImage, class TCoordRep>
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::~OptVectorLinearInterpolateImageFunction()
+{
+}
+
+/**
+ * PrintSelf
+ */
+template<class TInputImage, class TCoordRep>
+void
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::PrintSelf(std::ostream& os, Indent indent) const
+{
+ this->Superclass::PrintSelf(os,indent);
+}
+
+
+/**
+ * Evaluate at image index position
+ */
+template<class TInputImage, class TCoordRep>
+typename OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::OutputType
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::EvaluateUnoptimized( const ContinuousIndexType& index) const
+{
+ unsigned int dim; // index over dimension
+
+ /**
+ * Compute base index = closet index below point
+ * Compute distance from point to base index
+ */
+ IndexType baseIndex;
+ double distance[ImageDimension];
+
+ for( dim = 0; dim < ImageDimension; dim++ )
+ {
+ baseIndex[dim] = Math::Floor<IndexValueType>( index[dim] );
+ distance[dim] = index[dim] - static_cast< double >( baseIndex[dim] );
+ }
+
+ /**
+ * Interpolated value is the weighted sum of each of the surrounding
+ * neighbors. The weight for each neighbor is the fraction overlap
+ * of the neighbor pixel with respect to a pixel centered on point.
+ */
+ InputPixelType value = NumericTraits<RealType>::Zero;
+
+ typedef typename NumericTraits<InputPixelType>::ScalarRealType ScalarRealType;
+ ScalarRealType totalOverlap = NumericTraits<ScalarRealType>::Zero;
+
+ for( unsigned int counter = 0; counter < m_Neighbors; counter++ )
+ {
+
+ double overlap = 1.0; // fraction overlap
+ unsigned int upper = counter; // each bit indicates upper/lower neighbour
+ IndexType neighIndex;
+
+ // get neighbor index and overlap fraction
+ for( dim = 0; dim < ImageDimension; dim++ )
+ {
+
+ if ( upper & 1 )
+ {
+ neighIndex[dim] = baseIndex[dim] + 1;
+#ifdef ITK_USE_CENTERED_PIXEL_COORDINATES_CONSISTENTLY
+ // Take care of the case where the pixel is just
+ // in the outer upper boundary of the image grid.
+ if( neighIndex[dim] > this->m_EndIndex[dim] )
+ {
+ neighIndex[dim] = this->m_EndIndex[dim];
+ }
+#endif
+ overlap *= distance[dim];
+ }
+ else
+ {
+ neighIndex[dim] = baseIndex[dim];
+#ifdef ITK_USE_CENTERED_PIXEL_COORDINATES_CONSISTENTLY
+ // Take care of the case where the pixel is just
+ // in the outer lower boundary of the image grid.
+ if( neighIndex[dim] < this->m_StartIndex[dim] )
+ {
+ neighIndex[dim] = this->m_StartIndex[dim];
+ }
+#endif
+ overlap *= 1.0 - distance[dim];
+ }
+
+ upper >>= 1;
+
+ }
+
+ // get neighbor value only if overlap is not zero
+ if( overlap )
+ {
+ InputPixelType delta = static_cast<InputPixelType>( this->GetInputImage()->GetPixel( neighIndex ) ) * overlap;
+ value = value + delta;
+ totalOverlap += overlap;
+ }
+
+ if( totalOverlap == 1.0 )
+ {
+ // finished
+ break;
+ }
+
+ }
+
+ return ( static_cast<OutputType>( value ) );
+}
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/greedy/src/bk/lddmm_common.h b/Submodules/greedy/src/bk/lddmm_common.h
new file mode 100644
index 0000000..428898b
--- /dev/null
+++ b/Submodules/greedy/src/bk/lddmm_common.h
@@ -0,0 +1,10 @@
+#ifndef _LDDMM_COMMON_H_
+#define _LDDMM_COMMON_H_
+
+typedef unsigned int uint;
+
+typedef double myreal;
+
+// #define ITK_MANUAL_INSTANTIATION 1
+
+#endif
diff --git a/Submodules/greedy/src/bk/lddmm_data.cxx b/Submodules/greedy/src/bk/lddmm_data.cxx
new file mode 100644
index 0000000..5a584cd
--- /dev/null
+++ b/Submodules/greedy/src/bk/lddmm_data.cxx
@@ -0,0 +1,871 @@
+#include "lddmm_data.h"
+#include "itkImageRegionIterator.h"
+#include "SimpleWarpImageFilter.h"
+#include "itkNumericTraitsCovariantVectorPixel.h"
+#include "itkOptVectorLinearInterpolateImageFunction.h"
+#include "itkLinearInterpolateImageFunction.h"
+#include "itkAddImageFilter.h"
+#include "itkSubtractImageFilter.h"
+#include "itkMultiplyImageFilter.h"
+#include "itkGradientImageFilter.h"
+#include "itkUnaryFunctorImageFilter.h"
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include "itkVectorImage.h"
+#include "itkDisplacementFieldJacobianDeterminantFilter.h"
+#include "itkSmoothingRecursiveGaussianImageFilter.h"
+#include "itkDiscreteGaussianImageFilter.h"
+#include "itkMinimumMaximumImageFilter.h"
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::alloc_vf(VelocityField &vf, uint nt, ImageBaseType *ref)
+{
+ vf.resize(nt);
+ for(uint i = 0; i < nt; i++)
+ alloc_vimg(vf[i], ref);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::alloc_vimg(VectorImagePointer &img, ImageBaseType *ref)
+{
+ img = VectorImageType::New();
+ img->SetRegions(ref->GetBufferedRegion());
+ img->CopyInformation(ref);
+ img->Allocate();
+ img->FillBuffer(Vec(0.0));
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::alloc_img(ImagePointer &img, ImageBaseType *ref)
+{
+ img = ImageType::New();
+ img->SetRegions(ref->GetBufferedRegion());
+ img->CopyInformation(ref);
+ img->Allocate();
+ img->FillBuffer(0.0);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::init(LDDMMData<TFloat, VDim> &p,
+ ImageType *fix, ImageType *mov,
+ uint nt, double alpha, double gamma, double sigma)
+{
+ p.fix = fix;
+ p.mov = mov;
+ p.alpha = alpha;
+ p.sigma = sigma;
+ p.gamma = gamma;
+ p.nt = nt;
+ p.dt = 1.0 / (nt - 1.0);
+ p.sigma_sq = sigma * sigma;
+
+ // Initialize N and R
+ p.r = fix->GetBufferedRegion();
+ p.nv = fix->GetBufferedRegion().GetNumberOfPixels();
+ for(uint i = 0; i < VDim; i++)
+ p.n[i] = p.r.GetSize()[i];
+
+ // Initialize the velocity fields
+ alloc_vf(p.v, nt, fix);
+ alloc_vf(p.a, nt, fix);
+ alloc_vf(p.f, nt, fix);
+
+ // Initialize kernel terms
+ alloc_img(p.f_kernel, fix);
+ alloc_img(p.f_kernel_sq, fix);
+
+ // Compute these images
+ ImageIterator it(p.f_kernel, p.r), itsq(p.f_kernel_sq, p.r);
+ for(; !it.IsAtEnd(); ++it, ++itsq)
+ {
+ TFloat val = 0.0;
+ for(uint j = 0; j < VDim; j++)
+ val += 1.0 - cos(it.GetIndex()[j] * 2.0 * vnl_math::pi / p.n[j]);
+ it.Set(p.gamma + 2.0 * p.alpha * p.nv * val);
+ itsq.Set(it.Get() * it.Get());
+ }
+
+ // Initialize temporary vector field
+ alloc_vimg(p.vtmp, fix);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::interp_vimg(
+ VectorImageType *data, VectorImageType *field,
+ TFloat def_scale, VectorImageType *out)
+{
+ // Create a warp filter
+ typedef itk::SimpleWarpImageFilter<
+ VectorImageType, VectorImageType, VectorImageType, TFloat> WarpFilterType;
+ typename WarpFilterType::Pointer flt = WarpFilterType::New();
+
+ // Create an interpolation function
+ typedef itk::OptVectorLinearInterpolateImageFunction<
+ VectorImageType, TFloat> InterpType;
+ typename InterpType::Pointer func = InterpType::New();
+
+ // Graft output of the warp filter
+ flt->GraftOutput(out);
+
+ // Set inputs
+ flt->SetInput(data);
+ flt->SetInterpolator(func.GetPointer());
+ flt->SetDeformationField(field);
+ flt->SetDeformationScaling(def_scale);
+ flt->Update();
+
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::interp_img(ImageType *data, VectorImageType *field, ImageType *out)
+{
+ // Create a warp filter
+ typedef itk::SimpleWarpImageFilter<
+ ImageType, ImageType, VectorImageType, TFloat> WarpFilterType;
+ typename WarpFilterType::Pointer flt = WarpFilterType::New();
+
+ // Create an interpolation function
+ typedef itk::LinearInterpolateImageFunction<ImageType, TFloat> InterpType;
+ typename InterpType::Pointer func = InterpType::New();
+
+ // Graft output of the warp filter
+ flt->GraftOutput(out);
+
+ // Set inputs
+ flt->SetInput(data);
+ flt->SetInterpolator(func.GetPointer());
+ flt->SetDeformationField(field);
+ flt->SetDeformationScaling(1.0);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_add_in_place(VectorImagePointer &trg, VectorImageType *a)
+{
+ typedef itk::AddImageFilter<VectorImageType> AddFilter;
+ typename AddFilter::Pointer flt = AddFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->InPlaceOn();
+ flt->Update();
+ trg = flt->GetOutput();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_subtract_in_place(VectorImagePointer &trg, VectorImageType *a)
+{
+ typedef itk::SubtractImageFilter<VectorImageType> SubtractFilter;
+ typename SubtractFilter::Pointer flt = SubtractFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->InPlaceOn();
+ flt->Update();
+ trg = flt->GetOutput();
+}
+
+// Scalar math
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_multiply_in_place(VectorImagePointer &trg, ImageType *s)
+{
+ typedef itk::MultiplyImageFilter<
+ VectorImageType, ImageType, VectorImageType> MultiplyFilter;
+ typename MultiplyFilter::Pointer flt = MultiplyFilter::New();
+ flt->SetInput1(trg);
+ flt->SetInput2(s);
+ flt->InPlaceOn();
+ flt->Update();
+ trg = flt->GetOutput();
+}
+
+// Scalar math
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_subtract_in_place(ImagePointer &trg, ImageType *a)
+{
+ typedef itk::SubtractImageFilter<ImageType> SubtractFilter;
+ typename SubtractFilter::Pointer flt = SubtractFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->InPlaceOn();
+ flt->Update();
+ trg = flt->GetOutput();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_multiply_in_place(ImagePointer &trg, ImageType *a)
+{
+ typedef itk::MultiplyImageFilter<ImageType> MultiplyFilter;
+ typename MultiplyFilter::Pointer flt = MultiplyFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->InPlaceOn();
+ flt->Update();
+ trg = flt->GetOutput();
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMData<TFloat, VDim>
+::vimg_euclidean_norm_sq(VectorImageType *trg)
+{
+ // Add all voxels in the image
+ double accum = 0.0;
+ typedef itk::ImageRegionIterator<VectorImageType> Iter;
+ for(Iter it(trg, trg->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ for(uint d = 0; d < VDim; d++)
+ accum += it.Value()[d] * it.Value()[d];
+ }
+ return (TFloat) accum;
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMData<TFloat, VDim>
+::img_euclidean_norm_sq(ImageType *trg)
+{
+ // Add all voxels in the image
+ double accum = 0.0;
+ typedef itk::ImageRegionIterator<ImageType> Iter;
+ for(Iter it(trg, trg->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ { accum += it.Value() * it.Value(); }
+ return (TFloat) accum;
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMData<TFloat, VDim>
+::img_voxel_sum(ImageType *trg)
+{
+ // Add all voxels in the image
+ double accum = 0.0;
+ typedef itk::ImageRegionIterator<ImageType> Iter;
+ for(Iter it(trg, trg->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ accum += it.Value();
+ return (TFloat) accum;
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_min_max(ImageType *src, TFloat &out_min, TFloat &out_max)
+{
+ // Add all voxels in the image
+ typedef itk::MinimumMaximumImageFilter<ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(src);
+ filter->Update();
+ out_min = filter->GetMinimum();
+ out_max = filter->GetMaximum();
+}
+
+
+template <class TFloat, uint VDim>
+class VectorScaleFunctor
+{
+public:
+ VectorScaleFunctor() { this->Scale = 1.0; }
+ typedef itk::CovariantVector<TFloat,VDim> Vec;
+
+ Vec operator() (const Vec &x)
+ { return x * Scale; }
+
+ bool operator== (const VectorScaleFunctor<TFloat, VDim> &other)
+ { return Scale == other.Scale; }
+
+ bool operator!= (const VectorScaleFunctor<TFloat, VDim> &other)
+ { return Scale != other.Scale; }
+
+ TFloat Scale;
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_scale_in_place(VectorImagePointer &trg, TFloat s)
+{
+ typedef VectorScaleFunctor<TFloat, VDim> Functor;
+ typedef itk::UnaryFunctorImageFilter<
+ VectorImageType, VectorImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ func.Scale = s;
+ flt->SetFunctor(func);
+ flt->SetInput(trg);
+ flt->InPlaceOn();
+ flt->Update();
+
+ trg = flt->GetOutput();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_scale(VectorImageType*src, TFloat s, VectorImagePointer &trg)
+{
+ typedef VectorScaleFunctor<TFloat, VDim> Functor;
+ typedef itk::UnaryFunctorImageFilter<
+ VectorImageType, VectorImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ func.Scale = s;
+ flt->SetFunctor(func);
+ flt->SetInput(src);
+ flt->InPlaceOff();
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+class VectorScaleAddFunctor
+{
+public:
+ VectorScaleAddFunctor() { this->Scale = 1.0; }
+ typedef itk::CovariantVector<TFloat,VDim> Vec;
+
+ Vec operator() (const Vec &x, const Vec &y)
+ { return x + y * Scale; }
+
+ bool operator== (const VectorScaleAddFunctor<TFloat, VDim> &other)
+ { return Scale == other.Scale; }
+
+ bool operator!= (const VectorScaleAddFunctor<TFloat, VDim> &other)
+ { return Scale != other.Scale; }
+
+ TFloat Scale;
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_add_scaled_in_place(VectorImagePointer &trg, VectorImageType *a, TFloat s)
+{
+ typedef VectorScaleAddFunctor<TFloat, VDim> Functor;
+ typedef itk::BinaryFunctorImageFilter<
+ VectorImageType, VectorImageType, VectorImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ func.Scale = s;
+ flt->SetFunctor(func);
+ flt->SetInput1(trg);
+ flt->SetInput2(a);
+ flt->InPlaceOn();
+ flt->Update();
+
+ trg = flt->GetOutput();
+}
+
+template <class TFloat, uint VDim>
+class VectorDotProduct
+{
+public:
+ VectorDotProduct() {}
+ typedef itk::CovariantVector<TFloat,VDim> Vec;
+
+ TFloat operator() (const Vec &x, const Vec &y)
+ {
+ TFloat dp = 0.0;
+ for(uint d = 0; d < VDim; d++)
+ dp += x[d] * y[d];
+ return dp;
+ }
+
+ bool operator== (const VectorDotProduct<TFloat, VDim> &other)
+ { return true; }
+
+ bool operator!= (const VectorDotProduct<TFloat, VDim> &other)
+ { return false; }
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_euclidean_inner_product(ImagePointer &trg, VectorImageType *a, VectorImageType *b)
+{
+ typedef VectorDotProduct<TFloat, VDim> Functor;
+ typedef itk::BinaryFunctorImageFilter<
+ VectorImageType, VectorImageType, ImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ flt->SetFunctor(func);
+ flt->SetInput1(a);
+ flt->SetInput2(b);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::compute_semi_lagrangean_a()
+{
+ for(uint i = 0; i < nt; i++)
+ {
+ a[i]->FillBuffer(Vec(0.0));
+ for (uint j = 0; j < 5; j++)
+ {
+ interp_vimg(v[i], a[i], -0.5, a[i]);
+ vimg_scale_in_place(a[i], dt);
+ itk::Index<VDim> x;
+ x[0] = 63; x[1] = 63;
+ }
+ }
+
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::integrate_phi_t0()
+{
+ // Go through and compute phi_t0 for each time point
+ for(int m = 0; m < (int) nt; m++)
+ if(m==0)
+ {
+ f[m]->FillBuffer(Vec(0.0));
+ }
+ else
+ {
+ interp_vimg(f[m-1], a[m], -1.0, f[m]);
+ vimg_subtract_in_place(f[m], a[m]);
+ }
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::integrate_phi_t1()
+{
+ for(int m = nt-1; m >= 0; m--)
+ {
+ if(m == (int) nt-1)
+ {
+ f[m]->FillBuffer(Vec(0.0));
+ }
+ else
+ {
+ interp_vimg(f[m+1], a[m], 1.0, f[m]);
+ vimg_add_in_place(f[m], a[m]);
+ }
+ }
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::field_jacobian_det(VectorImageType *vec, ImageType *out)
+{
+ typedef itk::DisplacementFieldJacobianDeterminantFilter<
+ VectorImageType, TFloat, ImageType> Filter;
+ typename Filter::Pointer filter = Filter::New();
+ filter->SetInput(vec);
+ filter->SetUseImageSpacingOff();
+ filter->GraftOutput(out);
+ filter->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::image_gradient(ImageType *src, VectorImageType *grad)
+{
+ // Create a gradient image filter
+ typedef itk::GradientImageFilter<ImageType, TFloat, TFloat> Filter;
+ typename Filter::Pointer flt = Filter::New();
+ flt->SetInput(src);
+ flt->SetUseImageSpacingOff();
+ flt->SetUseImageDirection(false);
+ flt->GraftOutput(grad);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_smooth(ImageType *src, ImageType *trg, double sigma)
+{
+ // typedef itk::SmoothingRecursiveGaussianImageFilter<ImageType, ImageType> Filter;
+ typedef itk::DiscreteGaussianImageFilter<ImageType, ImageType> Filter;
+ typename Filter::Pointer flt = Filter::New();
+ flt->SetInput(src);
+ // flt->SetSigma(sigma);
+ flt->SetVariance(sigma * sigma);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_smooth(VectorImageType *src, VectorImageType *trg, double sigma)
+{
+ /// typedef itk::SmoothingRecursiveGaussianImageFilter<VectorImageType, VectorImageType> Filter;
+ typedef itk::DiscreteGaussianImageFilter<VectorImageType, VectorImageType> Filter;
+ typename Filter::Pointer flt = Filter::New();
+ flt->SetInput(src);
+ // flt->SetSigma(sigma);
+ // flt->GraftOutput(trg);
+ flt->SetVariance(sigma * sigma);
+ flt->Update();
+
+ vimg_write(src, "preshitme.nii.gz");
+ vimg_write(flt->GetOutput(), "shitme.nii.gz");
+}
+
+
+
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_read(const char *fn, ImagePointer &trg)
+{
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(fn);
+ reader->Update();
+ trg = reader->GetOutput();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_write(ImageType *src, const char *fn)
+{
+ typedef itk::ImageFileWriter<ImageType> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetFileName(fn);
+ writer->SetInput(src);
+ writer->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_read(const char *fn, VectorImagePointer &trg)
+{
+ typedef itk::ImageFileReader<VectorImageType> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(fn);
+ reader->Update();
+ trg = reader->GetOutput();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_write(VectorImageType *src, const char *fn)
+{
+ // Cast to vector image type
+ typedef itk::VectorImage<TFloat, VDim> OutputImageType;
+ typename OutputImageType::Pointer output = OutputImageType::New();
+ output->CopyInformation(src);
+ output->SetRegions(src->GetBufferedRegion());
+ output->SetNumberOfComponentsPerPixel(VDim);
+
+ // Override the data pointer
+ output->GetPixelContainer()->SetImportPointer(
+ (TFloat *) src->GetBufferPointer(),
+ VDim * src->GetPixelContainer()->Size(), false);
+
+ // Write the vector data
+ typedef itk::ImageFileWriter<OutputImageType> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetFileName(fn);
+ writer->SetInput(output);
+ writer->Update();
+
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vfield_read(uint nt, const char *fnpat, VelocityField &v)
+{
+ v.clear();
+ for(uint i = 0; i < nt; i++)
+ {
+ char fname[1024];
+ sprintf(fname, fnpat, i);
+ VectorImagePointer vt;
+ vimg_read(fname, vt);
+ v.push_back(vt);
+ }
+}
+
+/* =============================== */
+
+template <class TFloat, uint VDim>
+LDDMMFFTInterface<TFloat, VDim>
+::LDDMMFFTInterface(ImageType *ref)
+{
+ // Work out the data dimensions (large enough, and multiple of four)
+ m_Size = ref->GetBufferedRegion().GetSize();
+ m_Alloc = m_Size;
+ m_Alloc[VDim-1] = 2 * (m_Size[VDim-1] / 2 + 1);
+
+ // Size for calling the plan routines
+ int n[VDim];
+
+ // Get the data dimensions
+ m_AllocSize = 1; m_DataSize = 1;
+ for(uint i = 0; i < VDim; i++)
+ {
+ m_AllocSize *= m_Alloc[i];
+ m_DataSize *= m_Size[i];
+ n[i] = m_Size[i];
+ }
+
+ // Allocate the complex data (real data is packed in the complex data)
+ m_Data = (double *) fftw_malloc(m_AllocSize * sizeof(double));
+
+ // Create plans for forward and inverse transforms
+ m_Plan = fftw_plan_dft_r2c(VDim, n, m_Data, (fftw_complex *) m_Data, FFTW_MEASURE);
+ m_InvPlan = fftw_plan_dft_c2r(VDim, n, (fftw_complex *) m_Data, m_Data, FFTW_MEASURE);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMFFTInterface<TFloat, VDim>
+::convolution_fft(
+ VectorImageType *img, ImageType *kernel_ft, bool inv_kernel,
+ VectorImageType *out)
+{
+ // Pack the data into m_Data. This requires us to skip a few bytes at
+ // the end of each row of data
+ uint nskip = m_Alloc[VDim-1] - m_Size[VDim-1];
+ uint ncopy = m_Size[VDim-1];
+ uint nout = m_Alloc[VDim-1] / 2;
+ uint noutskip = kernel_ft->GetBufferedRegion().GetSize()[VDim-1] - nout;
+ uint nstrides = m_AllocSize / m_Alloc[VDim-1];
+
+ for(uint d = 0; d < VDim; d++)
+ {
+ const Vec *src = img->GetBufferPointer();
+ double *dst = m_Data;
+
+ // Funky loop
+ for(double *end = dst + m_AllocSize; dst < end; dst+=nskip)
+ for(double *rowend = dst + ncopy; dst < rowend; dst++, src++)
+ *dst = (double) (*src)[d];
+
+ // Execute the plan
+ fftw_execute(m_Plan);
+
+ // Multiply or divide the complex values in m_Data by the kernel array
+ fftw_complex *c = (fftw_complex *) m_Data;
+
+ // Scaling factor for producing final result
+ double scale = 1.0 / m_DataSize;
+
+ /*
+ // Precision weirdness (results differ from MATLAB fft in 6th, 7th decimal digit)
+ uint tp = (m_Alloc[VDim-1] / 2) * 6 + 8;
+ printf("Before scaling, value at %d is %12.12lf, %12.12lf\n",
+ tp, c[tp][0], c[tp][1]);
+ printf("Kernel value at %d is %12.12lf\n", 128*6+8, kp[128*6+8]);
+ */
+
+ // Another such loop
+ TFloat *kp = kernel_ft->GetBufferPointer();
+ if(inv_kernel)
+ {
+ for(uint i = 0; i < nstrides; i++)
+ {
+ for(uint j = 0; j < nout; j++)
+ {
+ (*c)[0] /= (*kp);
+ (*c)[1] /= (*kp);
+ c++; kp++;
+ }
+ kp += noutskip;
+ }
+ }
+ else
+ {
+ for(uint i = 0; i < nstrides; i++)
+ {
+ for(uint j = 0; j < nout; j++)
+ {
+ (*c)[0] *= (*kp);
+ (*c)[1] *= (*kp);
+ c++; kp++;
+ }
+ kp += noutskip;
+ }
+ }
+
+ /*
+ fftw_complex *ctest = (fftw_complex *) m_Data;
+ printf("After scaling, value at %d is %12.12lf, %12.12lf\n",
+ tp, ctest[tp][0], ctest[tp][1]);
+ */
+
+ // Inverse transform
+ fftw_execute(m_InvPlan);
+
+ // Copy the results to the output image
+ const double *osrc = m_Data;
+ Vec *odst = out->GetBufferPointer();
+ for(uint i = 0; i < nstrides; i++, osrc+=nskip)
+ for(uint j = 0; j < ncopy; j++, odst++, osrc++)
+ (*odst)[d] = (TFloat) ((*osrc) * scale);
+
+ /*
+ odst = out->GetBufferPointer();
+ printf("Result %12.12lf\n", odst[128*6+8][0]);
+ */
+ }
+
+}
+
+template <class TFloat, uint VDim>
+LDDMMFFTInterface<TFloat, VDim>
+::~LDDMMFFTInterface()
+{
+ fftw_destroy_plan(m_Plan);
+ fftw_destroy_plan(m_InvPlan);
+ fftw_free(m_Data);
+}
+
+template <class TFloat, uint VDim>
+LDDMMImageMatchingObjective<TFloat, VDim>
+::LDDMMImageMatchingObjective(LDDMM &p)
+ : fft(p.fix)
+{
+ // Allocate intermediate datasets
+ LDDMM::alloc_img(Jt0, p.fix);
+ LDDMM::alloc_img(Jt1, p.fix);
+ LDDMM::alloc_img(DetPhit1, p.fix);
+ LDDMM::alloc_vimg(GradJt0, p.fix);
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMImageMatchingObjective<TFloat, VDim>
+::compute_objective_and_gradient(LDDMM &p)
+{
+ // Compute the regularization energy of v. We can use a[0] for temporary storage
+ // e_field = lddmm_vector_field_dot_product(vx, vy, vx, vy, p);
+ TFloat e_field = 0.0;
+ for(uint m = 0; m < p.nt; m++)
+ {
+ // a[0] = Lv[m] .* v[m]
+ fft.convolution_fft(p.v[m], p.f_kernel_sq, false, p.a[0]);
+
+ // We're sticking the inner product in Jt0
+ LDDMM::vimg_euclidean_inner_product(Jt0, p.a[0], p.v[m]);
+ e_field += LDDMM::img_voxel_sum(Jt0) / p.nt;
+ }
+
+ // Compute the 'a' array (for semilagrangean scheme)
+ p.compute_semi_lagrangean_a();
+
+ // Go through and compute phi_t1 for each time point
+ p.integrate_phi_t1();
+
+ // Compute the update for v at each time step
+ for(uint m = 0; m < p.nt; m++)
+ {
+ // Currently, f[m] holds phi_t1[m]. Use it for whatever we need
+ // and then replace with phi_t0[m]
+
+ // TODO: for ft00 and ft11, don't waste time on interpolation
+
+ // Jt1 = lddmm_warp_scalar_field(p.I1, ft1x(:,:,it), ft1y(:,:,it), p);
+ LDDMM::interp_img(p.mov, p.f[m], Jt1);
+
+ // detjac_phi_t1 = lddmm_jacobian_determinant(ft1x(:,:,it), ft1y(:,:,it), p);
+ LDDMM::field_jacobian_det(p.f[m], DetPhit1);
+
+ // Place phi_t0 into the f array
+ if(m==0)
+ {
+ p.f[m]->FillBuffer(typename LDDMM::Vec(0.0));
+ }
+ else
+ {
+ LDDMM::interp_vimg(p.f[m-1], p.a[m], -1.0, p.f[m]);
+ LDDMM::vimg_subtract_in_place(p.f[m], p.a[m]);
+ }
+
+ // Jt0 = lddmm_warp_scalar_field(p.I0, ft0x(:,:,it), ft0y(:,:,it), p);
+ LDDMM::interp_img(p.fix, p.f[m], Jt0);
+
+ // [grad_Jt0_x grad_Jt0_y] = gradient(Jt0);
+ LDDMM::image_gradient(Jt0, GradJt0);
+
+ // pde_rhs_x = detjac_phi_t1 .* (Jt0 - Jt1) .* grad_Jt0_x;
+ // pde_rhs_y = detjac_phi_t1 .* (Jt0 - Jt1) .* grad_Jt0_y;
+
+ // Here we do some small tricks. We want to retain Jt0 because it's the warped
+ // template image, and we want to retain the difference Jt0-Jt1 = (J0-I1) for
+ // calculating the objective at the end.
+ LDDMM::img_subtract_in_place(Jt1, Jt0); // 'Jt1' stores Jt1 - Jt0
+ LDDMM::img_multiply_in_place(DetPhit1, Jt1); // 'DetPhit1' stores (det Phi_t1)(Jt1-Jt0)
+ LDDMM::vimg_multiply_in_place(GradJt0, DetPhit1); // 'GradJt0' stores GradJt0 * (det Phi_t1)(Jt1-Jt0)
+
+ // Solve PDE via FFT convolution
+ // pde_soln_x = ifft2(fft2(pde_rhs_x) ./ p.f_kernel_sq,'symmetric');
+ // pde_soln_y = ifft2(fft2(pde_rhs_y) ./ p.f_kernel_sq,'symmetric');
+ fft.convolution_fft(GradJt0, p.f_kernel_sq, true, GradJt0); // 'GradJt0' stores K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+
+ // dedvx(:,:,it) = dedvx(:,:,it) - 2 * pde_soln_x / p.sigma^2;
+ // dedvy(:,:,it) = dedvy(:,:,it) - 2 * pde_soln_y / p.sigma^2;
+
+ // Store the update in a[m]
+ LDDMM::vimg_scale_in_place(GradJt0, 1.0 / p.sigma_sq); // 'GradJt0' stores 1 / sigma^2 K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+ LDDMM::vimg_add_in_place(GradJt0, p.v[m]); // 'GradJt0' stores v + 1 / sigma^2 K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+ LDDMM::vimg_scale(GradJt0, 2.0, p.a[m]); // p.a[m] holds 2 v + 2 / sigma^2 K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+ }
+
+ // Ok, Jt1 currently contains (Jt1-Jt0), we just need to square it.
+ TFloat e_image = LDDMM::img_euclidean_norm_sq(Jt1) / p.sigma_sq;
+
+ // Return the energy
+ printf(" Energy components: %lf, %lf\n", e_field, e_image);
+ return e_field + e_image;
+}
+
+
+/*
+template class LDDMMData<float, 2>;
+template class LDDMMData<float, 3>;
+template class LDDMMFFTInterface<float, 2>;
+template class LDDMMFFTInterface<float, 3>;
+*/
+
+template class LDDMMData<double, 2>;
+template class LDDMMData<double, 3>;
+template class LDDMMData<double, 4>;
+
+template class LDDMMFFTInterface<myreal, 2>;
+template class LDDMMFFTInterface<myreal, 3>;
+template class LDDMMImageMatchingObjective<myreal, 2>;
+template class LDDMMImageMatchingObjective<myreal, 3>;
+
diff --git a/Submodules/greedy/src/bk/lddmm_data.h b/Submodules/greedy/src/bk/lddmm_data.h
new file mode 100644
index 0000000..23434ff
--- /dev/null
+++ b/Submodules/greedy/src/bk/lddmm_data.h
@@ -0,0 +1,172 @@
+#ifndef _LDDMM_DATA_H_
+#define _LDDMM_DATA_H_
+
+#include <lddmm_common.h>
+#include <itkNumericTraits.h>
+#include <itkImage.h>
+#include <itkImageRegionIteratorWithIndex.h>
+#include <itkCovariantVector.h>
+#include <vnl/vnl_math.h>
+#include <vector>
+
+#include <fftw3.h>
+
+template<class TFloat, uint VDim>
+class LDDMMData
+{
+public:
+ // Image data
+ typedef itk::ImageBase<VDim> ImageBaseType;
+ typedef itk::Image<TFloat, VDim> ImageType;
+ typedef typename ImageType::Pointer ImagePointer;
+ typedef itk::ImageRegionIteratorWithIndex<ImageType> ImageIterator;
+
+ // Vector fields, etc
+ typedef itk::CovariantVector<TFloat, VDim> Vec;
+ typedef itk::Image<Vec, VDim> VectorImageType;
+ typedef typename VectorImageType::Pointer VectorImagePointer;
+ typedef std::vector<VectorImagePointer> VelocityField;
+
+ // Regions, etc
+ typedef itk::ImageRegion<VDim> RegionType;
+
+ // Pointers to the fixed and moving images
+ ImagePointer fix, mov;
+
+ // Fourier space kernels
+ ImagePointer f_kernel, f_kernel_sq;
+
+ // Velocity field pointers (v, phi, a used for semi-lagrange scheme)
+ VelocityField v, f, a;
+
+ // Region for the velocity fields
+ RegionType r;
+
+ // Parameters
+ double alpha, sigma, gamma, dt, sigma_sq;
+
+ // Dimensions
+ uint n[VDim];
+
+ // Number of timesteps, number of voxels
+ uint nt, nv;
+
+ // Allocate a velocity field
+ static void alloc_vf(VelocityField &vf, uint nt, ImageBaseType *ref);
+ static void alloc_img(ImagePointer &img, ImageBaseType *ref);
+ static void alloc_vimg(VectorImagePointer &vimg, ImageBaseType *ref);
+
+ // Initialize LDDMM data
+ static void init(LDDMMData<TFloat, VDim> &,
+ ImageType *fix, ImageType *mov,
+ uint nt, double alpha, double gamma, double sigma);
+
+ // Apply deformation to data
+ static void interp_vimg(
+ VectorImageType *data, VectorImageType *field,
+ TFloat def_scale, VectorImageType *out);
+
+ // Apply deformation to data
+ static void interp_img(ImageType *data, VectorImageType *field, ImageType *out);
+
+ // Take Jacobian of deformation field
+ static void field_jacobian_det(VectorImageType *vec, ImageType *out);
+
+ // Smooth an image in-place
+ static void img_smooth(ImageType *src, ImageType *out, double sigma);
+ static void vimg_smooth(VectorImageType *src, VectorImageType *out, double sigma);
+
+ // Take gradient of an image
+ static void image_gradient(ImageType *src, VectorImageType *grad);
+
+ // Basic math
+ static void vimg_add_in_place(VectorImagePointer &trg, VectorImageType *a);
+ static void vimg_subtract_in_place(VectorImagePointer &trg, VectorImageType *a);
+ static void vimg_scale_in_place(VectorImagePointer &trg, TFloat s);
+
+ // compute trg = trg + s * a
+ static void vimg_add_scaled_in_place(VectorImagePointer &trg, VectorImageType *a, TFloat s);
+
+ static void vimg_scale(VectorImageType *src, TFloat s, VectorImagePointer &trg);
+ static void vimg_multiply_in_place(VectorImagePointer &trg, ImageType *s);
+ static void vimg_euclidean_inner_product(ImagePointer &trg, VectorImageType *a, VectorImageType *b);
+ static TFloat vimg_euclidean_norm_sq(VectorImageType *trg);
+
+ // Scalar math
+ static void img_subtract_in_place(ImagePointer &trg, ImageType *a);
+ static void img_multiply_in_place(ImagePointer &trg, ImageType *a);
+ static TFloat img_euclidean_norm_sq(ImageType *trg);
+ static TFloat img_voxel_sum(ImageType *trg);
+ static void img_min_max(ImageType *src, TFloat &out_min, TFloat &out_max);
+
+ // Some IO methods
+ static void img_read(const char *fn, ImagePointer &trg);
+ static void img_write(ImageType *src, const char *fn);
+ static void vimg_read(const char *fn, VectorImagePointer &trg);
+ static void vimg_write(VectorImageType *src, const char *fn);
+
+ static void vfield_read(uint nt, const char *fnpat, VelocityField &v);
+
+ // Compute a array from v
+ void compute_semi_lagrangean_a();
+
+ // Integrate forward tranform (phi_0_t)
+ void integrate_phi_t0();
+ void integrate_phi_t1();
+
+protected:
+
+ // A vector image for in-place interpolation operations
+ VectorImagePointer vtmp;
+
+};
+
+template <class TFloat, uint VDim>
+class LDDMMFFTInterface
+{
+public:
+ typedef typename LDDMMData<TFloat, VDim>::ImageType ImageType;
+ typedef typename LDDMMData<TFloat, VDim>::VectorImageType VectorImageType;
+ typedef typename LDDMMData<TFloat, VDim>::Vec Vec;
+
+ LDDMMFFTInterface(ImageType *ref);
+ ~LDDMMFFTInterface();
+
+ void convolution_fft(
+ VectorImageType *img, ImageType *kernel_ft, bool inv_kernel,
+ VectorImageType *out);
+
+private:
+
+ // Size of the input array and allocated array (bigger, for in-place math)
+ itk::Size<VDim> m_Size, m_Alloc;
+ uint m_AllocSize, m_DataSize;
+
+ // In-place data array
+ double *m_Data;
+
+ // FFT plan
+ fftw_plan m_Plan, m_InvPlan;
+
+};
+
+// Class for iteratively computing the objective function
+template<class TFloat, uint VDim>
+class LDDMMImageMatchingObjective
+{
+public:
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef LDDMMFFTInterface<TFloat, VDim> FFT;
+
+ LDDMMImageMatchingObjective(LDDMM &p);
+ TFloat compute_objective_and_gradient(LDDMM &p);
+
+ typename LDDMM::ImagePointer Jt0, Jt1, DetPhit1;
+ typename LDDMM::VectorImagePointer GradJt0;
+ FFT fft;
+};
+
+
+
+
+#endif
diff --git a/Submodules/greedy/src/bk/lddmm_main.cxx b/Submodules/greedy/src/bk/lddmm_main.cxx
new file mode 100644
index 0000000..fc395f2
--- /dev/null
+++ b/Submodules/greedy/src/bk/lddmm_main.cxx
@@ -0,0 +1,326 @@
+#include <iostream>
+#include <cstdio>
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+#include <itkImageFileReader.h>
+#include <itkGaussianInterpolateImageFunction.h>
+#include <itkResampleImageFilter.h>
+#include <itkIdentityTransform.h>
+#include <itkShrinkImageFilter.h>
+
+using namespace std;
+
+int usage()
+{
+ printf("lddmm: Paul's LDDMM implementation\n");
+ printf("Usage: \n");
+ printf(" lddmm DIM [options] fixed.nii moving.nii\n");
+ printf(" lddmm DIM --test test_id test_params\n");
+ printf("Options: \n");
+ return -1;
+}
+
+template <class TFloat, uint VDim>
+class Resampler
+{
+public:
+ typedef itk::Image<TFloat, VDim> ImageType;
+
+ Resampler(ImageType *image, uint factor)
+ {
+ m_Filter = FilterType::New();
+ m_Filter->SetInput(image);
+
+ typedef itk::IdentityTransform<TFloat, VDim> TranType;
+ typename TranType::Pointer tran = TranType::New();
+ m_Filter->SetTransform(tran);
+
+ m_Func = FuncType::New();
+ double sigma[VDim];
+ for(uint i = 0; i < VDim; i++)
+ sigma[i] = factor * image->GetSpacing()[i] * 1.2;
+ m_Func->SetParameters(sigma, 4.0);
+
+ m_Filter->SetInterpolator(m_Func);
+
+ m_Shrink = ShrinkType::New();
+ m_Shrink->SetInput(image);
+ m_Shrink->SetShrinkFactors(factor);
+ m_Shrink->Update();
+
+ m_Filter->UseReferenceImageOn();
+ m_Filter->SetReferenceImage(m_Shrink->GetOutput());
+ m_Filter->Update();
+ }
+
+ ImageType *GetResult()
+ { return m_Filter->GetOutput(); }
+private:
+ typedef itk::ShrinkImageFilter<ImageType, ImageType> ShrinkType;
+ typedef itk::ResampleImageFilter<ImageType, ImageType, TFloat> FilterType;
+ typedef itk::GaussianInterpolateImageFunction<ImageType, TFloat> FuncType;
+ typename ShrinkType::Pointer m_Shrink;
+ typename FilterType::Pointer m_Filter;
+ typename FuncType::Pointer m_Func;
+};
+
+template <class TFloat, uint VDim>
+int run_test(int argc, char *argv[])
+{
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef typename LDDMM::VectorImageType VectorImageType;
+
+
+ int test_id = atoi(argv[1]);
+ if(test_id == 0)
+ {
+ printf(
+ "TEST LISTING:\n"
+ " 1: FFT Convolution test for vector fields\n"
+ " --test 1 invec.nii compare.nii\n"
+ " 2: Test image warping given velocity field\n"
+ " --test 2 nt warp%%02d.nii input.nii compare.nii\n"
+ " 3: Test gradient w.r.t. variation\n"
+ " --test 3 fixed.nii moving.nii nt variation.nii\n"
+ );
+
+ return 0;
+ }
+
+ if(test_id == 1)
+ {
+ if(argc < 4)
+ throw itk::ExceptionObject("Wrong number of arguments for test 1");
+
+ // Read images
+ typename VectorImageType::Pointer src, comp;
+ LDDMM::vimg_read(argv[2], src);
+ LDDMM::vimg_read(argv[3], comp);
+
+ // Initialize LDDMM
+ typename ImageType::Pointer dummy_fix, dummy_mov;
+ LDDMM::alloc_img(dummy_fix, src);
+ LDDMM::alloc_img(dummy_mov, src);
+ LDDMM lddmm;
+ LDDMM::init(lddmm, dummy_fix, dummy_mov, 4, 0.01, 1.0, 1.0);
+ LDDMM::img_write(lddmm.f_kernel_sq, "test01_kernel.nii");
+
+ // Perform operation
+ typedef LDDMMFFTInterface<TFloat, VDim> FFT;
+ FFT fft(dummy_fix);
+ fft.convolution_fft(src, lddmm.f_kernel_sq, false, src);
+
+ // Save (temp)
+ LDDMM::vimg_write(src, "test01_result.nii");
+
+ // Compare to target
+ LDDMM::vimg_subtract_in_place(src, comp);
+ TFloat diff = LDDMM::vimg_euclidean_norm_sq(src);
+
+ printf("Difference with expected result: %f\n", diff);
+ return (diff > 0);
+ }
+ else if(test_id == 2)
+ {
+ if(argc < 6)
+ throw itk::ExceptionObject("Wrong number of arguments for test 1");
+
+ // Get the number of time steps
+ uint nt = atoi(argv[2]);
+
+ // Read input and output images
+ typename ImageType::Pointer src, comp;
+ LDDMM::img_read(argv[4], src);
+ LDDMM::img_read(argv[5], comp);
+
+ // Create a problem
+ LDDMM lddmm;
+ LDDMM::init(lddmm, src, src, nt, 0.01, 1.0, 1.0);
+
+ // Read velocity field
+ LDDMM::vfield_read(nt, argv[3], lddmm.v);
+
+ // Integrate the forward transform
+ lddmm.compute_semi_lagrangean_a();
+ lddmm.integrate_phi_t0();
+
+ // Warp the image using phi_10
+ typename ImageType::Pointer res;
+ LDDMM::alloc_img(res, src);
+ LDDMM::interp_img(src, lddmm.f[nt-1], res);
+
+ // Write the result
+ LDDMM::img_write(res, "test02_result.nii");
+
+ // Compare to target
+ LDDMM::img_subtract_in_place(res, comp);
+ TFloat diff = LDDMM::img_euclidean_norm_sq(res);
+
+ printf("Difference with expected result: %f\n", diff);
+ return (diff > 0);
+ }
+ else if(test_id == 3)
+ {
+ if(argc < 6)
+ throw itk::ExceptionObject("Wrong number of arguments for test 1");
+
+ // Read input and output images
+ typename ImageType::Pointer fix, mov;
+ LDDMM::img_read(argv[2], fix);
+ LDDMM::img_read(argv[3], mov);
+
+ // Number of time steps
+ uint nt = atoi(argv[4]);
+
+ // Create a problem
+ LDDMM p;
+ LDDMM::init(p, fix, mov, nt, 0.01, 1.0, 1.0);
+
+ // Iterate for 4 iterations (so that the starting point isn't zero
+ LDDMMImageMatchingObjective<TFloat, VDim> obj(p);
+ double ts = 0.01;
+ for(uint k = 0; k < 4; k++)
+ {
+ TFloat fx = obj.compute_objective_and_gradient(p);
+ for(uint it = 0; it < p.nt; it++)
+ LDDMM::vimg_add_scaled_in_place(p.v[it], p.a[it], -ts);
+ printf("Iter %04d Obj: %10.10f\n", k, fx);
+ }
+
+ // Call this one more time, so we have v in p.v and dv in p.a
+ TFloat fx = obj.compute_objective_and_gradient(p);
+
+ // Read the random variation
+ typename LDDMM::VelocityField h;
+ LDDMM::alloc_vf(h, p.nt, p.fix);
+ LDDMM::vfield_read(nt, argv[5], h);
+
+ // gateaux_analytic = lddmm_vector_field_dot_product(dedvx, dedvy, varx, vary, p);
+
+ // for expeps = -6:2
+
+ // eps = 10^expeps;
+
+ // E1 = lddmm_objective_and_gradient(vx - eps * varx, vy - eps * vary, p);
+ // E2 = lddmm_objective_and_gradient(vx + eps * varx, vy + eps * vary, p);
+
+ // gateaux_numeric = (E2 - E1) / (2 * eps);
+
+ // fprintf('Iter: %4i Eps: %8d Analytic: %12d Numeric: %12d\n', ...
+ // i, eps, gateaux_analytic, gateaux_numeric);
+ }
+ else
+ throw itk::ExceptionObject("Unknown test ID");
+}
+
+template <class TFloat, uint VDim>
+int my_main(int argc, char *argv[])
+{
+ // Parse options, look for test option
+ for(int i = 1; i < argc-1; i++)
+ {
+ if(!strcmp(argv[i], "--test"))
+ return run_test<TFloat,VDim>(argc-i, argv+i);
+ }
+
+ // Normal processing
+ char *fnmov = argv[argc-1];
+ char *fnfix = argv[argc-2];
+ // const char *outname = "lddmm.nii.gz";
+
+ // Number of iterations
+ uint n_res = 1;
+ uint n_iter[] = {100};
+
+ // Time steps
+ uint nt = 10;
+
+ // Read the images
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typename ReaderType::Pointer readfix = ReaderType::New();
+ readfix->SetFileName(fnfix);
+ readfix->Update();
+
+ typename ReaderType::Pointer readmov = ReaderType::New();
+ readmov->SetFileName(fnmov);
+ readmov->Update();
+
+ // Loop over the multi-resolution levels
+ for(int ires = n_res-1; ires >=0; ires--)
+ {
+ // Interpolate the input image and output image
+ typename ImageType::Pointer ifix, imov;
+ if(ires > 0)
+ {
+ Resampler<TFloat,VDim> rfix(readfix->GetOutput(), 1 << ires);
+ Resampler<TFloat,VDim> rmov(readmov->GetOutput(), 1 << ires);
+ ifix = rfix.GetResult();
+ imov = rmov.GetResult();
+ }
+ else
+ {
+ ifix = readfix->GetOutput();
+ imov = readmov->GetOutput();
+ }
+
+ // Create the problem
+ LDDMM p;
+
+ double avgdim = pow(ifix->GetBufferedRegion().GetNumberOfPixels() * 1.0, 1.0 / VDim);
+
+ // LDDMM::init(p, ifix, imov, nt, 0.01, 1, 0.008);
+ LDDMM::init(p, ifix, imov, nt, 0.01, 1, 1.0 / avgdim);
+
+ // TODO: initialize with earlier set of velocity fields
+
+ // Create an optimization problem
+ LDDMMImageMatchingObjective<TFloat, VDim> obj(p);
+
+ // Set time step
+ double ts = 0.01;
+
+ // Iterate
+ for(uint k = 0; k < n_iter[ires]; k++)
+ {
+ // Compute objective and gradient. This will put the gradient of the function
+ // into the 'a' array, retain the velocity field in 'v', and retain the inverse
+ // transform (ft0) in the 'f' array.
+ TFloat fx = obj.compute_objective_and_gradient(p);
+
+ // Update the solution by the time step (v = v - t * dv)
+ for(uint it = 0; it < p.nt; it++)
+ LDDMM::vimg_add_scaled_in_place(p.v[it], p.a[it], -ts);
+
+ // Save the current image
+ char fn[1024]; sprintf(fn, "lddmm_result_%04d.nii.gz", k);
+ LDDMM::img_write(obj.Jt0, fn);
+
+ // Print out the current status
+ printf("Iter %04d Obj: %10.10f\n", k, fx);
+ }
+ }
+
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ // Read input arguments
+ if(argc < 2)
+ return usage();
+
+ // Get the number of dimensions
+ uint ndim = (uint) atoi(argv[1]);
+ if(ndim == 2)
+ my_main<myreal, 2>(argc, argv);
+ else if(ndim == 3)
+ my_main<myreal, 3>(argc, argv);
+ else
+ return usage();
+}
diff --git a/Submodules/greedy/src/bk/lddmm_temp.cxx b/Submodules/greedy/src/bk/lddmm_temp.cxx
new file mode 100644
index 0000000..e69de29
diff --git a/Submodules/greedy/src/greedy_main.cxx b/Submodules/greedy/src/greedy_main.cxx
new file mode 100644
index 0000000..91ab70d
--- /dev/null
+++ b/Submodules/greedy/src/greedy_main.cxx
@@ -0,0 +1,473 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+
+#include "GreedyAPI.h"
+#include "CommandLineHelper.h"
+
+#include <iostream>
+#include <sstream>
+#include <cstdio>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <cerrno>
+
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+#include <itkImageFileReader.h>
+#include <itkAffineTransform.h>
+#include <itkTransformFactory.h>
+#include <itkTimeProbe.h>
+
+#include "MultiImageRegistrationHelper.h"
+#include "FastWarpCompositeImageFilter.h"
+#include <vnl/vnl_cost_function.h>
+#include <vnl/vnl_random.h>
+#include <vnl/algo/vnl_powell.h>
+#include <vnl/algo/vnl_svd.h>
+#include <vnl/vnl_trace.h>
+
+
+int usage()
+{
+ printf("greedy: Paul's greedy diffeomorphic registration implementation\n");
+ printf("Usage: \n");
+ printf(" greedy [options]\n");
+ printf("Required options: \n");
+ printf(" -d DIM : Number of image dimensions\n");
+ printf(" -i fix.nii mov.nii : Image pair (may be repeated)\n");
+ printf(" -o output.nii : Output file\n");
+ printf("Mode specification: \n");
+ printf(" -a : Perform affine registration and save to output (-o)\n");
+ printf(" -brute radius : Perform a brute force search around each voxel \n");
+ printf(" -moments <1|2> : Perform moments of inertia rigid alignment of given order.\n");
+ printf(" order 1 matches center of mass only\n");
+ printf(" order 2 matches second-order moments of inertia tensors\n");
+ printf(" -r [tran_spec] : Reslice images instead of doing registration \n");
+ printf(" tran_spec is a series of warps, affine matrices\n");
+ printf(" -iw inwarp outwarp : Invert previously computed warp\n");
+ printf(" -root inwarp outwarp N : Convert 2^N-th root of a warp \n");
+ printf("Options in deformable / affine mode: \n");
+ printf(" -w weight : weight of the next -i pair\n");
+ printf(" -m metric : metric for the entire registration\n");
+ printf(" SSD: sum of square differences (default)\n");
+ printf(" MI: mutual information\n");
+ printf(" NMI: normalized mutual information\n");
+ printf(" NCC <radius>: normalized cross-correlation\n");
+ printf(" -e epsilon : step size (default = 1.0), \n");
+ printf(" may also be specified per level (e.g. 0.3x0.1)\n");
+ printf(" -n NxNxN : number of iterations per level of multi-res (100x100) \n");
+ printf(" -threads N : set the number of allowed concurrent threads\n");
+ printf(" -gm mask.nii : mask for gradient computation\n");
+ printf(" -mm mask.nii : mask for the moving image\n");
+ printf(" -it filenames : sequence of transforms to apply to the moving image first \n");
+ printf("Specific to deformable mode: \n");
+ printf(" -tscale MODE : time step behavior mode: CONST, SCALE [def], SCALEDOWN\n");
+ printf(" -s sigma1 sigma2 : smoothing for the greedy update step. Must specify units,\n");
+ printf(" either `vox` or `mm`. Default: 1.732vox, 0.7071vox\n");
+ printf(" -oinv image.nii : compute and write the inverse of the warp field into image.nii\n");
+ printf(" -invexp VALUE : how many times to take the square root of the forward\n");
+ printf(" transform when computing inverse (default=2)\n");
+ printf(" -wp VALUE : Saved warp precision (in voxels; def=0.1; 0 for no compression).\n");
+ printf(" -noise VALUE : Standard deviation of white noise added to moving/fixed images when \n");
+ printf(" using NCC metric. Relative to intensity range. Def=0.001\n");
+ printf("Initial transform specification: \n");
+ printf(" -ia filename : initial affine matrix for optimization (not the same as -it) \n");
+ printf(" -ia-identity : initialize affine matrix based on NIFTI headers \n");
+ printf("Specific to affine mode (-a):\n");
+ printf(" -dof N : Degrees of freedom for affine reg. 6=rigid, 12=affine\n");
+ printf(" -jitter sigma : Jitter (in voxel units) applied to sample points (def: 0.5)\n");
+ printf(" -search N s_ang s_xyz : Random search over rigid transforms (N iter) before starting optimization\n");
+ printf(" s_ang, s_xyz: sigmas for rot-n angle (degrees) and offset between image centers\n");
+ printf("Specific to moments of inertia mode (-moments 2): \n");
+ printf(" -det <-1|1> : Force the determinant of transform to be either 1 (no flip) or -1 (flip)\n");
+ printf(" -cov-id : Assume identity covariance (match centers and do flips only, no rotation)\n");
+ printf("Specific to reslice mode (-r): \n");
+ printf(" -rf fixed.nii : fixed image for reslicing\n");
+ printf(" -rm mov.nii out.nii : moving/output image pair (may be repeated)\n");
+ printf(" -rs mov.vtk out.vtk : moving/output surface pair (vertices are warped from fixed space to moving)\n");
+ printf(" -ri interp_mode : interpolation for the next pair (NN, LINEAR*, LABEL sigma)\n");
+ printf(" -rc outwarp : write composed transforms to outwarp \n");
+ printf("For developers: \n");
+ printf(" -debug-deriv : enable periodic checks of derivatives (debug) \n");
+ printf(" -debug-deriv-eps : epsilon for derivative debugging \n");
+ printf(" -debug-aff-obj : plot affine objective in neighborhood of -ia matrix \n");
+ printf(" -dump-moving : dump moving image at each iter\n");
+ printf(" -dump-freq N : dump frequency\n");
+ printf(" -powell : use Powell's method instead of LGBFS\n");
+ printf(" -float : use single precision floating point (off by default)\n");
+
+ return -1;
+}
+
+
+
+template <unsigned int VDim, typename TReal>
+class GreedyRunner
+{
+public:
+ static int Run(GreedyParameters ¶m)
+ {
+ GreedyApproach<VDim, TReal> greedy;
+ return greedy.Run(param);
+
+ }
+};
+
+
+
+
+
+int main(int argc, char *argv[])
+{
+ GreedyParameters param;
+ GreedyParameters::SetToDefaults(param);
+
+ double current_weight = 1.0;
+
+ // reslice mode parameters
+ InterpSpec interp_current;
+
+ if(argc < 3)
+ return usage();
+
+ try
+ {
+ CommandLineHelper cl(argc, argv);
+ while(!cl.is_at_end())
+ {
+ // Read the next command
+ std::string arg = cl.read_command();
+
+ if(arg == "-d")
+ {
+ param.dim = cl.read_integer();
+ }
+ else if(arg == "-float")
+ {
+ param.flag_float_math = true;
+ }
+ else if(arg == "-n")
+ {
+ param.iter_per_level = cl.read_int_vector();
+ }
+ else if(arg == "-w")
+ {
+ current_weight = cl.read_double();
+ }
+ else if(arg == "-e")
+ {
+ param.epsilon_per_level = cl.read_double_vector();
+ }
+ else if(arg == "-m")
+ {
+ std::string metric_name = cl.read_string();
+ if(metric_name == "NCC" || metric_name == "ncc")
+ {
+ param.metric = GreedyParameters::NCC;
+ param.metric_radius = cl.read_int_vector();
+ }
+ else if(metric_name == "MI" || metric_name == "mi")
+ {
+ param.metric = GreedyParameters::MI;
+ }
+ else if(metric_name == "NMI" || metric_name == "nmi")
+ {
+ param.metric = GreedyParameters::NMI;
+ }
+ }
+ else if(arg == "-tscale")
+ {
+ std::string mode = cl.read_string();
+ if(mode == "SCALE" || mode == "scale")
+ param.time_step_mode = GreedyParameters::SCALE;
+ else if(mode == "SCALEDOWN" || mode == "scaledown")
+ param.time_step_mode = GreedyParameters::SCALEDOWN;
+ }
+ else if(arg == "-noise")
+ {
+ param.ncc_noise_factor = cl.read_double();
+ }
+ else if(arg == "-s")
+ {
+ param.sigma_pre.sigma = cl.read_scalar_with_units(param.sigma_pre.physical_units);
+ param.sigma_post.sigma = cl.read_scalar_with_units(param.sigma_post.physical_units);
+ }
+ else if(arg == "-i")
+ {
+ ImagePairSpec ip;
+ ip.weight = current_weight;
+ ip.fixed = cl.read_existing_filename();
+ ip.moving = cl.read_existing_filename();
+ param.inputs.push_back(ip);
+ }
+ else if(arg == "-ia")
+ {
+ param.affine_init_mode = RAS_FILENAME;
+ param.affine_init_transform = cl.read_transform_spec();
+ }
+ else if(arg == "-ia-identity" || arg == "-iaid" || arg == "-ia-id")
+ {
+ param.affine_init_mode = RAS_IDENTITY;
+ }
+ else if(arg == "-dof")
+ {
+ int dof = cl.read_integer();
+ if(dof == 6)
+ param.affine_dof = GreedyParameters::DOF_RIGID;
+ else if(dof == 12)
+ param.affine_dof = GreedyParameters::DOF_AFFINE;
+ else throw GreedyException("DOF parameter only accepts 6 and 12 as values");
+ }
+ else if(arg == "-jitter")
+ {
+ param.affine_jitter = cl.read_double();
+ }
+ else if(arg == "-search")
+ {
+ param.rigid_search.iterations = cl.read_integer();
+ param.rigid_search.sigma_angle = cl.read_double();
+ param.rigid_search.sigma_xyz = cl.read_double();
+ }
+ else if(arg == "-it")
+ {
+ int nFiles = cl.command_arg_count();
+ for(int i = 0; i < nFiles; i++)
+ param.moving_pre_transforms.push_back(cl.read_transform_spec());
+ }
+ else if(arg == "-gm")
+ {
+ param.gradient_mask = cl.read_existing_filename();
+ }
+ else if(arg == "-mm")
+ {
+ param.moving_mask = cl.read_existing_filename();
+ }
+ else if(arg == "-o")
+ {
+ param.output = cl.read_output_filename();
+ }
+ else if(arg == "-dump-moving")
+ {
+ param.flag_dump_moving = true;
+ }
+ else if(arg == "-powell")
+ {
+ param.flag_powell = true;
+ }
+ else if(arg == "-dump-frequency" || arg == "-dump-freq")
+ {
+ param.dump_frequency = cl.read_integer();
+ }
+ else if(arg == "-debug-deriv")
+ {
+ param.flag_debug_deriv = true;
+ }
+ else if(arg == "-debug-deriv-eps")
+ {
+ param.deriv_epsilon = cl.read_double();
+ }
+ else if(arg == "-debug-aff-obj")
+ {
+ param.flag_debug_aff_obj = true;
+ }
+ else if(arg == "-threads")
+ {
+ param.threads = cl.read_integer();
+ }
+ else if(arg == "-a")
+ {
+ param.mode = GreedyParameters::AFFINE;
+ }
+ else if(arg == "-moments")
+ {
+ param.mode = GreedyParameters::MOMENTS;
+
+ // For backward compatibility allow no parameter, which defaults to order 1
+ param.moments_order = cl.command_arg_count() > 0 ? cl.read_integer() : 2;
+ if(param.moments_order != 1 && param.moments_order != 2)
+ throw GreedyException("Parameter to -moments must be 1 or 2");
+ }
+ else if(arg == "-brute")
+ {
+ param.mode = GreedyParameters::BRUTE;
+ param.brute_search_radius = cl.read_int_vector();
+ }
+ else if(arg == "-r")
+ {
+ param.mode = GreedyParameters::RESLICE;
+ int nFiles = cl.command_arg_count();
+ for(int i = 0; i < nFiles; i++)
+ param.reslice_param.transforms.push_back(cl.read_transform_spec());
+ }
+ else if(arg == "-iw")
+ {
+ param.mode = GreedyParameters::INVERT_WARP;
+ param.invwarp_param.in_warp = cl.read_existing_filename();
+ param.invwarp_param.out_warp = cl.read_output_filename();
+ }
+ else if(arg == "-root")
+ {
+ param.mode = GreedyParameters::ROOT_WARP;
+ param.warproot_param.in_warp = cl.read_existing_filename();
+ param.warproot_param.out_warp = cl.read_output_filename();
+ param.warproot_param.exponent = cl.read_integer();
+ }
+
+ else if(arg == "-rm")
+ {
+ ResliceSpec rp;
+ rp.interp = interp_current;
+ rp.moving = cl.read_existing_filename();
+ rp.output = cl.read_output_filename();
+ param.reslice_param.images.push_back(rp);
+ }
+ else if(arg == "-rs")
+ {
+ ResliceMeshSpec rp;
+ rp.fixed = cl.read_existing_filename();
+ rp.output = cl.read_output_filename();
+ param.reslice_param.meshes.push_back(rp);
+ }
+ else if(arg == "-rf")
+ {
+ param.reslice_param.ref_image = cl.read_existing_filename();
+ }
+ else if(arg == "-rc")
+ {
+ param.reslice_param.out_composed_warp = cl.read_output_filename();
+ }
+ else if(arg == "-oinv")
+ {
+ param.inverse_warp = cl.read_output_filename();
+ }
+ else if(arg == "-invexp")
+ {
+ param.inverse_exponent = cl.read_integer();
+ }
+ else if(arg == "-ri")
+ {
+ std::string mode = cl.read_string();
+ if(mode == "nn" || mode == "NN" || mode == "0")
+ {
+ interp_current.mode = InterpSpec::NEAREST;
+ }
+ else if(mode == "linear" || mode == "LINEAR" || mode == "1")
+ {
+ interp_current.mode = InterpSpec::LINEAR;
+ }
+ else if(mode == "label" || mode == "LABEL")
+ {
+ interp_current.mode = InterpSpec::LABELWISE;
+ interp_current.sigma.sigma = cl.read_scalar_with_units(interp_current.sigma.physical_units);
+ }
+ else
+ {
+ std::cerr << "Unknown interpolation mode" << std::endl;
+ }
+ }
+ else if(arg == "-wp")
+ {
+ param.warp_precision = cl.read_double();
+ }
+ else if(arg == "-det")
+ {
+ int det_value = cl.read_integer();
+ if(det_value != -1 && det_value != 1)
+ {
+ std::cerr << "Incorrect -det parameter value " << det_value << std::endl;
+ return -1;
+ }
+ param.moments_flip_determinant = det_value;
+ }
+ else if(arg == "-cov-id")
+ {
+ param.flag_moments_id_covariance = true;
+ }
+ else
+ {
+ std::cerr << "Unknown parameter " << arg << std::endl;
+ return -1;
+ }
+ }
+
+ // Use the threads parameter
+ if(param.threads > 0)
+ {
+ std::cout << "Limiting the number of threads to " << param.threads << std::endl;
+ itk::MultiThreader::SetGlobalMaximumNumberOfThreads(param.threads);
+ }
+ else
+ {
+ std::cout << "Executing with the default number of threads: " << itk::MultiThreader::GetGlobalDefaultNumberOfThreads() << std::endl;
+
+ }
+
+ // Some parameters may be specified as either vector or scalar, and need to be verified
+ if(param.epsilon_per_level.size() != param.iter_per_level.size())
+ {
+ if(param.epsilon_per_level.size() == 1)
+ {
+ param.epsilon_per_level =
+ std::vector<double>(param.iter_per_level.size(), param.epsilon_per_level.back());
+ }
+ else
+ {
+ throw GreedyException("Mismatch in size of vectors supplied with -n and -e options");
+ }
+ }
+
+ // Run the main code
+ if(param.flag_float_math)
+ {
+ switch(param.dim)
+ {
+ case 2: return GreedyRunner<2, float>::Run(param); break;
+ case 3: return GreedyRunner<3, float>::Run(param); break;
+ case 4: return GreedyRunner<4, float>::Run(param); break;
+ default: throw GreedyException("Wrong number of dimensions requested: %d", param.dim);
+ }
+ }
+ else
+ {
+ switch(param.dim)
+ {
+ case 2: return GreedyRunner<2, double>::Run(param); break;
+ case 3: return GreedyRunner<3, double>::Run(param); break;
+ case 4: return GreedyRunner<4, double>::Run(param); break;
+ default: throw GreedyException("Wrong number of dimensions requested: %d", param.dim);
+ }
+ }
+ }
+ catch(std::exception &exc)
+ {
+ std::cerr << "ABORTING PROGRAM DUE TO RUNTIME EXCEPTION -- "
+ << exc.what() << std::endl;
+ return -1;
+ }
+}
diff --git a/Submodules/greedy/src/itkGaussianInterpolateImageFunction.h b/Submodules/greedy/src/itkGaussianInterpolateImageFunction.h
new file mode 100644
index 0000000..3b744a2
--- /dev/null
+++ b/Submodules/greedy/src/itkGaussianInterpolateImageFunction.h
@@ -0,0 +1,290 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkGaussianInterpolateImageFunction.h,v $
+ Language: C++
+ Date: $Date: 2009/07/01 12:59:34 $
+ Version: $Revision: 1.5 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkGaussianInterpolateImageFunction_h
+#define __itkGaussianInterpolateImageFunction_h
+
+#include "itkInterpolateImageFunction.h"
+#include "vnl/vnl_erf.h"
+
+namespace itk
+{
+
+/** \class GaussianInterpolateImageFunction
+ * \brief Gaussianly interpolate an image at specified positions.
+ *
+ * GaussianInterpolateImageFunction linearly interpolates image intensity at
+ * a non-integer pixel position. This class is templated
+ * over the input image type and the coordinate representation type
+ * (e.g. float or double).
+ *
+ * This function works for N-dimensional images.
+ *
+ * \ingroup ImageFunctions ImageInterpolators
+ */
+template <class TInputImage, class TCoordRep = double>
+class ITK_EXPORT GaussianInterpolateImageFunction :
+ public InterpolateImageFunction<TInputImage,TCoordRep>
+{
+public:
+ /** Standard class typedefs. */
+ typedef GaussianInterpolateImageFunction Self;
+ typedef InterpolateImageFunction<TInputImage,TCoordRep> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(GaussianInterpolateImageFunction, InterpolateImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** OutputType typedef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** InputImageType typedef support. */
+ typedef typename Superclass::InputImageType InputImageType;
+
+ /** RealType typedef support. */
+ typedef typename Superclass::RealType RealType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(VDim, unsigned int,Superclass::ImageDimension);
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Compute internals */
+ virtual void ComputeBoundingBox()
+ {
+ const TInputImage *img = this->GetInputImage();
+ if(img == NULL) return;
+
+ // Set the bounding box
+ for(size_t d = 0; d < VDim; d++)
+ {
+ bb_start[d] = -0.5;
+ bb_end[d] = img->GetBufferedRegion().GetSize()[d] - 0.5;
+ nt[d] = (int)(bb_end[d] - bb_start[d] + 0.5);
+ dx[d].set_size(nt[d]);
+ gx[d].set_size(nt[d]);
+ sf[d] = 1.0 / (sqrt(2.0) * sigma[d] / img->GetSpacing()[d]);
+ cut[d] = sigma[d] * alpha / img->GetSpacing()[d];
+ }
+ }
+
+ /** Set input */
+ virtual void SetInputImage(const TInputImage *img)
+ {
+ // Call parent method
+ Superclass::SetInputImage(img);
+ this->ComputeBoundingBox();
+ }
+
+ void SetParameters(double *sigma, double alpha)
+ {
+ // Set the parameters
+ for(size_t d = 0; d < VDim; d++)
+ this->sigma[d] = sigma[d];
+ this->alpha = alpha;
+
+ // If the image already set, recompute
+ this->ComputeBoundingBox();
+ }
+
+ /** Evaluate the function at a ContinuousIndex position
+ *
+ * Returns the linearly interpolated image intensity at a
+ * specified point position. No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType & index ) const
+ {
+ return EvaluateAtContinuousIndex(index, NULL);
+ }
+
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType &index,
+ OutputType *grad) const
+ {
+ // The bound variables for x, y, z
+ int i0[VDim], i1[VDim];
+
+ // Compute the ERF difference arrays
+ for(size_t d = 0; d < VDim; d++)
+ {
+ double *pdx = const_cast<double *>(dx[d].data_block());
+ double *pgx = grad ? const_cast<double *>(gx[d].data_block()) : NULL;
+ compute_erf_array(pdx, i0[d], i1[d], bb_start[d], nt[d], cut[d], index[d], sf[d], pgx);
+ }
+
+ // Get a pointer to the output value
+ double sum_me = 0.0, sum_m = 0.0;
+ vnl_vector_fixed<double, VDim> dsum_me(0.0), dsum_m(0.0), dw;
+
+ // Loop over the voxels in the region identified
+ ImageRegion<VDim> region;
+ for(size_t d = 0; d < VDim; d++)
+ {
+ region.SetIndex(d, i0[d]);
+ region.SetSize(d, i1[d] - i0[d]);
+ }
+
+ for(
+ ImageRegionConstIteratorWithIndex<InputImageType> it(this->GetInputImage(), region);
+ !it.IsAtEnd(); ++it)
+ {
+ size_t j = it.GetIndex()[0];
+ double w = dx[0][j];
+ if(grad)
+ {
+ dw[0] = gx[0][j];
+ for(size_t d = 1; d < VDim; d++) dw[d] = dx[0][j];
+ }
+ for(size_t d = 1; d < VDim; d++)
+ {
+ j = it.GetIndex()[d];
+ w *= dx[d][j];
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ dw[q] *= (d == q) ? gx[d][j] : dx[d][j];
+ }
+ }
+
+ double V = it.Get();
+ sum_me += V * w;
+ sum_m += w;
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ {
+ dsum_me[q] += V * dw[q];
+ dsum_m[q] += dw[q];
+ }
+ }
+ }
+
+ double rc = sum_me / sum_m;
+ if(grad)
+ {
+ for(size_t q = 0; q < VDim; q++)
+ {
+ grad[q] = (dsum_me[q] - rc * dsum_m[q]) / sum_m;
+ grad[q] /= -1.4142135623730951 * sigma[q];
+ }
+ }
+
+ // return sum_me / sum_m;
+ return rc;
+
+ }
+
+protected:
+ GaussianInterpolateImageFunction() {}
+ ~GaussianInterpolateImageFunction(){};
+ void PrintSelf(std::ostream& os, Indent indent) const
+ { this->Superclass::PrintSelf(os,indent); }
+
+private:
+ GaussianInterpolateImageFunction( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ /** Number of neighbors used in the interpolation */
+ static const unsigned long m_Neighbors;
+
+ vnl_vector<double> dx[VDim], gx[VDim];
+ double bb_start[VDim], bb_end[VDim], sf[VDim], cut[VDim];
+ int nt[VDim], stride[VDim];
+ double sigma[VDim], alpha;
+
+ void compute_erf_array (
+ double *dx_erf, // The output array of erf(p+i+1) - erf(p+i)
+ int &k0, int &k1, // The range of integration 0 <= k0 < k1 <= n
+ double b, // Lower bound of the bounding box
+ int n, // Size of the bounding box in steps
+ double cut, // The distance at which to cut off
+ double p, // the value p
+ double sfac, // scaling factor 1 / (Sqrt[2] sigma)
+ double *gx_erf = NULL // Output derivative/erf array (optional)
+ ) const
+ {
+ // Determine the range of voxels along the line where to evaluate erf
+ k0 = (int) floor(p - b - cut);
+ k1 = (int) ceil(p - b + cut);
+ if(k0 < 0) k0 = 0;
+ if(k1 > n) k1 = n;
+
+ // Start at the first voxel
+ double t = (b - p + k0) * sfac;
+ double e_last = vnl_erf(t);
+ double g_last = gx_erf ? 1.128379167095513 * exp(- t * t) : 0.0;
+ for(int i = k0; i < k1; i++)
+ {
+ t += sfac;
+ double e_now = vnl_erf(t);
+ dx_erf[i] = e_now - e_last;
+ if(gx_erf)
+ {
+ double g_now = 1.128379167095513 * exp(- t * t);
+ gx_erf[i] = g_now - g_last;
+ g_last = g_now;
+ }
+ e_last = e_now;
+ }
+ }
+
+};
+
+} // end namespace itk
+
+// Define instantiation macro for this template.
+#define ITK_TEMPLATE_GaussianInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \
+ _(2(class EXPORT GaussianInterpolateImageFunction< ITK_TEMPLATE_2 x >)) \
+ namespace Templates { typedef GaussianInterpolateImageFunction< ITK_TEMPLATE_2 x > \
+ GaussianInterpolateImageFunction##y; } \
+}
+
+#endif
diff --git a/Submodules/greedy/src/itkOptVectorLinearInterpolateImageFunction.h b/Submodules/greedy/src/itkOptVectorLinearInterpolateImageFunction.h
new file mode 100755
index 0000000..8369f4d
--- /dev/null
+++ b/Submodules/greedy/src/itkOptVectorLinearInterpolateImageFunction.h
@@ -0,0 +1,520 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkOptVectorLinearInterpolateImageFunction.h,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:19 $
+ Version: $Revision: 1.11 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkOptVectorLinearInterpolateImageFunction_h
+#define __itkOptVectorLinearInterpolateImageFunction_h
+
+#include "itkVectorInterpolateImageFunction.h"
+
+namespace itk
+{
+
+/** \class LinearInterpolateImageFunction
+ * \brief Linearly interpolate an image at specified positions.
+ *
+ * LinearInterpolateImageFunction linearly interpolates image intensity at
+ * a non-integer pixel position. This class is templated
+ * over the input image type and the coordinate representation type
+ * (e.g. float or double).
+ *
+ * This function works for N-dimensional images.
+ *
+ * \warning This function work only for images with scalar pixel
+ * types. For vector images use VectorLinearInterpolateImageFunction.
+ *
+ * \sa VectorLinearInterpolateImageFunction
+ *
+ * \ingroup ImageFunctions ImageInterpolators
+ */
+template <class TInputImage, class TCoordRep = double>
+class ITK_EXPORT OptVectorLinearInterpolateImageFunction :
+ public VectorInterpolateImageFunction<TInputImage,TCoordRep>
+{
+public:
+ /** Standard class typedefs. */
+ typedef OptVectorLinearInterpolateImageFunction Self;
+ typedef VectorInterpolateImageFunction<TInputImage,TCoordRep> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(OptVectorLinearInterpolateImageFunction, VectorInterpolateImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** OutputType typedef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** InputImageType typedef support. */
+ typedef typename Superclass::InputImageType InputImageType;
+
+ /** InputPixelType typedef support. */
+ typedef typename Superclass::InputPixelType InputPixelType;
+
+ /** RealType typedef support. */
+ typedef typename Superclass::RealType RealType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(ImageDimension, unsigned int,Superclass::ImageDimension);
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+ typedef typename Superclass::IndexValueType IndexValueType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Evaluate the function at a ContinuousIndex position
+ *
+ * Returns the linearly interpolated image intensity at a
+ * specified point position. No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual inline OutputType EvaluateAtContinuousIndex( const
+ ContinuousIndexType &
+ index ) const
+ {
+ return this->EvaluateOptimized( Dispatch< ImageDimension >(), index );
+ }
+
+protected:
+ OptVectorLinearInterpolateImageFunction();
+ ~OptVectorLinearInterpolateImageFunction();
+ void PrintSelf(std::ostream& os, Indent indent) const;
+
+private:
+ OptVectorLinearInterpolateImageFunction( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ /** Number of neighbors used in the interpolation */
+ static const unsigned long m_Neighbors;
+
+ struct DispatchBase {};
+ template< unsigned int > struct Dispatch : DispatchBase {};
+
+ inline OutputType EvaluateOptimized( const Dispatch<0> &,
+ const ContinuousIndexType & index) const
+ {
+ return 0;
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<1>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+
+ const double distance = index[0] - static_cast<double>(basei[0]);
+
+ const InputPixelType val0 = this->GetInputImage()->GetPixel( basei );
+
+ if(distance <= 0.)
+ {
+ return( static_cast<OutputType>( val0 ) );
+ }
+
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val0 ) );
+ }
+ const InputPixelType val1 = this->GetInputImage()->GetPixel( basei );
+
+ return( static_cast<OutputType>( val0 + ( val1 - val0 ) * distance ) );
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<2>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+ const double distance0 = index[0] - static_cast<double>(basei[0]);
+
+ basei[1] = Math::Floor<IndexValueType>(index[1]);
+ if( basei[1] < this->m_StartIndex[1] )
+ {
+ basei[1] = this->m_StartIndex[1];
+ }
+ const double distance1 = index[1] - static_cast<double>(basei[1]);
+
+
+ const InputPixelType val00 = this->GetInputImage()->GetPixel( basei );
+ if(distance0 <= 0. && distance1 <= 0.)
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ else if(distance1 <= 0.) // if they have the same "y"
+ {
+ ++basei[0]; // then interpolate across "x"
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const InputPixelType val10 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val10 - val00) * distance0) );
+ }
+ else if(distance0 <= 0.) // if they have the same "x"
+ {
+ ++basei[1]; // then interpolate across "y"
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const InputPixelType val01 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val01 - val00) * distance1) );
+ }
+ else // interpolate across "xy"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "y"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val00 ) );
+ }
+ const InputPixelType val01 = this->GetInputImage()->GetPixel( basei );
+ return( static_cast<OutputType>(val00 + (val01 - val00) * distance1) );
+ }
+ const InputPixelType val10 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx0 = val00 + (val10 - val00) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx0 ) );
+ }
+ const InputPixelType val11 = this->GetInputImage()->GetPixel( basei );
+ --basei[0];
+ const InputPixelType val01 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx1 = val01 + (val11 - val01) * distance0;
+
+ return( static_cast<OutputType>( valx0 + (valx1-valx0) * distance1 ) );
+ }
+ }
+
+ inline OutputType EvaluateOptimized( const Dispatch<3>&,
+ const ContinuousIndexType & index) const
+ {
+ IndexType basei;
+
+ basei[0] = Math::Floor<IndexValueType>(index[0]);
+ if( basei[0] < this->m_StartIndex[0] )
+ {
+ basei[0] = this->m_StartIndex[0];
+ }
+ const double distance0 = index[0] - static_cast<double>(basei[0]);
+
+ basei[1] = Math::Floor<IndexValueType>(index[1]);
+ if( basei[1] < this->m_StartIndex[1] )
+ {
+ basei[1] = this->m_StartIndex[1];
+ }
+ const double distance1 = index[1] - static_cast<double>(basei[1]);
+
+ basei[2] = Math::Floor<IndexValueType>(index[2]);
+ if( basei[2] < this->m_StartIndex[2] )
+ {
+ basei[2] = this->m_StartIndex[2];
+ }
+ const double distance2 = index[2] - static_cast<double>(basei[2]);
+
+ if(distance0<=0. && distance1<=0. && distance2<=0.)
+ {
+ return( static_cast<OutputType>( this->GetInputImage()->GetPixel( basei ) ) );
+ }
+
+
+ const InputPixelType val000 = this->GetInputImage()->GetPixel( basei );
+
+ if(distance2 <= 0.)
+ {
+ if(distance1 <= 0.) // interpolate across "x"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val100-val000) * distance0 );
+ }
+ else if(distance0 <= 0.) // interpolate across "y"
+ {
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val010-val000) * distance1 );
+ }
+ else // interpolate across "xy"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "y"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val010-val000) * distance1 );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const InputPixelType val110 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx10 = val010 + (val110-val010) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx10-valx00) * distance1 );
+ }
+ }
+ else
+ {
+ if(distance1 <= 0.)
+ {
+ if(distance0 <= 0.) // interpolate across "z"
+ {
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ else // interpolate across "xz"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "z"
+ {
+ --basei[0];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const InputPixelType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx01 = val001 + (val101-val001) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx01-valx00) * distance2 );
+ }
+ }
+ else if(distance0 <= 0.) // interpolate across "yz"
+ {
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "z"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x0 = val000 + (val010-val000) * distance1;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "y"
+ {
+ return( static_cast<OutputType>( val0x0 ) );
+ }
+ const InputPixelType val011 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x1 = val001 + (val011-val001) * distance1;
+
+ return static_cast<OutputType>( val0x0 + (val0x1-val0x0) * distance2 );
+ }
+ else // interpolate across "xyz"
+ {
+ ++basei[0];
+ if(basei[0]>this->m_EndIndex[0]) // interpolate across "yz"
+ {
+ --basei[0];
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "z"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2])
+ {
+ return( static_cast<OutputType>( val000 ) );
+ }
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ return static_cast<OutputType>( val000 + (val001-val000) * distance2 );
+ }
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x0 = val000 + (val010-val000) * distance1;
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "y"
+ {
+ return( static_cast<OutputType>( val0x0 ) );
+ }
+ const InputPixelType val011 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType val0x1 = val001 + (val011-val001) * distance1;
+
+ return static_cast<OutputType>( val0x0 + (val0x1-val0x0) * distance2 );
+ }
+ const InputPixelType val100 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx00 = val000 + (val100-val000) * distance0;
+
+ ++basei[1];
+ if(basei[1]>this->m_EndIndex[1]) // interpolate across "xz"
+ {
+ --basei[1];
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "x"
+ {
+ return( static_cast<OutputType>( valx00 ) );
+ }
+ const InputPixelType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx01 = val001 + (val101-val001) * distance0;
+
+ return static_cast<OutputType>( valx00 + (valx01-valx00) * distance2 );
+ }
+ const InputPixelType val110 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val010 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx10 = val010 + (val110-val010) * distance0;
+
+ const InputPixelType valxx0 = valx00 + (valx10-valx00) * distance1;
+
+
+ ++basei[2];
+ if(basei[2]>this->m_EndIndex[2]) // interpolate across "xy"
+ {
+ return( static_cast<OutputType>( valxx0 ) );
+ }
+ const InputPixelType val011 = this->GetInputImage()->GetPixel( basei );
+
+ ++basei[0];
+ const InputPixelType val111 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[1];
+ const InputPixelType val101 = this->GetInputImage()->GetPixel( basei );
+
+ --basei[0];
+ const InputPixelType val001 = this->GetInputImage()->GetPixel( basei );
+
+ const InputPixelType valx01 = val001 + (val101-val001) * distance0;
+ const InputPixelType valx11 = val011 + (val111-val011) * distance0;
+ const InputPixelType valxx1 = valx01 + (valx11-valx01) * distance1;
+
+ return( static_cast<OutputType>( valxx0 + (valxx1-valxx0) * distance2 ) );
+ }
+ }
+ }
+
+ inline OutputType EvaluateOptimized( const DispatchBase &,
+ const ContinuousIndexType & index) const
+ {
+ return this->EvaluateUnoptimized( index );
+ }
+
+ virtual inline OutputType EvaluateUnoptimized(
+ const ContinuousIndexType & index) const;
+};
+
+} // end namespace itk
+
+// Define instantiation macro for this template.
+#define ITK_TEMPLATE_OptVectorLinearInterpolateImageFunction(_, EXPORT, x, y) namespace itk { \
+ _(2(class EXPORT OptVectorLinearInterpolateImageFunction< ITK_TEMPLATE_2 x >)) \
+ namespace Templates { typedef OptVectorLinearInterpolateImageFunction< ITK_TEMPLATE_2 x > \
+ OptVectorLinearInterpolateImageFunction##y; } \
+ }
+
+#if ITK_TEMPLATE_EXPLICIT
+# include "Templates/itkOptVectorLinearInterpolateImageFunction+-.h"
+#endif
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkOptVectorLinearInterpolateImageFunction.txx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/itkOptVectorLinearInterpolateImageFunction.txx b/Submodules/greedy/src/itkOptVectorLinearInterpolateImageFunction.txx
new file mode 100755
index 0000000..337ce31
--- /dev/null
+++ b/Submodules/greedy/src/itkOptVectorLinearInterpolateImageFunction.txx
@@ -0,0 +1,160 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkOptVectorLinearInterpolateImageFunction.txx,v $
+ Language: C++
+ Date: $Date: 2009-10-29 11:19:19 $
+ Version: $Revision: 1.9 $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
+
+=========================================================================*/
+#ifndef __itkOptVectorLinearInterpolateImageFunction_txx
+#define __itkOptVectorLinearInterpolateImageFunction_txx
+
+#include "itkOptVectorLinearInterpolateImageFunction.h"
+
+#include "vnl/vnl_math.h"
+
+namespace itk
+{
+
+/**
+ * Define the number of neighbors
+ */
+template<class TInputImage, class TCoordRep>
+const unsigned long
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::m_Neighbors = 1 << TInputImage::ImageDimension;
+
+
+/**
+ * Constructor
+ */
+template<class TInputImage, class TCoordRep>
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::OptVectorLinearInterpolateImageFunction()
+{
+}
+
+template<class TInputImage, class TCoordRep>
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::~OptVectorLinearInterpolateImageFunction()
+{
+}
+
+/**
+ * PrintSelf
+ */
+template<class TInputImage, class TCoordRep>
+void
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::PrintSelf(std::ostream& os, Indent indent) const
+{
+ this->Superclass::PrintSelf(os,indent);
+}
+
+
+/**
+ * Evaluate at image index position
+ */
+template<class TInputImage, class TCoordRep>
+typename OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::OutputType
+OptVectorLinearInterpolateImageFunction< TInputImage, TCoordRep >
+::EvaluateUnoptimized( const ContinuousIndexType& index) const
+{
+ unsigned int dim; // index over dimension
+
+ /**
+ * Compute base index = closet index below point
+ * Compute distance from point to base index
+ */
+ IndexType baseIndex;
+ double distance[ImageDimension];
+
+ for( dim = 0; dim < ImageDimension; dim++ )
+ {
+ baseIndex[dim] = Math::Floor<IndexValueType>( index[dim] );
+ distance[dim] = index[dim] - static_cast< double >( baseIndex[dim] );
+ }
+
+ /**
+ * Interpolated value is the weighted sum of each of the surrounding
+ * neighbors. The weight for each neighbor is the fraction overlap
+ * of the neighbor pixel with respect to a pixel centered on point.
+ */
+ InputPixelType value = NumericTraits<RealType>::Zero;
+
+ typedef typename NumericTraits<InputPixelType>::ScalarRealType ScalarRealType;
+ ScalarRealType totalOverlap = NumericTraits<ScalarRealType>::Zero;
+
+ for( unsigned int counter = 0; counter < m_Neighbors; counter++ )
+ {
+
+ double overlap = 1.0; // fraction overlap
+ unsigned int upper = counter; // each bit indicates upper/lower neighbour
+ IndexType neighIndex;
+
+ // get neighbor index and overlap fraction
+ for( dim = 0; dim < ImageDimension; dim++ )
+ {
+
+ if ( upper & 1 )
+ {
+ neighIndex[dim] = baseIndex[dim] + 1;
+#ifdef ITK_USE_CENTERED_PIXEL_COORDINATES_CONSISTENTLY
+ // Take care of the case where the pixel is just
+ // in the outer upper boundary of the image grid.
+ if( neighIndex[dim] > this->m_EndIndex[dim] )
+ {
+ neighIndex[dim] = this->m_EndIndex[dim];
+ }
+#endif
+ overlap *= distance[dim];
+ }
+ else
+ {
+ neighIndex[dim] = baseIndex[dim];
+#ifdef ITK_USE_CENTERED_PIXEL_COORDINATES_CONSISTENTLY
+ // Take care of the case where the pixel is just
+ // in the outer lower boundary of the image grid.
+ if( neighIndex[dim] < this->m_StartIndex[dim] )
+ {
+ neighIndex[dim] = this->m_StartIndex[dim];
+ }
+#endif
+ overlap *= 1.0 - distance[dim];
+ }
+
+ upper >>= 1;
+
+ }
+
+ // get neighbor value only if overlap is not zero
+ if( overlap )
+ {
+ InputPixelType delta = static_cast<InputPixelType>( this->GetInputImage()->GetPixel( neighIndex ) ) * overlap;
+ value = value + delta;
+ totalOverlap += overlap;
+ }
+
+ if( totalOverlap == 1.0 )
+ {
+ // finished
+ break;
+ }
+
+ }
+
+ return ( static_cast<OutputType>( value ) );
+}
+
+} // end namespace itk
+
+#endif
diff --git a/Submodules/greedy/src/itkVectorImageCentralDifferenceImageFunction.h b/Submodules/greedy/src/itkVectorImageCentralDifferenceImageFunction.h
new file mode 100644
index 0000000..755708c
--- /dev/null
+++ b/Submodules/greedy/src/itkVectorImageCentralDifferenceImageFunction.h
@@ -0,0 +1,150 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+#ifndef itkVectorImageCentralDifferenceImageFunction_h
+#define itkVectorImageCentralDifferenceImageFunction_h
+
+#include "itkImageFunction.h"
+#include "itkMatrix.h"
+
+namespace itk
+{
+
+/**
+ * \class VectorImageCentralDifferenceImageFunction
+ * \brief Calculate the derivative by central differencing.
+ *
+ * This class is templated over the input image type and
+ * the coordinate representation type (e.g. float or double).
+ *
+ * Possible improvements:
+ * - the use of Neighborhood operators may improve efficiency.
+ *
+ * \author Tom Vercauteren, INRIA & Mauna Kea Technologies
+ *
+ * This implementation was taken from the Insight Journal paper:
+ * http://hdl.handle.net/1926/510
+ *
+ * \ingroup ImageFunctions
+ * \ingroup ITKReview
+ *
+ * ADAPTED by P Yushkevich to VectorImage
+ */
+template<
+ typename TInputImage,
+ typename TCoordRep = float >
+class VectorImageCentralDifferenceImageFunction:
+ public ImageFunction< TInputImage,
+ typename TInputImage::PixelType,
+ TCoordRep >
+{
+public:
+ typedef typename TInputImage::PixelType InputPixelType;
+
+ /** Dimension underlying input image. */
+ itkStaticConstMacro(ImageDimension, unsigned int,
+ TInputImage::ImageDimension);
+
+ /** Standard class typedefs. */
+ typedef VectorImageCentralDifferenceImageFunction Self;
+ typedef ImageFunction< TInputImage,
+ typename TInputImage::PixelType,
+ TCoordRep > Superclass;
+ typedef SmartPointer< Self > Pointer;
+ typedef SmartPointer< const Self > ConstPointer;
+
+ /** Run-time type information (and related methods). */
+ itkTypeMacro(VectorImageCentralDifferenceImageFunction, ImageFunction);
+
+ /** Method for creation through the object factory. */
+ itkNewMacro(Self);
+
+ /** InputImageType typedef support. */
+ typedef TInputImage InputImageType;
+
+ /** OutputType typdef support. */
+ typedef typename Superclass::OutputType OutputType;
+
+ /** Index typedef support. */
+ typedef typename Superclass::IndexType IndexType;
+
+ /** ContinuousIndex typedef support. */
+ typedef typename Superclass::ContinuousIndexType ContinuousIndexType;
+
+ /** Point typedef support. */
+ typedef typename Superclass::PointType PointType;
+
+ /** Evalulate the image derivative by central differencing at specified index.
+ *
+ * No bounds checking is done.
+ * The point is assume to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual void EvaluateAtIndex(const IndexType & index, OutputType &) const;
+
+ virtual OutputType EvaluateAtIndex(const IndexType & index) const
+ {
+ OutputType res(ImageDimension * this->GetInputImage()->GetNumberOfComponentsPerPixel());
+ this->EvaluateAtIndex(index, res);
+ return res;
+ }
+
+ /** Evalulate the image derivative by central differencing at non-integer
+ * positions.
+ *
+ * No bounds checking is done.
+ * The point is assumed to lie within the image buffer.
+ *
+ * ImageFunction::IsInsideBuffer() can be used to check bounds before
+ * calling the method. */
+ virtual OutputType Evaluate(const PointType & point) const
+ {
+ IndexType index;
+
+ this->ConvertPointToNearestIndex(point, index);
+ return this->EvaluateAtIndex(index);
+ }
+
+ virtual OutputType EvaluateAtContinuousIndex(
+ const ContinuousIndexType & cindex) const
+ {
+ IndexType index;
+
+ this->ConvertContinuousIndexToNearestIndex(cindex, index);
+ return this->EvaluateAtIndex(index);
+ }
+
+protected:
+ VectorImageCentralDifferenceImageFunction();
+ ~VectorImageCentralDifferenceImageFunction(){}
+ void PrintSelf(std::ostream & os, Indent indent) const;
+
+private:
+ VectorImageCentralDifferenceImageFunction(const Self &); //purposely not
+ // implemented
+ void operator=(const Self &); //purposely not
+ // implemented
+
+};
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkVectorImageCentralDifferenceImageFunction.hxx"
+#endif
+
+#endif
diff --git a/Submodules/greedy/src/itkVectorImageCentralDifferenceImageFunction.hxx b/Submodules/greedy/src/itkVectorImageCentralDifferenceImageFunction.hxx
new file mode 100644
index 0000000..423c573
--- /dev/null
+++ b/Submodules/greedy/src/itkVectorImageCentralDifferenceImageFunction.hxx
@@ -0,0 +1,93 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+#ifndef itkVectorImageCentralDifferenceImageFunction_hxx
+#define itkVectorImageCentralDifferenceImageFunction_hxx
+
+#include "itkVectorImageCentralDifferenceImageFunction.h"
+
+namespace itk
+{
+/**
+ * Constructor
+ */
+template< typename TInputImage, typename TCoordRep >
+VectorImageCentralDifferenceImageFunction< TInputImage, TCoordRep >
+::VectorImageCentralDifferenceImageFunction()
+{
+}
+
+/**
+ *
+ */
+template< typename TInputImage, typename TCoordRep >
+void
+VectorImageCentralDifferenceImageFunction< TInputImage, TCoordRep >
+::PrintSelf(std::ostream & os, Indent indent) const
+{
+ this->Superclass::PrintSelf(os, indent);
+}
+
+/**
+ *
+ */
+template< typename TInputImage, typename TCoordRep >
+void VectorImageCentralDifferenceImageFunction<TInputImage, TCoordRep>
+::EvaluateAtIndex(const IndexType & index, OutputType &derivative) const
+{
+ int nc = this->GetInputImage()->GetNumberOfComponentsPerPixel();
+ derivative.Fill(0.0);
+
+ IndexType neighIndex = index;
+
+ const typename InputImageType::SizeType & size =
+ this->GetInputImage()->GetBufferedRegion().GetSize();
+ const typename InputImageType::IndexType & start =
+ this->GetInputImage()->GetBufferedRegion().GetIndex();
+
+ int p = 0;
+ for ( unsigned int dim = 0; dim < TInputImage::ImageDimension; dim++ )
+ {
+ // bounds checking
+ if ( index[dim] < start[dim] + 1
+ || index[dim] > ( start[dim] + static_cast< OffsetValueType >( size[dim] ) - 2 ) )
+ {
+ p += nc;
+ }
+ else
+ {
+ // compute derivative
+ const double deriv_weight = 0.5 / this->GetInputImage()->GetSpacing()[dim];
+
+ neighIndex[dim] += 1;
+ const InputPixelType pixf = this->GetInputImage()->GetPixel(neighIndex);
+
+ neighIndex[dim] -= 2;
+ const InputPixelType pixb = this->GetInputImage()->GetPixel(neighIndex);
+
+ neighIndex[dim] += 1;
+
+ for ( unsigned int vdim = 0; vdim < nc; ++vdim )
+ {
+ derivative[p++] = (pixf[vdim] - pixb[vdim]) * deriv_weight;
+ }
+ }
+ }
+}
+} // end namespace itk
+
+#endif
diff --git a/Submodules/greedy/src/lddmm_common.h b/Submodules/greedy/src/lddmm_common.h
new file mode 100644
index 0000000..4297020
--- /dev/null
+++ b/Submodules/greedy/src/lddmm_common.h
@@ -0,0 +1,74 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef _LDDMM_COMMON_H_
+#define _LDDMM_COMMON_H_
+
+typedef unsigned int uint;
+
+typedef double myreal;
+
+// A macro for defining named inputs and outputs to ITK filters
+#define itkNamedInputMacro(name, type, key) \
+ virtual void Set##name (type *_arg) \
+ { \
+ itk::ProcessObject::SetInput(key, _arg); \
+ } \
+ \
+ virtual type * Get##name() \
+ { \
+ return dynamic_cast<type *>(itk::ProcessObject::GetInput(key)); \
+ }
+
+#define itkNamedInputGetMacro(name, type, key) \
+ virtual type * Get##name() \
+ { \
+ return dynamic_cast<type *>(itk::ProcessObject::GetInput(key)); \
+ }
+
+// A macro for defining named inputs and outputs to ITK filters
+#define itkNamedOutputMacro(name, type, key) \
+ virtual void Set##name (type *_arg) \
+ { \
+ itk::ProcessObject::SetOutput(key, _arg); \
+ } \
+ \
+ virtual type * Get##name() \
+ { \
+ return dynamic_cast<type *>(itk::ProcessObject::GetOutput(key)); \
+ }
+
+#define itkNamedOutputGetMacro(name, type, key) \
+ virtual type * Get##name() \
+ { \
+ return dynamic_cast<type *>(itk::ProcessObject::GetOutput(key)); \
+ }
+
+
+
+
+
+#endif
diff --git a/Submodules/greedy/src/lddmm_data.cxx b/Submodules/greedy/src/lddmm_data.cxx
new file mode 100644
index 0000000..d02046b
--- /dev/null
+++ b/Submodules/greedy/src/lddmm_data.cxx
@@ -0,0 +1,1433 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "lddmm_data.h"
+#include "itkImageRegionIterator.h"
+#include "SimpleWarpImageFilter.h"
+#include "itkNumericTraitsCovariantVectorPixel.h"
+#include "itkOptVectorLinearInterpolateImageFunction.h"
+#include "itkLinearInterpolateImageFunction.h"
+#include "itkAddImageFilter.h"
+#include "itkSubtractImageFilter.h"
+#include "itkMultiplyImageFilter.h"
+#include "itkGradientImageFilter.h"
+#include "itkUnaryFunctorImageFilter.h"
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include "itkVectorImage.h"
+#include "itkDisplacementFieldJacobianDeterminantFilter.h"
+#include "itkSmoothingRecursiveGaussianImageFilter.h"
+#include "itkDiscreteGaussianImageFilter.h"
+#include "itkMinimumMaximumImageFilter.h"
+#include "itkShrinkImageFilter.h"
+#include "itkResampleImageFilter.h"
+#include "itkVectorResampleImageFilter.h"
+#include "itkNearestNeighborInterpolateImageFunction.h"
+#include "itkVectorNearestNeighborInterpolateImageFunction.h"
+#include "itkBinaryThresholdImageFilter.h"
+#include "FastWarpCompositeImageFilter.h"
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::alloc_vf(VelocityField &vf, uint nt, ImageBaseType *ref)
+{
+ vf.resize(nt);
+ for(uint i = 0; i < nt; i++)
+ alloc_vimg(vf[i], ref);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::alloc_vimg(VectorImagePointer &img, ImageBaseType *ref)
+{
+ img = VectorImageType::New();
+ img->SetRegions(ref->GetBufferedRegion());
+ img->CopyInformation(ref);
+ img->Allocate();
+ img->FillBuffer(Vec(0.0));
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::alloc_cimg(CompositeImagePointer &img, ImageBaseType *ref, int n_comp)
+{
+ img = CompositeImageType::New();
+ img->SetRegions(ref->GetBufferedRegion());
+ img->CopyInformation(ref);
+ img->SetNumberOfComponentsPerPixel(n_comp);
+ img->Allocate();
+
+ typename CompositeImageType::PixelType cpix;
+ cpix.SetSize(n_comp);
+ cpix.Fill(0.0);
+ img->FillBuffer(cpix);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::alloc_img(ImagePointer &img, ImageBaseType *ref)
+{
+ img = ImageType::New();
+ img->SetRegions(ref->GetBufferedRegion());
+ img->CopyInformation(ref);
+ img->Allocate();
+ img->FillBuffer(0.0);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::init(LDDMMData<TFloat, VDim> &p,
+ ImageType *fix, ImageType *mov,
+ uint nt, double alpha, double gamma, double sigma)
+{
+ p.fix = fix;
+ p.mov = mov;
+ p.alpha = alpha;
+ p.sigma = sigma;
+ p.gamma = gamma;
+ p.nt = nt;
+ p.dt = 1.0 / (nt - 1.0);
+ p.sigma_sq = sigma * sigma;
+
+ // Initialize N and R
+ p.r = fix->GetBufferedRegion();
+ p.nv = fix->GetBufferedRegion().GetNumberOfPixels();
+ for(uint i = 0; i < VDim; i++)
+ p.n[i] = p.r.GetSize()[i];
+
+ // Initialize the velocity fields
+ alloc_vf(p.v, nt, fix);
+ alloc_vf(p.a, nt, fix);
+ alloc_vf(p.f, nt, fix);
+
+ // Initialize kernel terms
+ alloc_img(p.f_kernel, fix);
+ alloc_img(p.f_kernel_sq, fix);
+
+ // Compute these images
+ ImageIterator it(p.f_kernel, p.r), itsq(p.f_kernel_sq, p.r);
+ for(; !it.IsAtEnd(); ++it, ++itsq)
+ {
+ TFloat val = 0.0;
+ for(uint j = 0; j < VDim; j++)
+ val += 1.0 - cos(it.GetIndex()[j] * 2.0 * vnl_math::pi / p.n[j]);
+ it.Set(p.gamma + 2.0 * p.alpha * p.nv * val);
+ itsq.Set(it.Get() * it.Get());
+ }
+
+ // Initialize temporary vector field
+ alloc_vimg(p.vtmp, fix);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::compute_navier_stokes_kernel(ImageType *kernel, double alpha, double gamma)
+{
+ ImageIterator it(kernel, kernel->GetBufferedRegion());
+ itk::Size<VDim> sz = kernel->GetBufferedRegion().GetSize();
+ double alpha_scale = 2.0 * alpha * kernel->GetBufferedRegion().GetNumberOfPixels();
+
+ for(; !it.IsAtEnd(); ++it)
+ {
+ TFloat val = 0.0;
+ for(uint j = 0; j < VDim; j++)
+ val += 1.0 - cos(it.GetIndex()[j] * 2.0 * vnl_math::pi / sz[j]);
+ double k = gamma + alpha_scale * val;
+ it.Set(k * k);
+ }
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::interp_vimg(VectorImageType *data, VectorImageType *field,
+ TFloat def_scale, VectorImageType *out, bool use_nn, bool phys_space)
+{
+ /*
+ * THIS IS OLD CODE for VECTOR INTERPOLATION
+ * TODO: there is a small discrepancy between this and the new code
+ * that I never figured out
+ */
+
+ /*
+ // Create a warp filter
+ typedef itk::SimpleWarpImageFilter<
+ VectorImageType, VectorImageType, VectorImageType, TFloat> WarpFilterType;
+ typename WarpFilterType::Pointer flt = WarpFilterType::New();
+
+ // Create an interpolation function
+ typedef itk::OptVectorLinearInterpolateImageFunction<VectorImageType, TFloat> InterpType;
+ typedef itk::VectorNearestNeighborInterpolateImageFunction<VectorImageType, TFloat> NNInterpType;
+
+ typename InterpType::Pointer func = InterpType::New();
+ typename NNInterpType::Pointer funcNN = NNInterpType::New();
+
+ // Graft output of the warp filter
+ flt->GraftOutput(out);
+
+ // Set inputs
+ flt->SetInput(data);
+ if(use_nn)
+ {
+
+ flt->SetInterpolator(funcNN);
+ }
+ else
+ flt->SetInterpolator(func);
+ flt->SetDeformationField(field);
+ flt->SetDeformationScaling(def_scale);
+ flt->Update();
+ */
+
+ typedef FastWarpCompositeImageFilter<VectorImageType, VectorImageType, VectorImageType> WF;
+ typename WF::Pointer wf = WF::New();
+ wf->SetDeformationField(field);
+ wf->SetMovingImage(data);
+ wf->GraftOutput(out);
+ wf->SetDeformationScaling(def_scale);
+ wf->SetUseNearestNeighbor(use_nn);
+ wf->SetUsePhysicalSpace(phys_space);
+ wf->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::interp_img(ImageType *data, VectorImageType *field, ImageType *out, bool use_nn, bool phys_space)
+{
+ typedef FastWarpCompositeImageFilter<ImageType, ImageType, VectorImageType> WF;
+ typename WF::Pointer wf = WF::New();
+ wf->SetDeformationField(field);
+ wf->SetMovingImage(data);
+ wf->GraftOutput(out);
+ wf->SetUseNearestNeighbor(use_nn);
+ wf->SetUsePhysicalSpace(phys_space);
+ wf->Update();
+
+/*
+ // Create a warp filter
+ typedef itk::SimpleWarpImageFilter<
+ ImageType, ImageType, VectorImageType, TFloat> WarpFilterType;
+ typename WarpFilterType::Pointer flt = WarpFilterType::New();
+
+ // Create an interpolation function
+ typedef itk::LinearInterpolateImageFunction<ImageType, TFloat> InterpType;
+ typedef itk::NearestNeighborInterpolateImageFunction<ImageType, TFloat> NNInterpType;
+
+ typename InterpType::Pointer func = InterpType::New();
+ typename NNInterpType::Pointer funcNN = NNInterpType::New();
+
+ // Graft output of the warp filter
+ flt->GraftOutput(out);
+
+ // Set inputs
+ flt->SetInput(data);
+ if(use_nn)
+ flt->SetInterpolator(funcNN);
+ else
+ flt->SetInterpolator(func);
+ flt->SetDeformationField(field);
+ flt->SetDeformationScaling(1.0);
+ flt->Update();
+ */
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::interp_cimg(CompositeImageType *data, VectorImageType *field, CompositeImageType *out,
+ bool use_nn, bool phys_space)
+{
+ typedef FastWarpCompositeImageFilter<CompositeImageType, CompositeImageType, VectorImageType> WF;
+ typename WF::Pointer wf = WF::New();
+ wf->SetDeformationField(field);
+ wf->SetMovingImage(data);
+ wf->GraftOutput(out);
+ wf->SetUseNearestNeighbor(use_nn);
+ wf->SetUsePhysicalSpace(phys_space);
+ wf->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_add_in_place(VectorImageType *trg, VectorImageType *a)
+{
+ typedef itk::AddImageFilter<VectorImageType> AddFilter;
+ typename AddFilter::Pointer flt = AddFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_subtract_in_place(VectorImageType *trg, VectorImageType *a)
+{
+ typedef itk::SubtractImageFilter<VectorImageType> SubtractFilter;
+ typename SubtractFilter::Pointer flt = SubtractFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+// Scalar math
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_multiply_in_place(VectorImageType *trg, ImageType *s)
+{
+ typedef itk::MultiplyImageFilter<
+ VectorImageType, ImageType, VectorImageType> MultiplyFilter;
+ typename MultiplyFilter::Pointer flt = MultiplyFilter::New();
+ flt->SetInput1(trg);
+ flt->SetInput2(s);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+// Scalar math
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_add_in_place(ImagePointer &trg, ImageType *a)
+{
+ typedef itk::AddImageFilter<ImageType> AddFilter;
+ typename AddFilter::Pointer flt = AddFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_subtract_in_place(ImagePointer &trg, ImageType *a)
+{
+ typedef itk::SubtractImageFilter<ImageType> SubtractFilter;
+ typename SubtractFilter::Pointer flt = SubtractFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_multiply_in_place(ImagePointer &trg, ImageType *a)
+{
+ typedef itk::MultiplyImageFilter<ImageType> MultiplyFilter;
+ typename MultiplyFilter::Pointer flt = MultiplyFilter::New();
+ flt->SetInput(0,trg);
+ flt->SetInput(1,a);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMData<TFloat, VDim>
+::vimg_euclidean_norm_sq(VectorImageType *trg)
+{
+ // Add all voxels in the image
+ double accum = 0.0;
+ typedef itk::ImageRegionIterator<VectorImageType> Iter;
+ for(Iter it(trg, trg->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ for(uint d = 0; d < VDim; d++)
+ accum += it.Value()[d] * it.Value()[d];
+ }
+ return (TFloat) accum;
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMData<TFloat, VDim>
+::img_euclidean_norm_sq(ImageType *trg)
+{
+ // Add all voxels in the image
+ double accum = 0.0;
+ typedef itk::ImageRegionIterator<ImageType> Iter;
+ for(Iter it(trg, trg->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ { accum += it.Value() * it.Value(); }
+ return (TFloat) accum;
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMData<TFloat, VDim>
+::img_voxel_sum(ImageType *trg)
+{
+ // Add all voxels in the image
+ double accum = 0.0;
+ typedef itk::ImageRegionIterator<ImageType> Iter;
+ for(Iter it(trg, trg->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ accum += it.Value();
+ return (TFloat) accum;
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_min_max(ImageType *src, TFloat &out_min, TFloat &out_max)
+{
+ // Add all voxels in the image
+ typedef itk::MinimumMaximumImageFilter<ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(src);
+ filter->Update();
+ out_min = filter->GetMinimum();
+ out_max = filter->GetMaximum();
+}
+
+
+template <class TFloat, uint VDim>
+class VectorScaleFunctor
+{
+public:
+ VectorScaleFunctor() { this->Scale = 1.0; }
+ typedef itk::CovariantVector<TFloat,VDim> Vec;
+
+ Vec operator() (const Vec &x)
+ { return x * Scale; }
+
+ bool operator== (const VectorScaleFunctor<TFloat, VDim> &other)
+ { return Scale == other.Scale; }
+
+ bool operator!= (const VectorScaleFunctor<TFloat, VDim> &other)
+ { return Scale != other.Scale; }
+
+ TFloat Scale;
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_scale_in_place(VectorImageType *trg, TFloat s)
+{
+ typedef VectorScaleFunctor<TFloat, VDim> Functor;
+ typedef itk::UnaryFunctorImageFilter<
+ VectorImageType, VectorImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ func.Scale = s;
+ flt->SetFunctor(func);
+ flt->SetInput(trg);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_scale(VectorImageType*src, TFloat s, VectorImageType *trg)
+{
+ typedef VectorScaleFunctor<TFloat, VDim> Functor;
+ typedef itk::UnaryFunctorImageFilter<
+ VectorImageType, VectorImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ func.Scale = s;
+ flt->SetFunctor(func);
+ flt->SetInput(src);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+class VectorScaleAddFunctor
+{
+public:
+ VectorScaleAddFunctor() { this->Scale = 1.0; }
+ typedef itk::CovariantVector<TFloat,VDim> Vec;
+
+ Vec operator() (const Vec &x, const Vec &y)
+ { return x + y * Scale; }
+
+ bool operator== (const VectorScaleAddFunctor<TFloat, VDim> &other)
+ { return Scale == other.Scale; }
+
+ bool operator!= (const VectorScaleAddFunctor<TFloat, VDim> &other)
+ { return Scale != other.Scale; }
+
+ TFloat Scale;
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_add_scaled_in_place(VectorImageType *trg, VectorImageType *a, TFloat s)
+{
+ typedef VectorScaleAddFunctor<TFloat, VDim> Functor;
+ typedef itk::BinaryFunctorImageFilter<
+ VectorImageType, VectorImageType, VectorImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ func.Scale = s;
+ flt->SetFunctor(func);
+ flt->SetInput1(trg);
+ flt->SetInput2(a);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+class VectorDotProduct
+{
+public:
+ VectorDotProduct() {}
+ typedef itk::CovariantVector<TFloat,VDim> Vec;
+
+ TFloat operator() (const Vec &x, const Vec &y)
+ {
+ TFloat dp = 0.0;
+ for(uint d = 0; d < VDim; d++)
+ dp += x[d] * y[d];
+ return dp;
+ }
+
+ bool operator== (const VectorDotProduct<TFloat, VDim> &other)
+ { return true; }
+
+ bool operator!= (const VectorDotProduct<TFloat, VDim> &other)
+ { return false; }
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_euclidean_inner_product(ImagePointer &trg, VectorImageType *a, VectorImageType *b)
+{
+ typedef VectorDotProduct<TFloat, VDim> Functor;
+ typedef itk::BinaryFunctorImageFilter<
+ VectorImageType, VectorImageType, ImageType, Functor> Filter;
+ typename Filter::Pointer flt = Filter::New();
+
+ Functor func;
+ flt->SetFunctor(func);
+ flt->SetInput1(a);
+ flt->SetInput2(b);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::compute_semi_lagrangean_a()
+{
+ for(uint i = 0; i < nt; i++)
+ {
+ a[i]->FillBuffer(Vec(0.0));
+ for (uint j = 0; j < 5; j++)
+ {
+ interp_vimg(v[i], a[i], -0.5, a[i]);
+ vimg_scale_in_place(a[i], dt);
+ itk::Index<VDim> x;
+ x[0] = 63; x[1] = 63;
+ }
+ }
+
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::integrate_phi_t0()
+{
+ // Go through and compute phi_t0 for each time point
+ for(int m = 0; m < (int) nt; m++)
+ if(m==0)
+ {
+ f[m]->FillBuffer(Vec(0.0));
+ }
+ else
+ {
+ interp_vimg(f[m-1], a[m], -1.0, f[m]);
+ vimg_subtract_in_place(f[m], a[m]);
+ }
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::integrate_phi_t1()
+{
+ for(int m = nt-1; m >= 0; m--)
+ {
+ if(m == (int) nt-1)
+ {
+ f[m]->FillBuffer(Vec(0.0));
+ }
+ else
+ {
+ interp_vimg(f[m+1], a[m], 1.0, f[m]);
+ vimg_add_in_place(f[m], a[m]);
+ }
+ }
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::field_jacobian_det(VectorImageType *vec, ImageType *out)
+{
+ typedef itk::DisplacementFieldJacobianDeterminantFilter<
+ VectorImageType, TFloat, ImageType> Filter;
+ typename Filter::Pointer filter = Filter::New();
+ filter->SetInput(vec);
+ filter->SetUseImageSpacingOff();
+ filter->GraftOutput(out);
+ filter->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::image_gradient(ImageType *src, VectorImageType *grad)
+{
+ // Create a gradient image filter
+ typedef itk::GradientImageFilter<ImageType, TFloat, TFloat> Filter;
+ typename Filter::Pointer flt = Filter::New();
+ flt->SetInput(src);
+ flt->GraftOutput(grad);
+ flt->SetUseImageSpacingOff();
+ flt->SetUseImageDirection(false);
+ flt->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_smooth(ImageType *src, ImageType *trg, double sigma)
+{
+ // typedef itk::SmoothingRecursiveGaussianImageFilter<ImageType, ImageType> Filter;
+ typedef itk::DiscreteGaussianImageFilter<ImageType, ImageType> Filter;
+ typename Filter::Pointer flt = Filter::New();
+ flt->SetInput(src);
+ // flt->SetSigma(sigma);
+ flt->SetVariance(sigma * sigma);
+ flt->GraftOutput(trg);
+ flt->Update();
+}
+
+#include "itkVectorIndexSelectionCastImageFilter.h"
+#include "itkComposeImageFilter.h"
+#include "itkMinimumMaximumImageFilter.h"
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_smooth(VectorImageType *src, VectorImageType *trg, double sigma)
+{
+ Vec sa; sa.Fill(sigma);
+ vimg_smooth(src, trg, sa);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_smooth(VectorImageType *src, VectorImageType *trg, Vec sigma)
+{
+ typedef itk::SmoothingRecursiveGaussianImageFilter<VectorImageType, VectorImageType> Filter;
+ typename Filter::Pointer fltSmooth = Filter::New();
+ fltSmooth->SetInput(src);
+ fltSmooth->SetSigmaArray(sigma);
+ // fltSmooth->SetSigma(sigma);
+ // fltSmooth->GraftOutput(trg);
+ fltSmooth->Update();
+
+ // TODO: this is a work-around for a stupid bug with this recursive filter. When the data
+ // type is float, the filter does not allow me to graft an output
+ LDDMMData<TFloat, VDim>::vimg_copy(fltSmooth->GetOutput(), trg);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_smooth_withborder(VectorImageType *src, VectorImageType *trg, Vec sigma, int border_size)
+{
+
+ // Define a region of interest
+ RegionType region = src->GetBufferedRegion();
+ region.ShrinkByRadius(border_size);
+
+ // Perform smoothing
+ vimg_smooth(src, trg, sigma);
+
+ // Clear the border
+ Vec zerovec; zerovec.Fill(0);
+ typedef itk::ImageRegionIteratorWithIndex<VectorImageType> VIterator;
+ for(VIterator it(trg, trg->GetBufferedRegion()); !it.IsAtEnd(); ++it)
+ {
+ if(!region.IsInside(it.GetIndex()))
+ {
+ it.Set(zerovec);
+ }
+ }
+}
+
+
+template <class TFloat, unsigned int VDim>
+struct VectorSquareNormFunctor
+{
+ template <class TVector> TFloat operator() (const TVector &v)
+ {
+ TFloat norm_sqr = 0.0;
+ for(int i = 0; i < VDim; i++)
+ norm_sqr += v[i] * v[i];
+ return norm_sqr;
+ }
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_norm_min_max(VectorImageType *image, ImagePointer &normsqr,
+ TFloat &min_norm, TFloat &max_norm)
+{
+ // Compute the squared norm of the displacement
+ typedef VectorSquareNormFunctor<TFloat, VDim> NormFunctor;
+ typedef itk::UnaryFunctorImageFilter<VectorImageType, ImageType, NormFunctor> NormFilter;
+ typename NormFilter::Pointer fltNorm = NormFilter::New();
+ fltNorm->SetInput(image);
+ fltNorm->GraftOutput(normsqr);
+ fltNorm->Update();
+
+ // Compute the maximum squared norm of the displacement
+ img_min_max(normsqr, min_norm, max_norm);
+
+ min_norm = sqrt(min_norm);
+ max_norm = sqrt(max_norm);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_normalize_to_fixed_max_length(VectorImageType *trg, ImagePointer &normsqr,
+ double max_displacement, bool scale_down_only)
+{
+ // Compute the squared norm of the displacement
+ typedef VectorSquareNormFunctor<TFloat, VDim> NormFunctor;
+ typedef itk::UnaryFunctorImageFilter<VectorImageType, ImageType, NormFunctor> NormFilter;
+ typename NormFilter::Pointer fltNorm = NormFilter::New();
+ fltNorm->SetInput(trg);
+ fltNorm->GraftOutput(normsqr);
+ fltNorm->Update();
+
+ // Compute the maximum squared norm of the displacement
+ TFloat nsq_min, nsq_max;
+ img_min_max(normsqr, nsq_min, nsq_max);
+
+ // Compute the scale functor
+ TFloat scale = max_displacement / sqrt(nsq_max);
+
+ // Apply the scale
+ if(scale_down_only && scale >= 1.0)
+ return;
+ else
+ vimg_scale_in_place(trg, scale);
+}
+
+
+
+namespace lddmm_data_io {
+
+template <class TInputImage, class TOutputImage>
+void
+write_cast(TInputImage *image, const char *filename)
+{
+ typedef itk::CastImageFilter<TInputImage, TOutputImage> CastType;
+ typename CastType::Pointer cast = CastType::New();
+ cast->SetInput(image);
+
+ typedef itk::ImageFileWriter<TOutputImage> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetInput(cast->GetOutput());
+ writer->SetFileName(filename);
+ writer->SetUseCompression(true);
+ writer->Update();
+}
+
+template <class TInputImage, class TOutputComponent>
+struct image_type_cast
+{
+};
+
+template <class TPixel, unsigned int VDim, class TOutputComponent>
+struct image_type_cast< itk::Image<TPixel, VDim>, TOutputComponent>
+{
+ typedef itk::Image<TOutputComponent, VDim> OutputImageType;
+};
+
+template <class TPixel, unsigned int VDim, class TOutputComponent>
+struct image_type_cast< itk::Image<itk::CovariantVector<TPixel>, VDim>, TOutputComponent>
+{
+ typedef itk::Image<itk::CovariantVector<TOutputComponent>, VDim> OutputImageType;
+};
+
+template <class TPixel, unsigned int VDim, class TOutputComponent>
+struct image_type_cast< itk::VectorImage<TPixel, VDim>, TOutputComponent>
+{
+ typedef itk::VectorImage<TOutputComponent, VDim> OutputImageType;
+};
+
+template <class TInputImage>
+void write_cast_to_iocomp(TInputImage *image, const char *filename,
+ itk::ImageIOBase::IOComponentType comp)
+{
+ switch(comp)
+ {
+ case itk::ImageIOBase::UCHAR :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, unsigned char>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::CHAR :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, char>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::USHORT :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, unsigned short>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::SHORT :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, short>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::UINT :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, unsigned int>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::INT :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, int>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::ULONG :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, unsigned long>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::LONG :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, long>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::FLOAT :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, float>::OutputImageType>(image, filename);
+ break;
+ case itk::ImageIOBase::DOUBLE :
+ write_cast<TInputImage, typename image_type_cast<TInputImage, double>::OutputImageType>(image, filename);
+ break;
+ default:
+ typedef itk::ImageFileWriter<TInputImage> WriterType;
+ typename WriterType::Pointer writer = WriterType::New();
+ writer->SetInput(image);
+ writer->SetFileName(filename);
+ writer->SetUseCompression(true);
+ writer->Update();
+ }
+}
+
+} // namespace
+
+
+template <class TFloat, uint VDim>
+typename LDDMMData<TFloat, VDim>::IOComponentType
+LDDMMData<TFloat, VDim>
+::img_read(const char *fn, ImagePointer &trg)
+{
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(fn);
+ reader->Update();
+ trg = reader->GetOutput();
+
+ return reader->GetImageIO()->GetComponentType();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_write(ImageType *src, const char *fn, IOComponentType comp)
+{
+ lddmm_data_io::write_cast_to_iocomp(src, fn, comp);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::cimg_write(CompositeImageType *src, const char *fn, IOComponentType comp)
+{
+ lddmm_data_io::write_cast_to_iocomp(src, fn, comp);
+}
+
+
+template <class TFloat, uint VDim>
+typename LDDMMData<TFloat, VDim>::IOComponentType
+LDDMMData<TFloat, VDim>
+::vimg_read(const char *fn, VectorImagePointer &trg)
+{
+ typedef itk::ImageFileReader<VectorImageType> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(fn);
+ reader->Update();
+ trg = reader->GetOutput();
+
+ return reader->GetImageIO()->GetComponentType();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_write(VectorImageType *src, const char *fn, IOComponentType comp)
+{
+ // Cast to vector image type
+ typedef itk::VectorImage<TFloat, VDim> OutputImageType;
+ typename OutputImageType::Pointer output = OutputImageType::New();
+ output->CopyInformation(src);
+ output->SetRegions(src->GetBufferedRegion());
+ output->SetNumberOfComponentsPerPixel(VDim);
+
+ // Override the data pointer
+ output->GetPixelContainer()->SetImportPointer(
+ (TFloat *) src->GetBufferPointer(),
+ VDim * src->GetPixelContainer()->Size(), false);
+
+ // Write
+ lddmm_data_io::write_cast_to_iocomp(output.GetPointer(), fn, comp);
+}
+
+template <class TFloat, uint VDim>
+typename LDDMMData<TFloat, VDim>::IOComponentType
+LDDMMData<TFloat, VDim>
+::cimg_read(const char *fn, CompositeImagePointer &trg)
+{
+ typedef itk::ImageFileReader<CompositeImageType> ReaderType;
+ typename ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(fn);
+ reader->Update();
+ trg = reader->GetOutput();
+
+ return reader->GetImageIO()->GetComponentType();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vfield_read(uint nt, const char *fnpat, VelocityField &v)
+{
+ v.clear();
+ for(uint i = 0; i < nt; i++)
+ {
+ char fname[1024];
+ sprintf(fname, fnpat, i);
+ VectorImagePointer vt;
+ vimg_read(fname, vt);
+ v.push_back(vt);
+ }
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_copy(const VectorImageType *src, VectorImageType *trg)
+{
+ typedef itk::CastImageFilter<VectorImageType, VectorImageType> CastFilter;
+ typename CastFilter::Pointer fltCast = CastFilter::New();
+ fltCast->SetInput(src);
+ fltCast->GraftOutput(trg);
+ fltCast->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_copy(const ImageType *src, ImageType *trg)
+{
+ typedef itk::CastImageFilter<ImageType, ImageType> CastFilter;
+ typename CastFilter::Pointer fltCast = CastFilter::New();
+ fltCast->SetInput(src);
+ fltCast->GraftOutput(trg);
+ fltCast->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_shrink(ImageType *src, ImageType *trg, int factor)
+{
+ typedef itk::ShrinkImageFilter<ImageType, ImageType> Filter;
+ typename Filter::Pointer filter = Filter::New();
+ filter->SetInput(src);
+ filter->SetShrinkFactors(factor);
+ filter->GraftOutput(trg);
+ filter->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_resample_identity(ImageType *src, ImageBaseType *ref, ImageType *trg)
+{
+ typedef itk::ResampleImageFilter<ImageType, ImageType, TFloat> ResampleFilter;
+ typedef itk::IdentityTransform<TFloat, VDim> TranType;
+ typedef itk::LinearInterpolateImageFunction<ImageType, TFloat> InterpType;
+
+ typename ResampleFilter::Pointer filter = ResampleFilter::New();
+ typename TranType::Pointer tran = TranType::New();
+ typename InterpType::Pointer func = InterpType::New();
+
+ filter->SetInput(src);
+ filter->SetTransform(tran);
+ filter->SetInterpolator(func);
+ filter->UseReferenceImageOn();
+ filter->SetReferenceImage(ref);
+ filter->GraftOutput(trg);
+ filter->Update();
+}
+
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_downsample(ImageType *src, ImageType *trg, double factor)
+{
+ // Begin by smoothing the image
+ typedef itk::SmoothingRecursiveGaussianImageFilter<ImageType, ImageType> SmoothType;
+ typename SmoothType::Pointer fltSmooth = SmoothType::New();
+ fltSmooth->SetInput(src);
+ fltSmooth->SetSigmaArray(0.5 * factor * src->GetSpacing());
+
+ // Now resample the image to occupy the same physical space
+ typedef itk::ResampleImageFilter<ImageType, ImageType, TFloat> ResampleFilter;
+ typedef itk::IdentityTransform<TFloat, VDim> TranType;
+ typedef itk::LinearInterpolateImageFunction<ImageType, TFloat> InterpType;
+
+ typename ResampleFilter::Pointer filter = ResampleFilter::New();
+ typename TranType::Pointer tran = TranType::New();
+ typename InterpType::Pointer func = InterpType::New();
+
+ // Compute the size of the new image
+ typename ImageType::SizeType sz;
+ for(int i = 0; i < VDim; i++)
+ sz[i] = (unsigned long) vcl_ceil(src->GetBufferedRegion().GetSize()[i] / factor);
+
+ // Compute the spacing of the new image
+ typename ImageType::SpacingType spc_pre = src->GetSpacing();
+ typename ImageType::SpacingType spc_post = spc_pre;
+ for(size_t i = 0; i < VDim; i++)
+ spc_post[i] *= src->GetBufferedRegion().GetSize()[i] * 1.0 / sz[i];
+
+ // Get the bounding box of the input image
+ typename ImageType::PointType origin_pre = src->GetOrigin();
+
+ // Recalculate the origin. The origin describes the center of voxel 0,0,0
+ // so that as the voxel size changes, the origin will change as well.
+ typename ImageType::SpacingType off_pre = (src->GetDirection() * spc_pre) * 0.5;
+ typename ImageType::SpacingType off_post = (src->GetDirection() * spc_post) * 0.5;
+ typename ImageType::PointType origin_post = origin_pre - off_pre + off_post;
+
+ // Weird - have to allocate the output image?
+ trg->SetRegions(sz);
+ trg->SetOrigin(origin_post);
+ trg->SetSpacing(spc_post);
+ trg->SetDirection(src->GetDirection());
+ trg->Allocate();
+
+ // Set the image sizes and spacing.
+ filter->SetSize(sz);
+ filter->SetOutputSpacing(spc_post);
+ filter->SetOutputOrigin(origin_post);
+ filter->SetOutputDirection(src->GetDirection());
+ filter->SetInput(fltSmooth->GetOutput());
+ filter->SetTransform(tran);
+ filter->SetInterpolator(func);
+
+ filter->GraftOutput(trg);
+ filter->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::vimg_resample_identity(VectorImageType *src, ImageBaseType *ref, VectorImageType *trg)
+{
+ typedef itk::VectorResampleImageFilter<VectorImageType, VectorImageType, TFloat> ResampleFilter;
+ typedef itk::IdentityTransform<TFloat, VDim> TranType;
+ typedef itk::OptVectorLinearInterpolateImageFunction<VectorImageType, TFloat> InterpType;
+
+ typename ResampleFilter::Pointer filter = ResampleFilter::New();
+ typename TranType::Pointer tran = TranType::New();
+ typename InterpType::Pointer func = InterpType::New();
+
+ filter->SetInput(src);
+ filter->SetTransform(tran);
+ filter->SetInterpolator(func.GetPointer());
+ filter->SetSize(ref->GetBufferedRegion().GetSize());
+ filter->SetOutputSpacing(ref->GetSpacing());
+ filter->SetOutputOrigin(ref->GetOrigin());
+ filter->SetOutputDirection(ref->GetDirection());
+ filter->GraftOutput(trg);
+ filter->Update();
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::img_threshold_in_place(ImageType *src, double lt, double ut, double fore, double back)
+{
+ typedef itk::BinaryThresholdImageFilter<ImageType, ImageType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(src);
+ filter->GraftOutput(src);
+ filter->SetLowerThreshold(lt);
+ filter->SetUpperThreshold(ut);
+ filter->SetInsideValue(fore);
+ filter->SetOutsideValue(back);
+ filter->Update();
+}
+
+template <class TImage>
+struct VoxelToPhysicalFunctor
+{
+
+ typedef typename TImage::PixelType PixelType;
+ typedef itk::ImageBase<TImage::ImageDimension> ImageBaseType;
+
+ PixelType operator() (const PixelType &x)
+ {
+ typedef itk::ContinuousIndex<double, TImage::ImageDimension> CIType;
+ typedef typename TImage::PointType PtType;
+ CIType ci0, ci;
+ PtType p0, p;
+ for(int i = 0; i < TImage::ImageDimension; i++)
+ {
+ ci[i] = x[i];
+ ci0[i] = 0.0;
+ }
+
+ m_Image->TransformContinuousIndexToPhysicalPoint(ci, p);
+ m_Image->TransformContinuousIndexToPhysicalPoint(ci0, p0);
+ PixelType y;
+
+ for(int i = 0; i < TImage::ImageDimension; i++)
+ {
+ y[i] = p[i] - p0[i];
+ }
+
+ return y;
+ }
+
+ bool operator != (const VoxelToPhysicalFunctor<TImage> &other)
+ { return other.m_Image != m_Image; }
+
+ ImageBaseType *m_Image;
+};
+
+template <class TFloat, uint VDim>
+void
+LDDMMData<TFloat, VDim>
+::warp_voxel_to_physical(VectorImageType *src, ImageBaseType *ref_space, VectorImageType *trg)
+{
+ // Set up functor
+ typedef VoxelToPhysicalFunctor<VectorImageType> FunctorType;
+ FunctorType fnk;
+ fnk.m_Image = ref_space;
+
+ // Set up filter
+ typedef itk::UnaryFunctorImageFilter<VectorImageType, VectorImageType, FunctorType> FilterType;
+ typename FilterType::Pointer filter = FilterType::New();
+ filter->SetInput(src);
+ filter->GraftOutput(trg);
+ filter->SetFunctor(fnk);
+ filter->Update();
+}
+
+
+
+/* =============================== */
+
+#ifdef _LDDMM_FFT_
+
+
+template <class TFloat, uint VDim>
+LDDMMFFTInterface<TFloat, VDim>
+::LDDMMFFTInterface(ImageType *ref)
+{
+ // Work out the data dimensions (large enough, and multiple of four)
+ m_Size = ref->GetBufferedRegion().GetSize();
+ m_Alloc = m_Size;
+ m_Alloc[VDim-1] = 2 * (m_Size[VDim-1] / 2 + 1);
+
+ // Size for calling the plan routines
+ int n[VDim];
+
+ // Get the data dimensions
+ m_AllocSize = 1; m_DataSize = 1;
+ for(uint i = 0; i < VDim; i++)
+ {
+ m_AllocSize *= m_Alloc[i];
+ m_DataSize *= m_Size[i];
+ n[i] = m_Size[i];
+ }
+
+ // Allocate the complex data (real data is packed in the complex data)
+ m_Data = (double *) fftw_malloc(m_AllocSize * sizeof(double));
+
+ // Create plans for forward and inverse transforms
+ m_Plan = fftw_plan_dft_r2c(VDim, n, m_Data, (fftw_complex *) m_Data, FFTW_MEASURE);
+ m_InvPlan = fftw_plan_dft_c2r(VDim, n, (fftw_complex *) m_Data, m_Data, FFTW_MEASURE);
+}
+
+template <class TFloat, uint VDim>
+void
+LDDMMFFTInterface<TFloat, VDim>
+::convolution_fft(
+ VectorImageType *img, ImageType *kernel_ft, bool inv_kernel,
+ VectorImageType *out)
+{
+ // Pack the data into m_Data. This requires us to skip a few bytes at
+ // the end of each row of data
+ uint nskip = m_Alloc[VDim-1] - m_Size[VDim-1];
+ uint ncopy = m_Size[VDim-1];
+ uint nout = m_Alloc[VDim-1] / 2;
+ uint noutskip = kernel_ft->GetBufferedRegion().GetSize()[VDim-1] - nout;
+ uint nstrides = m_AllocSize / m_Alloc[VDim-1];
+
+ for(uint d = 0; d < VDim; d++)
+ {
+ const Vec *src = img->GetBufferPointer();
+ double *dst = m_Data;
+
+ // Funky loop
+ for(double *end = dst + m_AllocSize; dst < end; dst+=nskip)
+ for(double *rowend = dst + ncopy; dst < rowend; dst++, src++)
+ *dst = (double) (*src)[d];
+
+ // Execute the plan
+ fftw_execute(m_Plan);
+
+ // Multiply or divide the complex values in m_Data by the kernel array
+ fftw_complex *c = (fftw_complex *) m_Data;
+
+ // Scaling factor for producing final result
+ double scale = 1.0 / m_DataSize;
+
+ /*
+ // Precision weirdness (results differ from MATLAB fft in 6th, 7th decimal digit)
+ uint tp = (m_Alloc[VDim-1] / 2) * 6 + 8;
+ printf("Before scaling, value at %d is %12.12lf, %12.12lf\n",
+ tp, c[tp][0], c[tp][1]);
+ printf("Kernel value at %d is %12.12lf\n", 128*6+8, kp[128*6+8]);
+ */
+
+ // Another such loop
+ TFloat *kp = kernel_ft->GetBufferPointer();
+ if(inv_kernel)
+ {
+ for(uint i = 0; i < nstrides; i++)
+ {
+ for(uint j = 0; j < nout; j++)
+ {
+ (*c)[0] /= (*kp);
+ (*c)[1] /= (*kp);
+ c++; kp++;
+ }
+ kp += noutskip;
+ }
+ }
+ else
+ {
+ for(uint i = 0; i < nstrides; i++)
+ {
+ for(uint j = 0; j < nout; j++)
+ {
+ (*c)[0] *= (*kp);
+ (*c)[1] *= (*kp);
+ c++; kp++;
+ }
+ kp += noutskip;
+ }
+ }
+
+ /*
+ fftw_complex *ctest = (fftw_complex *) m_Data;
+ printf("After scaling, value at %d is %12.12lf, %12.12lf\n",
+ tp, ctest[tp][0], ctest[tp][1]);
+ */
+
+ // Inverse transform
+ fftw_execute(m_InvPlan);
+
+ // Copy the results to the output image
+ const double *osrc = m_Data;
+ Vec *odst = out->GetBufferPointer();
+ for(uint i = 0; i < nstrides; i++, osrc+=nskip)
+ for(uint j = 0; j < ncopy; j++, odst++, osrc++)
+ (*odst)[d] = (TFloat) ((*osrc) * scale);
+
+ /*
+ odst = out->GetBufferPointer();
+ printf("Result %12.12lf\n", odst[128*6+8][0]);
+ */
+ }
+
+}
+
+template <class TFloat, uint VDim>
+LDDMMFFTInterface<TFloat, VDim>
+::~LDDMMFFTInterface()
+{
+ fftw_destroy_plan(m_Plan);
+ fftw_destroy_plan(m_InvPlan);
+ fftw_free(m_Data);
+}
+
+#endif // _LDDMM_FFT_
+
+
+template <class TFloat, uint VDim>
+LDDMMImageMatchingObjective<TFloat, VDim>
+::LDDMMImageMatchingObjective(LDDMM &p)
+ : fft(p.fix)
+{
+ // Allocate intermediate datasets
+ LDDMM::alloc_img(Jt0, p.fix);
+ LDDMM::alloc_img(Jt1, p.fix);
+ LDDMM::alloc_img(DetPhit1, p.fix);
+ LDDMM::alloc_vimg(GradJt0, p.fix);
+}
+
+template <class TFloat, uint VDim>
+TFloat
+LDDMMImageMatchingObjective<TFloat, VDim>
+::compute_objective_and_gradient(LDDMM &p)
+{
+ // Compute the regularization energy of v. We can use a[0] for temporary storage
+ // e_field = lddmm_vector_field_dot_product(vx, vy, vx, vy, p);
+ TFloat e_field = 0.0;
+ for(uint m = 0; m < p.nt; m++)
+ {
+ // a[0] = Lv[m] .* v[m]
+ fft.convolution_fft(p.v[m], p.f_kernel_sq, false, p.a[0]);
+
+ // We're sticking the inner product in Jt0
+ LDDMM::vimg_euclidean_inner_product(Jt0, p.a[0], p.v[m]);
+ e_field += LDDMM::img_voxel_sum(Jt0) / p.nt;
+ }
+
+ // Compute the 'a' array (for semilagrangean scheme)
+ p.compute_semi_lagrangean_a();
+
+ // Go through and compute phi_t1 for each time point
+ p.integrate_phi_t1();
+
+ // Compute the update for v at each time step
+ for(uint m = 0; m < p.nt; m++)
+ {
+ // Currently, f[m] holds phi_t1[m]. Use it for whatever we need
+ // and then replace with phi_t0[m]
+
+ // TODO: for ft00 and ft11, don't waste time on interpolation
+
+ // Jt1 = lddmm_warp_scalar_field(p.I1, ft1x(:,:,it), ft1y(:,:,it), p);
+ LDDMM::interp_img(p.mov, p.f[m], Jt1);
+
+ // detjac_phi_t1 = lddmm_jacobian_determinant(ft1x(:,:,it), ft1y(:,:,it), p);
+ LDDMM::field_jacobian_det(p.f[m], DetPhit1);
+
+ // Place phi_t0 into the f array
+ if(m==0)
+ {
+ p.f[m]->FillBuffer(typename LDDMM::Vec(0.0));
+ }
+ else
+ {
+ LDDMM::interp_vimg(p.f[m-1], p.a[m], -1.0, p.f[m]);
+ LDDMM::vimg_subtract_in_place(p.f[m], p.a[m]);
+ }
+
+ // Jt0 = lddmm_warp_scalar_field(p.I0, ft0x(:,:,it), ft0y(:,:,it), p);
+ LDDMM::interp_img(p.fix, p.f[m], Jt0);
+
+ // [grad_Jt0_x grad_Jt0_y] = gradient(Jt0);
+ LDDMM::image_gradient(Jt0, GradJt0);
+
+ // pde_rhs_x = detjac_phi_t1 .* (Jt0 - Jt1) .* grad_Jt0_x;
+ // pde_rhs_y = detjac_phi_t1 .* (Jt0 - Jt1) .* grad_Jt0_y;
+
+ // Here we do some small tricks. We want to retain Jt0 because it's the warped
+ // template image, and we want to retain the difference Jt0-Jt1 = (J0-I1) for
+ // calculating the objective at the end.
+ LDDMM::img_subtract_in_place(Jt1, Jt0); // 'Jt1' stores Jt1 - Jt0
+ LDDMM::img_multiply_in_place(DetPhit1, Jt1); // 'DetPhit1' stores (det Phi_t1)(Jt1-Jt0)
+ LDDMM::vimg_multiply_in_place(GradJt0, DetPhit1); // 'GradJt0' stores GradJt0 * (det Phi_t1)(Jt1-Jt0)
+
+ // Solve PDE via FFT convolution
+ // pde_soln_x = ifft2(fft2(pde_rhs_x) ./ p.f_kernel_sq,'symmetric');
+ // pde_soln_y = ifft2(fft2(pde_rhs_y) ./ p.f_kernel_sq,'symmetric');
+ fft.convolution_fft(GradJt0, p.f_kernel_sq, true, GradJt0); // 'GradJt0' stores K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+
+ // dedvx(:,:,it) = dedvx(:,:,it) - 2 * pde_soln_x / p.sigma^2;
+ // dedvy(:,:,it) = dedvy(:,:,it) - 2 * pde_soln_y / p.sigma^2;
+
+ // Store the update in a[m]
+ LDDMM::vimg_scale_in_place(GradJt0, 1.0 / p.sigma_sq); // 'GradJt0' stores 1 / sigma^2 K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+ LDDMM::vimg_add_in_place(GradJt0, p.v[m]); // 'GradJt0' stores v + 1 / sigma^2 K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+ LDDMM::vimg_scale(GradJt0, 2.0, p.a[m]); // p.a[m] holds 2 v + 2 / sigma^2 K[ GradJt0 * (det Phi_t1)(Jt1-Jt0) ]
+ }
+
+ // Ok, Jt1 currently contains (Jt1-Jt0), we just need to square it.
+ TFloat e_image = LDDMM::img_euclidean_norm_sq(Jt1) / p.sigma_sq;
+
+ // Return the energy
+ printf(" Energy components: %lf, %lf\n", e_field, e_image);
+ return e_field + e_image;
+}
+
+
+template class LDDMMData<float, 2>;
+template class LDDMMData<float, 3>;
+template class LDDMMData<float, 4>;
+
+template class LDDMMData<double, 2>;
+template class LDDMMData<double, 3>;
+template class LDDMMData<double, 4>;
+
+#ifdef _LDDMM_FFT_
+template class LDDMMFFTInterface<double, 2>;
+template class LDDMMFFTInterface<double, 3>;
+template class LDDMMFFTInterface<double, 4>;
+#endif
+
+template class LDDMMImageMatchingObjective<myreal, 2>;
+template class LDDMMImageMatchingObjective<myreal, 3>;
+
diff --git a/Submodules/greedy/src/lddmm_data.h b/Submodules/greedy/src/lddmm_data.h
new file mode 100644
index 0000000..49fcce4
--- /dev/null
+++ b/Submodules/greedy/src/lddmm_data.h
@@ -0,0 +1,285 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#ifndef _LDDMM_DATA_H_
+#define _LDDMM_DATA_H_
+
+#include <lddmm_common.h>
+#include <itkNumericTraits.h>
+#include <itkImage.h>
+#include <itkVectorImage.h>
+#include <itkImageRegionIteratorWithIndex.h>
+#include <itkCovariantVector.h>
+#include <vnl/vnl_math.h>
+#include <vector>
+
+#include <itkImageIOBase.h>
+
+#ifdef _LDDMM_FFT_
+#include <fftw3.h>
+#endif
+
+template<class TFloat, uint VDim>
+class LDDMMData
+{
+public:
+ // Image data
+ typedef itk::ImageBase<VDim> ImageBaseType;
+ typedef itk::Image<TFloat, VDim> ImageType;
+ typedef typename ImageType::Pointer ImagePointer;
+ typedef itk::ImageRegionIteratorWithIndex<ImageType> ImageIterator;
+
+ // Vector fields, etc
+ typedef itk::CovariantVector<TFloat, VDim> Vec;
+ typedef itk::Image<Vec, VDim> VectorImageType;
+ typedef typename VectorImageType::Pointer VectorImagePointer;
+ typedef std::vector<VectorImagePointer> VelocityField;
+
+ // Composite images (variable number of components)
+ typedef itk::VectorImage<TFloat, VDim> CompositeImageType;
+ typedef typename CompositeImageType::Pointer CompositeImagePointer;
+
+ // Regions, etc
+ typedef itk::ImageRegion<VDim> RegionType;
+
+ // Pointers to the fixed and moving images
+ ImagePointer fix, mov;
+
+ // Fourier space kernels
+ ImagePointer f_kernel, f_kernel_sq;
+
+ // Velocity field pointers (v, phi, a used for semi-lagrange scheme)
+ VelocityField v, f, a;
+
+ // Region for the velocity fields
+ RegionType r;
+
+ // Parameters
+ double alpha, sigma, gamma, dt, sigma_sq;
+
+ // Dimensions
+ uint n[VDim];
+
+ // Number of timesteps, number of voxels
+ uint nt, nv;
+
+ // Allocate a velocity field
+ static void alloc_vf(VelocityField &vf, uint nt, ImageBaseType *ref);
+ static void alloc_img(ImagePointer &img, ImageBaseType *ref);
+ static void alloc_vimg(VectorImagePointer &vimg, ImageBaseType *ref);
+ static void alloc_cimg(CompositeImagePointer &img, ImageBaseType *ref, int n_comp);
+
+ // Initialize LDDMM data
+ static void init(LDDMMData<TFloat, VDim> &,
+ ImageType *fix, ImageType *mov,
+ uint nt, double alpha, double gamma, double sigma);
+
+ // Apply deformation to data
+ static void interp_vimg(
+ VectorImageType *data, VectorImageType *field,
+ TFloat def_scale, VectorImageType *out,
+ bool use_nn = false, bool phys_space = false);
+
+ // Apply deformation to data
+ static void interp_img(ImageType *data, VectorImageType *field, ImageType *out,
+ bool use_nn = false, bool phys_space = false);
+
+ // Apply deformation to data
+ static void interp_cimg(CompositeImageType *data, VectorImageType *field, CompositeImageType *out,
+ bool use_nn = false, bool phys_space = false);
+
+ // Take Jacobian of deformation field
+ static void field_jacobian_det(VectorImageType *vec, ImageType *out);
+
+ // Smooth an image in-place
+ static void img_smooth(ImageType *src, ImageType *out, double sigma);
+ static void vimg_smooth(VectorImageType *src, VectorImageType *out, double sigma);
+ static void vimg_smooth(VectorImageType *src, VectorImageType *out, Vec sigmas);
+
+ // Smooth a displacement field with a border of zeros around it
+ static void vimg_smooth_withborder(VectorImageType *src, VectorImageType *trg, Vec sigma, int border_size);
+
+ // Take gradient of an image
+ static void image_gradient(ImageType *src, VectorImageType *grad);
+
+ // Basic math
+ static void vimg_add_in_place(VectorImageType *trg, VectorImageType *a);
+ static void vimg_subtract_in_place(VectorImageType *trg, VectorImageType *a);
+ static void vimg_scale_in_place(VectorImageType *trg, TFloat s);
+
+ // compute trg = trg + s * a
+ static void vimg_add_scaled_in_place(VectorImageType *trg, VectorImageType *a, TFloat s);
+
+ static void vimg_scale(VectorImageType *src, TFloat s, VectorImageType *trg);
+ static void vimg_multiply_in_place(VectorImageType *trg, ImageType *s);
+ static void vimg_euclidean_inner_product(ImagePointer &trg, VectorImageType *a, VectorImageType *b);
+ static TFloat vimg_euclidean_norm_sq(VectorImageType *trg);
+
+ // Compute the range of the norm of a vector field
+ static void vimg_norm_min_max(VectorImageType *image, ImagePointer &normsqr,
+ TFloat &min_norm, TFloat &max_norm);
+
+ // Update a vector image to make its maxumum length equal to given value. The
+ // second parameter is a working image that will return unnormalized lengths squared
+ static void vimg_normalize_to_fixed_max_length(VectorImageType *trg,
+ ImagePointer &normsqr,
+ double max_displacement,
+ bool scale_down_only);
+
+ // Scalar math
+ static void img_add_in_place(ImagePointer &trg, ImageType *a);
+ static void img_subtract_in_place(ImagePointer &trg, ImageType *a);
+ static void img_multiply_in_place(ImagePointer &trg, ImageType *a);
+ static TFloat img_euclidean_norm_sq(ImageType *trg);
+ static TFloat img_voxel_sum(ImageType *trg);
+ static void img_min_max(ImageType *src, TFloat &out_min, TFloat &out_max);
+
+ // Generate a kernel image for navier-stokes operator
+ static void compute_navier_stokes_kernel(ImageType *kernel, double alpha, double gamma);
+
+ // Downsample and upsample images (includes smoothing, use sparingly)
+ static void img_downsample(ImageType *src, ImageType *trg, double factor);
+ static void img_shrink(ImageType *src, ImageType *trg, int factor);
+ static void img_resample_identity(ImageType *src, ImageBaseType *ref, ImageType *trg);
+ static void vimg_resample_identity(VectorImageType *src, ImageBaseType *ref, VectorImageType *trg);
+
+ // Threshold image
+ static void img_threshold_in_place(ImageType *src, double lt, double up, double fore, double back);
+
+ // Convert voxel-space warp to a physical space warp
+ static void warp_voxel_to_physical(VectorImageType *src, ImageBaseType *ref_space, VectorImageType *trg);
+
+ // Some IO methods
+ typedef itk::ImageIOBase::IOComponentType IOComponentType;
+
+ static IOComponentType img_read(const char *fn, ImagePointer &trg);
+ static IOComponentType vimg_read(const char *fn, VectorImagePointer &trg);
+ static IOComponentType cimg_read(const char *fn, CompositeImagePointer &trg);
+
+ // Write scalar image, with optional output format specification
+ static void img_write(ImageType *src, const char *fn,
+ IOComponentType comp = itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
+
+ // Write vector image, with optional output format specification
+ static void vimg_write(VectorImageType *src, const char *fn,
+ IOComponentType comp = itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
+
+ // Write composite image, with optional output format specification
+ static void cimg_write(CompositeImageType *src, const char *fn,
+ IOComponentType comp = itk::ImageIOBase::UNKNOWNCOMPONENTTYPE);
+
+ static void vfield_read(uint nt, const char *fnpat, VelocityField &v);
+
+ static void vimg_copy(const VectorImageType *src, VectorImageType *trg);
+ static void img_copy(const ImageType *src, ImageType *trg);
+
+ // Compute a array from v
+ void compute_semi_lagrangean_a();
+
+ // Integrate forward tranform (phi_0_t)
+ void integrate_phi_t0();
+ void integrate_phi_t1();
+
+protected:
+
+ // A vector image for in-place interpolation operations
+ VectorImagePointer vtmp;
+
+};
+
+#ifdef _LDDMM_FFT_
+
+template <class TFloat, uint VDim>
+class LDDMMFFTInterface
+{
+public:
+ typedef typename LDDMMData<TFloat, VDim>::ImageType ImageType;
+ typedef typename LDDMMData<TFloat, VDim>::VectorImageType VectorImageType;
+ typedef typename LDDMMData<TFloat, VDim>::Vec Vec;
+
+ LDDMMFFTInterface(ImageType *ref);
+ ~LDDMMFFTInterface();
+
+ void convolution_fft(
+ VectorImageType *img, ImageType *kernel_ft, bool inv_kernel,
+ VectorImageType *out);
+
+private:
+
+ // Size of the input array and allocated array (bigger, for in-place math)
+ itk::Size<VDim> m_Size, m_Alloc;
+ uint m_AllocSize, m_DataSize;
+
+ // In-place data array
+ double *m_Data;
+
+ // FFT plan
+ fftw_plan m_Plan, m_InvPlan;
+
+};
+
+#else
+
+template <class TFloat, uint VDim>
+class LDDMMFFTInterface
+{
+public:
+ typedef typename LDDMMData<TFloat, VDim>::ImageType ImageType;
+ typedef typename LDDMMData<TFloat, VDim>::VectorImageType VectorImageType;
+ typedef typename LDDMMData<TFloat, VDim>::Vec Vec;
+
+ LDDMMFFTInterface(ImageType *ref) {}
+ ~LDDMMFFTInterface() {}
+
+ void convolution_fft(
+ VectorImageType *img, ImageType *kernel_ft, bool inv_kernel,
+ VectorImageType *out) { throw std::string("Code was not compiled with _LDDMM_FFT_"); }
+
+};
+
+#endif // _LDDMM_FFT_
+
+
+// Class for iteratively computing the objective function
+template<class TFloat, uint VDim>
+class LDDMMImageMatchingObjective
+{
+public:
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef LDDMMFFTInterface<TFloat, VDim> FFT;
+
+ LDDMMImageMatchingObjective(LDDMM &p);
+ TFloat compute_objective_and_gradient(LDDMM &p);
+
+ typename LDDMM::ImagePointer Jt0, Jt1, DetPhit1;
+ typename LDDMM::VectorImagePointer GradJt0;
+ FFT fft;
+};
+
+
+
+
+#endif
diff --git a/Submodules/greedy/src/lddmm_main.cxx b/Submodules/greedy/src/lddmm_main.cxx
new file mode 100644
index 0000000..bc01534
--- /dev/null
+++ b/Submodules/greedy/src/lddmm_main.cxx
@@ -0,0 +1,354 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include <iostream>
+#include <cstdio>
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+#include <itkImageFileReader.h>
+#include <itkGaussianInterpolateImageFunction.h>
+#include <itkResampleImageFilter.h>
+#include <itkIdentityTransform.h>
+#include <itkShrinkImageFilter.h>
+
+using namespace std;
+
+int usage()
+{
+ printf("lddmm: Paul's LDDMM implementation\n");
+ printf("Usage: \n");
+ printf(" lddmm DIM [options] fixed.nii moving.nii\n");
+ printf(" lddmm DIM --test test_id test_params\n");
+ printf("Options: \n");
+ return -1;
+}
+
+template <class TFloat, uint VDim>
+class Resampler
+{
+public:
+ typedef itk::Image<TFloat, VDim> ImageType;
+
+ Resampler(ImageType *image, uint factor)
+ {
+ m_Filter = FilterType::New();
+ m_Filter->SetInput(image);
+
+ typedef itk::IdentityTransform<TFloat, VDim> TranType;
+ typename TranType::Pointer tran = TranType::New();
+ m_Filter->SetTransform(tran);
+
+ m_Func = FuncType::New();
+ double sigma[VDim];
+ for(uint i = 0; i < VDim; i++)
+ sigma[i] = factor * image->GetSpacing()[i] * 1.2;
+ m_Func->SetParameters(sigma, 4.0);
+
+ m_Filter->SetInterpolator(m_Func);
+
+ m_Shrink = ShrinkType::New();
+ m_Shrink->SetInput(image);
+ m_Shrink->SetShrinkFactors(factor);
+ m_Shrink->Update();
+
+ m_Filter->UseReferenceImageOn();
+ m_Filter->SetReferenceImage(m_Shrink->GetOutput());
+ m_Filter->Update();
+ }
+
+ ImageType *GetResult()
+ { return m_Filter->GetOutput(); }
+private:
+ typedef itk::ShrinkImageFilter<ImageType, ImageType> ShrinkType;
+ typedef itk::ResampleImageFilter<ImageType, ImageType, TFloat> FilterType;
+ typedef itk::GaussianInterpolateImageFunction<ImageType, TFloat> FuncType;
+ typename ShrinkType::Pointer m_Shrink;
+ typename FilterType::Pointer m_Filter;
+ typename FuncType::Pointer m_Func;
+};
+
+template <class TFloat, uint VDim>
+int run_test(int argc, char *argv[])
+{
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef typename LDDMM::VectorImageType VectorImageType;
+
+
+ int test_id = atoi(argv[1]);
+ if(test_id == 0)
+ {
+ printf(
+ "TEST LISTING:\n"
+ " 1: FFT Convolution test for vector fields\n"
+ " --test 1 invec.nii compare.nii\n"
+ " 2: Test image warping given velocity field\n"
+ " --test 2 nt warp%%02d.nii input.nii compare.nii\n"
+ " 3: Test gradient w.r.t. variation\n"
+ " --test 3 fixed.nii moving.nii nt variation.nii\n"
+ );
+
+ return 0;
+ }
+
+ if(test_id == 1)
+ {
+ if(argc < 4)
+ throw itk::ExceptionObject("Wrong number of arguments for test 1");
+
+ // Read images
+ typename VectorImageType::Pointer src, comp;
+ LDDMM::vimg_read(argv[2], src);
+ LDDMM::vimg_read(argv[3], comp);
+
+ // Initialize LDDMM
+ typename ImageType::Pointer dummy_fix, dummy_mov;
+ LDDMM::alloc_img(dummy_fix, src);
+ LDDMM::alloc_img(dummy_mov, src);
+ LDDMM lddmm;
+ LDDMM::init(lddmm, dummy_fix, dummy_mov, 4, 0.01, 1.0, 1.0);
+ LDDMM::img_write(lddmm.f_kernel_sq, "test01_kernel.nii");
+
+ // Perform operation
+ typedef LDDMMFFTInterface<TFloat, VDim> FFT;
+ FFT fft(dummy_fix);
+ fft.convolution_fft(src, lddmm.f_kernel_sq, false, src);
+
+ // Save (temp)
+ LDDMM::vimg_write(src, "test01_result.nii");
+
+ // Compare to target
+ LDDMM::vimg_subtract_in_place(src, comp);
+ TFloat diff = LDDMM::vimg_euclidean_norm_sq(src);
+
+ printf("Difference with expected result: %f\n", diff);
+ return (diff > 0);
+ }
+ else if(test_id == 2)
+ {
+ if(argc < 6)
+ throw itk::ExceptionObject("Wrong number of arguments for test 1");
+
+ // Get the number of time steps
+ uint nt = atoi(argv[2]);
+
+ // Read input and output images
+ typename ImageType::Pointer src, comp;
+ LDDMM::img_read(argv[4], src);
+ LDDMM::img_read(argv[5], comp);
+
+ // Create a problem
+ LDDMM lddmm;
+ LDDMM::init(lddmm, src, src, nt, 0.01, 1.0, 1.0);
+
+ // Read velocity field
+ LDDMM::vfield_read(nt, argv[3], lddmm.v);
+
+ // Integrate the forward transform
+ lddmm.compute_semi_lagrangean_a();
+ lddmm.integrate_phi_t0();
+
+ // Warp the image using phi_10
+ typename ImageType::Pointer res;
+ LDDMM::alloc_img(res, src);
+ LDDMM::interp_img(src, lddmm.f[nt-1], res);
+
+ // Write the result
+ LDDMM::img_write(res, "test02_result.nii");
+
+ // Compare to target
+ LDDMM::img_subtract_in_place(res, comp);
+ TFloat diff = LDDMM::img_euclidean_norm_sq(res);
+
+ printf("Difference with expected result: %f\n", diff);
+ return (diff > 0);
+ }
+ else if(test_id == 3)
+ {
+ if(argc < 6)
+ throw itk::ExceptionObject("Wrong number of arguments for test 1");
+
+ // Read input and output images
+ typename ImageType::Pointer fix, mov;
+ LDDMM::img_read(argv[2], fix);
+ LDDMM::img_read(argv[3], mov);
+
+ // Number of time steps
+ uint nt = atoi(argv[4]);
+
+ // Create a problem
+ LDDMM p;
+ LDDMM::init(p, fix, mov, nt, 0.01, 1.0, 1.0);
+
+ // Iterate for 4 iterations (so that the starting point isn't zero
+ LDDMMImageMatchingObjective<TFloat, VDim> obj(p);
+ double ts = 0.01;
+ for(uint k = 0; k < 4; k++)
+ {
+ TFloat fx = obj.compute_objective_and_gradient(p);
+ for(uint it = 0; it < p.nt; it++)
+ LDDMM::vimg_add_scaled_in_place(p.v[it], p.a[it], -ts);
+ printf("Iter %04d Obj: %10.10f\n", k, fx);
+ }
+
+ // Call this one more time, so we have v in p.v and dv in p.a
+ TFloat fx = obj.compute_objective_and_gradient(p);
+
+ // Read the random variation
+ typename LDDMM::VelocityField h;
+ LDDMM::alloc_vf(h, p.nt, p.fix);
+ LDDMM::vfield_read(nt, argv[5], h);
+
+ // gateaux_analytic = lddmm_vector_field_dot_product(dedvx, dedvy, varx, vary, p);
+
+ // for expeps = -6:2
+
+ // eps = 10^expeps;
+
+ // E1 = lddmm_objective_and_gradient(vx - eps * varx, vy - eps * vary, p);
+ // E2 = lddmm_objective_and_gradient(vx + eps * varx, vy + eps * vary, p);
+
+ // gateaux_numeric = (E2 - E1) / (2 * eps);
+
+ // fprintf('Iter: %4i Eps: %8d Analytic: %12d Numeric: %12d\n', ...
+ // i, eps, gateaux_analytic, gateaux_numeric);
+
+ return 0;
+ }
+ else
+ throw itk::ExceptionObject("Unknown test ID");
+}
+
+template <class TFloat, uint VDim>
+int my_main(int argc, char *argv[])
+{
+ // Parse options, look for test option
+ for(int i = 1; i < argc-1; i++)
+ {
+ if(!strcmp(argv[i], "--test"))
+ return run_test<TFloat,VDim>(argc-i, argv+i);
+ }
+
+ // Normal processing
+ char *fnmov = argv[argc-1];
+ char *fnfix = argv[argc-2];
+ // const char *outname = "lddmm.nii.gz";
+
+ // Number of iterations
+ uint n_res = 1;
+ uint n_iter[] = {100};
+
+ // Time steps
+ uint nt = 10;
+
+ // Read the images
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typename ReaderType::Pointer readfix = ReaderType::New();
+ readfix->SetFileName(fnfix);
+ readfix->Update();
+
+ typename ReaderType::Pointer readmov = ReaderType::New();
+ readmov->SetFileName(fnmov);
+ readmov->Update();
+
+ // Loop over the multi-resolution levels
+ for(int ires = n_res-1; ires >=0; ires--)
+ {
+ // Interpolate the input image and output image
+ typename ImageType::Pointer ifix, imov;
+ if(ires > 0)
+ {
+ Resampler<TFloat,VDim> rfix(readfix->GetOutput(), 1 << ires);
+ Resampler<TFloat,VDim> rmov(readmov->GetOutput(), 1 << ires);
+ ifix = rfix.GetResult();
+ imov = rmov.GetResult();
+ }
+ else
+ {
+ ifix = readfix->GetOutput();
+ imov = readmov->GetOutput();
+ }
+
+ // Create the problem
+ LDDMM p;
+
+ double avgdim = pow(ifix->GetBufferedRegion().GetNumberOfPixels() * 1.0, 1.0 / VDim);
+
+ // LDDMM::init(p, ifix, imov, nt, 0.01, 1, 0.008);
+ LDDMM::init(p, ifix, imov, nt, 0.01, 1, 1.0 / avgdim);
+
+ // TODO: initialize with earlier set of velocity fields
+
+ // Create an optimization problem
+ LDDMMImageMatchingObjective<TFloat, VDim> obj(p);
+
+ // Set time step
+ double ts = 0.01;
+
+ // Iterate
+ for(uint k = 0; k < n_iter[ires]; k++)
+ {
+ // Compute objective and gradient. This will put the gradient of the function
+ // into the 'a' array, retain the velocity field in 'v', and retain the inverse
+ // transform (ft0) in the 'f' array.
+ TFloat fx = obj.compute_objective_and_gradient(p);
+
+ // Update the solution by the time step (v = v - t * dv)
+ for(uint it = 0; it < p.nt; it++)
+ LDDMM::vimg_add_scaled_in_place(p.v[it], p.a[it], -ts);
+
+ // Save the current image
+ char fn[1024]; sprintf(fn, "lddmm_result_%04d.nii.gz", k);
+ LDDMM::img_write(obj.Jt0, fn);
+
+ // Print out the current status
+ printf("Iter %04d Obj: %10.10f\n", k, fx);
+ }
+ }
+
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ // Read input arguments
+ if(argc < 2)
+ return usage();
+
+ // Get the number of dimensions
+ uint ndim = (uint) atoi(argv[1]);
+ if(ndim == 2)
+ my_main<myreal, 2>(argc, argv);
+ else if(ndim == 3)
+ my_main<myreal, 3>(argc, argv);
+ else
+ return usage();
+}
diff --git a/Submodules/greedy/src/macf/macf_fixed_point_inverse.cxx b/Submodules/greedy/src/macf/macf_fixed_point_inverse.cxx
new file mode 100644
index 0000000..84a1f34
--- /dev/null
+++ b/Submodules/greedy/src/macf/macf_fixed_point_inverse.cxx
@@ -0,0 +1,75 @@
+#include <iostream>
+#include <cstdio>
+#include <string>
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+int usage()
+{
+ printf("macf_fixed_point_inverse: Paul's MACF implementation\n");
+ printf("Usage: \n");
+ printf(" macf_fixed_point_inverse DIM inwarp.nii outwarp.nii\n");
+ return -1;
+}
+
+template <class TFloat, uint VDim>
+int my_main(int argc, char *argv[])
+{
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef typename LDDMM::VectorImageType VectorImageType;
+ typedef typename LDDMM::ImagePointer ImagePointer;
+ typedef typename LDDMM::VectorImagePointer VectorImagePointer;
+
+ // No parameter checking!
+ if(argc < 4) return usage();
+
+ // Basic info
+ char *fninwarp = argv[2];
+ char *fnoutwarp = argv[3];
+
+ // Read the warp
+ VectorImagePointer u, v, vn;
+ LDDMM::vimg_read(fninwarp, u);
+
+ // Allocate the inverse
+ LDDMM::alloc_vimg(v, u);
+ LDDMM::alloc_vimg(vn, u);
+
+ // Perform fixed point algorithm
+ for (uint i = 1; i <= 100; i++)
+ {
+ // Apply iteration
+ LDDMM::interp_vimg(u, v, 1.0, true, vn);
+ LDDMM::vimg_scale_in_place(vn, -1.0);
+
+ // Measure change
+ LDDMM::vimg_subtract_in_place(v, vn);
+ myreal normdiff = LDDMM::vimg_euclidean_norm_sq(v);
+
+ // Report
+ printf("Iteration %03d, Change %f\n", i, normdiff);
+ LDDMM::vimg_scale(vn, 1.0, v);
+ }
+
+ LDDMM::vimg_write(v, fnoutwarp);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ // Read input arguments
+ if(argc < 2)
+ return usage();
+
+ // Get the number of dimensions
+ uint ndim = (uint) atoi(argv[1]);
+ if(ndim == 2)
+ my_main<myreal, 2>(argc, argv);
+ else if(ndim == 3)
+ my_main<myreal, 3>(argc, argv);
+ else
+ return usage();
+
+}
diff --git a/Submodules/greedy/src/macf/macf_gradient.cxx b/Submodules/greedy/src/macf/macf_gradient.cxx
new file mode 100644
index 0000000..e6d1741
--- /dev/null
+++ b/Submodules/greedy/src/macf/macf_gradient.cxx
@@ -0,0 +1,126 @@
+#include <iostream>
+#include <cstdio>
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+int usage()
+{
+ printf("macf_gradient: Paul's MACF implementation\n");
+ printf("Usage: \n");
+ printf(" macf_gradient DIM N sigma dt delta_i [delta_j] [psi_ji] [J_psi_ji] [w_ji] phi_i phi_i_out\n");
+ printf("Parameters: \n");
+ printf(" DIM : Image dimensionality\n"); // 1
+ printf(" N : Number of images\n"); // 1
+ printf(" sigma : Gaussian smoothing (VOXEL units)\n"); // 1
+ printf(" dt : Step size\n"); // 1
+ printf(" delta_i : A delta warp (from macf_objective_and_delta )\n"); // 1
+ printf(" delta_j : A list of N-1 deltas (for other subjects)\n"); // N-1
+ printf(" psi_ji : A list of N-1 warps (from subject to subject)\n");
+ printf(" J_psi_ji : A list of N-1 jacobian determinants of above warps\n");
+ printf(" w_ji : A list of N-1 weight maps\n");
+ printf(" phi_i : Mapping from subject to template from last step\n"); // 1
+ printf(" phi_out_i: Output image\n"); // 1
+ return -1;
+}
+
+template <class TFloat, uint VDim>
+int my_main(int argc, char *argv[])
+{
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef typename LDDMM::VectorImageType VectorImageType;
+ typedef typename LDDMM::ImagePointer ImagePointer;
+ typedef typename LDDMM::VectorImagePointer VectorImagePointer;
+
+ typedef std::vector<ImagePointer> ImageArray;
+ typedef std::vector<VectorImagePointer> VectorImageArray;
+
+ // No parameter checking!
+ if(argc < 3) return usage();
+
+ // Basic info
+ unsigned int N = (unsigned int) atoi(argv[2]);
+ if(argc < (int) (4 * N + 4)) return usage();
+
+ // Simple parameters
+ double sigma = atof(argv[3]);
+ double dt = atof(argv[4]);
+
+ // Get the lists of images
+ char **fndelta = argv + 5;
+ char **fnpsi = fndelta + N;
+ char **fnJpsi = fnpsi + N - 1;
+ char **fnw = fnJpsi + N - 1;
+ char **fnphi = fnw + N - 1;
+
+ // Read i-th warp, store in delta
+ VectorImagePointer grad;
+ LDDMM::vimg_read(fndelta[0], grad);
+
+ // Temporary velocity field for computations
+ VectorImagePointer q;
+ LDDMM::alloc_vimg(q, grad);
+
+ // Iterate over all other warps
+ for(unsigned int j = 0; j < N-1; j++)
+ {
+ VectorImagePointer psi_ji, delta_j;
+ ImagePointer w_ji, Jpsi_ji;
+
+ // Progress report
+ printf("Updating Grad_i by %s, %s, %s\n", fndelta[j+1], fnpsi[j], fnw[j]);
+
+ // Read the data
+ LDDMM::vimg_read(fndelta[j+1], delta_j);
+ LDDMM::vimg_read(fnpsi[j], psi_ji);
+ LDDMM::img_read(fnw[j], w_ji);
+ LDDMM::img_read(fnJpsi[j], Jpsi_ji);
+
+ // Scale delta_j by w
+ LDDMM::vimg_multiply_in_place(delta_j, w_ji);
+
+ // Interpolate using psi_ji
+ LDDMM::interp_vimg(delta_j, psi_ji, 1.0, true, q);
+
+ // Scale by the Jacobian of this map
+ LDDMM::vimg_multiply_in_place(q, Jpsi_ji);
+
+ // Subtract from grad
+ LDDMM::vimg_subtract_in_place(grad, q);
+ }
+
+ // Smooth the gradient and scale by timestep
+ LDDMM::vimg_gaussian_smooth(grad, false, sigma, q);
+ LDDMM::vimg_scale_in_place(q, -dt);
+
+ // Read the current transformation
+ VectorImagePointer phi_i;
+ LDDMM::vimg_read(fnphi[0], phi_i);
+
+ // Compose with the last phi to get the new phi
+ VectorImagePointer phiout = grad;
+ LDDMM::interp_vimg(phi_i, q, 1.0, true, phiout);
+ LDDMM::vimg_add_in_place(phiout, q);
+
+ // Save the new phi
+ LDDMM::vimg_write(phiout, fnphi[1]);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ // Read input arguments
+ if(argc < 2)
+ return usage();
+
+ // Get the number of dimensions
+ uint ndim = (uint) atoi(argv[1]);
+ if(ndim == 2)
+ my_main<float, 2>(argc, argv);
+ else if(ndim == 3)
+ my_main<float, 3>(argc, argv);
+ else
+ return usage();
+
+}
diff --git a/Submodules/greedy/src/macf/macf_objective_and_delta.cxx b/Submodules/greedy/src/macf/macf_objective_and_delta.cxx
new file mode 100644
index 0000000..1439815
--- /dev/null
+++ b/Submodules/greedy/src/macf/macf_objective_and_delta.cxx
@@ -0,0 +1,108 @@
+#include <iostream>
+#include <cstdio>
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+#include "itkMultiThreader.h"
+
+int usage()
+{
+ printf("macf_objective_and_delta: Paul's MACF implementation\n");
+ printf("Usage: \n");
+ printf(" macf_objgrad DIM N phiinv_i [phiinv_j] [psi_ij] [w_ij] [delta_i]\n");
+ printf("Parameters: \n");
+ printf(" phiinv_i : A warp (from subject to template)\n");
+ printf(" phiinv_j : A list of N-1 warps (from other subjects to template)\n");
+ printf(" psi_ij : A list of N-1 warps (from subject to subject)\n");
+ printf(" w_ij : A list of N-1 weight maps\n");
+ printf(" delta_i : Output image \n");
+ return -1;
+}
+
+template <class TFloat, uint VDim>
+int my_main(int argc, char *argv[])
+{
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef typename LDDMM::VectorImageType VectorImageType;
+ typedef typename LDDMM::ImagePointer ImagePointer;
+ typedef typename LDDMM::VectorImagePointer VectorImagePointer;
+
+ typedef std::vector<ImagePointer> ImageArray;
+ typedef std::vector<VectorImagePointer> VectorImageArray;
+
+ printf("MultiThreader Info: MAX = %d DEF = %d\n",
+ itk::MultiThreader::GetGlobalMaximumNumberOfThreads(),
+ itk::MultiThreader::GetGlobalDefaultNumberOfThreads());
+
+ // No parameter checking!
+ if(argc < 3) return usage();
+
+ // Basic info
+ unsigned int N = (unsigned int) atoi(argv[2]);
+ if(argc < (int) (3 * N + 2)) return usage();
+
+ // Get the lists of images
+ char **fnphi = argv + 3;
+ char **fnpsi = fnphi + N;
+ char **fnw = fnpsi + N - 1;
+ char **fndelta = fnw + N - 1;
+
+ // Read i-th warp, store in delta
+ VectorImagePointer delta;
+ LDDMM::vimg_read(fnphi[0], delta);
+
+ // Temporary velocity field for computations
+ VectorImagePointer q;
+ LDDMM::alloc_vimg(q, delta);
+
+ // Iterate over all other warps
+ for(unsigned int i = 0; i < N-1; i++)
+ {
+ VectorImagePointer psi_ij, phi_j;
+ ImagePointer w_ij;
+
+ // Progress report
+ printf("Updating Delta_i by %s, %s, %s\n", fnphi[i+1], fnpsi[i], fnw[i]);
+
+ // Read the data
+ LDDMM::vimg_read(fnphi[i+1], phi_j);
+ LDDMM::vimg_read(fnpsi[i], psi_ij);
+ LDDMM::img_read(fnw[i], w_ij);
+
+ // Compose transformations
+ LDDMM::interp_vimg(phi_j, psi_ij, 1.0, true, q);
+ LDDMM::vimg_add_in_place(q, psi_ij);
+
+ // Scale by W
+ LDDMM::vimg_multiply_in_place(q, w_ij);
+
+ // Subtract from current delta estimate
+ LDDMM::vimg_subtract_in_place(delta, q);
+ }
+
+ // Compute the energy for this iteration
+ double energy = LDDMM::vimg_euclidean_norm_sq(delta);
+ printf("ENERGY = %lf\n", energy);
+
+ // Write the delta
+ LDDMM::vimg_write(delta, fndelta[0]);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ // Read input arguments
+ if(argc < 2)
+ return usage();
+
+ // Get the number of dimensions
+ uint ndim = (uint) atoi(argv[1]);
+ if(ndim == 2)
+ my_main<float, 2>(argc, argv);
+ else if(ndim == 3)
+ my_main<float, 3>(argc, argv);
+ else
+ return usage();
+
+}
diff --git a/Submodules/greedy/src/macf/macf_objective_and_gradient.cxx b/Submodules/greedy/src/macf/macf_objective_and_gradient.cxx
new file mode 100644
index 0000000..d84df76
--- /dev/null
+++ b/Submodules/greedy/src/macf/macf_objective_and_gradient.cxx
@@ -0,0 +1,94 @@
+#include <iostream>
+#include <cstdio>
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+/**
+ * This program requires a directory to be in place. These are the files that will be accessed
+ * by the program:
+ *
+ * IDLIST.txt List of IDS (strings identifying subjects)
+ * *_macf_InverseWarp.nii Current estimate of warps from template to each image
+ * fx_${ID}_mv_*_regWarp.nii Warps from all images to image ID
+ * fx_${ID}_mv_*_regInverseWarp.nii Inverse warps from all images to image ID
+ * fx_${ID}_mv_*_rank.nii Weights associated with these warps
+ * fx_*_mv_${ID}_regWarp.nii Warps from image ID to all images
+ * fx_*_mv_${ID}_regInverseWarp.nii Inverse warps from image ID to all images
+ * fx_*_mv_${ID}_rank.nii Weights associated with these warps
+ */
+
+int usage()
+{
+ printf("macf_objective: Paul's MACF implementation\n");
+ printf("Usage: \n");
+ printf(" macf_objgrad DIM i\n");
+ printf("Options: \n");
+ return -1;
+}
+
+template <class TFloat, uint VDim>
+int my_main(int argc, char *argv[])
+{
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef typename LDDMM::VectorImageType VectorImageType;
+ typedef typename LDDMM::ImagePointer ImagePointer;
+ typedef typename LDDMM::VectorImagePointer VectorImagePointer;
+
+ typedef std::vector<ImagePointer> ImageArray;
+ typedef std::vector<VectorImagePointer> VectorImageArray;
+
+ // No parameter checking!
+ if(argc < 4)
+ return usage();
+
+ // Basic info
+ int N = atoi(argv[2]);
+ int k = atoi(argv[3]);
+ int iarg = 4;
+
+ if(N <= 0 || i < 0 || i >= N)
+ return usage();
+
+ if(argc < 4 + (N-1) * 5 + 2)
+ return usage();
+
+ // Read all the info
+ VectorImageArray nu, ivkx, ivxk;
+ ImageArray wkx, wxk;
+
+ // Read all the warps nu (template to image i)
+ for (int i = 0; i < N; i++)
+ {
+ if (i != k)
+ {
+ vimg_read(argv[iarg + i], nu[i]);
+ vimg_read(argv[iarg + (N-1) + i], ivkx[i]);
+ img_read(argv[iarg + 2 * (N-1) + i], wkx[i]);
+ vimg_read(argv[iarg + 3 * (N-1) + i], ivxk[i]);
+ img_read(argv[iarg + 4 * (N-1) + i], wxk[i]);
+ }
+ }
+
+ // Read all the warps vkx (image x to image k)
+ for (int i = 0; i < N; i++)
+ vimg_read(argv[iarg++], vkx[i]);
+
+}
+
+int main(int argc, char *argv[])
+{
+ // Read input arguments
+ if(argc < 2)
+ return usage();
+
+ // Get the number of dimensions
+ uint ndim = (uint) atoi(argv[1]);
+ if(ndim == 2)
+ my_main<myreal, 2>(argc, argv);
+ else if(ndim == 3)
+ my_main<myreal, 3>(argc, argv);
+ else
+ return usage();
+
+}
diff --git a/Submodules/greedy/src/macf/macf_pyramid.cxx b/Submodules/greedy/src/macf/macf_pyramid.cxx
new file mode 100644
index 0000000..804957b
--- /dev/null
+++ b/Submodules/greedy/src/macf/macf_pyramid.cxx
@@ -0,0 +1,86 @@
+#include <iostream>
+#include <cstdio>
+#include <string>
+#include "lddmm_common.h"
+#include "lddmm_data.h"
+
+int usage()
+{
+ printf("macf_pyramid: Paul's MACF implementation\n");
+ printf("Usage: \n");
+ printf(" macf_pyramid DIM NLEVELS sigma basename extension\n");
+ printf("Parameters: \n");
+ printf(" DIM : Image dimensionality\n"); // 1
+ printf(" NLEVELS : Number of multi-resolution levels\n"); // 1
+ printf(" sigma : Gaussian smoothing factor\n"); // 1
+ printf(" basename : Name of warps (all letters before Warp/InverseWarp)\n"); // 1
+ printf(" extension: Filename extension for input and output (nii.gz)\n");
+ return -1;
+}
+
+template <class TFloat, uint VDim>
+int my_main(int argc, char *argv[])
+{
+ typedef LDDMMData<TFloat, VDim> LDDMM;
+ typedef typename LDDMM::ImageType ImageType;
+ typedef typename LDDMM::VectorImageType VectorImageType;
+ typedef typename LDDMM::ImagePointer ImagePointer;
+ typedef typename LDDMM::VectorImagePointer VectorImagePointer;
+
+ // No parameter checking!
+ if(argc < 6) return usage();
+
+ // Basic info
+ unsigned int nlev = (unsigned int) atoi(argv[2]);
+ double sigma = atof(argv[3]);
+ char *basename = argv[4];
+ char *extension = argv[5];
+
+ // Read the warps
+ char fnwarp[1024], fninvw[1024];
+ sprintf(fnwarp, "%sWarp.%s", basename, extension);
+ sprintf(fninvw, "%sInverseWarp.%s", basename, extension);
+
+ VectorImagePointer w, iw;
+ LDDMM::vimg_read(fnwarp, w);
+ LDDMM::vimg_read(fninvw, iw);
+
+ // For each level, resample image
+ for (uint i = 1; i <= nlev; i++)
+ {
+ VectorImagePointer q, s;
+ uint factor = 1 << i;
+
+ char fnout[1024];
+ sprintf(fnout, "%s_mr%d_Warp.%s", basename, i, extension);
+ LDDMM::vimg_gaussian_smooth(w, false, factor * sigma, q);
+ LDDMM::vimg_shrink(q, factor, s);
+ LDDMM::vimg_clip_to_zero_in_place(s, 1.0e-4);
+ LDDMM::vimg_write(s, fnout);
+
+ sprintf(fnout, "%s_mr%d_InverseWarp.%s", basename, i, extension);
+ LDDMM::vimg_gaussian_smooth(iw, false, factor * sigma, q);
+ LDDMM::vimg_shrink(q, factor, s);
+ LDDMM::vimg_clip_to_zero_in_place(s, 1.0e-4);
+ LDDMM::vimg_write(s, fnout);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ // Read input arguments
+ if(argc < 2)
+ return usage();
+
+ // Get the number of dimensions
+ uint ndim = (uint) atoi(argv[1]);
+ if(ndim == 2)
+ my_main<myreal, 2>(argc, argv);
+ else if(ndim == 3)
+ my_main<myreal, 3>(argc, argv);
+ else
+ return usage();
+
+}
diff --git a/Submodules/greedy/testing/data/lddmm/test01_expected_result.nii.gz b/Submodules/greedy/testing/data/lddmm/test01_expected_result.nii.gz
new file mode 100644
index 0000000..4b1b671
Binary files /dev/null and b/Submodules/greedy/testing/data/lddmm/test01_expected_result.nii.gz differ
diff --git a/Submodules/greedy/testing/data/lddmm/test01_input.nii.gz b/Submodules/greedy/testing/data/lddmm/test01_input.nii.gz
new file mode 100644
index 0000000..aba0e6a
Binary files /dev/null and b/Submodules/greedy/testing/data/lddmm/test01_input.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom01_fixed.nii.gz b/Submodules/greedy/testing/data/phantom01_fixed.nii.gz
new file mode 100644
index 0000000..5de0ed4
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom01_fixed.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom01_mask.nii.gz b/Submodules/greedy/testing/data/phantom01_mask.nii.gz
new file mode 100644
index 0000000..08453ec
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom01_mask.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom01_moving.nii.gz b/Submodules/greedy/testing/data/phantom01_moving.nii.gz
new file mode 100644
index 0000000..53190ab
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom01_moving.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom01_readme.txt b/Submodules/greedy/testing/data/phantom01_readme.txt
new file mode 100644
index 0000000..d2a4baf
--- /dev/null
+++ b/Submodules/greedy/testing/data/phantom01_readme.txt
@@ -0,0 +1,9 @@
+PHANTOM 01/02
+===
+
+Phantom 01 is made of several blocks of four intensity levels, smoothed.
+Phantom 02 is the same, but with intensities swapped, to allow testing of
+mutual information metrics
+
+A simple rigid transform is applied to both
+
diff --git a/Submodules/greedy/testing/data/phantom01_rigid.mat b/Submodules/greedy/testing/data/phantom01_rigid.mat
new file mode 100644
index 0000000..2011784
--- /dev/null
+++ b/Submodules/greedy/testing/data/phantom01_rigid.mat
@@ -0,0 +1,4 @@
+0.96836 -0.202649 0.145646 -21.4932
+0.212385 0.975661 -0.0545691 19.7943
+-0.131043 0.0837755 0.987831 2.63485
+0 0 0 1
diff --git a/Submodules/greedy/testing/data/phantom01_source.nii.gz b/Submodules/greedy/testing/data/phantom01_source.nii.gz
new file mode 100644
index 0000000..7fbcc71
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom01_source.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom02_fixed.nii.gz b/Submodules/greedy/testing/data/phantom02_fixed.nii.gz
new file mode 100644
index 0000000..5000dd9
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom02_fixed.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom02_moving.nii.gz b/Submodules/greedy/testing/data/phantom02_moving.nii.gz
new file mode 100644
index 0000000..f8dc287
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom02_moving.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom03_fixed.nii.gz b/Submodules/greedy/testing/data/phantom03_fixed.nii.gz
new file mode 100644
index 0000000..b981435
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom03_fixed.nii.gz differ
diff --git a/Submodules/greedy/testing/data/phantom03_moving.nii.gz b/Submodules/greedy/testing/data/phantom03_moving.nii.gz
new file mode 100644
index 0000000..5c78678
Binary files /dev/null and b/Submodules/greedy/testing/data/phantom03_moving.nii.gz differ
diff --git a/Submodules/greedy/testing/data/runpair.sh b/Submodules/greedy/testing/data/runpair.sh
new file mode 100644
index 0000000..0cd267e
--- /dev/null
+++ b/Submodules/greedy/testing/data/runpair.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Parameters
+# $1 - fixed phantom number
+# $2 - moving phantom number
+# $3 - metric (NCC in quotes)
+# $4 - mask 1/0
+
+rm -rf /tmp/test_affine.mat /tmp/src_reslice.nii.gz
+
+if [[ ${4?} -eq 1 ]]; then
+ MASK="-gm phantom01_mask.nii.gz"
+fi
+
+# Perform the registration
+echo ../../../xc64rel/greedy -d 3 \
+ -m $3 -a -i phantom${1}_fixed.nii.gz phantom${2}_moving.nii.gz \
+ $MASK -o /tmp/test_affine.mat -n 40x40 -debug-deriv
+
+../../../xc64rel/greedy -d 3 \
+ -m $3 -a -i phantom${1}_fixed.nii.gz phantom${2}_moving.nii.gz \
+ $MASK -o /tmp/test_affine.mat -n 40x40 -debug-deriv
+
+# Apply to the phantom
+../../../xc64rel/greedy -d 3 -ri LABEL 0.1vox \
+ -rm phantom01_source.nii.gz /tmp/src_reslice.nii.gz \
+ -rf phantom01_fixed.nii.gz -r /tmp/test_affine.mat phantom01_rigid.mat
+
+# Compute the overlap
+c3d /tmp/src_reslice.nii.gz phantom01_source.nii.gz -label-overlap
+
+
diff --git a/Submodules/greedy/testing/data/src/phantoms.sh b/Submodules/greedy/testing/data/src/phantoms.sh
new file mode 100644
index 0000000..258f02a
--- /dev/null
+++ b/Submodules/greedy/testing/data/src/phantoms.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# Generate phantom 01
+c3d phantom01_source.nii.gz -scale 60 -smooth 1vox -info -type uchar -o phantom01_fixed.nii.gz
+
+# Create a mask
+c3d phantom01_source.nii.gz -thresh 0.5 inf 1 0 -dilate 1 2x2x2 \
+ -type uchar -o phantom01_mask.nii.gz
+
+# Generate the rigid transform
+c3d_affine_tool \
+ -tran -64 -64 64 \
+ -rot 15 1 2 3 \
+ -tran 3 4 5 \
+ -tran 64 64 -64 \
+ -mult -mult -mult -info \
+ -o phantom01_rigid.mat
+
+# Generate the moving phantom image 01
+c3d phantom01_fixed.nii.gz phantom01_fixed.nii.gz -reslice-matrix phantom01_rigid.mat \
+ -type uchar -o phantom01_moving.nii.gz
+
+# Generate the second phantom for mutual information
+c3d seg.nii.gz -replace 4 2 3 1 1 4 2 3 -scale 60 -smooth 1vox -info \
+ -type uchar -o phantom02_fixed.nii.gz
+
+# Apply rotation to the second phantom
+c3d phantom02_fixed.nii.gz phantom02_fixed.nii.gz -reslice-matrix phantom01_rigid.mat \
+ -type uchar -o phantom02_moving.nii.gz
+
+# Create a phantom with a smooth gradient to examine issues with NCC
+c3d phantom01_source.nii.gz -as X -pad 64x64x64 64x64x64 0 -info \
+ -dup -cmv -pop -popas Y -times -push Y -add -smooth 1mm -pim r -stretch 0% 100% 0 255 \
+ -popas Q -push X -push Q -reslice-identity -o phantom03_fixed.nii.gz \
+ -push X -push Q -reslice-matrix phantom01_rigid.mat -o phantom03_moving.nii.gz
diff --git a/Submodules/greedy/testing/src/TestOneDimensionalInPlaceAccumulateFilter.cxx b/Submodules/greedy/testing/src/TestOneDimensionalInPlaceAccumulateFilter.cxx
new file mode 100644
index 0000000..9564203
--- /dev/null
+++ b/Submodules/greedy/testing/src/TestOneDimensionalInPlaceAccumulateFilter.cxx
@@ -0,0 +1,68 @@
+/*=========================================================================
+
+ Program: ALFABIS fast medical image registration programs
+ Language: C++
+ Website: github.com/pyushkevich/greedy
+ Copyright (c) Paul Yushkevich, University of Pennsylvania. All rights reserved.
+
+ This program is part of ALFABIS: Adaptive Large-Scale Framework for
+ Automatic Biomedical Image Segmentation.
+
+ ALFABIS development is funded by the NIH grant R01 EB017255.
+
+ ALFABIS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ ALFABIS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ALFABIS. If not, see <http://www.gnu.org/licenses/>.
+
+=========================================================================*/
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include "OneDimensionalInPlaceAccumulateFilter.h"
+#include "itkTimeProbe.h"
+
+int main(int argc, char *argv[])
+{
+ typedef itk::VectorImage<float, 3> ImageType;
+ typedef itk::ImageFileReader<ImageType> ReaderType;
+ typedef itk::ImageFileWriter<ImageType> WriterType;
+
+ ReaderType::Pointer reader = ReaderType::New();
+ reader->SetFileName(argv[1]);
+ reader->Update();
+
+ ImageType::SizeType radius; radius.Fill(2);
+
+
+ typedef OneDimensionalInPlaceAccumulateFilter<ImageType> AccumFilterType;
+
+ itk::ImageSource<ImageType>::Pointer pipeTail;
+ for(int dir = 0; dir < ImageType::ImageDimension; dir++)
+ {
+ AccumFilterType::Pointer accum = AccumFilterType::New();
+ accum->SetInput(pipeTail.IsNull() ? reader->GetOutput() : pipeTail->GetOutput());
+ accum->SetDimension(dir);
+ accum->SetRadius(radius[dir]);
+ pipeTail = accum;
+
+ itk::TimeProbe tp;
+ tp.Start();
+ accum->Update();
+ tp.Stop();
+
+ printf("Direction %d elapsed ms: %6.2f\n", dir, 1000 * tp.GetTotal());
+ }
+
+ WriterType::Pointer writer = WriterType::New();
+ writer->SetFileName(argv[2]);
+ writer->SetInput(pipeTail->GetOutput());
+ writer->Update();
+}
diff --git a/Testing/GUI/Qt/SNAPTestQt.cxx b/Testing/GUI/Qt/SNAPTestQt.cxx
index 3875227..a2d6162 100644
--- a/Testing/GUI/Qt/SNAPTestQt.cxx
+++ b/Testing/GUI/Qt/SNAPTestQt.cxx
@@ -16,7 +16,8 @@
#include <QMouseEvent>
#include <QApplication>
#include <QKeySequence>
-
+#include <QDir>
+#include <SNAPQApplication.h>
#include "SNAPQtCommon.h"
@@ -73,7 +74,7 @@ SNAPTestQt::LaunchTest(std::string test)
if(test == "list")
{
ListTests();
- ::exit(SUCCESS);
+ application_exit(SUCCESS);
}
// Create and run the thread
@@ -135,6 +136,10 @@ QModelIndex SNAPTestQt::findItem(QObject *container, QVariant text)
return QModelIndex();
}
+void SNAPTestQt::invoke(QObject *object, QString slot)
+{
+ QMetaObject::invokeMethod(object, slot.toStdString().c_str(), Qt::QueuedConnection);
+}
QVariant SNAPTestQt::findItemRow(QObject *container, QVariant text)
@@ -210,7 +215,7 @@ void SNAPTestQt::validateValue(QVariant v1, QVariant v2)
qWarning() << QString("Validation %1 == %2 failed!").arg(v1.toString(),v2.toString());
// Exit with code corresponding to test failure
- ::exit(REGRESSION_TEST_FAILURE);
+ application_exit(REGRESSION_TEST_FAILURE);
}
else
{
@@ -220,6 +225,13 @@ void SNAPTestQt::validateValue(QVariant v1, QVariant v2)
}
+void SNAPTestQt::application_exit(int rc)
+{
+ QMetaObject::invokeMethod(
+ QCoreApplication::instance(), "quitWithReturnCode", Qt::QueuedConnection,
+ Q_ARG(int, rc));
+}
+
void SNAPTestQt::sleep(int milli_sec)
{
// Scale requested sleep time by acceleration factor
@@ -240,7 +252,7 @@ void SNAPTestQt::validateFloatValue(double v1, double v2, double precision)
qWarning() << QString("Validation %1 == %2 (with precision %3) failed!").arg(v1).arg(v2).arg(precision);
// Exit with code corresponding to test failure
- ::exit(REGRESSION_TEST_FAILURE);
+ application_exit(REGRESSION_TEST_FAILURE);
}
else
{
@@ -252,7 +264,7 @@ void SNAPTestQt::validateFloatValue(double v1, double v2, double precision)
void SNAPTestQt::testFailed(QString reason)
{
qWarning() << reason;
- ::exit(REGRESSION_TEST_FAILURE);
+ application_exit(REGRESSION_TEST_FAILURE);
}
@@ -315,11 +327,24 @@ void SNAPTestQt::postKeyEvent(QObject *object, QString key)
}
}
+
SNAPTestQt::ReturnCode
SNAPTestQt::ListTests()
{
+ QDir script_dir(":/scripts/Scripts");
+ QStringList filters; filters << "test_*.js";
+ script_dir.setNameFilters(filters);
+ QStringList files = script_dir.entryList();
+
+ QRegExp rx("test_(.*).js");
+
cout << "Available Tests" << endl;
- cout << " SnakeThreshQt : Test segmentation with thresholding option" << endl;
+ foreach(const QString &test, files)
+ {
+ if(rx.indexIn(test) >= 0)
+ cout << " " << rx.cap(1).toStdString() << endl;
+ }
+
return SUCCESS;
}
@@ -344,8 +369,8 @@ void TestWorker::run()
// Run the top-level script
source(m_MainScript);
- // Once the test has completed, we can exit the application
- ::exit(SNAPTestQt::SUCCESS);
+ // Exit script
+ SNAPTestQt::application_exit(SNAPTestQt::SUCCESS);
}
void TestWorker::sleep_ms(unsigned int msec)
@@ -365,7 +390,7 @@ void TestWorker::readScript(QString script_url, QString &script)
if(!file.open(QIODevice::ReadOnly))
{
qWarning() << QString("Unable to read test script %1").arg(script_url);
- ::exit(SNAPTestQt::NO_SUCH_TEST);
+ SNAPTestQt::application_exit(SNAPTestQt::NO_SUCH_TEST);
}
// Read the script
@@ -425,6 +450,6 @@ void TestWorker::source(QString script_url)
if(rc.isError())
{
qWarning() << "JavaScript exception:" << rc.toString();
- ::exit(SNAPTestQt::REGRESSION_TEST_FAILURE);
+ SNAPTestQt::application_exit(SNAPTestQt::EXCEPTION_CAUGHT);
}
}
diff --git a/Testing/GUI/Qt/SNAPTestQt.h b/Testing/GUI/Qt/SNAPTestQt.h
index 98ab9e1..58daaf3 100644
--- a/Testing/GUI/Qt/SNAPTestQt.h
+++ b/Testing/GUI/Qt/SNAPTestQt.h
@@ -48,6 +48,7 @@ protected:
double m_Acceleration;
void readScript(QString script_url, QString &script);
+
};
class SNAPTestQt : public QObject
@@ -78,6 +79,9 @@ public slots:
// Find a widget by name globally
QWidget *findWidget(QString widgetName);
+ // Invoke a slot
+ void invoke(QObject *object, QString slot);
+
// Return the contents of an item in a table
QVariant tableItemText(QObject *table, int row, int col);
@@ -105,6 +109,8 @@ public slots:
void sleep(int milli_sec);
+ static void application_exit(int rc);
+
protected:
ReturnCode ListTests();
diff --git a/Testing/GUI/Qt/Scripts/test_DiffSpace.js b/Testing/GUI/Qt/Scripts/test_DiffSpace.js
new file mode 100644
index 0000000..612fb27
--- /dev/null
+++ b/Testing/GUI/Qt/Scripts/test_DiffSpace.js
@@ -0,0 +1,49 @@
+// Read the function library
+include("Library");
+
+// Open the test workspace
+openWorkspace("diffspace.itksnap");
+
+//=== Probe the image intensities at one location
+setCursor(21,33,14);
+
+// Check the image intensity
+var value1 = readVoxelIntensity(0);
+engine.validateValue(value1, 31)
+
+var value2 = readVoxelIntensity(1);
+engine.validateFloatValue(value2, 608, 10)
+
+var value3 = readVoxelIntensity(2);
+engine.validateFloatValue(value3, 554, 10)
+
+//=== Opening registration panel
+engine.findChild(mainwin,"actionRegistration").trigger();
+var regpanel = engine.findChild(mainwin,"RegistrationDialog");
+
+//=== Run automatic registration
+engine.findChild(regpanel, "btnRunRegistration").click();
+engine.sleep(5000);
+
+//=== Play with the multi_chunk, make sure it can be resliced
+engine.print(engine.findChild(regpanel, "tabWidget").currentIndex);
+engine.print(engine.findChild(regpanel, "tabWidget").currentTabText);
+engine.findChild(regpanel, "tabWidget").currentIndex = 1;
+engine.findChild(regpanel, "inMovingLayer").currentText = "multi_chunk";
+
+//=== Set manual registration parameters
+engine.findChild(regpanel, "inRotX").value = -52.0;
+engine.findChild(regpanel, "inRotY").value = 46.0;
+engine.findChild(regpanel, "inTranX").value = 12.0;
+engine.findChild(regpanel, "inTranY").value = 8.0;
+engine.findChild(regpanel, "inTranZ").value = -26.0;
+engine.sleep(1000);
+
+var value4 = readVoxelIntensity(2);
+engine.validateFloatValue(value4, 513, 10)
+
+// Probe the image intensity outside of the image range
+setCursor(4,17,14);
+
+var value5 = readVoxelIntensity(2);
+engine.validateValue(value5, 0);
diff --git a/Testing/GUI/Qt/Scripts/test_Library.js b/Testing/GUI/Qt/Scripts/test_Library.js
index c4efa7d..9302e11 100644
--- a/Testing/GUI/Qt/Scripts/test_Library.js
+++ b/Testing/GUI/Qt/Scripts/test_Library.js
@@ -6,7 +6,6 @@ function setCursor(x, y, z)
engine.findChild(mainwin, "inCursorZ").value = z;
engine.sleep(200);
}
-
function openMainImage(name)
{
//=== Opening Dialog
@@ -24,7 +23,6 @@ function openMainImage(name)
engine.findChild(dialog, "qt_wizard_finish").click();
engine.sleep(1000);
}
-
function openWorkspace(name)
{
//=== Opening Dialog
@@ -35,10 +33,9 @@ function openWorkspace(name)
engine.findChild(dialog, "inFilename").text = datadir + "/" + name;
//=== Accepting text
- dialog.accept();
- engine.sleep(1000);
+ engine.invoke(dialog, "accept");
+ engine.sleep(4000);
}
-
function enterSnakeMode(pos_x, pos_y, pos_z, size_x, size_y, size_z)
{
//=== Entering snake mode
@@ -56,9 +53,8 @@ function enterSnakeMode(pos_x, pos_y, pos_z, size_x, size_y, size_z)
//=== Pushing the Segment3D button
engine.findChild(roipanel,"btnAuto").click();
- engine.sleep(1000);
+ engine.sleep(2000);
}
-
function enterSnakeModeFullROI()
{
//=== Entering snake mode
@@ -71,9 +67,8 @@ function enterSnakeModeFullROI()
//=== Pushing the Segment3D button
engine.findChild(roipanel,"btnAuto").click();
- engine.sleep(1000);
+ engine.sleep(2000);
}
-
function readVoxelIntensity(layer_row)
{
var voxtable = engine.findChild(mainwin, "tableVoxelUnderCursor");
@@ -81,21 +76,17 @@ function readVoxelIntensity(layer_row)
return value;
}
-
function setForegroundLabel(label_text)
{
var combo = engine.findChild(mainwin,"inForeLabel");
var index = engine.findItemRow(combo,label_text);
engine.print("Setting foreground label to " + label_text + " at pos " + index)
combo.setCurrentIndex(index);
-
}
-
function setBackgroundLabel(label_text)
{
var combo = engine.findChild(mainwin,"inBackLabel");
var index = engine.findItemRow(combo,label_text);
engine.print("Setting background label to " + label_text + " at pos " + index)
combo.setCurrentIndex(index);
-
}
diff --git a/Testing/GUI/Qt/Scripts/test_Workspace.js b/Testing/GUI/Qt/Scripts/test_Workspace.js
index 3e4dfc6..06eedc1 100644
--- a/Testing/GUI/Qt/Scripts/test_Workspace.js
+++ b/Testing/GUI/Qt/Scripts/test_Workspace.js
@@ -24,7 +24,7 @@ var inpreset = engine.findChild(cmpcolormap, "inPreset");
inpreset.setCurrentIndex(8);
//=== Close the inspector dialog
-layerdialog.close();
+engine.invoke(layerdialog, "close");
//=== Trigger the unload all action
engine.findChild(mainwin,"actionUnload_All").trigger();
diff --git a/Testing/GUI/Qt/TestingScripts.qrc b/Testing/GUI/Qt/TestingScripts.qrc
index f6deb02..23822e6 100644
--- a/Testing/GUI/Qt/TestingScripts.qrc
+++ b/Testing/GUI/Qt/TestingScripts.qrc
@@ -1,9 +1,10 @@
<RCC>
<qresource prefix="/scripts">
- <file>Scripts/test_Library.js</file>
- <file>Scripts/test_ProbeIntensity.js</file>
- <file>Scripts/test_RegionCompetition.js</file>
- <file>Scripts/test_RandomForest.js</file>
- <file>Scripts/test_Workspace.js</file>
+ <file>Scripts/test_Library.js</file>
+ <file>Scripts/test_ProbeIntensity.js</file>
+ <file>Scripts/test_RegionCompetition.js</file>
+ <file>Scripts/test_RandomForest.js</file>
+ <file>Scripts/test_Workspace.js</file>
+ <file>Scripts/test_DiffSpace.js</file>
</qresource>
</RCC>
diff --git a/Testing/Logic/SNAPTestDriver.cxx b/Testing/Logic/SNAPTestDriver.cxx
deleted file mode 100644
index 125d07f..0000000
--- a/Testing/Logic/SNAPTestDriver.cxx
+++ /dev/null
@@ -1,256 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: SNAPTestDriver.cxx,v $
- Language: C++
- Date: $Date: 2010/10/14 18:09:25 $
- Version: $Revision: 1.5 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-#include "SNAPTestDriver.h"
-#include "TestImageWrapper.h"
-#include "GreyImageWrapper.h"
-#include "LabelImageWrapper.h"
-#include "SpeedImageWrapper.h"
-
-
-#include "CommandLineArgumentParser.h"
-#include <iostream>
-#include <iomanip>
-
-using namespace std;
-
-const unsigned int SNAPTestDriver::NUMBER_OF_TESTS = 4;
-const char *SNAPTestDriver::m_TestNames[] = { "ImageWrapper",
- "IRISImageData","SNAPImageData","Preprocessing" };
-const bool SNAPTestDriver::m_TestTemplated[] = { true, false, false, false };
-
-void
-SNAPTestDriver
-::PrintUsage()
-{
- std::cerr << "SNAP Test Driver Usage:" << std::endl;
- std::cerr << " SNAPTest list" << std::endl;
- std::cerr << "or " << std::endl;
- std::cerr << " SNAPTest help NAME " << std::endl;
- std::cerr << "or " << std::endl;
- std::cerr << " SNAPTest test NAME [type TYPE] [options]" << std::endl;
- std::cerr << "Commands :" << std::endl;
- std::cerr << " list List all known tests" << std::endl;
- std::cerr << " help NAME Provide help on test 'NAME'" << std::endl;
- std::cerr << " test NAME Run test 'NAME'" << std::endl;
- std::cerr << " type TYPE Specify template parameter of test" << std::endl;
- std::cerr << " (e.g., unsigned_short)" << std::endl;
-}
-
-template <class TPixel>
-SNAPTestDriver::TemplatedTestCreator<TPixel>
-::TemplatedTestCreator(const char *name)
-{
- string strName = name;
-
- if(strName == "ImageWrapper")
- {
- if(typeid(TPixel) == typeid(GreyType))
- m_Test = new TestImageWrapper<GreyType, GreyImageWrapper<GreyType> >();
- else if(typeid(TPixel) == typeid(LabelType))
- m_Test = new TestImageWrapper<LabelType, LabelImageWrapper>();
- else if(typeid(TPixel) == typeid(float))
- m_Test = new TestImageWrapper<float, SpeedImageWrapper>();
- else
- m_Test = NULL;
- }
- else
- m_Test = NULL;
-}
-
-template <class TPixel>
-TestBase *
-SNAPTestDriver::TemplatedTestCreator<TPixel>
-::GetTest()
-{
- return m_Test;
-}
-
-TestBase *
-SNAPTestDriver
-::CreateNonTemplateTest(const char *name)
-{
- string strName = name;
- TestBase *test = NULL;
-
- return test;
-}
-
-TestBase *
-SNAPTestDriver
-::CreateTestInstance(const char *name)
-{
- TestBase *test = CreateNonTemplateTest(name);
- if(!test)
- test = TemplatedTestCreator<unsigned char>(name).GetTest();
- return test;
-}
-
-void
-SNAPTestDriver
-::Run(int argc, char *argv[])
-{
- // Configure the command line
- CommandLineArgumentParser clap;
- clap.AddOption("help",1);
- clap.AddOption("list",0);
- clap.AddOption("test",1);
- clap.AddOption("type",1);
-
- // Parse the command line
- CommandLineArgumentParseResult parms;
- if(!clap.TryParseCommandLine(argc,argv,parms,false))
- {
- PrintUsage();
- return;
- }
-
- // Check if the user wants help
- if(parms.IsOptionPresent("help"))
- {
- // Get the file name
- const char *name = parms.GetOptionParameter("help");
-
- // Create a test instance, ingoring type
- TestBase *test = CreateTestInstance(name);
-
- // Print test usage
- if(test)
- {
- std::cout << "SNAPTests " << name << " options" << std::endl;
- std::cout << "Options: " << std::endl;
- test->PrintUsage();
- }
-
- else
- std::cerr << "Unknown test name: " << name << std::endl;
- }
-
- else if(parms.IsOptionPresent("list"))
- {
- // Print out a header
- std::cout << std::setw(20) << std::ios::left << "Test Name";
- std::cout << std::setw(12) << std::ios::left << "Templated";
- std::cout << "Description" << std::endl;
-
- // Go through the list of known tests
- for(unsigned int i=0;i<NUMBER_OF_TESTS;i++)
- {
- TestBase *test = CreateTestInstance(m_TestNames[i]);
-
- if(test)
- {
- // Print out test info
- std::cout << std::setw(20) << std::ios::left << m_TestNames[i];
- std::cout << std::setw(12) << std::ios::left << (m_TestTemplated[i] ? "Yes" : "No");
- std::cout << test->GetDescription() << std::endl;
- }
- }
- }
- else if(parms.IsOptionPresent("test"))
- {
- // Get the file name
- const char *name = parms.GetOptionParameter("test");
-
- // Check if the test can be created without a template parameter
- TestBase *test = CreateNonTemplateTest(name);
-
- // If that failed, check if the test can be created using a template
- // parameter
- if(!test && (test = TemplatedTestCreator<unsigned char>(name).GetTest()))
- {
- // Get the template type or a blank string
- string type = parms.IsOptionPresent("type") ?
- parms.GetOptionParameter("type") : "";
-
- // Instantiate the template test of the right type
- if(type == "char")
- test = TemplatedTestCreator<char>(name).GetTest();
- else if(type == "unsigned_char")
- test = TemplatedTestCreator<unsigned char>(name).GetTest();
- if(type == "short")
- test = TemplatedTestCreator<short>(name).GetTest();
- else if(type == "unsigned_short")
- test = TemplatedTestCreator<unsigned short>(name).GetTest();
- if(type == "int")
- test = TemplatedTestCreator<int>(name).GetTest();
- else if(type == "unsigned_int")
- test = TemplatedTestCreator<unsigned int>(name).GetTest();
- if(type == "long")
- test = TemplatedTestCreator<long>(name).GetTest();
- else if(type == "unsigned_long")
- test = TemplatedTestCreator<unsigned long>(name).GetTest();
- if(type == "float")
- test = TemplatedTestCreator<float>(name).GetTest();
- else if(type == "double")
- test = TemplatedTestCreator<double>(name).GetTest();
- else
- {
- std::cerr << "Missing or invalid or missing type parameter. Using default (char)" << std::endl;
- std::cerr << "Should be one of: " << std::endl;
- std::cerr << " char, unsigned_char," << std::endl;
- std::cerr << " short, unsigned_short," << std::endl;
- std::cerr << " int, unsigned_int," << std::endl;
- std::cerr << " long, unsigned_long," << std::endl;
- std::cerr << " float, " << std::endl;
- std::cerr << " double." << std::endl;
- }
- }
-
- // See if a test has been created after all
- if(test)
- {
- try
- {
- // Configure the parameters of the test
- test->ConfigureCommandLineParser(clap);
-
- // Get the command line result
- CommandLineArgumentParseResult testParms;
- if(clap.TryParseCommandLine(argc,argv,testParms,false))
- {
- test->SetCommandLineParameters(testParms);
- test->Run();
- }
- else
- {
- test->PrintUsage();
- }
-
- delete test;
- }
- catch(TestUsageException)
- {
- test->PrintUsage();
- }
- catch(itk::ExceptionObject &exc)
- {
- std::cerr << "ITK Exception: " << std::endl << exc << std::endl;
- }
- catch(...)
- {
- std::cerr << "Unknowm Exception!" << std::endl;
- }
- }
- else
- {
- std::cerr << "Could not create test!" << std::endl;
- PrintUsage();
- }
- }
- else
- {
- PrintUsage();
- }
-}
diff --git a/Testing/Logic/SNAPTestDriver.h b/Testing/Logic/SNAPTestDriver.h
deleted file mode 100644
index 2afb5b0..0000000
--- a/Testing/Logic/SNAPTestDriver.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: SNAPTestDriver.h,v $
- Language: C++
- Date: $Date: 2006/12/02 04:22:20 $
- Version: $Revision: 1.1 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-#ifndef __SNAPTestDriver_h_
-#define __SNAPTestDriver_h_
-
-#include "TestBase.h"
-
-/**
- * \class SNAPTestDriver
- * \brief Class used to launch different tests
- */
-class SNAPTestDriver
-{
-public:
- /** Run a test as determined by the command line parameters */
- void Run(int argc, char *argv[]);
-
-private:
-
- /** Print the usage of the test application */
- void PrintUsage();
-
- /** Create a test of given name, or return NULL */
- static TestBase *CreateNonTemplateTest(const char *name);
-
- /** Class to create a templated test of given name, or return NULL */
- template <class TPixel> class TemplatedTestCreator
- {
- public:
- TemplatedTestCreator(const char *name);
- TestBase *GetTest();
- private:
- TestBase *m_Test;
- };
-
- /** Create a test of given name, either templated or not, o/w return NULL */
- TestBase *CreateTestInstance(const char *name);
-
- /** Number of tests */
- static const unsigned int NUMBER_OF_TESTS;
-
- /** Test names */
- static const char *m_TestNames[];
-
- /** Whether tests are templated or not */
- static const bool m_TestTemplated[];
-};
-
-#endif // __SNAPTestDriver_h_
-
-
diff --git a/Testing/Logic/SlicingPerformanceTest.cxx b/Testing/Logic/SlicingPerformanceTest.cxx
new file mode 100644
index 0000000..7e88a8f
--- /dev/null
+++ b/Testing/Logic/SlicingPerformanceTest.cxx
@@ -0,0 +1,331 @@
+#include <iostream>
+#include <stdexcept>
+#include <utility>
+
+using namespace std;
+
+#include <itkImage.h>
+#include <itkImageFileReader.h>
+#include <itkImageFileWriter.h>
+#include <itkLabelMap.h>
+#include <itkLabelObject.h>
+#include <itkLabelMapToLabelImageFilter.h>
+#include <itkLabelImageToLabelMapFilter.h>
+#include <itkRegionOfInterestImageFilter.h>
+#include <itkChangeRegionLabelMapFilter.h>
+#include <itkTestingComparisonImageFilter.h>
+#include <itkExtractImageFilter.h>
+#include "IRISSlicer.h"
+#include "RLERegionOfInterestImageFilter.h"
+#include <itkTimeProbe.h>
+
+typedef itk::Image<short, 3> Seg3DImageType;
+typedef itk::Image<short, 2> Seg2DImageType;
+typedef itk::LabelMap<itk::LabelObject<short, 3 > > Label3DType;
+typedef itk::LabelMap<itk::LabelObject<short, 2 > > Label2DType;
+
+typedef itk::ImageFileReader<Seg3DImageType> SegReaderType;
+typedef itk::ImageFileWriter<Seg2DImageType> SegWriterType;
+
+Seg3DImageType::Pointer loadImage(const string filename)
+{
+ SegReaderType::Pointer sr = SegReaderType::New();
+ sr->SetFileName(filename);
+ sr->Update();
+ return sr->GetOutput();
+}
+
+Label3DType::Pointer toLabelMap(Seg3DImageType::Pointer image)
+{
+ typedef itk::LabelImageToLabelMapFilter<Seg3DImageType> i2lType;
+ i2lType::Pointer i2l = i2lType::New();
+ i2l->SetInput(image);
+ i2l->Update();
+ return i2l->GetOutput();
+}
+
+typedef RLEImage<short> RLEImage3D;
+typedef std::pair<short, short> RLSegment;
+typedef std::vector<RLSegment> RLLine;
+typedef std::vector<std::vector<RLLine> > RLImage;
+
+RLImage toRLImage(Seg3DImageType::Pointer image)
+{
+ itk::Size<3> iSize = image->GetLargestPossibleRegion().GetSize();
+ RLImage result(iSize[2]);
+ for (int z = 0; z < iSize[2]; z++)
+ result[z].resize(iSize[1]);
+ itk::Index<3> ind;
+ for (int z = 0; z < iSize[2]; z++)
+ {
+ ind[2] = z;
+ for (int y = 0; y < iSize[1]; y++)
+ {
+ ind[1] = y;
+ ind[0] = 0;
+ int x = 0;
+ RLLine l;
+ while (x < iSize[0])
+ {
+ RLSegment s(0, image->GetPixel(ind));
+ while (x < iSize[0] && image->GetPixel(ind) == s.second)
+ {
+ x++;
+ s.first++;
+ ind[0] = x;
+ }
+ l.push_back(s);
+ }
+ result[z][y] = l;
+ }
+ }
+ return result;
+}
+
+int sliceIndex, axis;
+Seg3DImageType::RegionType reg;
+
+void uncompressLine(RLLine line, short *out)
+{
+ for (int x = 0; x < line.size(); x++)
+ for (int r = 0; r < line[x].first; r++)
+ *(out++) = line[x].second;
+}
+
+void cropRLI(RLImage image, short *outSlice)
+{
+ itk::Size<3> size;
+ size[2] = image.size();
+ size[1] = image[0].size();
+ size[0] = 0;
+ for (int x = 0; x < image[0][0].size(); x++)
+ size[0] += image[0][0][x].first;
+
+ if (axis == 2) //slicing along z
+ for (int y = 0; y < size[1]; y++)
+ uncompressLine(image[sliceIndex][y], outSlice + y*size[0]);
+ else if (axis == 1) //slicing along y
+ for (int z = 0; z < size[2]; z++)
+ uncompressLine(image[z][sliceIndex], outSlice + z*size[0]);
+ else //slicing along x, the low-preformance case
+ for (int z = 0; z < size[2]; z++)
+ for (int y = 0; y < size[1]; y++)
+ {
+ int t = 0;
+ for (int x = 0; x < image[z][y].size(); x++)
+ {
+ t += image[z][y][x].first;
+ if (t > sliceIndex)
+ {
+ *(outSlice++) = image[z][y][x].second;
+ break;
+ }
+ }
+ }
+}
+
+Seg3DImageType::Pointer cropNormal(Seg3DImageType::Pointer image)
+{
+ typedef itk::RegionOfInterestImageFilter<Seg3DImageType, Seg3DImageType> roiType;
+ roiType::Pointer roi = roiType::New();
+ roi->SetInput(image);
+ roi->SetRegionOfInterest(reg);
+ roi->Update();
+ return roi->GetOutput();
+}
+
+Seg2DImageType::Pointer cropIRIS(Seg3DImageType::Pointer image)
+{
+ typedef IRISSlicer<Seg3DImageType, Seg2DImageType, Seg3DImageType> roiType;
+ roiType::Pointer roi = roiType::New();
+ roi->SetInput(image);
+ roi->SetSliceIndex(sliceIndex);
+ roi->SetSliceDirectionImageAxis(axis);
+ if (axis == 0) //x
+ {
+ roi->SetLineDirectionImageAxis(2);
+ roi->SetPixelDirectionImageAxis(1);
+ }
+ else if (axis==1) //y
+ {
+ roi->SetLineDirectionImageAxis(2);
+ roi->SetPixelDirectionImageAxis(0);
+ }
+ else //z
+ {
+ roi->SetLineDirectionImageAxis(1);
+ roi->SetPixelDirectionImageAxis(0);
+ }
+ roi->Update();
+ return roi->GetOutput();
+}
+
+Seg2DImageType::Pointer cropRLEiris(RLEImage3D::Pointer image)
+{
+ typedef IRISSlicer<RLEImage3D, Seg2DImageType, RLEImage3D> roiType;
+ roiType::Pointer roi = roiType::New();
+ roi->SetInput(image);
+ roi->SetSliceIndex(sliceIndex);
+ roi->SetSliceDirectionImageAxis(axis);
+ if (axis == 0) //x
+ {
+ roi->SetLineDirectionImageAxis(2);
+ roi->SetPixelDirectionImageAxis(1);
+ }
+ else if (axis == 1) //y
+ {
+ roi->SetLineDirectionImageAxis(2);
+ roi->SetPixelDirectionImageAxis(0);
+ }
+ else //z
+ {
+ roi->SetLineDirectionImageAxis(1);
+ roi->SetPixelDirectionImageAxis(0);
+ }
+ roi->Update();
+ return roi->GetOutput();
+}
+
+Seg3DImageType::Pointer cropRLE(Label3DType::Pointer image)
+{
+ typedef itk::ChangeRegionLabelMapFilter<Label3DType> roiLMType;
+ roiLMType::Pointer roiLM = roiLMType::New();
+ roiLM->SetInput(image);
+ roiLM->SetRegion(reg);
+ roiLM->Update();
+
+ //below conversion has easily predictable and low computational complexity
+ typedef itk::LabelMapToLabelImageFilter<Label3DType, Seg3DImageType> lm2liType;
+ lm2liType::Pointer lm2li = lm2liType::New();
+ lm2li->SetInput(roiLM->GetOutput());
+ lm2li->Update();
+ return lm2li->GetOutput();
+}
+
+//do some slicing operations, measure time taken
+int main(int argc, char *argv[])
+{
+ if (argc < 5)
+ {
+ cout << "Usage:\n" << argv[0] << " InputImage3D.ext OutputSlice2D.ext X|Y|Z SliceNumber [RLE|RLI|IRIS|Normal]" << endl;
+ return 1;
+ }
+
+ sliceIndex = atoi(argv[4]);
+ switch (argv[3][0])
+ {
+ case 'X':
+ case 'x': axis = 0; break;
+ case 'Y':
+ case 'y': axis = 1; break;
+ case 'Z':
+ case 'z': axis = 2; break;
+ default: throw std::runtime_error("Axis should be X, Y or Z");
+ }
+
+ bool rle = true;
+ if (argc>5)
+ if (strcmp(argv[5], "RLE") && strcmp(argv[5], "rle"))
+ rle = false;
+ bool rli = false;
+ if (argc>5)
+ if (strcmp(argv[5], "RLI") == 0 || strcmp(argv[5], "rli") == 0)
+ rli = true;
+ bool iris = false;
+ if (argc>5)
+ if (strcmp(argv[5], "IRIS") == 0 || strcmp(argv[5], "iris") == 0)
+ iris = true;
+ bool irisRLE = false;
+ if (argc>5)
+ if (strcmp(argv[5], "irisRLE") == 0 || strcmp(argv[5], "irisrle") == 0)
+ irisRLE = true;
+ bool memCheck = false;
+ if (argc>6)
+ if (strcmp(argv[6], "MEM") == 0 || strcmp(argv[6], "mem") == 0)
+ memCheck = true;
+
+ Seg3DImageType::Pointer cropped, inImage = loadImage(argv[1]);
+ Label3DType::Pointer inLabelMap;
+ RLEImage3D::Pointer rleImage;
+ RLImage rlImage;
+ reg = inImage->GetLargestPossibleRegion();
+ reg.SetIndex(axis, sliceIndex);
+ reg.SetSize(axis, 1);
+ Seg2DImageType::Pointer cropped2D = cropIRIS(inImage); //pre-allocate slice
+
+ if (rle)
+ {
+ inLabelMap = toLabelMap(inImage);
+ inImage = Seg3DImageType::New(); //effectively deletes the image
+ }
+ if (rli)
+ {
+ rlImage = toRLImage(inImage);
+ inImage = Seg3DImageType::New(); //effectively deletes the image
+ memset(cropped2D->GetBufferPointer(), 0, sizeof(short)*cropped2D->GetOffsetTable()[2]); //clear buffer
+ }
+ if (irisRLE)
+ {
+ typedef itk::RegionOfInterestImageFilter<Seg3DImageType, RLEImage3D> inConverterType;
+ inConverterType::Pointer inConv = inConverterType::New();
+ inConv->SetInput(inImage);
+ inConv->SetRegionOfInterest(inImage->GetLargestPossibleRegion());
+ inConv->Update();
+ rleImage = inConv->GetOutput();
+ inImage = Seg3DImageType::New(); //effectively deletes the image
+ }
+ if (memCheck)
+ {
+ cout << "Now check memory consumption";
+ getchar();
+ }
+
+ itk::TimeProbe tp;
+ tp.Start();
+ if (rle)
+ cropped = cropRLE(inLabelMap);
+ else if (rli)
+ cropRLI(rlImage, cropped2D->GetBufferPointer());
+ else if (iris)
+ cropped2D = cropIRIS(inImage);
+ else if (irisRLE)
+ cropped2D = cropRLEiris(rleImage);
+ else
+ cropped = cropNormal(inImage);
+ tp.Stop();
+
+ if (rle)
+ cout << "RLE";
+ else if (rli)
+ cout << "RLI";
+ else if (iris)
+ cout << "IRIS";
+ else if (irisRLE)
+ cout << "irisRLE";
+ else
+ cout << "Normal";
+
+ cout << " slicing took: " << tp.GetMean() * 1000 << " ms " << endl;
+
+
+ if (!iris && !rli && !irisRLE)
+ {
+ typedef itk::ExtractImageFilter<Seg3DImageType, Seg2DImageType> eiType;
+ eiType::Pointer ei = eiType::New();
+ ei->SetDirectionCollapseToIdentity();
+ ei->SetInput(cropped);
+ reg.SetIndex(axis, 0);
+ cropped->SetRegions(reg);
+ reg.SetSize(axis, 0);
+ ei->SetExtractionRegion(reg);
+ ei->Update();
+ cropped2D = ei->GetOutput();
+ }
+
+ SegWriterType::Pointer wr = SegWriterType::New();
+ wr->SetInput(cropped2D);
+ wr->SetFileName(argv[2]);
+ wr->SetUseCompression(true);
+ wr->Update();
+ return 0;
+}
diff --git a/Testing/Logic/TestBase.h b/Testing/Logic/TestBase.h
deleted file mode 100644
index 42b26f4..0000000
--- a/Testing/Logic/TestBase.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: TestBase.h,v $
- Language: C++
- Date: $Date: 2009/11/14 16:19:56 $
- Version: $Revision: 1.2 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-#ifndef __TestBase_h_
-#define __TestBase_h_
-
-#include "CommandLineArgumentParser.h"
-#include "ImageIORoutines.h"
-#include "itkOrientedImage.h"
-
-/** Exception when needed parameters are omitted */
-class TestUsageException : public itk::ExceptionObject {};
-
-class TestBase
-{
-public:
- // Describe the usage of the test
- virtual void PrintUsage() = 0;
-
- // Get the ID of the test for command line specification
- virtual const char *GetTestName() = 0;
-
- // Get the ID of the test for command line specification
- virtual const char *GetDescription() = 0;
-
- // Tack on options to the command line parser
- virtual void ConfigureCommandLineParser(CommandLineArgumentParser &parser) = 0;
-
- // Pass command line parameters to the test
- virtual void SetCommandLineParameters(
- const CommandLineArgumentParseResult ¶meters)
- {
- m_Command = parameters;
- }
-
- // Create a new test with given command line parameters. The test
- // either runs succesfully and returns, or throws an exception
- // describing what happened.
- virtual void Run() = 0;
-
- // Destructor
- virtual ~TestBase() {}
-
-protected:
-
- // The command line parse result, i.e., parameters of the test
- CommandLineArgumentParseResult m_Command;
-};
-
-template <class TPixel>
-class TestBaseOneImage : public TestBase
-{
-public:
- // Type definitions
- typedef itk::OrientedImage<TPixel,3> ImageType;
- typedef typename ImageType::Pointer ImagePointer;
-
- // Print how to use this test
- virtual void PrintUsage()
- {
- std::cerr << " image FILE : Pass image name " << std::endl;
- }
-
- // Tack on options to the command line parser
- virtual void ConfigureCommandLineParser(CommandLineArgumentParser &parser)
- {
- parser.AddOption("image",1);
- }
-
- // Run method, loads the passed in message
- virtual void Run()
- {
- // Check if the image option is present
- if(!m_Command.IsOptionPresent("image"))
- throw new TestUsageException();
-
- // Load the image - will throw an exception if something fails
- LoadImageFromFile(m_Command.GetOptionParameter("image"),m_Image);
- }
-
- // Destructor
- virtual ~TestBaseOneImage() {}
-
-protected:
- // The loaded image
- ImagePointer m_Image;
-};
-
-#endif //__TestBase_h_
diff --git a/Testing/Logic/TestCompareLevelSets.cxx b/Testing/Logic/TestCompareLevelSets.cxx
deleted file mode 100644
index 7546249..0000000
--- a/Testing/Logic/TestCompareLevelSets.cxx
+++ /dev/null
@@ -1,850 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: TestCompareLevelSets.cxx,v $
- Language: C++
- Date: $Date: 2009/11/14 16:19:56 $
- Version: $Revision: 1.5 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-// Borland compiler is very lazy so we need to instantiate the template
-// by hand
-#if defined(__BORLANDC__)
-#include <SNAPBorlandDummyTypes.h>
-#include <time.h>
-#endif
-
-#include "TestCompareLevelSets.h"
-#include "SNAPRegistryIO.h"
-
-#include "IRISApplication.h"
-#include "IRISImageData.h"
-#include "SNAPImageData.h"
-#include "ImageWrapper.h"
-#include "GreyImageWrapper.h"
-#include "SNAPLevelSetFunction.h"
-
-#include "itkDenseFiniteDifferenceImageFilter.h"
-#include "itkImageRegionConstIterator.h"
-#include "itkSparseFieldLevelSetImageFilter.h"
-#include "itkParallelSparseFieldLevelSetImageFilter.h"
-#include "itkNarrowBandLevelSetImageFilter.h"
-#include "itkCommand.h"
-#include "itkUnaryFunctorImageFilter.h"
-
-
-#include <ctime>
-#include <iomanip>
-
-void
-TestCompareLevelSets
-::PrintUsage()
-{
- // Run the parent's part of the test
- Superclass::PrintUsage();
-
- // RAI may be passed to this test
- std::cout << " config FILE : Set configuration registry file (required)" << std::endl;
- std::cout << " generate : Instead of running the test, generate a sample config" << std::endl;
-}
-
-template <class TFilter>
-class LevelSetExtensionFilter : public TFilter
-{
-public:
-
- /** Standard class typedefs. */
- typedef LevelSetExtensionFilter<TFilter> Self;
- typedef TFilter Superclass;
- typedef itk::SmartPointer<Self> Pointer;
- typedef itk::SmartPointer<const Self> ConstPointer;
-
- /** Run-time type information. */
- itkTypeMacro(LevelSetExtensionFilter,TFilter);
-
- /** Capture information from the superclass. */
- typedef typename Superclass::InputImageType InputImageType;
- typedef typename Superclass::OutputImageType OutputImageType;
- // typedef typename Superclass::UpdateBufferType UpdateBufferType;
-
- /** Dimensionality of input and output data is assumed to be the same.
- * It is inherited from the superclass. */
- itkStaticConstMacro(ImageDimension, unsigned int,Superclass::ImageDimension);
-
- itkNewMacro(LevelSetExtensionFilter);
-
- /** The pixel type of the output image will be used in computations.
- * Inherited from the superclass. */
- typedef typename Superclass::PixelType PixelType;
- typedef typename Superclass::TimeStepType TimeStepType;
-
- /** Set/Get the number of iterations that the filter will run. */
- itkSetMacro(NumberOfIterations, unsigned int);
- itkGetConstReferenceMacro(NumberOfIterations, unsigned int);
-
-protected:
- LevelSetExtensionFilter()
- {
- m_NumberOfIterations = 10;
- }
-
- ~LevelSetExtensionFilter() {}
-
- void PrintSelf(std::ostream& os, itk::Indent indent) const
- {
- Superclass::PrintSelf(os,indent);
- }
-
- /** Supplies the halting criteria for this class of filters. The
- * algorithm will stop after a user-specified number of iterations. */
- virtual bool Halt()
- {
- if (this->GetElapsedIterations() >= m_NumberOfIterations) return true;
- else return false;
- }
-
-private:
- LevelSetExtensionFilter(const Self&); //purposely not implemented
- void operator=(const Self&); //purposely not implemented
-
- unsigned int m_NumberOfIterations;
-};
-
-#ifdef _USE_FastLevelSetFunction_
-class FastSNAPLevelSetFunction :
- public SNAPLevelSetFunction<itk::Image<float, 3> >
-{
-public:
-
- /** Standard class typedefs. */
- typedef FastSNAPLevelSetFunction Self;
- typedef itk::Image<float,3> ImageType;
- typedef itk::SegmentationLevelSetFunction<ImageType> Superclass;
- typedef itk::SmartPointer<Self> Pointer;
- typedef itk::SmartPointer<const Self> ConstPointer;
-
- /** Method for creation through the object factory. */
- itkNewMacro(FastSNAPLevelSetFunction);
-
- /** Run-time type information (and related methods) */
- itkTypeMacro( SNAPLevelSetFunction, itk::LevelSetFunction );
-
- /** Extract some parameters from the superclass. */
- typedef Superclass::ImageType ImageType;
- typedef ImageType::Pointer ImagePointer;
- typedef Superclass::NeighborhoodType NeighborhoodType;
- typedef Superclass::ScalarValueType ScalarValueType;
- typedef Superclass::RadiusType RadiusType;
- typedef Superclass::FloatOffsetType FloatOffsetType;
-
- typedef Superclass::VectorType VectorType;
- typedef itk::Image<VectorType,ImageDimension> VectorImageType;
- typedef VectorImageType::Pointer VectorImagePointer;
-
- typedef Superclass::TimeStepType TimeStepType;
- typedef Superclass::GlobalDataStruct GlobalDataStruct;
-
- /** Our own initialize method: computes all offsets */
- virtual void Initialize(const RadiusType &r);
-
- /** Our own compute update method: fast and furiuos */
- virtual float ComputeUpdate(const NeighborhoodType &it, void *globalData,
- const FloatOffsetType& offset);
-
- /** Image offsets for fast neighborhood operations */
- int m_OffsetFX;
- int m_OffsetFY;
- int m_OffsetFZ;
- int m_OffsetBX;
- int m_OffsetBY;
- int m_OffsetBZ;
- int m_OffsetFXFY;
- int m_OffsetFYFZ;
- int m_OffsetFZFX;
- int m_OffsetBXFY;
- int m_OffsetBYFZ;
- int m_OffsetBZFX;
- int m_OffsetFXBY;
- int m_OffsetFYBZ;
- int m_OffsetFZBX;
- int m_OffsetBXBY;
- int m_OffsetBYBZ;
- int m_OffsetBZBX;
-};
-
-void
-FastSNAPLevelSetFunction
-::Initialize(const RadiusType &r)
-{
- // Let the parent do his work
- Superclass::Initialize(r);
-
- // Compute all the offsets
- m_OffsetFX = m_xStride[0];
- m_OffsetFY = m_xStride[1];
- m_OffsetFZ = m_xStride[2];
-
- m_OffsetBX = -m_OffsetFX;
- m_OffsetBY = -m_OffsetFY;
- m_OffsetBZ = -m_OffsetFZ;
-
- m_OffsetFXFY = m_OffsetFX + m_OffsetFY;
- m_OffsetFYFZ = m_OffsetFY + m_OffsetFZ;
- m_OffsetFZFX = m_OffsetFZ + m_OffsetFX;
-
- m_OffsetBXFY = m_OffsetBX + m_OffsetFY;
- m_OffsetBYFZ = m_OffsetBY + m_OffsetFZ;
- m_OffsetBZFX = m_OffsetBZ + m_OffsetFX;
-
- m_OffsetFXBY = m_OffsetFX + m_OffsetBY;
- m_OffsetFYBZ = m_OffsetFY + m_OffsetBZ;
- m_OffsetFZBX = m_OffsetFZ + m_OffsetBX;
-
- m_OffsetBXBY = m_OffsetBX + m_OffsetBY;
- m_OffsetBYBZ = m_OffsetBY + m_OffsetBZ;
- m_OffsetBZBX = m_OffsetBZ + m_OffsetBX;
-}
-
-float
-FastSNAPLevelSetFunction
-::ComputeUpdate(const NeighborhoodType &it,
- void *globalData,const FloatOffsetType& offset)
-{
- // Get the central pixel location
- const float *pixel = it.GetCenterPointer();
-
- // Global data structure
- GlobalDataStruct *gd = (GlobalDataStruct *)globalData;
-
- // Get the neighboring pixel values
- float v = *(pixel);
- float fx = *(pixel + m_OffsetFX);
- float bx = *(pixel + m_OffsetBX);
- float fy = *(pixel + m_OffsetFY);
- float by = *(pixel + m_OffsetBY);
- float fz = *(pixel + m_OffsetFZ);
- float bz = *(pixel + m_OffsetBZ);
-
- // Compute the second derivatives
- float vv = v + v;
- float uxx = fx + bx - vv;
- float uyy = fy + by - vv;
- float uzz = fz + bz - vv;
-
- // Forward and backward differences
- float uxf = fx - v;
- float uyf = fy - v;
- float uzf = fz - v;
- float uxb = v - bx;
- float uyb = v - by;
- float uzb = v - bz;
-
- // Compute the central first derivatives
- float uxc = 0.5f * (fx - bx);
- float uyc = 0.5f * (fy - by);
- float uzc = 0.5f * (fz - bz);
-
- // Compute the squared central first derivatives
- float uxc2 = uxc*uxc;
- float uyc2 = uyc*uyc;
- float uzc2 = uzc*uzc;
-
- // Compute the Hessian matrix and various other derivatives. Some of these
- // derivatives may be used by overloaded virtual functions.
- gd->m_GradMagSqr = 1.0e-6 + uxc2 + uyc2 + uzc2;
-
- // Compute the curvature term
- float curvature_term = 0.0f;
- if(m_CurvatureWeight != 0.0f)
- {
- // More terms to compute
- float fxfy = *(pixel + m_OffsetFXFY);
- float fyfz = *(pixel + m_OffsetFYFZ);
- float fzfx = *(pixel + m_OffsetFZFX);
-
- float bxfy = *(pixel + m_OffsetBXFY);
- float byfz = *(pixel + m_OffsetBYFZ);
- float bzfx = *(pixel + m_OffsetBZFX);
-
- float fxby = *(pixel + m_OffsetFXBY);
- float fybz = *(pixel + m_OffsetFYBZ);
- float fzbx = *(pixel + m_OffsetFZBX);
-
- float bxby = *(pixel + m_OffsetBXBY);
- float bybz = *(pixel + m_OffsetBYBZ);
- float bzbx = *(pixel + m_OffsetBZBX);
-
- // Compute the mixed derivatives
- float uxy = 0.25f * (fxfy + bxby - bxfy - fxby);
- float uyz = 0.25f * (fyfz + bybz - byfz - fybz);
- float uzx = 0.25f * (fzfx + bzbx - bzfx - fzbx);
-
- curvature_term = m_CurvatureWeight *
- ((uxc2+uyc2)*uzz + (uyc2+uzc2)*uxx + (uzc2+uxc2)*uyy
- - 2*( uxy*uxc*uyc + uyz*uyc*uzc + uzx*uzc*uxc ))
- / gd->m_GradMagSqr;
-
- curvature_term *= this->CurvatureSpeed(it, offset);
- }
-
- // Compute the advection term
- float advection_term = 0.0f;
- if (m_AdvectionWeight != 0.0f)
- {
- VectorType advection_field = this->AdvectionField(it, offset, gd);
- float ax = advection_field[0] * m_AdvectionWeight;
- float ay = advection_field[1] * m_AdvectionWeight;
- float az = advection_field[2] * m_AdvectionWeight;
-
- advection_term =
- ((ax > 0) ? uxb * ax : uxf * ax) +
- ((ay > 0) ? uyb * ay : uyf * ay) +
- ((az > 0) ? uzb * az : uzf * az);
-
- // Compute the maximal advection change
- float maxaxay = (ax > ay) ? ax : ay;
- float maxazam = (az > gd->m_MaxAdvectionChange) ? az : gd->m_MaxAdvectionChange;
- gd->m_MaxAdvectionChange = (maxaxay > maxazam) ? maxaxay : maxazam;
- }
-
- float propagation_term = 0.0f;
- if (m_PropagationWeight != 0.0f)
- {
- // Get the propagation speed
- propagation_term = m_PropagationWeight * this->PropagationSpeed(it, offset, gd);
-
- float propagation_gradient = (propagation_term > 0) ?
- vnl_math_sqr( vnl_math_max(uxb, 0.0f) ) + vnl_math_sqr( vnl_math_min(uxf, 0.0f) ) +
- vnl_math_sqr( vnl_math_max(uyb, 0.0f) ) + vnl_math_sqr( vnl_math_min(uyf, 0.0f) ) +
- vnl_math_sqr( vnl_math_max(uzb, 0.0f) ) + vnl_math_sqr( vnl_math_min(uzf, 0.0f) )
- :
- vnl_math_sqr( vnl_math_min(uxb, 0.0f) ) + vnl_math_sqr( vnl_math_max(uxf, 0.0f) ) +
- vnl_math_sqr( vnl_math_min(uyb, 0.0f) ) + vnl_math_sqr( vnl_math_max(uyf, 0.0f) ) +
- vnl_math_sqr( vnl_math_min(uzb, 0.0f) ) + vnl_math_sqr( vnl_math_max(uzf, 0.0f) );
-
- // Collect energy change from propagation term. This will be used in
- // calculating the maximum time step that can be taken for this iteration.
- gd->m_MaxPropagationChange =
- vnl_math_max(gd->m_MaxPropagationChange,vnl_math_abs(propagation_term));
-
- // Scale the propagation term by gradient magnitude
- propagation_term *= vcl_sqrt( propagation_gradient );
- }
-
- float laplacian_term = 0.0f;
- if(m_LaplacianSmoothingWeight != 0.0f)
- {
- laplacian_term = (uxx + uyy + uzz)
- * m_LaplacianSmoothingWeight * LaplacianSmoothingSpeed(it,offset, gd);
- }
-
- // Return the combination of all the terms.
- return curvature_term - propagation_term - advection_term - laplacian_term;
-}
-
-#endif // _USE_FastLevelSetFunction_
-
-/** Used to report on each iteration */
-class IterationReporter {
-public:
- typedef itk::Image<float,3> ImageType;
- typedef itk::ImageRegionConstIterator<ImageType> IteratorType;
- typedef itk::ImageToImageFilter<ImageType,ImageType> FilterType;
-
- IterationReporter(FilterType *source,std::ofstream &fout)
- : m_Iteration(0), m_Out(fout), m_Source(source)
- {
- m_Command = CommandType::New();
- m_Command->SetCallbackFunction(this,&IterationReporter::Callback);
- source->AddObserver(itk::IterationEvent(),m_Command);
- m_LastClock = clock();
- }
-
- void Callback(
- itk::Object *irisNotUsed(object),
- const itk::EventObject &irisNotUsed(event))
- {
- // Record the time
- clock_t currentClock = clock();
- double delta = (currentClock - m_LastClock);
- m_Iteration++;
-
- // Write data
- m_Out << m_Iteration << "\t" << delta <<
- /* "\t" << CountInterfaceVoxels() << */ std::endl;
- }
-
- unsigned long CountInterfaceVoxels()
- {
- ImageType *out = m_Source->GetOutput();
- IteratorType it(out,out->GetBufferedRegion());
- unsigned long count = 0;
- while(!it.IsAtEnd())
- {
- if(it.Value() > -1.0f && it.Value() < 1.0f)
- count++;
- ++it;
- }
- return count;
- }
-
-private:
- int m_Iteration;
- clock_t m_LastClock;
- std::ofstream &m_Out;
-
- typedef itk::MemberCommand<IterationReporter> CommandType;
- CommandType::Pointer m_Command;
- FilterType::Pointer m_Source;
-};
-
-float
-TestCompareLevelSets
-::ComputeOverlapDistance(FloatImageType *i1,FloatImageType *i2)
-{
- typedef itk::ImageRegionConstIterator<FloatImageType> IteratorType;
-
- IteratorType it1(i1,i1->GetBufferedRegion());
- IteratorType it2(i2,i2->GetBufferedRegion());
-
- unsigned long nEither = 0;
- unsigned long nBoth = 0;
-
- while(!it1.IsAtEnd())
- {
- float v1 = it1.Value();
- float v2 = it2.Value();
-
- if(v1 <= 0 || v2 <= 0) nEither++;
- if(v1 <= 0 && v2 <= 0) nBoth++;
-
- ++it1;
- ++it2;
- }
-
- return nBoth * 1.0f / nEither;
-}
-
-void
-TestCompareLevelSets
-::RunExperiment()
-{
- // We will use a registry to save orload configuration parameters
- Registry registry;
- try
- {
- registry.ReadFromFile(m_Command.GetOptionParameter("config"));
- }
- catch(Registry::IOException &exc)
- {
- std::cerr << "Error: " << exc << std::endl;
- std::cerr << "Hint: Try generating a config file using 'generate' option!" << std::endl;
- throw new TestUsageException;
- }
-
- // Get the experiment ID
- string expID = registry["ExperimentId"]["000"];
-
- // Initialize an IRIS application
- IRISApplication *app = new IRISApplication();
-
- // Load the grey image and segmentation image
- app->LoadMainImage(registry["Image.Grey"][""], IRISApplication::MAIN_SCALAR);
- app->LoadLabelImageFile(registry["Image.Bubble"][""]);
-
- // Some image pointers
- SpeedImageWrapper::ImagePointer speed;
-
- // Get the preprocessing image from the registry
- LoadImageFromFile(registry["Image.Speed"][""],speed);
-
- // Specify the label used for segmentation
- app->GetGlobalState()->SetDrawingColorLabel(
- (LabelType)registry["Image.BubbleLabel"][255]);
-
- // Create an ROI object to specify the region of interest to select
- SNAPSegmentationROISettings roiSettings;
-
- // Use the entire region and no resampling of the region
- roiSettings.SetROI(app->GetCurrentImageData()->GetImageRegion());
- roiSettings.SetResampleFlag(false);
-
- // Start SNAP logic with the selected region settings
- app->InitializeSNAPImageData(roiSettings);
-
- // Get a pointer to the SNAP image data
- SNAPImageData *snap = app->GetSNAPImageData();
-
- // Get the snake mode
- bool edgeMode = registry["Image.SpeedIsEdge"][false];
-
- // A registry IO object to read/write SNAP properties
- SNAPRegistryIO regio;
-
- // Pass in the speed image
- // Preprocess the image and initialize the level set image
- SnakeParameters parameters;
- if(edgeMode)
- {
- app->UpdateSNAPSpeedImage(speed,EDGE_SNAKE);
- parameters = regio.ReadSnakeParameters(
- registry.Folder("SnakeParameters.Edge"),
- SnakeParameters::GetDefaultEdgeParameters());
- }
- else
- {
- app->UpdateSNAPSpeedImage(speed,IN_OUT_SNAKE);
- parameters = regio.ReadSnakeParameters(
- registry.Folder("SnakeParameters.InOut"),
- SnakeParameters::GetDefaultInOutParameters());
- }
-
- // Done with the initialization. Open an output dump file
- string targetPath = registry["OutputPath"]["."] + string("/");
- std::ofstream fout((targetPath + "report." + expID + ".txt").c_str());
-
- // Now, we have a speed image and a level set image. We are ready to
- // test different segmenter
- typedef SpeedImageWrapper::ImageType FloatImageType;
- typedef itk::DenseFiniteDifferenceImageFilter<
- FloatImageType,FloatImageType> DenseFilterType;
- typedef itk::SparseFieldLevelSetImageFilter<
- FloatImageType,FloatImageType> SparseFilterType;
- typedef itk::ParallelSparseFieldLevelSetImageFilter<
- FloatImageType,FloatImageType> ParallelSparseFilterType;
- typedef itk::NarrowBandLevelSetImageFilter<
- FloatImageType,FloatImageType> NarrowFilterType;
-
- typedef LevelSetExtensionFilter<DenseFilterType> DenseExtensionFilter;
- typedef LevelSetExtensionFilter<SparseFilterType> SparseExtensionFilter;
- typedef LevelSetExtensionFilter<ParallelSparseFilterType> ParallelExtensionFilter;
- typedef LevelSetExtensionFilter<NarrowFilterType> NarrowExtensionFilter;
-
- // Pull out the finite difference function
- std::vector<Bubble> dummy;
- snap->InitializeSegmentation(
- parameters,dummy,app->GetGlobalState()->GetDrawingColorLabel());
- SNAPLevelSetFunction<FloatImageType> *phi = snap->GetLevelSetFunction();
-
- SNAPLevelSetDriver(
- snap->GetSnake()->GetImage(),
- snap->GetSpeed()->GetImage(),
-
-
-
- // Decide on a number of iterations
- unsigned int nIterations = registry["Iterations"][10];
-
- // Set up the dense filter
- DenseExtensionFilter::Pointer fltDense = DenseExtensionFilter::New();
- fltDense->SetInput(snap->GetSnake()->GetImage());
- fltDense->SetDifferenceFunction(phi);
- fltDense->SetNumberOfIterations(nIterations);
-
- // Set up the sparse fileter
- SparseExtensionFilter::Pointer fltSparse = SparseExtensionFilter::New();
- fltSparse->SetInput(snap->GetSnake()->GetImage());
- fltSparse->SetDifferenceFunction(phi);
- fltSparse->SetNumberOfIterations(nIterations);
- fltSparse->SetNumberOfLayers(3);
- fltSparse->SetIsoSurfaceValue(0.0f);
-
- ParallelExtensionFilter::Pointer fltParallel = ParallelExtensionFilter::New();
- fltParallel->SetInput(snap->GetSnake()->GetImage());
- fltParallel->SetDifferenceFunction(phi);
- fltParallel->SetNumberOfIterations(nIterations);
- fltParallel->SetNumberOfLayers(3);
- fltParallel->SetIsoSurfaceValue(0.0f);
-
- // Create iteration reporters
- IterationReporter irDense(fltDense.GetPointer(),fout);
- IterationReporter irSparse(fltSparse.GetPointer(),fout);
- IterationReporter irParallel(fltParallel.GetPointer(),fout);
-
- // Create a filter for filling in the interior of a distance transform
- typedef itk::UnaryFunctorImageFilter<
- FloatImageType,LabelImageWrapper::ImageType,InteriorFunctor> InteriorFilter;
- InteriorFilter::Pointer fltInterior = InteriorFilter::New();
-
- // Run and save the three filters
- fout << "Running the Narrow filter!" << std::endl;
- fltNarrow->Update();
-
- fout << "Running the Sparse filter!" << std::endl;
- fltSparse->Update();
-
- fout << "Running the Parallel filter!" << std::endl;
- fltParallel->Update();
-
- // fout << "Running the Dense filter!" << std::endl;
- // fltDense->Update();
-
- // Compute the difference between filter outputs
- fout << "Narrow-Sparse\t" <<
- ComputeOverlapDistance(fltNarrow->GetOutput(),fltSparse->GetOutput()) << std::endl;
- fout << "Narrow-Dense\t" <<
- ComputeOverlapDistance(fltNarrow->GetOutput(),fltDense->GetOutput()) << std::endl;
- fout << "Narrow-Parallel\t" <<
- ComputeOverlapDistance(fltNarrow->GetOutput(),fltParallel->GetOutput()) << std::endl;
- fout << "Sparse-Dense\t" <<
- ComputeOverlapDistance(fltSparse->GetOutput(),fltDense->GetOutput()) << std::endl;
- fout << "Sparse-Parallel\t" <<
- ComputeOverlapDistance(fltSparse->GetOutput(),fltParallel->GetOutput()) << std::endl;
- fout << "Dense-Parallel\t" <<
- ComputeOverlapDistance(fltDense->GetOutput(),fltParallel->GetOutput()) << std::endl;
-
- // Write each result to a file
- fltInterior->SetInput(fltNarrow->GetOutput());
- string file = targetPath + "CompareLevelSets." + expID + ".narrow.gipl";
- SaveImageToFile(file.c_str(),fltInterior->GetOutput());
-
- fltInterior->SetInput(fltDense->GetOutput());
- file = targetPath + "CompareLevelSets." + expID + ".dense.gipl";
- SaveImageToFile(file.c_str(),fltInterior->GetOutput());
-
- fltInterior->SetInput(fltSparse->GetOutput());
- file = targetPath + "CompareLevelSets." + expID + ".sparse.gipl";
- SaveImageToFile(file.c_str(),fltInterior->GetOutput());
-
- fltInterior->SetInput(fltParallel->GetOutput());
- file = targetPath + "CompareLevelSets." + expID + ".parallel.gipl";
- SaveImageToFile(file.c_str(),fltInterior->GetOutput());
-
- // Close the output
- fout.close();
-}
-
-void
-TestCompareLevelSets
-::Run()
-{
- RunExperiment();
- return;
-
- // Run the parent's part of the test (loads image)
- Superclass::Run();
-
- // We need to wrap the image for some operations
- GreyImageWrapper wrapper;
- wrapper.SetImage(m_Image);
-
- // We need some default parameters for the test
- SnakeParameters parmSnakeEdge =
- SnakeParameters::GetDefaultEdgeParameters();
-
- SnakeParameters parmSnakeInOut =
- SnakeParameters::GetDefaultInOutParameters();
-
- EdgePreprocessingSettings parmPreprocessEdge =
- EdgePreprocessingSettings::MakeDefaultSettings();
-
- ThresholdSettings parmPreprocessInOut =
- ThresholdSettings::MakeDefaultSettings(&wrapper);
-
- // Some other defaults: which snake to use
- bool parmUseEdgeSnake = true;
-
- // Bubble default, based on the image
- Vector3d parmBubbleCenter = to_double(wrapper.GetSize()) / 2.0;
- double parmBubbleRadius = 2.0;
-
- // Get the configuration file
- if(!m_Command.IsOptionPresent("config"))
- throw new TestUsageException;
-
- // We will use a registry to save orload configuration parameters
- Registry registry;
-
- // A registry IO object to read/write SNAP properties
- SNAPRegistryIO regio;
-
- // Check if the user wants to generate a config file
- if(m_Command.IsOptionPresent("generate"))
- {
- // Store the options in the registry
- regio.WriteSnakeParameters(
- parmSnakeEdge,registry.Folder("SnakeParameters.Edge"));
-
- regio.WriteSnakeParameters(
- parmSnakeInOut,registry.Folder("SnakeParameters.InOut"));
-
- regio.WriteEdgePreprocessingSettings(
- parmPreprocessEdge,registry.Folder("Preprocessing.Edge"));
-
- regio.WriteThresholdSettings(
- parmPreprocessInOut,registry.Folder("Preprocessing.InOut"));
-
- // Store the default position (center of image, kinda-dumb)
- registry["Seed.Position"] << parmBubbleCenter;
- registry["Seed.Radius"] << parmBubbleRadius;
-
- // Store the snake mode to use
- registry["SnakeMode"] << parmUseEdgeSnake;
-
- // Write the registry
- registry.WriteToFile(m_Command.GetOptionParameter("config"));
- return;
- }
-
- // Try loading the registry
- try
- {
- registry.ReadFromFile(m_Command.GetOptionParameter("config"));
- }
- catch(Registry::IOException &exc)
- {
- std::cerr << "Error: " << exc << std::endl;
- std::cerr << "Hint: Try generating a config file using 'generate' option!" << std::endl;
- throw new TestUsageException;
- }
-
- // Read snake parameters from the registry
- parmSnakeEdge = regio.ReadSnakeParameters(
- registry.Folder("SnakeParameters.Edge"),parmSnakeEdge);
-
- parmSnakeInOut = regio.ReadSnakeParameters(
- registry.Folder("SnakeParameters.InOut"),parmSnakeInOut);
-
- // Read preprocessing settings from the registry
- parmPreprocessEdge = regio.ReadEdgePreprocessingSettings(
- registry.Folder("Preprocessing.Edge"),parmPreprocessEdge);
-
- parmPreprocessInOut = regio.ReadThresholdSettings(
- registry.Folder("Preprocessing.InOut"),parmPreprocessInOut);
-
- // Read the seed position
- parmBubbleCenter = registry["Seed.Position"][parmBubbleCenter];
- parmBubbleRadius = registry["Seed.Radius"][parmBubbleRadius];
-
- // Read the snake mode to use
- parmUseEdgeSnake = registry["SnakeMode"][parmUseEdgeSnake];
-
- /** Now we have some configuration parameters and an image to work with.
- Let's get to the gritty of it! */
-
- // Initialize an IRIS application
- IRISApplication *app = new IRISApplication();
-
- // Pass the image to the application
- app->UpdateIRISGreyImage(m_Image, GreyTypeToNativeFunctor());
-
- // Start SNAP logic with the entire region
- SNAPSegmentationROISettings roiSettings;
- roiSettings.SetROI(app->GetCurrentImageData()->GetImageRegion());
- roiSettings.SetResampleFlag(false);
- app->InitializeSNAPImageData(roiSettings);
-
- // Get a pointer to the SNAP image data
- SNAPImageData *snap = app->GetSNAPImageData();
-
- // Create an initialization bubble
- Bubble bubble;
- bubble.center = to_int(parmBubbleCenter);
- bubble.radius = (int) parmBubbleRadius;
- std::vector<Bubble> bubbleList;
- bubbleList.push_back(bubble);
-
- // Preprocess the image and initialize the level set image
- if(parmUseEdgeSnake)
- {
- snap->DoEdgePreprocessing(parmPreprocessEdge);
- snap->InitializeSegmentation(parmSnakeEdge,bubbleList,255);
- }
- else
- {
- snap->DoInOutPreprocessing(parmPreprocessInOut);
- snap->InitializeSegmentation(parmSnakeEdge,bubbleList,255);
- }
-
- // Now, we have a speed image and a level set image. We are ready to
- // test different segmenters
- typedef SpeedImageWrapper::ImageType FloatImageType;
- typedef itk::DenseFiniteDifferenceImageFilter<
- FloatImageType,FloatImageType> DenseFilterType;
- typedef itk::SparseFieldLevelSetImageFilter<
- FloatImageType,FloatImageType> SparseFilterType;
- typedef itk::NarrowBandLevelSetImageFilter<
- FloatImageType,FloatImageType> NarrowFilterType;
-
- typedef LevelSetExtensionFilter<DenseFilterType> DenseExtensionFilter;
- typedef LevelSetExtensionFilter<SparseFilterType> SparseExtensionFilter;
- typedef LevelSetExtensionFilter<NarrowFilterType> NarrowExtensionFilter;
-
- // Pull out the finite difference function
- SNAPLevelSetFunction<FloatImageType> *phi = snap->GetLevelSetFunction();
-
- // Decide on a number of iterations
- unsigned int nIterations = 10;
-
- // Create a callback object
- typedef itk::MemberCommand<TestCompareLevelSets> CommandType;
- CommandType::Pointer callback = CommandType::New();
- callback->SetCallbackFunction(this,&TestCompareLevelSets::IterationCallback);
-
- // Set up the dense filter
- DenseExtensionFilter::Pointer fltDense = DenseExtensionFilter::New();
- fltDense->SetInput(snap->GetSnake()->GetImage());
- fltDense->SetDifferenceFunction(phi);
- fltDense->SetNumberOfIterations(nIterations);
- fltDense->AddObserver(itk::IterationEvent(),callback);
-
- // Set up the sparse fileter
- SparseExtensionFilter::Pointer fltSparse = SparseExtensionFilter::New();
- fltSparse->SetInput(snap->GetSnake()->GetImage());
- fltSparse->SetDifferenceFunction(phi);
- fltSparse->SetNumberOfIterations(nIterations);
- fltSparse->SetNumberOfLayers(3);
- fltSparse->SetIsoSurfaceValue(0.0f);
- fltSparse->AddObserver(itk::IterationEvent(),callback);
-
- // Set up the narrow band filter
- NarrowExtensionFilter::Pointer fltNarrow = NarrowExtensionFilter::New();
- fltNarrow->SetSegmentationFunction(phi);
- fltNarrow->SetInput(snap->GetSnake()->GetImage());
- fltNarrow->SetFeatureImage(snap->GetSpeed()->GetImage());
- fltNarrow->SetNumberOfIterations(nIterations);
- fltNarrow->AddObserver(itk::IterationEvent(),callback);
-
- // Create a filter for filling in the interior of a distance transform
- typedef itk::UnaryFunctorImageFilter<
- FloatImageType,LabelImageWrapper::ImageType,InteriorFunctor> InteriorFilter;
- InteriorFilter::Pointer fltInterior = InteriorFilter::New();
-
- // Run and save the narrow filter
- std::cout << "Running the Narrow filter!" << std::endl;
- //fltInterior->SetInput(fltNarrow->GetOutput());
- //fltInterior->Update();
- //SaveImageToFile("CompareLevelSets.narrow.gipl",fltInterior->GetOutput());
-
- // Run and save the dense filter
- std::cout << "Running the Dense filter!" << std::endl;
- //fltInterior->SetInput(fltDense->GetOutput());
- //fltInterior->Update();
- //SaveImageToFile("CompareLevelSets.dense.gipl",fltInterior->GetOutput());
-
- // Run and save the sparse filter
- std::cout << "Running the Sparse filter!" << std::endl;
- fltInterior->SetInput(fltSparse->GetOutput());
- fltInterior->Update();
- SaveImageToFile("CompareLevelSets.sparse.gipl",fltInterior->GetOutput());
-}
-
-void
-TestCompareLevelSets
-::IterationCallback(itk::Object *object, const itk::EventObject &irisNotUsed(event))
-{
- static clock_t lastTime = clock();
- clock_t currentTime = clock();
- double delta = (double)(currentTime - lastTime) / CLOCKS_PER_SEC;
- double progress =
- reinterpret_cast<itk::ProcessObject *>(object)->GetProgress();
-
- std::cout << std::setw(20) << progress << std::setw(20) << delta * 1000 << std::endl;
-}
-
-
diff --git a/Testing/Logic/TestCompareLevelSets.h b/Testing/Logic/TestCompareLevelSets.h
deleted file mode 100644
index 0766827..0000000
--- a/Testing/Logic/TestCompareLevelSets.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: TestCompareLevelSets.h,v $
- Language: C++
- Date: $Date: 2006/12/02 04:22:20 $
- Version: $Revision: 1.1 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-#ifndef __TestCompareLevelSets_h_
-#define __TestCompareLevelSets_h_
-
-#include "SNAPCommon.h"
-#include "TestBase.h"
-
-/**
- * This class is used to test the functionality in the ImageWrapper class
- */
-class TestCompareLevelSets : public TestBaseOneImage<GreyType>
-{
-public:
- typedef TestBaseOneImage<GreyType> Superclass;
-
- void PrintUsage();
- void Run();
- void RunExperiment();
-
- const char *GetTestName()
- {
- return "CompareLevelSets";
- }
-
- const char *GetDescription()
- {
- return "Compare level set methods";
- }
-
- virtual void ConfigureCommandLineParser(CommandLineArgumentParser &parser)
- {
- Superclass::ConfigureCommandLineParser(parser);
- parser.AddOption("config",1);
- parser.AddOption("generate",0);
- }
-
-private:
- // Progress callback
- void IterationCallback(itk::Object *object, const itk::EventObject &event);
-
- // Interior extraction functor
- class InteriorFunctor {
- public:
- unsigned char operator()(float input) {
- return input <= 0.0f ? 255 : 0;
- }
- };
-
- // Image type
- typedef itk::Image<float,3> FloatImageType;
-
- // Compute volume overlap as (A int B) / (A union B)
- float ComputeOverlapDistance(FloatImageType *i1,FloatImageType *i2);
-};
-
-#endif // __TestCompareLevelSets_h_
-
-
-
-
diff --git a/Testing/Logic/TestImageWrapper.h b/Testing/Logic/TestImageWrapper.h
deleted file mode 100644
index 9c93882..0000000
--- a/Testing/Logic/TestImageWrapper.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: TestImageWrapper.h,v $
- Language: C++
- Date: $Date: 2009/11/14 16:19:56 $
- Version: $Revision: 1.3 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-#ifndef __TestImageWrapper_h_
-#define __TestImageWrapper_h_
-
-#include "TestBase.h"
-#include "ScalarImageWrapper.h"
-#include "GreyImageWrapper.h"
-#include "LabelImageWrapper.h"
-
-/**
- * This class is used to test the functionality in the ImageWrapper class
- */
-template<class TPixel, class TWrapper>
-class TestImageWrapper : public TestBaseOneImage<TPixel>
-{
-public:
- typedef TestBaseOneImage<TPixel> Superclass;
- typedef TWrapper WrapperType;
-
- void PrintUsage();
- void Run();
-
- const char *GetTestName()
- {
- return "ImageWrapper";
- }
-
- const char *GetDescription()
- {
- return "A suite of ImageWrapper tests";
- }
-};
-
-template<class TPixel, class TWrapper>
-void TestImageWrapper<TPixel, TWrapper>
-::PrintUsage()
-{
- // Run the parent's part of the test
- Superclass::PrintUsage();
-
- // RAI may be passed to this test
- std::cout << " rai CODE : Pass in an RAI anatomy-image code" << std::endl;
-}
-
-template<class TPixel, class TWrapper>
-void TestImageWrapper<TPixel, TWrapper>
-::Run()
-{
- // Run the parent's part of the test (loads image)
- Superclass::Run();
-
- // Do the rest
- std::cout << "Testing code in ImageWrapper.h" << std::endl;
-
- // Create an image wrapper
- WrapperType *wrapper = new WrapperType();
-
- // Insert image into the wrapper
- wrapper->SetImage(this->m_Image);
-
- // Set the cursor position in the slice wrapper
- wrapper->SetSliceIndex(wrapper->GetSize() / ((unsigned int)2));
-
- // Create a transform that specifies the image-slice geometry
- ImageCoordinateTransform ict;
- ict.SetTransform(Vector3i(-2,1,-3),wrapper->GetSize());
-
- // Report min/max intensities
- std::cout << "Max intensity: " << wrapper->GetImageMax() << std::endl;
- std::cout << "Min intensity: " << wrapper->GetImageMin() << std::endl;
-
- // We are finished testing
- std::cout << "Testing complete" << std::endl;
-}
-
-#endif //__TestImageWrapper_h_
-
-
-
-
diff --git a/Testing/Logic/TestMain.cxx b/Testing/Logic/TestMain.cxx
deleted file mode 100644
index cff6250..0000000
--- a/Testing/Logic/TestMain.cxx
+++ /dev/null
@@ -1,25 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: TestMain.cxx,v $
- Language: C++
- Date: $Date: 2006/12/02 04:22:20 $
- Version: $Revision: 1.1 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-// Define the verbose output stream
-#include "SNAPTestDriver.h"
-
-std::ostream &verbose = std::cout;
-
-int main(int argc, char *argv[])
-{
- SNAPTestDriver driver;
- driver.Run(argc,argv);
- return 0;
-}
diff --git a/Testing/Logic/TutorialTest.cxx b/Testing/Logic/TutorialTest.cxx
deleted file mode 100644
index 8660a79..0000000
--- a/Testing/Logic/TutorialTest.cxx
+++ /dev/null
@@ -1,373 +0,0 @@
-/*=========================================================================
-
- Program: Insight Segmentation & Registration Toolkit
- Module: $RCSfile: TutorialTest.cxx,v $
- Language: C++
- Date: $Date: 2006/12/02 04:22:20 $
- Version: $Revision: 1.1 $
- Copyright (c) 2003 Insight Consortium. All rights reserved.
- See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information.
-=========================================================================*/
-// Borland compiler is very lazy so we need to instantiate the template
-// by hand
-#if defined(__BORLANDC__)
-#include <SNAPBorlandDummyTypes.h>
-#endif
-
-#include "ImageIORoutines.h"
-#include "IRISApplication.h"
-#include "IRISImageData.h"
-#include "LevelSetMeshPipeline.h"
-#include "SNAPImageData.h"
-
-#include "vtkPolyData.h"
-#include "vtkPolyDataWriter.h"
-
-#include <vector>
-
-/**
- * This function is designed to serve as a tutorial through the classes
- * that form the logical framework of SNAP. In this test, the student will
- * be able to load images, perform preprocessing, initialize and run segmentation,
- * output slices from the segmentation and relabel the segmentatinon
- *
- * The method should be invoked with two parameters. First is the full path to
- * the file MRIcrop-orig.gipl which contains the test image. Second is the full
- * path to the label file MRIcrop-seg.label. Both of these files can be downloaded
- * from the SNAP website
- */
-int main(int argc, char *argv[])
-{
- if(argc < 3)
- {
- cerr << "Must specify two arguments." << endl;
- return 1;
- }
- /* =======================================================================
- * Section 1. Starting the Application and Loading an Image
- * ====================================================================== */
-
- /* We begin by creating an IRISApplication object. This is the most high
- * level object in the SNAP class hierarchy and it provides access to the
- * remainder of the SNAP logic classes */
- IRISApplication application;
-
- /* Next we will load the test image. In this example it is expected that the
- * image MRIcrop-orig.gipl, which can be downloaded from the SNAP website
- * will be used. The user will pass the full path to this file as the first
- * parameter to the program */
- const char *sImageFileName = argv[1];
-
- try {
- /* The responsibility for loading the image is with the user. First, we
- * create an itk::Image object into which the image will be loaded from
- * disk */
- IRISApplication::GreyImageType::Pointer imgGrey;
-
- /* SNAP provides a helper method LoadImageFromFile for loading images */
- LoadImageFromFile(sImageFileName,imgGrey);
-
- /* Once the image has been loaded, it can be passed on to the IRISApplication.
- * However, SNAP needs to know the orientation of the image, which is
- * expressed as the position of the image's origin {X,Y,Z}={0,0,0} in
- * patient coordinates. This information is passed on in the form of a
- * three letter RAI code. The value "ASR" means that the origin lies in the
- * Anterior-Superior-Right corner of the patient coordinate system, with
- * X axis corresponding to the Anterior-Posterior axis, Y axis corresponding
- * to the Inferior-Superior axis and Z to the Right-Left axis. For the image
- * we are loading, the correct code is "RAI" */
- application.UpdateIRISGreyImage(imgGrey,"RAI");
-
- /* In addition to loading the image, we are going to load the segmentation
- * labels associated with this image. Segmentation labels are stored in the
- * file MRIcrop-seg.label and passed on as an argument to this test program */
- const char *sLabelFileName = argv[2];
- application.GetColorLabelTable()->LoadFromFile(sLabelFileName);
- }
- catch(itk::ExceptionObject &exception)
- {
- // This code is called if LoadImageFromFile fails.
- cout << "Error Loading Image: " << exception;
- return -1;
- }
-
- /* =======================================================================
- * Section 2. Accessing Image Data Using IRISImageData Class
- * ====================================================================== */
-
- /* Now the image has been loaded into IRISApplication. IRISApplication stores
- * its data using two high level classes: IRISImageData and SNAPImageData.
- * IRISImageData holds the original greyscale image that we have loaded as
- * well as the labelling of that image that is constructed throughout the
- * segmentation. SNAPImageData is used for automatic segmentation and contains
- * a subregion of the original greyscale image. At this point SNAPImageData has
- * not yet been allocated. SNAPImageData is a child class of IRISImageData.
- *
- * The method IRISApplication::GetCurrentImageData() returns the IRISImageData
- * object associated with the current state of SNAP. In manual state (current
- * state right now) it returns a pointer to an IRISImageData object. In
- * automatic segmentation state, it returns a pointer to a SNAPImageData
- * object.
- *
- * The IRISImageData and SNAPImageData objects can also be accessed explicitly
- * using the GetIRISImageData() and GetSNAPImageData() pointers.
- *
- * We will begin by examining the IRISImageData class. We can use it to access
- * the underlying image and to examine the segmentation. */
- IRISImageData *irisData = application.GetCurrentImageData();
-
- /* IRISImageData contains two images: the grey level image and a label or
- * segmentation image. Both images are of the same dimensions. The grey image
- * has voxels of type GreyType and the segmentation image of type LabelType */
- cout << "Image Dimensions: " <<
- to_int(irisData->GetVolumeExtents()) << endl; // Ignore the to_int!
-// cout << "Voxel Size : " << irisData->GetVoxelScaleFactor() << endl;
-
- /* IRISImageData also handles segmentation labels. We can inquire about each label.
- * Labels are indexed from 1 to 255 (0 is the clear label). Only some labels are
- * defined as 'valid', i.e., available to the user to edit */
- for(unsigned int iLabel=0; iLabel < MAX_COLOR_LABELS; iLabel++)
- {
- /* Labels are represented by the class ColorLabel. Each label has a color,
- * a transparency level, a visible flag, a 'visible in 3d mesh flag', and a
- * textual description */
- const ColorLabel &label = application.GetColorLabelTable()->GetColorLabel(iLabel);
- cout << "Label Number : " << iLabel << endl;
- cout << " Decription : " << label.GetLabel() << endl;
- cout << " Color : {"
- << (int) label.GetRGB(0) << ","
- << (int) label.GetRGB(1) << ","
- << (int) label.GetRGB(2) << "}" << endl;
- cout << " Opacity : " << label.GetAlpha() << endl;
- }
-
- /* The current drawing label and the current draw-over label are properties of
- * the SNAP application and are accessed using the GlobalState class.
- * GlobalState stores a great number of application-wide settings */
- application.GetGlobalState()->SetDrawingColorLabel(7); // caudate label
- application.GetGlobalState()->SetCoverageMode(PAINT_OVER_ALL);
-
- /* The next class we will learn about is the ImageWrapper class, which is a
- * wrapper around the standard itk::Image class. The ImageWrapper provides
- * several mini-pipelines involving the itk::Image stored within. First
- * let's access the grey images' wrapper in IRISImageData */
- GreyImageWrapper *wrapGrey = irisData->GetGrey();
-
- /* The class GreyImageWrapper is a subclass of the generic templated class
- * ImageWrapper<TPixel> with template argument TPixel=GreyType. We can
- * use ImageWrapper to get to the itk::Image itself */
- GreyImageWrapper::ImagePointer imgGrey = wrapGrey->GetImage();
-
- /* Here are some of the methods available through the ImageWrapper class */
- cout << "Grey Min : " << wrapGrey->GetImageMin() << endl;
- cout << "Grey Max : " << wrapGrey->GetImageMax() << endl;
-
- /* The IRISImageData and ImageWrapper classes keep track of the current
- * position of the SNAP 3D cursor. When an image is loaded, the cursor is
- * positioned in the center of the image, as we can see by querying the
- * ImageWrapper class */
- Vector3ui vCursor = wrapGrey->GetSliceIndex();
- cout << "Cursor Position : " << to_int(vCursor) << endl; // Ignore the to_int!
- cout << "Value at Cursor : "
- << wrapGrey->GetVoxel(vCursor) << endl;
-
- /* The cursor position obtained above is given in image coordinates, where
- * Z is the slice number, Y is the row number and X is the column number.
- * In order to specify slice coordinates in patient (anatomical)
- * coordinates, we need to know the correct transformation, which we had \
- * specified using an 'RAI' code when loading the grey image. The
- * transformation can be accessed through the IRISApplication object */
- ImageCoordinateGeometry geometry = irisData->GetImageGeometry();
- Vector3ui vCursorPatient =
- geometry.GetImageToAnatomyTransform().TransformVoxelIndex(vCursor);
- cout << "Anatomical Posn.: " << to_int(vCursorPatient) << endl;
-
- /* The cursor position is the same for the grey image wrapper and the label
- * image wrapper. Hence, to set the cursor position, we must call the
- * appropriate method in IRISImageData. We can also call the method
- * IRISApplication::SetCursorPosition for the same effect. */
- vCursor -= Vector3ui(1,0,0);
- irisData->SetCrosshairs(vCursor);
-
- /* ImageWrapper provides a way to extract orthogonal 2D slices form the
- * image. A slice is extracted by setting the crosshair position and calling
- * the GetDisplaySlice method. This returns a 2D image of type unsigned char
- * that can be displayed on the screen.
- *
- * The mapping between the intensities in the grey image (which are shorts)
- * and intensities in the slice (which are bytes) can be changed by calling
- * the method SetIntensityMapFunction() */
- GreyImageWrapper::DisplaySlicePointer imgSlice = wrapGrey->GetDisplaySlice(0);
-
- /* So far, we have looked at the grey image wrapper. The segmentation image
- * wrapper is very similar, except that it produces slices that are RGB
- * images.
- *
- * When we loaded the grey image, the segmentation image in IRISImageData was
- * automatically initialized to the same size and filled with voxels of
- * label 0, which is the 'Clear' label in SNAP. To update the segmentation,
- * we can directly modify the image wrapped by the LabelImageWrapper or
- * we can use the GetVoxelForUpdate() method: */
- irisData->GetSegmentation()->GetVoxelForUpdate(vCursor) = 2;
- LabelImageWrapper::DisplaySlicePointer imgRGBSlice =
- irisData->GetSegmentation()->GetDisplaySlice(0);
-
- /* For validation purposes, we are going to write to disk the grey and
- * segmentation slices that we've extracted in this Section */
- SaveImageToFile("greyslice01.png",imgSlice.GetPointer());
- SaveImageToFile("rgbslice01.png",imgRGBSlice.GetPointer());
-
- /* =======================================================================
- * Section 3. Automatic Segmentation Mode
- * ====================================================================== */
-
- /* To begin automatic segmentation, we need to switch on the automatic
- * segmentation mode. In order to do that, we must specify the region of
- * interest that we want to pass on to this mode and whether or not we
- * want to perform resampling on the region of interest. In this case we
- * will use the entire image as the region of interest and do no resampling */
- SNAPSegmentationROISettings roiSettings;
- roiSettings.SetROI(irisData->GetImageRegion());
- roiSettings.SetResampleFlag(false);
-
- /* We can now tell the IRISApplication to initialize the SNAPImageData object.
- * This method takes the ROI settings as the first parameter and an
- * itk::Command object as the optional second parameter. The Command is used
- * as a callback during the resampling, and is provided primarify for GUIs
- * that want to display a progress bar. */
- application.InitializeSNAPImageData(roiSettings);
-
- /* Once the SNAP data is initalized, we can instruct the IRISApplication to
- * use it as the current image data, in other words, to switch to the auto
- * segmentation mode */
- application.SetCurrentImageDataToSNAP();
-
- /* The SNAPImageData class inherits the methods and attributes of the
- * IRISImageData class and provides additional functionality related to
- * level set image segmentation. In addition to the Grey and Segmentation
- * images (and image wrappers), the SNAPImageData class contains a Speed
- * image wrapper, a SnakeInitialization image wrapper and a Snake image
- * wrapper. All three of these image wrappers contain itk::Image objects
- * with pixel type float. In addition, the SNAPImageData class provides
- * method for running the automatic segmetnation pipeline */
- SNAPImageData *snapData = application.GetSNAPImageData();
-
- /* Automatic segmentation begins with preprocessing. We must choose the type
- * of preprocessing to apply and the parameters of the preprocessing. In this
- * tutorial we will use edge-based snakes and edge-detection preprocessing */
- EdgePreprocessingSettings edgeSettings =
- EdgePreprocessingSettings::MakeDefaultSettings();
- edgeSettings.SetGaussianBlurScale(0.6);
- edgeSettings.SetRemappingSteepness(0.02);
- edgeSettings.SetRemappingExponent(2.0);
-
- /* The preprocessing is performed when we call the DoEdgePreprocessing method.
- * the first argument is an EdgePreprocessingSettings object and the second
- * optional argument is a Command that can be used to implement a progress bar*/
- snapData->DoEdgePreprocessing(edgeSettings);
-
- /* The result of the preprocessing is stored in the Speed image wrapper in
- * the SNAPImageData class. We can write out a slice of the preprocessed image */
- SpeedImageWrapper *wrapSpeed = snapData->GetSpeed();
- SaveImageToFile("speedslice00.mha",wrapSpeed->GetDisplaySlice(0));
-
- /* The next step after preprocessing is to initialize the segmentation with
- * bubbles. This is performed by passing an array of bubbles to the method
- * SNAPImageData::InitializeSegmentationPipeline. The bubbles given below
- * are located in the caudate nuclei of our image */
- std::vector<Bubble> bubbles(2);
- bubbles[0].center = Vector3i(50,29,26); bubbles[0].radius = 3;
- bubbles[1].center = Vector3i(50,57,34); bubbles[1].radius = 2;
-
- /* In addition to the bubble array, the method InitializeSegmentationPipeline
- * requires a set of level set segmentation parameters, which are stored in
- * the SnakeParameters class */
- SnakeParameters parameters = SnakeParameters::GetDefaultEdgeParameters();
-
- /* In order for the snake to converge, we enable the advection force */
- parameters.SetAdvectionWeight(5.0);
-
- /* We can now initialize the segmentation pipeline. In addition to the
- * snake parameters and the bubbles, we need to specify the color label to
- * be used for the snake-based segmentation */
- snapData->InitializeSegmentation(
- parameters,bubbles,application.GetGlobalState()->GetDrawingColorLabel());
-
- /* Now the pipeline is initialized and ready to run. Run 250 iterations */
- snapData->RunSegmentation(250);
-
- /* We can also rewind the segmentation */
- snapData->RestartSegmentation();
-
- /* And we can change the parameters on the fly */
- snapData->RunSegmentation(100);
- parameters.SetAdvectionWeight(3.0);
- snapData->SetSegmentationParameters(parameters);
- snapData->RunSegmentation(200);
-
- /* Now the segmentation is done. The Snake image wrapper contains a floating
- * point image whose positive voxels correspond to the pixels outside of the
- * segmentation boundary and whose negative valued pixels are inside. In
- * to get an actual snake surface, we can use the LevelSetMeshPipeline object,
- * which uses VTK to trace the zero-level-set of the Snake image */
- LevelSetMeshPipeline meshPipeline;
- meshPipeline.SetImage(snapData->GetSnake()->GetImage());
- // meshPipeline.SetImage(snapData->GetLevelSetImage());
-
- /* Pass the globally stored mesh options to the mesh pipeline */
- meshPipeline.SetMeshOptions(application.GetGlobalState()->GetMeshOptions());
-
- /* Create a vtkPolyData to store the resulting mesh, and run the pipeline */
- vtkPolyData *meshResult = vtkPolyData::New();
- meshPipeline.ComputeMesh(meshResult);
-
- /* Export the mesh to VTK format */
- vtkPolyDataWriter *polyWriter = vtkPolyDataWriter::New();
- polyWriter->SetInput(meshResult);
- polyWriter->SetFileTypeToASCII();
- polyWriter->SetFileName("levelset.poly");
- polyWriter->Write();
- polyWriter->Delete();
-
- /* Great. We have now executed automatic segmentation and exported the leve set
- * as a mesh. What's left is to exit the automatic segmentation mode and to
- * merge the segmentation results with the multi-label segmentation image
- * stored in IRISImageData. The following method will do that for us. */
- application.UpdateIRISWithSnapImageData();
- application.SetCurrentImageDataToIRIS();
-
- /* Since we are done with the segmentation data, we should release resources
- * associated with it */
- application.ReleaseSNAPImageData();
-
- /* =======================================================================
- * Section 4. Additional Functionality
- * ====================================================================== */
-
- /* One of the features of SNAP it to be able to divide segmentations into two
- * labels using a cut-plane. This can be done programatically, using the
- * following code. In our example, this will divide the left and the right
- * caudates, giving them different labels */
-
- /* We will paint with label 9 over label 8 */
- application.GetGlobalState()->SetDrawingColorLabel(9);
- application.GetGlobalState()->SetCoverageMode(PAINT_OVER_COLORS);
- application.GetGlobalState()->SetOverWriteColorLabel(8);
-
- /* This actually performs the cut, given an normal vector and intercept that
- * define the cut plane */
- application.RelabelSegmentationWithCutPlane(Vector3d(1,0,0), 64);
-
- /* We can now save the segmentation result as a 3d image */
- SaveImageToFile("result.gipl",irisData->GetSegmentation()->GetImage());
-
- /* Thank you for reading this tutorial! */
- return 0;
-}
-
-// A global variable required for linkage
-std::ostream &verbose = std::cout;
diff --git a/Testing/Logic/iteratorTests.cxx b/Testing/Logic/iteratorTests.cxx
new file mode 100644
index 0000000..1a0adc6
--- /dev/null
+++ b/Testing/Logic/iteratorTests.cxx
@@ -0,0 +1,25 @@
+#include "RLEImageScanlineIterator.h"
+
+extern int itkImageScanlineIteratorTest1(int argc, char *argv[]);
+extern int itkIteratorTests(int argc, char *argv[]);
+extern int itkImageIteratorTest(int argc, char *argv[]);
+extern int itkImageIteratorsForwardBackwardTest(int argc, char *argv[]);
+extern int itkImageIteratorWithIndexTest(int argc, char *argv[]);
+extern int itkImageRegionConstIteratorWithOnlyIndexTest(int argc, char *argv[]);
+extern int itkImageRegionIteratorTest(int argc, char *argv[]);
+extern int itkRegionOfInterestImageFilterTest(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+ itkImageRegionIteratorTest(argc, argv);
+ itkImageScanlineIteratorTest1(argc, argv);
+ itkIteratorTests(argc, argv);
+ itkImageIteratorTest(argc, argv);
+ itkImageIteratorsForwardBackwardTest(argc, argv);
+ itkImageIteratorWithIndexTest(argc, argv);
+ itkImageRegionConstIteratorWithOnlyIndexTest(argc, argv);
+ itkRegionOfInterestImageFilterTest(argc, argv);
+ typedef RLEImage<char, 2, char> charred2dType;
+ //test size 256
+ typedef RLEImage<char, 4> charred4dType; //test size 65536
+}
\ No newline at end of file
diff --git a/Testing/Logic/itkImageIteratorTest.cxx b/Testing/Logic/itkImageIteratorTest.cxx
new file mode 100644
index 0000000..d45c48c
--- /dev/null
+++ b/Testing/Logic/itkImageIteratorTest.cxx
@@ -0,0 +1,280 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+
+#include <iostream>
+#include "RLEImage.h"
+#include "RLEImageRegionIterator.h"
+
+
+// This routine is used to make sure that we call the "const" version
+// of GetPixel() (via the operator[])
+template <typename T>
+void TestConstPixelAccess(const RLEImage<T> &in,
+ RLEImage<T> &out)
+{
+ typename RLEImage<T>::IndexType regionStartIndex3D = {{5, 10, 15}};
+ typename RLEImage<T>::IndexType regionEndIndex3D = {{8, 15, 17}};
+
+ T vec;
+
+ vec[0] = 5;
+ vec[1] = 4;
+ vec[2] = 3;
+ vec[3] = 2;
+ vec[4] = 1;
+
+ out.SetPixel(regionStartIndex3D, vec);
+ out.SetPixel(regionEndIndex3D, in[regionStartIndex3D]);
+}
+
+
+int itkImageIteratorTest(int, char* [] )
+{
+ const unsigned int ImageDimension = 3;
+
+ std::cout << "Creating an image" << std::endl;
+ RLEImage<itk::Vector<unsigned short, 5> >::Pointer
+ o3 = RLEImage<itk::Vector<unsigned short, 5> >::New();
+
+ float origin3D[ImageDimension] = { 5, 2.1, 8.1};
+ float spacing3D[ImageDimension] = { 1.5, 2.1, 1};
+
+ RLEImage<itk::Vector<unsigned short, 5> >::SizeType imageSize3D = {{ 20, 40, 60 }};
+
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType startIndex3D = {{5, 4, 1}};
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType regionStartIndex3D = {{6, 10, 12}};
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType regionEndIndex3D = {{8, 15, 17}};
+
+
+ RLEImage<itk::Vector<unsigned short, 5> >::RegionType region;
+ region.SetSize(imageSize3D);
+ region.SetIndex(startIndex3D);
+ o3->SetRegions( region );
+ o3->SetOrigin(origin3D);
+ o3->SetSpacing(spacing3D);
+
+ o3->Allocate();
+ itk::Vector<unsigned short, 5> fillValue;
+ fillValue.Fill(itk::NumericTraits<unsigned short>::max());
+ o3->FillBuffer(fillValue);
+
+ std::cout << "Setting/Getting a pixel" << std::endl;
+ itk::Vector<unsigned short, 5> vec;
+
+ vec[0] = 5;
+ vec[1] = 4;
+ vec[2] = 3;
+ vec[3] = 2;
+ vec[4] = 1;
+
+ (*o3).SetPixel(regionStartIndex3D, vec);
+ (*o3).SetPixel(regionEndIndex3D, (*o3)[regionStartIndex3D]);
+ TestConstPixelAccess(*o3, *o3);
+
+ typedef itk::Vector< unsigned short, 5 > VectorPixelType;
+ typedef RLEImage< VectorPixelType > VectorImageType;
+
+ typedef itk::ImageIterator< VectorImageType > VectorImageIterator;
+ typedef itk::ImageConstIterator< VectorImageType > VectorImageConstIterator;
+
+ VectorImageIterator itr1( o3, region );
+ VectorImageConstIterator itr2( o3, region );
+
+ // Exercise copy constructor
+ VectorImageIterator itr3( itr1 );
+
+ // Exercise assignment operator
+ VectorImageIterator itr4;
+ itr4 = itr1;
+
+ // Exercise operator!=
+ if( itr4 != itr1 )
+ {
+ std::cerr << "Error in operator= or operator!=" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise operator==
+ if( !( itr4 == itr1 ) )
+ {
+ std::cerr << "Error in operator= or operator==" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise operator<=
+ if( !( itr4 <= itr1 ) )
+ {
+ std::cerr << "Error in operator= or operator<=" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise operator<
+ if( itr4 < itr1 )
+ {
+ std::cerr << "Error in operator= or operator<" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise operator>=
+ if( !( itr4 >= itr1 ) )
+ {
+ std::cerr << "Error in operator= or operator>=" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise operator>
+ if( itr4 > itr1 )
+ {
+ std::cerr << "Error in operator= or operator>" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise GetImageIteratorDimension()
+ if( itr1.GetImageIteratorDimension() != ImageDimension )
+ {
+ std::cerr << "Error in GetImageIteratorDimension" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise GetIndex()
+ VectorImageType::IndexType index1 = itr1.GetIndex();
+ if( index1 != startIndex3D )
+ {
+ std::cerr << "Error in GetIndex()" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise SetIndex()
+ VectorImageType::IndexType index2 = index1;
+ index2[0]++;
+ VectorImageIterator itr5 = itr1;
+ itr5.SetIndex( index2 );
+ if( itr5.GetIndex() != index2 )
+ {
+ std::cerr << "Error in GetIndex() and/or SetIndex()" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if( itr5.GetIndex() == itr1.GetIndex() )
+ {
+ std::cerr << "Error in GetIndex() and/or SetIndex()" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise GetRegion()
+ VectorImageType::RegionType region1 = itr1.GetRegion();
+ if( region1 != region )
+ {
+ std::cerr << "Error in GetRegion()" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise GetImage() non-const version
+ VectorImageType * image1 = itr1.GetImage();
+ if( image1 != o3.GetPointer() )
+ {
+ std::cerr << "Error in GetImage()" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise GetImage() const version
+ const VectorImageType * image2 = itr2.GetImage();
+ if( image2 != o3.GetPointer() )
+ {
+ std::cerr << "Error in GetImage()" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Exercise Get() non-const and const version
+ {
+ VectorPixelType vp1 = itr1.Get();
+ VectorPixelType vp2 = itr2.Get();
+ std::cout << "vp1: " << vp1 << std::endl;
+ std::cout << "vp2: " << vp2 << std::endl;
+ if( vp1 != vp2 )
+ {
+ std::cerr << "Error in Get()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ // verify that the value can be modified
+ vp1[0]++;
+ itr1.Set( vp1 );
+ itr2 = itr1; //we need to do this because Set invalidates other itarators
+ vp2 = itr2.Get();
+ if( vp1 != vp2 )
+ {
+ std::cerr << "Error in Get() and/or Set()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+
+ // Exercise Value() const and non-const methods
+ {
+ VectorPixelType vp1 = itr1.Value();
+ VectorPixelType vp2 = itr2.Value();
+ if( vp1 != vp2 )
+ {
+ std::cerr << "Error in Value()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ // verify that the value can be modified
+ vp1[0]++;
+ itr1.Set(vp1);
+ vp2 = itr2.Value();
+ if( vp1 != vp2 )
+ {
+ std::cerr << "Error in Get() and/or Set()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Exercise Begin(), GoToBegin(), IsAtBegin() and IsAtEnd()
+ {
+ itr1.GoToBegin();
+ if( !itr1.IsAtBegin() )
+ {
+ std::cerr << "Error in Begin() and/or IsAtBegin()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ if( itr1.IsAtEnd() )
+ {
+ std::cerr << "Error in Begin() and/or IsAtEnd()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+
+ // Exercise End(), GoToEnd(), IsAtBegin() and IsAtEnd()
+ {
+ itr1.GoToEnd();
+ if( !itr1.IsAtEnd() )
+ {
+ std::cerr << "Error in End() and/or IsAtEnd()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ if( itr1.IsAtBegin() )
+ {
+ std::cerr << "Error in End() and/or IsAtBegin()" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/Testing/Logic/itkImageIteratorWithIndexTest.cxx b/Testing/Logic/itkImageIteratorWithIndexTest.cxx
new file mode 100644
index 0000000..237e869
--- /dev/null
+++ b/Testing/Logic/itkImageIteratorWithIndexTest.cxx
@@ -0,0 +1,399 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+
+#include <iostream>
+
+#include "RLEImageRegionIterator.h"
+#include "itkNumericTraits.h"
+
+
+template <typename TPixelType>
+class itkImageIteratorWithIndexTestIteratorTester
+{
+
+ public:
+ typedef TPixelType PixelType;
+
+ typedef RLEImage< PixelType > ImageType;
+
+ typedef itk::ImageRegionIteratorWithIndex< ImageType > IteratorType;
+
+ typedef itk::ImageRegionConstIteratorWithIndex< ImageType > ConstIteratorType;
+
+ itkImageIteratorWithIndexTestIteratorTester( const PixelType & value )
+ {
+ m_Image = ImageType::New();
+
+ typename ImageType::SizeType size;
+ size.Fill(100);
+
+ typename ImageType::IndexType start;
+ start.Fill(0);
+
+ typename ImageType::RegionType region;
+ region.SetSize( size );
+ region.SetIndex( start );
+
+ m_Image->SetRegions( region );
+ m_Image->Allocate();
+
+ m_Image->FillBuffer( value );
+ }
+
+ bool TestIterator()
+ {
+ IteratorType it( m_Image, m_Image->GetBufferedRegion() );
+ it.GoToBegin();
+ while( !it.IsAtEnd() )
+ {
+ PixelType value = it.Get();
+ PixelType testValue = value * static_cast<typename itk::NumericTraits<PixelType>::ValueType>( 2 );
+ it.Set( testValue);
+ if( it.Get() != testValue )
+ {
+ std::cerr << "TestIterator failed!" << std::endl;
+ return false;
+ }
+ ++it;
+ }
+ return true;
+ }
+
+ bool TestConstIterator()
+ {
+ ConstIteratorType it( m_Image, m_Image->GetBufferedRegion() );
+ it.GoToBegin();
+ while( !it.IsAtEnd() )
+ {
+ PixelType value = it.Get();
+ if( value != it.Get() ) // check repeatibility
+ {
+ std::cerr << "TestConstIterator failed!" << std::endl;
+ return false;
+ }
+ ++it;
+ }
+ return true;
+ }
+
+ bool TestReverseIteration()
+ {
+ ConstIteratorType it( m_Image, m_Image->GetBufferedRegion() );
+ it.GoToReverseBegin();
+ while( !it.IsAtReverseEnd() )
+ {
+ PixelType value = it.Get();
+ if( value != it.Get() ) // check repeatibility
+ {
+ std::cerr << "TestReverseIteration failed!" << std::endl;
+ return false;
+ }
+ --it;
+ }
+ return true;
+ }
+
+ private:
+
+ typename ImageType::Pointer m_Image;
+
+};
+
+int itkImageIteratorWithIndexTest(int, char* [] )
+{
+
+ bool testPassed = true; // let's be optimistic
+
+ // Instantiate image of various types and
+ // test the iterators on them
+
+ std::cout << "Testing with Image< char, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< char > TesterC( 10 );
+ if( TesterC.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterC.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterC.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+
+ std::cout << "Testing with Image< unsigned char, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< unsigned char > TesterUC( 10 );
+ if( TesterUC.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterUC.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterUC.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< short, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< short > TesterS( 10 );
+ if( TesterS.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterS.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterS.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< unsigned short, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< unsigned short > TesterUS( 10 );
+ if( TesterUS.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterUS.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterUS.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< int, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< int > TesterI( 10 );
+ if( TesterI.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterI.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterI.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< unsigned int, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< unsigned int > TesterUI( 10 );
+ if( TesterUI.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterUI.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterUI.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< float, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< float > TesterF( 10.0 );
+ if( TesterF.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterF.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterF.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< double, 3 > " << std::endl;
+ itkImageIteratorWithIndexTestIteratorTester< double > TesterD( 10.0 );
+ if( TesterD.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterD.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterD.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<char,4>, 3 > " << std::endl;
+ typedef itk::Vector<char,4> VC;
+ VC vc;
+ vc.Fill( 127 );
+ itkImageIteratorWithIndexTestIteratorTester< VC > TesterVC( vc );
+ if( TesterVC.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVC.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVC.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<unsigned char,4>, 3 > " << std::endl;
+ typedef itk::Vector<unsigned char,4> VUC;
+ VUC vuc;
+ vuc.Fill( 10 );
+ itkImageIteratorWithIndexTestIteratorTester< VUC > TesterVUC( vuc );
+ if( TesterVUC.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVUC.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVUC.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<short,4>, 3 > " << std::endl;
+ typedef itk::Vector<short,4> VS;
+ VS vs;
+ vs.Fill( 10 );
+ itkImageIteratorWithIndexTestIteratorTester< VS > TesterVS( vs );
+ if( TesterVS.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVS.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVS.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<unsigned short,4>, 3 > " << std::endl;
+ typedef itk::Vector<unsigned short,4> VUS;
+ VUS vus;
+ vus.Fill( 10 );
+ itkImageIteratorWithIndexTestIteratorTester< VUS > TesterVUS( vus );
+ if( TesterVUS.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVUS.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVUS.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<int,4>, 3 > " << std::endl;
+ typedef itk::Vector<int,4> VI;
+ VI vi;
+ vi.Fill( 10 );
+ itkImageIteratorWithIndexTestIteratorTester< VI > TesterVI( vi );
+ if( TesterVI.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVI.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVI.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<unsigned int,4>, 3 > " << std::endl;
+ typedef itk::Vector<unsigned int,4> VUI;
+ VUI vui;
+ vui.Fill( 10 );
+ itkImageIteratorWithIndexTestIteratorTester< VUI > TesterVUI( vui );
+ if( TesterVUI.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVUI.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVUI.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<float,4>, 3 > " << std::endl;
+ typedef itk::Vector<float,4> VF;
+ VF vf;
+ vf.Fill( 10 );
+ itkImageIteratorWithIndexTestIteratorTester< VF > TesterVF( vf );
+ if( TesterVF.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVF.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVF.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ std::cout << "Testing with Image< itk::Vector<double,4>, 3 > " << std::endl;
+ typedef itk::Vector<double,4> VD;
+ VD vd;
+ vd.Fill( 10 );
+ itkImageIteratorWithIndexTestIteratorTester< VD > TesterVD( vd );
+ if( TesterVD.TestIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVD.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ if( TesterVD.TestReverseIteration() == false )
+ {
+ testPassed = false;
+ }
+
+ if ( !testPassed )
+ {
+ std::cout << "Failed" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::cout << "Success" << std::endl;
+ return EXIT_SUCCESS;
+
+}
diff --git a/Testing/Logic/itkImageIteratorsForwardBackwardTest.cxx b/Testing/Logic/itkImageIteratorsForwardBackwardTest.cxx
new file mode 100644
index 0000000..82501cb
--- /dev/null
+++ b/Testing/Logic/itkImageIteratorsForwardBackwardTest.cxx
@@ -0,0 +1,172 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+
+#include <iostream>
+
+#include "RLEImageRegionIterator.h"
+
+int itkImageIteratorsForwardBackwardTest(int, char* [] )
+{
+
+ std::cout << "Creating an image" << std::endl;
+ typedef RLEImage<unsigned short> ImageType;
+
+ ImageType::Pointer myImage = ImageType::New();
+
+ ImageType::SizeType size;
+
+ size[0] = 4;
+ size[1] = 4;
+ size[2] = 4;
+
+ ImageType::IndexType start;
+ start.Fill(0);
+
+ ImageType::RegionType region;
+ region.SetIndex( start );
+ region.SetSize( size );
+
+ myImage->SetLargestPossibleRegion( region );
+ myImage->SetBufferedRegion( region );
+ myImage->SetRequestedRegion( region );
+ myImage->Allocate();
+
+ typedef itk::ImageRegionIteratorWithIndex< ImageType > IteratorType;
+
+ typedef itk::ImageRegionConstIteratorWithIndex< ImageType > ConstIteratorType;
+
+ IteratorType it( myImage, region );
+
+ ImageType::PixelType value;
+
+ value = itk::NumericTraits< ImageType::PixelType >::ZeroValue();
+
+ // Store information on the Image
+ std::cout << "Storing data on the image ... " << std::endl;
+
+ while( !it.IsAtEnd() )
+ {
+ value++;
+ it.Set( value );
+ ++it;
+ }
+
+
+ // Verification
+ IteratorType ot( myImage, region );
+ std::cout << "Verifying the data forwards... ";
+
+ value = itk::NumericTraits< ImageType::PixelType >::ZeroValue();
+
+ while( !ot.IsAtEnd() )
+ {
+ value++;
+
+ if( ot.Get() != value )
+ {
+ std::cerr << "Error in forward pass" << std::endl;
+ std::cerr << "Values don't correspond to what was stored "
+ << std::endl;
+ std::cerr << "Test failed at index ";
+ std::cerr << ot.GetIndex() << std::endl;
+ std::cerr << "Value stored is = " << ot.Get() << std::endl;
+ std::cerr << "Value should be = " << value << std::endl;
+ return EXIT_FAILURE;
+ }
+ ++ot;
+ }
+
+ std::cout << " PASSED !" << std::endl;
+
+ // Verification
+ std::cout << "Verifying the data backwards... ";
+
+ ot.GoToEnd();
+ do
+ {
+ --ot;
+ if( ot.Get() != value )
+ {
+ std::cerr << "Error in backwards pass" << std::endl;
+ std::cerr << "Values don't correspond to what was stored "
+ << std::endl;
+ std::cerr << "Test failed at index ";
+ std::cerr << ot.GetIndex() << std::endl;
+ std::cerr << "Value stored is = " << ot.Get() << std::endl;
+ std::cerr << "Value should be = " << value << std::endl;
+ return EXIT_FAILURE;
+ }
+ value--;
+ } while (!ot.IsAtBegin());
+
+ std::cout << " PASSED !" << std::endl;
+
+ // Verification
+ ConstIteratorType cot( myImage, region );
+ std::cout << "Const Iterator: Verifying the data forwards... ";
+
+ value = itk::NumericTraits< ImageType::PixelType >::ZeroValue();
+
+ while( !cot.IsAtEnd() )
+ {
+ value++;
+
+ if( cot.Get() != value )
+ {
+ std::cerr << "Error in forward pass" << std::endl;
+ std::cerr << "Values don't correspond to what was stored "
+ << std::endl;
+ std::cerr << "Test failed at index ";
+ std::cerr << cot.GetIndex() << std::endl;
+ std::cerr << "Value stored is = " << cot.Get() << std::endl;
+ std::cerr << "Value should be = " << value << std::endl;
+ return EXIT_FAILURE;
+ }
+ ++cot;
+ }
+
+ std::cout << " PASSED !" << std::endl;
+
+ // Verification
+ std::cout << "Const Iterator : Verifying the data backwards... ";
+
+ cot.GoToEnd();
+ do
+ {
+ --cot;
+ if( cot.Get() != value )
+ {
+ std::cerr << "Error in backwards pass" << std::endl;
+ std::cerr << "Values don't correspond to what was stored "
+ << std::endl;
+ std::cerr << "Test failed at index ";
+ std::cerr << cot.GetIndex() << std::endl;
+ std::cerr << "Value stored is = " << cot.Get() << std::endl;
+ std::cerr << "Value should be = " << value << std::endl;
+ return EXIT_FAILURE;
+ }
+ value--;
+ } while (!cot.IsAtBegin());
+
+ std::cout << " PASSED !" << std::endl;
+
+ std::cout << std::endl << "Test passed" << std::endl;
+
+ return EXIT_SUCCESS;
+
+}
diff --git a/Testing/Logic/itkImageRegionConstIteratorWithOnlyIndexTest.cxx b/Testing/Logic/itkImageRegionConstIteratorWithOnlyIndexTest.cxx
new file mode 100644
index 0000000..6aaf1c8
--- /dev/null
+++ b/Testing/Logic/itkImageRegionConstIteratorWithOnlyIndexTest.cxx
@@ -0,0 +1,230 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+
+#include <iostream>
+
+#include "RLEImageRegionIterator.h"
+
+template <typename TImage>
+class itkImageRegionConstIteratorWithOnlyIndexTestIteratorTester
+{
+
+ public:
+ typedef TImage ImageType;
+ typedef typename ImageType::IndexType IndexType;
+
+ typedef itk::ImageRegionConstIteratorWithOnlyIndex<ImageType > ConstIteratorType;
+
+ itkImageRegionConstIteratorWithOnlyIndexTestIteratorTester( )
+ {
+ m_Image = ImageType::New();
+
+ typename ImageType::SizeType size;
+ size.Fill(100);
+
+ typename ImageType::IndexType start;
+ start.Fill(0);
+
+ typename ImageType::RegionType region;
+ region.SetSize( size );
+ region.SetIndex( start );
+
+ m_Image->SetRegions( region );
+ m_Image->Allocate();
+
+ // Setup a smaller requested region
+ size.Fill( 50 );
+// size[0] = 40;
+ start.Fill ( 10 );
+// start[0] = 8;
+ region.SetSize( size );
+ region.SetIndex( start );
+ m_Image->SetRequestedRegion( region );
+ }
+
+ bool TestConstIterator()
+ {
+ typename ImageType::RegionType region = m_Image->GetBufferedRegion();
+ if( TestByRegion( region ) == false )
+ {
+ std::cout << "Failed testing buffered region." << std::endl;
+ return false;
+ }
+
+ region = m_Image->GetRequestedRegion();
+ if( TestByRegion( region ) == false )
+ {
+ std::cout << "Failed testing requested region." << std::endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool TestByRegion( typename ImageType::RegionType & region )
+ {
+ ConstIteratorType it( m_Image, region );
+ it.GoToBegin();
+ typename ImageType::IndexValueType step = 0;
+
+ while( !it.IsAtEnd() )
+ {
+ IndexType index = it.GetIndex();
+ // Check to see if the index is within allowed bounds
+ bool isInside = region.IsInside(index);
+ if( !isInside )
+ {
+ std::cout << "Index is not inside region! - " << index << std::endl;
+ return false;
+ }
+ // check repeatibility
+ if( index != it.GetIndex() )
+ {
+ std::cout << "Failed to repeat GetIndex." << std::endl;
+ return false;
+ }
+ // increment and test index
+ ++it;
+ IndexType truthIndex;
+ truthIndex[0] = step % region.GetSize()[0] + region.GetIndex()[0];
+ truthIndex[1] = step / region.GetSize()[0] + region.GetIndex()[1];
+ if( ImageType::GetImageDimension() > 2 )
+ {
+ truthIndex[1] = ( step / region.GetSize()[0] ) % region.GetSize()[1] + region.GetIndex()[1];
+ truthIndex[2] = step / ( region.GetSize()[0] * region.GetSize()[1] ) + region.GetIndex()[2];
+ }
+
+ if( index != truthIndex )
+ {
+ std::cout << "Failed single increment. step: " << step << " index: " << index << " truthIndex: " << truthIndex << std::endl;
+ return false;
+ }
+ ++step;
+ // check repeatibility after decrement
+ --it;
+ if( index != it.GetIndex() )
+ {
+ std::cout << "Failed to increment and decrement." << std::endl;
+ return false;
+ }
+ ++it;
+ }
+
+ // Test iterating fwd by line
+ IndexType index;
+ it.GoToBegin();
+ index = it.GetIndex();
+ for( unsigned int i=0; i < region.GetSize()[0]; i++ )
+ {
+ ++it;
+ }
+ if( index[0] != it.GetIndex()[0] || index[1] != it.GetIndex()[1] - 1 )
+ {
+ std::cout << "Failed iterating forward by line." << std::endl;
+ }
+
+ // iterate back
+ for( unsigned int i=0; i < region.GetSize()[0]; i++ )
+ {
+ --it;
+ }
+ if( index != it.GetIndex() )
+ {
+ std::cout << "Failed iterating back by line." << std::endl;
+ }
+
+ // Test iterating fwd by slice
+ if( ImageType::GetImageDimension() > 2 )
+ {
+ it.GoToBegin();
+ index = it.GetIndex();
+ for( unsigned int i=0; i < region.GetSize()[0] * region.GetSize()[1]; i++ )
+ {
+ ++it;
+ }
+ ++it; //extra step
+ if( index[0] != it.GetIndex()[0] - 1 || index[1] != it.GetIndex()[1] || index[2] != it.GetIndex()[2] - 1 )
+ {
+ std::cout << "Failed iterating forward by slice." << std::endl;
+ }
+
+ // iterate back
+ for( unsigned int i=0; i < region.GetSize()[0] * region.GetSize()[1]; i++ )
+ {
+ --it;
+ }
+ --it;
+ if( index != it.GetIndex() )
+ {
+ std::cout << "Failed iterating back by slice." << std::endl;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+
+ typename ImageType::Pointer m_Image;
+
+};
+
+int itkImageRegionConstIteratorWithOnlyIndexTest(int, char* [] )
+{
+ bool testPassed = true; // let's be optimistic
+
+ // Instantiate image of various types and
+ // test the iterators on them
+
+ {
+ std::cout << "Testing with Image< char, 3 >... " << std::endl;
+ itkImageRegionConstIteratorWithOnlyIndexTestIteratorTester< RLEImage< char > > Tester;
+ if( Tester.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ }
+
+ {
+ std::cout << "Testing with ImageBase< 2 >... " << std::endl;
+ itkImageRegionConstIteratorWithOnlyIndexTestIteratorTester< itk::ImageBase< 2 > > Tester;
+ if( Tester.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ }
+
+ {
+ std::cout << "Testing with ImageBase< 3 >... " << std::endl;
+ itkImageRegionConstIteratorWithOnlyIndexTestIteratorTester< itk::ImageBase< 3 > > Tester;
+ if( Tester.TestConstIterator() == false )
+ {
+ testPassed = false;
+ }
+ }
+
+ if ( !testPassed )
+ {
+ std::cout << "Failed" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::cout << "Success" << std::endl;
+ return EXIT_SUCCESS;
+
+}
diff --git a/Testing/Logic/itkImageRegionIteratorTest.cxx b/Testing/Logic/itkImageRegionIteratorTest.cxx
new file mode 100644
index 0000000..5b6fb89
--- /dev/null
+++ b/Testing/Logic/itkImageRegionIteratorTest.cxx
@@ -0,0 +1,246 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+
+#include <iostream>
+
+#include "RLEImageRegionIterator.h"
+
+
+// This routine is used to make sure that we call the "const" version
+// of GetPixel() (via the operator[])
+template <typename T>
+void TestConstPixelAccess(const RLEImage<T> &in,
+ RLEImage<T> &out)
+{
+ typename RLEImage<T>::IndexType regionStartIndex3D = {{5, 10, 15}};
+ typename RLEImage<T>::IndexType regionEndIndex3D = {{8, 15, 17}};
+
+ T vec;
+
+ vec[0] = 5;
+ vec[1] = 4;
+ vec[2] = 3;
+ vec[3] = 2;
+ vec[4] = 1;
+
+ out.SetPixel(regionStartIndex3D, vec);
+ out.SetPixel(regionEndIndex3D, in[regionStartIndex3D]);
+ //out[regionStartIndex3D] = vec;
+ //out[regionEndIndex3D] = in[regionStartIndex3D];
+}
+
+
+int itkImageRegionIteratorTest(int, char* [] )
+{
+ std::cout << "Creating an image" << std::endl;
+ RLEImage<itk::Vector<unsigned short, 5> >::Pointer
+ o3 = RLEImage<itk::Vector<unsigned short, 5> >::New();
+
+ int status = 0;
+
+ float origin3D[3] = { 5, 2.1, 8.1};
+ float spacing3D[3] = { 1.5, 2.1, 1};
+
+ RLEImage<itk::Vector<unsigned short, 5> >::SizeType imageSize3D = {{ 20, 40, 60 }};
+ RLEImage<itk::Vector<unsigned short, 5> >::SizeType bufferSize3D = {{ 20, 20, 17 }};
+ RLEImage<itk::Vector<unsigned short, 5> >::SizeType regionSize3D = {{ 4, 6, 6 }};
+
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType startIndex3D = {{5, 4, 1}};
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType bufferStartIndex3D = {{5, 5, 1}};
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType regionStartIndex3D = {{5, 10, 12}};
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType regionEndIndex3D = {{8, 15, 17}};
+
+
+ RLEImage<itk::Vector<unsigned short, 5> >::RegionType region;
+ region.SetSize(imageSize3D);
+ region.SetIndex(startIndex3D);
+ o3->SetLargestPossibleRegion( region );
+ region.SetSize(bufferSize3D);
+ region.SetIndex(bufferStartIndex3D);
+ o3->SetBufferedRegion( region );
+ region.SetSize(regionSize3D);
+ region.SetIndex(regionStartIndex3D);
+ o3->SetRequestedRegion( region );
+
+ o3->SetOrigin(origin3D);
+ o3->SetSpacing(spacing3D);
+
+ o3->Allocate();
+
+ std::cout << "Setting/Getting a pixel" << std::endl;
+ itk::Vector<unsigned short, 5> vec;
+
+ vec[0] = 5;
+ vec[1] = 4;
+ vec[2] = 3;
+ vec[3] = 2;
+ vec[4] = 1;
+
+ (*o3).SetPixel(regionStartIndex3D, vec);
+ (*o3).SetPixel(regionEndIndex3D, (*o3)[regionStartIndex3D]);
+ TestConstPixelAccess(*o3, *o3);
+
+
+ itk::ImageIterator<RLEImage<itk::Vector<unsigned short, 5> > > standardIt(o3, region);
+
+ // Iterate over a region using a simple for loop
+ itk::ImageRegionIterator<RLEImage<itk::Vector<unsigned short, 5> > > it(o3, region);
+
+ std::cout << "Simple iterator loop: ";
+ for (; !it.IsAtEnd(); ++it)
+ {
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType index = it.GetIndex();
+ std::cout << index << std::endl;
+ }
+
+ itk::ImageRegionConstIterator<RLEImage<itk::Vector<unsigned short, 5> > > standardCIt(o3, region);
+
+ // Iterate over a region using a simple for loop and a const iterator
+ itk::ImageRegionConstIterator<RLEImage<itk::Vector<unsigned short, 5> > > cit(o3, region);
+
+ std::cout << "Simple const iterator loop: ";
+ for (; !cit.IsAtEnd(); ++cit)
+ {
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType index = cit.GetIndex();
+ std::cout << index << std::endl;
+ }
+
+
+ // Iterator over the region backwards using a simple for loop
+ itk::ImageRegionIterator<RLEImage<itk::Vector<unsigned short, 5> > > backIt(o3, region);
+
+ backIt.GoToEnd(); // one pixel past the end of the region
+ do
+ {
+ --backIt;
+
+ RLEImage<itk::Vector<unsigned short, 5> >::IndexType index = backIt.GetIndex();
+ std::cout << "Simple iterator backwards loop: ";
+ for (unsigned int i=0; i < index.GetIndexDimension(); i++)
+ {
+ std::cout << index[i] << " ";
+ }
+ std::cout << std::endl;
+ }
+ while (!backIt.IsAtBegin()); // stop when we reach the beginning
+
+ // Iterate over a region, then change the region and iterate over the new region
+ {
+ // Create an image
+ typedef RLEImage<int> TestImageType;
+ TestImageType::IndexType imageCorner;
+ imageCorner.Fill(0);
+
+ TestImageType::SizeType imageSize;
+ imageSize.Fill(3);
+
+ TestImageType::RegionType imageRegion(imageCorner, imageSize);
+
+ TestImageType::Pointer image = TestImageType::New();
+ image->SetRegions(imageRegion);
+ image->Allocate();
+
+ itk::ImageRegionIterator<TestImageType> createImageIterator(image,imageRegion);
+
+ // Set all pixels with first index == 0 to 0, and set the rest of the image to 255
+ while(!createImageIterator.IsAtEnd())
+ {
+ if(createImageIterator.GetIndex()[0] == 0)
+ {
+ createImageIterator.Set(0);
+ }
+ else
+ {
+ createImageIterator.Set(255);
+ }
+
+ ++createImageIterator;
+ }
+
+ // Setup and iterate over the first region
+ TestImageType::IndexType region1Start;
+ region1Start.Fill(0);
+
+ TestImageType::SizeType regionSize;
+ regionSize.Fill(2);
+
+ TestImageType::RegionType region1(region1Start, regionSize);
+
+ itk::ImageRegionConstIterator<TestImageType> imageIterator(image,region1);
+
+ std::vector<int> expectedValuesRegion1(8);
+ expectedValuesRegion1[0] = 0;
+ expectedValuesRegion1[1] = 255;
+ expectedValuesRegion1[2] = 0;
+ expectedValuesRegion1[3] = 255;
+ expectedValuesRegion1[4] = 0;
+ expectedValuesRegion1[5] = 255;
+ expectedValuesRegion1[6] = 0;
+ expectedValuesRegion1[7] = 255;
+ unsigned int counter = 0;
+ while(!imageIterator.IsAtEnd())
+ {
+ if(imageIterator.Get() != expectedValuesRegion1[counter])
+ {
+ status = 1; // Fail
+ }
+ counter++;
+ ++imageIterator;
+ }
+
+ // Change iteration region
+ TestImageType::IndexType region2start;
+ region2start.Fill(1);
+
+ TestImageType::RegionType region2(region2start, regionSize);
+
+ imageIterator.SetRegion(region2);
+ imageIterator.GoToBegin();
+
+ std::vector<int> expectedValuesRegion2(8);
+ expectedValuesRegion2[0] = 255;
+ expectedValuesRegion2[1] = 255;
+ expectedValuesRegion2[2] = 255;
+ expectedValuesRegion2[3] = 255;
+ expectedValuesRegion2[4] = 255;
+ expectedValuesRegion2[5] = 255;
+ expectedValuesRegion2[6] = 255;
+ expectedValuesRegion2[7] = 255;
+ counter = 0;
+ while(!imageIterator.IsAtEnd())
+ {
+ if(imageIterator.Get() != expectedValuesRegion2[counter])
+ {
+ status = 1; // Fail
+ }
+ counter++;
+ ++imageIterator;
+ }
+
+ } // end "Change Region" test
+
+ if (status == 0)
+ {
+ std::cout << "Passed" << std::endl;
+ }
+ else
+ {
+ std::cout << "Failed" << std::endl;
+ }
+ return status;
+}
diff --git a/Testing/Logic/itkImageScanlineIteratorTest1.cxx b/Testing/Logic/itkImageScanlineIteratorTest1.cxx
new file mode 100644
index 0000000..2568ec9
--- /dev/null
+++ b/Testing/Logic/itkImageScanlineIteratorTest1.cxx
@@ -0,0 +1,235 @@
+
+#include <iostream>
+
+#include "RLEImageScanlineIterator.h"
+
+// This routine is used to make sure that we call the "const" version
+// of GetPixel() (via the operator[])
+template< typename TPixel, unsigned int VImageDimension, typename CounterType >
+void TestConstPixelAccess(const RLEImage<TPixel, VImageDimension, CounterType> &in,
+ RLEImage<TPixel, VImageDimension, CounterType> &out)
+{
+ typename RLEImage<TPixel, VImageDimension, CounterType>::IndexType regionStartIndex3D = { { 5, 10, 15 } };
+ typename RLEImage<TPixel, VImageDimension, CounterType>::IndexType regionEndIndex3D = { { 8, 15, 17 } };
+
+ TPixel vec;
+
+ vec[0] = 5;
+ vec[1] = 4;
+ vec[2] = 3;
+ vec[3] = 2;
+ vec[4] = 1;
+
+ out.SetPixel(regionStartIndex3D, vec);
+ out.SetPixel(regionEndIndex3D, in[regionStartIndex3D]);
+ //out[regionStartIndex3D] = vec;
+ //out[regionEndIndex3D] = in[regionStartIndex3D];
+}
+
+
+int itkImageScanlineIteratorTest1(int, char* [] )
+{
+ RLEImage<itk::Vector<unsigned short, 5> >::Pointer
+ o3 = RLEImage<itk::Vector<unsigned short, 5> >::New();
+
+ int status = EXIT_SUCCESS;
+
+ float origin3D[3] = { 5, 2.1, 8.1};
+ float spacing3D[3] = { 1.5, 2.1, 1};
+
+ typedef RLEImage<itk::Vector<unsigned short, 5> > ImageType;
+
+ ImageType::SizeType imageSize3D = {{ 20, 40, 60 }};
+ ImageType::SizeType bufferSize3D = {{ 20, 20, 14 }};
+ ImageType::SizeType regionSize3D = {{ 4, 6, 6 }};
+
+ ImageType::IndexType startIndex3D = {{5, 4, 1}};
+ ImageType::IndexType bufferStartIndex3D = {{5, 3, 5}};
+ ImageType::IndexType regionStartIndex3D = {{5, 10, 12}};
+ ImageType::IndexType regionEndIndex3D = {{8, 15, 17}};
+
+
+ ImageType::RegionType region;
+ region.SetSize(imageSize3D);
+ region.SetIndex(startIndex3D);
+ o3->SetLargestPossibleRegion( region );
+ region.SetSize(bufferSize3D);
+ region.SetIndex(bufferStartIndex3D);
+ o3->SetBufferedRegion( region );
+ region.SetSize(regionSize3D);
+ region.SetIndex(regionStartIndex3D);
+ o3->SetRequestedRegion( region );
+
+ o3->SetOrigin(origin3D);
+ o3->SetSpacing(spacing3D);
+
+ o3->Allocate();
+
+ std::cout << "Setting/Getting a pixel" << std::endl;
+ itk::Vector<unsigned short, 5> vec;
+
+ vec[0] = 5;
+ vec[1] = 4;
+ vec[2] = 3;
+ vec[3] = 2;
+ vec[4] = 1;
+
+ (*o3).SetPixel(regionStartIndex3D, vec);
+ (*o3).SetPixel(regionEndIndex3D, (*o3)[regionStartIndex3D]);
+ TestConstPixelAccess(*o3, *o3);
+
+
+ itk::ImageIterator<ImageType > standardIt(o3, region);
+
+ // Iterate over a region using a simple for loop
+ itk::ImageScanlineIterator<ImageType > it(standardIt);
+
+ std::cout << "Simple iterator loop 1\n";
+ while ( !it.IsAtEnd() )
+ {
+ while ( !it.IsAtEndOfLine() )
+ {
+ ++it;
+ }
+ it.NextLine();
+ }
+
+ itk::ImageScanlineIterator<ImageType > it2(o3, o3->GetBufferedRegion());
+ std::cout << "Simple iterator loop 1bis\n";
+ while (!it2.IsAtEnd())
+ {
+ while (!it2.IsAtEndOfLine())
+ {
+ ++it2;
+ }
+ it2.NextLine();
+ }
+
+ itk::ImageScanlineConstIterator<ImageType > testBeginEnd(o3, region);
+ testBeginEnd.GoToBeginOfLine();
+ testBeginEnd.GoToEndOfLine();
+
+ itk::ImageScanlineConstIterator<ImageType > standardCIt(o3, region);
+
+ // Iterate over a region using a simple loop and a const iterator
+ itk::ImageScanlineConstIterator<ImageType > cit(standardIt);
+
+ std::cout << "Simple const iterator loop 2\n";
+ while ( !cit.IsAtEnd() )
+ {
+ while ( !cit.IsAtEndOfLine() )
+ {
+ ++cit;
+ }
+ cit.NextLine();
+ }
+
+ while ( !cit.IsAtEnd() )
+ {
+ cit.NextLine();
+ }
+
+
+ // Iterate over a region, then change the region and iterate over the new region
+ {
+ // Create an image
+ typedef itk::Image<int, 2> TestImageType;
+ TestImageType::IndexType imageCorner;
+ imageCorner.Fill(0);
+
+ TestImageType::SizeType imageSize;
+ imageSize.Fill(3);
+
+ TestImageType::RegionType imageRegion(imageCorner, imageSize);
+
+ TestImageType::Pointer image = TestImageType::New();
+ image->SetRegions(imageRegion);
+ image->Allocate();
+
+ itk::ImageScanlineIterator<TestImageType> createImageIterator(image,imageRegion);
+
+ // Set all pixels with first index == 0 to 0, and set the rest of the image to 255
+ while(!createImageIterator.IsAtEnd())
+ {
+ while(!createImageIterator.IsAtEndOfLine())
+ {
+ if(createImageIterator.GetIndex()[0] == 0)
+ {
+ createImageIterator.Set(0);
+ }
+ else
+ {
+ createImageIterator.Set(255);
+ }
+ ++createImageIterator;
+ }
+ createImageIterator.NextLine();
+ }
+
+ // Setup and iterate over the first region
+ TestImageType::IndexType region1Start;
+ region1Start.Fill(0);
+
+ TestImageType::SizeType regionSize;
+ regionSize.Fill(2);
+
+ TestImageType::RegionType region1(region1Start, regionSize);
+
+ itk::ImageScanlineConstIterator<TestImageType> imageIterator(image,region1);
+
+ std::vector<int> expectedValuesRegion1(4);
+ expectedValuesRegion1[0] = 0;
+ expectedValuesRegion1[1] = 255;
+ expectedValuesRegion1[2] = 0;
+ expectedValuesRegion1[3] = 255;
+ unsigned int counter = 0;
+ while(!imageIterator.IsAtEnd())
+ {
+ while(!imageIterator.IsAtEndOfLine())
+ {
+ if(imageIterator.Get() != expectedValuesRegion1[counter])
+ {
+ status = EXIT_FAILURE; // Fail
+ }
+ counter++;
+ ++imageIterator;
+ }
+ imageIterator.NextLine();
+ }
+
+ // Change iteration region
+ TestImageType::IndexType region2start;
+ region2start.Fill(1);
+
+ TestImageType::RegionType region2(region2start, regionSize);
+
+ imageIterator.SetRegion(region2);
+ imageIterator.GoToBegin();
+
+ std::vector<int> expectedValuesRegion2(4);
+ expectedValuesRegion2[0] = 255;
+ expectedValuesRegion2[1] = 255;
+ expectedValuesRegion2[2] = 255;
+ expectedValuesRegion2[3] = 255;
+ counter = 0;
+ while(!imageIterator.IsAtEnd())
+ {
+ while(!imageIterator.IsAtEndOfLine())
+ {
+ if(imageIterator.Get() != expectedValuesRegion2[counter])
+ {
+ status = EXIT_FAILURE; // Fail
+ }
+ counter++;
+ ++imageIterator;
+ }
+ imageIterator.NextLine();
+ }
+
+ } // end "Change Region" test
+ if (status == EXIT_SUCCESS)
+ std::cout << "All scanline iterator tests successful!\n";
+ else
+ std::cout << "Some scanline iterator tests failed!\n";
+ return status;
+}
diff --git a/Testing/Logic/itkIteratorTests.cxx b/Testing/Logic/itkIteratorTests.cxx
new file mode 100644
index 0000000..847c860
--- /dev/null
+++ b/Testing/Logic/itkIteratorTests.cxx
@@ -0,0 +1,124 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+
+#include <iostream>
+
+#include "itkImage.h"
+#include "itkVector.h"
+#include "RLEImageRegionIterator.h"
+#include <time.h>
+
+int itkIteratorTests(int, char* [] )
+{
+ std::cout << "Creating an image" << std::endl;
+ typedef RLEImage<unsigned short> ScalarImage;
+ ScalarImage::Pointer o3 = ScalarImage::New();
+
+ double origin3D[3] = { 5, 2.1, 8.1};
+ double spacing3D[3] = { 1.5, 2.1, 1};
+
+ ScalarImage::SizeType imageSize3D = {{ 100, 100, 100 }};
+ ScalarImage::SizeType bufferSize3D = {{ 100, 100, 100 }};
+ ScalarImage::SizeType regionSize3D = {{ 91, 93, 87 }};
+
+ ScalarImage::IndexType startIndex3D = {{0, 0, 0}};
+ ScalarImage::IndexType bufferStartIndex3D = {{0, 0, 0}};
+ ScalarImage::IndexType regionStartIndex3D = {{5,5, 5}};
+
+
+ ScalarImage::RegionType region;
+ region.SetSize(imageSize3D);
+ region.SetIndex(startIndex3D);
+ o3->SetLargestPossibleRegion( region );
+ region.SetSize(bufferSize3D);
+ region.SetIndex(bufferStartIndex3D);
+ o3->SetBufferedRegion( region );
+ region.SetSize(regionSize3D);
+ region.SetIndex(regionStartIndex3D);
+ o3->SetRequestedRegion( region );
+
+ o3->SetOrigin(origin3D);
+ o3->SetSpacing(spacing3D);
+
+ o3->Allocate();
+
+ // extra variables
+ double elapsedTime;
+ clock_t start, end;
+ unsigned long num = regionSize3D[0] * regionSize3D[1] * regionSize3D[2];
+ unsigned long i;
+ bool passed = true;
+
+ // ImageRegionIterator
+ start = clock();
+ itk::ImageRegionIterator<ScalarImage> it(o3, region);
+
+ unsigned short scalar;
+ scalar = 5;
+
+ i = 0;
+ for (; !it.IsAtEnd(); ++it)
+ {
+ it.Set( scalar );
+ ++i;
+ }
+ end = clock();
+ elapsedTime = (end - start) / (double) CLOCKS_PER_SEC;
+
+ std::cout << "ImageRegionIterator" << std::endl;
+ std::cout << "\tTime = " << elapsedTime << std::endl;
+ std::cout << "\tPixels = " << i << std::endl;
+
+ if (i != num)
+ {
+ passed = false;
+ }
+
+ // ImageRegionIteratorWithIndex
+ start = clock();
+ itk::ImageRegionIteratorWithIndex<ScalarImage> it2(o3, region);
+
+ i = 0;
+ for (; !it2.IsAtEnd(); ++it2)
+ {
+ it2.Set( scalar );
+ ++i;
+ }
+ end = clock();
+ elapsedTime = (end - start) / (double) CLOCKS_PER_SEC;
+
+ std::cout << "ImageRegionIteratorWithIndex" << std::endl;
+ std::cout << "\tTime = " << elapsedTime << std::endl;
+ std::cout << "\tPixels = " << i << std::endl;
+
+ if (i != num)
+ {
+ passed = false;
+ }
+
+ if (passed)
+ {
+ std::cout << "Iterator tests passed" << std::endl;
+ return EXIT_SUCCESS;
+ }
+ else
+ {
+ std::cout << "Iterator tests failed" << std::endl;
+ return EXIT_FAILURE;
+ }
+}
diff --git a/Testing/Logic/itkRegionOfInterestImageFilterTest.cxx b/Testing/Logic/itkRegionOfInterestImageFilterTest.cxx
new file mode 100644
index 0000000..7f3a7e2
--- /dev/null
+++ b/Testing/Logic/itkRegionOfInterestImageFilterTest.cxx
@@ -0,0 +1,144 @@
+/*=========================================================================
+ *
+ * Copyright Insight Software Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * 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.
+ *
+ *=========================================================================*/
+
+#include <iostream>
+#include "RLERegionOfInterestImageFilter.h"
+#include "RLEImageRegionIterator.h"
+#include "itkSimpleFilterWatcher.h"
+
+int itkRegionOfInterestImageFilterTest(int, char* [] )
+{
+
+ const unsigned int Dimension = 3;
+ typedef itk::Index<Dimension> PixelType;
+
+ typedef RLEImage< PixelType > ImageType;
+
+ typedef itk::RegionOfInterestImageFilter<
+ ImageType,
+ ImageType > FilterType;
+
+
+ typedef ImageType::RegionType RegionType;
+ typedef ImageType::SizeType SizeType;
+ typedef ImageType::IndexType IndexType;
+ typedef ImageType::DirectionType DirectionType;
+
+ typedef itk::ImageRegionIterator<
+ ImageType > IteratorType;
+
+ FilterType::Pointer filter = FilterType::New();
+
+
+ ImageType::Pointer image = ImageType::New();
+
+ IndexType start;
+ start.Fill( 0 );
+
+ SizeType size;
+ size[0] = 40;
+ size[1] = 40;
+ size[2] = 40;
+
+ RegionType region;
+ region.SetIndex( start );
+ region.SetSize( size );
+
+ image->SetRegions( region );
+ image->Allocate();
+
+ DirectionType directions;
+ directions.SetIdentity();
+ directions[0][0] = 0.0;
+ directions[1][0] = 1.0;
+ directions[2][0] = 0.0;
+ directions[0][1] = 1.0;
+ directions[1][1] = 0.0;
+ directions[2][1] = 0.0;
+ image->SetDirection (directions);
+
+ // Fill the image pixels with their own index.
+ IteratorType intr( image, region );
+ intr.GoToBegin();
+ while( !intr.IsAtEnd() )
+ {
+ intr.Set( intr.GetIndex() );
+ ++intr;
+ }
+
+
+ filter->SetInput( image );
+
+ SizeType roiSize;
+ roiSize[0] = 20;
+ roiSize[1] = 20;
+ roiSize[2] = 20;
+
+ IndexType roiStart;
+ roiStart[0] = 9;
+ roiStart[1] = 9;
+ roiStart[2] = 9;
+
+ RegionType regionOfInterest;
+ regionOfInterest.SetIndex( roiStart );
+ regionOfInterest.SetSize( roiSize );
+
+ //itk::SimpleFilterWatcher watcher(filter);
+ filter->SetRegionOfInterest( regionOfInterest );
+
+ //filter->SetNumberOfThreads(1);
+ filter->Update();
+ filter->GetOutput()->Print(std::cout);
+
+
+ IteratorType ot( filter->GetOutput(),
+ filter->GetOutput()->GetLargestPossibleRegion() );
+
+ IteratorType it( image, regionOfInterest );
+
+ it.GoToBegin();
+ ot.GoToBegin();
+
+ bool passed = true;
+ while( !it.IsAtEnd() )
+ {
+ IndexType inIndex = it.Get();
+ IndexType outIndex = ot.Get();
+ if( inIndex[0] != outIndex[0] ||
+ inIndex[1] != outIndex[1] ||
+ inIndex[2] != outIndex[2] )
+ {
+ std::cerr << "Test failed at pixel " << inIndex << std::endl;
+ std::cerr << "pixel value is " << outIndex << std::endl;
+ passed = false;
+ break;
+ }
+
+ ++it;
+ ++ot;
+ }
+
+ if( !passed )
+ {
+ return EXIT_FAILURE;
+ }
+
+ std::cout << "Test PASSED !" << std::endl;
+ return EXIT_SUCCESS;
+
+}
diff --git a/Testing/Logic/testRLE.cxx b/Testing/Logic/testRLE.cxx
new file mode 100644
index 0000000..7c32c9e
--- /dev/null
+++ b/Testing/Logic/testRLE.cxx
@@ -0,0 +1,145 @@
+#include "RLEImageRegionIterator.h"
+#include "RLERegionOfInterestImageFilter.h"
+#include <iostream>
+#include <string>
+#include <itkImageFileReader.h>
+#include <itkImageFileWriter.h>
+#include <itkTimeProbe.h>
+#include "IRISSlicer.h"
+#include "itkTestingComparisonImageFilter.h"
+
+//using namespace std;
+
+typedef itk::Image<short, 3> Seg3DImageType;
+typedef itk::Image<short, 2> Seg2DImageType;
+
+typedef RLEImage<short> shortRLEImage;
+typedef itk::RegionOfInterestImageFilter<shortRLEImage, shortRLEImage> roiType;
+
+Seg3DImageType::Pointer loadImage(const std::string filename)
+{
+ typedef itk::ImageFileReader<Seg3DImageType> SegReaderType;
+ SegReaderType::Pointer sr = SegReaderType::New();
+ sr->SetFileName(filename);
+ sr->Update();
+ return sr->GetOutput();
+}
+
+void writeImage(Seg2DImageType::Pointer image, const std::string filename, bool compress = true)
+{
+ typedef itk::ImageFileWriter<Seg2DImageType> SegWriterType;
+ SegWriterType::Pointer sw = SegWriterType::New();
+ sw->SetInput(image);
+ sw->SetFileName(filename);
+ sw->SetUseCompression(compress);
+ sw->Update();
+}
+
+void writeImage(Seg3DImageType::Pointer image, const std::string filename, bool compress = true)
+{
+ typedef itk::ImageFileWriter<Seg3DImageType> SegWriterType;
+ SegWriterType::Pointer sw = SegWriterType::New();
+ sw->SetInput(image);
+ sw->SetFileName(filename);
+ sw->SetUseCompression(compress);
+ sw->Update();
+}
+
+inline char axisToLetter(unsigned axis)
+{
+ if (axis == 0)
+ return 'X';
+ else if (axis == 1)
+ return 'Y';
+ else if (axis == 2)
+ return 'Z';
+ else
+ return '?';
+}
+
+//invokes IRISSlicer<itk> and IRISSlicer<rle> and compares results
+void testIRISSlicer(shortRLEImage::Pointer rleImage, Seg3DImageType::Pointer itkImage,
+ unsigned sliceIndex, unsigned sliceAxis, unsigned lineAxis, unsigned pixelAxis,
+ bool lineForward, bool pixelForward)
+{
+ itk::TimeProbe tp;
+ std::cout << "Axes (slice/line/pixel): " << axisToLetter(sliceAxis) << '/'
+ << axisToLetter(lineAxis) << '/' << axisToLetter(pixelAxis) << std::endl;
+ std::cout << "Slice index: " << sliceIndex << ". Line forward: " << lineForward
+ << ". Pixel forward: " << pixelForward << std::endl;;
+
+ std::cout << "IRISSlicer<rle>: "; tp.Start();
+ typedef IRISSlicer<shortRLEImage, Seg2DImageType, shortRLEImage> slicerTypeRLE;
+ slicerTypeRLE::Pointer roiRLE = slicerTypeRLE::New();
+ roiRLE->SetInput(rleImage);
+ roiRLE->SetSliceIndex(sliceIndex);
+ roiRLE->SetSliceDirectionImageAxis(sliceAxis);
+ roiRLE->SetLineDirectionImageAxis(lineAxis);
+ roiRLE->SetPixelDirectionImageAxis(pixelAxis);
+ roiRLE->SetLineTraverseForward(lineForward);
+ roiRLE->SetPixelTraverseForward(pixelForward);
+ roiRLE->Update();
+ Seg2DImageType::Pointer sliceRLE = roiRLE->GetOutput();
+ tp.Stop(); std::cout << tp.GetMean() * 1000 << " ms " << std::endl; tp.Reset();
+
+ std::cout << "IRISSlicer<itk>: "; tp.Start();
+ typedef IRISSlicer<Seg3DImageType, Seg2DImageType, Seg3DImageType> slicerTypeITK;
+ slicerTypeITK::Pointer roiITK = slicerTypeITK::New();
+ roiITK->SetInput(itkImage);
+ roiITK->SetSliceIndex(sliceIndex);
+ roiITK->SetSliceDirectionImageAxis(sliceAxis);
+ roiITK->SetLineDirectionImageAxis(lineAxis);
+ roiITK->SetPixelDirectionImageAxis(pixelAxis);
+ roiITK->SetLineTraverseForward(lineForward);
+ roiITK->SetPixelTraverseForward(pixelForward);
+ roiITK->Update();
+ Seg2DImageType::Pointer sliceITK = roiITK->GetOutput();
+ tp.Stop(); std::cout << tp.GetMean() * 1000 << " ms " << std::endl; tp.Reset();
+
+ // Now compare the two images
+ typedef itk::Testing::ComparisonImageFilter< Seg2DImageType, Seg2DImageType > DiffType;
+ DiffType::Pointer diff = DiffType::New();
+ diff->SetValidInput(sliceITK);
+ diff->SetTestInput(sliceRLE);
+ diff->UpdateLargestPossibleRegion();
+ std::cout << "Number of pixels with difference: " <<
+ diff->GetNumberOfPixelsWithDifferences() << std::endl << std::endl;
+}
+
+//test all 4 combinations of bool parameters (lineForward and pixelForward)
+void test4bools(shortRLEImage::Pointer rleImage, Seg3DImageType::Pointer itkImage,
+ unsigned sliceIndex, unsigned sliceAxis, unsigned lineAxis, unsigned pixelAxis)
+{
+ testIRISSlicer(rleImage, itkImage, sliceIndex, sliceAxis, lineAxis, pixelAxis, true, true);
+ testIRISSlicer(rleImage, itkImage, sliceIndex, sliceAxis, lineAxis, pixelAxis, true, false);
+ testIRISSlicer(rleImage, itkImage, sliceIndex, sliceAxis, lineAxis, pixelAxis, false, true);
+ testIRISSlicer(rleImage, itkImage, sliceIndex, sliceAxis, lineAxis, pixelAxis, false, false);
+}
+
+int main(int argc, char* argv[])
+{
+ itk::TimeProbe tp;
+ std::cout << "Loading image: "; tp.Start();
+ Seg3DImageType::Pointer inImage = loadImage(argv[1]);
+ tp.Stop(); std::cout << tp.GetMean() * 1000 << " ms " << std::endl; tp.Reset();
+
+ std::cout << "itk->RLE conversion: "; tp.Start();
+ shortRLEImage::Pointer test = shortRLEImage::New();
+ typedef itk::RegionOfInterestImageFilter<Seg3DImageType, shortRLEImage> inConverterType;
+ inConverterType::Pointer inConv = inConverterType::New();
+ inConv->SetInput(inImage);
+ inConv->SetRegionOfInterest(inImage->GetLargestPossibleRegion());
+ inConv->Update();
+ test = inConv->GetOutput();
+ tp.Stop(); std::cout << tp.GetMean() * 1000 << " ms " << std::endl; tp.Reset();
+
+ //Test all 6 permutations of axes
+ test4bools(test, inImage, inImage->GetBufferedRegion().GetSize(2) / 2, 2, 1, 0);
+ test4bools(test, inImage, inImage->GetBufferedRegion().GetSize(2) / 2, 2, 0, 1);
+ test4bools(test, inImage, inImage->GetBufferedRegion().GetSize(1) / 2, 1, 2, 0);
+ test4bools(test, inImage, inImage->GetBufferedRegion().GetSize(1) / 2, 1, 0, 2);
+ test4bools(test, inImage, inImage->GetBufferedRegion().GetSize(0) / 2, 0, 2, 1);
+ test4bools(test, inImage, inImage->GetBufferedRegion().GetSize(0) / 2, 0, 1, 2);
+ std::cout << "All tests finished!";
+ getchar();
+}
diff --git a/Testing/TestData/X300.mha b/Testing/TestData/X300.mha
new file mode 100644
index 0000000..649b830
Binary files /dev/null and b/Testing/TestData/X300.mha differ
diff --git a/Testing/TestData/X39.nii.gz b/Testing/TestData/X39.nii.gz
new file mode 100644
index 0000000..c9cdfb4
Binary files /dev/null and b/Testing/TestData/X39.nii.gz differ
diff --git a/Testing/TestData/Y300.mha b/Testing/TestData/Y300.mha
new file mode 100644
index 0000000..229594a
Binary files /dev/null and b/Testing/TestData/Y300.mha differ
diff --git a/Testing/TestData/Y55.nii.gz b/Testing/TestData/Y55.nii.gz
new file mode 100644
index 0000000..dbac0a8
Binary files /dev/null and b/Testing/TestData/Y55.nii.gz differ
diff --git a/Testing/TestData/Z150.mha b/Testing/TestData/Z150.mha
new file mode 100644
index 0000000..13862c1
Binary files /dev/null and b/Testing/TestData/Z150.mha differ
diff --git a/Testing/TestData/Z32.nii.gz b/Testing/TestData/Z32.nii.gz
new file mode 100644
index 0000000..ca4d233
Binary files /dev/null and b/Testing/TestData/Z32.nii.gz differ
diff --git a/Testing/TestData/diffspace.itksnap b/Testing/TestData/diffspace.itksnap
new file mode 100644
index 0000000..1a4d8b4
--- /dev/null
+++ b/Testing/TestData/diffspace.itksnap
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--ITK-SNAP (itksnap.org) Project File
+
+This file can be moved/copied along with the images that it references
+as long as the relative location of the images to the project file is
+the same. Do not modify the SaveLocation entry, or this will not work.
+-->
+<!DOCTYPE registry [
+<!ELEMENT registry (entry*,folder*)>
+<!ELEMENT folder (entry*,folder*)>
+<!ELEMENT entry EMPTY>
+<!ATTLIST folder key CDATA #REQUIRED>
+<!ATTLIST entry key CDATA #REQUIRED>
+<!ATTLIST entry value CDATA #REQUIRED>
+]>
+<registry>
+ <entry key="SaveLocation" value="/Users/pauly/tk/snapdata/7t" />
+ <entry key="Version" value="20151215" />
+ <folder key="Annotations" >
+ <entry key="Format" value="ITK-SNAP Annotation File" />
+ <entry key="FormatDate" value="20150624" />
+ <folder key="Annotations" >
+ <entry key="ArraySize" value="0" />
+ </folder>
+ </folder>
+ <folder key="Layers" >
+ <folder key="Layer[000]" >
+ <entry key="AbsolutePath" value="/Users/pauly/tk/snapdata/7t/t1_chunk.nii.gz" />
+ <entry key="Role" value="MainRole" />
+ <folder key="LayerMetaData" >
+ <entry key="Alpha" value="255" />
+ <entry key="CustomNickName" value="" />
+ <entry key="Sticky" value="0" />
+ <folder key="DisplayMapping" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.211209" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="0.422417" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ <folder key="ProjectMetaData" >
+ <entry key="GaussianBlurScale" value="1" />
+ <entry key="RemappingExponent" value="3" />
+ <entry key="RemappingSteepness" value="0.04" />
+ <folder key="Files" >
+ <folder key="Grey" >
+ <entry key="Dimensions" value="43 54 32" />
+ <entry key="Orientation" value="RPI" />
+ </folder>
+ </folder>
+ <folder key="IOHistory" >
+ <folder key="AnatomicImage" >
+ <entry key="ArraySize" value="3" />
+ <entry key="Element[0]" value="/Users/pauly/tk/snapdata/7t/t2_chunk.nii.gz" />
+ <entry key="Element[1]" value="/Users/pauly/tk/snapdata/7t/py01_t2star.nii.gz" />
+ <entry key="Element[2]" value="/Users/pauly/tk/snapdata/7t/multi_chunk.nii.gz" />
+ </folder>
+ <folder key="Project" >
+ <entry key="ArraySize" value="0" />
+ </folder>
+ </folder>
+ <folder key="IRIS" >
+ <entry key="SliceViewLayerLayout" value="Tiled" />
+ <folder key="BoundingBox" >
+ <entry key="InterpolationMethod" value="Nearest" />
+ <entry key="ResampleDimensions" value="43 54 32" />
+ <entry key="SeedWithCurrentSegmentation" value="0" />
+ <folder key="ROIBox[0]" >
+ <entry key="Index" value="0" />
+ <entry key="Size" value="43" />
+ </folder>
+ <folder key="ROIBox[1]" >
+ <entry key="Index" value="0" />
+ <entry key="Size" value="54" />
+ </folder>
+ <folder key="ROIBox[2]" >
+ <entry key="Index" value="0" />
+ <entry key="Size" value="32" />
+ </folder>
+ </folder>
+ <folder key="DisplayMapping" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.211209" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="0.422417" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="LabelState" >
+ <entry key="CoverageMode" value="OverAll" />
+ <entry key="DrawingLabel" value="1" />
+ <entry key="OverwriteLabel" value="0" />
+ <entry key="PolygonInvert" value="0" />
+ <entry key="SegmentationAlpha" value="0.5" />
+ </folder>
+ <folder key="LabelTable" >
+ <entry key="NumberOfElements" value="6" />
+ <folder key="Element[0]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="255 0 0" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="1" />
+ <entry key="Label" value="Label 1" />
+ </folder>
+ <folder key="Element[1]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="0 255 0" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="2" />
+ <entry key="Label" value="Label 2" />
+ </folder>
+ <folder key="Element[2]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="0 0 255" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="3" />
+ <entry key="Label" value="Label 3" />
+ </folder>
+ <folder key="Element[3]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="255 255 0" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="4" />
+ <entry key="Label" value="Label 4" />
+ </folder>
+ <folder key="Element[4]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="0 255 255" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="5" />
+ <entry key="Label" value="Label 5" />
+ </folder>
+ <folder key="Element[5]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="255 0 255" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="6" />
+ <entry key="Label" value="Label 6" />
+ </folder>
+ </folder>
+ <folder key="MeshOptions" >
+ <entry key="DecimateFeatureAngle" value="45" />
+ <entry key="DecimateMaximumError" value="0.002" />
+ <entry key="DecimatePreserveTopology" value="1" />
+ <entry key="DecimateTargetReduction" value="0.95" />
+ <entry key="GaussianError" value="0.04" />
+ <entry key="GaussianStandardDeviation" value="0.8" />
+ <entry key="MeshSmoothingBoundarySmoothing" value="0" />
+ <entry key="MeshSmoothingConvergence" value="0" />
+ <entry key="MeshSmoothingFeatureAngle" value="45" />
+ <entry key="MeshSmoothingFeatureEdgeSmoothing" value="0" />
+ <entry key="MeshSmoothingIterations" value="200" />
+ <entry key="MeshSmoothingRelaxationFactor" value="0.01" />
+ <entry key="UseDecimation" value="0" />
+ <entry key="UseGaussianSmoothing" value="0" />
+ <entry key="UseMeshSmoothing" value="0" />
+ </folder>
+ </folder>
+ <folder key="SNAP" >
+ <folder key="SnakeParameters" >
+ <entry key="AdvectionSpeedExponent" value="0" />
+ <entry key="AdvectionWeight" value="0" />
+ <entry key="AutomaticTimeStep" value="1" />
+ <entry key="Clamp" value="1" />
+ <entry key="CurvatureSpeedExponent" value="-1" />
+ <entry key="CurvatureWeight" value="0.2" />
+ <entry key="Ground" value="5" />
+ <entry key="LaplacianSpeedExponent" value="0" />
+ <entry key="LaplacianWeight" value="0" />
+ <entry key="PropagationWeight" value="1" />
+ <entry key="SnakeType" value="RegionCompetition" />
+ <entry key="SolverAlgorithm" value="ParallelSparseField" />
+ <entry key="TimeStepFactor" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Layer[001]" >
+ <entry key="AbsolutePath" value="/Users/pauly/tk/snapdata/7t/t2_chunk.nii.gz" />
+ <entry key="Role" value="OverlayRole" />
+ <folder key="LayerMetaData" >
+ <entry key="Alpha" value="0.5" />
+ <entry key="CustomNickName" value="" />
+ <entry key="Sticky" value="0" />
+ <folder key="DisplayMapping" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.362159" />
+ <entry key="xValue" value="0.769688" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="0.897436" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Layer[002]" >
+ <entry key="AbsolutePath" value="/Users/pauly/tk/snapdata/7t/multi_chunk.nii.gz" />
+ <entry key="Role" value="OverlayRole" />
+ <folder key="LayerMetaData" >
+ <entry key="Alpha" value="0.5" />
+ <entry key="CustomNickName" value="" />
+ <entry key="Sticky" value="0" />
+ <folder key="DisplayMapping" >
+ <entry key="SelectedComponent" value="1" />
+ <entry key="SelectedScalarRep" value="Component" />
+ <entry key="UseRGB" value="0" />
+ <folder key="Average" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Black to green" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Component" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Black to green" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0.672489" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.865316" />
+ <entry key="xValue" value="0.535938" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="0.958519" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Magnitude" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Black to green" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Maximum" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Black to green" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+</registry>
diff --git a/Testing/TestData/fixed_image.nii.gz b/Testing/TestData/fixed_image.nii.gz
new file mode 100644
index 0000000..2402095
Binary files /dev/null and b/Testing/TestData/fixed_image.nii.gz differ
diff --git a/Testing/TestData/moving_image.nii.gz b/Testing/TestData/moving_image.nii.gz
new file mode 100644
index 0000000..50f769b
Binary files /dev/null and b/Testing/TestData/moving_image.nii.gz differ
diff --git a/Testing/TestData/multi_chunk.nii.gz b/Testing/TestData/multi_chunk.nii.gz
new file mode 100644
index 0000000..b56e18e
Binary files /dev/null and b/Testing/TestData/multi_chunk.nii.gz differ
diff --git a/Testing/TestData/reslice_image.nii.gz b/Testing/TestData/reslice_image.nii.gz
new file mode 100644
index 0000000..30225cf
Binary files /dev/null and b/Testing/TestData/reslice_image.nii.gz differ
diff --git a/Testing/TestData/t1_chunk.nii.gz b/Testing/TestData/t1_chunk.nii.gz
new file mode 100644
index 0000000..156eccd
Binary files /dev/null and b/Testing/TestData/t1_chunk.nii.gz differ
diff --git a/Testing/TestData/t2_chunk.nii.gz b/Testing/TestData/t2_chunk.nii.gz
new file mode 100644
index 0000000..9939824
Binary files /dev/null and b/Testing/TestData/t2_chunk.nii.gz differ
diff --git a/Testing/TestData/vb-seg.mha b/Testing/TestData/vb-seg.mha
new file mode 100644
index 0000000..e270a00
Binary files /dev/null and b/Testing/TestData/vb-seg.mha differ
diff --git a/Testing/TestData/warp_image.nii.gz b/Testing/TestData/warp_image.nii.gz
new file mode 100644
index 0000000..43e9ee4
Binary files /dev/null and b/Testing/TestData/warp_image.nii.gz differ
diff --git a/Testing/TestData/warpfield.itksnap b/Testing/TestData/warpfield.itksnap
new file mode 100644
index 0000000..88f632d
--- /dev/null
+++ b/Testing/TestData/warpfield.itksnap
@@ -0,0 +1,349 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--ITK-SNAP (itksnap.org) Project File
+
+This file can be moved/copied along with the images that it references
+as long as the relative location of the images to the project file is
+the same. Do not modify the SaveLocation entry, or this will not work.
+-->
+<!DOCTYPE registry [
+<!ELEMENT registry (entry*,folder*)>
+<!ELEMENT folder (entry*,folder*)>
+<!ELEMENT entry EMPTY>
+<!ATTLIST folder key CDATA #REQUIRED>
+<!ATTLIST entry key CDATA #REQUIRED>
+<!ATTLIST entry value CDATA #REQUIRED>
+]>
+<registry>
+ <entry key="SaveLocation" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData" />
+ <entry key="Version" value="20170224" />
+ <folder key="Annotations" >
+ <entry key="Format" value="ITK-SNAP Annotation File" />
+ <entry key="FormatDate" value="20150624" />
+ <folder key="Annotations" >
+ <entry key="ArraySize" value="0" />
+ </folder>
+ </folder>
+ <folder key="Layers" >
+ <folder key="Layer[000]" >
+ <entry key="AbsolutePath" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData/fixed_image.nii.gz" />
+ <entry key="Role" value="MainRole" />
+ <folder key="IOHints" >
+ <entry key="Format" value="NiFTI" />
+ </folder>
+ <folder key="LayerMetaData" >
+ <entry key="Alpha" value="255" />
+ <entry key="CustomNickName" value="" />
+ <entry key="Sticky" value="0" />
+ <folder key="DisplayMapping" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ <folder key="ProjectMetaData" >
+ <entry key="GaussianBlurScale" value="1" />
+ <entry key="RemappingExponent" value="3" />
+ <entry key="RemappingSteepness" value="0.04" />
+ <folder key="Files" >
+ <folder key="Grey" >
+ <entry key="Dimensions" value="36 36 20" />
+ <entry key="Orientation" value="RPI" />
+ </folder>
+ </folder>
+ <folder key="IOHistory" >
+ <folder key="AnatomicImage" >
+ <entry key="ArraySize" value="3" />
+ <entry key="Element[0]" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData/reslice_image.nii.gz" />
+ <entry key="Element[1]" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData/moving_image.nii.gz" />
+ <entry key="Element[2]" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData/warp_image.nii.gz" />
+ </folder>
+ <folder key="LabelImage" >
+ <entry key="ArraySize" value="1" />
+ <entry key="Element[0]" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData/moving_image.nii.gz" />
+ </folder>
+ <folder key="Project" >
+ <entry key="ArraySize" value="0" />
+ </folder>
+ </folder>
+ <folder key="IRIS" >
+ <entry key="SliceViewLayerLayout" value="Tiled" />
+ <folder key="BoundingBox" >
+ <entry key="InterpolationMethod" value="Nearest" />
+ <entry key="ResampleDimensions" value="36 36 20" />
+ <entry key="SeedWithCurrentSegmentation" value="0" />
+ <folder key="ROIBox[0]" >
+ <entry key="Index" value="0" />
+ <entry key="Size" value="36" />
+ </folder>
+ <folder key="ROIBox[1]" >
+ <entry key="Index" value="0" />
+ <entry key="Size" value="36" />
+ </folder>
+ <folder key="ROIBox[2]" >
+ <entry key="Index" value="0" />
+ <entry key="Size" value="20" />
+ </folder>
+ </folder>
+ <folder key="DisplayMapping" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="LabelState" >
+ <entry key="CoverageMode" value="OverAll" />
+ <entry key="DrawingLabel" value="1" />
+ <entry key="OverwriteLabel" value="0" />
+ <entry key="PolygonInvert" value="0" />
+ <entry key="SegmentationAlpha" value="0.5" />
+ </folder>
+ <folder key="LabelTable" >
+ <entry key="NumberOfElements" value="6" />
+ <folder key="Element[0]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="255 0 0" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="1" />
+ <entry key="Label" value="Label 1" />
+ </folder>
+ <folder key="Element[1]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="0 255 0" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="2" />
+ <entry key="Label" value="Label 2" />
+ </folder>
+ <folder key="Element[2]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="0 0 255" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="3" />
+ <entry key="Label" value="Label 3" />
+ </folder>
+ <folder key="Element[3]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="255 255 0" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="4" />
+ <entry key="Label" value="Label 4" />
+ </folder>
+ <folder key="Element[4]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="0 255 255" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="5" />
+ <entry key="Label" value="Label 5" />
+ </folder>
+ <folder key="Element[5]" >
+ <entry key="Alpha" value="255" />
+ <entry key="Color" value="255 0 255" />
+ <entry key="Flags" value="1 1" />
+ <entry key="Index" value="6" />
+ <entry key="Label" value="Label 6" />
+ </folder>
+ </folder>
+ <folder key="MeshOptions" >
+ <entry key="DecimateFeatureAngle" value="45" />
+ <entry key="DecimateMaximumError" value="0.002" />
+ <entry key="DecimatePreserveTopology" value="1" />
+ <entry key="DecimateTargetReduction" value="0.95" />
+ <entry key="GaussianError" value="0.04" />
+ <entry key="GaussianStandardDeviation" value="0.8" />
+ <entry key="MeshSmoothingBoundarySmoothing" value="0" />
+ <entry key="MeshSmoothingConvergence" value="0" />
+ <entry key="MeshSmoothingFeatureAngle" value="45" />
+ <entry key="MeshSmoothingFeatureEdgeSmoothing" value="0" />
+ <entry key="MeshSmoothingIterations" value="200" />
+ <entry key="MeshSmoothingRelaxationFactor" value="0.01" />
+ <entry key="UseDecimation" value="0" />
+ <entry key="UseGaussianSmoothing" value="0" />
+ <entry key="UseMeshSmoothing" value="0" />
+ </folder>
+ </folder>
+ <folder key="SNAP" >
+ <folder key="SnakeParameters" >
+ <entry key="AdvectionSpeedExponent" value="0" />
+ <entry key="AdvectionWeight" value="0" />
+ <entry key="AutomaticTimeStep" value="1" />
+ <entry key="Clamp" value="1" />
+ <entry key="CurvatureSpeedExponent" value="-1" />
+ <entry key="CurvatureWeight" value="0.2" />
+ <entry key="Ground" value="5" />
+ <entry key="LaplacianSpeedExponent" value="0" />
+ <entry key="LaplacianWeight" value="0" />
+ <entry key="PropagationSpeedExponent" value="1" />
+ <entry key="PropagationWeight" value="1" />
+ <entry key="SnakeType" value="RegionCompetition" />
+ <entry key="SolverAlgorithm" value="ParallelSparseField" />
+ <entry key="TimeStepFactor" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Layer[001]" >
+ <entry key="AbsolutePath" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData/moving_image.nii.gz" />
+ <entry key="Role" value="OverlayRole" />
+ <folder key="IOHints" >
+ <entry key="Format" value="NiFTI" />
+ </folder>
+ <folder key="ImageTransform" >
+ <entry key="IsIdentity" value="1" />
+ </folder>
+ <folder key="LayerMetaData" >
+ <entry key="Alpha" value="0.5" />
+ <entry key="CustomNickName" value="" />
+ <entry key="Sticky" value="0" />
+ <folder key="DisplayMapping" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Layer[002]" >
+ <entry key="AbsolutePath" value="/Users/pauly/tk/qtnsap/itksnap/Testing/TestData/warp_image.nii.gz" />
+ <entry key="Role" value="OverlayRole" />
+ <folder key="IOHints" >
+ <entry key="Format" value="NiFTI" />
+ </folder>
+ <folder key="ImageTransform" >
+ <entry key="IsIdentity" value="1" />
+ </folder>
+ <folder key="LayerMetaData" >
+ <entry key="Alpha" value="0.2" />
+ <entry key="CustomNickName" value="" />
+ <entry key="Sticky" value="0" />
+ <folder key="DisplayMapping" >
+ <entry key="RenderAsGrid" value="1" />
+ <entry key="SelectedComponent" value="0" />
+ <entry key="SelectedScalarRep" value="Component" />
+ <entry key="UseRGB" value="0" />
+ <folder key="Average" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Component" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Magnitude" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ <folder key="Maximum" >
+ <folder key="ColorMap" >
+ <entry key="Preset" value="Grayscale" />
+ </folder>
+ <folder key="Curve" >
+ <entry key="NumberOfControlPoints" value="3" />
+ <folder key="ControlPoint[0]" >
+ <entry key="tValue" value="0" />
+ <entry key="xValue" value="0" />
+ </folder>
+ <folder key="ControlPoint[1]" >
+ <entry key="tValue" value="0.5" />
+ <entry key="xValue" value="0.5" />
+ </folder>
+ <folder key="ControlPoint[2]" >
+ <entry key="tValue" value="1" />
+ <entry key="xValue" value="1" />
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+ </folder>
+</registry>
diff --git a/Utilities/MacOS/BundleResources/Info.plist b/Utilities/MacOS/BundleResources/Info.plist
index 12c30f7..e6f6653 100644
--- a/Utilities/MacOS/BundleResources/Info.plist
+++ b/Utilities/MacOS/BundleResources/Info.plist
@@ -109,7 +109,18 @@
<array>
<string>gz</string>
</array>
- </dict>
- </array>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>DICOM Image</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>dcm</string>
+ <string>*</string>
+ </array>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ </array>
</dict>
</plist>
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/itksnap.git
More information about the debian-med-commit
mailing list