diff --git a/Modules/CEST/CMakeLists.txt b/Modules/CEST/CMakeLists.txt index b48bd100e0..8aee176bf4 100644 --- a/Modules/CEST/CMakeLists.txt +++ b/Modules/CEST/CMakeLists.txt @@ -1,8 +1,9 @@ MITK_CREATE_MODULE( DEPENDS MitkCore + PRIVATE MitkDICOMReader PACKAGE_DEPENDS PRIVATE ITK|ITKIOImageBase+ITKIOGDCM Poco ) add_subdirectory(autoload/IO) add_subdirectory(test) diff --git a/Modules/CEST/autoload/IO/CMakeLists.txt b/Modules/CEST/autoload/IO/CMakeLists.txt index cb10fda5b5..0e9e46ddbb 100644 --- a/Modules/CEST/autoload/IO/CMakeLists.txt +++ b/Modules/CEST/autoload/IO/CMakeLists.txt @@ -1,6 +1,6 @@ MITK_CREATE_MODULE( CESTIO DEPENDS MitkCEST MitkDICOMReader PACKAGE_DEPENDS PRIVATE ITK|ITKIOGDCM - AUTOLOAD_WITH MitkCore + AUTOLOAD_WITH MitkDICOMReader ) diff --git a/Modules/CEST/autoload/IO/files.cmake b/Modules/CEST/autoload/IO/files.cmake index 8864bec575..d6dda67b3b 100644 --- a/Modules/CEST/autoload/IO/files.cmake +++ b/Modules/CEST/autoload/IO/files.cmake @@ -1,9 +1,10 @@ set(CPP_FILES mitkCESTDICOMReaderService.cpp + mitkCESTGenericDICOMReaderService.cpp mitkCESTIOMimeTypes.cpp mitkCESTIOActivator.cpp ) set(RESOURCE_FILES cest_DKFZ.xml ) diff --git a/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.cpp b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.cpp new file mode 100644 index 0000000000..0276670704 --- /dev/null +++ b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.cpp @@ -0,0 +1,407 @@ +/*============================================================================ + +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 + +#include "mitkCESTImageNormalizationFilter.h" + +#include "itksys/SystemTools.hxx" + +#include +#include +#include + +#include +#include + +namespace +{ + std::string OPTION_NAME_B1() + { + return "B1 amplitude"; + } + + std::string OPTION_NAME_PULSE() + { + return "Pulse duration [us]"; + } + + std::string OPTION_NAME_DC() + { + return "Duty cycle [%]"; + } + + std::string OPTION_NAME_NORMALIZE() + { + return "Normalize data"; + } + + std::string OPTION_NAME_NORMALIZE_AUTOMATIC() + { + return "Automatic"; + } + + std::string OPTION_NAME_NORMALIZE_NO() + { + return "No"; + } + + std::string OPTION_NAME_MERGE() + { + return "Merge all series"; + } + + std::string OPTION_NAME_MERGE_YES() + { + return "Yes"; + } + + std::string OPTION_NAME_MERGE_NO() + { + return "No"; + } + + std::string META_FILE_OPTION_NAME_MERGE() + { + return "CEST.MergeAllSeries"; + } + +} + +namespace mitk +{ + DICOMTagPath DICOM_IMAGING_FREQUENCY_PATH() + { + return mitk::DICOMTagPath(0x0018, 0x0084); + } + + CESTDICOMManualReaderService::CESTDICOMManualReaderService(const CustomMimeType& mimeType, const std::string& description) + : BaseDICOMReaderService(mimeType, description) + { + IFileIO::Options options; + options[OPTION_NAME_B1()] = 0.0; + options[OPTION_NAME_PULSE()] = 0.0; + options[OPTION_NAME_DC()] = 0.0; + std::vector normalizationStrategy; + normalizationStrategy.push_back(OPTION_NAME_NORMALIZE_AUTOMATIC()); + normalizationStrategy.push_back(OPTION_NAME_NORMALIZE_NO()); + options[OPTION_NAME_NORMALIZE()] = normalizationStrategy; + std::vector mergeStrategy; + mergeStrategy.push_back(OPTION_NAME_MERGE_NO()); + mergeStrategy.push_back(OPTION_NAME_MERGE_YES()); + options[OPTION_NAME_MERGE()] = mergeStrategy; + this->SetDefaultOptions(options); + + this->RegisterService(); + } + + namespace + { + void ExtractOptionFromPropertyTree(const std::string& key, boost::property_tree::ptree& root, std::map& options) + { + auto finding = root.find(key); + if (finding != root.not_found()) + { + try + { + options[key] = finding->second.get_value(); + } + catch (const boost::property_tree::ptree_bad_data& /*e*/) + { + options[key] = finding->second.get_value(); + } + } + } + + IFileIO::Options ExtractOptionsFromFile(const std::string& file) + { + boost::property_tree::ptree root; + + if (itksys::SystemTools::FileExists(file)) + { + 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(); + } + } + else + { + 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_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); + ExtractOptionFromPropertyTree(META_FILE_OPTION_NAME_MERGE(), 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; + } + } + + void TransferMergeOption(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) + { + try + { + us::any_cast(finding->second); + } + catch (const us::BadAnyCastException& /*e*/) + { + replaceValue = true; + //if we cannot cast in string the user has not made a selection yet + } + } + + if (sourceFinding != sourceOptions.end() && us::any_cast(sourceFinding->second) != OPTION_NAME_MERGE_NO() && 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_B1Amplitude(), options, OPTION_NAME_B1()); + TransferOption(fileOptions, CEST_PROPERTY_NAME_PULSEDURATION(), options, OPTION_NAME_PULSE()); + TransferOption(fileOptions, CEST_PROPERTY_NAME_DutyCycle(), options, OPTION_NAME_DC()); + TransferMergeOption(fileOptions, META_FILE_OPTION_NAME_MERGE(), options, OPTION_NAME_MERGE()); + } + 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 + { + auto selector = mitk::DICOMFileReaderSelector::New(); + + const std::string mergeStrategy = this->GetOption(OPTION_NAME_MERGE()).ToString(); + + if (mergeStrategy == OPTION_NAME_MERGE_YES()) + { + auto r = ::us::GetModuleContext()->GetModule()->GetResource("cest_DKFZ.xml"); + selector->AddConfigFromResource(r); + } + + 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() + { + const Options userOptions = this->GetOptions(); + + const std::string mergeStrategy = userOptions.find(OPTION_NAME_MERGE())->second.ToString(); + this->SetOnlyRegardOwnSeries(mergeStrategy != OPTION_NAME_MERGE_YES()); + + std::vector result; + std::vector dicomResult = BaseDICOMReaderService::Read(); + + const std::string normalizationStrategy = userOptions.find(OPTION_NAME_NORMALIZE())->second.ToString(); + + for (const auto &item : dicomResult) + { + auto fileOptions = ExtractOptionsFromFile(this->GetCESTMetaFilePath()); + IFileIO::Options options; + TransferOption(userOptions, OPTION_NAME_B1(), options, CEST_PROPERTY_NAME_B1Amplitude()); + TransferOption(userOptions, OPTION_NAME_PULSE(), options, CEST_PROPERTY_NAME_PULSEDURATION()); + TransferOption(userOptions, OPTION_NAME_DC(), options, CEST_PROPERTY_NAME_DutyCycle()); + 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 freqProp = item->GetProperty(mitk::DICOMTagPathToPropertyName(DICOM_IMAGING_FREQUENCY_PATH()).c_str()); + + if (freqProp.IsNull()) + { + mitkThrow() << "Loaded image in invalid state. Does not contain the DICOM Imaging Frequency tag."; + } + SetCESTFrequencyMHz(item, mitk::ConvertDICOMStrToValue(freqProp->GetValueAsString())); + + 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 == OPTION_NAME_NORMALIZE_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/mitkCESTGenericDICOMReaderService.h b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.h new file mode 100644 index 0000000000..2918b325c8 --- /dev/null +++ b/Modules/CEST/autoload/IO/mitkCESTGenericDICOMReaderService.h @@ -0,0 +1,54 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITKCESTGenericDICOMReaderService_H +#define MITKCESTGenericDICOMReaderService_H + +#include + +namespace mitk { + + /** + Service wrapper that auto selects (using the mitk::DICOMFileReaderSelector) the best DICOMFileReader from + the DICOMReader module and loads the CEST relevant meta data from a provided cest_meta.json file or + provided from the user as reader options. + */ + class CESTDICOMManualReaderService : public BaseDICOMReaderService + { + public: + CESTDICOMManualReaderService(const CustomMimeType& mimeType, const std::string& description); + + /** Uses the AbstractFileReader Read function and add extra steps for CEST meta data */ + using AbstractFileReader::Read; + std::vector > Read() override; + + Options GetOptions() const override; + us::Any GetOption(const std::string& name) const override; + + protected: + CESTDICOMManualReaderService(const CESTDICOMManualReaderService&) = default; + CESTDICOMManualReaderService& operator=(const CESTDICOMManualReaderService&) = default; + + std::string GetCESTMetaFilePath() const; + std::string GetTRECFilePath() const; + std::string GetLISTFilePath() const; + + mitk::DICOMFileReader::Pointer GetReader(const mitk::StringList& relevantFiles) const override; + + private: + CESTDICOMManualReaderService* Clone() const override; + }; + + DICOMTagPath DICOM_IMAGING_FREQUENCY_PATH(); +} + +#endif // MITKCESTGenericDICOMReaderService_H diff --git a/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp b/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp index 008c80bf88..c9be3c3167 100644 --- a/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp +++ b/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp @@ -1,43 +1,102 @@ /*============================================================================ 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 "mitkCESTIOActivator.h" #include "mitkCESTDICOMReaderService.h" +#include "mitkCESTGenericDICOMReaderService.h" #include +#include #include "mitkCESTIOMimeTypes.h" +#include + namespace mitk { void CESTIOActivator::Load(us::ModuleContext *context) { us::ServiceProperties props; props[us::ServiceConstants::SERVICE_RANKING()] = 10; m_MimeTypes = mitk::MitkCESTIOMimeTypes::Get(); - for (std::vector::const_iterator mimeTypeIter = m_MimeTypes.begin(), - iterEnd = m_MimeTypes.end(); - mimeTypeIter != iterEnd; - ++mimeTypeIter) + for (const auto& mimeType : m_MimeTypes) + { + if (mimeType->GetName() == mitk::MitkCESTIOMimeTypes::CEST_DICOM_WITHOUT_META_FILE_NAME()) + { // "w/o meta" mimetype should only registered with low priority. + context->RegisterService(mimeType); + } + else + { + context->RegisterService(mimeType, props); + } + } + + m_CESTDICOMReader = std::make_unique(); + m_CESTDICOMManualWithMetaFileReader = std::make_unique(MitkCESTIOMimeTypes::CEST_DICOM_WITH_META_FILE_MIMETYPE(), "CEST DICOM Manual Reader"); + m_CESTDICOMManualWithOutMetaFileReader = std::make_unique(MitkCESTIOMimeTypes::CEST_DICOM_WITHOUT_META_FILE_MIMETYPE(), "CEST DICOM Manual Reader"); + + m_Context = context; + { + std::lock_guard lock(m_Mutex); + // Listen for events pertaining to dictionary services. + m_Context->AddServiceListener(this, &CESTIOActivator::DICOMTagsOfInterestServiceChanged, + std::string("(&(") + us::ServiceConstants::OBJECTCLASS() + "=" + + us_service_interface_iid() + "))"); + // Query for any service references matching any language. + std::vector > refs = + context->GetServiceReferences(); + if (!refs.empty()) + { + for (const auto& ref : refs) + { + this->RegisterTagsOfInterest(m_Context->GetService(ref)); + m_Context->UngetService(ref); + } + } + } + } + + void CESTIOActivator::Unload(us::ModuleContext *) + { + for (auto& elem : m_MimeTypes) { - context->RegisterService(*mimeTypeIter, props); + delete elem; + elem = nullptr; } + } - m_CESTDICOMReader.reset(new CESTDICOMReaderService()); + void CESTIOActivator::RegisterTagsOfInterest(IDICOMTagsOfInterest* toiService) const + { + if (toiService != nullptr) + { + toiService->AddTagOfInterest(mitk::DICOM_IMAGING_FREQUENCY_PATH()); + } } - void CESTIOActivator::Unload(us::ModuleContext *) {} + void CESTIOActivator::DICOMTagsOfInterestServiceChanged(const us::ServiceEvent event) + { + std::lock_guard lock(m_Mutex); + // If a dictionary service was registered, see if we + // need one. If so, get a reference to it. + if (event.GetType() == us::ServiceEvent::REGISTERED) + { + // Get a reference to the service object. + us::ServiceReference ref = event.GetServiceReference(); + this->RegisterTagsOfInterest(m_Context->GetService(ref)); + m_Context->UngetService(ref); + } + } } US_EXPORT_MODULE_ACTIVATOR(mitk::CESTIOActivator) diff --git a/Modules/CEST/autoload/IO/mitkCESTIOActivator.h b/Modules/CEST/autoload/IO/mitkCESTIOActivator.h index 87f2aca5ff..5c19a699b0 100644 --- a/Modules/CEST/autoload/IO/mitkCESTIOActivator.h +++ b/Modules/CEST/autoload/IO/mitkCESTIOActivator.h @@ -1,40 +1,53 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKCESTIOActivator_H #define MITKCESTIOActivator_H #include +#include #include #include #include +#include namespace mitk { struct IFileReader; class IDICOMTagsOfInterest; class CESTIOActivator : public us::ModuleActivator { public: void Load(us::ModuleContext *context) override; void Unload(us::ModuleContext *context) override; + private: + void RegisterTagsOfInterest(IDICOMTagsOfInterest* toiService) const; + void DICOMTagsOfInterestServiceChanged(const us::ServiceEvent event); + std::unique_ptr m_CESTDICOMReader; + std::unique_ptr m_CESTDICOMManualWithMetaFileReader; + std::unique_ptr m_CESTDICOMManualWithOutMetaFileReader; std::vector m_MimeTypes; + + // Module context + us::ModuleContext* m_Context; + /**mutex to guard the service listening */ + std::mutex m_Mutex; }; } #endif // MITKCESTIOActivator_H diff --git a/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp b/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp index 28b696d7ea..449b415072 100644 --- a/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp +++ b/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.cpp @@ -1,123 +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() : CustomMimeType(CEST_DICOM_MIMETYPE_NAME()) + MitkCESTIOMimeTypes::MitkCESTDicomMimeType::MitkCESTDicomMimeType() : IOMimeTypes::BaseDicomMimeType(CEST_DICOM_MIMETYPE_NAME()) { - this->AddExtension("gdcm"); - this->AddExtension("dcm"); - this->AddExtension("DCM"); - this->AddExtension("dc3"); - this->AddExtension("DC3"); - this->AddExtension("ima"); - this->AddExtension("img"); - this->SetCategory(IOMimeTypes::CATEGORY_IMAGES()); this->SetComment("CEST DICOM"); } bool MitkCESTIOMimeTypes::MitkCESTDicomMimeType::AppliesTo(const std::string &path) const { - bool canRead(CustomMimeType::AppliesTo(path)); + 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 - // Ask the GDCM ImageIO class directly - itk::GDCMImageIO::Pointer gdcmIO = itk::GDCMImageIO::New(); - canRead = gdcmIO->CanReadFile(path.c_str()); - 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()->empty(); + } return mapNotEmpty; } MitkCESTIOMimeTypes::MitkCESTDicomMimeType *MitkCESTIOMimeTypes::MitkCESTDicomMimeType::Clone() const { return new MitkCESTDicomMimeType(*this); } MitkCESTIOMimeTypes::MitkCESTDicomMimeType MitkCESTIOMimeTypes::CEST_DICOM_MIMETYPE() { return MitkCESTDicomMimeType(); } - // Names 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; + } + } diff --git a/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.h b/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.h index e94a72adf8..a35ba83274 100644 --- a/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.h +++ b/Modules/CEST/autoload/IO/mitkCESTIOMimeTypes.h @@ -1,52 +1,85 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKCESTIOMIMETYPES_H #define MITKCESTIOMIMETYPES_H -#include "mitkCustomMimeType.h" +#include "mitkIOMimeTypes.h" #include namespace mitk { /// Provides the custom mime types for MitkCEST class MitkCESTIOMimeTypes { public: /** Mime type that parses dicom files to determine whether they are CEST dicom files. * * Accepts dicom files that contain the substring "CEST_Rev" (via the mitk::CustomTagParser parsing) * in the tSequenceFileName parameter in dicom tag (0x0029, 0x1020) */ - class MitkCESTDicomMimeType : public CustomMimeType + class MitkCESTDicomMimeType : public IOMimeTypes::BaseDicomMimeType { public: MitkCESTDicomMimeType(); bool AppliesTo(const std::string &path) const override; MitkCESTDicomMimeType *Clone() const override; }; static MitkCESTDicomMimeType CEST_DICOM_MIMETYPE(); static std::string CEST_DICOM_MIMETYPE_NAME(); + /** Mime type that indicated generic CEST dicom files. + * + * The mime type assumes that dicom files that have a CEST_META.json file in the + * same directory are CEST DICOMs and relevant for this mime type. + */ + class MitkCESTDicomWithMetaFileMimeType : public IOMimeTypes::BaseDicomMimeType + { + public: + MitkCESTDicomWithMetaFileMimeType(); + bool AppliesTo(const std::string& path) const override; + MitkCESTDicomWithMetaFileMimeType* Clone() const override; + }; + + static MitkCESTDicomWithMetaFileMimeType CEST_DICOM_WITH_META_FILE_MIMETYPE(); + static std::string CEST_DICOM_WITH_META_FILE_NAME(); + + /** Mime type that indicated dicom files that can be potantially read as Generic + * CEST but have *NO* CEST meta information. + * + * The mime type is used to offer the manual CEST loading for all DICOM images with + * low priority if no CEST meta file is present. + */ + class MitkCESTDicomWOMetaFileMimeType : public IOMimeTypes::BaseDicomMimeType + { + public: + MitkCESTDicomWOMetaFileMimeType(); + bool AppliesTo(const std::string& path) const override; + MitkCESTDicomWOMetaFileMimeType* Clone() const override; + }; + + static MitkCESTDicomWOMetaFileMimeType CEST_DICOM_WITHOUT_META_FILE_MIMETYPE(); + static std::string CEST_DICOM_WITHOUT_META_FILE_NAME(); + // Get all Mime Types static std::vector Get(); private: // purposely not implemented MitkCESTIOMimeTypes(); MitkCESTIOMimeTypes(const MitkCESTIOMimeTypes &); }; } #endif // MITKCESTIOMIMETYPES_H diff --git a/Modules/CEST/files.cmake b/Modules/CEST/files.cmake index 34a71a20af..8d29b23be8 100644 --- a/Modules/CEST/files.cmake +++ b/Modules/CEST/files.cmake @@ -1,13 +1,15 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCESTImageNormalizationFilter.cpp mitkCustomTagParser.cpp mitkCESTImageDetectionHelper.cpp + mitkExtractCESTOffset.cpp + mitkCESTPropertyHelper.cpp ) set(RESOURCE_FILES 1416.json 1485.json 1494.json ) diff --git a/Modules/CEST/include/mitkCESTPropertyHelper.h b/Modules/CEST/include/mitkCESTPropertyHelper.h new file mode 100644 index 0000000000..f23f9140b1 --- /dev/null +++ b/Modules/CEST/include/mitkCESTPropertyHelper.h @@ -0,0 +1,60 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef __CEST_PROERTY_HELPER_H +#define __CEST_PROERTY_HELPER_H + +#include "mitkIPropertyProvider.h" +#include "mitkIPropertyOwner.h" + +#include "MitkCESTExports.h" + +namespace mitk +{ + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PREPERATIONTYPE(); + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_RECOVERYMODE(); + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_SPOILINGTYPE(); + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_OFFSETS(); + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_TREC(); + + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_FREQ(); + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PULSEDURATION(); + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_B1Amplitude(); + const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_DutyCycle(); + + /**Helper function that gets the CEST B1 amplitude property ("CEST.B1Amplitude") from the passed property provider. + If it is not possible to generate/get the value an mitk::Exception will be thrown.*/ + double MITKCEST_EXPORT GetCESTB1Amplitude(const IPropertyProvider* provider); + + /**Helper function that gets the CEST frequency property ("CEST.FREQ") from the input image. + If it is not possible to generate/get the value an mitk::Exception will be thrown. + The value is returned in [MHz]. Normally in the property it is stored in [Hz].*/ + double MITKCEST_EXPORT GetCESTFrequency(const IPropertyProvider* provider); + + /**Helper function that sets the CEST frequency property ("CEST.FREQ") in the passed owner. + If it owner is nullptr nothing will be done. + The value is passed in [MHz] and set in the property in [Hz].*/ + void MITKCEST_EXPORT SetCESTFrequencyMHz(IPropertyOwner* owner, double freqInMHz); + + /**Helper function that gets the CEST pulse duration property ("CEST.PulseDuration") from the input image. + If it is not possible to generate/get the value an mitk::Exception will be thrown. + The value is returned in [s]. Normally in the property it is stored in micro secs.*/ + double MITKCEST_EXPORT GetCESTPulseDuration(const IPropertyProvider* provider); + + /**Helper function that gets the CEST duty cycle property ("CEST.DutyCycle") from the input image. + If it is not possible to generate/get the value an mitk::Exception will be thrown. + The value is returned as scaling factor (1 == 100%), in contrast to the porperty where it is stored as + a percentage value (e.g. 56 %, so the function return will be 0.56).*/ + double MITKCEST_EXPORT GetCESTDutyCycle(const IPropertyProvider* provider); +} + +#endif // __CEST_PROERTY_HELPER_H diff --git a/Modules/CEST/include/mitkCustomTagParser.h b/Modules/CEST/include/mitkCustomTagParser.h index 9798b975d3..4718aab1c9 100644 --- a/Modules/CEST/include/mitkCustomTagParser.h +++ b/Modules/CEST/include/mitkCustomTagParser.h @@ -1,147 +1,139 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKCUSTOMTAGPARSER_H #define MITKCUSTOMTAGPARSER_H #include #include #include namespace mitk { /** The custom tag parser can be used to parse the custom dicom tag of the siemens private tag (0x0029, 0x1020) to extract relevant CEST data. An initial parsing determines whether the provided string belongs to CEST data at all. To make the check and extract the revision number the following rules are aplied: \n
  1. Sequence name (tSequenceFileName) must either
    1. start with the substring "CEST" (case insensitiv), or
    2. contain the substring "_CEST" (case insensitiv).
  2. Sequence name (tSequenceFileName) must contain the substring "_Rev" (case insensitiv).
  3. All numbers after "_Rev" represent the revision number; until either
    1. the next _, or
    2. end of sequence name.
Which custom parameters to save and to which property name can be controlled by a json file. This file can be either provided as a resource for the MitkCEST module during compilation or placed next to the MitkCEST library in your binary folder. The expected format for the file "REVISIONNUMBER.json":
{
"REVISIONNUMBER" : "revision_json",
"sWiPMemBlock.alFree[1]" : "AdvancedMode",
"sWiPMemBlock.alFree[2]" : "RetreatMode"
}
where :
  • REVISIONNUMBER is the revision number of this json parameter mapping (files with non digit characters in their name will be ignored)
  • sWiPMemBlock.alFree[1] is the name of one parameter in the private dicom tag
  • AdvancedMode is the name of the property the content of sWiPMemBlock.alFree[1] should be saved to
\note It is assumed that the entire content of tag (0x0029, 0x1020) is provided and that it es hex encoded (12\23\04...). If the sampling type is list it will try to access LIST.txt at the location provided in the constructor to read the offsets. */ class MITKCEST_EXPORT CustomTagParser { public: /// the constructor expects a path to one of the files to be loaded or the directory of the dicom files CustomTagParser(std::string relevantFile); /// parse the provided dicom property and return a property list based on the closest revision parameter mapping mitk::PropertyList::Pointer ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty); /// parse the provided string and return a property list based on the closest revision parameter mapping mitk::PropertyList::Pointer ParseDicomPropertyString(std::string dicomPropertyString); + static std::string ReadListFromFile(const std::string& filePath); + /** Extract the revision out of the passed sequenceFileName. If the file name is not a valid CEST file name (see rules in the class documentation) exceptions will be thrown. If the file name is valid but contains no revision number an empty string will be returned. */ static std::string ExtractRevision(std::string sequenceFileName); void SetParseStrategy(std::string parseStrategy); void SetRevisionMappingStrategy(std::string revisionMappingStrategy); - /// name of the property for the offsets, including normalization offsets - static const std::string m_OffsetsPropertyName; - /// name of the property for the data acquisition revision static const std::string m_RevisionPropertyName; /// name of the property for the json parameter mapping revision static const std::string m_JSONRevisionPropertyName; /// prefix for all CEST related property names static const std::string m_CESTPropertyPrefix; protected: std::string GetRevisionAppropriateJSONString(std::string revisionString); void GetClosestLowerRevision(std::string revisionString); std::string GetClosestLowerRevision(std::string revisionString, std::vector availableRevisionsVector); /// Decides whether or not the image is likely to be a T1Map, if not it is assumed to be a CEST sequence bool IsT1Sequence(std::string preparationType, std::string recoveryMode, std::string spoilingType, std::string revisionString); /// Get a string filled with the properly formated offsets based on the sampling type and offset std::string GetOffsetString(std::string samplingType, std::string offset, std::string measurements); /// returns a vector revision numbers of all REVISIONNUMBER.json found beside the MitkCEST library std::vector GetExternalRevisions(); /// returns a vector revision numbers of all REVISIONNUMBER.json provided as resources during the compile std::vector GetInternalRevisions(); /// returns the path where external jsons are expected to be located std::string GetExternalJSONDirectory(); /// the closest lower revision provided as resource, empty if none found std::string m_ClosestInternalRevision; /// the closest lower revision provided as a json beside the library, empty if none found std::string m_ClosestExternalRevision; /// revision independent mapping to inject into the revision dependent json string static const std::string m_RevisionIndependentMapping; /// default revision dependent json string if none is found static const std::string m_DefaultJsonString; /// path to the dicom data std::string m_DicomDataPath; /// Should the kind of data be automatically determined or should it be parsed as a specific one std::string m_ParseStrategy; /// How to handle parameter mapping based on absent revision jsons std::string m_RevisionMappingStrategy; }; - const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_TOTALSCANTIME(); - const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PREPERATIONTYPE(); - const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_RECOVERYMODE(); - const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_SPOILINGTYPE(); - const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_OFFSETS(); - const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_TREC(); - } #endif // MITKCUSTOMTAGPARSER_H diff --git a/Modules/CEST/include/mitkExtractCESTOffset.h b/Modules/CEST/include/mitkExtractCESTOffset.h new file mode 100644 index 0000000000..6434060a8b --- /dev/null +++ b/Modules/CEST/include/mitkExtractCESTOffset.h @@ -0,0 +1,39 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef __MITK_EXTRACT_CEST_OFFSET_H_ +#define __MITK_EXTRACT_CEST_OFFSET_H_ + +#include + +#include "MitkCESTExports.h" + +namespace mitk +{ + /**Helper function that gets the CEST offset property ("CEST.Offsets") from the input + image as vector of ScalarType. + If it is not possible to generate/get the offset an mitk::Excpetion will be thrown. + The values of the vector are in [ppm]. + @post Number of extracted offsets equal the number of timesteps of the image. + */ + MITKCEST_EXPORT std::vector ExtractCESTOffset(const BaseData* image); + + /**Helper function that gets the CEST offset property ("CEST.TREC") from the input image as vector of ScalarType. + If it is not possible to generate/get the T1 times an mitk::Excpetion will be thrown. + The values of the vector are in [sec]. In the property they are stored in [ms] and scaled appropriately + before returning. + @post Number of extracted T1 times equal the number of timesteps of the image. + */ + MITKCEST_EXPORT std::vector ExtractCESTT1Time(const BaseData* image); +} + +#endif diff --git a/Modules/CEST/src/mitkCESTImageDetectionHelper.cpp b/Modules/CEST/src/mitkCESTImageDetectionHelper.cpp index b653029097..f87333b2f8 100644 --- a/Modules/CEST/src/mitkCESTImageDetectionHelper.cpp +++ b/Modules/CEST/src/mitkCESTImageDetectionHelper.cpp @@ -1,85 +1,83 @@ /*============================================================================ 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 "mitkCESTImageDetectionHelper.h" -#include "mitkCustomTagParser.h" +#include "mitkCESTPropertyHelper.h" #include "mitkImage.h" #include "mitkDataNode.h" #include "mitkNodePredicateFunction.h" bool mitk::IsAnyCESTImage(const Image* cestImage) { - if (!cestImage) return false; - - auto prop = cestImage->GetProperty(mitk::CEST_PROPERTY_NAME_TOTALSCANTIME().c_str()); - - return prop.IsNotNull(); + return IsCESTorWasabiImage(cestImage) || IsCESTT1Image(cestImage); }; bool mitk::IsCESTorWasabiImage(const Image* cestImage) { if (!cestImage) return false; - return IsAnyCESTImage(cestImage) && !IsCESTT1Image(cestImage); + auto prop = cestImage->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str()); + + return prop.IsNotNull(); }; bool mitk::IsCESTT1Image(const Image* cestImage) { if (!cestImage) return false; auto prop = cestImage->GetProperty(mitk::CEST_PROPERTY_NAME_TREC().c_str()); return prop.IsNotNull(); }; mitk::NodePredicateBase::Pointer mitk::CreateAnyCESTImageNodePredicate() { auto cestCheck = [](const mitk::DataNode * node) { if (node) { return mitk::IsAnyCESTImage(dynamic_cast(node->GetData())); } return false; }; return mitk::NodePredicateFunction::New(cestCheck).GetPointer(); }; mitk::NodePredicateBase::Pointer mitk::CreateCESTorWasabiImageNodePredicate() { auto cestCheck = [](const mitk::DataNode * node) { if (node) { return mitk::IsCESTorWasabiImage(dynamic_cast(node->GetData())); } return false; }; return mitk::NodePredicateFunction::New(cestCheck).GetPointer(); }; mitk::NodePredicateBase::Pointer mitk::CreateCESTT1ImageNodePredicate() { auto cestCheck = [](const mitk::DataNode * node) { if (node) { return mitk::IsCESTT1Image(dynamic_cast(node->GetData())); } return false; }; return mitk::NodePredicateFunction::New(cestCheck).GetPointer(); }; diff --git a/Modules/CEST/src/mitkCESTImageNormalizationFilter.cpp b/Modules/CEST/src/mitkCESTImageNormalizationFilter.cpp index ac4f25cb88..1edb27f3e3 100644 --- a/Modules/CEST/src/mitkCESTImageNormalizationFilter.cpp +++ b/Modules/CEST/src/mitkCESTImageNormalizationFilter.cpp @@ -1,229 +1,202 @@ /*============================================================================ 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 "mitkCESTImageNormalizationFilter.h" -#include +#include +#include #include #include #include #include mitk::CESTImageNormalizationFilter::CESTImageNormalizationFilter() { } mitk::CESTImageNormalizationFilter::~CESTImageNormalizationFilter() { } void mitk::CESTImageNormalizationFilter::GenerateData() { mitk::Image::ConstPointer inputImage = this->GetInput(0); if ((inputImage->GetDimension() != 4)) { mitkThrow() << "mitk::CESTImageNormalizationFilter:GenerateData works only with 4D images, sorry."; return; } auto resultMitkImage = this->GetOutput(); AccessFixedDimensionByItk(inputImage, NormalizeTimeSteps, 4); auto originalTimeGeometry = this->GetInput()->GetTimeGeometry(); auto resultTimeGeometry = mitk::ProportionalTimeGeometry::New(); unsigned int numberOfNonM0s = m_NonM0Indices.size(); resultTimeGeometry->Expand(numberOfNonM0s); for (unsigned int index = 0; index < numberOfNonM0s; ++index) { resultTimeGeometry->SetTimeStepGeometry(originalTimeGeometry->GetGeometryCloneForTimeStep(m_NonM0Indices.at(index)), index); } resultMitkImage->SetTimeGeometry(resultTimeGeometry); resultMitkImage->SetPropertyList(this->GetInput()->GetPropertyList()->Clone()); - resultMitkImage->GetPropertyList()->SetStringProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str(), m_RealOffsets.c_str()); + resultMitkImage->GetPropertyList()->SetStringProperty(CEST_PROPERTY_NAME_OFFSETS().c_str(), m_RealOffsets.c_str()); // remove uids resultMitkImage->GetPropertyList()->DeleteProperty("DICOM.0008.0018"); resultMitkImage->GetPropertyList()->DeleteProperty("DICOM.0020.000D"); resultMitkImage->GetPropertyList()->DeleteProperty("DICOM.0020.000E"); } -std::vector ExtractOffsets(const mitk::Image* image) -{ - std::vector result; - - if (image) - { - std::string offsets = ""; - std::vector parts; - if (image->GetPropertyList()->GetStringProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str(), offsets) && !offsets.empty()) - { - boost::algorithm::trim(offsets); - boost::split(parts, offsets, boost::is_any_of(" ")); - - for (auto part : parts) - { - std::istringstream iss(part); - iss.imbue(std::locale("C")); - double d; - iss >> d; - result.push_back(d); - } - } - } - - return result; -} - - template void mitk::CESTImageNormalizationFilter::NormalizeTimeSteps(const itk::Image* image) { typedef itk::Image ImageType; typedef itk::Image OutputImageType; - auto offsets = ExtractOffsets(this->GetInput()); + auto offsets = ExtractCESTOffset(this->GetInput()); // determine normalization images std::vector mZeroIndices; std::stringstream offsetsWithoutM0; offsetsWithoutM0.imbue(std::locale("C")); m_NonM0Indices.clear(); for (unsigned int index = 0; index < offsets.size(); ++index) { if ((offsets.at(index) < -299) || (offsets.at(index) > 299)) { mZeroIndices.push_back(index); } else { offsetsWithoutM0 << offsets.at(index) << " "; m_NonM0Indices.push_back(index); } } auto resultImage = OutputImageType::New(); typename ImageType::RegionType targetEntireRegion = image->GetLargestPossibleRegion(); targetEntireRegion.SetSize(3, m_NonM0Indices.size()); resultImage->SetRegions(targetEntireRegion); resultImage->Allocate(); resultImage->FillBuffer(0); unsigned int numberOfTimesteps = image->GetLargestPossibleRegion().GetSize(3); typename ImageType::RegionType lowerMZeroRegion = image->GetLargestPossibleRegion(); lowerMZeroRegion.SetSize(3, 1); typename ImageType::RegionType upperMZeroRegion = image->GetLargestPossibleRegion(); upperMZeroRegion.SetSize(3, 1); typename ImageType::RegionType sourceRegion = image->GetLargestPossibleRegion(); sourceRegion.SetSize(3, 1); typename OutputImageType::RegionType targetRegion = resultImage->GetLargestPossibleRegion(); targetRegion.SetSize(3, 1); unsigned int targetTimestep = 0; for (unsigned int sourceTimestep = 0; sourceTimestep < numberOfTimesteps; ++sourceTimestep) { unsigned int lowerMZeroIndex = mZeroIndices[0]; unsigned int upperMZeroIndex = mZeroIndices[0]; for (unsigned int loop = 0; loop < mZeroIndices.size(); ++loop) { if (mZeroIndices[loop] <= sourceTimestep) { lowerMZeroIndex = mZeroIndices[loop]; } if (mZeroIndices[loop] > sourceTimestep) { upperMZeroIndex = mZeroIndices[loop]; break; } } bool isMZero = (lowerMZeroIndex == sourceTimestep); double weight = 0.0; if (lowerMZeroIndex == upperMZeroIndex) { weight = 1.0; } else { weight = 1.0 - double(sourceTimestep - lowerMZeroIndex) / double(upperMZeroIndex - lowerMZeroIndex); } if (isMZero) { //do nothing } else { lowerMZeroRegion.SetIndex(3, lowerMZeroIndex); upperMZeroRegion.SetIndex(3, upperMZeroIndex); sourceRegion.SetIndex(3, sourceTimestep); targetRegion.SetIndex(3, targetTimestep); itk::ImageRegionConstIterator lowerMZeroIterator(image, lowerMZeroRegion); itk::ImageRegionConstIterator upperMZeroIterator(image, upperMZeroRegion); itk::ImageRegionConstIterator sourceIterator(image, sourceRegion); itk::ImageRegionIterator targetIterator(resultImage.GetPointer(), targetRegion); while (!sourceIterator.IsAtEnd()) { double normalizationFactor = weight * lowerMZeroIterator.Get() + (1.0 - weight) * upperMZeroIterator.Get(); if (mitk::Equal(normalizationFactor, 0)) { targetIterator.Set(0); } else { targetIterator.Set(double(sourceIterator.Get()) / normalizationFactor); } ++lowerMZeroIterator; ++upperMZeroIterator; ++sourceIterator; ++targetIterator; } ++targetTimestep; } } // get Pointer to output image mitk::Image::Pointer resultMitkImage = this->GetOutput(); // write into output image mitk::CastToMitkImage(resultImage, resultMitkImage); m_RealOffsets = offsetsWithoutM0.str(); } void mitk::CESTImageNormalizationFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<< "GenerateOutputInformation()"); } bool mitk::IsNotNormalizedCESTImage(const Image* cestImage) { - auto offsets = ExtractOffsets(cestImage); + auto offsets = ExtractCESTOffset(cestImage); - for (auto offset : offsets) + for (const auto& offset : offsets) { if (offset < -299 || offset > 299) { return true; } } return false; }; diff --git a/Modules/CEST/src/mitkCESTPropertyHelper.cpp b/Modules/CEST/src/mitkCESTPropertyHelper.cpp new file mode 100644 index 0000000000..5cf42bfe28 --- /dev/null +++ b/Modules/CEST/src/mitkCESTPropertyHelper.cpp @@ -0,0 +1,146 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkCESTPropertyHelper.h" + +#include "mitkDICOMProperty.h" +#include "mitkStringProperty.h" + +const std::string mitk::CEST_PROPERTY_NAME_PREPERATIONTYPE() +{ + return "CEST.PreparationType"; +} + +const std::string mitk::CEST_PROPERTY_NAME_RECOVERYMODE() +{ + return "CEST.RecoveryMode"; +} + +const std::string mitk::CEST_PROPERTY_NAME_SPOILINGTYPE() +{ + return "CEST.SpoilingType"; +} + +const std::string mitk::CEST_PROPERTY_NAME_OFFSETS() +{ + return "CEST.Offsets"; +} + +const std::string mitk::CEST_PROPERTY_NAME_TREC() +{ + return std::string("CEST.TREC"); +} + +const std::string mitk::CEST_PROPERTY_NAME_FREQ() +{ + return std::string("CEST.FREQ"); +} + +const std::string mitk::CEST_PROPERTY_NAME_PULSEDURATION() +{ + return std::string("CEST.PulseDuration"); +} + +const std::string mitk::CEST_PROPERTY_NAME_B1Amplitude() +{ + return std::string("CEST.B1Amplitude"); +} + +const std::string mitk::CEST_PROPERTY_NAME_DutyCycle() +{ + return std::string("CEST.DutyCycle"); +} + + +double mitk::GetCESTB1Amplitude(const IPropertyProvider* provider) +{ + if (!provider) + { + mitkThrow() << "Cannot determine B! amplitude. Passed property provider is invalid."; + } + + double result = 0.0; + + auto prop = provider->GetConstProperty(CEST_PROPERTY_NAME_B1Amplitude().c_str()); + if (prop.IsNotNull()) + { + result = mitk::ConvertDICOMStrToValue(prop->GetValueAsString()); + } + else mitkThrow() << "Cannot determine B! amplitude. Selected input has no property \"" << CEST_PROPERTY_NAME_B1Amplitude() << "\""; + + return result; +} + +double mitk::GetCESTFrequency(const IPropertyProvider* provider) +{ + if (!provider) + { + mitkThrow() << "Cannot determine frequency. Passed property provider is invalid."; + } + + double result = 0.0; + + auto prop = provider->GetConstProperty(CEST_PROPERTY_NAME_FREQ().c_str()); + if (prop.IsNotNull()) + { + result = mitk::ConvertDICOMStrToValue(prop->GetValueAsString()) * 0.000001; + } + else mitkThrow() << "Cannot determine frequency. Selected input has no property \"" << CEST_PROPERTY_NAME_FREQ() << "\""; + + return result; +} + +void mitk::SetCESTFrequencyMHz(IPropertyOwner* owner, double freqInMHz) +{ + if (nullptr != owner) + { + owner->SetProperty(CEST_PROPERTY_NAME_FREQ().c_str(), mitk::StringProperty::New(ConvertValueToDICOMStr(freqInMHz * 1000000))); + } +} + +double mitk::GetCESTPulseDuration(const IPropertyProvider* provider) +{ + if (!provider) + { + mitkThrow() << "Cannot determine pulse duration. Passed property provider is invalid."; + } + + double result = 0.0; + + auto prop = provider->GetConstProperty(CEST_PROPERTY_NAME_PULSEDURATION().c_str()); + if (prop.IsNotNull()) + { + result = mitk::ConvertDICOMStrToValue(prop->GetValueAsString()) * 0.000001; + } + else mitkThrow() << "Cannot determine pulse duration. Selected input has no property \"" << CEST_PROPERTY_NAME_PULSEDURATION() << "\""; + + return result; +} + +double mitk::GetCESTDutyCycle(const IPropertyProvider* provider) +{ + if (!provider) + { + mitkThrow() << "Cannot determine duty cycle. Passed property provider is invalid."; + } + + double result = 0.0; + + auto prop = provider->GetConstProperty(CEST_PROPERTY_NAME_DutyCycle().c_str()); + if (prop.IsNotNull()) + { + result = mitk::ConvertDICOMStrToValue(prop->GetValueAsString()) * 0.01; + } + else mitkThrow() << "Cannot determine duty cycle. Selected input has no property \"" << CEST_PROPERTY_NAME_DutyCycle() << "\""; + + return result; +} diff --git a/Modules/CEST/src/mitkCustomTagParser.cpp b/Modules/CEST/src/mitkCustomTagParser.cpp index 4f0f0f3d9d..304c76fc12 100644 --- a/Modules/CEST/src/mitkCustomTagParser.cpp +++ b/Modules/CEST/src/mitkCustomTagParser.cpp @@ -1,872 +1,846 @@ /*============================================================================ 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 "mitkCustomTagParser.h" #include #include -#include +#include "mitkCESTPropertyHelper.h" #include "mitkIPropertyPersistence.h" + #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleContext.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" #include #include #include #include #include #include #include #include #include #include #include namespace { mitk::IPropertyPersistence *GetPersistenceService() { mitk::IPropertyPersistence *result = nullptr; std::vector> persRegisters = us::GetModuleContext()->GetServiceReferences(); if (!persRegisters.empty()) { if (persRegisters.size() > 1) { MITK_WARN << "Multiple property description services found. Using just one."; } result = us::GetModuleContext()->GetService(persRegisters.front()); } return result; }; } const std::string mitk::CustomTagParser::m_CESTPropertyPrefix = "CEST."; -const std::string mitk::CustomTagParser::m_OffsetsPropertyName = m_CESTPropertyPrefix + "Offsets"; const std::string mitk::CustomTagParser::m_RevisionPropertyName = m_CESTPropertyPrefix + "Revision"; const std::string mitk::CustomTagParser::m_JSONRevisionPropertyName = m_CESTPropertyPrefix + "revision_json"; const std::string mitk::CustomTagParser::m_RevisionIndependentMapping = "\n" " \"sProtConsistencyInfo.tSystemType\" : \"SysType\",\n" " \"sProtConsistencyInfo.flNominalB0\" : \"NominalB0\",\n" " \"sTXSPEC.asNucleusInfo[0].lFrequency\" : \"FREQ\",\n" " \"sTXSPEC.asNucleusInfo[0].flReferenceAmplitude\" : \"RefAmp\",\n" " \"alTR[0]\" : \"TR\",\n" " \"alTE[0]\" : \"TE\",\n" " \"lAverages\" : \"averages\",\n" " \"lRepetitions\" : \"repetitions\",\n" " \"adFlipAngleDegree[0]\" : \"ImageFlipAngle\",\n" " \"lTotalScanTimeSec\" : \"TotalScanTime\","; const std::string mitk::CustomTagParser::m_DefaultJsonString = "{\n" " \"default mapping, corresponds to revision 1416\" : \"revision_json\",\n" " \"sWiPMemBlock.alFree[1]\" : \"AdvancedMode\",\n" " \"sWiPMemBlock.alFree[2]\" : \"RecoveryMode\",\n" " \"sWiPMemBlock.alFree[3]\" : \"DoubleIrrMode\",\n" " \"sWiPMemBlock.alFree[4]\" : \"BinomMode\",\n" " \"sWiPMemBlock.alFree[5]\" : \"MtMode\",\n" " \"sWiPMemBlock.alFree[6]\" : \"PreparationType\",\n" " \"sWiPMemBlock.alFree[7]\" : \"PulseType\",\n" " \"sWiPMemBlock.alFree[8]\" : \"SamplingType\",\n" " \"sWiPMemBlock.alFree[9]\" : \"SpoilingType\",\n" " \"sWiPMemBlock.alFree[10]\" : \"measurements\",\n" " \"sWiPMemBlock.alFree[11]\" : \"NumberOfPulses\",\n" " \"sWiPMemBlock.alFree[12]\" : \"NumberOfLockingPulses\",\n" " \"sWiPMemBlock.alFree[13]\" : \"PulseDuration\",\n" " \"sWiPMemBlock.alFree[14]\" : \"DutyCycle\",\n" " \"sWiPMemBlock.alFree[15]\" : \"RecoveryTime\",\n" " \"sWiPMemBlock.alFree[16]\" : \"RecoveryTimeM0\",\n" " \"sWiPMemBlock.alFree[17]\" : \"ReadoutDelay\",\n" " \"sWiPMemBlock.alFree[18]\" : \"BinomDuration\",\n" " \"sWiPMemBlock.alFree[19]\" : \"BinomDistance\",\n" " \"sWiPMemBlock.alFree[20]\" : \"BinomNumberofPulses\",\n" " \"sWiPMemBlock.alFree[21]\" : \"BinomPreRepetions\",\n" " \"sWiPMemBlock.alFree[22]\" : \"BinomType\",\n" " \"sWiPMemBlock.adFree[1]\" : \"Offset\",\n" " \"sWiPMemBlock.adFree[2]\" : \"B1Amplitude\",\n" " \"sWiPMemBlock.adFree[3]\" : \"AdiabaticPulseMu\",\n" " \"sWiPMemBlock.adFree[4]\" : \"AdiabaticPulseBW\",\n" " \"sWiPMemBlock.adFree[5]\" : \"AdiabaticPulseLength\",\n" " \"sWiPMemBlock.adFree[6]\" : \"AdiabaticPulseAmp\",\n" " \"sWiPMemBlock.adFree[7]\" : \"FermiSlope\",\n" " \"sWiPMemBlock.adFree[8]\" : \"FermiFWHM\",\n" " \"sWiPMemBlock.adFree[9]\" : \"DoubleIrrDuration\",\n" " \"sWiPMemBlock.adFree[10]\" : \"DoubleIrrAmplitude\",\n" " \"sWiPMemBlock.adFree[11]\" : \"DoubleIrrRepetitions\",\n" " \"sWiPMemBlock.adFree[12]\" : \"DoubleIrrPreRepetitions\"\n" "}"; mitk::CustomTagParser::CustomTagParser(std::string relevantFile) : m_ClosestInternalRevision(""), m_ClosestExternalRevision("") { std::string pathToDirectory; std::string fileName; itksys::SystemTools::SplitProgramPath(relevantFile, pathToDirectory, fileName); m_DicomDataPath = pathToDirectory; m_ParseStrategy = "Automatic"; m_RevisionMappingStrategy = "Fuzzy"; } std::string mitk::CustomTagParser::ExtractRevision(std::string sequenceFileName) { //all rules are case insesitive. Thus we convert everything to lower case //in order to check everything only once. std::string cestPrefix = "cest"; std::string cestPrefix2 = "_cest"; std::string cestPrefix3 = "\\cest"; //this version covers the fact that the strings extracted //from the SIEMENS tag has an additional prefix that is seperated by backslash. std::string revisionPrefix = "_rev"; std::transform(sequenceFileName.begin(), sequenceFileName.end(), sequenceFileName.begin(), ::tolower); bool isCEST = sequenceFileName.compare(0, cestPrefix.length(), cestPrefix) == 0; std::size_t foundPosition = 0; if (!isCEST) { foundPosition = sequenceFileName.find(cestPrefix2); isCEST = foundPosition != std::string::npos; } if (!isCEST) { foundPosition = sequenceFileName.find(cestPrefix3); isCEST = foundPosition != std::string::npos; } if (!isCEST) { mitkThrow() << "Invalid CEST sequence file name. No CEST prefix found. Could not extract revision."; } foundPosition = sequenceFileName.find(revisionPrefix, foundPosition); if (foundPosition == std::string::npos) { mitkThrow() << "Invalid CEST sequence file name. No revision prefix was found in CEST sequence file name. Could not extract revision."; } std::string revisionString = sequenceFileName.substr(foundPosition + revisionPrefix.length(), std::string::npos); std::size_t firstNoneNumber = revisionString.find_first_not_of("0123456789"); if (firstNoneNumber != std::string::npos) { revisionString.erase(firstNoneNumber, std::string::npos); } return revisionString; } bool mitk::CustomTagParser::IsT1Sequence(std::string preparationType, std::string recoveryMode, std::string spoilingType, std::string revisionString) { bool isT1 = false; // if a forced parse strategy is set, use that one if ("T1" == m_ParseStrategy) { return true; } if ("CEST/WASABI" == m_ParseStrategy) { return false; } if (("T1Recovery" == preparationType) || ("T1Inversion" == preparationType)) { isT1 = true; } // How to interpret the recoveryMode depends on the age of the sequence // older sequences use 0 = false and 1 = true, newer ones 1 = false and 2 = true. // A rough rule of thumb is to assume that if the SpoilingType is 0, then the first // convention is chosen, if it is 1, then the second applies. Otherwise // we assume revision 1485 and newer to follow the new convention. // This unfortunate heuristic is due to somewhat arbitrary CEST sequence implementations. if (!isT1) { std::string thisIsTrue = "1"; std::string thisIsFalse = "0"; if ("0" == spoilingType) { thisIsFalse = "0"; thisIsTrue = "1"; } else if ("1" == spoilingType) { thisIsFalse = "1"; thisIsTrue = "2"; } else { int revisionNrWeAssumeToBeDifferenciating = 1485; if (std::stoi(revisionString) - revisionNrWeAssumeToBeDifferenciating < 0) { thisIsFalse = "0"; thisIsTrue = "1"; } else { thisIsFalse = "1"; thisIsTrue = "2"; } } if (thisIsFalse == recoveryMode) { isT1 = false; } else if (thisIsTrue == recoveryMode) { isT1 = true; } } return isT1; } mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomPropertyString(std::string dicomPropertyString) { auto results = mitk::PropertyList::New(); if ("" == dicomPropertyString) { //MITK_ERROR << "Could not parse empty custom dicom string"; return results; } std::map privateParameters; // The Siemens private tag contains information like "43\52\23\34". // We jump over each "\" and convert the number; std::string bytes; { const std::size_t SUBSTR_LENGTH = 2; const std::size_t INPUT_LENGTH = dicomPropertyString.length(); if (INPUT_LENGTH < SUBSTR_LENGTH) return results; const std::size_t MAX_INPUT_OFFSET = INPUT_LENGTH - SUBSTR_LENGTH; bytes.reserve(INPUT_LENGTH / 3 + 1); try { for (std::size_t i = 0; i <= MAX_INPUT_OFFSET; i += 3) { std::string byte_string = dicomPropertyString.substr(i, SUBSTR_LENGTH); int byte = static_cast(std::stoi(byte_string.c_str(), nullptr, 16)); bytes.push_back(byte); } } catch (const std::invalid_argument&) // std::stoi() could not perform conversion { return results; } } // extract parameter list std::string parameterListString; { const std::string ASCCONV_BEGIN = "### ASCCONV BEGIN ###"; const std::string ASCCONV_END = "### ASCCONV END ###"; auto offset = bytes.find(ASCCONV_BEGIN); if (std::string::npos == offset) return results; offset += ASCCONV_BEGIN.length(); auto count = bytes.find(ASCCONV_END, offset); if (std::string::npos == count) return results; count -= offset; parameterListString = bytes.substr(offset, count); } boost::replace_all(parameterListString, "\r\n", "\n"); boost::char_separator newlineSeparator("\n"); boost::tokenizer> parameters(parameterListString, newlineSeparator); for (const auto ¶meter : parameters) { std::vector parts; boost::split(parts, parameter, boost::is_any_of("=")); if (parts.size() == 2) { parts[0].erase(std::remove(parts[0].begin(), parts[0].end(), ' '), parts[0].end()); parts[1].erase(parts[1].begin(), parts[1].begin() + 1); // first character is a space privateParameters[parts[0]] = parts[1]; } } std::string revisionString = ""; try { revisionString = ExtractRevision(privateParameters["tSequenceFileName"]); } catch (const std::exception &e) { MITK_ERROR << "Cannot deduce revision information. Reason: "<< e.what(); return results; } results->SetProperty(m_RevisionPropertyName, mitk::StringProperty::New(revisionString)); std::string jsonString = GetRevisionAppropriateJSONString(revisionString); boost::property_tree::ptree root; std::istringstream jsonStream(jsonString); try { boost::property_tree::read_json(jsonStream, root); } catch (const boost::property_tree::json_parser_error &e) { mitkThrow() << "Could not parse json file. Error was:\n" << e.what(); } for (auto it : root) { if (it.second.empty()) { std::string propertyName = m_CESTPropertyPrefix + it.second.data(); if (m_JSONRevisionPropertyName == propertyName) { results->SetProperty(propertyName, mitk::StringProperty::New(it.first)); } else { results->SetProperty(propertyName, mitk::StringProperty::New(privateParameters[it.first])); } } else { MITK_ERROR << "Currently no support for nested dicom tag descriptors in json file."; } } std::string offset = ""; std::string measurements = ""; results->GetStringProperty("CEST.Offset", offset); results->GetStringProperty("CEST.measurements", measurements); if (measurements.empty()) { std::string stringRepetitions = ""; results->GetStringProperty("CEST.repetitions", stringRepetitions); std::string stringAverages = ""; results->GetStringProperty("CEST.averages", stringAverages); const auto ERROR_STRING = "Could not find measurements, fallback assumption of repetitions + averages could not be determined either."; if (!stringRepetitions.empty() && !stringAverages.empty()) { std::stringstream measurementStream; try { measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages); measurements = measurementStream.str(); MITK_INFO << "Could not find measurements, assuming repetitions + averages. That is: " << measurements; } catch (const std::invalid_argument&) { MITK_ERROR << ERROR_STRING; } } else { MITK_WARN << ERROR_STRING; } } std::string preparationType = ""; std::string recoveryMode = ""; std::string spoilingType = ""; results->GetStringProperty(CEST_PROPERTY_NAME_PREPERATIONTYPE().c_str(), preparationType); results->GetStringProperty(CEST_PROPERTY_NAME_RECOVERYMODE().c_str(), recoveryMode); results->GetStringProperty(CEST_PROPERTY_NAME_SPOILINGTYPE().c_str(), spoilingType); if (this->IsT1Sequence(preparationType, recoveryMode, spoilingType, revisionString)) { MITK_INFO << "Parsed as T1 image"; - mitk::LocaleSwitch localeSwitch("C"); - std::stringstream trecStream; std::string trecPath = m_DicomDataPath + "/TREC.txt"; - std::ifstream list(trecPath.c_str()); + auto trec = ReadListFromFile(trecPath); - if (list.good()) - { - std::string currentTime; - while (std::getline(list, currentTime)) - { - trecStream << currentTime << " "; - } - } - else + if(trec.empty()) { MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath; } - results->SetStringProperty(CEST_PROPERTY_NAME_TREC().c_str(), trecStream.str().c_str()); + results->SetStringProperty(CEST_PROPERTY_NAME_TREC().c_str(), trec.c_str()); } else { MITK_INFO << "Parsed as CEST or WASABI image"; std::string sampling = ""; bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling); if (hasSamplingInformation) { std::string offsets = GetOffsetString(sampling, offset, measurements); - results->SetStringProperty(m_OffsetsPropertyName.c_str(), offsets.c_str()); + results->SetStringProperty(CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets.c_str()); } else { MITK_WARN << "Could not determine sampling type."; } } //persist all properties mitk::IPropertyPersistence *persSrv = GetPersistenceService(); if (persSrv) { auto propertyMap = results->GetMap(); for (auto const &prop : *propertyMap) { PropertyPersistenceInfo::Pointer info = PropertyPersistenceInfo::New(); std::string key = prop.first; std::replace(key.begin(), key.end(), '.', '_'); info->SetNameAndKey(prop.first, key); persSrv->AddInfo(info); } } return results; } +std::string mitk::CustomTagParser::ReadListFromFile(const std::string& filePath) +{ + std::stringstream listStream; + std::ifstream list(filePath.c_str()); + list.imbue(std::locale("C")); + + if (list.good()) + { + std::string currentValue; + while (std::getline(list, currentValue)) + { + listStream << currentValue << " "; + } + } + return listStream.str(); +} + mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty) { if (!dicomProperty) { MITK_ERROR << "DICOM property empty"; } auto results = mitk::PropertyList::New(); if (dicomProperty) { results = ParseDicomPropertyString(dicomProperty->GetValue()); } return results; } std::vector mitk::CustomTagParser::GetInternalRevisions() { const std::vector configs = us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false); std::vector availableRevisionsVector; for (auto const resource : configs) { availableRevisionsVector.push_back(std::stoi(resource.GetBaseName())); } return availableRevisionsVector; } std::vector mitk::CustomTagParser::GetExternalRevisions() { std::string stringToJSONDirectory = GetExternalJSONDirectory(); std::string prospectiveJsonsPath = stringToJSONDirectory + "/*.json"; std::set JsonFiles; Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS); std::vector availableRevisionsVector; for (auto const jsonpath : JsonFiles) { std::string jsonDir; std::string jsonName; itksys::SystemTools::SplitProgramPath(jsonpath, jsonDir, jsonName); std::string revision = itksys::SystemTools::GetFilenameWithoutExtension(jsonName); // disregard jsons which contain letters in their name bool onlyNumbers = (revision.find_first_not_of("0123456789") == std::string::npos); if(onlyNumbers) { availableRevisionsVector.push_back(std::stoi(revision)); } } return availableRevisionsVector; } std::string mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString, std::vector availableRevisionsVector) { // descending order std::sort(availableRevisionsVector.begin(), availableRevisionsVector.end(), std::greater<>()); int revision = std::stoi(revisionString); int index = 0; int numberOfRevisions = availableRevisionsVector.size(); while (index < numberOfRevisions) { // current mapping still has a higher revision number if ((availableRevisionsVector[index] - revision) > 0) { ++index; } else { break; } } if (index < numberOfRevisions) { std::stringstream foundRevisionStream; foundRevisionStream << availableRevisionsVector[index]; return foundRevisionStream.str(); } return ""; } void mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString) { m_ClosestInternalRevision = GetClosestLowerRevision(revisionString, GetInternalRevisions()); m_ClosestExternalRevision = GetClosestLowerRevision(revisionString, GetExternalRevisions()); if ("Strict" == m_RevisionMappingStrategy && !((0 == m_ClosestInternalRevision.compare(revisionString)) || (0 == m_ClosestExternalRevision.compare(revisionString)))) { // strict revision mapping and neither revision does match the dicom meta data std::stringstream errorMessageStream; errorMessageStream << "\nCould not parse dicom data in strict mode, data revision " << revisionString << " has no known matching parameter mapping. To use the closest known older parameter mapping select the " << "\"Fuzzy\" revision mapping option when loading the data.\n" << "\nCurrently known revision mappings are:\n Precompiled:"; for (const auto revision : GetInternalRevisions()) { errorMessageStream << " " << revision; } errorMessageStream << "\n External:"; for (const auto revision : GetExternalRevisions()) { errorMessageStream << " " << revision; } errorMessageStream << "\n\nExternal revision mapping descriptions should be located at\n\n"; std::string stringToJSONDirectory = GetExternalJSONDirectory(); errorMessageStream << stringToJSONDirectory; errorMessageStream << "\n\nTo provide an external mapping for this revision create a " << revisionString << ".json there. You might need to create the directory first."; mitkThrow() << errorMessageStream.str(); } } std::string mitk::CustomTagParser::GetRevisionAppropriateJSONString(std::string revisionString) { std::string returnValue = ""; if ("" == revisionString) { MITK_WARN << "Could not extract revision"; } else { GetClosestLowerRevision(revisionString); bool useExternal = false; bool useInternal = false; if ("" != m_ClosestExternalRevision) { useExternal = true; } if ("" != m_ClosestInternalRevision) { useInternal = true; } if (useExternal && useInternal) { if (std::stoi(m_ClosestInternalRevision) > std::stoi(m_ClosestExternalRevision)) { useExternal = false; } } if (useExternal) { std::string stringToJSONDirectory = GetExternalJSONDirectory(); std::string prospectiveJsonPath = stringToJSONDirectory + "/" + m_ClosestExternalRevision + ".json"; std::ifstream externalJSON(prospectiveJsonPath.c_str()); if (externalJSON.good()) { MITK_INFO << "Found external json for CEST parameters at " << prospectiveJsonPath; std::stringstream buffer; buffer << externalJSON.rdbuf(); returnValue = buffer.str(); useInternal = false; } } if (useInternal) { std::string filename = m_ClosestInternalRevision + ".json"; us::ModuleResource jsonResource = us::GetModuleContext()->GetModule()->GetResource(filename); if (jsonResource.IsValid() && jsonResource.IsFile()) { MITK_INFO << "Found no external json for CEST parameters. Closest internal mapping is for revision " << m_ClosestInternalRevision; us::ModuleResourceStream jsonStream(jsonResource); std::stringstream buffer; buffer << jsonStream.rdbuf(); returnValue = buffer.str(); } } } if ("" == returnValue) { MITK_WARN << "Could not identify parameter mapping for the given revision " << revisionString << ", using default mapping."; returnValue = m_DefaultJsonString; } // inject the revision independent mapping before the first newline { returnValue.insert(returnValue.find("\n"), m_RevisionIndependentMapping); } return returnValue; } std::string mitk::CustomTagParser::GetOffsetString(std::string samplingType, std::string offset, std::string measurements) { - mitk::LocaleSwitch localeSwitch("C"); std::stringstream results; + results.imbue(std::locale("C")); std::string normalizationIndicatingOffset = "-300"; double offsetDouble = 0.0; int measurementsInt = 0; bool validOffset = false; bool validMeasurements = false; if ("" != offset) { validOffset = true; offsetDouble = std::stod(offset); } if ("" != measurements) { validMeasurements = true; measurementsInt = std::stoi(measurements); } std::vector offsetVector; if (validOffset && validMeasurements) { for (int step = 0; step < measurementsInt -1; ++step) { double currentOffset = -offsetDouble + 2 * step * offsetDouble / (measurementsInt - 2.0); offsetVector.push_back(currentOffset); } } else { MITK_WARN << "Invalid offset or measurements, offset calculation will only work for list sampling type."; } if (samplingType == "1" || samplingType == "Regular") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (const auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "2" || samplingType == "Alternating") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (auto& entry : offsetVector) { entry = std::abs(entry); } std::sort(offsetVector.begin(), offsetVector.end(), std::greater<>()); for (unsigned int index = 0; index < offsetVector.size(); ++index) { offsetVector[index] = std::pow(-1, index) * offsetVector[index]; } for (auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "3" || samplingType == "List") { std::string listPath = m_DicomDataPath + "/LIST.txt"; - std::ifstream list(listPath.c_str()); - if (list.good()) + auto values = ReadListFromFile(listPath); + + if (!values.empty()) { - std::string currentOffset; - while (std::getline(list, currentOffset)) - { - results << currentOffset << " "; - } + results << values; } else { MITK_ERROR << "Could not load list at " << listPath; } } else if (samplingType == "4" || samplingType == "SingleOffset") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (int step = 0; step < measurementsInt - 1; ++step) { results << offsetDouble << " "; } } } else { MITK_WARN << "Encountered unknown sampling type."; } std::string resultString = results.str(); // replace multiple spaces by a single space std::string::iterator newEnditerator = std::unique(resultString.begin(), resultString.end(), [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } ); resultString.erase(newEnditerator, resultString.end()); if ((resultString.length() > 0) && (resultString.at(resultString.length() - 1) == ' ')) { resultString.erase(resultString.end() - 1, resultString.end()); } if ((resultString.length() > 0) && (resultString.at(0) == ' ')) { resultString.erase(resultString.begin(), ++(resultString.begin())); } return resultString; } void mitk::CustomTagParser::SetParseStrategy(std::string parseStrategy) { m_ParseStrategy = parseStrategy; } void mitk::CustomTagParser::SetRevisionMappingStrategy(std::string revisionMappingStrategy) { m_RevisionMappingStrategy = revisionMappingStrategy; } std::string mitk::CustomTagParser::GetExternalJSONDirectory() { std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation(); std::string stringToModule; std::string libraryName; itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName); std::stringstream jsonDirectory; jsonDirectory << stringToModule << "/CESTRevisionMapping"; return jsonDirectory.str(); } - -const std::string mitk::CEST_PROPERTY_NAME_TOTALSCANTIME() -{ - return "CEST.TotalScanTime"; -}; - -const std::string mitk::CEST_PROPERTY_NAME_PREPERATIONTYPE() -{ - return "CEST.PreparationType"; -}; - -const std::string mitk::CEST_PROPERTY_NAME_RECOVERYMODE() -{ - return "CEST.RecoveryMode"; -}; - -const std::string mitk::CEST_PROPERTY_NAME_SPOILINGTYPE() -{ - return "CEST.SpoilingType"; -}; - -const std::string mitk::CEST_PROPERTY_NAME_OFFSETS() -{ - return "CEST.Offsets"; -}; - -const std::string mitk::CEST_PROPERTY_NAME_TREC() -{ - return std::string("CEST.TREC"); -} diff --git a/Modules/CEST/src/mitkExtractCESTOffset.cpp b/Modules/CEST/src/mitkExtractCESTOffset.cpp new file mode 100644 index 0000000000..4599ebc345 --- /dev/null +++ b/Modules/CEST/src/mitkExtractCESTOffset.cpp @@ -0,0 +1,82 @@ +/*============================================================================ + +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 "mitkExtractCESTOffset.h" +#include "mitkCESTPropertyHelper.h" + +#include +#include + + +std::vector mitk::ExtractCESTT1Time(const BaseData* image) +{ + std::vector result; + + auto prop = image->GetProperty(CEST_PROPERTY_NAME_TREC().c_str()); + if (prop.IsNotNull()) + { + auto valueStr = prop->GetValueAsString(); + + std::istringstream iss; + iss.imbue(std::locale("C")); + iss.str(valueStr); + double d; + + while (iss >> d) + { + if (!iss.fail()) + { + result.emplace_back(d); + } + } + + if (result.size() != image->GetTimeSteps()) mitkThrow() << "Cannot determine T1 times. Selected input has an property \"" << CEST_PROPERTY_NAME_TREC() << "\" that don't match the number of time steps of input."; + + for (auto& value : result) + { + value *= 0.001; + } + } + else mitkThrow() << "Cannot determine T1 time grid (TREC). Selected input has no property \"" << CEST_PROPERTY_NAME_TREC() << "\""; + + return result; +} + +std::vector mitk::ExtractCESTOffset(const BaseData* image) +{ + std::vector result; + + auto prop = image->GetProperty(CEST_PROPERTY_NAME_OFFSETS().c_str()); + if (prop.IsNotNull()) + { + auto valueStr = prop->GetValueAsString(); + + std::istringstream iss; + iss.imbue(std::locale("C")); + iss.str(valueStr); + double d; + + while (iss >> d) + { + if (!iss.fail()) + { + result.emplace_back(d); + } + } + + if (result.size() != image->GetTimeSteps()) mitkThrow() << "Cannot determine offset. Selected input has an property \"" << CEST_PROPERTY_NAME_OFFSETS() << "\" that don't match the number of time steps of input."; + + } + else mitkThrow() << "Cannot determine frequency. Selected input has no property \"" << CEST_PROPERTY_NAME_OFFSETS() << "\""; + + return result; +} diff --git a/Modules/CEST/test/mitkCESTDICOMReaderServiceTest.cpp b/Modules/CEST/test/mitkCESTDICOMReaderServiceTest.cpp index 9e3e9d1bde..4bdf8f651f 100644 --- a/Modules/CEST/test/mitkCESTDICOMReaderServiceTest.cpp +++ b/Modules/CEST/test/mitkCESTDICOMReaderServiceTest.cpp @@ -1,94 +1,95 @@ /*============================================================================ 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. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.h" // std includes #include // MITK includes #include #include #include #include "mitkCESTImageDetectionHelper.h" #include "mitkCustomTagParser.h" +#include "mitkCESTPropertyHelper.h" class mitkCESTDICOMReaderServiceTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkCESTDICOMReaderServiceTestSuite); // Test the dicom property parsing MITK_TEST(LoadCESTDICOMData_Success); MITK_TEST(LoadNormalizedCESTDICOMData_Success); MITK_TEST(LoadT1DICOMData_Success); CPPUNIT_TEST_SUITE_END(); private: public: void setUp() override { } void tearDown() override { } void LoadCESTDICOMData_Success() { mitk::IFileReader::Options options; options["Force type"] = std::string( "Automatic" ); options["Revision mapping"] = std::string( "Strict" ); options["Normalize data"] = std::string("No"); mitk::Image::Pointer cestImage = mitk::IOUtil::Load(GetTestDataFilePath("CEST/B1=0.6MUT/MEI_NER_PHANTOM.MR.E0202_MEISSNER.0587.0001.2017.10.25.22.11.10.373351.41828677.IMA"), options); CPPUNIT_ASSERT_MESSAGE("Make certain offsets have been correctly loaded for CEST image." ,cestImage->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str())->GetValueAsString() == "-300 2 -2 1.92982 -1.92982 1.85965 -1.85965 1.78947 -1.78947 1.7193 -1.7193 1.64912 -1.64912 1.57895 -1.57895 1.50877 -1.50877 1.4386 -1.4386 1.36842 -1.36842 1.29825 -1.29825 1.22807 -1.22807 1.15789 -1.15789 1.08772 -1.08772 1.01754 -1.01754 0.947368 -0.947368 0.877193 -0.877193 0.807018 -0.807018 0.736842 -0.736842 0.666667 -0.666667 0.596491 -0.596491 0.526316 -0.526316 0.45614 -0.45614 0.385965 -0.385965 0.315789 -0.315789 0.245614 -0.245614 0.175439 -0.175439 0.105263 -0.105263 0.0350877 -0.0350877"); CPPUNIT_ASSERT_MESSAGE("Wrong number of frames are reconstructed.", cestImage->GetTimeSteps() == 59); std::string temp; CPPUNIT_ASSERT_MESSAGE("Make certain image is not loaded as T1.", !mitk::IsCESTT1Image(cestImage)); } void LoadNormalizedCESTDICOMData_Success() { mitk::IFileReader::Options options; options["Force type"] = std::string("Automatic"); options["Revision mapping"] = std::string("Strict"); options["Normalize data"] = std::string("Automatic"); mitk::Image::Pointer cestImage = mitk::IOUtil::Load(GetTestDataFilePath("CEST/B1=0.6MUT/MEI_NER_PHANTOM.MR.E0202_MEISSNER.0587.0001.2017.10.25.22.11.10.373351.41828677.IMA"), options); auto tempREsult = cestImage->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str())->GetValueAsString(); CPPUNIT_ASSERT_MESSAGE("Make certain offsets have been correctly loaded for CEST image.", cestImage->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str())->GetValueAsString() == "2 -2 1.92982 -1.92982 1.85965 -1.85965 1.78947 -1.78947 1.7193 -1.7193 1.64912 -1.64912 1.57895 -1.57895 1.50877 -1.50877 1.4386 -1.4386 1.36842 -1.36842 1.29825 -1.29825 1.22807 -1.22807 1.15789 -1.15789 1.08772 -1.08772 1.01754 -1.01754 0.947368 -0.947368 0.877193 -0.877193 0.807018 -0.807018 0.736842 -0.736842 0.666667 -0.666667 0.596491 -0.596491 0.526316 -0.526316 0.45614 -0.45614 0.385965 -0.385965 0.315789 -0.315789 0.245614 -0.245614 0.175439 -0.175439 0.105263 -0.105263 0.0350877 -0.0350877 "); CPPUNIT_ASSERT_MESSAGE("Wrong number of frames are reconstructed.", cestImage->GetTimeSteps() == 58); std::string temp; CPPUNIT_ASSERT_MESSAGE("Make certain image is not loaded as T1.", !mitk::IsCESTT1Image(cestImage)); } void LoadT1DICOMData_Success() { mitk::IFileReader::Options options; options["Force type"] = std::string("Automatic"); options["Revision mapping"] = std::string("Strict"); mitk::Image::Pointer cestImage = mitk::IOUtil::Load(GetTestDataFilePath("CEST/T1MAP/MEI_NER_PHANTOM.MR.E0202_MEISSNER.0279.0001.2017.10.25.20.21.27.996834.41803047.IMA"), options); std::string temp; CPPUNIT_ASSERT_MESSAGE("Make certain image is loaded as T1.", mitk::IsCESTT1Image(cestImage)); CPPUNIT_ASSERT_MESSAGE("Wrong number of frames are reconstructed.", cestImage->GetTimeSteps() == 17); } }; MITK_TEST_SUITE_REGISTRATION(mitkCESTDICOMReaderService) diff --git a/Modules/CEST/test/mitkCustomTagParserTest.cpp b/Modules/CEST/test/mitkCustomTagParserTest.cpp index 9aa1a04dba..6b34a86053 100644 --- a/Modules/CEST/test/mitkCustomTagParserTest.cpp +++ b/Modules/CEST/test/mitkCustomTagParserTest.cpp @@ -1,374 +1,375 @@ /*============================================================================ 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. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.h" // std includes #include // MITK includes #include "mitkCustomTagParser.h" +#include "mitkCESTPropertyHelper.h" #include //itksys #include #include // microservice includes #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleContext.h" // VTK includes #include class mitkCustomTagParserTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkCustomTagParserTestSuite); // Test the dicom property parsing MITK_TEST(ValidPropertyParsedToPropertyList_Success); MITK_TEST(ValidPropertyMissingParametersParsedToEmptyPropertiesPropertyList_Success); MITK_TEST(ValidPropertyRevisionVeryLow_UseDefault_Success); MITK_TEST(ValidPropertyNoExactRevisionMatchUseInternal_Success); MITK_TEST(ValidPropertyNoExactRevisionMatchUseExternal_Success); MITK_TEST(ValidPropertyWindowsLineEndings_Success); MITK_TEST(InvalidPropertyInvalidRevision_Failure); MITK_TEST(InvalidPropertyNoCESTSequence_Failure); MITK_TEST(InvalidPropertyGarbageInDelimiters_Failure); MITK_TEST(ValidPropertyAlternatingOffset_Success); MITK_TEST(ValidPropertySimpleOffset_Success); MITK_TEST(ValidPropertyListOffset_Success); MITK_TEST(ExtractRevision); CPPUNIT_TEST_SUITE_END(); private: std::string m_PathToModule; std::string m_ValidCESTCustomTag; std::string m_ValidCESTCustomTagAllParametersMissing; std::string m_ValidCESTCustomTagUnsupportedRevisionTooLow; // rev 12 std::string m_ValidCESTCustomTagUnsupportedRevisionNoExactJSONUseInternal; // rev 1417 std::string m_ValidCESTCustomTagUnsupportedRevisionNoExactJSONUseExternal; // rev 120 std::string m_InvalidCESTCustomTagRevisionNoNumber; //rev abc std::string m_NonCESTCustomTag; // no CEST_Rev substring std::string m_GarbageWithinDelimiters; // ASCII part of custom tag is just garbage std::string m_ValidCESTCustomTagWindowsLineEndings; // have windows line endings encoded std::string m_ValidCESTCustomTagAlternatingOffset; std::string m_ValidCESTCustomTagSingleOffset; std::string m_ValidCESTCustomTagListOffset; public: void setUp() override { std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation(); std::string libraryName; itksys::SystemTools::SplitProgramPath(moduleLocation, m_PathToModule, libraryName); m_ValidCESTCustomTag = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\34\\31\\36\\22\\22\\0A\\74\\50\\72\\6F\\74\\6F\\63\\6F\\6C\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\57\\41\\53\\41\\42\\49\\2B\\41\\46\\38\\2D\\4D\\49\\54\\4B\\2D\\54\\45\\53\\54\\2B\\41\\46\\38\\2D\\37\\54\\2B\\41\\46\\38\\2D\\33\\73\\6C\\69\\63\\65\\73\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\74\\53\\79\\73\\74\\65\\6D\\54\\79\\70\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\30\\39\\35\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\66\\6C\\4E\\6F\\6D\\69\\6E\\61\\6C\\42\\30\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\2E\\39\\38\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\6C\\46\\72\\65\\71\\75\\65\\6E\\63\\79\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\39\\37\\31\\35\\34\\37\\35\\36\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\66\\6C\\52\\65\\66\\65\\72\\65\\6E\\63\\65\\41\\6D\\70\\6C\\69\\74\\75\\64\\65\\20\\3D\\20\\31\\36\\31\\2E\\34\\33\\35\\0A\\61\\6C\\54\\52\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\33\\30\\30\\30\\0A\\61\\6C\\54\\45\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\36\\34\\30\\0A\\6C\\41\\76\\65\\72\\61\\67\\65\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\6C\\52\\65\\70\\65\\74\\69\\74\\69\\6F\\6E\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\31\\0A\\61\\64\\46\\6C\\69\\70\\41\\6E\\67\\6C\\65\\44\\65\\67\\72\\65\\65\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\0A\\6C\\54\\6F\\74\\61\\6C\\53\\63\\61\\6E\\54\\69\\6D\\65\\53\\65\\63\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\39\\37\\0A\\73\\45\\46\\49\\53\\50\\45\\43\\2E\\62\\45\\46\\49\\44\\61\\74\\61\\56\\61\\6C\\69\\64\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\39\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\32\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\38\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\2E\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\31\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_ValidCESTCustomTagAllParametersMissing = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\34\\31\\36\\22\\22\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_ValidCESTCustomTagUnsupportedRevisionTooLow = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\32\\22\\22\\0A\\74\\50\\72\\6F\\74\\6F\\63\\6F\\6C\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\57\\41\\53\\41\\42\\49\\2B\\41\\46\\38\\2D\\4D\\49\\54\\4B\\2D\\54\\45\\53\\54\\2B\\41\\46\\38\\2D\\37\\54\\2B\\41\\46\\38\\2D\\33\\73\\6C\\69\\63\\65\\73\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\74\\53\\79\\73\\74\\65\\6D\\54\\79\\70\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\30\\39\\35\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\66\\6C\\4E\\6F\\6D\\69\\6E\\61\\6C\\42\\30\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\2E\\39\\38\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\6C\\46\\72\\65\\71\\75\\65\\6E\\63\\79\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\39\\37\\31\\35\\34\\37\\35\\36\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\66\\6C\\52\\65\\66\\65\\72\\65\\6E\\63\\65\\41\\6D\\70\\6C\\69\\74\\75\\64\\65\\20\\3D\\20\\31\\36\\31\\2E\\34\\33\\35\\0A\\61\\6C\\54\\52\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\33\\30\\30\\30\\0A\\61\\6C\\54\\45\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\36\\34\\30\\0A\\6C\\41\\76\\65\\72\\61\\67\\65\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\6C\\52\\65\\70\\65\\74\\69\\74\\69\\6F\\6E\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\31\\0A\\61\\64\\46\\6C\\69\\70\\41\\6E\\67\\6C\\65\\44\\65\\67\\72\\65\\65\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\0A\\6C\\54\\6F\\74\\61\\6C\\53\\63\\61\\6E\\54\\69\\6D\\65\\53\\65\\63\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\39\\37\\0A\\73\\45\\46\\49\\53\\50\\45\\43\\2E\\62\\45\\46\\49\\44\\61\\74\\61\\56\\61\\6C\\69\\64\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\39\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\32\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\38\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\2E\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\31\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_ValidCESTCustomTagUnsupportedRevisionNoExactJSONUseInternal = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\34\\31\\37\\22\\22\\0A\\74\\50\\72\\6F\\74\\6F\\63\\6F\\6C\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\57\\41\\53\\41\\42\\49\\2B\\41\\46\\38\\2D\\4D\\49\\54\\4B\\2D\\54\\45\\53\\54\\2B\\41\\46\\38\\2D\\37\\54\\2B\\41\\46\\38\\2D\\33\\73\\6C\\69\\63\\65\\73\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\74\\53\\79\\73\\74\\65\\6D\\54\\79\\70\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\30\\39\\35\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\66\\6C\\4E\\6F\\6D\\69\\6E\\61\\6C\\42\\30\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\2E\\39\\38\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\6C\\46\\72\\65\\71\\75\\65\\6E\\63\\79\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\39\\37\\31\\35\\34\\37\\35\\36\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\66\\6C\\52\\65\\66\\65\\72\\65\\6E\\63\\65\\41\\6D\\70\\6C\\69\\74\\75\\64\\65\\20\\3D\\20\\31\\36\\31\\2E\\34\\33\\35\\0A\\61\\6C\\54\\52\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\33\\30\\30\\30\\0A\\61\\6C\\54\\45\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\36\\34\\30\\0A\\6C\\41\\76\\65\\72\\61\\67\\65\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\6C\\52\\65\\70\\65\\74\\69\\74\\69\\6F\\6E\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\31\\0A\\61\\64\\46\\6C\\69\\70\\41\\6E\\67\\6C\\65\\44\\65\\67\\72\\65\\65\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\0A\\6C\\54\\6F\\74\\61\\6C\\53\\63\\61\\6E\\54\\69\\6D\\65\\53\\65\\63\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\39\\37\\0A\\73\\45\\46\\49\\53\\50\\45\\43\\2E\\62\\45\\46\\49\\44\\61\\74\\61\\56\\61\\6C\\69\\64\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\39\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\32\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\38\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\2E\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\31\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_ValidCESTCustomTagUnsupportedRevisionNoExactJSONUseExternal = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\32\\30\\22\\22\\0A\\74\\50\\72\\6F\\74\\6F\\63\\6F\\6C\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\57\\41\\53\\41\\42\\49\\2B\\41\\46\\38\\2D\\4D\\49\\54\\4B\\2D\\54\\45\\53\\54\\2B\\41\\46\\38\\2D\\37\\54\\2B\\41\\46\\38\\2D\\33\\73\\6C\\69\\63\\65\\73\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\74\\53\\79\\73\\74\\65\\6D\\54\\79\\70\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\30\\39\\35\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\66\\6C\\4E\\6F\\6D\\69\\6E\\61\\6C\\42\\30\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\2E\\39\\38\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\6C\\46\\72\\65\\71\\75\\65\\6E\\63\\79\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\39\\37\\31\\35\\34\\37\\35\\36\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\66\\6C\\52\\65\\66\\65\\72\\65\\6E\\63\\65\\41\\6D\\70\\6C\\69\\74\\75\\64\\65\\20\\3D\\20\\31\\36\\31\\2E\\34\\33\\35\\0A\\61\\6C\\54\\52\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\33\\30\\30\\30\\0A\\61\\6C\\54\\45\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\36\\34\\30\\0A\\6C\\41\\76\\65\\72\\61\\67\\65\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\6C\\52\\65\\70\\65\\74\\69\\74\\69\\6F\\6E\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\31\\0A\\61\\64\\46\\6C\\69\\70\\41\\6E\\67\\6C\\65\\44\\65\\67\\72\\65\\65\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\0A\\6C\\54\\6F\\74\\61\\6C\\53\\63\\61\\6E\\54\\69\\6D\\65\\53\\65\\63\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\39\\37\\0A\\73\\45\\46\\49\\53\\50\\45\\43\\2E\\62\\45\\46\\49\\44\\61\\74\\61\\56\\61\\6C\\69\\64\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\39\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\32\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\38\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\2E\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\31\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_InvalidCESTCustomTagRevisionNoNumber = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\61\\62\\63\\22\\22\\0A\\74\\50\\72\\6F\\74\\6F\\63\\6F\\6C\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\57\\41\\53\\41\\42\\49\\2B\\41\\46\\38\\2D\\4D\\49\\54\\4B\\2D\\54\\45\\53\\54\\2B\\41\\46\\38\\2D\\37\\54\\2B\\41\\46\\38\\2D\\33\\73\\6C\\69\\63\\65\\73\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\74\\53\\79\\73\\74\\65\\6D\\54\\79\\70\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\30\\39\\35\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\66\\6C\\4E\\6F\\6D\\69\\6E\\61\\6C\\42\\30\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\2E\\39\\38\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\6C\\46\\72\\65\\71\\75\\65\\6E\\63\\79\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\39\\37\\31\\35\\34\\37\\35\\36\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\66\\6C\\52\\65\\66\\65\\72\\65\\6E\\63\\65\\41\\6D\\70\\6C\\69\\74\\75\\64\\65\\20\\3D\\20\\31\\36\\31\\2E\\34\\33\\35\\0A\\61\\6C\\54\\52\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\33\\30\\30\\30\\0A\\61\\6C\\54\\45\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\36\\34\\30\\0A\\6C\\41\\76\\65\\72\\61\\67\\65\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\6C\\52\\65\\70\\65\\74\\69\\74\\69\\6F\\6E\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\31\\0A\\61\\64\\46\\6C\\69\\70\\41\\6E\\67\\6C\\65\\44\\65\\67\\72\\65\\65\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\0A\\6C\\54\\6F\\74\\61\\6C\\53\\63\\61\\6E\\54\\69\\6D\\65\\53\\65\\63\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\39\\37\\0A\\73\\45\\46\\49\\53\\50\\45\\43\\2E\\62\\45\\46\\49\\44\\61\\74\\61\\56\\61\\6C\\69\\64\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\39\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\32\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\38\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\2E\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\31\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_NonCESTCustomTag = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\59\\59\\59\\59\\5F\\5A\\5A\\5A\\5A\\5A\\5A\\5A\\22\\22\\0A\\74\\50\\72\\6F\\74\\6F\\63\\6F\\6C\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\57\\41\\53\\41\\42\\49\\2B\\41\\46\\38\\2D\\4D\\49\\54\\4B\\2D\\54\\45\\53\\54\\2B\\41\\46\\38\\2D\\37\\54\\2B\\41\\46\\38\\2D\\33\\73\\6C\\69\\63\\65\\73\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\74\\53\\79\\73\\74\\65\\6D\\54\\79\\70\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\30\\39\\35\\22\\22\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\66\\6C\\4E\\6F\\6D\\69\\6E\\61\\6C\\42\\30\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\2E\\39\\38\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\6C\\46\\72\\65\\71\\75\\65\\6E\\63\\79\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\39\\37\\31\\35\\34\\37\\35\\36\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\66\\6C\\52\\65\\66\\65\\72\\65\\6E\\63\\65\\41\\6D\\70\\6C\\69\\74\\75\\64\\65\\20\\3D\\20\\31\\36\\31\\2E\\34\\33\\35\\0A\\61\\6C\\54\\52\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\33\\30\\30\\30\\0A\\61\\6C\\54\\45\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\36\\34\\30\\0A\\6C\\41\\76\\65\\72\\61\\67\\65\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\6C\\52\\65\\70\\65\\74\\69\\74\\69\\6F\\6E\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\31\\0A\\61\\64\\46\\6C\\69\\70\\41\\6E\\67\\6C\\65\\44\\65\\67\\72\\65\\65\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\0A\\6C\\54\\6F\\74\\61\\6C\\53\\63\\61\\6E\\54\\69\\6D\\65\\53\\65\\63\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\39\\37\\0A\\73\\45\\46\\49\\53\\50\\45\\43\\2E\\62\\45\\46\\49\\44\\61\\74\\61\\56\\61\\6C\\69\\64\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\39\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\32\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\38\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\2E\\31\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\31\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_GarbageWithinDelimiters = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\32\\33\\39\\37\\34\\30\\32\\62\\76\\30\\77\\6E\\65\\72\\62\\66\\6B\\3C\\73\\79\\64\\68\\66\\6A\\6F\\5A\\49\\47\\28\\3D\\2F\\51\\22\\26\\54\\24\\29\\28\\2F\\26\\54\\C2\\A7\\51\\3D\\28\\25\\2F\\36\\7A\\C3\\9F\\61\\62\\72\\6E\\65\\62\\74\\6C\\61\\6B\\73\\64\\20\\76\\6E\\66\\6C\\61\\69\\72\\62\\74\\7A\\38\\33\\34\\35\\74\\31\\38\\33\\30\\37\\34\\7A\\62\\74\\28\\2F\\54\\26\\3D\\26\\28\\51\\50\\57\\C2\\A7\\45\\25\\3D\\51\\57\\C2\\A7\\62\\70\\39\\38\\65\\72\\74\\76\\68\\6E\\38\\39\\33\\34\\36\\7A\\31\\76\\C3\\9F\\30\\35\\39\\34\\75\\31\\6E\\30\\20\\74\\76\\6C\\61\\62\\72\\7A\\70\\39\\30\\65\\77\\35\\37\\32\\33\\38\\34\\36\\30\\39\\74\\7A\\C3\\9F\\39\\42\\20\\4E\\50\\29\\28\\20\\4E\\26\\2F\\3D\\2F\\24\\22\\26\\20\\50\\29\\4E\\72\\65\\74\\62\\68\\61\\70\\74\\6E\\76\\61\\70\\39\\72\\38\\74\\7A\\76\\42\\20\\26\\60\\3F\\29\\C2\\A7\\3D\\57\\26\\2F\\28\\29\\3F\\57\\26\\2F\\42\\20\\56\\24\\45\\57\\28\\26\\3F\\20\\4E\\62\\38\\34\\77\\7A\\61\\77\\C3\\9F\\20\\38\\34\\62\\6D\\C3\\9F\\61\\39\\65\\6D\\63\\36\\7A\\76\\77\\75\\69\\35\\34\\62\\36\\C3\\9F\\61\\38\\39\\6E\\36\\7A\\34\\35\\76\\6D\\38\\20\\6E\\75\\20\\20\\20\\20\\38\\33\\7A\\6E\\36\\76\\30\\33\\7A\\35\\36\\76\\C3\\9F\\5E\\5E\\39\\20\\5E\\62\\74\\7A\\C3\\9F\\34\\39\\38\\62\\7A\\6E\\20\\71\\38\\34\\65\\36\\7A\\76\\37\\38\\39\\C3\\9F\\C3\\9F\\20\\28\\2F\\20\\24\\3F\\57\\20\\26\\2F\\5A\\3F\\4E\\57\\24\\42\\20\\4D\\48\\4A\\55\\20\\28\\24\\2F\\20\\56\\4E\\5A\\3F\\57\\29\\28\\24\\29\\42\\26\\24\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0A\\20\\20\\20\\20\\7D\\0A\\20\\20\\7D\\0A\\7D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_ValidCESTCustomTagWindowsLineEndings = "20\\20\\20\\20\\20\\20\\20\\20\\3C\\43\\6F\\6E\\6E\\65\\63\\74\\69\\6F\\6E\\2E\\22\\22\\63\\31\\22\\22\\3E\\20\\20\\7B\\20\\22\\22\\49\\6D\\61\\67\\65\\52\\65\\61\\64\\79\\22\\22\\20\\22\\22\\22\\22\\20\\22\\22\\43\\6F\\6D\\70\\75\\74\\65\\49\\6D\\61\\67\\65\\22\\22\\20\\20\\7D\\0D\\0A\\20\\20\\20\\20\\20\\20\\7D\\0D\\0A\\20\\20\\20\\20\\7D\\0D\\0A\\20\\20\\7D\\0D\\0A\\7D\\0D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0D\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0D\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\34\\31\\36\\22\\22\\0D\\0A\\74\\50\\72\\6F\\74\\6F\\63\\6F\\6C\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\57\\41\\53\\41\\42\\49\\2B\\41\\46\\38\\2D\\4D\\49\\54\\4B\\2D\\54\\45\\53\\54\\2B\\41\\46\\38\\2D\\37\\54\\2B\\41\\46\\38\\2D\\33\\73\\6C\\69\\63\\65\\73\\22\\22\\0D\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\74\\53\\79\\73\\74\\65\\6D\\54\\79\\70\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\30\\39\\35\\22\\22\\0D\\0A\\73\\50\\72\\6F\\74\\43\\6F\\6E\\73\\69\\73\\74\\65\\6E\\63\\79\\49\\6E\\66\\6F\\2E\\66\\6C\\4E\\6F\\6D\\69\\6E\\61\\6C\\42\\30\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\2E\\39\\38\\0D\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\6C\\46\\72\\65\\71\\75\\65\\6E\\63\\79\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\39\\37\\31\\35\\34\\37\\35\\36\\0D\\0A\\73\\54\\58\\53\\50\\45\\43\\2E\\61\\73\\4E\\75\\63\\6C\\65\\75\\73\\49\\6E\\66\\6F\\5B\\30\\5D\\2E\\66\\6C\\52\\65\\66\\65\\72\\65\\6E\\63\\65\\41\\6D\\70\\6C\\69\\74\\75\\64\\65\\20\\3D\\20\\31\\36\\31\\2E\\34\\33\\35\\0D\\0A\\61\\6C\\54\\52\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\33\\30\\30\\30\\0D\\0A\\61\\6C\\54\\45\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\36\\34\\30\\0D\\0A\\6C\\41\\76\\65\\72\\61\\67\\65\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\6C\\52\\65\\70\\65\\74\\69\\74\\69\\6F\\6E\\73\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\31\\0D\\0A\\61\\64\\46\\6C\\69\\70\\41\\6E\\67\\6C\\65\\44\\65\\67\\72\\65\\65\\5B\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\0D\\0A\\6C\\54\\6F\\74\\61\\6C\\53\\63\\61\\6E\\54\\69\\6D\\65\\53\\65\\63\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\39\\37\\0D\\0A\\73\\45\\46\\49\\53\\50\\45\\43\\2E\\62\\45\\46\\49\\44\\61\\74\\61\\56\\61\\6C\\69\\64\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\36\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\30\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\39\\30\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\32\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\32\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\33\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\36\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\34\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\32\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\35\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\38\\30\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\36\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\37\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\2E\\31\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\31\\32\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\39\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\35\\30\\30\\30\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\2E\\37\\0D\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\31\\0D\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22\\20\\0D\\0A\\20\\20\\20\\20\\7D\\0D\\0A\\20\\20\\7D\\0D\\0A\\7D\\0D\\0A\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\47\\72\\61\\64\\69\\65\\6E\\74\\4D\\6F\\64\\65\\20\\72\\6D\\61\\6E\\63\\65\\43\\61\\63\\68\\65\\2E\\69\\6E\\6C\\69\\6E\\65\\5F\\70\\6F\\73\\64\\69\\73\\70\\5F\\63\\61\\6E\\5F\\73\\65\\74\\22\\22\\20\\3C\\44\\6C\\6C\\3E\\20\\22\\22\\4D\\72\\4D\\75\\6C\\74\\01\\20\\20\\20\\53\\48\\20\\20\\16\\20\\20\\20\\06\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\05\\20\\20\\20\\4D\\20\\20\\20\\05\\20\\20\\20\\46\\61\\73\\74\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\CD\\A0\\20\\20\\20\\20\\20\\20\\46\\6C\\6F\\77\\43\\6F\\6D\\70\\65\\6E\\73\\61\\74\\69\\6F\\6E\\20\\72\\65\\53\\6F\\75\\6E\\64\\22\\22\\20\\22\\22\\50\\72\\6F\\70\\65\\72\\74\\69\\65\\73\\2E\\53\\6F\\75\\6E\\64\\2E\\50\\6F\\73\\74\\53\\6F\\75\\6E\\64\\22\\22"; m_ValidCESTCustomTagAlternatingOffset = "36\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\34\\31\\36\\22\\22\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22"; m_ValidCESTCustomTagSingleOffset = "36\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\34\\31\\36\\22\\22\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\34\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22"; m_ValidCESTCustomTagListOffset = "36\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\42\\45\\47\\49\\4E\\20\\23\\23\\23\\0A\\75\\6C\\56\\65\\72\\73\\69\\6F\\6E\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\30\\78\\31\\34\\62\\34\\34\\62\\36\\0A\\74\\53\\65\\71\\75\\65\\6E\\63\\65\\46\\69\\6C\\65\\4E\\61\\6D\\65\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\22\\22\\25\\43\\75\\73\\74\\6F\\6D\\65\\72\\53\\65\\71\\25\\5C\\58\\58\\58\\58\\58\\5F\\43\\45\\53\\54\\5F\\52\\65\\76\\31\\34\\31\\36\\22\\22\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\38\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\6C\\46\\72\\65\\65\\5B\\31\\30\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\33\\32\\0A\\73\\57\\69\\50\\4D\\65\\6D\\42\\6C\\6F\\63\\6B\\2E\\61\\64\\46\\72\\65\\65\\5B\\31\\5D\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\20\\3D\\20\\32\\0A\\23\\23\\23\\20\\41\\53\\43\\43\\4F\\4E\\56\\20\\45\\4E\\44\\20\\23\\23\\23\\22"; } void tearDown() override { } void ValidPropertyParsedToPropertyList_Success() { mitk::CustomTagParser tagParser(m_PathToModule); auto tsproperty = mitk::TemporoSpatialStringProperty::New(); tsproperty->SetValue(0, 0, m_ValidCESTCustomTag); auto parsedPropertyList = tagParser.ParseDicomProperty(tsproperty); std::string sampling = ""; std::string offset = ""; std::string offsets = ""; std::string measurements = ""; std::string revision = ""; std::string jsonRevision = ""; bool hasRevision = parsedPropertyList->GetStringProperty("CEST.Revision", revision); parsedPropertyList->GetStringProperty("CEST.Offset", offset); parsedPropertyList->GetStringProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets); parsedPropertyList->GetStringProperty("CEST.measurements", measurements); parsedPropertyList->GetStringProperty("CEST.SamplingType", sampling); parsedPropertyList->GetStringProperty("CEST.revision_json", jsonRevision); bool offsetsMatch =( offsets == "-300 -2 -1.86667 -1.73333 -1.6 -1.46667 -1.33333 -1.2 -1.06667 -0.933333 -0.8 -0.666667 -0.533333 -0.4 -0.266667 -0.133333 0 0.133333 0.266667 0.4 0.533333 0.666667 0.8 0.933333 1.06667 1.2 1.33333 1.46667 1.6 1.73333 1.86667 2"); CPPUNIT_ASSERT_MESSAGE("Verify we found a revision.", hasRevision); CPPUNIT_ASSERT_MESSAGE("Verify the revision is the one we expect.", revision == "1416"); CPPUNIT_ASSERT_MESSAGE("Verify the revision and the json revision match.", revision == jsonRevision); CPPUNIT_ASSERT_MESSAGE("Verify a couple of resulting properties match our expectation.", offset == "2" && sampling == "1" && measurements == "32"); CPPUNIT_ASSERT_MESSAGE("Verify offsets are correctly parsed.", offsetsMatch); } void ValidPropertyMissingParametersParsedToEmptyPropertiesPropertyList_Success() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagAllParametersMissing); std::string revision = ""; std::string jsonRevision = ""; bool hasRevision = parsedPropertyList->GetStringProperty("CEST.Revision", revision); bool hasJsonRevision = parsedPropertyList->GetStringProperty("CEST.revision_json", jsonRevision); auto propertyMap = parsedPropertyList->GetMap(); bool propertiesEmpty = true; for (auto const &prop : *propertyMap) { std::string key = prop.first; if (key != "CEST.Revision" && key != "CEST.revision_json") { propertiesEmpty = propertiesEmpty && prop.second->GetValueAsString() == ""; } } CPPUNIT_ASSERT_MESSAGE("Verify we found a revision.", hasRevision); CPPUNIT_ASSERT_MESSAGE("Verify we found a json revision.", hasJsonRevision); CPPUNIT_ASSERT_MESSAGE("Property list properties are empty but for the revision information", propertiesEmpty); } void ValidPropertyRevisionVeryLow_UseDefault_Success() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagUnsupportedRevisionTooLow); std::string revision = ""; std::string jsonRevision = ""; bool hasRevision = parsedPropertyList->GetStringProperty("CEST.Revision", revision); bool hasJsonRevision = parsedPropertyList->GetStringProperty("CEST.revision_json", jsonRevision); bool usedDefault = (jsonRevision == "default mapping, corresponds to revision 1416"); CPPUNIT_ASSERT_MESSAGE("Verify we found a revision.", hasRevision); CPPUNIT_ASSERT_MESSAGE("Verify we found a json revision.", hasJsonRevision); CPPUNIT_ASSERT_MESSAGE("Verify we used default mapping.", usedDefault); } void ValidPropertyNoExactRevisionMatchUseInternal_Success() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagUnsupportedRevisionNoExactJSONUseInternal); std::string revision = ""; std::string jsonRevision = ""; bool hasRevision = parsedPropertyList->GetStringProperty("CEST.Revision", revision); bool hasJsonRevision = parsedPropertyList->GetStringProperty("CEST.revision_json", jsonRevision); bool usedInternal = (jsonRevision == "1416"); CPPUNIT_ASSERT_MESSAGE("Verify we found a revision.", hasRevision); CPPUNIT_ASSERT_MESSAGE("Verify we found a json revision.", hasJsonRevision); CPPUNIT_ASSERT_MESSAGE("Verify we used internal mapping.", usedInternal); } void ValidPropertyNoExactRevisionMatchUseExternal_Success() { std::string externalMappingString = "{\n" " \"external mapping for test\" : \"revision_json\",\n" " \"sWiPMemBlock.alFree[1]\" : \"AdvancedMode\"\n" "}"; // we assume the test library will be in the same location as the MitkCEST library on windows // on linux the test driver should have a relative path of ../bin/ #ifdef _WIN32 std::string dirname = m_PathToModule + "/CESTRevisionMapping"; #else std::string dirname = m_PathToModule + "/../lib/CESTRevisionMapping"; #endif //bool dirWasThere = itksys::SystemTools::FileIsDirectory(dirname); std::string filename = dirname + "/118.json"; itk::FileTools::CreateDirectory(dirname); std::ofstream externalFile(filename.c_str()); if (externalFile.is_open()) { externalFile << externalMappingString; externalFile.close(); } mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagUnsupportedRevisionNoExactJSONUseExternal); std::string revision = ""; std::string jsonRevision = ""; bool hasRevision = parsedPropertyList->GetStringProperty("CEST.Revision", revision); bool hasJsonRevision = parsedPropertyList->GetStringProperty("CEST.revision_json", jsonRevision); bool usedExternal = (jsonRevision == "external mapping for test"); CPPUNIT_ASSERT_MESSAGE("Verify we found a revision.", hasRevision); CPPUNIT_ASSERT_MESSAGE("Verify we found a json revision.", hasJsonRevision); CPPUNIT_ASSERT_MESSAGE("Verify we used external mapping.", usedExternal); bool wasError = std::remove(filename.c_str()); if (wasError) { MITK_ERROR << "Could not delete test revision file"; } } void ValidPropertyWindowsLineEndings_Success() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagWindowsLineEndings); std::string sampling = ""; std::string offset = ""; std::string offsets = ""; std::string measurements = ""; std::string revision = ""; std::string jsonRevision = ""; bool hasRevision = parsedPropertyList->GetStringProperty("CEST.Revision", revision); parsedPropertyList->GetStringProperty("CEST.Offset", offset); parsedPropertyList->GetStringProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets); parsedPropertyList->GetStringProperty("CEST.measurements", measurements); parsedPropertyList->GetStringProperty("CEST.SamplingType", sampling); parsedPropertyList->GetStringProperty("CEST.revision_json", jsonRevision); bool offsetsMatch = (offsets == "-300 -2 -1.86667 -1.73333 -1.6 -1.46667 -1.33333 -1.2 -1.06667 -0.933333 -0.8 -0.666667 -0.533333 -0.4 -0.266667 -0.133333 0 0.133333 0.266667 0.4 0.533333 0.666667 0.8 0.933333 1.06667 1.2 1.33333 1.46667 1.6 1.73333 1.86667 2"); CPPUNIT_ASSERT_MESSAGE("Verify we found a revision.", hasRevision); CPPUNIT_ASSERT_MESSAGE("Verify the revision is the one we expect.", revision == "1416"); CPPUNIT_ASSERT_MESSAGE("Verify the revision and the json revision match.", revision == jsonRevision); CPPUNIT_ASSERT_MESSAGE("Verify a couple of resulting properties match our expectation.", offset == "2" && sampling == "1" && measurements == "32"); CPPUNIT_ASSERT_MESSAGE("Verify offsets are correctly parsed.", offsetsMatch); } void InvalidPropertyInvalidRevision_Failure() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_InvalidCESTCustomTagRevisionNoNumber); std::string revision = ""; std::string jsonRevision = ""; bool hasRevision = parsedPropertyList->GetStringProperty("CEST.Revision", revision); bool hasJsonRevision = parsedPropertyList->GetStringProperty("CEST.revision_json", jsonRevision); bool usedDefault = (jsonRevision == "default mapping, corresponds to revision 1416"); CPPUNIT_ASSERT_MESSAGE("Verify we found a revision.", hasRevision); CPPUNIT_ASSERT_MESSAGE("Verify we found a json revision.", hasJsonRevision); CPPUNIT_ASSERT_MESSAGE("Verify we used default mapping.", usedDefault); } void InvalidPropertyNoCESTSequence_Failure() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_NonCESTCustomTag); auto size = parsedPropertyList->GetMap()->size(); CPPUNIT_ASSERT_MESSAGE("Property list is empty", mitk::Equal(size, 0)); } void InvalidPropertyGarbageInDelimiters_Failure() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_GarbageWithinDelimiters); auto size = parsedPropertyList->GetMap()->size(); CPPUNIT_ASSERT_MESSAGE("Property list is empty", mitk::Equal(size, 0)); } void ValidPropertyAlternatingOffset_Success() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagAlternatingOffset); std::string offsets = ""; parsedPropertyList->GetStringProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets); bool offsetsMatch = (offsets == "-300 2 -2 1.86667 -1.86667 1.73333 -1.73333 1.6 -1.6 1.46667 -1.46667 1.33333 -1.33333 1.2 -1.2 1.06667 -1.06667 0.933333 -0.933333 0.8 -0.8 0.666667 -0.666667 0.533333 -0.533333 0.4 -0.4 0.266667 -0.266667 0.133333 -0.133333 0"); CPPUNIT_ASSERT_MESSAGE("Verify offsets are correctly parsed.", offsetsMatch); } void ValidPropertySimpleOffset_Success() { mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagSingleOffset); std::string offsets = ""; parsedPropertyList->GetStringProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets); bool offsetsMatch = (offsets == "-300 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2"); CPPUNIT_ASSERT_MESSAGE("Verify offsets are correctly parsed.", offsetsMatch); } void ValidPropertyListOffset_Success() { std::string offsetList = "-300\n -100 \n -50 \n -35\n -25 \n -17\n -12\n -9.5 \n -8.25\n -7\n -6.1 \n -5.4 \n -4.7 \n -4\n -3.3\n -2.7\n -2\n -1.7\n -1.5 \n -1.1 \n -0.9\n -300\n -0.6 \n -0.4\n -0.2\n 0 \n 0.2\n 0.4\n 0.6\n 0.95 \n 1.1 \n 1.25 \n 1.4\n 1.55\n 1.7\n 1.85 \n 2 \n 2.15 \n 2.3\n 2.45 \n 2.6\n 2.75 \n 2.9 \n 3.05\n -300 \n 3.2\n 3.35 \n 3.5\n 3.65 \n 3.8 \n 3.95\n 4.1 \n 4.25\n 4.4 \n 4.7\n 5.2\n 6\n 7\n 9\n 12 \n 17\n 25\n 35\n 50 \n 100\n -300" ; std::string filename = m_PathToModule + "/" + "LIST.txt"; std::ofstream externalFile(filename.c_str()); if (externalFile.is_open()) { externalFile << offsetList; externalFile.close(); } mitk::CustomTagParser tagParser(m_PathToModule); auto parsedPropertyList = tagParser.ParseDicomPropertyString(m_ValidCESTCustomTagListOffset); std::string offsets = ""; parsedPropertyList->GetStringProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets); std::string referenceString = "-300 -100 -50 -35 -25 -17 -12 -9.5 -8.25 -7 -6.1 -5.4 -4.7 -4 -3.3 -2.7 -2 -1.7 -1.5 -1.1 -0.9 -300 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.95 1.1 1.25 1.4 1.55 1.7 1.85 2 2.15 2.3 2.45 2.6 2.75 2.9 3.05 -300 3.2 3.35 3.5 3.65 3.8 3.95 4.1 4.25 4.4 4.7 5.2 6 7 9 12 17 25 35 50 100 -300"; bool offsetsMatch = (offsets == referenceString); CPPUNIT_ASSERT_MESSAGE("Verify offsets are correctly parsed.", offsetsMatch); bool wasError = std::remove(filename.c_str()); if (wasError) { MITK_ERROR << "Could not delete test offset list file"; } } void ExtractRevision() { std::string empty = ""; std::string invalidRule1a = "CESaaaaaaa"; std::string invalidRule1b = "aaaCESTaaa"; std::string invalidRule2a = "CESTaaaa"; std::string invalidRule2b = "aaa_CESTaaa"; std::string invalidRule3a = "CESTaaa_REVaaaa"; std::string valid1 = "CEST_REV12345"; std::string valid2 = "aaa_CESTaaaa_REV2"; std::string valid3 = "CESTaaaa_REV3_c"; std::string valid4 = "cest_rev4"; std::string valid5 = "aaa_cestaaaa_rev5"; std::string valid6 = "cestaaaa_rev6_c"; CPPUNIT_ASSERT_THROW_MESSAGE("Verify exception on empty", mitk::CustomTagParser::ExtractRevision(empty), mitk::Exception); CPPUNIT_ASSERT_THROW_MESSAGE("Verify exception on invalidRule1a", mitk::CustomTagParser::ExtractRevision(invalidRule1a), mitk::Exception); CPPUNIT_ASSERT_THROW_MESSAGE("Verify exception on invalidRule1b", mitk::CustomTagParser::ExtractRevision(invalidRule1b), mitk::Exception); CPPUNIT_ASSERT_THROW_MESSAGE("Verify exception on invalidRule2a", mitk::CustomTagParser::ExtractRevision(invalidRule2a), mitk::Exception); CPPUNIT_ASSERT_THROW_MESSAGE("Verify exception on invalidRule2b", mitk::CustomTagParser::ExtractRevision(invalidRule2b), mitk::Exception); CPPUNIT_ASSERT_MESSAGE("Verify empty revision on invalidRule3a", mitk::CustomTagParser::ExtractRevision(invalidRule3a) == ""); CPPUNIT_ASSERT_MESSAGE("Extract revision from valid1.", mitk::CustomTagParser::ExtractRevision(valid1) == "12345"); CPPUNIT_ASSERT_MESSAGE("Extract revision from valid2.", mitk::CustomTagParser::ExtractRevision(valid2) == "2"); CPPUNIT_ASSERT_MESSAGE("Extract revision from valid3.", mitk::CustomTagParser::ExtractRevision(valid3) == "3"); CPPUNIT_ASSERT_MESSAGE("Extract revision from valid4.", mitk::CustomTagParser::ExtractRevision(valid4) == "4"); CPPUNIT_ASSERT_MESSAGE("Extract revision from valid5.", mitk::CustomTagParser::ExtractRevision(valid5) == "5"); CPPUNIT_ASSERT_MESSAGE("Extract revision from valid6.", mitk::CustomTagParser::ExtractRevision(valid6) == "6"); } }; MITK_TEST_SUITE_REGISTRATION(mitkCustomTagParser) diff --git a/Modules/Core/include/mitkIOMimeTypes.h b/Modules/Core/include/mitkIOMimeTypes.h index 8df3c8f990..c51f1eb3d0 100644 --- a/Modules/Core/include/mitkIOMimeTypes.h +++ b/Modules/Core/include/mitkIOMimeTypes.h @@ -1,92 +1,103 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIOMIMETYPES_H #define MITKIOMIMETYPES_H #include "mitkCustomMimeType.h" #include namespace mitk { /** * @ingroup IO * @brief The IOMimeTypes class */ class MITKCORE_EXPORT IOMimeTypes { public: - class MITKCORE_EXPORT DicomMimeType : public CustomMimeType + + /** Base mime types for all kind of DICOM images, that can be reused + by more specific mime types based on DICOM images.*/ + class MITKCORE_EXPORT BaseDicomMimeType : public CustomMimeType + { + public: + BaseDicomMimeType(const std::string &name); + BaseDicomMimeType(const BaseDicomMimeType& other) = default; + bool AppliesTo(const std::string& path) const override; + BaseDicomMimeType* Clone() const override; + }; + + class MITKCORE_EXPORT DicomMimeType : public BaseDicomMimeType { public: DicomMimeType(); - bool AppliesTo(const std::string &path) const override; DicomMimeType *Clone() const override; }; static std::vector Get(); static std::string DEFAULT_BASE_NAME(); // application/vnd.mitk static std::string CATEGORY_IMAGES(); // Images static std::string CATEGORY_SURFACES(); // Surfaces // ------------------------------ VTK formats ---------------------------------- static CustomMimeType VTK_IMAGE_MIMETYPE(); // (mitk::Image) vti static CustomMimeType VTK_IMAGE_LEGACY_MIMETYPE(); // (mitk::Image) vtk static CustomMimeType VTK_PARALLEL_IMAGE_MIMETYPE(); // (mitk::Image) pvti static CustomMimeType VTK_POLYDATA_MIMETYPE(); // (mitk::Surface) vtp, vtk static CustomMimeType VTK_POLYDATA_LEGACY_MIMETYPE(); // (mitk::Surface) vtk static CustomMimeType VTK_PARALLEL_POLYDATA_MIMETYPE(); // (mitk::Surface) pvtp static CustomMimeType STEREOLITHOGRAPHY_MIMETYPE(); // (mitk::Surface) stl static CustomMimeType WAVEFRONT_OBJ_MIMETYPE(); // (mitk::Surface) obj static CustomMimeType STANFORD_PLY_MIMETYPE(); // (mitk::Surface) ply static std::string STEREOLITHOGRAPHY_NAME(); // DEFAULT_BASE_NAME.stl static std::string VTK_IMAGE_NAME(); // DEFAULT_BASE_NAME.vtk.image static std::string VTK_IMAGE_LEGACY_NAME(); // DEFAULT_BASE_NAME.vtk.image.legacy static std::string VTK_PARALLEL_IMAGE_NAME(); // DEFAULT_BASE_NAME.vtk.parallel.image static std::string VTK_POLYDATA_NAME(); // DEFAULT_BASE_NAME.vtk.polydata static std::string VTK_POLYDATA_LEGACY_NAME(); // DEFAULT_BASE_NAME.vtk.polydata.legacy static std::string VTK_PARALLEL_POLYDATA_NAME(); // DEFAULT_BASE_NAME.vtk.parallel.polydata static std::string WAVEFRONT_OBJ_NAME(); // DEFAULT_BASE_NAME.obj static std::string STANFORD_PLY_NAME(); // DEFAULT_BASE_NAME.ply // ------------------------- Image formats (ITK based) -------------------------- static CustomMimeType NRRD_MIMETYPE(); // nrrd, nhdr static CustomMimeType NIFTI_MIMETYPE(); static CustomMimeType RAW_MIMETYPE(); // raw static DicomMimeType DICOM_MIMETYPE(); static std::string NRRD_MIMETYPE_NAME(); // DEFAULT_BASE_NAME.nrrd static std::string NIFTI_MIMETYPE_NAME(); static std::string RAW_MIMETYPE_NAME(); // DEFAULT_BASE_NAME.raw static std::string DICOM_MIMETYPE_NAME(); // ------------------------------ MITK formats ---------------------------------- static CustomMimeType POINTSET_MIMETYPE(); // mps static CustomMimeType GEOMETRY_DATA_MIMETYPE(); // .mitkgeometry static std::string POINTSET_MIMETYPE_NAME(); // DEFAULT_BASE_NAME.pointset private: // purposely not implemented IOMimeTypes(); IOMimeTypes(const IOMimeTypes &); }; } #endif // MITKIOMIMETYPES_H diff --git a/Modules/Core/src/IO/mitkIOMimeTypes.cpp b/Modules/Core/src/IO/mitkIOMimeTypes.cpp index 15983508f6..e9cf938b29 100644 --- a/Modules/Core/src/IO/mitkIOMimeTypes.cpp +++ b/Modules/Core/src/IO/mitkIOMimeTypes.cpp @@ -1,352 +1,359 @@ /*============================================================================ 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 "mitkIOMimeTypes.h" #include "mitkCustomMimeType.h" #include "mitkLogMacros.h" #include "itkGDCMImageIO.h" #include "itkMetaDataObject.h" #include #include namespace mitk { - IOMimeTypes::DicomMimeType::DicomMimeType() : CustomMimeType(DICOM_MIMETYPE_NAME()) + IOMimeTypes::BaseDicomMimeType::BaseDicomMimeType(const std::string& name) : CustomMimeType(name) { this->AddExtension("gdcm"); this->AddExtension("dcm"); this->AddExtension("DCM"); this->AddExtension("dc3"); this->AddExtension("DC3"); this->AddExtension("ima"); this->AddExtension("img"); this->SetCategory(CATEGORY_IMAGES()); this->SetComment("DICOM"); } - bool IOMimeTypes::DicomMimeType::AppliesTo(const std::string &path) const + bool IOMimeTypes::BaseDicomMimeType::AppliesTo(const std::string &path) const { // check whether directory or file // if directory try to find first file within it instead bool pathIsDirectory = itksys::SystemTools::FileIsDirectory(path); std::string filepath = path; if (pathIsDirectory) { itksys::Directory input; input.Load(path.c_str()); std::vector files; for (unsigned long idx = 0; idxSetFileName(filepath); try { gdcmIO->ReadImageInformation(); } catch (const itk::ExceptionObject & /*err*/) { return false; } //DICOMRT modalities have specific reader, don't read with normal DICOM readers std::string modality; itk::MetaDataDictionary& dict = gdcmIO->GetMetaDataDictionary(); itk::ExposeMetaData(dict, "0008|0060", modality); - MITK_INFO << "DICOM Modality is " << modality; + MITK_DEBUG << "DICOM Modality detected by MimeType "<< this->GetName() << " is " << modality; if (modality == "RTSTRUCT" || modality == "RTDOSE" || modality == "RTPLAN") { return false; } else { return gdcmIO->CanReadFile(filepath.c_str()); } } - IOMimeTypes::DicomMimeType *IOMimeTypes::DicomMimeType::Clone() const { return new DicomMimeType(*this); } + IOMimeTypes::BaseDicomMimeType*IOMimeTypes::BaseDicomMimeType::Clone() const { return new BaseDicomMimeType(*this); } + + IOMimeTypes::DicomMimeType::DicomMimeType() : BaseDicomMimeType(DICOM_MIMETYPE_NAME()) + { + } + + IOMimeTypes::DicomMimeType* IOMimeTypes::DicomMimeType::Clone() const { return new DicomMimeType(*this); } + std::vector IOMimeTypes::Get() { std::vector mimeTypes; // order matters here (descending rank for mime types) mimeTypes.push_back(NRRD_MIMETYPE().Clone()); mimeTypes.push_back(NIFTI_MIMETYPE().Clone()); mimeTypes.push_back(VTK_IMAGE_MIMETYPE().Clone()); mimeTypes.push_back(VTK_PARALLEL_IMAGE_MIMETYPE().Clone()); mimeTypes.push_back(VTK_IMAGE_LEGACY_MIMETYPE().Clone()); mimeTypes.push_back(DICOM_MIMETYPE().Clone()); mimeTypes.push_back(VTK_POLYDATA_MIMETYPE().Clone()); mimeTypes.push_back(VTK_PARALLEL_POLYDATA_MIMETYPE().Clone()); mimeTypes.push_back(VTK_POLYDATA_LEGACY_MIMETYPE().Clone()); mimeTypes.push_back(STEREOLITHOGRAPHY_MIMETYPE().Clone()); mimeTypes.push_back(WAVEFRONT_OBJ_MIMETYPE().Clone()); mimeTypes.push_back(STANFORD_PLY_MIMETYPE().Clone()); mimeTypes.push_back(RAW_MIMETYPE().Clone()); mimeTypes.push_back(POINTSET_MIMETYPE().Clone()); return mimeTypes; } CustomMimeType IOMimeTypes::VTK_IMAGE_MIMETYPE() { CustomMimeType mimeType(VTK_IMAGE_NAME()); mimeType.AddExtension("vti"); mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_IMAGE_LEGACY_MIMETYPE() { CustomMimeType mimeType(VTK_IMAGE_LEGACY_NAME()); mimeType.AddExtension("vtk"); mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Legacy Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_PARALLEL_IMAGE_MIMETYPE() { CustomMimeType mimeType(VTK_PARALLEL_IMAGE_NAME()); mimeType.AddExtension("pvti"); mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Parallel Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_POLYDATA_MIMETYPE() { CustomMimeType mimeType(VTK_POLYDATA_NAME()); mimeType.AddExtension("vtp"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK PolyData"); return mimeType; } CustomMimeType IOMimeTypes::VTK_POLYDATA_LEGACY_MIMETYPE() { CustomMimeType mimeType(VTK_POLYDATA_LEGACY_NAME()); mimeType.AddExtension("vtk"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK Legacy PolyData"); return mimeType; } CustomMimeType IOMimeTypes::VTK_PARALLEL_POLYDATA_MIMETYPE() { CustomMimeType mimeType(VTK_PARALLEL_POLYDATA_NAME()); mimeType.AddExtension("pvtp"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK Parallel PolyData"); return mimeType; } CustomMimeType IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE() { CustomMimeType mimeType(STEREOLITHOGRAPHY_NAME()); mimeType.AddExtension("stl"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("Stereolithography"); return mimeType; } CustomMimeType IOMimeTypes::WAVEFRONT_OBJ_MIMETYPE() { CustomMimeType mimeType(WAVEFRONT_OBJ_NAME()); mimeType.AddExtension("obj"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("Wavefront OBJ"); return mimeType; } CustomMimeType IOMimeTypes::STANFORD_PLY_MIMETYPE() { CustomMimeType mimeType(STANFORD_PLY_NAME()); mimeType.AddExtension("ply"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("Stanford PLY"); return mimeType; } std::string IOMimeTypes::STEREOLITHOGRAPHY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".stl"; return name; } std::string IOMimeTypes::WAVEFRONT_OBJ_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".obj"; return name; } std::string IOMimeTypes::STANFORD_PLY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".ply"; return name; } std::string IOMimeTypes::DEFAULT_BASE_NAME() { static std::string name = "application/vnd.mitk"; return name; } std::string IOMimeTypes::CATEGORY_IMAGES() { static std::string cat = "Images"; return cat; } std::string IOMimeTypes::CATEGORY_SURFACES() { static std::string cat = "Surfaces"; return cat; } std::string IOMimeTypes::VTK_IMAGE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.image"; return name; } std::string IOMimeTypes::VTK_IMAGE_LEGACY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.image.legacy"; return name; } std::string IOMimeTypes::VTK_PARALLEL_IMAGE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.image"; return name; } std::string IOMimeTypes::VTK_POLYDATA_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata"; return name; } std::string IOMimeTypes::VTK_POLYDATA_LEGACY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata.legacy"; return name; } std::string IOMimeTypes::VTK_PARALLEL_POLYDATA_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.polydata"; return name; } CustomMimeType IOMimeTypes::NRRD_MIMETYPE() { CustomMimeType mimeType(NRRD_MIMETYPE_NAME()); mimeType.AddExtension("nrrd"); mimeType.AddExtension("nhdr"); mimeType.SetCategory("Images"); mimeType.SetComment("NRRD"); return mimeType; } CustomMimeType IOMimeTypes::NIFTI_MIMETYPE() { CustomMimeType mimeType(NIFTI_MIMETYPE_NAME()); mimeType.AddExtension("nii"); mimeType.AddExtension("nii.gz"); mimeType.AddExtension("hdr"); mimeType.AddExtension("hdr.gz"); mimeType.AddExtension("img"); mimeType.AddExtension("img.gz"); mimeType.AddExtension("nia"); mimeType.SetCategory("Images"); mimeType.SetComment("Nifti"); return mimeType; } CustomMimeType IOMimeTypes::RAW_MIMETYPE() { CustomMimeType mimeType(RAW_MIMETYPE_NAME()); mimeType.AddExtension("raw"); mimeType.SetCategory("Images"); mimeType.SetComment("Raw data"); return mimeType; } IOMimeTypes::DicomMimeType IOMimeTypes::DICOM_MIMETYPE() { return DicomMimeType(); } std::string IOMimeTypes::NRRD_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.nrrd"; return name; } std::string IOMimeTypes::NIFTI_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.nifti"; return name; } std::string IOMimeTypes::RAW_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.raw"; return name; } std::string IOMimeTypes::DICOM_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.dicom"; return name; } CustomMimeType IOMimeTypes::POINTSET_MIMETYPE() { CustomMimeType mimeType(POINTSET_MIMETYPE_NAME()); mimeType.AddExtension("mps"); mimeType.SetCategory("Point Sets"); mimeType.SetComment("MITK Point Set"); return mimeType; } std::string IOMimeTypes::POINTSET_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".pointset"; return name; } CustomMimeType IOMimeTypes::GEOMETRY_DATA_MIMETYPE() { mitk::CustomMimeType mimeType(DEFAULT_BASE_NAME() + ".geometrydata"); mimeType.AddExtension("mitkgeometry"); mimeType.SetCategory("Geometries"); mimeType.SetComment("GeometryData object"); return mimeType; } } diff --git a/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h b/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h index aa56718ad8..e8527a8a93 100644 --- a/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h +++ b/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h @@ -1,70 +1,70 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKBASEDICOMREADERSERVICE_H #define MITKBASEDICOMREADERSERVICE_H #include #include #include "MitkDICOMReaderExports.h" namespace mitk { /** Base class for service wrappers that make DICOMFileReader from the DICOMReader module usable. */ class MITKDICOMREADER_EXPORT BaseDICOMReaderService : public AbstractFileReader { public: - BaseDICOMReaderService(const std::string& description); - BaseDICOMReaderService(const mitk::CustomMimeType& customType, const std::string& description); - using AbstractFileReader::Read; IFileReader::ConfidenceLevel GetConfidenceLevel() const override; protected: + BaseDICOMReaderService(const std::string& description); + BaseDICOMReaderService(const mitk::CustomMimeType& customType, const std::string& description); + /** Uses this->GetRelevantFile() and this->GetReader to load the image. * data and puts it into base data instances-*/ std::vector> DoRead() override; /** Returns the list of all DCM files that are in the same directory * like this->GetLocalFileName().*/ mitk::StringList GetDICOMFilesInSameDirectory() const; /** Returns the reader instance that should be used. The descission may be based * one the passed relevant file list.*/ virtual mitk::DICOMFileReader::Pointer GetReader(const mitk::StringList& relevantFiles) const = 0; void SetOnlyRegardOwnSeries(bool); bool GetOnlyRegardOwnSeries() const; private: /** Flags that constrols if the read() operation should only regard DICOM files of the same series if the specified GetLocalFileName() is a file. If it is a director, this flag has no impact (it is assumed false then). */ bool m_OnlyRegardOwnSeries = true; }; class IPropertyProvider; /** Helper function that generates a name string (e.g. for DataNode names) from the DICOM properties of the passed provider instance. If the instance is nullptr, or has no dicom properties DataNode::NO_NAME_VALUE() will be returned.*/ std::string MITKDICOMREADER_EXPORT GenerateNameFromDICOMProperties(const mitk::IPropertyProvider* provider); } #endif // MITKBASEDICOMREADERSERVICE_H diff --git a/Modules/DICOMReader/include/mitkDICOMProperty.h b/Modules/DICOMReader/include/mitkDICOMProperty.h index 895c9e8cda..4f24614af2 100644 --- a/Modules/DICOMReader/include/mitkDICOMProperty.h +++ b/Modules/DICOMReader/include/mitkDICOMProperty.h @@ -1,65 +1,82 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkDICOMProperty_h #define mitkDICOMProperty_h #include "mitkDICOMImageBlockDescriptor.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkDICOMTagPath.h" #include "MitkDICOMReaderExports.h" namespace mitk { typedef TemporoSpatialStringProperty DICOMProperty; /** Generation functor for DICOMFileReader classes to convert the collected tag values into DICOMProperty instances. */ MITKDICOMREADER_EXPORT mitk::BaseProperty::Pointer GetDICOMPropertyForDICOMValuesFunctor(const DICOMCachedValueLookupTable& cacheLookupTable); class PropertyList; class BaseData; /** Helper function that searches for all properties in a given property list that matches the passed path. * The result will be the matching properties in a map*/ MITKDICOMREADER_EXPORT std::map< std::string, BaseProperty::Pointer> GetPropertyByDICOMTagPath(const PropertyList* list, const DICOMTagPath& path); /** Helper function that searches for all properties in a given base data that matches the passed path. * The result will be the matching properties in a map*/ MITKDICOMREADER_EXPORT std::map< std::string, BaseProperty::Pointer> GetPropertyByDICOMTagPath(const BaseData* data, const DICOMTagPath& path); /**Helper function that can be used to convert the content of a DICOM property into the given return type. The function makes the following assumptions: 1. dcmValueString does only encode one number. 2. The value is encoded compliant to locale "C". @pre dcmValueString must be convertibel into the return type. If this is not the case an exception will be thrown. */ template TNumericReturnType ConvertDICOMStrToValue(const std::string& dcmValueString) { std::istringstream iss(dcmValueString); iss.imbue(std::locale("C")); TNumericReturnType d; if (!(iss >> d) || !(iss.eof())) { mitkThrow() << "Cannot convert string to value type. Type: " << typeid(TNumericReturnType).name() << "; String: " << dcmValueString; } return d; }; + /**Helper function that can be used to convert a numeric value into content of a DICOM property. + @pre value must be convertibel into a string. + If this is not the case an exception will be thrown. + */ + template + std::string ConvertValueToDICOMStr(const TNumericType value) + { + std::ostringstream oss; + oss.imbue(std::locale("C")); + if (!(oss << value)) + { + mitkThrow() << "Cannot convert value type to dicom string. Type: " << typeid(TNumericType).name() << "; value: " << value; + } + + return oss.str(); + }; + } #endif diff --git a/Modules/DICOMReader/test/mitkDICOMPropertyTest.cpp b/Modules/DICOMReader/test/mitkDICOMPropertyTest.cpp index cf891996ff..96218f9203 100644 --- a/Modules/DICOMReader/test/mitkDICOMPropertyTest.cpp +++ b/Modules/DICOMReader/test/mitkDICOMPropertyTest.cpp @@ -1,159 +1,166 @@ /*============================================================================ 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 "mitkDICOMProperty.h" #include "mitkImage.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" class mitkDICOMPropertyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkDICOMPropertyTestSuite); MITK_TEST(GetPropertyByDICOMTagPath); MITK_TEST(GetPropertyByDICOMTagPath_2); MITK_TEST(ConvertDICOMStrToValue); + MITK_TEST(ConvertValueToDICOMStr); CPPUNIT_TEST_SUITE_END(); private: mitk::DICOMTagPath simplePath; mitk::DICOMTagPath deepPath; mitk::DICOMTagPath deepPath2; mitk::DICOMTagPath deepPath_withAnyElement; mitk::DICOMTagPath deepPath_withAnySelection; mitk::DICOMTagPath deepPath_withSelection; mitk::DICOMTagPath deepPath_withSelection2; mitk::DICOMTagPath emptyPath; mitk::Image::Pointer data; std::string simplePathStr; std::string deepPathStr; std::string deepPath2Str; std::string deepPath_withSelectionStr; public: void setUp() override { simplePath.AddElement(0x0010, 0x0010); deepPath.AddElement(0x0010, 0x0011).AddElement(0x0020, 0x0022).AddElement(0x0030, 0x0033); deepPath2.AddElement(0x0010, 0x0011).AddElement(0x0020, 0x0023).AddElement(0x0030, 0x0033); deepPath_withAnyElement.AddElement(0x0010, 0x0011).AddAnyElement().AddElement(0x0030, 0x0033); deepPath_withAnySelection.AddElement(0x0010, 0x0011).AddAnySelection(0x0020, 0x0024).AddElement(0x0030, 0x0033); deepPath_withSelection.AddElement(0x0010, 0x0011).AddSelection(0x0020, 0x0024, 1).AddElement(0x0030, 0x0033); deepPath_withSelection2.AddElement(0x0010, 0x0011).AddSelection(0x0020, 0x0024, 2).AddElement(0x0030, 0x0033); simplePathStr = mitk::DICOMTagPathToPropertyName(simplePath); deepPathStr = mitk::DICOMTagPathToPropertyName(deepPath); deepPath2Str = mitk::DICOMTagPathToPropertyName(deepPath2); deepPath_withSelectionStr = mitk::DICOMTagPathToPropertyName(deepPath_withSelection); data = mitk::Image::New(); data->GetPropertyList()->SetStringProperty(simplePathStr.c_str(), "simplePath"); data->GetPropertyList()->SetStringProperty(deepPathStr.c_str(), "deepPath"); data->GetPropertyList()->SetStringProperty(deepPath2Str.c_str(), "deepPath2"); data->GetPropertyList()->SetStringProperty(deepPath_withSelectionStr.c_str(), "deepPath_withSelection"); data->GetPropertyList()->SetStringProperty("DICOM.0003.0003", "otherPath"); data->GetPropertyList()->SetStringProperty("not_a_dicom_prop", "not_a_dicom_prop"); } void tearDown() override { } void GetPropertyByDICOMTagPath() { std::map< std::string, mitk::BaseProperty::Pointer> result = mitk::GetPropertyByDICOMTagPath(data, simplePath); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result.begin()->second->GetValueAsString(), std::string("simplePath")); result = mitk::GetPropertyByDICOMTagPath(data, deepPath); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result.begin()->second->GetValueAsString(), std::string("deepPath")); result = mitk::GetPropertyByDICOMTagPath(data, deepPath2); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result.begin()->second->GetValueAsString(), std::string("deepPath2")); result = mitk::GetPropertyByDICOMTagPath(data, deepPath_withAnyElement); CPPUNIT_ASSERT(result.size() == 3); CPPUNIT_ASSERT_EQUAL(result[deepPathStr]->GetValueAsString(), std::string("deepPath")); CPPUNIT_ASSERT_EQUAL(result[deepPath2Str]->GetValueAsString(), std::string("deepPath2")); CPPUNIT_ASSERT_EQUAL(result[deepPath_withSelectionStr]->GetValueAsString(), std::string("deepPath_withSelection")); result = mitk::GetPropertyByDICOMTagPath(data, deepPath_withSelection); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result[deepPath_withSelectionStr]->GetValueAsString(), std::string("deepPath_withSelection")); result = mitk::GetPropertyByDICOMTagPath(data, deepPath_withSelection2); CPPUNIT_ASSERT(result.size() == 0); result = mitk::GetPropertyByDICOMTagPath(data, emptyPath); CPPUNIT_ASSERT(result.size() == 0); } void GetPropertyByDICOMTagPath_2() { std::map< std::string, mitk::BaseProperty::Pointer> result = mitk::GetPropertyByDICOMTagPath(data->GetPropertyList(), simplePath); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result.begin()->second->GetValueAsString(), std::string("simplePath")); result = mitk::GetPropertyByDICOMTagPath(data->GetPropertyList(), deepPath); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result.begin()->second->GetValueAsString(), std::string("deepPath")); result = mitk::GetPropertyByDICOMTagPath(data->GetPropertyList(), deepPath2); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result.begin()->second->GetValueAsString(), std::string("deepPath2")); result = mitk::GetPropertyByDICOMTagPath(data->GetPropertyList(), deepPath_withAnyElement); CPPUNIT_ASSERT(result.size() == 3); CPPUNIT_ASSERT_EQUAL(result[deepPathStr]->GetValueAsString(), std::string("deepPath")); CPPUNIT_ASSERT_EQUAL(result[deepPath2Str]->GetValueAsString(), std::string("deepPath2")); CPPUNIT_ASSERT_EQUAL(result[deepPath_withSelectionStr]->GetValueAsString(), std::string("deepPath_withSelection")); result = mitk::GetPropertyByDICOMTagPath(data->GetPropertyList(), deepPath_withSelection); CPPUNIT_ASSERT(result.size() == 1); CPPUNIT_ASSERT_EQUAL(result[deepPath_withSelectionStr]->GetValueAsString(), std::string("deepPath_withSelection")); result = mitk::GetPropertyByDICOMTagPath(data->GetPropertyList(), deepPath_withSelection2); CPPUNIT_ASSERT(result.size() == 0); result = mitk::GetPropertyByDICOMTagPath(data->GetPropertyList(), emptyPath); CPPUNIT_ASSERT(result.size() == 0); } void ConvertDICOMStrToValue() { CPPUNIT_ASSERT_EQUAL(mitk::ConvertDICOMStrToValue("1.35"), 1.35); CPPUNIT_ASSERT_EQUAL(mitk::ConvertDICOMStrToValue("1"), 1.); CPPUNIT_ASSERT_THROW(mitk::ConvertDICOMStrToValue("1.35"), mitk::Exception); CPPUNIT_ASSERT_EQUAL(mitk::ConvertDICOMStrToValue("1"), 1); CPPUNIT_ASSERT_THROW(mitk::ConvertDICOMStrToValue("1,35"), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::ConvertDICOMStrToValue("nonumber"), mitk::Exception); } + void ConvertValueToDICOMStr() + { + CPPUNIT_ASSERT_EQUAL(mitk::ConvertValueToDICOMStr(1.35), std::string("1.35")); + CPPUNIT_ASSERT_EQUAL(mitk::ConvertValueToDICOMStr(1), std::string("1")); + } + }; MITK_TEST_SUITE_REGISTRATION(mitkDICOMProperty) diff --git a/Modules/QtWidgets/include/QmitkFileReaderOptionsDialog.h b/Modules/QtWidgets/include/QmitkFileReaderOptionsDialog.h index fda8fc7f47..189648e7c0 100644 --- a/Modules/QtWidgets/include/QmitkFileReaderOptionsDialog.h +++ b/Modules/QtWidgets/include/QmitkFileReaderOptionsDialog.h @@ -1,45 +1,48 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKFILEREADEROPTIONSDIALOG_H #define QMITKFILEREADEROPTIONSDIALOG_H #include "mitkIOUtil.h" #include namespace Ui { class QmitkFileReaderOptionsDialog; } class QmitkFileReaderWriterOptionsWidget; class QmitkFileReaderOptionsDialog : public QDialog { Q_OBJECT public: explicit QmitkFileReaderOptionsDialog(mitk::IOUtil::LoadInfo &loadInfo, QWidget *parent = nullptr); ~QmitkFileReaderOptionsDialog() override; bool ReuseOptions() const; void accept() override; +protected slots: + void SetCurrentReader(int index); + private: Ui::QmitkFileReaderOptionsDialog *ui; mitk::IOUtil::LoadInfo &m_LoadInfo; std::vector m_ReaderItems; }; #endif // QMITKFILEREADEROPTIONSDIALOG_H diff --git a/Modules/QtWidgets/src/QmitkFileReaderOptionsDialog.cpp b/Modules/QtWidgets/src/QmitkFileReaderOptionsDialog.cpp index 0f1760a299..a8ef227f47 100644 --- a/Modules/QtWidgets/src/QmitkFileReaderOptionsDialog.cpp +++ b/Modules/QtWidgets/src/QmitkFileReaderOptionsDialog.cpp @@ -1,86 +1,89 @@ /*============================================================================ 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 "QmitkFileReaderOptionsDialog.h" #include "ui_QmitkFileReaderOptionsDialog.h" #include "QmitkFileReaderWriterOptionsWidget.h" #include "mitkIFileReader.h" QmitkFileReaderOptionsDialog::QmitkFileReaderOptionsDialog(mitk::IOUtil::LoadInfo &loadInfo, QWidget *parent) : QDialog(parent, Qt::WindowStaysOnTopHint), ui(new Ui::QmitkFileReaderOptionsDialog), m_LoadInfo(loadInfo) { ui->setupUi(this); m_ReaderItems = loadInfo.m_ReaderSelector.Get(); - bool hasOptions = false; int selectedIndex = 0; long selectedReaderId = loadInfo.m_ReaderSelector.GetSelectedId(); int i = 0; for (std::vector::const_reverse_iterator iter = m_ReaderItems.rbegin(), iterEnd = m_ReaderItems.rend(); iter != iterEnd; ++iter) { ui->m_ReaderComboBox->addItem(QString::fromStdString(iter->GetDescription())); mitk::IFileReader::Options options = iter->GetReader()->GetOptions(); if (!options.empty()) { - hasOptions = true; } ui->m_StackedOptionsWidget->addWidget(new QmitkFileReaderWriterOptionsWidget(options)); if (iter->GetServiceId() == selectedReaderId) { selectedIndex = i; } } - ui->m_ReaderComboBox->setCurrentIndex(selectedIndex); - if (!hasOptions) - { - ui->m_OptionsBox->setVisible(false); - } + connect(ui->m_ReaderComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetCurrentReader(int))); + ui->m_ReaderComboBox->setCurrentIndex(selectedIndex); if (m_ReaderItems.size() < 2) { ui->m_ReaderLabel->setVisible(false); ui->m_ReaderComboBox->setVisible(false); ui->m_FilePathLabel->setText(QString("File: %1").arg(QString::fromStdString(loadInfo.m_Path))); } else { ui->m_FilePathLabel->setText(QString("for %1").arg(QString::fromStdString(loadInfo.m_Path))); } + ui->m_OptionsBox->setVisible(!qobject_cast(ui->m_StackedOptionsWidget->currentWidget())->GetOptions().empty()); + this->setWindowTitle("File reading options"); } QmitkFileReaderOptionsDialog::~QmitkFileReaderOptionsDialog() { delete ui; } +void QmitkFileReaderOptionsDialog::SetCurrentReader(int index) +{ + ui->m_StackedOptionsWidget->setCurrentIndex(index); + ui->m_OptionsBox->setVisible(!qobject_cast(ui->m_StackedOptionsWidget->currentWidget())->GetOptions().empty()); +} + bool QmitkFileReaderOptionsDialog::ReuseOptions() const { return ui->m_ReuseOptionsCheckBox->isChecked(); } void QmitkFileReaderOptionsDialog::accept() { const int index = m_ReaderItems.size() - ui->m_ReaderComboBox->currentIndex() - 1; m_ReaderItems[index].GetReader()->SetOptions( qobject_cast(ui->m_StackedOptionsWidget->currentWidget())->GetOptions()); m_LoadInfo.m_ReaderSelector.Select(m_ReaderItems[index]); QDialog::accept(); } diff --git a/Modules/QtWidgets/src/QmitkFileReaderOptionsDialog.ui.autosave b/Modules/QtWidgets/src/QmitkFileReaderOptionsDialog.ui.autosave new file mode 100644 index 0000000000..ef10c99113 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkFileReaderOptionsDialog.ui.autosave @@ -0,0 +1,115 @@ + + + QmitkFileReaderOptionsDialog + + + + 0 + 0 + 272 + 186 + + + + Dialog + + + false + + + true + + + + QLayout::SetFixedSize + + + + + Choose file reader + + + + + + + + + + + + + true + + + + + + + Options + + + false + + + + + + + + + + + + Apply to the next files with same type + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QmitkFileReaderOptionsDialog + accept() + + + 254 + 179 + + + 157 + 274 + + + + + buttonBox + rejected() + QmitkFileReaderOptionsDialog + reject() + + + 265 + 179 + + + 286 + 274 + + + + + diff --git a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp index 05c8fb560a..6ff6b43f96 100644 --- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp +++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp @@ -1,110 +1,110 @@ /*============================================================================ 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 "QmitkCESTNormalizeView.h" #include #include "mitkWorkbenchUtil.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateDataProperty.h" #include "mitkNodePredicateDataType.h" #include "QmitkDataStorageComboBoxWithSelectNone.h" #include #include "mitkCESTImageNormalizationFilter.h" -#include "mitkCustomTagParser.h" +#include "mitkCESTPropertyHelper.h" #include "mitkCESTImageDetectionHelper.h" const std::string QmitkCESTNormalizeView::VIEW_ID = "org.mitk.gui.qt.cest.normalize"; void QmitkCESTNormalizeView::SetFocus() { m_Controls.btnNormalize->setFocus(); } void QmitkCESTNormalizeView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnNormalize->setEnabled(false); m_Controls.comboCESTImage->SetPredicate(this->m_IsCESTImagePredicate); m_Controls.comboCESTImage->SetDataStorage(this->GetDataStorage()); connect(m_Controls.btnNormalize, SIGNAL(clicked()), this, SLOT(OnNormalizeButtonClicked())); connect(m_Controls.comboCESTImage, SIGNAL(OnSelectionChanged(const mitk::DataNode *)), this, SLOT(UpdateGUIControls())); UpdateGUIControls(); } void QmitkCESTNormalizeView::UpdateGUIControls() { m_Controls.btnNormalize->setEnabled(m_Controls.comboCESTImage->GetSelectedNode().IsNotNull()); } void QmitkCESTNormalizeView::OnNormalizeButtonClicked() { auto selectedImageNode = m_Controls.comboCESTImage->GetSelectedNode(); if (!selectedImageNode) { MITK_ERROR << "Invalid system state. CEST selection is invalid. Selected node is null_ptr."; return; } auto selectedImage = dynamic_cast(selectedImageNode->GetData()); if (!selectedImageNode) { MITK_ERROR << "Invalid system state. CEST selection is invalid. Selected node is not an image."; return; } std::string offsetsStr = ""; - bool hasOffsets = selectedImage->GetPropertyList()->GetStringProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str(), offsetsStr); + bool hasOffsets = selectedImage->GetPropertyList()->GetStringProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str(), offsetsStr); if (!hasOffsets) { QMessageBox::information(nullptr, "CEST normalization", "Selected image was missing CEST offset information."); return; } if (!mitk::IsNotNormalizedCESTImage(selectedImage)) { QMessageBox::information(nullptr, "CEST normalization", "Selected image already seems to be normalized."); return; } if (selectedImage->GetDimension() == 4) { auto normalizationFilter = mitk::CESTImageNormalizationFilter::New(); normalizationFilter->SetInput(selectedImage); normalizationFilter->Update(); auto resultImage = normalizationFilter->GetOutput(); mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); dataNode->SetData(resultImage); std::string normalizedName = selectedImageNode->GetName() + "_normalized"; dataNode->SetName(normalizedName); this->GetDataStorage()->Add(dataNode); } } QmitkCESTNormalizeView::QmitkCESTNormalizeView() { auto isImage = mitk::NodePredicateDataType::New("Image"); this->m_IsCESTImagePredicate = mitk::NodePredicateAnd::New(isImage, mitk::CreateAnyCESTImageNodePredicate()).GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp index d10b02bf0f..2b8c2c96bf 100644 --- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp @@ -1,815 +1,815 @@ /*============================================================================ 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. ============================================================================*/ // itk #include "itksys/SystemTools.hxx" #include #include // Blueberry #include #include // Qmitk #include "QmitkCESTStatisticsView.h" // Qt #include #include // qwt #include // mitk #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include // boost #include #include // stl #include #include #include #include #include #include namespace { template void GetSortPermutation(std::vector &out, const std::vector &determiningVector, Compare compare = std::less()) { out.resize(determiningVector.size()); std::iota(out.begin(), out.end(), 0); std::sort(out.begin(), out.end(), [&](unsigned i, unsigned j) { return compare(determiningVector[i], determiningVector[j]); }); } template void ApplyPermutation(const std::vector &order, std::vector &vectorToSort) { assert(order.size() == vectorToSort.size()); std::vector tempVector(vectorToSort.size()); for (unsigned i = 0; i < vectorToSort.size(); i++) { tempVector[i] = vectorToSort[order[i]]; } vectorToSort = tempVector; } template void ApplyPermutation(const std::vector &order, std::vector ¤tVector, std::vector &... remainingVectors) { ApplyPermutation(order, currentVector); ApplyPermutation(order, remainingVectors...); } template void SortVectors(const std::vector &orderDeterminingVector, Compare comparison, std::vector &... vectorsToBeSorted) { std::vector order; GetSortPermutation(order, orderDeterminingVector, comparison); ApplyPermutation(order, vectorsToBeSorted...); } } // namespace const std::string QmitkCESTStatisticsView::VIEW_ID = "org.mitk.views.ceststatistics"; QmitkCESTStatisticsView::QmitkCESTStatisticsView(QObject * /*parent*/, const char * /*name*/) { this->m_CalculatorJob = new QmitkImageStatisticsCalculationJob(); m_currentSelectedPosition.Fill(0.0); m_currentSelectedTimePoint = 0.; m_CrosshairPointSet = mitk::PointSet::New(); } QmitkCESTStatisticsView::~QmitkCESTStatisticsView() { while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } delete this->m_CalculatorJob; } void QmitkCESTStatisticsView::SetFocus() { m_Controls.threeDimToFourDimPushButton->setFocus(); } void QmitkCESTStatisticsView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect( m_Controls.threeDimToFourDimPushButton, SIGNAL(clicked()), this, SLOT(OnThreeDimToFourDimPushButtonClicked())); connect((QObject *)this->m_CalculatorJob, SIGNAL(finished()), this, SLOT(OnThreadedStatisticsCalculationEnds()), Qt::QueuedConnection); connect((QObject *)(this->m_Controls.fixedRangeCheckBox), SIGNAL(toggled(bool)), (QObject *)this, SLOT(OnFixedRangeCheckBoxToggled(bool))); connect((QObject *)(this->m_Controls.fixedRangeLowerDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); connect((QObject *)(this->m_Controls.fixedRangeUpperDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); m_Controls.threeDimToFourDimPushButton->setEnabled(false); m_Controls.widget_statistics->SetDataStorage(this->GetDataStorage()); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } void QmitkCESTStatisticsView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkCESTStatisticsView::RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkCESTStatisticsView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList &nodes) { if (nodes.empty()) { std::stringstream message; message << "Please select an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } // iterate all selected objects bool atLeastOneWasCESTImage = false; foreach (mitk::DataNode::Pointer node, nodes) { if (node.IsNull()) { continue; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( - node->GetData()->GetProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str()).GetPointer())); + node->GetData()->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str()).GetPointer())); atLeastOneWasCESTImage = atLeastOneWasCESTImage || zSpectrumSet; if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetImageNodes({node.GetPointer()}); } else { m_MaskImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } } if (dynamic_cast(node->GetData()) != nullptr) { m_MaskPlanarFigure = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } if (dynamic_cast(node->GetData()) != nullptr) { m_PointSet = dynamic_cast(node->GetData()); } } // We only want to offer normalization or timestep copying if one object is selected if (nodes.size() == 1) { if (dynamic_cast(nodes.front()->GetData())) { m_Controls.threeDimToFourDimPushButton->setDisabled(atLeastOneWasCESTImage); } else { m_Controls.threeDimToFourDimPushButton->setEnabled(false); std::stringstream message; message << "The selected node is not an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } this->Clear(); return; } // we always need a mask, either image or planar figure as well as an image for further processing if (nodes.size() != 2) { this->Clear(); return; } m_Controls.threeDimToFourDimPushButton->setEnabled(false); if (!atLeastOneWasCESTImage) { std::stringstream message; message << "None of the selected data nodes contains required CEST meta information"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } bool bothAreImages = (m_ZImage.GetPointer() != nullptr) && (m_MaskImage.GetPointer() != nullptr); if (bothAreImages) { bool geometriesMatch = mitk::Equal(*(m_ZImage->GetTimeGeometry()), *(m_MaskImage->GetTimeGeometry()), mitk::eps, false); if (!geometriesMatch) { std::stringstream message; message << "The selected images have different geometries."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } } if (!this->DataSanityCheck()) { this->Clear(); return; } if (m_PointSet.IsNull()) { // initialize thread and trigger it this->m_CalculatorJob->SetIgnoreZeroValueVoxel(false); this->m_CalculatorJob->Initialize(m_ZImage.GetPointer(), m_MaskImage.GetPointer(), m_MaskPlanarFigure.GetPointer()); std::stringstream message; message << "Calculating statistics..."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); try { // Compute statistics this->m_CalculatorJob->start(); } catch (const mitk::Exception &e) { std::stringstream message; message << "" << e.GetDescription() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::runtime_error &e) { // In case of exception, print error message on GUI std::stringstream message; message << "" << e.what() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::exception &e) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible "; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } } if (m_PointSet.IsNotNull()) { if (m_ZImage->GetDimension() == 4) { AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } else { MITK_WARN << "Expecting a 4D image."; } } } void QmitkCESTStatisticsView::OnThreadedStatisticsCalculationEnds() { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); if (this->m_CalculatorJob->GetStatisticsUpdateSuccessFlag()) { auto statistics = this->m_CalculatorJob->GetStatisticsData(); std::string statisticsNodeName = "CEST_statistics"; auto statisticsNode = mitk::CreateImageStatisticsNode(statistics, statisticsNodeName); auto imageRule = mitk::StatisticsToImageRelationRule::New(); imageRule->Connect(statistics, m_CalculatorJob->GetStatisticsImage()); if (m_CalculatorJob->GetMaskImage()) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); maskRule->Connect(statistics, m_CalculatorJob->GetMaskImage()); } else if (m_CalculatorJob->GetPlanarFigure()) { auto planarFigureRule = mitk::StatisticsToMaskRelationRule::New(); planarFigureRule->Connect(statistics, m_CalculatorJob->GetPlanarFigure()); } this->GetDataStorage()->Add(statisticsNode); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); QmitkPlotWidget::DataVector means(numberOfSpectra); QmitkPlotWidget::DataVector stdevs(numberOfSpectra); for (unsigned int index = 0; index < numberOfSpectra; ++index) { means[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::MEAN()); stdevs[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::STANDARDDEVIATION()); } QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, means, stdevs); ::SortVectors(xValues, std::less(), xValues, means, stdevs); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve("Spectrum"); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, means, stdevs, stdevs); this->m_Controls.m_DataViewWidget->SetErrorPen(curveId, QPen(Qt::blue)); QwtSymbol *blueSymbol = new QwtSymbol(QwtSymbol::Rect, QColor(Qt::blue), QColor(Qt::blue), QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, blueSymbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); m_Controls.m_StatisticsGroupBox->setEnabled(true); m_Controls.m_StatisticsGroupBox->setEnabled(true); if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale( 2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } } else { m_Controls.labelWarning->setText(m_CalculatorJob->GetLastErrorMessage().c_str()); m_Controls.labelWarning->setVisible(true); this->Clear(); } } void QmitkCESTStatisticsView::OnFixedRangeDoubleSpinBoxChanged() { if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } this->m_Controls.m_DataViewWidget->Replot(); } template void QmitkCESTStatisticsView::PlotPointSet(itk::Image *image) { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); mitk::PointSet::Pointer internalPointset; if (m_PointSet.IsNotNull()) { internalPointset = m_PointSet; } else { internalPointset = m_CrosshairPointSet; } if (internalPointset.IsNull()) { return; } if (!this->DataSanityCheck()) { m_Controls.labelWarning->setText("Data can not be plotted, internally inconsistent."); m_Controls.labelWarning->show(); return; } auto maxIndex = internalPointset->GetMaxId().Index(); for (std::size_t number = 0; number < maxIndex + 1; ++number) { mitk::PointSet::PointType point; if (!internalPointset->GetPointIfExists(number, &point)) { continue; } if (!this->m_ZImage->GetGeometry()->IsInside(point)) { continue; } itk::Index<3> itkIndex; this->m_ZImage->GetGeometry()->WorldToIndex(point, itkIndex); itk::Index itkIndexTime; itkIndexTime[0] = itkIndex[0]; itkIndexTime[1] = itkIndex[1]; itkIndexTime[2] = itkIndex[2]; QmitkPlotWidget::DataVector values(numberOfSpectra); for (std::size_t step = 0; step < numberOfSpectra; ++step) { if (VImageDimension == 4) { itkIndexTime[3] = step; } values[step] = image->GetPixel(itkIndexTime); } std::stringstream name; name << "Point " << number; // Qcolor enums go from 0 to 19, but 19 is transparent and 0,1 are for bitmaps // 3 is white and thus not visible QColor color(static_cast(number % 17 + 4)); QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, values); ::SortVectors(xValues, std::less(), xValues, values); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve(name.str().c_str()); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, values); this->m_Controls.m_DataViewWidget->SetCurvePen(curveId, QPen(color)); QwtSymbol *symbol = new QwtSymbol(QwtSymbol::Rect, color, color, QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, symbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); } if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); } void QmitkCESTStatisticsView::OnFixedRangeCheckBoxToggled(bool state) { this->m_Controls.fixedRangeLowerDoubleSpinBox->setEnabled(state); this->m_Controls.fixedRangeUpperDoubleSpinBox->setEnabled(state); } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); } } xValues = tempX; yValues = tempY; } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues, QmitkPlotWidget::DataVector &stdDevs) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; QmitkPlotWidget::DataVector tempDevs; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); tempDevs.push_back(stdDevs.at(index)); } } xValues = tempX; yValues = tempY; stdDevs = tempDevs; } void QmitkCESTStatisticsView::OnThreeDimToFourDimPushButtonClicked() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode *node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(nullptr, "CEST View", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData *data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image *image = dynamic_cast(data); if (image) { if (image->GetDimension() == 4) { AccessFixedDimensionByItk(image, CopyTimesteps, 4); } this->Clear(); } } } template void QmitkCESTStatisticsView::CopyTimesteps(itk::Image *image) { typedef itk::Image ImageType; // typedef itk::PasteImageFilter PasteImageFilterType; unsigned int numberOfTimesteps = image->GetLargestPossibleRegion().GetSize(3); typename ImageType::RegionType sourceRegion = image->GetLargestPossibleRegion(); sourceRegion.SetSize(3, 1); typename ImageType::RegionType targetRegion = image->GetLargestPossibleRegion(); targetRegion.SetSize(3, 1); for (unsigned int timestep = 1; timestep < numberOfTimesteps; ++timestep) { targetRegion.SetIndex(3, timestep); itk::ImageRegionConstIterator sourceIterator(image, sourceRegion); itk::ImageRegionIterator targetIterator(image, targetRegion); while (!sourceIterator.IsAtEnd()) { targetIterator.Set(sourceIterator.Get()); ++sourceIterator; ++targetIterator; } } } bool QmitkCESTStatisticsView::SetZSpectrum(mitk::StringProperty *zSpectrumProperty) { if (zSpectrumProperty == nullptr) { return false; } mitk::LocaleSwitch localeSwitch("C"); std::string zSpectrumString = zSpectrumProperty->GetValueAsString(); std::istringstream iss(zSpectrumString); std::vector zSpectra; std::copy( std::istream_iterator(iss), std::istream_iterator(), std::back_inserter(zSpectra)); m_zSpectrum.clear(); m_zSpectrum.resize(0); for (auto const &spectrumString : zSpectra) { m_zSpectrum.push_back(std::stod(spectrumString)); } return (m_zSpectrum.size() > 0); } bool QmitkCESTStatisticsView::DataSanityCheck() { QmitkPlotWidget::DataVector::size_type numberOfSpectra = m_zSpectrum.size(); // if we do not have a spectrum, the data can not be processed if (numberOfSpectra == 0) { return false; } // if we do not have CEST image data, the data can not be processed if (m_ZImage.IsNull()) { return false; } // if the CEST image data and the meta information do not match, the data can not be processed if (numberOfSpectra != m_ZImage->GetTimeSteps()) { MITK_INFO << "CEST meta information and number of volumes does not match."; return false; } // if we have neither a mask image, a point set nor a mask planar figure, we can not do statistics // statistics on the whole image would not make sense if (m_MaskImage.IsNull() && m_MaskPlanarFigure.IsNull() && m_PointSet.IsNull() && m_CrosshairPointSet->IsEmpty()) { return false; } // if we have a mask image and a mask planar figure, we can not do statistics // we do not know which one to use if (m_MaskImage.IsNotNull() && m_MaskPlanarFigure.IsNotNull()) { return false; } return true; } void QmitkCESTStatisticsView::Clear() { this->m_zSpectrum.clear(); this->m_zSpectrum.resize(0); this->m_ZImage = nullptr; this->m_MaskImage = nullptr; this->m_MaskPlanarFigure = nullptr; this->m_PointSet = nullptr; this->m_Controls.m_DataViewWidget->Clear(); this->m_Controls.m_StatisticsGroupBox->setEnabled(false); this->m_Controls.widget_statistics->SetImageNodes({}); this->m_Controls.widget_statistics->SetMaskNodes({}); } void QmitkCESTStatisticsView::OnSliceChanged() { mitk::Point3D currentSelectedPosition = this->GetRenderWindowPart()->GetSelectedPosition(nullptr); mitk::TimePointType currentSelectedTimePoint = this->GetRenderWindowPart()->GetSelectedTimePoint(); if (m_currentSelectedPosition != currentSelectedPosition || currentSelectedTimePoint != currentSelectedTimePoint) //|| m_selectedNodeTime > m_currentPositionTime) { // the current position has been changed or the selected node has been changed since the last position validation -> // check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimePoint = currentSelectedTimePoint; m_currentPositionTime.Modified(); m_CrosshairPointSet->Clear(); m_CrosshairPointSet->SetPoint(0, m_currentSelectedPosition); QList nodes = this->GetDataManagerSelection(); if (nodes.empty() || nodes.size() > 1) return; mitk::DataNode *node = nodes.front(); if (!node) { return; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( - node->GetData()->GetProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str()).GetPointer())); + node->GetData()->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str()).GetPointer())); if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); } else { return; } } else { return; } this->m_Controls.m_DataViewWidget->Clear(); AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } }