diff --git a/Modules/CEST/files.cmake b/Modules/CEST/files.cmake
index 19a794674c..34a71a20af 100644
--- a/Modules/CEST/files.cmake
+++ b/Modules/CEST/files.cmake
@@ -1,11 +1,13 @@
 file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*")
 
 set(CPP_FILES
   mitkCESTImageNormalizationFilter.cpp
   mitkCustomTagParser.cpp
+  mitkCESTImageDetectionHelper.cpp
 )
 
 set(RESOURCE_FILES
   1416.json
   1485.json
+  1494.json
 )
diff --git a/Modules/CEST/include/mitkCESTImageDetectionHelper.h b/Modules/CEST/include/mitkCESTImageDetectionHelper.h
new file mode 100644
index 0000000000..c97d3b6815
--- /dev/null
+++ b/Modules/CEST/include/mitkCESTImageDetectionHelper.h
@@ -0,0 +1,45 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+#ifndef __mitkCESTImageDetectionHelper_h
+#define __mitkCESTImageDetectionHelper_h
+
+#include <MitkCESTExports.h>
+
+#include "mitkNodePredicateBase.h"
+
+namespace mitk
+{
+  class Image;
+
+  /** This helper function can be used to check if an image is an type of CEST image.
+  */
+  MITKCEST_EXPORT bool IsAnyCESTImage(const Image* cestImage);
+
+  /** This helper function can be used to check if an image is an CEST image (recording a CEST, Wasabi or simelar sequence).
+  */
+  MITKCEST_EXPORT bool IsCESTorWasabiImage(const Image* cestImage);
+
+  /** This helper function can be used to check if an image is an CEST T1 image (recording a T1 sequence for CEST contrasts).
+  */
+  MITKCEST_EXPORT bool IsCESTT1Image(const Image* cestImage);
+
+  MITKCEST_EXPORT NodePredicateBase::Pointer CreateAnyCESTImageNodePredicate();
+  MITKCEST_EXPORT NodePredicateBase::Pointer CreateCESTorWasabiImageNodePredicate();
+  MITKCEST_EXPORT NodePredicateBase::Pointer CreateCESTT1ImageNodePredicate();
+
+} // END mitk namespace
+
+#endif
diff --git a/Modules/CEST/include/mitkCESTImageNormalizationFilter.h b/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
index 190fb4e123..b681a6c08b 100644
--- a/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
+++ b/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
@@ -1,88 +1,88 @@
 /*===================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center,
 Division of Medical and Biological Informatics.
 All rights reserved.
 
 This software is distributed WITHOUT ANY WARRANTY; without
 even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.
 
 See LICENSE.txt or http://www.mitk.org for details.
 
 ===================================================================*/
 #ifndef __mitkCESTImageNormalizationFilter_h
 #define __mitkCESTImageNormalizationFilter_h
 
 #include <MitkCESTExports.h>
 
 // MITK
 #include "mitkImageToImageFilter.h"
 
 namespace mitk
 {
   /** \brief Normalization filter for CEST images.
    *
    * This filter can be used to normalize CEST images, it only works with 4D images and assumes that the input
    * mitk::Image has a property called mitk::CustomTagParser::m_OffsetsPropertyName, whith offsets separated by
    * spaces. The number of offsets has to match the number of timesteps.
    *
    * Each timestep with a corresponding offset greater than 299 or less than -299 will be interpreted as normalization (M0) image.
    * If only one M0 image is present normalization will be done by dividing the voxel value by the corresponding
    * M0 voxel value. If multiple M0 images are present normalization between any two M0 images will be done by
    * dividing by a linear interpolation between the two.
    * The M0 images themselves will be removed from the result.
    * The output image will have the same 3D geometry as the input image, a time geometry only consisting of non M0 images and a double pixel type.
    */
   class MITKCEST_EXPORT CESTImageNormalizationFilter : public ImageToImageFilter
   {
   public:
     mitkClassMacro(CESTImageNormalizationFilter, ImageToImageFilter);
     itkFactorylessNewMacro(Self) itkCloneMacro(Self)
 
   protected:
     /*!
     \brief standard constructor
     */
     CESTImageNormalizationFilter();
     /*!
     \brief standard destructor
     */
     ~CESTImageNormalizationFilter() override;
     /*!
     \brief Method generating the output information of this filter (e.g. image dimension, image type, etc.).
     The interface ImageToImageFilter requires this implementation. Everything is taken from the input image.
     */
     void GenerateOutputInformation() override;
     /*!
     \brief Method generating the output of this filter. Called in the updated process of the pipeline.
     This method generates the normalized output image.
     */
     void GenerateData() override;
 
     /** Internal templated method that normalizes across timesteps
     */
     template <typename TPixel, unsigned int VImageDimension>
     void NormalizeTimeSteps(const itk::Image<TPixel, VImageDimension>* image);
 
     /// Offsets without M0s
     std::string m_RealOffsets;
 
     /// non M0 indices
     std::vector< unsigned int > m_NonM0Indices;
 
   };
 
   /** This helper function can be used to check if an image was already normalized.
   * The function assumes that an image that is not normalized is indicated by the following properties:
-  * - mitk::Image has a property called mitk::CustomTagParser::m_OffsetsPropertyName, whith offsets separated by spaces.
+  * - mitk::Image has a property called mitk::CEST_PROPERTY_NAME_OFFSETS, with offsets separated by spaces.
   * - The number of offsets has to match the number of timesteps.
   * - At least one of the offsets is a normalization (M0) image. M0 are indicated by offsets greater than 299 or less than -299.
   */
   MITKCEST_EXPORT bool IsNotNormalizedCESTImage(const Image* cestImage);
 
 } // END mitk namespace
 
 #endif
diff --git a/Modules/CEST/resource/1494.json b/Modules/CEST/resource/1494.json
new file mode 100644
index 0000000000..5a0a20aebe
--- /dev/null
+++ b/Modules/CEST/resource/1494.json
@@ -0,0 +1,20 @@
+{
+  "1493_3D" : "revision_json",
+  "sWiPMemBlock.alFree[0]" : "AdvancedMode",
+  "sWiPMemBlock.alFree[1]" : "RecoveryMode",
+  "sWiPMemBlock.alFree[2]" : "DoubleIrrMode",
+  "sWiPMemBlock.alFree[3]" : "MtMode",
+  "sWiPMemBlock.alFree[4]" : "PreparationType",
+  "sWiPMemBlock.alFree[5]" : "PulseType",
+  "sWiPMemBlock.alFree[6]" : "SamplingType",
+  "sWiPMemBlock.alFree[7]" : "SpoilingType",
+  "sWiPMemBlock.alFree[8]" : "measurements",
+  "sWiPMemBlock.alFree[9]" : "NumberRFBlocks",
+  "sWiPMemBlock.alFree[10]" : "PulsesPerRFBlock",
+  "sWiPMemBlock.alFree[11]" : "PulseDuration",
+  "sWiPMemBlock.alFree[12]" : "DutyCycle",
+  "sWiPMemBlock.alFree[13]" : "RecoveryTime",
+  "sWiPMemBlock.alFree[14]" : "RecoveryTimeM0",
+  "sWiPMemBlock.adFree[0]" : "Offset",
+  "sWiPMemBlock.adFree[1]" : "B1Amplitude"
+}
diff --git a/Modules/CEST/src/mitkCESTImageDetectionHelper.cpp b/Modules/CEST/src/mitkCESTImageDetectionHelper.cpp
new file mode 100644
index 0000000000..9df6530d8c
--- /dev/null
+++ b/Modules/CEST/src/mitkCESTImageDetectionHelper.cpp
@@ -0,0 +1,89 @@
+/*===================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center,
+Division of Medical and Biological Informatics.
+All rights reserved.
+
+This software is distributed WITHOUT ANY WARRANTY; without
+even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE.
+
+See LICENSE.txt or http://www.mitk.org for details.
+
+===================================================================*/
+
+#include "mitkCESTImageDetectionHelper.h"
+
+#include "mitkCustomTagParser.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();
+};
+
+bool mitk::IsCESTorWasabiImage(const Image* cestImage)
+{
+  if (!cestImage) return false;
+
+  return IsAnyCESTImage(cestImage) && !IsCESTT1Image(cestImage);
+};
+
+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<const mitk::Image*>(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<const mitk::Image*>(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<const mitk::Image*>(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 9b8b6ff04a..7377fa6189 100644
--- a/Modules/CEST/src/mitkCESTImageNormalizationFilter.cpp
+++ b/Modules/CEST/src/mitkCESTImageNormalizationFilter.cpp
@@ -1,233 +1,233 @@
 /*===================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center,
 Division of Medical and Biological Informatics.
 All rights reserved.
 
 This software is distributed WITHOUT ANY WARRANTY; without
 even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.
 
 See LICENSE.txt or http://www.mitk.org for details.
 
 ===================================================================*/
 
 #include "mitkCESTImageNormalizationFilter.h"
 
 #include <mitkCustomTagParser.h>
 #include <mitkImage.h>
 #include <mitkImageAccessByItk.h>
 #include <mitkImageCast.h>
 
 #include <boost/algorithm/string.hpp>
 
 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());
   // remove uids
   resultMitkImage->GetPropertyList()->DeleteProperty("DICOM.0008.0018");
   resultMitkImage->GetPropertyList()->DeleteProperty("DICOM.0020.000D");
   resultMitkImage->GetPropertyList()->DeleteProperty("DICOM.0020.000E");
 
 }
 
 std::vector<double> ExtractOffsets(const mitk::Image* image)
 {
   std::vector<double> result;
 
   if (image)
   {
     std::string offsets = "";
     std::vector<std::string> parts;
-    if (image->GetPropertyList()->GetStringProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str(), offsets))
+    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 <typename TPixel, unsigned int VImageDimension>
 void mitk::CESTImageNormalizationFilter::NormalizeTimeSteps(const itk::Image<TPixel, VImageDimension>* image)
 {
   typedef itk::Image<TPixel, VImageDimension> ImageType;
   typedef itk::Image<double, VImageDimension> OutputImageType;
 
   auto offsets = ExtractOffsets(this->GetInput());
 
   // determine normalization images
   std::vector<unsigned int> 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<ImageType> lowerMZeroIterator(image, lowerMZeroRegion);
       itk::ImageRegionConstIterator<ImageType> upperMZeroIterator(image, upperMZeroRegion);
       itk::ImageRegionConstIterator<ImageType> sourceIterator(image, sourceRegion);
       itk::ImageRegionIterator<OutputImageType> 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<OutputImageType>(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);
 
   for (auto offset : offsets)
   {
     if (offset < -299 || offset > 299)
     {
       return true;
     }
   }
   return false;
 };
diff --git a/Modules/CEST/src/mitkCustomTagParser.cpp b/Modules/CEST/src/mitkCustomTagParser.cpp
index 9ed83c1699..6b1bbfbda7 100644
--- a/Modules/CEST/src/mitkCustomTagParser.cpp
+++ b/Modules/CEST/src/mitkCustomTagParser.cpp
@@ -1,827 +1,827 @@
 /*===================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center,
 Division of Medical and Biological Informatics.
 All rights reserved.
 
 This software is distributed WITHOUT ANY WARRANTY; without
 even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.
 
 See LICENSE.txt or http://www.mitk.org for details.
 
 ===================================================================*/
 
 #include "mitkCustomTagParser.h"
 
 #include <mitkProperties.h>
 #include <mitkStringProperty.h>
 #include <mitkLocaleSwitch.h>
 
 #include "mitkIPropertyPersistence.h"
 #include "usGetModuleContext.h"
 #include "usModule.h"
 #include "usModuleContext.h"
 #include "usModuleResource.h"
 #include "usModuleResourceStream.h"
 
 #include <itksys/SystemTools.hxx>
 
 #include <Poco/Glob.h>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/property_tree/json_parser.hpp>
 #include <boost/property_tree/ptree.hpp>
 #include <boost/tokenizer.hpp>
 
 #include <algorithm>
 #include <map>
 
 namespace
 {
   mitk::IPropertyPersistence *GetPersistenceService()
   {
     mitk::IPropertyPersistence *result = nullptr;
 
     std::vector<us::ServiceReference<mitk::IPropertyPersistence>> persRegisters =
       us::GetModuleContext()->GetServiceReferences<mitk::IPropertyPersistence>();
     if (!persRegisters.empty())
     {
       if (persRegisters.size() > 1)
       {
         MITK_WARN << "Multiple property description services found. Using just one.";
       }
       result = us::GetModuleContext()->GetService<mitk::IPropertyPersistence>(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<std::string, std::string> privateParameters;
 
   // convert hex to ascii
   // the Siemens private tag contains the information like this
   // "43\52\23\34" we jump over each \ and convert the number
   int len = dicomPropertyString.length();
   std::string asciiString;
   for (int i = 0; i < len; i += 3)
   {
     std::string byte = dicomPropertyString.substr(i, 2);
     auto chr = (char)(int)strtol(byte.c_str(), nullptr, 16);
     asciiString.push_back(chr);
   }
 
   // extract parameter list
   std::size_t beginning = asciiString.find("### ASCCONV BEGIN ###") + 21;
   std::size_t ending = asciiString.find("### ASCCONV END ###");
   std::string parameterListString = asciiString.substr(beginning, ending - beginning);
 
   boost::replace_all(parameterListString, "\r\n", "\n");
   boost::char_separator<char> newlineSeparator("\n");
   boost::tokenizer<boost::char_separator<char>> parameters(parameterListString, newlineSeparator);
   for (const auto &parameter : parameters)
   {
     std::vector<std::string> 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 sampling = "";
   std::string offset = "";
   std::string measurements = "";
   bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling);
-  results->GetStringProperty(CEST_PROPERTY_NAME_OFFSETS().c_str(), offset);
+  results->GetStringProperty("CEST.Offset", offset);
   results->GetStringProperty("CEST.measurements", measurements);
 
   if ("" == measurements)
   {
     std::string stringRepetitions = "";
     std::string stringAverages = "";
     results->GetStringProperty("CEST.repetitions", stringRepetitions);
     results->GetStringProperty("CEST.averages", stringAverages);
     std::stringstream  measurementStream;
     try
     {
       measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages);
       measurements = measurementStream.str();
       MITK_INFO << "Could not find measurements, assuming repetitions + averages. Which is: " << measurements;
     }
     catch (const std::invalid_argument &ia)
     {
       MITK_ERROR
         << "Could not find measurements, fallback assumption of repetitions + averages could not be determined either: "
         << ia.what();
     }
   }
 
   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());
 
     if (list.good())
     {
       std::string currentTime;
       while (std::getline(list, currentTime))
       {
         trecStream << currentTime << " ";
       }
     }
     else
     {
       MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath;
     }
 
     results->SetStringProperty(CEST_PROPERTY_NAME_TREC().c_str(), trecStream.str().c_str());
   }
   else
   {
     MITK_INFO << "Parsed as CEST or WASABI image";
   }
 
   if (hasSamplingInformation)
   {
     std::string offsets = GetOffsetString(sampling, offset, measurements);
     results->SetStringProperty(m_OffsetsPropertyName.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;
 }
 
 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<int> mitk::CustomTagParser::GetInternalRevisions()
 {
   const std::vector<us::ModuleResource> configs =
     us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false);
 
   std::vector<int> availableRevisionsVector;
 
   for (auto const resource : configs)
   {
     availableRevisionsVector.push_back(std::stoi(resource.GetBaseName()));
   }
 
   return availableRevisionsVector;
 }
 
 std::vector<int> mitk::CustomTagParser::GetExternalRevisions()
 {
   std::string stringToJSONDirectory = GetExternalJSONDirectory();
 
   std::string prospectiveJsonsPath = stringToJSONDirectory + "/*.json";
 
   std::set<std::string> JsonFiles;
   Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS);
 
   std::vector<int> 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<int> 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;
 
   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<double> 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())
     {
       std::string currentOffset;
       while (std::getline(list, currentOffset))
       {
         results << currentOffset << " ";
       }
     }
     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/test/mitkCESTDICOMReaderServiceTest.cpp b/Modules/CEST/test/mitkCESTDICOMReaderServiceTest.cpp
index 87357f080d..74204eb069 100644
--- a/Modules/CEST/test/mitkCESTDICOMReaderServiceTest.cpp
+++ b/Modules/CEST/test/mitkCESTDICOMReaderServiceTest.cpp
@@ -1,95 +1,95 @@
 /*===================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center,
 Division of Medical and Biological Informatics.
 All rights reserved.
 
 This software is distributed WITHOUT ANY WARRANTY; without
 even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.
 
 See LICENSE.txt or http://www.mitk.org for details.
 
 ===================================================================*/
 
 // Testing
 #include "mitkTestFixture.h"
 #include "mitkTestingMacros.h"
 
 // std includes
 #include <string>
 
 // MITK includes
 #include <mitkImage.h>
 #include <mitkIOUtil.h>
 #include <mitkTemporoSpatialStringProperty.h>
-
+#include "mitkCESTImageDetectionHelper.h"
 #include "mitkCustomTagParser.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<mitk::Image>(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");
     std::string temp;
-    CPPUNIT_ASSERT_MESSAGE("Make certain image is not loaded as T1.", !cestImage->GetPropertyList()->GetStringProperty(mitk::CEST_PROPERTY_NAME_TREC().c_str(), 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<mitk::Image>(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 ");
     std::string temp;
-    CPPUNIT_ASSERT_MESSAGE("Make certain image is not loaded as T1.", !cestImage->GetPropertyList()->GetStringProperty(mitk::CEST_PROPERTY_NAME_TREC().c_str(), 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<mitk::Image>(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.", cestImage->GetPropertyList()->GetStringProperty(mitk::CEST_PROPERTY_NAME_TREC().c_str(), temp));
+    CPPUNIT_ASSERT_MESSAGE("Make certain image is loaded as T1.", mitk::IsCESTT1Image(cestImage));
   }
 
 };
 
 MITK_TEST_SUITE_REGISTRATION(mitkCESTDICOMReaderService)
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 9cc85d061b..60d7607605 100644
--- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp
+++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp
@@ -1,114 +1,114 @@
 /*===================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center,
 Division of Medical and Biological Informatics.
 All rights reserved.
 
 This software is distributed WITHOUT ANY WARRANTY; without
 even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.
 
 See LICENSE.txt or http://www.mitk.org for details.
 
 ===================================================================*/
 
 #include "QmitkCESTNormalizeView.h"
 
 #include <QMessageBox>
 
 #include "mitkWorkbenchUtil.h"
 
 #include "mitkNodePredicateAnd.h"
 #include "mitkNodePredicateDataProperty.h"
 #include "mitkNodePredicateDataType.h"
 
 #include "QmitkDataStorageComboBoxWithSelectNone.h"
 
 #include <mitkImage.h>
 #include "mitkCESTImageNormalizationFilter.h"
 #include "mitkCustomTagParser.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<mitk::Image*>(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);
     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");
-  auto isCESTImage = mitk::NodePredicateDataProperty::New(mitk::CEST_PROPERTY_NAME_TOTALSCANTIME().c_str());
 
-  this->m_IsCESTImagePredicate = mitk::NodePredicateAnd::New(isImage, isCESTImage).GetPointer();
+  this->m_IsCESTImagePredicate = mitk::NodePredicateAnd::New(isImage, mitk::CreateAnyCESTImageNodePredicate()).GetPointer();
 }