diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp index 2598294207..ddbc35cfdc 100644 --- a/Modules/Multilabel/mitkLabelSetImage.cpp +++ b/Modules/Multilabel/mitkLabelSetImage.cpp @@ -1,958 +1,983 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLabelSetImage.h" #include "mitkImageAccessByItk.h" #include "mitkInteractionConst.h" #include "mitkRenderingManager.h" #include "mitkImageCast.h" #include "mitkImageReadAccessor.h" #include "mitkLookupTableProperty.h" #include "mitkPadImageFilter.h" #include #include #include #include #include #include //#include #include mitk::LabelSetImage::LabelSetImage() : mitk::Image(), m_ActiveLayer(0), m_ExteriorLabel(nullptr) { // Iniitlaize Background Label mitk::Color color; color.Set(0,0,0); m_ExteriorLabel = mitk::Label::New(); m_ExteriorLabel->SetColor(color); m_ExteriorLabel->SetName("Exterior"); m_ExteriorLabel->SetOpacity(0.0); m_ExteriorLabel->SetLocked(false); m_ExteriorLabel->SetValue(0); } mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage & other): Image(other), m_ActiveLayer(other.GetActiveLayer()), m_ExteriorLabel(other.GetExteriorLabel()->Clone()) { for(unsigned int i = 0 ; i < other.GetNumberOfLayers(); i++) { // Clone LabelSet data mitk::LabelSet::Pointer lsClone = other.GetLabelSet(i)->Clone(); // add modified event listener to LabelSet (listen to LabelSet changes) itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this,&mitk::LabelSetImage::OnLabelSetModified); lsClone->AddObserver(itk::ModifiedEvent(), command); m_LabelSetContainer.push_back( lsClone ); // clone layer Image data mitk::Image::Pointer liClone = other.GetLayerImage(i)->Clone(); m_LayerContainer.push_back( liClone ); } } void mitk::LabelSetImage::OnLabelSetModified() { Superclass::Modified(); } void mitk::LabelSetImage::SetExteriorLabel(mitk::Label * label) { m_ExteriorLabel = label; } mitk::Label* mitk::LabelSetImage::GetExteriorLabel() { return m_ExteriorLabel; } const mitk::Label* mitk::LabelSetImage::GetExteriorLabel() const { return m_ExteriorLabel; } void mitk::LabelSetImage::Initialize(const mitk::Image* other) { mitk::PixelType pixelType(mitk::MakeScalarPixelType() ); if (other->GetDimension() == 2) { const unsigned int dimensions[] = { other->GetDimension(0), other->GetDimension(1), 1 }; Superclass::Initialize(pixelType, 3, dimensions); } else { Superclass::Initialize(pixelType, other->GetDimension(), other->GetDimensions()); } mitk::TimeGeometry::Pointer originalGeometry = other->GetTimeGeometry()->Clone(); this->SetTimeGeometry( originalGeometry ); // Add a inital LabelSet ans corresponding image data to the stack AddLayer(); } mitk::LabelSetImage::~LabelSetImage() { m_LabelSetContainer.clear(); } mitk::Image* mitk::LabelSetImage::GetLayerImage(unsigned int layer) { return m_LayerContainer[layer]; } const mitk::Image* mitk::LabelSetImage::GetLayerImage(unsigned int layer) const { return m_LayerContainer[layer]; } unsigned int mitk::LabelSetImage::GetActiveLayer() const { return m_ActiveLayer; } unsigned int mitk::LabelSetImage::GetNumberOfLayers() const { return m_LabelSetContainer.size(); } void mitk::LabelSetImage::RemoveLayer() { int layerToDelete = GetActiveLayer(); // remove all observers from active label set GetLabelSet(layerToDelete)->RemoveAllObservers(); // set the active layer to one below, if exists. SetActiveLayer(layerToDelete-1); // remove labelset and image data m_LabelSetContainer.erase( m_LabelSetContainer.begin() + layerToDelete); m_LayerContainer.erase( m_LayerContainer.begin() + layerToDelete); this->Modified(); } unsigned int mitk::LabelSetImage::AddLayer(mitk::LabelSet::Pointer lset) { mitk::Image::Pointer newImage = mitk::Image::New(); newImage->Initialize( this->GetPixelType(), this->GetDimension(), this->GetDimensions(), this->GetImageDescriptor()->GetNumberOfChannels() ); newImage->SetGeometry(this->GetGeometry()->Clone()); LabelSetImageType::Pointer itkImage; mitk::CastToItkImage(newImage, itkImage); itkImage->FillBuffer(0); unsigned int newLabelSetId = this->AddLayer(newImage, lset); // important to release the itk image itkImage = nullptr; return newLabelSetId; } unsigned int mitk::LabelSetImage::AddLayer(mitk::Image::Pointer layerImage, mitk::LabelSet::Pointer lset) { unsigned int newLabelSetId = m_LayerContainer.size(); // Add labelset to layer mitk::LabelSet::Pointer ls; if (lset.IsNotNull()) { ls = lset; } else { ls = mitk::LabelSet::New(); ls->AddLabel(GetExteriorLabel()); ls->SetActiveLabel(0 /*Exterior Label*/); } ls->SetLayer(newLabelSetId); // Add exterior Label to label set //mitk::Label::Pointer exteriorLabel = CreateExteriorLabel(); // push a new working image for the new layer m_LayerContainer.push_back(layerImage); // push a new labelset for the new layer m_LabelSetContainer.push_back(ls); // add modified event listener to LabelSet (listen to LabelSet changes) itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::LabelSetImage::OnLabelSetModified); ls->AddObserver(itk::ModifiedEvent(), command); SetActiveLayer(newLabelSetId); //MITK_INFO << GetActiveLayer(); this->Modified(); return newLabelSetId; } +void mitk::LabelSetImage::AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet::Pointer labelSet) +{ + if (m_LayerContainer.size() <= layerIdx) + { + mitkThrow() << "Trying to add labelSet to non-existing layer."; + } + + if (layerIdx < m_LabelSetContainer.size()) + { + m_LabelSetContainer[layerIdx] = labelSet; + } + else + { + while (layerIdx >= m_LabelSetContainer.size()) + { + mitk::LabelSet::Pointer defaultLabelSet = mitk::LabelSet::New(); + defaultLabelSet->AddLabel(GetExteriorLabel()); + defaultLabelSet->SetActiveLabel(0 /*Exterior Label*/); + defaultLabelSet->SetLayer(m_LabelSetContainer.size()); + m_LabelSetContainer.push_back(defaultLabelSet); + } + m_LabelSetContainer.push_back(labelSet); + } +} + void mitk::LabelSetImage::SetActiveLayer(unsigned int layer) { try { if ( (layer != GetActiveLayer()) && (layerGetNumberOfLayers()) ) { BeforeChangeLayerEvent.Send(); AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer()); m_ActiveLayer = layer; // only at this place m_ActiveLayer should be manipulated!!! Use Getter and Setter AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer()); AfterchangeLayerEvent.Send(); } } catch(itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } this->Modified(); } void mitk::LabelSetImage::Concatenate(mitk::LabelSetImage* other) { const unsigned int* otherDims = other->GetDimensions(); const unsigned int* thisDims = this->GetDimensions(); if ( (otherDims[0] != thisDims[0]) || (otherDims[1] != thisDims[1]) || (otherDims[2] != thisDims[2]) ) mitkThrow() << "Dimensions do not match."; try { int numberOfLayers = other->GetNumberOfLayers(); for (int layer=0; layerSetActiveLayer(layer); AccessByItk_1(this, ConcatenateProcessing, other); mitk::LabelSet * ls = other->GetLabelSet(layer); auto it = ls->IteratorConstBegin(); auto end = ls->IteratorConstEnd(); it++;// skip exterior while(it != end) { GetLabelSet()->AddLabel((it->second)); //AddLabelEvent.Send(); it++; } } } catch(itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } this->Modified(); } void mitk::LabelSetImage::ClearBuffer() { try { AccessByItk(this, ClearBufferProcessing); this->Modified(); } catch(itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } } bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue) const { bool exist = false; for(unsigned int lidx = 0 ; lidx < GetNumberOfLayers(); lidx++) exist |= m_LabelSetContainer[lidx]->ExistLabel(pixelValue); return exist; } bool mitk::LabelSetImage::ExistLabel(PixelType pixelValue, unsigned int layer) const { bool exist = m_LabelSetContainer[layer]->ExistLabel(pixelValue); return exist; } bool mitk::LabelSetImage::ExistLabelSet(unsigned int layer) const { return layer < m_LabelSetContainer.size(); } void mitk::LabelSetImage::MergeLabel(PixelType pixelValue, unsigned int /*layer*/) { int targetPixelValue = GetActiveLabel()->GetValue(); try { AccessByItk_2(this, MergeLabelProcessing, targetPixelValue, pixelValue); } catch(itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } Modified(); } void mitk::LabelSetImage::MergeLabels(std::vector &VectorOfLablePixelValues, PixelType pixelValue, unsigned int layer) { GetLabelSet(layer)->SetActiveLabel(pixelValue); try { for (unsigned int idx=0; idx& VectorOfLabelPixelValues, unsigned int layer) { for (unsigned int idx=0; idxRemoveLabel(VectorOfLabelPixelValues[idx]); EraseLabel(VectorOfLabelPixelValues[idx],layer); } } void mitk::LabelSetImage::EraseLabels(std::vector &VectorOfLabelPixelValues, unsigned int layer) { for (unsigned int i=0; iEraseLabel(VectorOfLabelPixelValues[i], layer); } } void mitk::LabelSetImage::EraseLabel(PixelType pixelValue, unsigned int layer) { try { AccessByItk_2(this, EraseLabelProcessing, pixelValue, layer); } catch(itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } Modified(); } mitk::Label *mitk::LabelSetImage::GetActiveLabel(unsigned int layer) { if (m_LabelSetContainer.size() > layer) return m_LabelSetContainer[layer]->GetActiveLabel(); else return nullptr; } mitk::Label *mitk::LabelSetImage::GetLabel(PixelType pixelValue, unsigned int layer) const { if(m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer]->GetLabel(pixelValue); } mitk::LabelSet* mitk::LabelSetImage::GetLabelSet(unsigned int layer) { if(m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer].GetPointer(); } const mitk::LabelSet* mitk::LabelSetImage::GetLabelSet(unsigned int layer) const { if(m_LabelSetContainer.size() <= layer) return nullptr; else return m_LabelSetContainer[layer].GetPointer(); } mitk::LabelSet* mitk::LabelSetImage::GetActiveLabelSet() { return m_LabelSetContainer[GetActiveLayer()].GetPointer(); } void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue, unsigned int layer) { AccessByItk_2( this, CalculateCenterOfMassProcessing, pixelValue, layer ); } unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const { return m_LabelSetContainer[layer]->GetNumberOfLabels(); } unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const { unsigned int totalLabels(0); auto layerIter = m_LabelSetContainer.begin(); for (; layerIter != m_LabelSetContainer.end(); ++layerIter) totalLabels += (*layerIter)->GetNumberOfLabels(); return totalLabels; } void mitk::LabelSetImage::MaskStamp(mitk::Image* mask, bool forceOverwrite) { try { mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New(); padImageFilter->SetInput(0, mask); padImageFilter->SetInput(1, this); padImageFilter->SetPadConstant(0); padImageFilter->SetBinaryFilter(false); padImageFilter->SetLowerThreshold(0); padImageFilter->SetUpperThreshold(1); padImageFilter->Update(); mitk::Image::Pointer paddedMask = padImageFilter->GetOutput(); if (paddedMask.IsNull()) return; AccessByItk_2(this, MaskStampProcessing, paddedMask, forceOverwrite); } catch(...) { mitkThrow() << "Could not stamp the provided mask on the selected label."; } } mitk::Image::Pointer mitk::LabelSetImage::CreateLabelMask(PixelType index) { mitk::Image::Pointer mask = mitk::Image::New(); try { mask->Initialize(this); unsigned int byteSize = sizeof(LabelSetImage::PixelType); for (unsigned int dim = 0; dim < mask->GetDimension(); ++dim) { byteSize *= mask->GetDimension(dim); } mitk::ImageWriteAccessor* accessor = new mitk::ImageWriteAccessor(static_cast(mask)); memset( accessor->GetData(), 0, byteSize ); delete accessor; mitk::SlicedGeometry3D::Pointer geometry = this->GetSlicedGeometry()->Clone(); mask->SetGeometry( geometry ); AccessByItk_2(this, CreateLabelMaskProcessing, mask, index); } catch(...) { mitkThrow() << "Could not create a mask out of the selected label."; } return mask; } void mitk::LabelSetImage::SurfaceStamp(mitk::Surface* surface, bool forceOverwrite) { if (!surface) { MITK_ERROR << "Input surface is NULL."; return; } try { LabelSetImageType::Pointer itkImage; mitk::CastToItkImage(this, itkImage); vtkPolyData *polydata = surface->GetVtkPolyData(); vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetMatrix( surface->GetGeometry()->GetVtkTransform()->GetMatrix() ); transform->Update(); vtkSmartPointer transformer = vtkSmartPointer::New(); transformer->SetInputData(polydata); transformer->SetTransform(transform); transformer->Update(); typedef double Coord; typedef itk::QuadEdgeMesh< Coord, 3 > MeshType; MeshType::Pointer mesh = MeshType::New(); mesh->SetCellsAllocationMethod( MeshType::CellsAllocatedDynamicallyCellByCell ); int numberOfPoints = polydata->GetNumberOfPoints(); mesh->GetPoints()->Reserve( numberOfPoints ); vtkPoints* points = polydata->GetPoints(); MeshType::PointType point; for( int i=0; i < numberOfPoints; i++ ) { double* aux = points->GetPoint(i); point[0] = aux[0]; point[1] = aux[1]; point[2] = aux[2]; mesh->SetPoint( i, point ); } // Load the polygons into the itk::Mesh typedef MeshType::CellAutoPointer CellAutoPointerType; typedef MeshType::CellType CellType; typedef itk::TriangleCell< CellType > TriangleCellType; typedef MeshType::PointIdentifier PointIdentifierType; typedef MeshType::CellIdentifier CellIdentifierType; // Read the number of polygons CellIdentifierType numberOfPolygons = 0; numberOfPolygons = polydata->GetNumberOfPolys(); PointIdentifierType numberOfCellPoints = 3; for (CellIdentifierType i=0; iGetCell(i); cellIds = vcell->GetPointIds(); CellAutoPointerType cell; auto triangleCell = new TriangleCellType; PointIdentifierType k; for( k = 0; k < numberOfCellPoints; k++ ) { triangleCell->SetPointId( k, cellIds->GetId(k) ); } cell.TakeOwnership( triangleCell ); mesh->SetCell( i, cell ); } typedef itk::TriangleMeshToBinaryImageFilter TriangleMeshToBinaryImageFilterType; TriangleMeshToBinaryImageFilterType::Pointer filter = TriangleMeshToBinaryImageFilterType::New(); filter->SetInput(mesh); filter->SetInfoImage(itkImage); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); LabelSetImageType::Pointer resultImage = filter->GetOutput(); resultImage->DisconnectPipeline(); typedef itk::ImageRegionConstIterator< LabelSetImageType > SourceIteratorType; typedef itk::ImageRegionIterator< LabelSetImageType > TargetIteratorType; SourceIteratorType sourceIter( resultImage, resultImage->GetLargestPossibleRegion() ); sourceIter.GoToBegin(); TargetIteratorType targetIter( itkImage, itkImage->GetLargestPossibleRegion() ); targetIter.GoToBegin(); int activeLabel = GetActiveLabel(GetActiveLayer())->GetValue(); while ( !sourceIter.IsAtEnd() ) { int sourceValue = static_cast(sourceIter.Get()); int targetValue = static_cast(targetIter.Get()); if ( (sourceValue != 0) && (forceOverwrite || !this->GetLabel(targetValue)->GetLocked()) ) // skip exterior and locked labels { targetIter.Set( activeLabel ); } ++sourceIter; ++targetIter; } } catch(itk::ExceptionObject& e) { mitkThrow() << e.GetDescription(); } this->Modified(); } void mitk::LabelSetImage::InitializeByLabeledImage(mitk::Image::Pointer image) { if (image.IsNull() || image->IsEmpty() || !image->IsInitialized()) mitkThrow() << "Invalid labeled image."; try { this->Initialize(image); unsigned int byteSize = sizeof(LabelSetImage::PixelType); for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) { byteSize *= image->GetDimension(dim); } mitk::ImageWriteAccessor* accessor = new mitk::ImageWriteAccessor(static_cast(this)); memset( accessor->GetData(), 0, byteSize ); delete accessor; mitk::SlicedGeometry3D::Pointer geometry = image->GetSlicedGeometry()->Clone(); this->SetGeometry( geometry ); AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing,3); } catch(...) { mitkThrow() << "Could not intialize by provided labeled image."; } this->Modified(); } template < typename ImageType1, typename ImageType2 > void mitk::LabelSetImage::InitializeByLabeledImageProcessing(ImageType1* output, ImageType2* input) { typedef itk::ImageRegionConstIterator< ImageType2 > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType1 > TargetIteratorType; // typedef itk::RelabelComponentImageFilter FilterType; TargetIteratorType targetIter( output, output->GetLargestPossibleRegion() ); targetIter.GoToBegin(); //SourceIteratorType sourceIter( relabelFilter->GetOutput(), relabelFilter->GetOutput()->GetLargestPossibleRegion() ); SourceIteratorType sourceIter( input, input->GetLargestPossibleRegion() ); sourceIter.GoToBegin(); while ( !sourceIter.IsAtEnd() ) { PixelType sourceValue = static_cast(sourceIter.Get()); targetIter.Set( sourceValue ); if(!ExistLabel(sourceValue)) { std::stringstream name; name << "object-" << sourceValue; mitk::Label::Pointer label = mitk::Label::New(); label->SetName( name.str().c_str() ); double rgba[4]; m_LabelSetContainer[GetActiveLayer()]->GetLookupTable()->GetTableValue(sourceValue, rgba ); mitk::Color newColor; newColor.SetRed(rgba[0]); newColor.SetGreen(rgba[1]); newColor.SetBlue(rgba[2]); label->SetColor( newColor ); label->SetOpacity( rgba[3] ); label->SetValue( sourceValue ); GetLabelSet()->AddLabel(label); if(GetActiveLabelSet()->GetNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE || sourceValue >= mitk::Label::MAX_LABEL_VALUE) { AddLayer(); } } ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::MaskStampProcessing(ImageType* itkImage, mitk::Image* mask, bool forceOverwrite) { typename ImageType::Pointer itkMask; mitk::CastToItkImage(mask, itkMask); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter( itkMask, itkMask->GetLargestPossibleRegion() ); sourceIter.GoToBegin(); TargetIteratorType targetIter( itkImage, itkImage->GetLargestPossibleRegion() ); targetIter.GoToBegin(); int activeLabel = this->GetActiveLabel(GetActiveLayer())->GetValue(); while ( !sourceIter.IsAtEnd() ) { PixelType sourceValue = sourceIter.Get(); PixelType targetValue = targetIter.Get(); if ( (sourceValue != 0) && (forceOverwrite || !this->GetLabel(targetValue)->GetLocked()) ) // skip exterior and locked labels { targetIter.Set( activeLabel ); } ++sourceIter; ++targetIter; } this->Modified(); } template < typename ImageType > void mitk::LabelSetImage::CreateLabelMaskProcessing(ImageType* itkImage, mitk::Image* mask, PixelType index) { typename ImageType::Pointer itkMask; mitk::CastToItkImage(mask, itkMask); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter( itkImage, itkImage->GetLargestPossibleRegion() ); sourceIter.GoToBegin(); TargetIteratorType targetIter( itkMask, itkMask->GetLargestPossibleRegion() ); targetIter.GoToBegin(); while ( !sourceIter.IsAtEnd() ) { PixelType sourceValue = sourceIter.Get(); if ( sourceValue == index ) { targetIter.Set( 1 ); } ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType* itkImage, PixelType pixelValue, unsigned int layer) { // for now, we just retrieve the voxel in the middle typedef itk::ImageRegionConstIterator< ImageType > IteratorType; IteratorType iter( itkImage, itkImage->GetLargestPossibleRegion() ); iter.GoToBegin(); std::vector< typename ImageType::IndexType > indexVector; while ( !iter.IsAtEnd() ) { // TODO fix comparison warning more effective if ( iter.Get() == pixelValue ) { indexVector.push_back(iter.GetIndex()); } ++iter; } mitk::Point3D pos; pos.Fill(0.0); if (!indexVector.empty()) { typename itk::ImageRegionConstIteratorWithIndex< ImageType >::IndexType centerIndex; centerIndex = indexVector.at(indexVector.size()/2); if (centerIndex.GetIndexDimension() == 3) { pos[0] = centerIndex[0]; pos[1] = centerIndex[1]; pos[2] = centerIndex[2]; } else return; } GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassIndex(pos); this->GetSlicedGeometry()->IndexToWorld(pos, pos); GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos); } template < typename ImageType > void mitk::LabelSetImage::ClearBufferProcessing(ImageType* itkImage) { itkImage->FillBuffer(0); } // todo: concatenate all layers and not just the active one template < typename ImageType > void mitk::LabelSetImage::ConcatenateProcessing(ImageType* itkTarget, mitk::LabelSetImage* other) { typename ImageType::Pointer itkSource = ImageType::New(); mitk::CastToItkImage( other, itkSource ); typedef itk::ImageRegionConstIterator< ImageType > ConstIteratorType; typedef itk::ImageRegionIterator< ImageType > IteratorType; ConstIteratorType sourceIter( itkSource, itkSource->GetLargestPossibleRegion() ); IteratorType targetIter( itkTarget, itkTarget->GetLargestPossibleRegion() ); int numberOfTargetLabels = this->GetNumberOfLabels(GetActiveLayer()) - 1; // skip exterior sourceIter.GoToBegin(); targetIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { PixelType sourceValue = sourceIter.Get(); PixelType targetValue = targetIter.Get(); if ( (sourceValue != 0) && !this->GetLabel(targetValue)->GetLocked() ) // skip exterior and locked labels { targetIter.Set( sourceValue + numberOfTargetLabels ); } ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::LayerContainerToImageProcessing(ImageType* target, unsigned int layer) { typename ImageType::Pointer itkSource; mitk::CastToItkImage(m_LayerContainer[layer], itkSource); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter( itkSource, itkSource->GetLargestPossibleRegion() ); sourceIter.GoToBegin(); TargetIteratorType targetIter( target, target->GetLargestPossibleRegion() ); targetIter.GoToBegin(); while(!sourceIter.IsAtEnd()) { targetIter.Set( sourceIter.Get() ); ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::ImageToLayerContainerProcessing(ImageType* source, unsigned int layer) const { typename ImageType::Pointer itkTarget; mitk::CastToItkImage(m_LayerContainer[layer], itkTarget); typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; SourceIteratorType sourceIter( source, source->GetLargestPossibleRegion() ); sourceIter.GoToBegin(); TargetIteratorType targetIter( itkTarget, itkTarget->GetLargestPossibleRegion() ); targetIter.GoToBegin(); while(!sourceIter.IsAtEnd()) { targetIter.Set( sourceIter.Get() ); ++sourceIter; ++targetIter; } } template < typename ImageType > void mitk::LabelSetImage::EraseLabelProcessing(ImageType* itkImage, PixelType pixelValue, unsigned int /*layer*/) { typedef itk::ImageRegionIterator< ImageType > IteratorType; IteratorType iter( itkImage, itkImage->GetLargestPossibleRegion() ); iter.GoToBegin(); while (!iter.IsAtEnd()) { PixelType value = iter.Get(); if (value == pixelValue) { iter.Set( 0 ); } ++iter; } } template < typename ImageType > void mitk::LabelSetImage::MergeLabelProcessing(ImageType* itkImage, PixelType pixelValue, PixelType index) { typedef itk::ImageRegionIterator< ImageType > IteratorType; IteratorType iter( itkImage, itkImage->GetLargestPossibleRegion() ); iter.GoToBegin(); while (!iter.IsAtEnd()) { if (iter.Get() == index) { iter.Set( pixelValue ); } ++iter; } } bool mitk::Equal(const mitk::LabelSetImage& leftHandSide, const mitk::LabelSetImage& rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; /* LabelSetImage members */ MITK_INFO(verbose) << "--- LabelSetImage Equal ---"; // number layers returnValue = leftHandSide.GetNumberOfLayers() == rightHandSide.GetNumberOfLayers(); if(!returnValue) { MITK_INFO(verbose) << "Number of layers not equal."; return false; } // total number labels returnValue = leftHandSide.GetTotalNumberOfLabels() == rightHandSide.GetTotalNumberOfLabels(); if(!returnValue) { MITK_INFO(verbose) << "Total number of labels not equal."; return false; } // active layer returnValue = leftHandSide.GetActiveLayer() == rightHandSide.GetActiveLayer(); if(!returnValue) { MITK_INFO(verbose) << "Active layer not equal."; return false; } // working image data returnValue = mitk::Equal((const mitk::Image&)leftHandSide,(const mitk::Image&)rightHandSide,eps,verbose); if(!returnValue) { MITK_INFO(verbose) << "Working image data not equal."; return false; } for(unsigned int layerIndex = 0 ; layerIndex < leftHandSide.GetNumberOfLayers(); layerIndex++) { // layer image data returnValue = mitk::Equal(*leftHandSide.GetLayerImage(layerIndex),*rightHandSide.GetLayerImage(layerIndex),eps,verbose); if(!returnValue) { MITK_INFO(verbose) << "Layer image data not equal."; return false; } // layer labelset data returnValue = mitk::Equal(*leftHandSide.GetLabelSet(layerIndex),*rightHandSide.GetLabelSet(layerIndex),eps,verbose); if(!returnValue) { MITK_INFO(verbose) << "Layer labelset data not equal."; return false; } } return returnValue; } diff --git a/Modules/Multilabel/mitkLabelSetImage.h b/Modules/Multilabel/mitkLabelSetImage.h index f450df4300..1512c1c4fa 100644 --- a/Modules/Multilabel/mitkLabelSetImage.h +++ b/Modules/Multilabel/mitkLabelSetImage.h @@ -1,338 +1,352 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkLabelSetImage_H_ #define __mitkLabelSetImage_H_ #include "mitkImage.h" #include "MitkMultilabelExports.h" #include #include #include #include namespace mitk { //##Documentation //## @brief LabelSetImage class for handling labels and layers in a segmentation session. //## //## Handles operations for adding, removing, erasing and editing labels and layers. //## @ingroup Data class MITKMULTILABEL_EXPORT LabelSetImage : public Image { public: mitkClassMacro(LabelSetImage, Image) itkNewMacro(Self) typedef mitk::Label::PixelType PixelType; typedef itk::Image< PixelType, 3 > LabelSetImageType; typedef itk::VariableLengthVector< PixelType > VariableVectorType; /** * \brief BeforeChangeLayerEvent (e.g. used for GUI integration) * As soon as active labelset should be changed, the signal emits. * Emitted by SetActiveLayer(int layer); */ Message<> BeforeChangeLayerEvent; /** * \brief AfterchangeLayerEvent (e.g. used for GUI integration) * As soon as active labelset was changed, the signal emits. * Emitted by SetActiveLayer(int layer); */ Message<> AfterchangeLayerEvent; /** * @brief Initialize an empty mitk::LabelSetImage using the information * of an mitk::Image * @param image the image which is used for initializing the mitk::LabelSetImage */ using mitk::Image::Initialize; virtual void Initialize(const mitk::Image* image) override; /** * \brief */ void Concatenate(mitk::LabelSetImage* image); /** * \brief */ void ClearBuffer(); /** * @brief Merges the mitk::Label with a given target value with the active label * @param targetPixelValue the value of the mitk::Label that should be merged with the active one * @param layer the layer in which the merge should be performed */ void MergeLabel(PixelType targetPixelValue, unsigned int layer = 0); /** * @brief Merges a list of mitk::Labels with the mitk::Label that has a specific value * @param VectorOfLablePixelValues the list of labels that should be merge with the specified one * @param index the value of the label into which the other should be merged * @param layer the layer in which the merge should be performed */ void MergeLabels(std::vector& VectorOfLablePixelValues, PixelType index, unsigned int layer = 0); /** * \brief */ void UpdateCenterOfMass(PixelType pixelValue, unsigned int layer =0); /** * @brief Removes labels from the mitk::LabelSet of given layer. * Calls mitk::LabelSetImage::EraseLabels() which also removes the labels from within the image. * @param VectorOfLabelPixelValues a list of labels to be removed * @param layer the layer in which the labels should be removed */ void RemoveLabels(std::vector& VectorOfLabelPixelValues, unsigned int layer = 0); /** * @brief Erases the label with the given value in the given layer from the underlying image. * The label itself will not be erased from the respective mitk::LabelSet. In order to * remove the label itself use mitk::LabelSetImage::RemoveLabels() * @param pixelValue the label which will be remove from the image * @param layer the layer in which the label should be removed */ void EraseLabel(PixelType pixelValue, unsigned int layer = 0); /** * @brief Similar to mitk::LabelSetImage::EraseLabel() this funtion erase a list of labels from the image * @param VectorOfLabelPixelValues the list of labels that should be remove * @param layer the layer for which the labels should be removed */ void EraseLabels(std::vector& VectorOfLabelPixelValues, unsigned int layer = 0); /** * \brief Returns true if the value exists in one of the labelsets*/ bool ExistLabel(PixelType pixelValue) const; /** * @brief Checks if a label exists in a certain layer * @param pixelValue the label value * @param layer the layer in which should be searched for the label * @return true if the label exists otherwise false */ bool ExistLabel(PixelType pixelValue, unsigned int layer) const; /** * \brief Returns true if the labelset exists*/ bool ExistLabelSet(unsigned int layer) const; /** * @brief Returns the active label of a specific layer * @param layer the layer ID for which the active label should be returned * @return the active label of the specified layer */ mitk::Label* GetActiveLabel(unsigned int layer = 0); /** * @brief Returns the mitk::Label with the given pixelValue and for the given layer * @param pixelValue the pixel value of the label * @param layer the layer in which the labels should be located * @return the mitk::Label if available otherwise NULL */ mitk::Label* GetLabel(PixelType pixelValue, unsigned int layer = 0) const; /** * @brief Returns the currently active mitk::LabelSet * @return the mitk::LabelSet of the active layer or NULL if non is present */ mitk::LabelSet* GetActiveLabelSet(); /** * @brief Gets the mitk::LabelSet for the given layer * @param layer the layer for which the mitk::LabelSet should be retrieved * @return the respective mitk::LabelSet or NULL if non exists for the given layer */ mitk::LabelSet * GetLabelSet(unsigned int layer = 0); const mitk::LabelSet * GetLabelSet(unsigned int layer = 0) const; /** * @brief Gets the ID of the currently active layer * @return the ID of the active layer */ unsigned int GetActiveLayer() const; /** * @brief Get the number of all existing mitk::Labels for a given layer * @param layer the layer ID for which the active mitk::Labels should be retrieved * @return the number of all existing mitk::Labels for the given layer */ unsigned int GetNumberOfLabels(unsigned int layer = 0) const; /** * @brief Returns the number of all labels summed up across all layers * @return the overall number of labels across all layers */ unsigned int GetTotalNumberOfLabels() const; /** * \brief */ void SurfaceStamp(mitk::Surface* surface, bool forceOverwrite); /** * \brief */ mitk::Image::Pointer CreateLabelMask(PixelType index); /** * @brief Initialize a new mitk::LabelSetImage by an given image. * For all distinct pixel values of the parameter image new labels will * be created. If the number of distinct pixel values exceeds mitk::Label::MAX_LABEL_VALUE * a new layer will be created * @param image the image which is used for initialization */ void InitializeByLabeledImage(mitk::Image::Pointer image); /** * \brief */ void MaskStamp(mitk::Image* mask, bool forceOverwrite); /** * \brief */ void SetActiveLayer(unsigned int layer); /** * \brief */ unsigned int GetNumberOfLayers() const; /** * @brief Adds a new layer to the LabelSetImage. The new layer will be set as the active one * @param layer a mitk::LabelSet which will be set as new layer. * @return the layer ID of the new layer */ unsigned int AddLayer(mitk::LabelSet::Pointer layer =nullptr); /** * \brief Add a layer based on a provided mitk::Image * \param layerImage is added to the vector of label images * \param lset a label set that will be added to the new layer if provided *\return the layer ID of the new layer */ unsigned int AddLayer(mitk::Image::Pointer layerImage, mitk::LabelSet::Pointer lset = nullptr); + /** + * \brief Add a LabelSet to an existing layer + * + * This will replace an existing labelSet if one exists. Throws an exceptions if you are trying + * to add a labelSet to a non-existing layer. + * + * If there are no labelSets for layers with an id less than layerIdx default ones will be added + * for them. + * + * \param layerIdx The index of the layer the LabelSet should be added to + * \param labelSet The LabelSet that should be added + */ + void AddLabelSetToLayer(const unsigned int layerIdx, const mitk::LabelSet::Pointer labelSet); + /** * @brief Removes the active layer and the respective mitk::LabelSet and image information. * The new active layer is the one below, if exists */ void RemoveLayer(); /** * \brief */ mitk::Image* GetLayerImage(unsigned int layer); const mitk::Image* GetLayerImage(unsigned int layer) const; void OnLabelSetModified(); /** * @brief Sets the label which is used as default exterior label when creating a new layer * @param label the label which will be used as new exterior label */ void SetExteriorLabel(mitk::Label * label); /** * @brief Gets the mitk::Label which is used as default exterior label * @return the exterior mitk::Label */ mitk::Label* GetExteriorLabel(); const mitk::Label* GetExteriorLabel() const; protected: mitkCloneMacro(Self) LabelSetImage(); LabelSetImage(const LabelSetImage & other); virtual ~LabelSetImage(); template < typename ImageType1, typename ImageType2 > void ChangeLayerProcessing( ImageType1* source, ImageType2* target ); template < typename ImageType > void LayerContainerToImageProcessing( ImageType* source, unsigned int layer); template < typename ImageType > void ImageToLayerContainerProcessing( ImageType* source, unsigned int layer) const; template < typename ImageType > void CalculateCenterOfMassProcessing( ImageType* input, PixelType index, unsigned int layer); template < typename ImageType > void ClearBufferProcessing( ImageType* input); template < typename ImageType > void EraseLabelProcessing( ImageType* input, PixelType index, unsigned int layer); // template < typename ImageType > // void ReorderLabelProcessing( ImageType* input, int index, int layer); template < typename ImageType > void MergeLabelProcessing( ImageType* input, PixelType pixelValue, PixelType index); template < typename ImageType > void ConcatenateProcessing( ImageType* input, mitk::LabelSetImage* other); template < typename ImageType > void MaskStampProcessing( ImageType* input, mitk::Image* mask, bool forceOverwrite); template < typename ImageType > void CreateLabelMaskProcessing( ImageType* input, mitk::Image* mask, PixelType index); template < typename ImageType1, typename ImageType2 > void InitializeByLabeledImageProcessing( ImageType1* input, ImageType2* other); std::vector< LabelSet::Pointer > m_LabelSetContainer; std::vector< Image::Pointer > m_LayerContainer; int m_ActiveLayer; mitk::Label::Pointer m_ExteriorLabel; }; /** * @brief Equal A function comparing two label set images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - LabelSetImage members * - working image data * - layer image data * - labels in label set * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKMULTILABEL_EXPORT bool Equal( const mitk::LabelSetImage& leftHandSide, const mitk::LabelSetImage& rightHandSide, ScalarType eps, bool verbose ); } // namespace mitk #endif // __mitkLabelSetImage_H_ diff --git a/Modules/Multilabel/mitkLabelSetImageIO.cpp b/Modules/Multilabel/mitkLabelSetImageIO.cpp index 5d7fb60476..8985e29254 100644 --- a/Modules/Multilabel/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/mitkLabelSetImageIO.cpp @@ -1,691 +1,693 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkLabelSetImageIO.h" #include "mitkLabelSetImageConverter.h" // itk #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" #include "mitkImageAccessByItk.h" #include "tinyxml.h" + + namespace mitk { LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const LabelSetImage* input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); const LabelSetImage* input = static_cast(this->GetInput()); const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { mitkThrow() << "Could not set locale " << currLocale; } } mitk::Image::ConstPointer inputVector = mitk::LabelSetImageConverter::ConvertLabelSetImageToImage(input); // image write if ( inputVector.IsNull() ) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int* const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } //use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdxGetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03d", layerIdx); // layer idx sprintf(valbuffer, "%1d", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); mitk::LabelSet::LabelContainerConstIteratorType iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { std::auto_ptr document; document.reset(new TiXmlDocument()); TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... document->LinkEndChild(decl); TiXmlElement * labelElem = GetLabelAsTiXmlElement(iter->second); document->LinkEndChild(labelElem); TiXmlPrinter printer; printer.SetIndent(""); printer.SetLineBreak(""); document->Accept(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.Str()); ++iter; ++count; } } // end label set specific meta data ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception& e) { mitkThrow() << e.what(); } // end image write try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { mitkThrow() << "Could not reset locale " << currLocale; } } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value (""); itk::ExposeMetaData (imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::Read() { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { mitkThrow() << "Could not set locale."; } } // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (iGetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i<3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void* buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = NULL; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { std::string key = std::string("meta.") + iter->first; if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); image->SetProperty(key.c_str(), mitk::StringProperty::New(value)); } } // end regular image loading - // get labels and add them as properties to the image - //char keybuffer[256]; + LabelSetImage::Pointer output = LabelSetImageConverter::ConvertImageToLabelSetImage(image); - //int numberOfLayers = GetIntByKey(dictionary, "layers"); - //std::string _xmlStr; - //mitk::Label::Pointer label; + // get labels and add them as properties to the image + char keybuffer[256]; - //for (int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) - //{ - // sprintf(keybuffer, "layer_%03d", layerIdx); - // int numberOfLabels = GetIntByKey(imgMetaDictionary, keybuffer); + int numberOfLayers = GetIntByKey(dictionary, "layers"); + std::string _xmlStr; + mitk::Label::Pointer label; - // mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); + for (int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) + { + sprintf(keybuffer, "layer_%03d", layerIdx); + int numberOfLabels = GetIntByKey(dictionary, keybuffer); - // for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) - // { - // TiXmlDocument doc; - // sprintf(keybuffer, "label_%03d_%05d", layerIdx, labelIdx); - // _xmlStr = GetStringByKey(imgMetaDictionary, keybuffer); - // doc.Parse(_xmlStr.c_str()); + mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); - // TiXmlElement * labelElem = doc.FirstChildElement("Label"); - // if (labelElem == 0) - // mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; + for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) + { + TiXmlDocument doc; + sprintf(keybuffer, "label_%03d_%05d", layerIdx, labelIdx); + _xmlStr = GetStringByKey(dictionary, keybuffer); + doc.Parse(_xmlStr.c_str()); - // label = LoadLabelFromTiXmlDocument(labelElem); + TiXmlElement * labelElem = doc.FirstChildElement("Label"); + if (labelElem == 0) + mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; - // if (label->GetValue() == 0) // set exterior label is needed to hold exterior information - // output->SetExteriorLabel(label); - // labelSet->AddLabel(label); - // } - // output->AddLayer(labelSet); - //} + label = LoadLabelFromTiXmlDocument(labelElem); - LabelSetImage::Pointer output = LabelSetImageConverter::ConvertImageToLabelSetImage(image); + if (label->GetValue() == 0) // set exterior label is needed to hold exterior information + output->SetExteriorLabel(label); + labelSet->AddLabel(label); + } + output->AddLabelSetToLayer(layerIdx, labelSet); + } MITK_INFO << "...finished!" << std::endl; try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { mitkThrow() << "Could not reset locale!"; } std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary & dic,const std::string & str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary & dic,const std::string & str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } Label::Pointer LabelSetImageIO::LoadLabelFromTiXmlDocument(TiXmlElement * labelElem) { // reread TiXmlElement * propElem = labelElem->FirstChildElement("property"); std::string name; mitk::BaseProperty::Pointer prop; mitk::Label::Pointer label = mitk::Label::New(); while(propElem) { LabelSetImageIO::PropertyFromXmlElem( name, prop, propElem ); label->SetProperty( name, prop ); propElem = propElem->NextSiblingElement( "property" ); } return label.GetPointer(); } bool LabelSetImageIO::PropertyFromXmlElem(std::string& key, mitk::BaseProperty::Pointer& prop, TiXmlElement* elem) { std::string type; elem->QueryStringAttribute("type", &type); elem->QueryStringAttribute("key", &key); // construct name of serializer class std::string serializername(type); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << type << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one."; for ( std::list::iterator iter = allSerializers.begin(); iter != allSerializers.end(); ++iter ) { if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) { try { prop = serializer->Deserialize(elem->FirstChildElement()); } catch (std::exception& e) { MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what(); return false; } break; } } if(prop.IsNull()) return false; return true; } void LabelSetImageIO::LoadLabelSetImagePreset(const std::string & presetFilename, mitk::LabelSetImage::Pointer& inputImage ) { std::unique_ptr presetXmlDoc(new TiXmlDocument()); bool ok = presetXmlDoc->LoadFile(presetFilename); if ( !ok ) return; TiXmlElement * presetElem = presetXmlDoc->FirstChildElement("LabelSetImagePreset"); if(!presetElem) { MITK_INFO << "No valid preset XML"; return; } int numberOfLayers; presetElem->QueryIntAttribute("layers", &numberOfLayers); for(int i = 0 ; i < numberOfLayers; i++) { TiXmlElement * layerElem = presetElem->FirstChildElement("Layer"); int numberOfLabels; layerElem->QueryIntAttribute("labels", &numberOfLabels); if(inputImage->GetLabelSet(i) == NULL) inputImage->AddLayer(); TiXmlElement * labelElement = layerElem->FirstChildElement("Label"); if(labelElement == NULL) break; for(int j = 0 ; j < numberOfLabels; j++) { mitk::Label::Pointer label = mitk::LabelSetImageIO::LoadLabelFromTiXmlDocument(labelElement); inputImage->GetLabelSet()->AddLabel(label); labelElement = labelElement->NextSiblingElement("Label"); if(labelElement == NULL) break; } } } LabelSetImageIO* LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } TiXmlElement * LabelSetImageIO::GetLabelAsTiXmlElement(Label * label) { TiXmlElement* labelElem = new TiXmlElement("Label"); // add XML contents const PropertyList::PropertyMap* propmap = label->GetMap(); for ( PropertyList::PropertyMap::const_iterator iter = propmap->begin(); iter != propmap->end(); ++iter ) { std::string key = iter->first; const BaseProperty* property = iter->second; TiXmlElement* element = PropertyToXmlElem( key, property ); if (element) labelElem->LinkEndChild( element ); } return labelElem; } TiXmlElement* LabelSetImageIO::PropertyToXmlElem( const std::string& key, const BaseProperty* property ) { TiXmlElement* keyelement = new TiXmlElement("property"); keyelement->SetAttribute("key", key); keyelement->SetAttribute("type", property->GetNameOfClass()); // construct name of serializer class std::string serializername(property->GetNameOfClass()); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one."; for ( std::list::iterator iter = allSerializers.begin(); iter != allSerializers.end(); ++iter ) { if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) { serializer->SetProperty(property); try { TiXmlElement* valueelement = serializer->Serialize(); if (valueelement) keyelement->LinkEndChild( valueelement ); } catch (std::exception& e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } return keyelement; } bool LabelSetImageIO::SaveLabelSetImagePreset(const std::string & presetFilename, LabelSetImage::Pointer & inputImage) { TiXmlDocument * presetXmlDoc = new TiXmlDocument(); TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); presetXmlDoc->LinkEndChild( decl ); TiXmlElement * presetElement = new TiXmlElement("LabelSetImagePreset"); presetElement->SetAttribute("layers", inputImage->GetNumberOfLayers()); presetXmlDoc->LinkEndChild(presetElement); for (unsigned int layerIdx=0; layerIdxGetNumberOfLayers(); layerIdx++) { TiXmlElement * layerElement = new TiXmlElement("Layer"); layerElement->SetAttribute("index", layerIdx); layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIdx)); presetElement->LinkEndChild(layerElement); for (unsigned int labelIdx=0; labelIdxGetNumberOfLabels(layerIdx); labelIdx++) { TiXmlElement * labelAsXml = LabelSetImageIO::GetLabelAsTiXmlElement(inputImage->GetLabel(labelIdx,layerIdx)); layerElement->LinkEndChild(labelAsXml); } } bool wasSaved = presetXmlDoc->SaveFile(presetFilename); delete presetXmlDoc; return wasSaved; } } //namespace #endif //__mitkLabelSetImageWriter__cpp