diff --git a/Modules/Multilabel/autoload/IO/files.cmake b/Modules/Multilabel/autoload/IO/files.cmake index 71bd04bf86..09d98eca07 100644 --- a/Modules/Multilabel/autoload/IO/files.cmake +++ b/Modules/Multilabel/autoload/IO/files.cmake @@ -1,13 +1,15 @@ set(CPP_FILES - mitkLabelSetImageIO.cpp - mitkLabelSetImageIO.h - mitkLabelSetImageSerializer.cpp - mitkLabelSetImageSerializer.h + mitkLegacyLabelSetImageIO.cpp + mitkLegacyLabelSetImageIO.h + mitkMultiLabelSegmentationIO.cpp + mitkMultiLabelSegmentationIO.h + mitkMultiLabelSegmentationSerializer.cpp + mitkMultiLabelSegmentationSerializer.h mitkMultilabelActivator.cpp mitkMultilabelIOMimeTypes.cpp mitkMultilabelIOMimeTypes.h mitkSegmentationTaskListIO.cpp mitkSegmentationTaskListIO.h mitkSegmentationTaskListSerializer.cpp mitkSegmentationTaskListSerializer.h ) diff --git a/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.cpp new file mode 100644 index 0000000000..9692c653a2 --- /dev/null +++ b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.cpp @@ -0,0 +1,296 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef __mitkLabelSetImageWriter__cpp +#define __mitkLabelSetImageWriter__cpp + +#include "mitkLegacyLabelSetImageIO.h" +#include "mitkBasePropertySerializer.h" +#include "mitkMultilabelIOMimeTypes.h" +#include "mitkImageAccessByItk.h" +#include "mitkMultiLabelIOHelper.h" +#include "mitkLabelSetImageConverter.h" +#include +#include +#include +#include +#include +#include + +// itk +#include "itkImageFileReader.h" +#include "itkImageFileWriter.h" +#include "itkMetaDataDictionary.h" +#include "itkMetaDataObject.h" +#include "itkNrrdImageIO.h" + +#include + + +namespace mitk +{ + + constexpr char* const OPTION_NAME_MULTI_LAYER = "Multi layer handling"; + constexpr char* const OPTION_NAME_MULTI_LAYER_ADAPT = "Adapt label values"; + constexpr char* const OPTION_NAME_MULTI_LAYER_SPLIT = "Split layers"; + + LegacyLabelSetImageIO::LegacyLabelSetImageIO() + : AbstractFileReader(MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE(), "MITK LabelSetImage (legacy)") + { + this->InitializeDefaultMetaDataKeys(); + AbstractFileReader::SetRanking(10); + + IFileIO::Options options; + std::vector multiLayerStrategy; + multiLayerStrategy.push_back(OPTION_NAME_MULTI_LAYER_ADAPT); + multiLayerStrategy.push_back(OPTION_NAME_MULTI_LAYER_SPLIT); + options[OPTION_NAME_MULTI_LAYER] = multiLayerStrategy; + this->SetDefaultOptions(options); + + this->RegisterService(); + } + + + IFileIO::ConfidenceLevel LegacyLabelSetImageIO::GetConfidenceLevel() const + { + if (AbstractFileReader::GetConfidenceLevel() == 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 ExtractLabelSetsFromMetaData(const itk::MetaDataDictionary& dictionary) + { + std::vector result; + + // get labels and add them as properties to the image + char keybuffer[256]; + + unsigned int numberOfLayers = LegacyLabelSetImageIO::GetIntByKey(dictionary, "layers"); + std::string _xmlStr; + mitk::Label::Pointer label; + + for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) + { + sprintf(keybuffer, "layer_%03u", layerIdx); + int numberOfLabels = LegacyLabelSetImageIO::GetIntByKey(dictionary, keybuffer); + + mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); + + for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) + { + tinyxml2::XMLDocument doc; + sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx); + _xmlStr = LegacyLabelSetImageIO::GetStringByKey(dictionary, keybuffer); + doc.Parse(_xmlStr.c_str(), _xmlStr.size()); + + auto* labelElem = doc.FirstChildElement("Label"); + if (labelElem == nullptr) + mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; + + label = mitk::MultiLabelIOHelper::LoadLabelFromXMLDocument(labelElem); + + if (label->GetValue() != mitk::LabelSetImage::UnlabeledLabelValue) + { + labelSet->AddLabel(label); + labelSet->SetLayer(layerIdx); + } + else + { + MITK_INFO << "Multi label image contains a label specification for unlabeled pixels. This legacy information is ignored."; + } + } + result.push_back(labelSet); + } + + return result; + } + + std::vector LegacyLabelSetImageIO::DoRead() + { + itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); + + std::vector result; + + auto rawimage = ItkImageIO::LoadRawMitkImageFromImageIO(nrrdImageIO, this->GetLocalFileName()); + + const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); + + auto groupImages = SplitVectorImage(rawimage); + auto labelsets = ExtractLabelSetsFromMetaData(dictionary); + + if (labelsets.size() != groupImages.size()) + { + mitkThrow() << "Loaded data is in an invalid state. Number of extracted layer images and labels sets does not match. Found layer images: " << groupImages.size() << "; found labelsets: " << labelsets.size(); + } + + auto props = ItkImageIO::ExtractMetaDataAsPropertyList(nrrdImageIO->GetMetaDataDictionary(), this->GetMimeType()->GetName(), this->m_DefaultMetaDataKeys); + + const Options userOptions = this->GetOptions(); + + const auto multiLayerStrategy = userOptions.find(OPTION_NAME_MULTI_LAYER)->second.ToString(); + + if (multiLayerStrategy == OPTION_NAME_MULTI_LAYER_SPLIT) + { //just split layers in different multi label images + auto labelSetIterator = labelsets.begin(); + for (auto image : groupImages) + { + auto output = ConvertImageToLabelSetImage(image); + output->AddLabelSetToLayer(0, *labelSetIterator); + + //meta data handling + for (auto& [name, prop] : *(props->GetMap())) + { + output->SetProperty(name, prop->Clone()); //need to clone to avoid that all outputs pointing to the same prop instances. + } + // Handle UID + //Remark if we split the legacy label set into distinct layer images, the outputs should have new IDs. So we don't get the old one. + + result.push_back(output.GetPointer()); + labelSetIterator++; + } + } + else + { //Avoid label id collision. + LabelSetImage::LabelValueType maxValue = LabelSetImage::UnlabeledLabelValue; + auto imageIterator = groupImages.begin(); + std::vector adaptedLabelSets; + + for (auto labelset : labelsets) + { + const auto setValues = labelset->GetUsedLabelValues(); + + //generate mapping table; + std::vector > labelMapping; + for (auto vIter = setValues.crbegin(); vIter != setValues.crend(); vIter++) + { //have to use reverse loop because TransferLabelContent (used to adapt content in the same image; see below) + //would potentially corrupt otherwise the content due to "value collision between old values still present + //and already adapted values. By going from highest value to lowest, we avoid that. + if (LabelSetImage::UnlabeledLabelValue != *vIter) + labelMapping.push_back({ *vIter, *vIter + maxValue }); + } + + + if (LabelSetImage::UnlabeledLabelValue != maxValue) + { + //adapt labelset + auto mappedLabelSet = GenerateLabelSetWithMappedValues(labelset, labelMapping); + adaptedLabelSets.emplace_back(mappedLabelSet); + + //adapt image (it is an inplace operation. the image instance stays the same. + TransferLabelContent(*imageIterator, *imageIterator, mappedLabelSet, LabelSetImage::UnlabeledLabelValue, LabelSetImage::UnlabeledLabelValue, + false, labelMapping, MultiLabelSegmentation::MergeStyle::Replace, MultiLabelSegmentation::OverwriteStyle::IgnoreLocks); + } + else + { + adaptedLabelSets.emplace_back(labelset); + } + + const auto setMaxValue = *(std::max_element(setValues.begin(), setValues.end())); + maxValue += setMaxValue; + imageIterator++; + } + + auto output = ConvertImageVectorToLabelSetImage(groupImages, rawimage->GetTimeGeometry()); + + LabelSetImage::SpatialGroupIndexType id = 0; + for (auto labelset : adaptedLabelSets) + { + output->AddLabelSetToLayer(id, labelset); + id++; + } + + //meta data handling + for (auto& [name, prop] : *(props->GetMap())) + { + output->SetProperty(name, prop->Clone()); //need to clone to avoid that all outputs pointing to the same prop instances. + } + + // Handle UID + if (dictionary.HasKey(PROPERTY_KEY_UID)) + { + itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); + if (uidData.IsNotNull()) + { + mitk::UIDManipulator uidManipulator(output); + uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); + } + } + result.push_back(output.GetPointer()); + } + + MITK_INFO << "...finished!"; + return result; + } + + int LegacyLabelSetImageIO::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 LegacyLabelSetImageIO::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; + } + + LegacyLabelSetImageIO *LegacyLabelSetImageIO::Clone() const { return new LegacyLabelSetImageIO(*this); } + + void LegacyLabelSetImageIO::InitializeDefaultMetaDataKeys() + { + this->m_DefaultMetaDataKeys.push_back("NRRD.space"); + this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); + this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); + this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); + this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); + this->m_DefaultMetaDataKeys.push_back("label."); + this->m_DefaultMetaDataKeys.push_back("layer."); + this->m_DefaultMetaDataKeys.push_back("layers"); + this->m_DefaultMetaDataKeys.push_back("org.mitk.label."); + } + +} // namespace + +#endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h similarity index 66% copy from Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h copy to Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h index 4e443688ec..063ca96a63 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h +++ b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h @@ -1,69 +1,64 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef mitkLabelSetImageIO_h -#define mitkLabelSetImageIO_h +#ifndef mitkLegacyLabelSetImageIO_h +#define mitkLegacyLabelSetImageIO_h -#include +#include #include namespace mitk { /** * Writes a LabelSetImage to a file. * mitk::Identifiable UID is supported and will be serialized. * @ingroup Process */ // The export macro should be removed. Currently, the unit // tests directly instantiate this class. - class LabelSetImageIO : public mitk::AbstractFileIO + class LegacyLabelSetImageIO : public mitk::AbstractFileReader { public: typedef mitk::LabelSetImage InputType; - LabelSetImageIO(); + LegacyLabelSetImageIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - ConfidenceLevel GetReaderConfidenceLevel() const override; + ConfidenceLevel GetConfidenceLevel() const override; - // -------------- AbstractFileWriter ------------- + // -------------- LegacyLabelSetImageIO specific functions ------------- - void Write() override; - ConfidenceLevel GetWriterConfidenceLevel() const override; - - // -------------- LabelSetImageIO specific functions ------------- - - int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str); - std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str); + static int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str); + static std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str); protected: /** * @brief Reads a number of mitk::LabelSetImages from the file system * @return a vector of mitk::LabelSetImages * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header */ std::vector> DoRead() override; // Fills the m_DefaultMetaDataKeys vector with default values virtual void InitializeDefaultMetaDataKeys(); private: - LabelSetImageIO *IOClone() const override; + LegacyLabelSetImageIO *Clone() const override; std::vector m_DefaultMetaDataKeys; }; } // end of namespace mitk #endif diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.cpp similarity index 93% rename from Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp rename to Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.cpp index 669ebd7d79..1592ef01ba 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.cpp @@ -1,662 +1,653 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef __mitkLabelSetImageWriter__cpp -#define __mitkLabelSetImageWriter__cpp - -#include "mitkLabelSetImageIO.h" +#include "mitkMultiLabelSegmentationIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" -#include "mitkLabelSetIOHelper.h" +#include "mitkMultiLabelIOHelper.h" #include "mitkLabelSetImageConverter.h" #include #include #include #include #include #include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include namespace mitk { - const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; - const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; - const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; - const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; - const char* const PROPERTY_KEY_UID = "org_mitk_uid"; + constexpr char* const MULTILABEL_SEGMENTATION_MODALITY_VALUE = "org.mitk.image.multilabel.segmentation"; - LabelSetImageIO::LabelSetImageIO() + MultiLabelSegmentationIO::MultiLabelSegmentationIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { this->InitializeDefaultMetaDataKeys(); AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } - IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const + IFileIO::ConfidenceLevel MultiLabelSegmentationIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const auto *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } - void LabelSetImageIO::Write() + void MultiLabelSegmentationIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::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(static_cast(pixelType.GetComponentType()) < PixelComponentUserType ? pixelType.GetComponentType() : itk::IOComponentEnum::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(0.0); mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref()); 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"); + sprintf(valbuffer, MULTILABEL_SEGMENTATION_MODALITY_VALUE); 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; layerIdx < input->GetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); - auto *labelElem = mitk::LabelSetIOHelper::GetLabelAsXMLElement(document, iter->second); + auto *labelElem = mitk::MultiLabelIOHelper::GetLabelAsXMLElement(document, iter->second); document.InsertEndChild(labelElem); tinyxml2::XMLPrinter printer; document.Print(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.CStr()); ++iter; ++count; } } // end label set specific meta data // Handle time geometry const auto* arbitraryTG = dynamic_cast(input->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList(); for (const auto& property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID()); ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } - IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const + IFileIO::ConfidenceLevel MultiLabelSegmentationIO::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) + if (value.compare(MULTILABEL_SEGMENTATION_MODALITY_VALUE) == 0) { return Supported; } else return Unsupported; } - std::vector LabelSetImageIO::DoRead() + std::vector MultiLabelSegmentationIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); // 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 (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(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 const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { tinyxml2::XMLDocument doc; sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str(), _xmlStr.size()); auto *labelElem = doc.FirstChildElement("Label"); if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; - label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElem); + label = mitk::MultiLabelIOHelper::LoadLabelFromXMLDocument(labelElem); if (label->GetValue() != mitk::LabelSetImage::UnlabeledLabelValue) { labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } else { MITK_INFO << "Multi label image contains a label specification for unlabeled pixels. This legacy information is ignored."; } } output->AddLabelSetToLayer(layerIdx, labelSet); } for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string& key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); if (loadedProp.IsNull()) { MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; break; } output->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey = false; for (const auto& defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(output); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; std::vector result; result.push_back(output.GetPointer()); return result; } - int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) + int MultiLabelSegmentationIO::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::string MultiLabelSegmentationIO::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; } - LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } + MultiLabelSegmentationIO *MultiLabelSegmentationIO::IOClone() const { return new MultiLabelSegmentationIO(*this); } - void LabelSetImageIO::InitializeDefaultMetaDataKeys() + void MultiLabelSegmentationIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); this->m_DefaultMetaDataKeys.push_back("label."); this->m_DefaultMetaDataKeys.push_back("layer."); this->m_DefaultMetaDataKeys.push_back("layers"); this->m_DefaultMetaDataKeys.push_back("org.mitk.label."); } } // namespace - -#endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h similarity index 85% rename from Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h rename to Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h index 4e443688ec..a60e0ed1c3 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h +++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h @@ -1,69 +1,69 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef mitkLabelSetImageIO_h -#define mitkLabelSetImageIO_h +#ifndef mitkMultiLabelSegmentationIO_h +#define mitkMultiLabelSegmentationIO_h #include #include namespace mitk { /** * Writes a LabelSetImage to a file. * mitk::Identifiable UID is supported and will be serialized. * @ingroup Process */ // The export macro should be removed. Currently, the unit // tests directly instantiate this class. - class LabelSetImageIO : public mitk::AbstractFileIO + class MultiLabelSegmentationIO : public mitk::AbstractFileIO { public: typedef mitk::LabelSetImage InputType; - LabelSetImageIO(); + MultiLabelSegmentationIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; - // -------------- LabelSetImageIO specific functions ------------- + // -------------- MultiLabelSegmentationIO specific functions ------------- int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str); std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str); protected: /** * @brief Reads a number of mitk::LabelSetImages from the file system * @return a vector of mitk::LabelSetImages * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header */ std::vector> DoRead() override; // Fills the m_DefaultMetaDataKeys vector with default values virtual void InitializeDefaultMetaDataKeys(); private: - LabelSetImageIO *IOClone() const override; + MultiLabelSegmentationIO *IOClone() const override; std::vector m_DefaultMetaDataKeys; }; } // end of namespace mitk #endif diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.cpp similarity index 82% rename from Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp rename to Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.cpp index 892c656dbd..9a05adcc3c 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp +++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.cpp @@ -1,64 +1,64 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#include "mitkLabelSetImageSerializer.h" +#include "mitkMultiLabelSegmentationSerializer.h" #include "mitkLabelSetImage.h" #include #include -MITK_REGISTER_SERIALIZER(LabelSetImageSerializer) +MITK_REGISTER_SERIALIZER(MultiLabelSegmentationSerializer) -mitk::LabelSetImageSerializer::LabelSetImageSerializer() +mitk::MultiLabelSegmentationSerializer::MultiLabelSegmentationSerializer() { } -mitk::LabelSetImageSerializer::~LabelSetImageSerializer() +mitk::MultiLabelSegmentationSerializer::~MultiLabelSegmentationSerializer() { } -std::string mitk::LabelSetImageSerializer::Serialize() +std::string mitk::MultiLabelSegmentationSerializer::Serialize() { const auto *image = dynamic_cast(m_Data.GetPointer()); if (image == nullptr) { MITK_ERROR << " Object at " << (const void *)this->m_Data << " is not an mitk::LabelSetImage. Cannot serialize as LabelSetImage."; return ""; } std::string filename(this->GetUniqueFilenameInWorkingDirectory()); filename += "_"; filename += m_FilenameHint; filename += ".nrrd"; std::string fullname(m_WorkingDirectory); fullname += "/"; fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str()); try { mitk::IOUtil::Save(image, fullname); // LabelSetImageWriter::Pointer writer = LabelSetImageWriter::New(); // writer->SetFileName(fullname); // writer->SetInput(const_cast(image)); // writer->Write(); } catch (std::exception &e) { MITK_ERROR << " Error serializing object at " << (const void *)this->m_Data << " to " << fullname << ": " << e.what(); return ""; } return filename; } diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.h b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.h similarity index 66% rename from Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.h rename to Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.h index 3b205dd51c..5c6fdab21d 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.h +++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationSerializer.h @@ -1,37 +1,37 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef mitkLabelSetImageSerializer_h -#define mitkLabelSetImageSerializer_h +#ifndef mitkMultiLabelSegmentationSerializer_h +#define mitkMultiLabelSegmentationSerializer_h #include "mitkBaseDataSerializer.h" namespace mitk { /** \brief Serializes mitk::LabelSetImage for mitk::SceneIO */ - class LabelSetImageSerializer : public BaseDataSerializer + class MultiLabelSegmentationSerializer : public BaseDataSerializer { public: - mitkClassMacro(LabelSetImageSerializer, BaseDataSerializer); + mitkClassMacro(MultiLabelSegmentationSerializer, BaseDataSerializer); itkFactorylessNewMacro(Self); itkCloneMacro(Self); std::string Serialize() override; protected: - LabelSetImageSerializer(); - ~LabelSetImageSerializer() override; + MultiLabelSegmentationSerializer(); + ~MultiLabelSegmentationSerializer() override; }; } // namespace #endif diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp index 58201f45c4..237c838341 100644 --- a/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp +++ b/Modules/Multilabel/autoload/IO/mitkMultilabelActivator.cpp @@ -1,55 +1,59 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include -#include "mitkLabelSetImageIO.h" +#include "mitkLegacyLabelSetImageIO.h" +#include "mitkMultiLabelSegmentationIO.h" #include "mitkMultilabelIOMimeTypes.h" #include "mitkSegmentationTaskListIO.h" namespace mitk { /** \brief Registers services for multilabel module. */ class MultilabelIOModuleActivator : public us::ModuleActivator { std::vector m_FileIOs; + std::unique_ptr m_LegacyLabelSetImageIOReader; public: void Load(us::ModuleContext *context) override { auto mimeTypes = MitkMultilabelIOMimeTypes::Get(); us::ServiceProperties props; props[us::ServiceConstants::SERVICE_RANKING()] = 10; for (const auto &mimeType : mimeTypes) context->RegisterService(mimeType, props); - m_FileIOs.push_back(new LabelSetImageIO()); + m_LegacyLabelSetImageIOReader = std::make_unique(); + + m_FileIOs.push_back(new MultiLabelSegmentationIO()); m_FileIOs.push_back(new SegmentationTaskListIO); } void Unload(us::ModuleContext *) override { for (auto &elem : m_FileIOs) { delete elem; } } }; } US_EXPORT_MODULE_ACTIVATOR(mitk::MultilabelIOModuleActivator) diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp index 231f8d4aed..aee9aa0727 100644 --- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp +++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp @@ -1,75 +1,146 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMultilabelIOMimeTypes.h" #include - +#include #include #include #include +#include +#include "itkMetaDataObject.h" + mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::MitkSegmentationTaskListMimeType() : CustomMimeType(SEGMENTATIONTASKLIST_MIMETYPE_NAME()) { this->AddExtension("json"); this->SetCategory("MITK Segmentation Task List"); this->SetComment("MITK Segmentation Task List"); } bool mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::AppliesTo(const std::string& path) const { bool result = CustomMimeType::AppliesTo(path); if (!std::filesystem::exists(path)) // T18572 return result; std::ifstream file(path); if (!file.is_open()) return false; auto json = nlohmann::json::parse(file, nullptr, false); if (json.is_discarded() || !json.is_object()) return false; if ("MITK Segmentation Task List" != json.value("FileFormat", "")) return false; if (1 != json.value("Version", 0)) return false; return true; } mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType* mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::Clone() const { return new MitkSegmentationTaskListMimeType(*this); } mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE() { return MitkSegmentationTaskListMimeType(); } std::string mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE_NAME() { return IOMimeTypes::DEFAULT_BASE_NAME() + ".segmentationtasklist"; } +mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::LegacyLabelSetMimeType() + : CustomMimeType(LEGACYLABELSET_MIMETYPE_NAME()) +{ + this->AddExtension("nrrd"); + this->SetCategory("MITK LabelSetImage"); + this->SetComment("MITK LabelSetImage (legacy format)"); +} + +bool mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::AppliesTo(const std::string& path) const +{ + bool canRead = CustomMimeType::AppliesTo(path); + + if (!std::filesystem::exists(path)) // T18572 + return canRead; + + if (!canRead) + { + return false; + } + + std::string value(""); + try + { + std::ifstream file(path); + + if (!file.is_open()) + return false; + + itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); + io->SetFileName(path); + io->ReadImageInformation(); + + itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); + itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); + } + catch(const std::exception& e) + { + MITK_DEBUG << "Error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path <<"; Error: " << e.what(); + } + catch(...) + { + MITK_DEBUG << "Unkown error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path; + } + + if (value.compare("org.mitk.image.multilabel") != 0) + { + return false; + } + + return true; +} + +mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType* mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::Clone() const +{ + return new LegacyLabelSetMimeType(*this); +} + +mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE() +{ + return LegacyLabelSetMimeType(); +} + +std::string mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE_NAME() +{ + return IOMimeTypes::DEFAULT_BASE_NAME() + ".legacylabelsetimage"; +} + std::vector mitk::MitkMultilabelIOMimeTypes::Get() { std::vector mimeTypes; mimeTypes.push_back(SEGMENTATIONTASKLIST_MIMETYPE().Clone()); + mimeTypes.push_back(LEGACYLABELSET_MIMETYPE().Clone()); return mimeTypes; } diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h index 7343895f13..1f92d4692a 100644 --- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h +++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.h @@ -1,39 +1,51 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkMultilabelIOMimeTypes_h #define mitkMultilabelIOMimeTypes_h #include #include namespace mitk { namespace MitkMultilabelIOMimeTypes { class MITKMULTILABELIO_EXPORT MitkSegmentationTaskListMimeType : public CustomMimeType { public: MitkSegmentationTaskListMimeType(); bool AppliesTo(const std::string& path) const override; MitkSegmentationTaskListMimeType* Clone() const override; }; MITKMULTILABELIO_EXPORT MitkSegmentationTaskListMimeType SEGMENTATIONTASKLIST_MIMETYPE(); MITKMULTILABELIO_EXPORT std::string SEGMENTATIONTASKLIST_MIMETYPE_NAME(); + class MITKMULTILABELIO_EXPORT LegacyLabelSetMimeType : public CustomMimeType + { + public: + LegacyLabelSetMimeType(); + + bool AppliesTo(const std::string& path) const override; + LegacyLabelSetMimeType* Clone() const override; + }; + + MITKMULTILABELIO_EXPORT LegacyLabelSetMimeType LEGACYLABELSET_MIMETYPE(); + MITKMULTILABELIO_EXPORT std::string LEGACYLABELSET_MIMETYPE_NAME(); + MITKMULTILABELIO_EXPORT std::vector Get(); } } #endif