diff --git a/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.cpp b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.cpp index db6ddc6dcb..7181193335 100644 --- a/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.cpp +++ b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.cpp @@ -1,287 +1,294 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCESTGenericDICOMReaderService.h" #include "mitkIOMimeTypes.h" #include #include #include #include #include #include "mitkCESTImageNormalizationFilter.h" #include "itksys/SystemTools.hxx" #include #include #include #include #include namespace mitk { CESTDICOMManualReaderService::CESTDICOMManualReaderService(const CustomMimeType& mimeType, const std::string& description) : BaseDICOMReaderService(mimeType, description) { IFileIO::Options options; options["B1 amplitude"] = 0.0; options["CEST frequency [Hz]"] = 0.0; options["Pulse duration [us]"] = 0.0; options["Duty cycle [%]"] = 0.0; std::vector normalizationStrategy; normalizationStrategy.push_back("Automatic"); normalizationStrategy.push_back("No"); options["Normalize data"] = normalizationStrategy; this->SetDefaultOptions(options); this->RegisterService(); } CESTDICOMManualReaderService::CESTDICOMManualReaderService(const mitk::CESTDICOMManualReaderService& other) : BaseDICOMReaderService(other) { } void ExtractOptionFromPropertyTree(const std::string& key, boost::property_tree::ptree& root, std::map& options) { auto finding = root.find(key); if (finding != root.not_found()) { options[key] = finding->second.get_value(); } } IFileIO::Options ExtractOptionsFromFile(const std::string& file) { boost::property_tree::ptree root; - try + if (itksys::SystemTools::FileExists(file)) { - boost::property_tree::read_json(file, root, std::locale("C")); + try + { + boost::property_tree::read_json(file, root, std::locale("C")); + } + catch (const boost::property_tree::json_parser_error & e) + { + MITK_WARN << "Could not parse CEST meta file. Fall back to default values. Error was:\n" << e.what(); + } } - catch (const boost::property_tree::json_parser_error & e) + else { - MITK_WARN << "Could not parse CEST meta file. Fall back to default values. Error was:\n" << e.what(); + MITK_DEBUG << "CEST meta file does not exist. Fall back to default values. CEST meta file path: " << file; } IFileIO::Options options; ExtractOptionFromPropertyTree(CEST_PROPERTY_NAME_FREQ(),root, options); ExtractOptionFromPropertyTree(CEST_PROPERTY_NAME_B1Amplitude(), root, options); ExtractOptionFromPropertyTree(CEST_PROPERTY_NAME_PULSEDURATION(), root, options); ExtractOptionFromPropertyTree(CEST_PROPERTY_NAME_DutyCycle(), root, options); ExtractOptionFromPropertyTree(CEST_PROPERTY_NAME_OFFSETS(), root, options); ExtractOptionFromPropertyTree(CEST_PROPERTY_NAME_TREC(), root, options); return options; } void TransferOption(const mitk::IFileIO::Options& sourceOptions, const std::string& sourceName, mitk::IFileIO::Options& options, const std::string& newName) { auto sourceFinding = sourceOptions.find(sourceName); auto finding = options.find(newName); bool replaceValue = finding == options.end(); if (!replaceValue) { replaceValue = us::any_cast(finding->second) == 0.; } if (sourceFinding != sourceOptions.end() && us::any_cast(sourceFinding->second) != 0. && replaceValue) { options[newName] = sourceFinding->second; } } std::string CESTDICOMManualReaderService::GetCESTMetaFilePath() const { auto dir = itksys::SystemTools::GetFilenamePath(this->GetInputLocation()); std::string metafile = dir + "/" + "CEST_META.json"; return metafile; } std::string CESTDICOMManualReaderService::GetTRECFilePath() const { auto dir = itksys::SystemTools::GetFilenamePath(this->GetInputLocation()); std::string metafile = dir + "/" + "TREC.txt"; return metafile; } std::string CESTDICOMManualReaderService::GetLISTFilePath() const { auto dir = itksys::SystemTools::GetFilenamePath(this->GetInputLocation()); std::string metafile = dir + "/" + "LIST.txt"; return metafile; } IFileIO::Options CESTDICOMManualReaderService::GetOptions() const { auto options = AbstractFileReader::GetOptions(); if (!this->GetInputLocation().empty()) { auto fileOptions = ExtractOptionsFromFile(this->GetCESTMetaFilePath()); TransferOption(fileOptions, CEST_PROPERTY_NAME_FREQ(), options, "CEST frequency [Hz]"); TransferOption(fileOptions, CEST_PROPERTY_NAME_B1Amplitude(), options, "B1 amplitude"); TransferOption(fileOptions, CEST_PROPERTY_NAME_PULSEDURATION(), options, "Pulse duration [us]"); TransferOption(fileOptions, CEST_PROPERTY_NAME_DutyCycle(), options, "Duty cycle [%]"); } return options; } us::Any CESTDICOMManualReaderService::GetOption(const std::string& name) const { this->GetOptions(); //ensure (default) options are set. return AbstractFileReader::GetOption(name); } DICOMFileReader::Pointer CESTDICOMManualReaderService::GetReader(const mitk::StringList& relevantFiles) const { mitk::DICOMFileReaderSelector::Pointer selector = mitk::DICOMFileReaderSelector::New(); selector->LoadBuiltIn3DnTConfigs(); selector->SetInputFiles(relevantFiles); mitk::DICOMFileReader::Pointer reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages(); if (reader.IsNotNull()) { //reset tag cache to ensure that additional tags of interest //will be regarded by the reader if set later on. reader->SetTagCache(nullptr); } return reader; }; std::vector> CESTDICOMManualReaderService::Read() { std::vector result; std::vector dicomResult = BaseDICOMReaderService::Read(); const Options userOptions = this->GetOptions(); const std::string normalizationStrategy = userOptions.find("Normalize data")->second.ToString(); for (auto &item : dicomResult) { auto fileOptions = ExtractOptionsFromFile(this->GetCESTMetaFilePath()); IFileIO::Options options; TransferOption(userOptions, "CEST frequency [Hz]", options, CEST_PROPERTY_NAME_FREQ()); TransferOption(userOptions, "B1 amplitude", options, CEST_PROPERTY_NAME_B1Amplitude()); TransferOption(userOptions, "Pulse duration [us]", options, CEST_PROPERTY_NAME_PULSEDURATION()); TransferOption(userOptions, "Duty cycle [%]", options, CEST_PROPERTY_NAME_DutyCycle()); TransferOption(fileOptions, CEST_PROPERTY_NAME_FREQ(), options, CEST_PROPERTY_NAME_FREQ()); TransferOption(fileOptions, CEST_PROPERTY_NAME_B1Amplitude(), options, CEST_PROPERTY_NAME_B1Amplitude()); TransferOption(fileOptions, CEST_PROPERTY_NAME_PULSEDURATION(), options, CEST_PROPERTY_NAME_PULSEDURATION()); TransferOption(fileOptions, CEST_PROPERTY_NAME_DutyCycle(), options, CEST_PROPERTY_NAME_DutyCycle()); TransferOption(fileOptions, CEST_PROPERTY_NAME_OFFSETS(), options, CEST_PROPERTY_NAME_OFFSETS()); TransferOption(fileOptions, CEST_PROPERTY_NAME_TREC(), options, CEST_PROPERTY_NAME_TREC()); auto trecValues = CustomTagParser::ReadListFromFile(this->GetTRECFilePath()); auto offsetValues = CustomTagParser::ReadListFromFile(this->GetLISTFilePath()); bool isCEST = !offsetValues.empty(); bool isT1 = !trecValues.empty(); if (!isCEST && !isT1) {//check if there are settings in the metafile auto finding = fileOptions.find(CEST_PROPERTY_NAME_OFFSETS()); if (finding != fileOptions.end()) { isCEST = true; offsetValues = finding->second.ToString(); }; finding = fileOptions.find(CEST_PROPERTY_NAME_TREC()); if (finding != fileOptions.end()) { isT1 = true; trecValues = finding->second.ToString(); }; } if (isCEST) { MITK_INFO << "CEST image detected due to LIST.txt or offset property in CEST_META.json"; options[CEST_PROPERTY_NAME_OFFSETS()] = offsetValues; } else if (isT1) { MITK_INFO << "T1 image detected due to TREC.txt or trec property in CEST_META.json"; options[CEST_PROPERTY_NAME_TREC()] = trecValues; } else { mitkThrow() << "Cannot load CEST/T1 file. No CEST offsets or T1 trec values specified. LIST.txt/TREC.txt or information in CEST_META.json is missing."; } for (const auto& option : options) { item->GetPropertyList()->SetStringProperty(option.first.c_str(), option.second.ToString().c_str()); } auto image = dynamic_cast(item.GetPointer()); if (isCEST) { try { auto offsets = ExtractCESTOffset(image); } catch (...) { mitkThrow() << "Cannot load CEST file. Number of CEST offsets do not equal the number of image time steps. Image time steps: " << image->GetTimeSteps() << "; offset values: " << offsetValues; } } else if (isT1) { try { auto t1s = ExtractCESTT1Time(image); } catch (...) { mitkThrow() << "Cannot load T1 file. Number of T1 times do not equal the number of image time steps. Image time steps: " << image->GetTimeSteps() << "; T1 values: " << trecValues; } } if (normalizationStrategy == "Automatic" && mitk::IsNotNormalizedCESTImage(image)) { MITK_INFO << "Unnormalized CEST image was loaded and will be normalized automatically."; auto normalizationFilter = mitk::CESTImageNormalizationFilter::New(); normalizationFilter->SetInput(image); normalizationFilter->Update(); auto normalizedImage = normalizationFilter->GetOutput(); auto nameProp = item->GetProperty("name"); if (!nameProp) { mitkThrow() << "Cannot load CEST file. Property \"name\" is missing after BaseDICOMReaderService::Read()."; } normalizedImage->SetProperty("name", mitk::StringProperty::New(nameProp->GetValueAsString() + "_normalized")); result.push_back(normalizedImage); } else { result.push_back(item); } } return result; } CESTDICOMManualReaderService *CESTDICOMManualReaderService::Clone() const { return new CESTDICOMManualReaderService(*this); } } diff --git a/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp b/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp index e752e8a8ff..ed2c8cb5bf 100644 --- a/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp +++ b/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp @@ -1,223 +1,229 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCESTIOMimeTypes.h" #include "mitkIOMimeTypes.h" #include #include #include #include #include #include namespace mitk { std::vector MitkCESTIOMimeTypes::Get() { std::vector mimeTypes; // order matters here (descending rank for mime types) mimeTypes.push_back(CEST_DICOM_WITH_META_FILE_MIMETYPE().Clone()); mimeTypes.push_back(CEST_DICOM_MIMETYPE().Clone()); mimeTypes.push_back(CEST_DICOM_WITHOUT_META_FILE_MIMETYPE().Clone()); return mimeTypes; } // Mime Types MitkCESTIOMimeTypes::MitkCESTDicomMimeType::MitkCESTDicomMimeType() : IOMimeTypes::BaseDicomMimeType(CEST_DICOM_MIMETYPE_NAME()) { this->SetCategory(IOMimeTypes::CATEGORY_IMAGES()); this->SetComment("CEST DICOM"); } bool MitkCESTIOMimeTypes::MitkCESTDicomMimeType::AppliesTo(const std::string &path) const { bool canRead(IOMimeTypes::BaseDicomMimeType::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 if (!canRead) { return canRead; } mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); mitk::DICOMTag siemensCESTprivateTag(0x0029, 0x1020); mitk::StringList relevantFiles; relevantFiles.push_back(path); scanner->AddTag(siemensCESTprivateTag); scanner->SetInputFiles(relevantFiles); scanner->Scan(); mitk::DICOMTagCache::Pointer tagCache = scanner->GetScanCache(); mitk::DICOMImageFrameList imageFrameList = mitk::ConvertToDICOMImageFrameList(tagCache->GetFrameInfoList()); - mitk::DICOMImageFrameInfo *firstFrame = imageFrameList.begin()->GetPointer(); - std::string byteString = tagCache->GetTagValue(firstFrame, siemensCESTprivateTag).value; + bool mapNotEmpty = false; - if (byteString.empty()) { - return false; - } - mitk::CustomTagParser tagParser(relevantFiles[0]); + if (!imageFrameList.empty()) + { + mitk::DICOMImageFrameInfo* firstFrame = imageFrameList.begin()->GetPointer(); + + std::string byteString = tagCache->GetTagValue(firstFrame, siemensCESTprivateTag).value; - auto parsedPropertyList = tagParser.ParseDicomPropertyString(byteString); + if (byteString.empty()) { + return false; + } + mitk::CustomTagParser tagParser(relevantFiles[0]); - bool mapNotEmpty = parsedPropertyList->GetMap()->size() > 0; + auto parsedPropertyList = tagParser.ParseDicomPropertyString(byteString); + + mapNotEmpty = parsedPropertyList->GetMap()->size() > 0; + } return mapNotEmpty; } MitkCESTIOMimeTypes::MitkCESTDicomMimeType *MitkCESTIOMimeTypes::MitkCESTDicomMimeType::Clone() const { return new MitkCESTDicomMimeType(*this); } MitkCESTIOMimeTypes::MitkCESTDicomMimeType MitkCESTIOMimeTypes::CEST_DICOM_MIMETYPE() { return MitkCESTDicomMimeType(); } std::string MitkCESTIOMimeTypes::CEST_DICOM_MIMETYPE_NAME() { // create a unique and sensible name for this mime type static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".image.dicom.cest"; return name; } MitkCESTIOMimeTypes::MitkCESTDicomWithMetaFileMimeType::MitkCESTDicomWithMetaFileMimeType() : IOMimeTypes::BaseDicomMimeType(CEST_DICOM_WITH_META_FILE_NAME()) { this->SetCategory(IOMimeTypes::CATEGORY_IMAGES()); this->SetComment("CEST DICOM"); } bool MitkCESTIOMimeTypes::MitkCESTDicomWithMetaFileMimeType::AppliesTo(const std::string& path) const { bool canRead(IOMimeTypes::BaseDicomMimeType::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 if (!canRead) { return canRead; } std::string dir = path; if (!itksys::SystemTools::FileIsDirectory(path)) { dir = itksys::SystemTools::GetProgramPath(path); } std::string metafilePath = dir +"/" + "CEST_META.json"; canRead = itksys::SystemTools::FileExists(metafilePath.c_str()); return canRead; } MitkCESTIOMimeTypes::MitkCESTDicomWithMetaFileMimeType* MitkCESTIOMimeTypes::MitkCESTDicomWithMetaFileMimeType::Clone() const { return new MitkCESTDicomWithMetaFileMimeType(*this); } MitkCESTIOMimeTypes::MitkCESTDicomWithMetaFileMimeType MitkCESTIOMimeTypes::CEST_DICOM_WITH_META_FILE_MIMETYPE() { return MitkCESTDicomWithMetaFileMimeType(); } std::string MitkCESTIOMimeTypes::CEST_DICOM_WITH_META_FILE_NAME() { // create a unique and sensible name for this mime type static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".image.dicom.cest.generic.meta"; return name; } MitkCESTIOMimeTypes::MitkCESTDicomWOMetaFileMimeType::MitkCESTDicomWOMetaFileMimeType() : IOMimeTypes::BaseDicomMimeType(CEST_DICOM_WITHOUT_META_FILE_NAME()) { this->SetCategory(IOMimeTypes::CATEGORY_IMAGES()); this->SetComment("CEST DICOM"); } bool MitkCESTIOMimeTypes::MitkCESTDicomWOMetaFileMimeType::AppliesTo(const std::string& path) const { bool canRead(IOMimeTypes::BaseDicomMimeType::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 if (!canRead) { return canRead; } std::string dir = path; if (!itksys::SystemTools::FileIsDirectory(path)) { dir = itksys::SystemTools::GetProgramPath(path); } std::string metafilePath = dir + "/" + "CEST_META.json"; canRead = !itksys::SystemTools::FileExists(metafilePath.c_str()); return canRead; } MitkCESTIOMimeTypes::MitkCESTDicomWOMetaFileMimeType* MitkCESTIOMimeTypes::MitkCESTDicomWOMetaFileMimeType::Clone() const { return new MitkCESTDicomWOMetaFileMimeType(*this); } MitkCESTIOMimeTypes::MitkCESTDicomWOMetaFileMimeType MitkCESTIOMimeTypes::CEST_DICOM_WITHOUT_META_FILE_MIMETYPE() { return MitkCESTDicomWOMetaFileMimeType(); } std::string MitkCESTIOMimeTypes::CEST_DICOM_WITHOUT_META_FILE_NAME() { // create a unique and sensible name for this mime type static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".image.dicom.cest.generic.nometa"; return name; } }