Notice to GitKraken users: A vulnerability has been found in the SSH key generation of GitKraken versions 7.6.0 to 8.0.0 (https://www.gitkraken.com/blog/weak-ssh-key-fix). If you use GitKraken and have generated a SSH key using one of these versions, please remove it both from your local workstation and from your LRZ GitLab profile.

21.10.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit 84c27e4a authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'itk-segmentation-registration' into 'development'

Itk Segmentation and Registration: wrappers and demos

This branch makes use of the ItkReader and currently contains a SegmentationDemo and a RegistrationDemo that use Itk wrappers for segmentation and registration algorithms. It is meant to be a starting point for future Itk segmentation and registration algorithms that are to be wrapped. Commits have been cherry-picked from the mhaimagereader branch.
parents 45d731ff ab101dde
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitt Mnchen
// Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// 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
//
// 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 "itkregistration.h"
#include "tgt/glmath.h"
#include "tgt/logmanager.h"
#include "modules/itk/core/genericimagerepresentationitk.h"
#include <itkIntTypes.h>
#include <itkCastImageFilter.h>
#include <itkImageRegistrationMethod.h>
#include <itkMattesMutualInformationImageToImageMetric.h>
#include <itkResampleImageFilter.h>
#include <itkVersorRigid3DTransform.h>
#include <itkCenteredTransformInitializer.h>
#include <itkVersorRigid3DTransformOptimizer.h>
#include <itkRescaleIntensityImageFilter.h>
#include "core/datastructures/imagedata.h"
#include "core/datastructures/genericimagerepresentationlocal.h"
// In this class we want to use various ITK registration methods.
/**
* Executes the specified registration on the data.
* \param MA_baseType base type of input images
* \param MA_returnType base type of output image
* \param MA_numChannels number of channels of input image
* \param MA_dimensionality dimensionality of images
* \param MA_registrationType type name of the ITK registration to use
* \param MA_transformationType type name of the ITK transformation to use (within itk:: namespace)
* \param MD_registrationBody additional stuff to execute between registration definition and execution
*/
#define PERFORM_ITK_REGISTRATION(MA_baseType, MA_returnType, MA_numChannels, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
{ \
GenericImageRepresentationItk<MA_baseType, MA_numChannels, MA_dimensionality>::ScopedRepresentation itkRepFixed(data, p_sourceImageIDFixed.getValue()); \
GenericImageRepresentationItk<MA_baseType, MA_numChannels, MA_dimensionality>::ScopedRepresentation itkRepMoving(data, p_sourceImageIDMoving.getValue()); \
if ((MA_dimensionality == 3) && ( itkRepFixed != 0) && (itkRepMoving != 0)) { \
typedef itk::VersorRigid3DTransformOptimizer OptimizerType; \
typedef GenericImageRepresentationItk<MA_baseType, MA_numChannels, MA_dimensionality>::ItkImageType InputImageType; \
typedef GenericImageRepresentationItk<MA_returnType, MA_numChannels, MA_dimensionality>::ItkImageType OutputImageType; \
typedef itk::LinearInterpolateImageFunction<InputImageType, double> InterpolatorType; \
typedef itk::ImageRegistrationMethod<InputImageType, InputImageType> RegistrationType; \
typedef itk::MA_registrationType<InputImageType, InputImageType> MetricType; \
typedef itk::ResampleImageFilter<InputImageType, InputImageType> ResampleFilterType; \
typedef itk::MA_transformationType<double> TransformType; \
TransformType::Pointer transform = TransformType::New(); \
OptimizerType::Pointer optimizer = OptimizerType::New(); \
InterpolatorType::Pointer interpolator = InterpolatorType::New(); \
RegistrationType::Pointer registration = RegistrationType::New(); \
MetricType::Pointer metric = MetricType::New(); \
\
registration->SetOptimizer(optimizer); \
registration->SetTransform(transform); \
registration->SetInterpolator(interpolator); \
registration->SetMetric(metric); \
MD_registrationBody \
registration->SetFixedImage(itkRepFixed->getItkImage()); \
registration->SetMovingImage(itkRepMoving->getItkImage()); \
registration->SetFixedImageRegion(itkRepFixed->getItkImage()->GetBufferedRegion()); \
typedef itk::CenteredTransformInitializer<TransformType, InputImageType, InputImageType> TransformInitializerType; \
TransformInitializerType::Pointer initializer = TransformInitializerType::New(); \
initializer->SetTransform(transform); \
initializer->SetFixedImage(itkRepFixed->getItkImage()); \
initializer->SetMovingImage(itkRepMoving->getItkImage()); \
initializer->MomentsOn(); \
initializer->InitializeTransform(); \
typedef TransformType::VersorType VersorType; \
typedef VersorType::VectorType VectorType; \
VersorType rotation; \
VectorType axis; \
axis[0] = 0.0; \
axis[1] = 0.0; \
axis[2] = 1.0; \
const double angle = 0; \
rotation.Set(axis, angle); \
transform->SetRotation(rotation); \
registration->SetInitialTransformParameters(transform->GetParameters()); \
\
typedef OptimizerType::ScalesType OptimizerScalesType; \
OptimizerScalesType optimizerScales(transform->GetNumberOfParameters()); \
optimizer->MinimizeOn(); \
const double translationScale = 1.0 / 1000.0; \
optimizerScales[0] = 1.0; \
optimizerScales[1] = 1.0; \
optimizerScales[2] = 1.0; \
optimizerScales[3] = translationScale; \
optimizerScales[4] = translationScale; \
optimizerScales[5] = translationScale; \
optimizer->SetScales(optimizerScales); \
optimizer->SetMaximumStepLength(0.2000); \
optimizer->SetMinimumStepLength(0.0001); \
optimizer->SetNumberOfIterations(200); \
registration->Update(); \
\
OptimizerType::ParametersType finalParameters = registration->GetLastTransformParameters(); \
transform->SetParameters(finalParameters); \
TransformType::Pointer finalTransform = TransformType::New(); \
finalTransform->SetCenter(transform->GetCenter()); \
finalTransform->SetParameters(finalParameters); \
finalTransform->SetFixedParameters(transform->GetFixedParameters()); \
\
ResampleFilterType::Pointer resample = ResampleFilterType::New(); \
resample->SetTransform(finalTransform); \
resample->SetInput(itkRepMoving->getItkImage()); \
MA_baseType defaultPixelValue = 0; \
resample->SetSize(itkRepFixed->getItkImage()->GetLargestPossibleRegion().GetSize()); \
resample->SetOutputOrigin(itkRepFixed->getItkImage()->GetOrigin()); \
resample->SetOutputSpacing(itkRepFixed->getItkImage()->GetSpacing()); \
resample->SetOutputDirection(itkRepFixed->getItkImage()->GetDirection()); \
resample->SetDefaultPixelValue(defaultPixelValue); \
\
itk::CastImageFilter<InputImageType, OutputImageType>::Pointer caster = itk::CastImageFilter<InputImageType, OutputImageType>::New(); \
caster->SetInput(resample->GetOutput()); \
caster->Update(); \
\
GenericImageRepresentationItk<MA_baseType, MA_numChannels, MA_dimensionality>::create(id, caster->GetOutput()); \
} \
}
#define DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, MA_baseType, MA_returnType, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
tgtAssert(MA_WTPF._numChannels == 1, "ItkRegistration only supports single-channel images.") \
PERFORM_ITK_REGISTRATION(MA_baseType, MA_returnType, 1, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody)
#define DISPATCH_ITK_REGISTRATION_D(MA_WTPF, MA_WTPM, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
switch (MA_WTPF._baseType) { \
case WeaklyTypedPointer::UINT8: \
DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, uint8_t, uint8_t, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
break; \
case WeaklyTypedPointer::INT8: \
DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, int8_t, int8_t, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
break; \
case WeaklyTypedPointer::UINT16: \
DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, uint16_t, uint16_t, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
break; \
case WeaklyTypedPointer::INT16: \
DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, int16_t, int16_t, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
break; \
case WeaklyTypedPointer::UINT32: \
DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, uint32_t, uint32_t, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
break; \
case WeaklyTypedPointer::INT32: \
DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, int32_t, int32_t, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
break; \
case WeaklyTypedPointer::FLOAT: \
DISPATCH_ITK_REGISTRATION_BRD(MA_WTPF, MA_WTPM, float, float, MA_dimensionality, MA_registrationType, MA_transformationType, MD_registrationBody) \
break; \
default: \
tgtAssert(false, "Should not reach this - wrong base type in WeaklyTypedPointer!"); \
} \
/**
* Dispatches the execution for the ITK registration based on transformation \a MA_transformationType
* and registration \a MA_RegistrationType for the images \a MA_localRepFixed and \a MA_localRepMoving.
* \param MA_localRepFixed local representation of the fixed image to be registered with the moving one
* \param MA_localRepMoving local representation of the moving image to be registered with the fixed one
* \param MA_registrationType type name of the ITK registration to use
* \param MA_transformationType type name of the ITK transformation to use (within itk:: namespace)
* \param MD_registrationBody additional stuff to execute between registration definition and execution
*/
#define DISPATCH_ITK_REGISTRATION(MA_localRepFixed, MA_localRepMoving, MA_registrationType, MA_transformationType, MD_registrationBody) \
do { \
WeaklyTypedPointer wtpf = MA_localRepFixed->getWeaklyTypedPointer(); \
WeaklyTypedPointer wtpm = MA_localRepMoving->getWeaklyTypedPointer(); \
switch (MA_localRepFixed->getDimensionality()) { \
case 3: DISPATCH_ITK_REGISTRATION_D(wtpf, wtpm, 3, MA_registrationType, MA_transformationType, MD_registrationBody) break; \
default: tgtAssert(false, "Unsupported dimensionality!"); break; \
} \
} while (0)
// ================================================================================================
// = Macros defined, let the party begin! =
// ================================================================================================
namespace campvis {
static const GenericOption<std::string> registrationTypes[1] = {
GenericOption<std::string>("MattesMIRigid3D", "Mattes Mutual Information Rigid 3D")
};
const std::string ItkRegistration::loggerCat_ = "CAMPVis.modules.classification.ItkRegistration";
ItkRegistration::ItkRegistration()
: AbstractProcessor()
, p_sourceImageIDFixed("InputVolumeFixed", "Fixed Input Volume ID", "volume_fixed", DataNameProperty::READ)
, p_sourceImageIDMoving("InputVolumeMoving", "Moving Input Volume ID", "volume_moving", DataNameProperty::READ)
, p_targetImageID("OutputRegistered", "Output Registered Volume ID", "registered_volume", DataNameProperty::WRITE)
, p_registrationType("RegistrationType", "Registration Type", registrationTypes, 1)
, p_noOfBins("NoOfBins", "No. of Bins", 20, 1, 256, 1)
, p_noOfSamples("NoOfSampels", "No. of Samples", 10000, 1, 20000, 1)
{
addProperty(p_sourceImageIDFixed);
addProperty(p_sourceImageIDMoving);
addProperty(p_targetImageID);
addProperty(p_registrationType, INVALID_RESULT | INVALID_PROPERTIES);
addProperty(p_noOfBins);
}
ItkRegistration::~ItkRegistration() {
}
void ItkRegistration::updateResult(DataContainer& data) {
ImageRepresentationLocal::ScopedRepresentation inputFixed(data, p_sourceImageIDFixed.getValue());
ImageRepresentationLocal::ScopedRepresentation inputMoving(data, p_sourceImageIDMoving.getValue());
if (inputFixed != 0 && inputMoving != 0) {
size_t dimInputFixed = inputFixed->getDimensionality();
size_t dimInputMoving = inputMoving->getDimensionality();
if(inputFixed->getParent()->getNumChannels() == 1 &&
inputMoving->getParent()->getNumChannels() == 1 &&
(dimInputFixed == dimInputMoving) && dimInputFixed == 3) {
const size_t dim = dimInputFixed;
ImageData* id = new ImageData(dim, inputFixed->getSize(), 1);
if (p_registrationType.getOptionValue() == "MattesMIRigid3D") {
if (dim == 3) {
#pragma GCC diagnostic ignored "-Warray-bounds"
DISPATCH_ITK_REGISTRATION(inputFixed, inputMoving, MattesMutualInformationImageToImageMetric, VersorRigid3DTransform, \
unsigned long noOfBins = p_noOfBins.getValue(); \
unsigned long noOfSamples = p_noOfSamples.getValue(); \
metric->SetNumberOfHistogramBins(noOfBins); \
metric->SetNumberOfSpatialSamples(noOfSamples); \
);
}
else {
tgtAssert(false, "Unsupported dimensionality!");
}
}
data.addData(p_targetImageID.getValue(), id);
}
else {
LDEBUG("No suitable input image found.");
}
}
else {
LDEBUG("No suitable input image found.");
}
validate(INVALID_RESULT);
}
void ItkRegistration::updateProperties(DataContainer& /*dataContainer*/) {
if (p_registrationType.getOptionValue() == "MattesMIRigid3D") {
p_noOfBins.setVisible(true);
p_noOfSamples.setVisible(true);
}
validate(AbstractProcessor::INVALID_PROPERTIES);
}
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitt Mnchen
// Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// 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
//
// 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 ITKREGISTRATION_H__
#define ITKREGISTRATION_H__
#include <string>
#include "core/pipeline/visualizationprocessor.h"
#include "core/properties/datanameproperty.h"
#include "core/properties/genericproperty.h"
#include "core/properties/floatingpointproperty.h"
#include "core/properties/numericproperty.h"
#include "core/properties/optionproperty.h"
#include "modules/preprocessing/tools/abstractimagefilter.h"
namespace campvis {
/**
* Performs watershed image filter on input image using ITK.
*/
class ItkRegistration : public AbstractProcessor {
public:
/**
* Constructs a new ItkRegistration Processor
**/
ItkRegistration();
/**
* Destructor
**/
virtual ~ItkRegistration();
/// \see AbstractProcessor::getName()
virtual const std::string getName() const { return "ItkRegistration"; };
/// \see AbstractProcessor::getDescription()
virtual const std::string getDescription() const { return "Performs registration between 2 input images using ITK."; };
/// \see AbstractProcessor::getAuthor()
virtual const std::string getAuthor() const { return "Cristina Precup <cristina.precup@tum.de>"; };
/// \see AbstractProcessor::getProcessorState()
virtual ProcessorState getProcessorState() const { return AbstractProcessor::EXPERIMENTAL; };
DataNameProperty p_sourceImageIDFixed; ///< ID for fixed input volume
DataNameProperty p_sourceImageIDMoving; ///< ID for moving input volume
DataNameProperty p_targetImageID; ///< ID for output volume
GenericOptionProperty<std::string> p_registrationType; ///< Registration type
IntProperty p_noOfBins;
IntProperty p_noOfSamples;
protected:
/// \see AbstractProcessor::updateResult
virtual void updateResult(DataContainer& dataContainer);
/// \see AbstractProcessor::updateProperties
virtual void updateProperties(DataContainer& dataContainer);
static const std::string loggerCat_;
};
}
#endif // ITKREGISTRATION_H__
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitt Mnchen
// Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// 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
//
// 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 "itksegmentation.h"
#include "tgt/glmath.h"
#include "tgt/logmanager.h"
#include "modules/itk/core/genericimagerepresentationitk.h"
#include <itkIntTypes.h>
#include <itkCastImageFilter.h>
#include <itkConnectedThresholdImageFilter.h>
#include <itkMaskImageFilter.h>
#include "core/datastructures/imagedata.h"
#include "core/datastructures/genericimagerepresentationlocal.h"
// In this class we want to use various ITK segmentation methods.
/**
* Executes the specified segmentation on the data.
* \param MA_baseType base type of input image
* \param MA_returnType base type of output image
* \param MA_numChannels number of channels of input image
* \param MA_dimensionality dimensionality of images
* \param MD_filterBody additional stuff to execute between filter definition and execution
*/
#define PERFORM_ITK_SEGMENTATION(MA_baseType, MA_returnType, MA_numChannels, MA_dimensionality, MA_filterType, MD_filterBody) \
{ \
GenericImageRepresentationItk<MA_baseType, MA_numChannels, MA_dimensionality>::ScopedRepresentation itkRep(data, p_sourceImageID.getValue()); \
if (itkRep != 0) { \
typedef GenericImageRepresentationItk<MA_baseType, MA_numChannels, MA_dimensionality>::ItkImageType InputImageType; \
typedef GenericImageRepresentationItk<MA_returnType, MA_numChannels, MA_dimensionality>::ItkImageType OutputImageType; \
itk::MA_filterType<InputImageType, OutputImageType>::Pointer filter = itk::MA_filterType<InputImageType, OutputImageType>::New(); \
typedef itk::Image<itk::IdentifierType, MA_dimensionality> LabelImageType; \
typedef itk::MaskImageFilter< OutputImageType, OutputImageType > MaskFilterType;\
MaskFilterType::Pointer maskFilter = MaskFilterType::New();\
MD_filterBody \
filter->SetInput(itkRep->getItkImage()); \
filter->Update(); \
maskFilter->SetInput(itkRep->getItkImage()); \
maskFilter->SetMaskImage(filter->GetOutput());\
itk::CastImageFilter<OutputImageType, OutputImageType>::Pointer caster = itk::CastImageFilter<OutputImageType, OutputImageType>::New(); \
caster->SetInput(maskFilter->GetOutput()); \
caster->Update(); \
\
GenericImageRepresentationItk<MA_baseType, MA_numChannels, MA_dimensionality>::create(id, caster->GetOutput()); \
} \
}
#define DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, MA_baseType, MA_returnType, MA_dimensionality, MA_filterType, MD_filterBody) \
tgtAssert(MA_WTP._numChannels == 1, "ItkSegmentation only supports single-channel images.") \
PERFORM_ITK_SEGMENTATION(MA_baseType, MA_returnType, 1, MA_dimensionality, MA_filterType, MD_filterBody)
#define DISPATCH_ITK_SEGMENTATION_D(MA_WTP, MA_dimensionality, MA_filterType, MD_filterBody) \
switch (MA_WTP._baseType) { \
case WeaklyTypedPointer::UINT8: \
DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, uint8_t, uint8_t, MA_dimensionality, MA_filterType, MD_filterBody) \
break; \
case WeaklyTypedPointer::INT8: \
DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, int8_t, int8_t, MA_dimensionality, MA_filterType, MD_filterBody) \
break; \
case WeaklyTypedPointer::UINT16: \
DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, uint16_t, uint16_t, MA_dimensionality, MA_filterType, MD_filterBody) \
break; \
case WeaklyTypedPointer::INT16: \
DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, int16_t, int16_t, MA_dimensionality, MA_filterType, MD_filterBody) \
break; \
case WeaklyTypedPointer::UINT32: \
DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, uint32_t, uint32_t, MA_dimensionality, MA_filterType, MD_filterBody) \
break; \
case WeaklyTypedPointer::INT32: \
DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, int32_t, int32_t, MA_dimensionality, MA_filterType, MD_filterBody) \
break; \
case WeaklyTypedPointer::FLOAT: \
DISPATCH_ITK_SEGMENTATION_BRD(MA_WTP, float, float, MA_dimensionality, MA_filterType, MD_filterBody) \
break; \
default: \
tgtAssert(false, "Should not reach this - wrong base type in WeaklyTypedPointer!"); \
} \
/**
* Dispatches the execution for the ITK filter \a MA_filterType for the image \a MA_localRep.
* \param MA_localRep local representation of the image to apply the filter to
* \param MA_filterType type name of the ITK filter to use (within itk:: namespace)
* \param MD_filterBody additional stuff to execute between filter definition and execution
*/
#define DISPATCH_ITK_SEGMENTATION(MA_localRep, MA_filterType, MD_filterBody) \
do { \
WeaklyTypedPointer wtp = MA_localRep->getWeaklyTypedPointer(); \
switch (MA_localRep->getDimensionality()) { \
case 2: DISPATCH_ITK_SEGMENTATION_D(wtp, 2, MA_filterType, MD_filterBody) break; \
case 3: DISPATCH_ITK_SEGMENTATION_D(wtp, 3, MA_filterType, MD_filterBody) break; \
default: tgtAssert(false, "Unsupported dimensionality!"); break; \
} \
} while (0)
// ================================================================================================
// = Macros defined, let the party begin! =
// ================================================================================================
namespace campvis {
static const GenericOption<std::string> segmentationTypes[1] = {
GenericOption<std::string>("regionGrowing", "Region Growing")
};
const std::string ItkSegmentation::loggerCat_ = "CAMPVis.modules.classification.ItkSegmentation";
ItkSegmentation::ItkSegmentation(IVec2Property* viewportSizeProp)
: VolumeExplorer(viewportSizeProp)
, p_sourceImageID("InputSegmentationVolume", "Input Segmentation Volume ID", "volume", DataNameProperty::READ)
, p_targetImageID("OutputSegmentationVolume", "Output Segmented Volume ID", "segmented_volume", DataNameProperty::WRITE)
, p_segmentationType("SegmentationType", "Segmentation Type", segmentationTypes, 1)
, p_seedX("SeedX", "Seed X", 0, 0, 0, 1)
, p_seedY("SeedY", "Seed Y", 0, 0, 0, 1)
, p_seedZ("SeedZ", "Seed Z", 0, 0, 0, 1)
, p_thresMin("ThresMin", "Min Threshold", 70, 0, 255, 1)
, p_thresMax("ThresMax", "Max Threshold", 130, 0, 255, 1)
{
addProperty(p_sourceImageID, INVALID_RESULT | INVALID_PROPERTIES);
addProperty(p_targetImageID);
addProperty(p_segmentationType, INVALID_RESULT | INVALID_PROPERTIES);
addProperty(p_seedX);
addProperty(p_seedY);
addProperty(p_seedZ);
addProperty(p_thresMin);
addProperty(p_thresMax);
p_enableScribbling.setValue(true);
}
ItkSegmentation::~ItkSegmentation() {
}
void ItkSegmentation::updateResult(DataContainer& data) {
VolumeExplorer::updateResult(data);
ImageRepresentationLocal::ScopedRepresentation input(data, p_sourceImageID.getValue());
if (input != 0 && input->getParent()->getNumChannels() == 1 && (input->getDimensionality() == 2 || input->getDimensionality() == 3)) {
const size_t dim = input->getDimensionality();
p_seedX.setMaxValue(input->getSize().elem[0]);
p_seedY.setMaxValue(input->getSize().elem[1]);
p_seedZ.setMaxValue(input->getSize().elem[2]);
ImageData* id = new ImageData(dim, input->getSize(), 1);
if (p_segmentationType.getOptionValue() == "regionGrowing") {
if (dim == 2) {
#pragma GCC diagnostic ignored "-Warray-bounds"
DISPATCH_ITK_SEGMENTATION(input, ConnectedThresholdImageFilter, \
InputImageType::IndexType index; \
index[0] = p_seedX.getValue(); \
index[1] = p_seedY.getValue(); \
filter->SetLower(p_thresMin.getValue()); \
filter->SetUpper(p_thresMax.getValue()); \
filter->SetReplaceValue(255); \
filter->SetSeed(index); \
);
} else if (dim == 3) {
#pragma GCC diagnostic ignored "-Warray-bounds"
DISPATCH_ITK_SEGMENTATION(input, ConnectedThresholdImageFilter, \
InputImageType::IndexType index; \
index[0] = p_seedX.getValue(); \
index[1] = p_seedY.getValue(); \
index[2] = p_seedZ.getValue(); \
filter->SetLower(p_thresMin.getValue()); \
filter->SetUpper(p_thresMax.getValue()); \
filter->SetReplaceValue(255); \
filter->SetSeed(index); \
);
} else {
tgtAssert(false, "Unsupported dimensionality!");
}
}
data.addData(p_targetImageID.getValue(), id);
}
else {
LDEBUG("No suitable input image found.");
}
validate(INVALID_RESULT);
}
void ItkSegmentation::updateProperties(DataContainer& data) {
VolumeExplorer::updateProperties(data);
if (p_segmentationType.getOptionValue() == "regionGrowing") {
p_seedX.setVisible(true);
p_seedY.setVisible(true);
p_seedZ.setVisible(true);
p_thresMin.setVisible(true);
p_thresMax.setVisible(true);
}
validate(AbstractProcessor::INVALID_PROPERTIES);
}
void ItkSegmentation::onEvent(tgt::Event* e) {
VolumeExplorer::onEvent(e);
if (typeid(*e) == typeid(tgt::MouseEvent)) {
tgt::MouseEvent* me = static_cast<tgt::MouseEvent*>(e);
if (p_enableScribbling.getValue() && (me->modifiers() & tgt::Event::CTRL || me->modifiers() & tgt::Event::ALT)) {
//update the input image for the segmentation (take the one that is explored by the VolumeExplorer)
p_sourceImageID.setValue(p_inputVolume.getValue());
// update the maximum size
p_seedX.setMaxValue(_sliceExtractor.p_xSliceNumber.getMaxValue());
p_seedY.setMaxValue(_sliceExtractor.p_ySliceNumber.getMaxValue());
p_seedZ.setMaxValue(_sliceExtractor.p_zSliceNumber.getMaxValue());
tgt::svec3 voxel;
voxel = tgt::vec3(_yesScribbles[0]);
p_seedX.setValue(voxel.x);
p_seedY.setValue(voxel.y);
p_seedZ.setValue(voxel.z);
}
}
}
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitt Mnchen