diff --git a/Modules/DICOMQI/CMakeLists.txt b/Modules/DICOMQI/CMakeLists.txt new file mode 100644 index 0000000000..b819f74008 --- /dev/null +++ b/Modules/DICOMQI/CMakeLists.txt @@ -0,0 +1,4 @@ +MITK_CREATE_MODULE( + DEPENDS MitkCore MitkDICOMReader +) +add_subdirectory(autoload/IO) \ No newline at end of file diff --git a/Modules/Multilabel/autoload/DICOMQIIO/CMakeLists.txt b/Modules/DICOMQI/autoload/IO/CMakeLists.txt similarity index 100% rename from Modules/Multilabel/autoload/DICOMQIIO/CMakeLists.txt rename to Modules/DICOMQI/autoload/IO/CMakeLists.txt diff --git a/Modules/Multilabel/autoload/DICOMQIIO/files.cmake b/Modules/DICOMQI/autoload/IO/files.cmake similarity index 100% rename from Modules/Multilabel/autoload/DICOMQIIO/files.cmake rename to Modules/DICOMQI/autoload/IO/files.cmake diff --git a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOActivator.cpp b/Modules/DICOMQI/autoload/IO/mitkDICOMQIIOActivator.cpp similarity index 100% rename from Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOActivator.cpp rename to Modules/DICOMQI/autoload/IO/mitkDICOMQIIOActivator.cpp diff --git a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOMimeTypes.cpp b/Modules/DICOMQI/autoload/IO/mitkDICOMQIIOMimeTypes.cpp similarity index 73% rename from Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOMimeTypes.cpp rename to Modules/DICOMQI/autoload/IO/mitkDICOMQIIOMimeTypes.cpp index c940129ca4..ca04d29fb0 100644 --- a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOMimeTypes.cpp +++ b/Modules/DICOMQI/autoload/IO/mitkDICOMQIIOMimeTypes.cpp @@ -1,132 +1,137 @@ /*=================================================================== 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 "mitkDICOMQIIOMimeTypes.h" #include "mitkIOMimeTypes.h" #include #include #include #include #include namespace mitk { std::vector MitkDICOMQIIOMimeTypes::Get() { std::vector mimeTypes; // order matters here (descending rank for mime types) - mimeTypes.push_back(DICOMQI_MIMETYPE().Clone()); + mimeTypes.push_back(DICOMSEG_MIMETYPE().Clone()); + //mimeTypes.push_back(DICOMPM_MIMETYPE().Clone()); return mimeTypes; } // Mime Types - MitkDICOMQIIOMimeTypes::MitkDICOMQIMimeType::MitkDICOMQIMimeType() : CustomMimeType(DICOMQI_MIMETYPE_NAME()) + //======= Mime Type DICOM SEG ======= + MitkDICOMQIIOMimeTypes::MitkDICOMSEGMimeType:::MitkDICOMSEGMimeType() : CustomMimeType(DICOMSEG_MIMETYPE_NAME()) { this->AddExtension("dcm"); this->SetCategory(IOMimeTypes::CATEGORY_IMAGES()); this->SetComment("DICOM SEG"); } - bool MitkDICOMQIIOMimeTypes::MitkDICOMQIMimeType::AppliesTo(const std::string &path) const + bool MitkDICOMQIIOMimeTypes::MitkDICOMSEGMimeType::AppliesTo(const std::string &path) const { std::ifstream myfile; - myfile.open (path, std::ios::binary); -// myfile.seekg (128); - char *buffer = new char [128]; - myfile.read (buffer,128); - myfile.read (buffer,4); - if (std::string(buffer).compare("DICM")!=0) + myfile.open(path, std::ios::binary); + // myfile.seekg (128); + char *buffer = new char[128]; + myfile.read(buffer, 128); + myfile.read(buffer, 4); + if (std::string(buffer).compare("DICM") != 0) { delete[] buffer; return false; } delete[] buffer; bool canRead(CustomMimeType::AppliesTo(path)); // fix for bug 18572 // Currently this function is called for writing as well as reading, in that case // the image information can of course not be read // This is a bug, this function should only be called for reading. if (!itksys::SystemTools::FileExists(path.c_str())) { return canRead; } // end fix for bug 18572 DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(path.c_str()); if (status.bad()) { canRead = false; } if (!canRead) { return canRead; } OFString modality; OFString sopClassUID; if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good() && dcmFileFormat.getDataset()->findAndGetOFString(DCM_SOPClassUID, sopClassUID).good()) { if (modality.compare("SEG") == 0) {//atm we could read SegmentationStorage files. Other storage classes with "SEG" modality, e.g. SurfaceSegmentationStorage (1.2.840.10008.5.1.4.1.1.66.5), are not supported yet. if (sopClassUID.compare("1.2.840.10008.5.1.4.1.1.66.4") == 0) { canRead = true; } else { canRead = false; } } else { canRead = false; } } return canRead; } - MitkDICOMQIIOMimeTypes::MitkDICOMQIMimeType *MitkDICOMQIIOMimeTypes::MitkDICOMQIMimeType::Clone() const + MitkDICOMQIIOMimeTypes::MitkDICOMSEGMimeType *MitkDICOMQIIOMimeTypes::MitkDICOMSEGMimeType::Clone() const { - return new MitkDICOMQIMimeType(*this); + return new MitkDICOMSEGMimeType(*this); } - MitkDICOMQIIOMimeTypes::MitkDICOMQIMimeType MitkDICOMQIIOMimeTypes::DICOMQI_MIMETYPE() + MitkDICOMQIIOMimeTypes::MitkDICOMSEGMimeType MitkDICOMQIIOMimeTypes::DICOMSEG_MIMETYPE() { - return MitkDICOMQIMimeType(); + return MitkDICOMSEGMimeType(); } - // Names - std::string MitkDICOMQIIOMimeTypes::DICOMQI_MIMETYPE_NAME() + std::string MitkDICOMQIOMimeTypes::DICOMSEG_MIMETYPE_NAME() { // create a unique and sensible name for this mime type static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".image.dicom.seg"; return name; } + + + //======= Mime Type DICOM PM ======= + //... } diff --git a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOMimeTypes.h b/Modules/DICOMQI/autoload/IO/mitkDICOMQIIOMimeTypes.h similarity index 59% rename from Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOMimeTypes.h rename to Modules/DICOMQI/autoload/IO/mitkDICOMQIIOMimeTypes.h index 625cedd88e..f5ea23c857 100644 --- a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMQIIOMimeTypes.h +++ b/Modules/DICOMQI/autoload/IO/mitkDICOMQIIOMimeTypes.h @@ -1,53 +1,69 @@ /*=================================================================== 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 MITKDICOMQIIOMIMETYPES_H #define MITKDICOMQIIOMIMETYPES_H #include "mitkCustomMimeType.h" #include namespace mitk { - /// Provides the custom mime types for dicom segmentation objects loaded with DCMQI + /// Provides the custom mime types for dicom qi objects loaded with DCMQI class MitkDICOMQIIOMimeTypes { public: /** Mime type that parses dicom files to determine whether they are dicom segmentation objects. */ - class MitkDICOMQIMimeType : public CustomMimeType + class MitkDICOMSEGMimeType : public CustomMimeType { public: - MitkDICOMQIMimeType(); + MitkDICOMSEGMimeType(); bool AppliesTo(const std::string &path) const override; - MitkDICOMQIMimeType *Clone() const override; + MitkDICOMSEGMimeType *Clone() const override; }; - static MitkDICOMQIMimeType DICOMQI_MIMETYPE(); - static std::string DICOMQI_MIMETYPE_NAME(); + static MitkDICOMSEGMimeType DICOMSEG_MIMETYPE(); + static std::string DICOMSEG_MIMETYPE_NAME(); + + /** Mime type that parses dicom files to determine whether they are dicom pm objects. + */ + /* + class MitkDICOMPMMimeType : public CustomMimeType + { + public: + MitkDICOMPMMimeType(); + virtual bool AppliesTo(const std::string &path) const override; + virtual MitkDICOMPMMimeType *Clone() const override; + }; + + static MitkDICOMPMMimeType DICOMPM_MIMETYPE(); + static std::string DICOMPM_MIMETYPE_NAME(); + */ + // Get all Mime Types static std::vector Get(); private: // purposely not implemented MitkDICOMQIIOMimeTypes(); MitkDICOMQIIOMimeTypes(const MitkDICOMQIIOMimeTypes &); }; } #endif // MITKDICOMQIIOMIMETYPES_H diff --git a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.cpp b/Modules/DICOMQI/autoload/IO/mitkDICOMSegmentationIO.cpp similarity index 92% rename from Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.cpp rename to Modules/DICOMQI/autoload/IO/mitkDICOMSegmentationIO.cpp index 7a78ff006b..b269692655 100644 --- a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.cpp +++ b/Modules/DICOMQI/autoload/IO/mitkDICOMSegmentationIO.cpp @@ -1,706 +1,692 @@ /*=================================================================== 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 __mitkDICOMSegmentationIO__cpp #define __mitkDICOMSegmentationIO__cpp #include "mitkDICOMSegmentationIO.h" #include "mitkDICOMQIIOMimeTypes.h" #include "mitkDICOMSegmentationConstants.h" #include #include #include #include #include #include #include #include // itk #include // dcmqi #include // us #include #include namespace mitk { DICOMSegmentationIO::DICOMSegmentationIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), - mitk::MitkDICOMQIIOMimeTypes::DICOMQI_MIMETYPE_NAME(), - "DICOM Segmentation") + mitk::MitkDICOMQIIOMimeTypes::DICOMQI_MIMETYPE_NAME(), + "DICOM Segmentation") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); this->AddDICOMTagsToService(); } void DICOMSegmentationIO::AddDICOMTagsToService() { IDICOMTagsOfInterest *toiService = GetDicomTagsOfInterestService(); if (toiService != nullptr) { toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()); } } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const LabelSetImage *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void DICOMSegmentationIO::Write() { ValidateOutputLocation(); mitk::LocaleSwitch localeSwitch("C"); LocalFile localFile(this); const std::string path = localFile.GetFileName(); auto input = dynamic_cast(this->GetInput()); if (input == nullptr) mitkThrow() << "Cannot write non-image data"; // Get DICOM information from referenced image - vector dcmDatasets; - DcmFileFormat *readFileFormat = new DcmFileFormat(); + vector> dcmDatasetsSourceImage; + std::unique_ptr readFileFormat(new DcmFileFormat()); try { // TODO: Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the // property list mitk::StringLookupTableProperty::Pointer filesProp = dynamic_cast(input->GetProperty("referenceFiles").GetPointer()); if (filesProp.IsNull()) { mitkThrow() << "No property with dicom file path."; return; } StringLookupTable filesLut = filesProp->GetValue(); const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable(); for (auto it : lookUpTableMap) { const char *fileName = (it.second).c_str(); if (readFileFormat->loadFile(fileName, EXS_Unknown).good()) - dcmDatasets.push_back(readFileFormat->getAndRemoveDataset()); + { + std::unique_ptr readDCMDataset(readFileFormat->getAndRemoveDataset()); + dcmDatasetsSourceImage.push_back(std::move(readDCMDataset)); + } } } catch (const std::exception &e) { MITK_ERROR << "An error occurred while getting the dicom informations: " << e.what() << endl; return; } // Iterate over all layers. For each a dcm file will be generated for (unsigned int layer = 0; layer < input->GetNumberOfLayers(); ++layer) { vector segmentations; try { // Hack: Remove the const attribute to switch between the layer images. Normally you could get the different // layer images by input->GetLayerImage(layer) mitk::LabelSetImage *mitkLayerImage = const_cast(input); mitkLayerImage->SetActiveLayer(layer); // Cast mitk layer image to itk ImageToItk::Pointer imageToItkFilter = ImageToItk::New(); imageToItkFilter->SetInput(mitkLayerImage); - imageToItkFilter->Update(); - // Cast from original itk type to dcmqi input itk image type typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(imageToItkFilter->GetOutput()); castFilter->Update(); itkInternalImageType::Pointer itkLabelImage = castFilter->GetOutput(); itkLabelImage->DisconnectPipeline(); // Iterate over all labels. For each label a segmentation image will be created const LabelSet *labelSet = input->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { // Thresold over the image with the given label value itk::ThresholdImageFilter::Pointer thresholdFilter = itk::ThresholdImageFilter::New(); thresholdFilter->SetInput(itkLabelImage); thresholdFilter->ThresholdOutside(labelIter->first, labelIter->first); thresholdFilter->SetOutsideValue(0); thresholdFilter->Update(); itkInternalImageType::Pointer segmentImage = thresholdFilter->GetOutput(); segmentImage->DisconnectPipeline(); segmentations.push_back(segmentImage); } } catch (const itk::ExceptionObject &e) { MITK_ERROR << e.GetDescription() << endl; return; } // Create segmentation meta information - const std::string &tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer); + const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer); MITK_INFO << "Writing image: " << path << std::endl; try { + //TODO is there a better way? Interface expects a vector of raw pointer. + vector rawVecDataset; + for (const auto& dcmDataSet : dcmDatasetsSourceImage) + rawVecDataset.push_back(dcmDataSet.get()); + // Convert itk segmentation images to dicom image - dcmqi::ImageSEGConverter *converter = new dcmqi::ImageSEGConverter(); - DcmDataset *result = converter->itkimage2dcmSegmentation(dcmDatasets, segmentations, tmpMetaInfoFile); + std::unique_ptr converter = std::make_unique(); + std::unique_ptr result(converter->itkimage2dcmSegmentation(rawVecDataset, segmentations, tmpMetaInfoFile)); // Write dicom file - DcmFileFormat dcmFileFormat(result); + DcmFileFormat dcmFileFormat(result.get()); std::string filePath = path.substr(0, path.find_last_of(".")); // If there is more than one layer, we have to write more than 1 dicom file if (input->GetNumberOfLayers() != 1) filePath = filePath + std::to_string(layer) + ".dcm"; else filePath = filePath + ".dcm"; dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit); - - // Clean up - if (converter != nullptr) - delete converter; - if (result != nullptr) - delete result; } catch (const std::exception &e) { MITK_ERROR << "An error occurred during writing the DICOM Seg: " << e.what() << endl; return; } } // Write a dcm file for the next layer - - // End of image writing; clean up - if (readFileFormat) - delete readFileFormat; - - for (auto obj : dcmDatasets) - delete obj; - dcmDatasets.clear(); } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(fileName.c_str()); if (status.bad()) return Unsupported; OFString modality; if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good()) { if (modality.compare("SEG") == 0) return Supported; else return Unsupported; } return Unsupported; } std::vector DICOMSegmentationIO::Read() { mitk::LocaleSwitch localeSwitch("C"); LabelSetImage::Pointer labelSetImage; std::vector result; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << std::endl; if (path.empty()) mitkThrow() << "Empty filename in mitk::ItkImageIO "; try { // Get the dcm data set from file path DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(path.c_str()); if (status.bad()) mitkThrow() << "Can't read the input file!"; DcmDataset *dataSet = dcmFileFormat.getDataset(); if (dataSet == nullptr) mitkThrow() << "Can't read data from input file!"; //=============================== dcmqi part ==================================== // Read the DICOM SEG images (segItkImages) and DICOM tags (metaInfo) - dcmqi::ImageSEGConverter *converter = new dcmqi::ImageSEGConverter(); + std::unique_ptr converter = std::make_unique(); pair, string> dcmqiOutput = converter->dcmSegmentation2itkimage(dataSet); map segItkImages = dcmqiOutput.first; dcmqi::JSONSegmentationMetaInformationHandler metaInfo(dcmqiOutput.second.c_str()); metaInfo.read(); MITK_INFO << "Input " << metaInfo.getJSONOutputAsString(); //=============================================================================== // Get the label information from segment attributes for each itk image vector>::const_iterator segmentIter = metaInfo.segmentsAttributesMappingList.begin(); // For each itk image add a layer to the LabelSetImage output for (auto &element : segItkImages) { // Get the labeled image and cast it to mitkImage typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(element.second); castFilter->Update(); Image::Pointer layerImage; CastToMitkImage(castFilter->GetOutput(), layerImage); // Get pixel value of the label itkInternalImageType::ValueType segValue = 1; typedef itk::ImageRegionIterator IteratorType; // Iterate over the image to find the pixel value of the label IteratorType iter(element.second, element.second->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { itkInputImageType::PixelType value = iter.Get(); if (value != 0) { segValue = value; break; } ++iter; } // Get Segment information map map segmentMap = (*segmentIter); map::const_iterator segmentMapIter = (*segmentIter).begin(); dcmqi::SegmentAttributes *segmentAttribute = (*segmentMapIter).second; OFString labelName; if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr) { segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName); if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr) { OFString modifier; segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier); labelName.append(" (").append(modifier).append(")"); } } else { labelName = std::to_string(segmentAttribute->getLabelID()).c_str(); if (labelName.empty()) labelName = "Unnamed"; } - float tmp[3] = {0.0, 0.0, 0.0}; + float tmp[3] = { 0.0, 0.0, 0.0 }; if (segmentAttribute->getRecommendedDisplayRGBValue() != nullptr) { tmp[0] = segmentAttribute->getRecommendedDisplayRGBValue()[0] / 255.0; tmp[1] = segmentAttribute->getRecommendedDisplayRGBValue()[1] / 255.0; tmp[2] = segmentAttribute->getRecommendedDisplayRGBValue()[2] / 255.0; } Label *newLabel = nullptr; // If labelSetImage do not exists (first image) if (labelSetImage.IsNull()) { // Initialize the labelSetImage with the read image labelSetImage = LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(layerImage); // Already a label was generated, so set the information to this newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer()); newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); } else { // Add a new layer to the labelSetImage. Background label is set automatically labelSetImage->AddLayer(layerImage); // Add new label newLabel = new Label; newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); labelSetImage->GetLabelSet(labelSetImage->GetActiveLayer())->AddLabel(newLabel); } // Add some more label properties this->SetLabelProperties(newLabel, segmentAttribute); ++segmentIter; } // Add some general DICOM Segmentation properties mitk::IDICOMTagsOfInterest *toiSrv = GetDicomTagsOfInterestService(); auto tagsOfInterest = toiSrv->GetTagsOfInterest(); DICOMTagPathList tagsOfInterestList; for (const auto &tag : tagsOfInterest) { tagsOfInterestList.push_back(tag.first); } mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); - scanner->SetInputFiles({GetInputLocation()}); + scanner->SetInputFiles({ GetInputLocation() }); scanner->AddTagPaths(tagsOfInterestList); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); if (frames.empty()) { MITK_ERROR << "Error reading the DICOM Seg file" << std::endl; return result; } auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); SetProperties(labelSetImage, findings); // Set active layer to the first layer of the labelset image if (labelSetImage->GetNumberOfLayers() > 1 && labelSetImage->GetActiveLayer() != 0) labelSetImage->SetActiveLayer(0); - - // Clean up - if (converter != nullptr) - delete converter; } catch (const std::exception &e) { MITK_ERROR << "An error occurred while reading the DICOM Seg file: " << e.what(); return result; } catch (...) { MITK_ERROR << "An error occurred in dcmqi while reading the DICOM Seg file"; return result; } - result.push_back(labelSetImage.GetPointer()); - return result; } const std::string mitk::DICOMSegmentationIO::CreateMetaDataJsonFile(int layer) { const mitk::LabelSetImage *image = dynamic_cast(this->GetInput()); const std::string output; dcmqi::JSONSegmentationMetaInformationHandler handler; // 1. Metadata attributes that will be listed in the resulting DICOM SEG object std::string contentCreatorName; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(), - contentCreatorName)) + contentCreatorName)) contentCreatorName = "MITK"; handler.setContentCreatorName(contentCreatorName); std::string clinicalTrailSeriesId; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(), - clinicalTrailSeriesId)) + clinicalTrailSeriesId)) clinicalTrailSeriesId = "Session 1"; handler.setClinicalTrialSeriesID(clinicalTrailSeriesId); std::string clinicalTrialTimePointID; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(), - clinicalTrialTimePointID)) + clinicalTrialTimePointID)) clinicalTrialTimePointID = "0"; handler.setClinicalTrialTimePointID(clinicalTrialTimePointID); std::string clinicalTrialCoordinatingCenterName = ""; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(), - clinicalTrialCoordinatingCenterName)) + clinicalTrialCoordinatingCenterName)) clinicalTrialCoordinatingCenterName = "Unknown"; handler.setClinicalTrialCoordinatingCenterName(clinicalTrialCoordinatingCenterName); std::string seriesDescription; if (!image->GetPropertyList()->GetStringProperty("name", seriesDescription)) seriesDescription = "MITK Segmentation"; handler.setSeriesDescription(seriesDescription); handler.setSeriesNumber("0" + std::to_string(layer)); handler.setInstanceNumber("1"); handler.setBodyPartExamined(""); const LabelSet *labelSet = image->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { const Label *label = labelIter->second; if (label != nullptr) { TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str())); TemporoSpatialStringProperty *segmentLabelProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str())); TemporoSpatialStringProperty *algorithmTypeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str())); dcmqi::SegmentAttributes *segmentAttribute = nullptr; if (segmentNumberProp->GetValue() == "") { MITK_ERROR << "Something went wrong with the label ID."; } else { int labelId = std::stoi(segmentNumberProp->GetValue()); segmentAttribute = handler.createAndGetNewSegment(labelId); } if (segmentAttribute != nullptr) { segmentAttribute->setSegmentDescription(segmentLabelProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmType(algorithmTypeProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmName("MITK Segmentation"); if (segmentCategoryCodeValueProp != nullptr && segmentCategoryCodeSchemeProp != nullptr && - segmentCategoryCodeMeaningProp != nullptr) + segmentCategoryCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyCategoryCodeSequence( segmentCategoryCodeValueProp->GetValueAsString(), segmentCategoryCodeSchemeProp->GetValueAsString(), segmentCategoryCodeMeaningProp->GetValueAsString()); else // some default values segmentAttribute->setSegmentedPropertyCategoryCodeSequence( "M-01000", "SRT", "Morphologically Altered Structure"); if (segmentTypeCodeValueProp != nullptr && segmentTypeCodeSchemeProp != nullptr && - segmentTypeCodeMeaningProp != nullptr) + segmentTypeCodeMeaningProp != nullptr) { segmentAttribute->setSegmentedPropertyTypeCodeSequence(segmentTypeCodeValueProp->GetValueAsString(), - segmentTypeCodeSchemeProp->GetValueAsString(), - segmentTypeCodeMeaningProp->GetValueAsString()); + segmentTypeCodeSchemeProp->GetValueAsString(), + segmentTypeCodeMeaningProp->GetValueAsString()); handler.setBodyPartExamined(segmentTypeCodeMeaningProp->GetValueAsString()); } else { // some default values segmentAttribute->setSegmentedPropertyTypeCodeSequence("M-03000", "SRT", "Mass"); handler.setBodyPartExamined("Mass"); } if (segmentModifierCodeValueProp != nullptr && segmentModifierCodeSchemeProp != nullptr && - segmentModifierCodeMeaningProp != nullptr) + segmentModifierCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyTypeModifierCodeSequence( segmentModifierCodeValueProp->GetValueAsString(), segmentModifierCodeSchemeProp->GetValueAsString(), segmentModifierCodeMeaningProp->GetValueAsString()); Color color = label->GetColor(); segmentAttribute->setRecommendedDisplayRGBValue(color[0] * 255, color[1] * 255, color[2] * 255); } } } return handler.getJSONOutputAsString(); } void mitk::DICOMSegmentationIO::SetLabelProperties(mitk::Label *label, dcmqi::SegmentAttributes *segmentAttribute) { // Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique // within the Segmentation instance in which it is created label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(), - TemporoSpatialStringProperty::New(std::to_string(label->GetValue()))); + TemporoSpatialStringProperty::New(std::to_string(label->GetValue()))); // Segment Label: User-defined label identifying this segment. label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(), - TemporoSpatialStringProperty::New(label->GetName())); + TemporoSpatialStringProperty::New(label->GetName())); // Segment Algorithm Type: Type of algorithm used to generate the segment. if (!segmentAttribute->getSegmentAlgorithmType().empty()) label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(), - TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType())); + TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType())); // Add Segmented Property Category Code Sequence tags auto categoryCodeSequence = segmentAttribute->getSegmentedPropertyCategoryCodeSequence(); if (categoryCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value categoryCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator categoryCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning categoryCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Code Sequence tags auto typeCodeSequence = segmentAttribute->getSegmentedPropertyTypeCodeSequence(); if (typeCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value typeCodeSequence->getCodeValue(codeValue); label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(), - TemporoSpatialStringProperty::New(codeValue.c_str())); + TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator typeCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning typeCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Modifier Code Sequence tags auto modifierCodeSequence = segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence(); if (modifierCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value modifierCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator modifierCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning modifierCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Atomic RegionSequence tags auto atomicRegionSequence = segmentAttribute->getAnatomicRegionSequence(); if (atomicRegionSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value atomicRegionSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator atomicRegionSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning atomicRegionSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } } DICOMSegmentationIO *DICOMSegmentationIO::IOClone() const { return new DICOMSegmentationIO(*this); } } // namespace #endif //__mitkDICOMSegmentationIO__cpp diff --git a/Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.h b/Modules/DICOMQI/autoload/IO/mitkDICOMSegmentationIO.h similarity index 100% rename from Modules/Multilabel/autoload/DICOMQIIO/mitkDICOMSegmentationIO.h rename to Modules/DICOMQI/autoload/IO/mitkDICOMSegmentationIO.h diff --git a/Modules/DICOMQI/files.cmake b/Modules/DICOMQI/files.cmake new file mode 100644 index 0000000000..4aaf3522eb --- /dev/null +++ b/Modules/DICOMQI/files.cmake @@ -0,0 +1,6 @@ +set(CPP_FILES + mitkDICOMQIPropertyHelper.cpp +) + +set(RESOURCE_FILES +) diff --git a/Modules/DICOMQI/mitkDICOMQIPropertyHelper.cpp b/Modules/DICOMQI/mitkDICOMQIPropertyHelper.cpp new file mode 100644 index 0000000000..b9c1bb6f49 --- /dev/null +++ b/Modules/DICOMQI/mitkDICOMQIPropertyHelper.cpp @@ -0,0 +1,94 @@ +/*=================================================================== + +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 +#include +#include +#include + +#include "mitkDICOMQIPropertyHelper.h" + +namespace mitk +{ + void DICOMQIPropertyHandler::DeriveDICOMSourceProperties(const BaseData *sourceDICOMImage, BaseData *derivedDICOMImage) + { + // Check if original image is a DICOM image; if so, store relevant DICOM Tags into the PropertyList of new + // segmentation image + + PropertyList::Pointer sourcePropertyList = sourceDICOMImage->GetPropertyList(); + bool parentIsDICOM = false; + + for (const auto &element : *(sourcePropertyList->GetMap())) + { + if (element.first.find("DICOM") == 0) + { + parentIsDICOM = true; + break; + } + } + + if (!parentIsDICOM) + return; + + PropertyList::Pointer propertyList = derivedDICOMImage->GetPropertyList(); + + //====== Patient information ====== + // Add DICOM Tag (0010,0010) patient's name; default "No Name" + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0010, 0x0010), "NO NAME"); + // Add DICOM Tag (0010,0020) patient id; default "No Name" + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0010, 0x0020), "NO NAME"); + // Add DICOM Tag (0010,0030) patient's birth date; no default + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0010, 0x0030)); + // Add DICOM Tag (0010,0040) patient's sex; default "U" (Unknown) + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0010, 0x0040), "U"); + + //====== General study ====== + // Add DICOM Tag (0020,000D) Study Instance UID; no default --> MANDATORY! + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0020, 0x000D)); + // Add DICOM Tag (0080,0020) Study Date; no default (think about "today") + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0080, 0x0020)); + // Add DICOM Tag (0008,0050) Accession Number; no default + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0008, 0x0050)); + // Add DICOM Tag (0008,1030) Study Description; no default + AdoptReferenceDICOMProperty(sourcePropertyList, propertyList, DICOMTag(0x0008, 0x1030)); + + //====== Reference DICOM data ====== + // Add reference file paths to referenced DICOM data + BaseProperty::Pointer dcmFilesProp = sourcePropertyList->GetProperty("files"); + if (dcmFilesProp.IsNotNull()) + propertyList->SetProperty("referenceFiles", dcmFilesProp); + } + + void DICOMQIPropertyHandler::AdoptReferenceDICOMProperty(PropertyList *referencedPropertyList, + PropertyList *propertyList, + const DICOMTag &tag, + const std::string &defaultString) + { + std::string tagString = GeneratePropertyNameForDICOMTag(tag.GetGroup(), tag.GetElement()); + + // Get DICOM property from referenced image + BaseProperty::Pointer originalProperty = referencedPropertyList->GetProperty(tagString.c_str()); + + // if property exists, copy the informtaion to the derived image + if (originalProperty.IsNotNull()) + propertyList->SetProperty(tagString.c_str(), originalProperty); + else // use the default value, if there is one + { + if (!defaultString.empty()) + propertyList->SetProperty(tagString.c_str(), TemporoSpatialStringProperty::New(defaultString).GetPointer()); + } + } +} diff --git a/Modules/DICOMQI/mitkDICOMQIPropertyHelper.h b/Modules/DICOMQI/mitkDICOMQIPropertyHelper.h new file mode 100644 index 0000000000..114ae2aff9 --- /dev/null +++ b/Modules/DICOMQI/mitkDICOMQIPropertyHelper.h @@ -0,0 +1,40 @@ +/*=================================================================== + +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 DICOMQIPROPERTYHANDLER_H_ +#define DICOMQIPROPERTYHANDLER_H_ + +#include +#include + +#include + +namespace mitk +{ + class MITKDICOMQI_EXPORT DICOMQIPropertyHandler + { + public: + static void DeriveDICOMSourceProperties(const BaseData *sourceDICOMImage, BaseData *derivedDICOMImage); + + private: + static void AdoptReferenceDICOMProperty(PropertyList *referencedPropertyList, + PropertyList *propertyList, + const DICOMTag &tag, + const std::string &defaultString = ""); + //------------- + }; +} +#endif diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 00e894d947..e34b1219a1 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,82 +1,83 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Core CommandLine AppUtil RDF LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOMReader DICOMReaderServices + DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel ImageStatistics ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation QtWidgets QtWidgetsExt Chart SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification GPGPU OpenIGTLink IGTBase IGT CameraCalibration OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI PhotoacousticsHardware PhotoacousticsAlgorithms PhotoacousticsLib US USUI DicomUI Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI DicomRT RTUI IOExt XNAT TubeGraph BiophotonicsHardware DiffusionImaging TumorInvasionAnalysis BoundingShape RenderWindowManager RenderWindowManagerUI CEST BasicImageProcessing ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI ) if(MITK_ENABLE_PIC_READER) list(APPEND MITK_MODULES IpPicSupportIO) endif() diff --git a/Modules/Multilabel/CMakeLists.txt b/Modules/Multilabel/CMakeLists.txt index dfe56177a3..b3fdf625d9 100644 --- a/Modules/Multilabel/CMakeLists.txt +++ b/Modules/Multilabel/CMakeLists.txt @@ -1,10 +1,9 @@ MITK_CREATE_MODULE( - DEPENDS MitkCore MitkAlgorithmsExt MitkSceneSerializationBase MitkDICOMReader + DEPENDS MitkCore MitkAlgorithmsExt MitkSceneSerializationBase MitkDICOMQI MitkDICOMReader PACKAGE_DEPENDS PRIVATE ITK|ITKQuadEdgeMesh+ITKAntiAlias+ITKIONRRD ) add_subdirectory(autoload/IO) -add_subdirectory(autoload/DICOMQIIO) if(BUILD_TESTING) add_subdirectory(Testing) endif() diff --git a/Modules/Multilabel/mitkDICOMSegmentationConstants.cpp b/Modules/Multilabel/mitkDICOMSegmentationConstants.cpp index d939903d01..2cff2c4791 100644 --- a/Modules/Multilabel/mitkDICOMSegmentationConstants.cpp +++ b/Modules/Multilabel/mitkDICOMSegmentationConstants.cpp @@ -1,159 +1,120 @@ /*=================================================================== 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 "mitkDICOMSegmentationConstants.h" namespace mitk { DICOMTagPath DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH() { - static DICOMTagPath path = DICOMTagPath().AddElement(0x0062, 0x0002); - return path; + return DICOMTagPath().AddElement(0x0062, 0x0002); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_NUMBER_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()).AddElement(0x0062, 0x0004); - return path; + return DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH().AddElement(0x0062, 0x0004); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_LABEL_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()).AddElement(0x0062, 0x0005); - return path; + return DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH().AddElement(0x0062, 0x0005); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()).AddElement(0x0062, 0x0008); - return path; + return DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH().AddElement(0x0062, 0x0008); } DICOMTagPath DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()).AddElement(0x0008, 0x2218); - return path; + return DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH().AddElement(0x0008, 0x2218); } DICOMTagPath DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()).AddElement(0x008, 0x0100); - return path; + return DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH().AddElement(0x008, 0x0100); } DICOMTagPath DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()).AddElement(0x008, 0x0102); - return path; + return DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH().AddElement(0x008, 0x0102); } DICOMTagPath DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()).AddElement(0x008, 0x0104); - return path; + return DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH().AddElement(0x008, 0x0104); } DICOMTagPath DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()).AddElement(0x0062, 0x0003); - return path; + return DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH().AddElement(0x0062, 0x0003); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()).AddElement(0x008, 0x0100); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH().AddElement(0x008, 0x0100); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()).AddElement(0x008, 0x0102); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH().AddElement(0x008, 0x0102); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()).AddElement(0x008, 0x0104); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH().AddElement(0x008, 0x0104); } DICOMTagPath DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()).AddElement(0x0062, 0x000F); - return path; + return DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH().AddElement(0x0062, 0x000F); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()).AddElement(0x008, 0x0100); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH().AddElement(0x008, 0x0100); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()).AddElement(0x008, 0x0102); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH().AddElement(0x008, 0x0102); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()).AddElement(0x008, 0x0104); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH().AddElement(0x008, 0x0104); } DICOMTagPath DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()).AddElement(0x0062, 0x0011); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH().AddElement(0x0062, 0x0011); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()).AddElement(0x008, 0x0100); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH().AddElement(0x008, 0x0100); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()).AddElement(0x008, 0x0102); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH().AddElement(0x008, 0x0102); } DICOMTagPath DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH() { - static DICOMTagPath path = - DICOMTagPath(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()).AddElement(0x008, 0x0104); - return path; + return DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH().AddElement(0x008, 0x0104); } } diff --git a/Modules/Multilabel/mitkDICOMSegmentationPropertyHelper.cpp b/Modules/Multilabel/mitkDICOMSegmentationPropertyHelper.cpp index dfbd67e2a8..170d926eb4 100644 --- a/Modules/Multilabel/mitkDICOMSegmentationPropertyHelper.cpp +++ b/Modules/Multilabel/mitkDICOMSegmentationPropertyHelper.cpp @@ -1,232 +1,173 @@ /*=================================================================== 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 #include -#include #include -#include -#include #include #include +#include -#include - -// us -#include -#include +#include "mitkDICOMSegmentationPropertyHelper.h" namespace mitk { - struct MITKMULTILABEL_EXPORT DICOMSegmentationPropertyHandler + void DICOMSegmentationPropertyHandler::DeriveDICOMSegmentationProperties(LabelSetImage* dicomSegImage) { - static PropertyList::Pointer GetDICOMSegmentationProperties(PropertyList *referencedPropertyList) + PropertyList::Pointer propertyList = dicomSegImage->GetPropertyList(); + + // Add DICOM Tag (0008, 0060) Modality "SEG" + propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), + TemporoSpatialStringProperty::New("SEG")); + // Add DICOM Tag (0008,103E) Series Description + propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x103E).c_str(), + TemporoSpatialStringProperty::New("MITK Segmentation")); + // Add DICOM Tag (0070,0084) Content Creator Name + propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(), + TemporoSpatialStringProperty::New("MITK")); + // Add DICOM Tag (0012, 0071) Clinical Trial Series ID + propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(), + TemporoSpatialStringProperty::New("Session 1")); + // Add DICOM Tag (0012,0050) Clinical Trial Time Point ID + propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(), + TemporoSpatialStringProperty::New("0")); + // Add DICOM Tag (0012, 0060) Clinical Trial Coordinating Center Name + propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(), + TemporoSpatialStringProperty::New("Unknown")); + + // Set DICOM properties for each label + // Iterate over all layers + for (unsigned int layer = 0; layer < dicomSegImage->GetNumberOfLayers(); ++layer) { - PropertyList::Pointer propertyList = PropertyList::New(); - - // Add DICOM Tag (0008, 0060) Modality "SEG" - propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str(), - TemporoSpatialStringProperty::New("SEG")); - // Add DICOM Tag (0008,103E) Series Description - propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x103E).c_str(), - TemporoSpatialStringProperty::New("MITK Segmentation")); - // Add DICOM Tag (0070,0084) Content Creator Name - propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(), - TemporoSpatialStringProperty::New("MITK")); - // Add DICOM Tag (0012, 0071) Clinical Trial Series ID - propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(), - TemporoSpatialStringProperty::New("Session 1")); - // Add DICOM Tag (0012,0050) Clinical Trial Time Point ID - propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(), - TemporoSpatialStringProperty::New("0")); - // Add DICOM Tag (0012, 0060) Clinical Trial Coordinating Center Name - propertyList->SetProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(), - TemporoSpatialStringProperty::New("Unknown")); - - // Check if original image is a DICOM image; if so, store relevant DICOM Tags into the PropertyList of new - // segmentation image - bool parentIsDICOM = false; - - for (const auto &element : *(referencedPropertyList->GetMap())) + // Iterate over all labels + const LabelSet *labelSet = dicomSegImage->GetLabelSet(layer); + auto labelIter = labelSet->IteratorConstBegin(); + // Ignore background label + ++labelIter; + + for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { - if (element.first.find("DICOM") == 0) - { - parentIsDICOM = true; - break; - } + Label::Pointer label = labelIter->second; + SetDICOMSegmentProperties(label); } - - if (!parentIsDICOM) - return propertyList; - - //====== Patient information ====== - - // Add DICOM Tag (0010,0010) patient's name; default "No Name" - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0010, 0x0010), "NO NAME"); - // Add DICOM Tag (0010,0020) patient id; default "No Name" - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0010, 0x0020), "NO NAME"); - // Add DICOM Tag (0010,0030) patient's birth date; no default - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0010, 0x0030)); - // Add DICOM Tag (0010,0040) patient's sex; default "U" (Unknown) - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0010, 0x0040), "U"); - - //====== General study ====== - - // Add DICOM Tag (0020,000D) Study Instance UID; no default --> MANDATORY! - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0020, 0x000D)); - // Add DICOM Tag (0080,0020) Study Date; no default (think about "today") - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0080, 0x0020)); - // Add DICOM Tag (0008,0050) Accession Number; no default - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0008, 0x0050)); - // Add DICOM Tag (0008,1030) Study Description; no default - SetReferenceDICOMProperty(referencedPropertyList, propertyList, DICOMTag(0x0008, 0x1030)); - - //====== Reference DICOM data ====== - - // Add reference file paths to referenced DICOM data - BaseProperty::Pointer dcmFilesProp = referencedPropertyList->GetProperty("files"); - if (dcmFilesProp.IsNotNull()) - propertyList->SetProperty("referenceFiles", dcmFilesProp); - - return propertyList; } + } - static void GetDICOMSegmentProperties(Label *label) - { - PropertyList::Pointer propertyList = PropertyList::New(); - - AnatomicalStructureColorPresets::Category category; - AnatomicalStructureColorPresets::Type type; - AnatomicalStructureColorPresets *anatomicalStructureColorPresets = AnatomicalStructureColorPresets::New(); - anatomicalStructureColorPresets->LoadPreset(); + void DICOMSegmentationPropertyHandler::SetDICOMSegmentProperties(Label *label) + { + PropertyList::Pointer propertyList = PropertyList::New(); - for (const auto &preset : anatomicalStructureColorPresets->GetCategoryPresets()) - { - auto presetOrganName = preset.first; - if (label->GetName().compare(presetOrganName) == 0) - { - category = preset.second; - break; - } - } + AnatomicalStructureColorPresets::Category category; + AnatomicalStructureColorPresets::Type type; + AnatomicalStructureColorPresets *anatomicalStructureColorPresets = AnatomicalStructureColorPresets::New(); + anatomicalStructureColorPresets->LoadPreset(); - for (const auto &preset : anatomicalStructureColorPresets->GetTypePresets()) + for (const auto &preset : anatomicalStructureColorPresets->GetCategoryPresets()) + { + auto presetOrganName = preset.first; + if (label->GetName().compare(presetOrganName) == 0) { - auto presetOrganName = preset.first; - if (label->GetName().compare(presetOrganName) == 0) - { - type = preset.second; - break; - } + category = preset.second; + break; } - - //------------------------------------------------------------ - // Add Segment Sequence tags (0062, 0002) - // Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique - // within the Segmentation instance in which it is created - label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(), - TemporoSpatialStringProperty::New(std::to_string(label->GetValue()))); - - // Segment Label: User-defined label identifying this segment. - label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(), - TemporoSpatialStringProperty::New(label->GetName())); - - // Segment Algorithm Type: Type of algorithm used to generate the segment. AUTOMATIC SEMIAUTOMATIC MANUAL - label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(), - TemporoSpatialStringProperty::New("SEMIAUTOMATIC")); - //------------------------------------------------------------ - // Add Segmented Property Category Code Sequence tags (0062, 0003): Sequence defining the general category of this - // segment. - // (0008,0100) Code Value - if (!category.codeValue.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(), - TemporoSpatialStringProperty::New(category.codeValue)); - - // (0008,0102) Coding Scheme Designator - if (!category.codeScheme.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(), - TemporoSpatialStringProperty::New(category.codeScheme)); - - // (0008,0104) Code Meaning - if (!category.codeName.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(), - TemporoSpatialStringProperty::New(category.codeName)); - //------------------------------------------------------------ - // Add Segmented Property Type Code Sequence (0062, 000F): Sequence defining the specific property type of this - // segment. - // (0008,0100) Code Value - if (!type.codeValue.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(), - TemporoSpatialStringProperty::New(type.codeValue)); - - // (0008,0102) Coding Scheme Designator - if (!type.codeScheme.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(), - TemporoSpatialStringProperty::New(type.codeScheme)); - - // (0008,0104) Code Meaning - if (!type.codeName.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(), - TemporoSpatialStringProperty::New(type.codeName)); - //------------------------------------------------------------ - // Add Segmented Property Type Modifier Code Sequence (0062,0011): Sequence defining the modifier of the property - // type of this segment. - // (0008,0100) Code Value - if (!type.modifier.codeValue.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(), - TemporoSpatialStringProperty::New(type.modifier.codeValue)); - - // (0008,0102) Coding Scheme Designator - if (!type.modifier.codeScheme.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(), - TemporoSpatialStringProperty::New(type.modifier.codeScheme)); - - // (0008,0104) Code Meaning - if (!type.modifier.codeName.empty()) - label->SetProperty( - DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(), - TemporoSpatialStringProperty::New(type.modifier.codeName)); } - static void SetReferenceDICOMProperty(PropertyList *referencedPropertyList, - PropertyList *propertyList, - const DICOMTag &tag, - const std::string &defaultString = "") + for (const auto &preset : anatomicalStructureColorPresets->GetTypePresets()) { - std::string tagString = GeneratePropertyNameForDICOMTag(tag.GetGroup(), tag.GetElement()); - - // Get DICOM property from referenced image - BaseProperty::Pointer originalProperty = referencedPropertyList->GetProperty(tagString.c_str()); - - // if property exists, copy the informtaion to the segmentation - if (originalProperty.IsNotNull()) - propertyList->SetProperty(tagString.c_str(), originalProperty); - else // use the default value, if there is one + auto presetOrganName = preset.first; + if (label->GetName().compare(presetOrganName) == 0) { - if (!defaultString.empty()) - propertyList->SetProperty(tagString.c_str(), TemporoSpatialStringProperty::New(defaultString).GetPointer()); + type = preset.second; + break; } } - }; + + //------------------------------------------------------------ + // Add Segment Sequence tags (0062, 0002) + // Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique + // within the Segmentation instance in which it is created + label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(), + TemporoSpatialStringProperty::New(std::to_string(label->GetValue()))); + + // Segment Label: User-defined label identifying this segment. + label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(), + TemporoSpatialStringProperty::New(label->GetName())); + + // Segment Algorithm Type: Type of algorithm used to generate the segment. AUTOMATIC SEMIAUTOMATIC MANUAL + label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(), + TemporoSpatialStringProperty::New("SEMIAUTOMATIC")); + //------------------------------------------------------------ + // Add Segmented Property Category Code Sequence tags (0062, 0003): Sequence defining the general category of this + // segment. + // (0008,0100) Code Value + if (!category.codeValue.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(), + TemporoSpatialStringProperty::New(category.codeValue)); + + // (0008,0102) Coding Scheme Designator + if (!category.codeScheme.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(), + TemporoSpatialStringProperty::New(category.codeScheme)); + + // (0008,0104) Code Meaning + if (!category.codeName.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(), + TemporoSpatialStringProperty::New(category.codeName)); + //------------------------------------------------------------ + // Add Segmented Property Type Code Sequence (0062, 000F): Sequence defining the specific property type of this + // segment. + // (0008,0100) Code Value + if (!type.codeValue.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(), + TemporoSpatialStringProperty::New(type.codeValue)); + + // (0008,0102) Coding Scheme Designator + if (!type.codeScheme.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(), + TemporoSpatialStringProperty::New(type.codeScheme)); + + // (0008,0104) Code Meaning + if (!type.codeName.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(), + TemporoSpatialStringProperty::New(type.codeName)); + //------------------------------------------------------------ + // Add Segmented Property Type Modifier Code Sequence (0062,0011): Sequence defining the modifier of the property + // type of this segment. + // (0008,0100) Code Value + if (!type.modifier.codeValue.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(), + TemporoSpatialStringProperty::New(type.modifier.codeValue)); + + // (0008,0102) Coding Scheme Designator + if (!type.modifier.codeScheme.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(), + TemporoSpatialStringProperty::New(type.modifier.codeScheme)); + + // (0008,0104) Code Meaning + if (!type.modifier.codeName.empty()) + label->SetProperty( + DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(), + TemporoSpatialStringProperty::New(type.modifier.codeName)); + } } diff --git a/Modules/Multilabel/mitkDICOMSegmentationPropertyHelper.h b/Modules/Multilabel/mitkDICOMSegmentationPropertyHelper.h new file mode 100644 index 0000000000..76c9abbaa0 --- /dev/null +++ b/Modules/Multilabel/mitkDICOMSegmentationPropertyHelper.h @@ -0,0 +1,35 @@ +/*=================================================================== + +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 DICOMSEGMENTATIONPROPERTYHANDLER_H_ +#define DICOMSEGMENTATIONPROPERTYHANDLER_H_ + +#include +#include +#include + +#include + +namespace mitk +{ + class MITKMULTILABEL_EXPORT DICOMSegmentationPropertyHandler + { + public: + static void DeriveDICOMSegmentationProperties(LabelSetImage* dicomSegImage); + static void SetDICOMSegmentProperties(Label *label); + }; +} +#endif diff --git a/Modules/Multilabel/mitkLabelSet.cpp b/Modules/Multilabel/mitkLabelSet.cpp index 5314023b3d..b6cc3b9248 100644 --- a/Modules/Multilabel/mitkLabelSet.cpp +++ b/Modules/Multilabel/mitkLabelSet.cpp @@ -1,337 +1,345 @@ /*=================================================================== 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 "mitkLabelSet.h" +#include "mitkDICOMSegmentationPropertyHelper.h" + #include mitk::LabelSet::LabelSet() : m_ActiveLabelValue(0), m_Layer(0) { m_LookupTable = mitk::LookupTable::New(); m_LookupTable->SetType(mitk::LookupTable::MULTILABEL); } mitk::LabelSet::~LabelSet() { m_LabelContainer.clear(); } mitk::LabelSet::LabelSet(const LabelSet &other) : itk::Object(), m_LookupTable(other.GetLookupTable()->Clone()), m_ActiveLabelValue(other.GetActiveLabel()->GetValue()), m_Layer(other.GetLayer()) { // clone Labels auto otherIt = other.IteratorConstBegin(); for (; otherIt != other.IteratorConstEnd(); ++otherIt) { m_LabelContainer[otherIt->first] = otherIt->second->Clone(); itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &LabelSet::OnLabelModified); m_LabelContainer[otherIt->first]->AddObserver(itk::ModifiedEvent(), command); } } void mitk::LabelSet::OnLabelModified() { ModifyLabelEvent.Send(); Superclass::Modified(); } mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstEnd() const { return m_LabelContainer.end(); } mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstBegin() const { return m_LabelContainer.begin(); } mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorEnd() { return m_LabelContainer.end(); } mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorBegin() { return m_LabelContainer.begin(); } unsigned int mitk::LabelSet::GetNumberOfLabels() const { return m_LabelContainer.size(); } void mitk::LabelSet::SetLayer(unsigned int layer) { m_Layer = layer; Modified(); } void mitk::LabelSet::SetActiveLabel(PixelType pixelValue) { m_ActiveLabelValue = pixelValue; ActiveLabelEvent.Send(pixelValue); Modified(); } bool mitk::LabelSet::ExistLabel(PixelType pixelValue) { return m_LabelContainer.count(pixelValue) > 0 ? true : false; } // TODO Parameter as Smartpointer void mitk::LabelSet::AddLabel(mitk::Label *label) { unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1; if (m_LabelContainer.size() >= max_size) return; mitk::Label::Pointer newLabel(label->Clone()); // TODO use layer of label parameter newLabel->SetLayer(m_Layer); PixelType pixelValue; if (m_LabelContainer.empty()) { pixelValue = newLabel->GetValue(); } else { pixelValue = m_LabelContainer.rbegin()->first; if (pixelValue >= newLabel->GetValue() && m_LabelContainer.find(newLabel->GetValue()) != m_LabelContainer.end()) { ++pixelValue; newLabel->SetValue(pixelValue); } else { pixelValue = newLabel->GetValue(); } } // new map entry m_LabelContainer[pixelValue] = newLabel; UpdateLookupTable(pixelValue); + // add DICOM information of the label + DICOMSegmentationPropertyHandler::SetDICOMSegmentProperties(newLabel); + itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &LabelSet::OnLabelModified); newLabel->AddObserver(itk::ModifiedEvent(), command); // newLabel->AddObserver(itk::ModifiedEvent(),command); SetActiveLabel(newLabel->GetValue()); AddLabelEvent.Send(); Modified(); } void mitk::LabelSet::AddLabel(const std::string &name, const mitk::Color &color) { if (m_LabelContainer.size() > 255) return; mitk::Label::Pointer newLabel = mitk::Label::New(); newLabel->SetName(name); newLabel->SetColor(color); AddLabel(newLabel); } void mitk::LabelSet::RenameLabel(PixelType pixelValue, const std::string &name, const mitk::Color &color) { mitk::Label *label = GetLabel(pixelValue); label->SetName(name); label->SetColor(color); + + // change DICOM information of the label + DICOMSegmentationPropertyHandler::SetDICOMSegmentProperties(label); } void mitk::LabelSet::SetLookupTable(mitk::LookupTable *lut) { m_LookupTable = lut; Modified(); } void mitk::LabelSet::PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const { } void mitk::LabelSet::RemoveLabel(PixelType pixelValue) { auto it = m_LabelContainer.rbegin(); PixelType nextActivePixelValue = it->first; for (; it != m_LabelContainer.rend(); it++) { if (it->first == pixelValue) { it->second->RemoveAllObservers(); m_LabelContainer.erase(pixelValue); break; } nextActivePixelValue = it->first; } if (m_ActiveLabelValue == pixelValue) { if (ExistLabel(nextActivePixelValue)) SetActiveLabel(nextActivePixelValue); else SetActiveLabel(m_LabelContainer.rbegin()->first); } RemoveLabelEvent.Send(); Modified(); } void mitk::LabelSet::RemoveAllLabels() { auto _it = IteratorBegin(); for (; _it != IteratorConstEnd();) { RemoveLabelEvent.Send(); m_LabelContainer.erase(_it++); } AllLabelsModifiedEvent.Send(); } void mitk::LabelSet::SetAllLabelsLocked(bool value) { auto _end = m_LabelContainer.end(); auto _it = m_LabelContainer.begin(); for (; _it != _end; ++_it) _it->second->SetLocked(value); AllLabelsModifiedEvent.Send(); Modified(); } void mitk::LabelSet::SetAllLabelsVisible(bool value) { auto _end = m_LabelContainer.end(); auto _it = m_LabelContainer.begin(); for (; _it != _end; ++_it) { _it->second->SetVisible(value); UpdateLookupTable(_it->first); } AllLabelsModifiedEvent.Send(); Modified(); } void mitk::LabelSet::UpdateLookupTable(PixelType pixelValue) { const mitk::Color &color = GetLabel(pixelValue)->GetColor(); double rgba[4]; m_LookupTable->GetTableValue(static_cast(pixelValue), rgba); rgba[0] = color.GetRed(); rgba[1] = color.GetGreen(); rgba[2] = color.GetBlue(); if (GetLabel(pixelValue)->GetVisible()) rgba[3] = GetLabel(pixelValue)->GetOpacity(); else rgba[3] = 0.0; m_LookupTable->SetTableValue(static_cast(pixelValue), rgba); } mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue) { if (m_LabelContainer.find(pixelValue) == m_LabelContainer.end()) return nullptr; return m_LabelContainer[pixelValue]; } const mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue) const { auto it = m_LabelContainer.find(pixelValue); if (it == m_LabelContainer.end()) return nullptr; return it->second.GetPointer(); } bool mitk::Equal(const mitk::LabelSet &leftHandSide, const mitk::LabelSet &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // LabelSetmembers MITK_INFO(verbose) << "--- LabelSet Equal ---"; // m_LookupTable; const mitk::LookupTable *lhsLUT = leftHandSide.GetLookupTable(); const mitk::LookupTable *rhsLUT = rightHandSide.GetLookupTable(); returnValue = *lhsLUT == *rhsLUT; if (!returnValue) { MITK_INFO(verbose) << "Lookup tabels not equal."; return returnValue; ; } // m_ActiveLabel; returnValue = mitk::Equal(*leftHandSide.GetActiveLabel(), *rightHandSide.GetActiveLabel(), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Active label not equal."; return returnValue; ; } // m_Layer; returnValue = leftHandSide.GetLayer() == rightHandSide.GetLayer(); if (!returnValue) { MITK_INFO(verbose) << "Layer index not equal."; return returnValue; ; } // container size; returnValue = leftHandSide.GetNumberOfLabels() == rightHandSide.GetNumberOfLabels(); if (!returnValue) { MITK_INFO(verbose) << "Number of labels not equal."; return returnValue; ; } // Label container (map) // m_LabelContainer; auto lhsit = leftHandSide.IteratorConstBegin(); auto rhsit = rightHandSide.IteratorConstBegin(); for (; lhsit != leftHandSide.IteratorConstEnd(); ++lhsit, ++rhsit) { returnValue = rhsit->first == lhsit->first; if (!returnValue) { MITK_INFO(verbose) << "Label in label container not equal."; return returnValue; ; } returnValue = mitk::Equal(*(rhsit->second), *(lhsit->second), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Label in label container not equal."; return returnValue; ; } } return returnValue; } diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp index 47493bbe0d..ec80b3fd17 100644 --- a/Modules/Multilabel/mitkLabelSetImage.cpp +++ b/Modules/Multilabel/mitkLabelSetImage.cpp @@ -1,963 +1,971 @@ /*=================================================================== 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 "mitkImageCast.h" #include "mitkImageReadAccessor.h" #include "mitkInteractionConst.h" #include "mitkLookupTableProperty.h" #include "mitkPadImageFilter.h" #include "mitkRenderingManager.h" +#include "mitkDICOMSegmentationPropertyHelper.h" +#include "mitkDICOMQIPropertyHelper.h" #include #include #include #include #include #include //#include #include template void SetToZero(itk::Image *source) { source->FillBuffer(0); } mitk::LabelSetImage::LabelSetImage() : mitk::Image(), m_ActiveLayer(0), m_activeLayerInvalid(false), 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); + + // Add some DICOM Tags as properties to segmentation image + DICOMSegmentationPropertyHandler::DeriveDICOMSegmentationProperties(this); } mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage &other) : Image(other), m_ActiveLayer(other.GetActiveLayer()), m_activeLayerInvalid(false), 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()); } auto originalGeometry = other->GetTimeGeometry()->Clone(); this->SetTimeGeometry(originalGeometry); // initialize image memory to zero if (4 == this->GetDimension()) { AccessFixedDimensionByItk(this, SetToZero, 4); } else { AccessByItk(this, SetToZero); } + // Transfer some general DICOM properties from the source image to derived image (e.g. Patient information,...) + DICOMQIPropertyHandler::DeriveDICOMSourceProperties(other, this); + // 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. if (layerToDelete != 0) { SetActiveLayer(layerToDelete - 1); } else { // we are deleting layer zero, it should not be copied back into the vector m_activeLayerInvalid = true; } // remove labelset and image data m_LabelSetContainer.erase(m_LabelSetContainer.begin() + layerToDelete); m_LayerContainer.erase(m_LayerContainer.begin() + layerToDelete); if (layerToDelete == 0) { this->SetActiveLayer(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->SetTimeGeometry(this->GetTimeGeometry()->Clone()); if (newImage->GetDimension() < 4) { AccessByItk(newImage, SetToZero); } else { AccessFixedDimensionByItk(newImage, SetToZero, 4); } unsigned int newLabelSetId = this->AddLayer(newImage, lset); 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 (4 == this->GetDimension()) { if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers())) { BeforeChangeLayerEvent.Send(); if (m_activeLayerInvalid) { // We should not write the invalid layer back to the vector m_activeLayerInvalid = false; } else { AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (GetActiveLayer())); } m_ActiveLayer = layer; // only at this place m_ActiveLayer should be manipulated!!! Use Getter and Setter AccessFixedDimensionByItk_n(this, LayerContainerToImageProcessing, 4, (GetActiveLayer())); AfterChangeLayerEvent.Send(); } } else { if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers())) { BeforeChangeLayerEvent.Send(); if (m_activeLayerInvalid) { // We should not write the invalid layer back to the vector m_activeLayerInvalid = false; } else { 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; layer < numberOfLayers; ++layer) { this->SetActiveLayer(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, PixelType sourcePixelValue, unsigned int layer) { try { AccessByItk_2(this, MergeLabelProcessing, pixelValue, sourcePixelValue); } catch (itk::ExceptionObject &e) { mitkThrow() << e.GetDescription(); } GetLabelSet(layer)->SetActiveLabel(pixelValue); Modified(); } void mitk::LabelSetImage::MergeLabels(PixelType pixelValue, std::vector& vectorOfSourcePixelValues, unsigned int layer) { try { for (unsigned int idx = 0; idx < vectorOfSourcePixelValues.size(); idx++) { AccessByItk_2(this, MergeLabelProcessing, pixelValue, vectorOfSourcePixelValues[idx]); } } catch (itk::ExceptionObject &e) { mitkThrow() << e.GetDescription(); } GetLabelSet(layer)->SetActiveLabel(pixelValue); Modified(); } void mitk::LabelSetImage::RemoveLabels(std::vector &VectorOfLabelPixelValues, unsigned int layer) { for (unsigned int idx = 0; idx < VectorOfLabelPixelValues.size(); idx++) { GetLabelSet(layer)->RemoveLabel(VectorOfLabelPixelValues[idx]); EraseLabel(VectorOfLabelPixelValues[idx], layer); } } void mitk::LabelSetImage::EraseLabels(std::vector &VectorOfLabelPixelValues, unsigned int layer) { for (unsigned int i = 0; i < VectorOfLabelPixelValues.size(); i++) { this->EraseLabel(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 nullptr; else return m_LabelSetContainer[layer]->GetActiveLabel();; } 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() { if (m_LabelSetContainer.size() == 0) return nullptr; else 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; auto geometry = this->GetTimeGeometry()->Clone(); mask->SetTimeGeometry(geometry); AccessByItk_2(this, CreateLabelMaskProcessing, mask, index); } catch (...) { mitkThrow() << "Could not create a mask out of the selected label."; } return mask; } 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; auto geometry = image->GetTimeGeometry()->Clone(); this->SetTimeGeometry(geometry); if (image->GetDimension() == 3) { AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 3); } else if (image->GetDimension() == 4) { AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 4); } else { mitkThrow() << image->GetDimension() << "-dimensional label set images not yet supported"; } } catch (...) { mitkThrow() << "Could not intialize by provided labeled image."; } this->Modified(); } template void mitk::LabelSetImage::InitializeByLabeledImageProcessing(LabelSetImageType *labelSetImage, ImageType *image) { typedef itk::ImageRegionConstIteratorWithIndex SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; TargetIteratorType targetIter(labelSetImage, labelSetImage->GetRequestedRegion()); targetIter.GoToBegin(); SourceIteratorType sourceIter(image, image->GetRequestedRegion()); sourceIter.GoToBegin(); while (!sourceIter.IsAtEnd()) { auto sourceValue = static_cast(sourceIter.Get()); targetIter.Set(sourceValue); if (!this->ExistLabel(sourceValue)) { std::stringstream name; name << "object-" << sourceValue; double rgba[4]; m_LabelSetContainer[this->GetActiveLayer()]->GetLookupTable()->GetTableValue(sourceValue, rgba); mitk::Color color; color.SetRed(rgba[0]); color.SetGreen(rgba[1]); color.SetBlue(rgba[2]); auto label = mitk::Label::New(); label->SetName(name.str().c_str()); label->SetColor(color); label->SetOpacity(rgba[3]); label->SetValue(sourceValue); this->GetLabelSet()->AddLabel(label); if (GetActiveLabelSet()->GetNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE || sourceValue >= mitk::Label::MAX_LABEL_VALUE) this->AddLayer(); } ++sourceIter; ++targetIter; } } template void mitk::LabelSetImage::MaskStampProcessing(ImageType *itkImage, mitk::Image *mask, bool forceOverwrite) { typename ImageType::Pointer itkMask; mitk::CastToItkImage(mask, itkMask); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator 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 void mitk::LabelSetImage::CreateLabelMaskProcessing(ImageType *itkImage, mitk::Image *mask, PixelType index) { typename ImageType::Pointer itkMask; mitk::CastToItkImage(mask, itkMask); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator 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 void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType *itkImage, PixelType pixelValue, unsigned int layer) { // for now, we just retrieve the voxel in the middle typedef itk::ImageRegionConstIterator IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); std::vector 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::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); // TODO: TimeGeometry? GetLabelSet(layer)->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos); } template void mitk::LabelSetImage::ClearBufferProcessing(ImageType *itkImage) { itkImage->FillBuffer(0); } // todo: concatenate all layers and not just the active one template void mitk::LabelSetImage::ConcatenateProcessing(ImageType *itkTarget, mitk::LabelSetImage *other) { typename ImageType::Pointer itkSource = ImageType::New(); mitk::CastToItkImage(other, itkSource); typedef itk::ImageRegionConstIterator ConstIteratorType; typedef itk::ImageRegionIterator 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 void mitk::LabelSetImage::LayerContainerToImageProcessing(itk::Image *target, unsigned int layer) { typedef itk::Image ImageType; typename ImageType::Pointer itkSource; // mitk::CastToItkImage(m_LayerContainer[layer], itkSource); itkSource = ImageToItkImage(m_LayerContainer[layer]); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator 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 void mitk::LabelSetImage::ImageToLayerContainerProcessing(itk::Image *source, unsigned int layer) const { typedef itk::Image ImageType; typename ImageType::Pointer itkTarget; // mitk::CastToItkImage(m_LayerContainer[layer], itkTarget); itkTarget = ImageToItkImage(m_LayerContainer[layer]); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator 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 void mitk::LabelSetImage::EraseLabelProcessing(ImageType *itkImage, PixelType pixelValue, unsigned int /*layer*/) { typedef itk::ImageRegionIterator IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { PixelType value = iter.Get(); if (value == pixelValue) { iter.Set(0); } ++iter; } } template void mitk::LabelSetImage::MergeLabelProcessing(ImageType *itkImage, PixelType pixelValue, PixelType index) { typedef itk::ImageRegionIterator 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; } if (4 == leftHandSide.GetDimension()) { MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check."; } else { // 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++) { if (4 == leftHandSide.GetDimension()) { MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check."; } else { // 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/Segmentation/Interactions/mitkTool.cpp b/Modules/Segmentation/Interactions/mitkTool.cpp index 2ee94efca1..f6e273a34e 100644 --- a/Modules/Segmentation/Interactions/mitkTool.cpp +++ b/Modules/Segmentation/Interactions/mitkTool.cpp @@ -1,323 +1,318 @@ /*=================================================================== 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 "mitkTool.h" #include #include "mitkDisplayInteractor.h" #include "mitkImageReadAccessor.h" #include "mitkImageWriteAccessor.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include // us #include #include // itk #include mitk::Tool::Tool(const char *type) : m_EventConfig("DisplayConfigMITK.xml"), m_ToolManager(nullptr), m_PredicateImages(NodePredicateDataType::New("Image")), // for reference images m_PredicateDim3(NodePredicateDimension::New(3, 1)), m_PredicateDim4(NodePredicateDimension::New(4, 1)), m_PredicateDimension(mitk::NodePredicateOr::New(m_PredicateDim3, m_PredicateDim4)), m_PredicateImage3D(NodePredicateAnd::New(m_PredicateImages, m_PredicateDimension)), m_PredicateBinary(NodePredicateProperty::New("binary", BoolProperty::New(true))), m_PredicateNotBinary(NodePredicateNot::New(m_PredicateBinary)), m_PredicateSegmentation(NodePredicateProperty::New("segmentation", BoolProperty::New(true))), m_PredicateNotSegmentation(NodePredicateNot::New(m_PredicateSegmentation)), m_PredicateHelper(NodePredicateProperty::New("helper object", BoolProperty::New(true))), m_PredicateNotHelper(NodePredicateNot::New(m_PredicateHelper)), m_PredicateImageColorful(NodePredicateAnd::New(m_PredicateNotBinary, m_PredicateNotSegmentation)), m_PredicateImageColorfulNotHelper(NodePredicateAnd::New(m_PredicateImageColorful, m_PredicateNotHelper)), m_PredicateReference(NodePredicateAnd::New(m_PredicateImage3D, m_PredicateImageColorfulNotHelper)), m_IsSegmentationPredicate( NodePredicateAnd::New(NodePredicateOr::New(m_PredicateBinary, m_PredicateSegmentation), m_PredicateNotHelper)), m_InteractorType(type), m_DisplayInteractorConfigs() { } mitk::Tool::~Tool() { } bool mitk::Tool::CanHandle(BaseData *) const { return true; } void mitk::Tool::InitializeStateMachine() { if (m_InteractorType.empty()) return; m_InteractorType += ".xml"; try { LoadStateMachine(m_InteractorType, us::GetModuleContext()->GetModule()); SetEventConfig("SegmentationToolsConfig.xml", us::GetModuleContext()->GetModule()); } catch (const std::exception &e) { MITK_ERROR << "Could not load statemachine pattern " << m_InteractorType << " with exception: " << e.what(); } } void mitk::Tool::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled) { this->HandleEvent(interactionEvent, nullptr); } } void mitk::Tool::ConnectActionsAndFunctions() { } bool mitk::Tool::FilterEvents(InteractionEvent *, DataNode *) { return true; } const char *mitk::Tool::GetGroup() const { return "default"; } void mitk::Tool::SetToolManager(ToolManager *manager) { m_ToolManager = manager; } void mitk::Tool::Activated() { // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_DisplayInteractorConfigs.clear(); std::vector> listEventObserver = us::GetModuleContext()->GetServiceReferences(); for (auto it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { auto *displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayInteractor != nullptr) { // remember the original configuration m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); // here the alternative configuration is loaded displayInteractor->SetEventConfig(m_EventConfig.c_str()); } } } void mitk::Tool::Deactivated() { // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (auto it = m_DisplayInteractorConfigs.begin(); it != m_DisplayInteractorConfigs.end(); ++it) { if (it->first) { DisplayInteractor *displayInteractor = static_cast(us::GetModuleContext()->GetService(it->first)); if (displayInteractor != nullptr) { // here the regular configuration is loaded again displayInteractor->SetEventConfig(it->second); } } } m_DisplayInteractorConfigs.clear(); } itk::Object::Pointer mitk::Tool::GetGUI(const std::string &toolkitPrefix, const std::string &toolkitPostfix) { itk::Object::Pointer object; std::string classname = this->GetNameOfClass(); std::string guiClassname = toolkitPrefix + classname + toolkitPostfix; std::list allGUIs = itk::ObjectFactoryBase::CreateAllInstance(guiClassname.c_str()); for (auto iter = allGUIs.begin(); iter != allGUIs.end(); ++iter) { if (object.IsNull()) { object = dynamic_cast(iter->GetPointer()); } else { MITK_ERROR << "There is more than one GUI for " << classname << " (several factories claim ability to produce a " << guiClassname << " ) " << std::endl; return nullptr; // people should see and fix this error } } return object; } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetReferenceDataPreference() const { return m_PredicateReference.GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetWorkingDataPreference() const { return m_IsSegmentationPredicate.GetPointer(); } mitk::DataNode::Pointer mitk::Tool::CreateEmptySegmentationNode(Image *original, const std::string &organName, const mitk::Color &color) { // we NEED a reference image for size etc. if (!original) return nullptr; // actually create a new empty segmentation PixelType pixelType(mitk::MakeScalarPixelType()); LabelSetImage::Pointer segmentation = LabelSetImage::New(); if (original->GetDimension() == 2) { const unsigned int dimensions[] = {original->GetDimension(0), original->GetDimension(1), 1}; segmentation->Initialize(pixelType, 3, dimensions); segmentation->AddLayer(); } else { segmentation->Initialize(original); } mitk::Label::Pointer label = mitk::Label::New(); label->SetName(organName); label->SetColor(color); label->SetValue(1); segmentation->GetActiveLabelSet()->AddLabel(label); segmentation->GetActiveLabelSet()->SetActiveLabel(1); unsigned int byteSize = sizeof(mitk::Label::PixelType); if (segmentation->GetDimension() < 4) { for (unsigned int dim = 0; dim < segmentation->GetDimension(); ++dim) { byteSize *= segmentation->GetDimension(dim); } mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(0)); memset(writeAccess.GetData(), 0, byteSize); } else { // if we have a time-resolved image we need to set memory to 0 for each time step for (unsigned int dim = 0; dim < 3; ++dim) { byteSize *= segmentation->GetDimension(dim); } for (unsigned int volumeNumber = 0; volumeNumber < segmentation->GetDimension(3); volumeNumber++) { mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(volumeNumber)); memset(writeAccess.GetData(), 0, byteSize); } } if (original->GetTimeGeometry()) { TimeGeometry::Pointer originalGeometry = original->GetTimeGeometry()->Clone(); segmentation->SetTimeGeometry(originalGeometry); } else { Tool::ErrorMessage("Original image does not have a 'Time sliced geometry'! Cannot create a segmentation."); return nullptr; } - // Add some DICOM Tags as properties to segmentation image - PropertyList::Pointer dicomSegPropertyList = mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentationProperties(original->GetPropertyList()); - segmentation->GetPropertyList()->ConcatenatePropertyList(dicomSegPropertyList); - mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentProperties(segmentation->GetActiveLabel(segmentation->GetActiveLayer())); - return CreateSegmentationNode(segmentation, organName, color); } mitk::DataNode::Pointer mitk::Tool::CreateSegmentationNode(Image *image, const std::string &organName, const mitk::Color &color) { if (!image) return nullptr; // decorate the datatreenode with some properties DataNode::Pointer segmentationNode = DataNode::New(); segmentationNode->SetData(image); // name segmentationNode->SetProperty("name", StringProperty::New(organName)); // visualization properties segmentationNode->SetProperty("binary", BoolProperty::New(true)); segmentationNode->SetProperty("color", ColorProperty::New(color)); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::MULTILABEL); mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); lutProp->SetLookupTable(lut); segmentationNode->SetProperty("LookupTable", lutProp); segmentationNode->SetProperty("texture interpolation", BoolProperty::New(false)); segmentationNode->SetProperty("layer", IntProperty::New(10)); segmentationNode->SetProperty("levelwindow", LevelWindowProperty::New(LevelWindow(0.5, 1))); segmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); segmentationNode->SetProperty("segmentation", BoolProperty::New(true)); segmentationNode->SetProperty("reslice interpolation", VtkResliceInterpolationProperty::New()); // otherwise -> segmentation appears in 2 // slices sometimes (only visual effect, not // different data) // For MITK-3M3 release, the volume of all segmentations should be shown segmentationNode->SetProperty("showVolume", BoolProperty::New(true)); return segmentationNode; } us::ModuleResource mitk::Tool::GetIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } us::ModuleResource mitk::Tool::GetCursorIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp index 87f00eaef4..b4038a9da9 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp @@ -1,1137 +1,1129 @@ /*=================================================================== 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 "QmitkMultiLabelSegmentationView.h" // blueberry #include #include // mitk #include "mitkApplicationCursor.h" #include "mitkLabelSetImage.h" #include "mitkStatusBar.h" #include "mitkToolManagerProvider.h" //#include "mitkSegmentationObjectFactory.h" #include "mitkInteractionEventObserver.h" #include "mitkPlanePositionManager.h" #include "mitkPluginActivator.h" #include "mitkSegTool2D.h" -#include "mitkDICOMSegmentationPropertyHelper.cpp" // Qmitk #include "QmitkNewSegmentationDialog.h" #include "QmitkRenderWindow.h" #include "QmitkSegmentationOrganNamesHandling.cpp" // us #include #include #include #include #include // Qt #include #include #include #include #include "tinyxml.h" #include const std::string QmitkMultiLabelSegmentationView::VIEW_ID = "org.mitk.views.multilabelsegmentation"; QmitkMultiLabelSegmentationView::QmitkMultiLabelSegmentationView() : m_Parent(nullptr), m_IRenderWindowPart(nullptr), m_ToolManager(nullptr), m_ReferenceNode(nullptr), m_WorkingNode(nullptr), m_AutoSelectionEnabled(false), m_MouseCursorSet(false) { m_SegmentationPredicate = mitk::NodePredicateAnd::New(); m_SegmentationPredicate->AddPredicate(mitk::TNodePredicateDataType::New()); m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isMask = mitk::NodePredicateAnd::New(isBinary, isImage); mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); auto isSegment = mitk::NodePredicateDataType::New("Segment"); mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New(); validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment))); validImages->AddPredicate(isDwi); validImages->AddPredicate(isDti); validImages->AddPredicate(isOdf); m_ReferencePredicate = mitk::NodePredicateAnd::New(); m_ReferencePredicate->AddPredicate(validImages); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate)); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(isMask)); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); } QmitkMultiLabelSegmentationView::~QmitkMultiLabelSegmentationView() { // m_ToolManager->ActivateTool(-1); /* todo: check this m_Controls.m_SliceBasedInterpolatorWidget->EnableInterpolation(false); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); service->RemoveAllPlanePositions(); context->ungetService(ppmRef); */ // m_ToolManager->SetReferenceData(nullptr); // m_ToolManager->SetWorkingData(nullptr); // m_ServiceRegistration.Unregister(); // Loose LabelSetConnections OnLooseLabelSetConnection(); } void QmitkMultiLabelSegmentationView::CreateQtPartControl(QWidget *parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls.setupUi(parent); // *------------------------ // * DATA SELECTION WIDGETS // *------------------------ m_Controls.m_cbReferenceNodeSelector->SetAutoSelectNewItems(true); m_Controls.m_cbReferenceNodeSelector->SetPredicate(m_ReferencePredicate); m_Controls.m_cbReferenceNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.m_cbWorkingNodeSelector->SetAutoSelectNewItems(true); m_Controls.m_cbWorkingNodeSelector->SetPredicate(m_SegmentationPredicate); m_Controls.m_cbWorkingNodeSelector->SetDataStorage(this->GetDataStorage()); connect(m_Controls.m_cbReferenceNodeSelector, SIGNAL(OnSelectionChanged(const mitk::DataNode *)), this, SLOT(OnReferenceSelectionChanged(const mitk::DataNode *))); connect(m_Controls.m_cbWorkingNodeSelector, SIGNAL(OnSelectionChanged(const mitk::DataNode *)), this, SLOT(OnSegmentationSelectionChanged(const mitk::DataNode *))); // *------------------------ // * ToolManager // *------------------------ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); assert(m_ToolManager); m_ToolManager->SetDataStorage(*(this->GetDataStorage())); m_ToolManager->InitializeTools(); // use the same ToolManager instance for our 3D Tools m_Controls.m_ManualToolSelectionBox3D->SetToolManager(*m_ToolManager); // *------------------------ // * LabelSetWidget // *------------------------ m_Controls.m_LabelSetWidget->SetDataStorage(this->GetDataStorage()); m_Controls.m_LabelSetWidget->SetOrganColors(mitk::OrganNamesHandling::GetDefaultOrganColorString()); m_Controls.m_LabelSetWidget->hide(); // *------------------------ // * Interpolation // *------------------------ m_Controls.m_SurfaceBasedInterpolatorWidget->SetDataStorage(*(this->GetDataStorage())); m_Controls.m_SliceBasedInterpolatorWidget->SetDataStorage(*(this->GetDataStorage())); connect(m_Controls.m_cbInterpolation, SIGNAL(activated(int)), this, SLOT(OnInterpolationSelectionChanged(int))); m_Controls.m_cbInterpolation->setCurrentIndex(0); m_Controls.m_swInterpolation->hide(); // *------------------------ // * ToolSelection 2D // *------------------------ m_Controls.m_ManualToolSelectionBox2D->SetGenerateAccelerators(true); m_Controls.m_ManualToolSelectionBox2D->SetToolGUIArea(m_Controls.m_ManualToolGUIContainer2D); m_Controls.m_ManualToolSelectionBox2D->SetDisplayedToolGroups( "Add Subtract Fill Erase Paint Wipe 'Region Growing' FastMarching2D Correction 'Live Wire'"); // todo: "Correction // 'Live Wire'" m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); connect(m_Controls.m_ManualToolSelectionBox2D, SIGNAL(ToolSelected(int)), this, SLOT(OnManualTool2DSelected(int))); // *------------------------ // * ToolSelection 3D // *------------------------ m_Controls.m_ManualToolSelectionBox3D->SetGenerateAccelerators(true); m_Controls.m_ManualToolSelectionBox3D->SetToolGUIArea(m_Controls.m_ManualToolGUIContainer3D); m_Controls.m_ManualToolSelectionBox3D->SetDisplayedToolGroups( "Threshold 'Two Thresholds' 'Auto Threshold' 'Multiple Otsu'"); // todo add : FastMarching3D RegionGrowing Watershed m_Controls.m_ManualToolSelectionBox3D->SetLayoutColumns(2); m_Controls.m_ManualToolSelectionBox3D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); // *------------------------* // * Connect PushButtons (pb) // *------------------------* connect(m_Controls.m_pbNewLabel, SIGNAL(clicked()), this, SLOT(OnNewLabel())); connect(m_Controls.m_pbNewSegmentationSession, SIGNAL(clicked()), this, SLOT(OnNewSegmentationSession())); connect(m_Controls.m_pbShowLabelTable, SIGNAL(toggled(bool)), this, SLOT(OnShowLabelTable(bool))); // *------------------------* // * Connect LabelSetWidget // *------------------------* connect(m_Controls.m_LabelSetWidget, SIGNAL(goToLabel(const mitk::Point3D &)), this, SLOT(OnGoToLabel(const mitk::Point3D &))); connect(m_Controls.m_LabelSetWidget, SIGNAL(resetView()), this, SLOT(OnResetView())); // *------------------------* // * DATA SLECTION WIDGET // *------------------------* m_IRenderWindowPart = this->GetRenderWindowPart(); if (m_IRenderWindowPart) { QList controllers; controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls.m_SliceBasedInterpolatorWidget->SetSliceNavigationControllers(controllers); // m_Controls.m_LabelSetWidget->SetRenderWindowPart(this->m_IRenderWindowPart); } // this->InitializeListeners(); connect(m_Controls.m_btAddLayer, SIGNAL(clicked()), this, SLOT(OnAddLayer())); connect(m_Controls.m_btDeleteLayer, SIGNAL(clicked()), this, SLOT(OnDeleteLayer())); connect(m_Controls.m_btPreviousLayer, SIGNAL(clicked()), this, SLOT(OnPreviousLayer())); connect(m_Controls.m_btNextLayer, SIGNAL(clicked()), this, SLOT(OnNextLayer())); connect(m_Controls.m_btLockExterior, SIGNAL(toggled(bool)), this, SLOT(OnLockExteriorToggled(bool))); connect(m_Controls.m_cbActiveLayer, SIGNAL(currentIndexChanged(int)), this, SLOT(OnChangeLayer(int))); m_Controls.m_btAddLayer->setEnabled(false); m_Controls.m_btDeleteLayer->setEnabled(false); m_Controls.m_btNextLayer->setEnabled(false); m_Controls.m_btPreviousLayer->setEnabled(false); m_Controls.m_cbActiveLayer->setEnabled(false); m_Controls.m_pbNewLabel->setEnabled(false); m_Controls.m_btLockExterior->setEnabled(false); m_Controls.m_pbShowLabelTable->setEnabled(false); // Make sure the GUI notices if appropriate data is already present on creation this->OnReferenceSelectionChanged(m_Controls.m_cbReferenceNodeSelector->GetSelectedNode()); this->OnSegmentationSelectionChanged(m_Controls.m_cbWorkingNodeSelector->GetSelectedNode()); } void QmitkMultiLabelSegmentationView::Activated() { m_ToolManager->SetReferenceData(m_Controls.m_cbReferenceNodeSelector->GetSelectedNode()); m_ToolManager->SetWorkingData(m_Controls.m_cbWorkingNodeSelector->GetSelectedNode()); } void QmitkMultiLabelSegmentationView::Deactivated() { // Not yet implemented } void QmitkMultiLabelSegmentationView::Visible() { // Not yet implemented } void QmitkMultiLabelSegmentationView::Hidden() { // Not yet implemented } int QmitkMultiLabelSegmentationView::GetSizeFlags(bool width) { if (!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkMultiLabelSegmentationView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if (width == false) { return 100; } else { return preferredResult; } } /************************************************************************/ /* protected slots */ /************************************************************************/ void QmitkMultiLabelSegmentationView::OnManualTool2DSelected(int id) { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); if (id >= 0) { std::string text = "Active Tool: \""; text += m_ToolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource(); if (resource.IsValid()) this->SetMouseCursor(resource, 0, 0); } } void QmitkMultiLabelSegmentationView::OnNewLabel() { m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (!workingNode) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog(m_Parent); dialog->SetSuggestionList(mitk::OrganNamesHandling::GetDefaultOrganColorString()); dialog->setWindowTitle("New Label"); int dialogReturnValue = dialog->exec(); if (dialogReturnValue == QDialog::Rejected) { return; } QString segName = dialog->GetSegmentationName(); if (segName.isEmpty()) { segName = "Unnamed"; } workingImage->GetActiveLabelSet()->AddLabel(segName.toStdString(), dialog->GetColor()); - // Set specific DICOM SEG properties for the label - mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentProperties( - workingImage->GetActiveLabel(workingImage->GetActiveLayer())); UpdateControls(); m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); mitk::RenderingManager::GetInstance()->InitializeViews(workingNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } void QmitkMultiLabelSegmentationView::OnShowLabelTable(bool value) { if (value) m_Controls.m_LabelSetWidget->show(); else m_Controls.m_LabelSetWidget->hide(); } void QmitkMultiLabelSegmentationView::OnNewSegmentationSession() { mitk::DataNode *referenceNode = m_Controls.m_cbReferenceNodeSelector->GetSelectedNode(); if (!referenceNode) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } m_ToolManager->ActivateTool(-1); mitk::Image* referenceImage = dynamic_cast(referenceNode->GetData()); assert(referenceImage); QString newName = QString::fromStdString(referenceNode->GetName()); newName.append("-labels"); bool ok = false; newName = QInputDialog::getText(m_Parent, "New Segmentation Session", "New name:", QLineEdit::Normal, newName, &ok); if (!ok) { return; } this->WaitCursorOn(); mitk::LabelSetImage::Pointer workingImage = mitk::LabelSetImage::New(); try { workingImage->Initialize(referenceImage); } catch (mitk::Exception& e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(m_Parent, "New Segmentation Session", "Could not create a new segmentation session.\n"); return; } this->WaitCursorOff(); mitk::DataNode::Pointer workingNode = mitk::DataNode::New(); workingNode->SetData(workingImage); workingNode->SetName(newName.toStdString()); workingImage->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(referenceNode->GetName().c_str())); workingImage->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New(newName.toStdString().c_str())); - // Set DICOM SEG properties for segmentation session - mitk::PropertyList::Pointer dicomSegPropertyList = - mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentationProperties(referenceImage->GetPropertyList()); - workingImage->GetPropertyList()->ConcatenatePropertyList(dicomSegPropertyList); if (!GetDataStorage()->Exists(workingNode)) { GetDataStorage()->Add(workingNode, referenceNode); } OnNewLabel(); } void QmitkMultiLabelSegmentationView::OnGoToLabel(const mitk::Point3D& pos) { if (m_IRenderWindowPart) m_IRenderWindowPart->SetSelectedPosition(pos); } void QmitkMultiLabelSegmentationView::OnResetView() { if (m_IRenderWindowPart) m_IRenderWindowPart->ForceImmediateUpdate(); } void QmitkMultiLabelSegmentationView::OnAddLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); QString question = "Do you really want to add a layer to the current segmentation session?"; QMessageBox::StandardButton answerButton = QMessageBox::question( m_Controls.m_LabelSetWidget, "Add layer", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton != QMessageBox::Yes) return; try { WaitCursorOn(); workingImage->AddLayer(); WaitCursorOff(); } catch ( mitk::Exception& e ) { WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information( m_Controls.m_LabelSetWidget, "Add Layer", "Could not add a new layer. See error log for details.\n"); return; } OnNewLabel(); } void QmitkMultiLabelSegmentationView::OnDeleteLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); if (workingImage->GetNumberOfLayers() < 2) return; QString question = "Do you really want to delete the current layer?"; QMessageBox::StandardButton answerButton = QMessageBox::question( m_Controls.m_LabelSetWidget, "Delete layer", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton != QMessageBox::Yes) { return; } try { this->WaitCursorOn(); workingImage->RemoveLayer(); this->WaitCursorOff(); } catch (mitk::Exception& e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(m_Controls.m_LabelSetWidget, "Delete Layer", "Could not delete the currently active layer. See error log for details.\n"); return; } UpdateControls(); m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); } void QmitkMultiLabelSegmentationView::OnPreviousLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); OnChangeLayer(workingImage->GetActiveLayer() - 1); } void QmitkMultiLabelSegmentationView::OnNextLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); OnChangeLayer(workingImage->GetActiveLayer() + 1); } void QmitkMultiLabelSegmentationView::OnChangeLayer(int layer) { m_ToolManager->ActivateTool(-1); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); this->WaitCursorOn(); workingImage->SetActiveLayer(layer); this->WaitCursorOff(); UpdateControls(); m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); } void QmitkMultiLabelSegmentationView::OnDeactivateActiveTool() { m_ToolManager->ActivateTool(-1); } void QmitkMultiLabelSegmentationView::OnLockExteriorToggled(bool checked) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); workingImage->GetLabel(0)->SetLocked(checked); } void QmitkMultiLabelSegmentationView::OnReferenceSelectionChanged(const mitk::DataNode* node) { m_ToolManager->ActivateTool(-1); m_ReferenceNode = const_cast(node); m_ToolManager->SetReferenceData(m_ReferenceNode); if (m_ReferenceNode.IsNotNull()) { if (m_AutoSelectionEnabled) { // if an image is selected find a possible working / segmentation image mitk::DataStorage::SetOfObjects::ConstPointer derivations = this->GetDataStorage()->GetDerivations(m_ReferenceNode, m_SegmentationPredicate); if (derivations->Size() != 0) { // use the first segmentation child node m_WorkingNode = derivations->ElementAt(0); m_ToolManager->SetWorkingData(m_WorkingNode); m_Controls.m_cbWorkingNodeSelector->blockSignals(true); m_Controls.m_cbWorkingNodeSelector->SetSelectedNode(m_WorkingNode); m_Controls.m_cbWorkingNodeSelector->blockSignals(false); } else if (derivations->size() == 0) { m_Controls.m_cbWorkingNodeSelector->setCurrentIndex(-1); } // hide all image and segmentation nodes to later show only the automatically selected ones mitk::DataStorage::SetOfObjects::ConstPointer patientNodes = GetDataStorage()->GetSubset(m_ReferencePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = patientNodes->begin(); iter != patientNodes->end(); ++iter) { (*iter)->SetVisibility(false); } mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = GetDataStorage()->GetSubset(m_SegmentationPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = segmentationNodes->begin(); iter != segmentationNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_ReferenceNode->SetVisibility(true); // check match of segmentation and reference image geometries if (m_WorkingNode.IsNotNull()) { mitk::Image* workingImage = dynamic_cast(m_WorkingNode->GetData()); assert(workingImage); mitk::Image* referenceImage = dynamic_cast(node->GetData()); assert(referenceImage); if (!this->CheckForSameGeometry(referenceImage, workingImage)) { return; } m_WorkingNode->SetVisibility(true); } } UpdateControls(); if (m_WorkingNode.IsNotNull()) { m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); mitk::RenderingManager::GetInstance()->InitializeViews(m_WorkingNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } void QmitkMultiLabelSegmentationView::OnSegmentationSelectionChanged(const mitk::DataNode* node) { m_ToolManager->ActivateTool(-1); if (m_WorkingNode.IsNotNull()) OnLooseLabelSetConnection(); m_WorkingNode = const_cast(node); m_ToolManager->SetWorkingData(m_WorkingNode); if (m_WorkingNode.IsNotNull()) { OnEstablishLabelSetConnection(); if (m_AutoSelectionEnabled) { // if a segmentation is selected find a possible reference image mitk::DataStorage::SetOfObjects::ConstPointer sources = this->GetDataStorage()->GetSources(m_WorkingNode, m_ReferencePredicate); if (sources->Size() != 0) { m_ReferenceNode = sources->ElementAt(0); m_ToolManager->SetReferenceData(m_ReferenceNode); m_Controls.m_cbReferenceNodeSelector->blockSignals(true); m_Controls.m_cbReferenceNodeSelector->SetSelectedNode(m_ReferenceNode); m_Controls.m_cbReferenceNodeSelector->blockSignals(false); } else if(sources->size() == 0) { m_Controls.m_cbReferenceNodeSelector->setCurrentIndex(-1); } // hide all image and segmentation nodes to later show only the automatically selected ones mitk::DataStorage::SetOfObjects::ConstPointer patientNodes = GetDataStorage()->GetSubset(m_ReferencePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = patientNodes->begin(); iter != patientNodes->end(); ++iter) { (*iter)->SetVisibility(false); } mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = GetDataStorage()->GetSubset(m_SegmentationPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = segmentationNodes->begin(); iter != segmentationNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_WorkingNode->SetVisibility(true); // check match of segmentation and reference image geometries if (m_ReferenceNode.IsNotNull()) { mitk::Image* referenceImage = dynamic_cast(m_ReferenceNode->GetData()); assert(referenceImage); mitk::Image* workingImage = dynamic_cast(m_WorkingNode->GetData()); assert(workingImage); if (!this->CheckForSameGeometry(referenceImage, workingImage)) { return; } m_ReferenceNode->SetVisibility(true); } } UpdateControls(); if (m_WorkingNode.IsNotNull()) { m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); mitk::RenderingManager::GetInstance()->InitializeViews(m_WorkingNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } void QmitkMultiLabelSegmentationView::OnInterpolationSelectionChanged(int index) { if (index == 1) { m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false);//OnToggleWidgetActivation(false); m_Controls.m_swInterpolation->setCurrentIndex(0); m_Controls.m_swInterpolation->show(); } else if (index == 2) { m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); m_Controls.m_swInterpolation->setCurrentIndex(1); m_Controls.m_swInterpolation->show(); } else { m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); m_Controls.m_swInterpolation->setCurrentIndex(2); m_Controls.m_swInterpolation->hide(); } } /************************************************************************/ /* protected */ /************************************************************************/ void QmitkMultiLabelSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList &nodes) { if (m_AutoSelectionEnabled) { // automatically set the reference node and the working node of the multi label plugin if (1 == nodes.size()) { mitk::DataNode::Pointer selectedNode = nodes.at(0); if (selectedNode.IsNull()) { return; } // check selected node mitk::LabelSetImage::Pointer labelSetImage = dynamic_cast(selectedNode->GetData()); if (labelSetImage.IsNotNull()) { // reset the image / reference node selector in case the current selected segmentation has no image parent m_Controls.m_cbReferenceNodeSelector->setCurrentIndex(-1); // selected a label set image (a segmentation ( working node) m_Controls.m_cbWorkingNodeSelector->SetSelectedNode(selectedNode); return; } mitk::Image::Pointer selectedImage = dynamic_cast(selectedNode->GetData()); if (selectedImage.IsNotNull()) { // reset the segmentation / working node selector in case the current selected image has no segmentation child m_Controls.m_cbWorkingNodeSelector->setCurrentIndex(-1); // selected an image (a reference node) m_Controls.m_cbReferenceNodeSelector->SetSelectedNode(selectedNode); return; } } } } void QmitkMultiLabelSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if (m_Parent && m_WorkingNode.IsNotNull()) { m_AutoSelectionEnabled = prefs->GetBool("auto selection", false); mitk::BoolProperty::Pointer drawOutline = mitk::BoolProperty::New(prefs->GetBool("draw outline", true)); mitk::BoolProperty::Pointer volumeRendering = mitk::BoolProperty::New(prefs->GetBool("volume rendering", false)); mitk::LabelSetImage* labelSetImage; mitk::DataNode* segmentation; // iterate all segmentations (binary (single label) and LabelSetImages) mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateOr::Pointer allSegmentationsPredicate = mitk::NodePredicateOr::New(isBinaryPredicate, m_SegmentationPredicate); mitk::DataStorage::SetOfObjects::ConstPointer allSegmentations = GetDataStorage()->GetSubset(allSegmentationsPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator it = allSegmentations->begin(); it != allSegmentations->end(); ++it) { segmentation = *it; labelSetImage = dynamic_cast(segmentation->GetData()); if (nullptr != labelSetImage) { // segmentation node is a multi label segmentation segmentation->SetProperty("labelset.contour.active", drawOutline); segmentation->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); segmentation->SetProperty("volumerendering", volumeRendering); // force render window update to show outline segmentation->GetData()->Modified(); } else if (nullptr != segmentation->GetData()) { // node is actually a 'single label' segmentation, // but its outline property can be set in the 'multi label' segmentation preference page as well bool isBinary = false; segmentation->GetBoolProperty("binary", isBinary); if (isBinary) { segmentation->SetProperty("outline binary", drawOutline); segmentation->SetProperty("outline width", mitk::FloatProperty::New(2.0)); segmentation->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); segmentation->SetProperty("volumerendering", volumeRendering); // force render window update to show outline segmentation->GetData()->Modified(); } } else { // "interpolation feedback" data nodes have binary flag but don't have a data set. So skip them for now. MITK_INFO << "DataNode " << segmentation->GetName() << " doesn't contain a base data."; } } } } void QmitkMultiLabelSegmentationView::NodeAdded(const mitk::DataNode *) { /* bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) return; if (m_ReferenceNode.IsNotNull() && dynamic_cast(node->GetData())) { mitk::LabelSetImage* workingImage = dynamic_cast(node->GetData()); if (workingImage->GetNumberOfLabels() > 2) m_Controls.m_LabelSetWidget->show(); else m_Controls.m_LabelSetWidget->hide(); } */ } void QmitkMultiLabelSegmentationView::NodeRemoved(const mitk::DataNode *node) { bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) { return; } if (m_ReferenceNode.IsNotNull() && dynamic_cast(node->GetData())) { // remove all possible contour markers of the segmentation mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations( node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); ctkPluginContext *context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService *service = context->getService(ppmRef); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } context->ungetService(ppmRef); service = nullptr; } } void QmitkMultiLabelSegmentationView::OnEstablishLabelSetConnection() { MITK_INFO << "Connection Established"; if (m_WorkingNode.IsNull()) { return; } mitk::LabelSetImage *workingImage = dynamic_cast(m_WorkingNode->GetData()); assert(workingImage); workingImage->GetActiveLabelSet()->AddLabelEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1(m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); workingImage->BeforeChangeLayerEvent += mitk::MessageDelegate( this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); } void QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection() { MITK_INFO << "Connection Lost"; if (m_WorkingNode.IsNull()) { return; } mitk::LabelSetImage *workingImage = dynamic_cast(m_WorkingNode->GetData()); assert(workingImage); // Reset LabelSetWidget Events workingImage->GetActiveLabelSet()->AddLabelEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1(m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); workingImage->BeforeChangeLayerEvent -= mitk::MessageDelegate( this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); } void QmitkMultiLabelSegmentationView::SetFocus() { } void QmitkMultiLabelSegmentationView::UpdateControls() { mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); bool hasReferenceNode = referenceNode != nullptr; mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); bool hasValidWorkingNode = workingNode != nullptr; m_Controls.m_pbNewLabel->setEnabled(false); m_Controls.m_gbInterpolation->setEnabled(false); m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(false); m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(false); m_Controls.m_LabelSetWidget->setEnabled(false); m_Controls.m_btAddLayer->setEnabled(false); m_Controls.m_btDeleteLayer->setEnabled(false); m_Controls.m_cbActiveLayer->setEnabled(false); m_Controls.m_btPreviousLayer->setEnabled(false); m_Controls.m_btNextLayer->setEnabled(false); m_Controls.m_btLockExterior->setChecked(false); m_Controls.m_btLockExterior->setEnabled(false); m_Controls.m_pbShowLabelTable->setChecked(false); m_Controls.m_pbShowLabelTable->setEnabled(false); m_Controls.m_ManualToolSelectionBox3D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); if (hasValidWorkingNode) { // TODO adapt tool manager so that this check is done there, e.g. convenience function mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); hasValidWorkingNode = workingImage != nullptr; if (hasValidWorkingNode) { m_Controls.m_pbNewLabel->setEnabled(true); m_Controls.m_btLockExterior->setEnabled(true); m_Controls.m_pbShowLabelTable->setEnabled(true); m_Controls.m_gbInterpolation->setEnabled(true); m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(true); m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(true); m_Controls.m_LabelSetWidget->setEnabled(true); m_Controls.m_btAddLayer->setEnabled(true); int activeLayer = workingImage->GetActiveLayer(); int numberOfLayers = workingImage->GetNumberOfLayers(); m_Controls.m_cbActiveLayer->blockSignals(true); m_Controls.m_cbActiveLayer->clear(); for (unsigned int lidx = 0; lidx < workingImage->GetNumberOfLayers(); ++lidx) { m_Controls.m_cbActiveLayer->addItem(QString::number(lidx)); } m_Controls.m_cbActiveLayer->setCurrentIndex(activeLayer); m_Controls.m_cbActiveLayer->blockSignals(false); m_Controls.m_cbActiveLayer->setEnabled(numberOfLayers > 1); m_Controls.m_btDeleteLayer->setEnabled(numberOfLayers > 1); m_Controls.m_btPreviousLayer->setEnabled(activeLayer > 0); m_Controls.m_btNextLayer->setEnabled(activeLayer != numberOfLayers - 1); m_Controls.m_btLockExterior->setChecked(workingImage->GetLabel(0, activeLayer)->GetLocked()); m_Controls.m_pbShowLabelTable->setChecked(workingImage->GetNumberOfLabels() > 1 /*1st is exterior*/); //MLI TODO //m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithWorkingDataVisible); } } if (hasValidWorkingNode && hasReferenceNode) { int layer = -1; referenceNode->GetIntProperty("layer", layer); workingNode->SetIntProperty("layer", layer + 1); } this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_ALL); } void QmitkMultiLabelSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_IRenderWindowPart != renderWindowPart) { m_IRenderWindowPart = renderWindowPart; m_Parent->setEnabled(true); QList controllers; controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls.m_SliceBasedInterpolatorWidget->SetSliceNavigationControllers(controllers); } } void QmitkMultiLabelSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_ToolManager->ActivateTool(-1); m_IRenderWindowPart = 0; m_Parent->setEnabled(false); } void QmitkMultiLabelSegmentationView::ResetMouseCursor() { if (m_MouseCursorSet) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkMultiLabelSegmentationView::SetMouseCursor(const us::ModuleResource resource, int hotspotX, int hotspotY) { // Remove previously set mouse cursor if (m_MouseCursorSet) { mitk::ApplicationCursor::GetInstance()->PopCursor(); } us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY); m_MouseCursorSet = true; } void QmitkMultiLabelSegmentationView::InitializeListeners() { if (m_Interactor.IsNull()) { us::Module* module = us::GetModuleContext()->GetModule(); std::vector resources = module->FindResources("/", "*", true); for (std::vector::iterator iter = resources.begin(); iter != resources.end(); ++iter) { MITK_INFO << iter->GetResourcePath(); } m_Interactor = mitk::SegmentationInteractor::New(); if (!m_Interactor->LoadStateMachine("SegmentationInteraction.xml", module)) { MITK_WARN << "Error loading state machine"; } if (!m_Interactor->SetEventConfig("ConfigSegmentation.xml", module)) { MITK_WARN << "Error loading state machine configuration"; } // Register as listener via micro services us::ServiceProperties props; props["name"] = std::string("SegmentationInteraction"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(m_Interactor.GetPointer(), props); } } bool QmitkMultiLabelSegmentationView::CheckForSameGeometry(const mitk::Image *image1, const mitk::Image *image2) const { bool isSameGeometry(true); if (image1 && image2) { mitk::BaseGeometry::Pointer geo1 = image1->GetGeometry(); mitk::BaseGeometry::Pointer geo2 = image2->GetGeometry(); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetOrigin(), geo2->GetOrigin()); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(0), geo2->GetExtent(0)); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(1), geo2->GetExtent(1)); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(2), geo2->GetExtent(2)); isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetSpacing(), geo2->GetSpacing()); isSameGeometry = isSameGeometry && mitk::MatrixEqualElementWise(geo1->GetIndexToWorldTransform()->GetMatrix(), geo2->GetIndexToWorldTransform()->GetMatrix()); return isSameGeometry; } else { return false; } } QString QmitkMultiLabelSegmentationView::GetLastFileOpenPath() { return this->GetPreferences()->Get("LastFileOpenPath", ""); } void QmitkMultiLabelSegmentationView::SetLastFileOpenPath(const QString &path) { this->GetPreferences()->Put("LastFileOpenPath", path); this->GetPreferences()->Flush(); }